a6d590b915f74dc2e4e1d0a6fb85473b5d1b35f8
[platform/upstream/libsolv.git] / tools / repo_susetags.c
1 /*
2  * Copyright (c) 2007, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 #include <sys/types.h>
9 #include <limits.h>
10 #include <fcntl.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14
15 #include "pool.h"
16 #include "repo.h"
17 #include "tools_util.h"
18 #include "repo_susetags.h"
19
20 struct parsedata {
21   char *kind;
22   Repo *repo;
23   Repodata *data;
24   struct parsedata_common common;
25   int last_found_source;
26   char **share_with;
27   int nshare;
28   Id (*dirs)[3]; // dirid, size, nfiles
29   int ndirs;
30   Id langcache[ID_NUM_INTERNAL];
31 };
32
33 static char *flagtab[] = {
34   ">",
35   "=",
36   ">=",
37   "<",
38   "!=",
39   "<="
40 };
41
42
43 static Id
44 langtag(struct parsedata *pd, Id tag, const char *language)
45 {
46   char *p;
47   const char *tagname;
48
49   if (!language || tag >= ID_NUM_INTERNAL)
50     return tag;
51   if (!pd->langcache[tag])
52     {
53       tagname = id2str(pd->repo->pool, tag);
54       p = sat_malloc(strlen(tagname) + strlen(language) + 2);
55       sprintf(p, "%s:%s", tagname, language);
56       pd->langcache[tag] = str2id(pd->repo->pool, p, 1);
57       sat_free(p);
58     }
59   return pd->langcache[tag];
60 }
61
62 /*
63  * adddep
64  * create and add dependency
65  */
66
67 static unsigned int
68 adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, char *line, Id marker, char *kind)
69 {
70   int i, flags;
71   Id id, evrid;
72   char *sp[4];
73
74   i = split(line + 5, sp, 4); /* name, <op>, evr, ? */
75   if (i != 1 && i != 3) /* expect either 'name' or 'name' <op> 'evr' */
76     {
77       fprintf(stderr, "Bad dependency line: %s\n", line);
78       exit(1);
79     }
80   if (kind)
81     id = str2id(pool, join2(kind, ":", sp[0]), 1);
82   else
83     id = str2id(pool, sp[0], 1);
84   if (i == 3)
85     {
86       evrid = makeevr(pool, sp[2]);
87       for (flags = 0; flags < 6; flags++)
88         if (!strcmp(sp[1], flagtab[flags]))
89           break;
90       if (flags == 6)
91         {
92           fprintf(stderr, "Unknown relation '%s'\n", sp[1]);
93           exit(1);
94         }
95       id = rel2id(pool, id, evrid, flags + 1, 1);
96     }
97   return repo_addid_dep(pd->repo, olddeps, id, marker);
98 }
99
100
101 /*
102  * add_location
103  * 
104  */
105
106 static void
107 add_location(struct parsedata *pd, char *line, Solvable *s, unsigned entry)
108 {
109   Pool *pool = s->repo->pool;
110   char *sp[3];
111   int i;
112
113   i = split(line, sp, 3);
114   if (i != 2 && i != 3)
115     {
116       fprintf(stderr, "Bad location line: %s\n", line);
117       exit(1);
118     }
119   /* If we have a dirname, let's see if it's the same as arch.  In that
120      case don't store it.  */
121   if (i == 3 && !strcmp (sp[2], id2str (pool, s->arch)))
122     sp[2] = 0, i = 2;
123   if (i == 3 && sp[2])
124     {
125       /* medianr filename dir
126          don't optimize this one */
127       repodata_set_constant(pd->data, entry, SOLVABLE_MEDIANR, atoi(sp[0]));
128       repodata_set_poolstr(pd->data, entry, SOLVABLE_MEDIADIR, sp[2]);
129       repodata_set_str(pd->data, entry, SOLVABLE_MEDIAFILE, sp[1]);
130       return;
131     }
132   else
133     {
134       /* Let's see if we can optimize this a bit.  If the media file name
135          can be formed by the base rpm information we don't store it, but
136          only a flag that we've seen it.  */
137       unsigned int medianr = atoi (sp[0]);
138       const char *n1 = sp[1];
139       const char *n2 = id2str (pool, s->name);
140       for (n2 = id2str (pool, s->name); *n2; n1++, n2++)
141         if (*n1 != *n2)
142           break;
143       if (*n2 || *n1 != '-')
144         goto nontrivial;
145
146       n1++;
147       for (n2 = id2str (pool, s->evr); *n2; n1++, n2++)
148         if (*n1 != *n2)
149           break;
150       if (*n2 || *n1 != '.')
151         goto nontrivial;
152       n1++;
153       for (n2 = id2str (pool, s->arch); *n2; n1++, n2++)
154         if (*n1 != *n2)
155           break;
156       if (*n2 || strcmp (n1, ".rpm"))
157         goto nontrivial;
158
159       repodata_set_constant(pd->data, entry, SOLVABLE_MEDIANR, medianr);
160       repodata_set_void(pd->data, entry, SOLVABLE_MEDIADIR);
161       repodata_set_void(pd->data, entry, SOLVABLE_MEDIAFILE);
162       return;
163
164 nontrivial:
165       repodata_set_constant(pd->data, entry, SOLVABLE_MEDIANR, medianr);
166       repodata_set_void(pd->data, entry, SOLVABLE_MEDIADIR);
167       repodata_set_str(pd->data, entry, SOLVABLE_MEDIAFILE, sp[1]);
168       return;
169     }
170 }
171
172 /*
173  * add_source
174  * 
175  */
176
177 static void
178 add_source(struct parsedata *pd, char *line, Solvable *s, unsigned entry)
179 {
180   Repo *repo = s->repo;
181   Pool *pool = repo->pool;
182   char *sp[5];
183
184   if (split(line, sp, 5) != 4)
185     {
186       fprintf(stderr, "Bad source line: %s\n", line);
187       exit(1);
188     }
189
190   Id name = str2id(pool, sp[0], 1);
191   Id evr = makeevr(pool, join2(sp[1], "-", sp[2]));
192   Id arch = str2id(pool, sp[3], 1);
193   /* XXX: could record a dep here, depends on where we want to store the data */
194   if (name == s->name)
195     repodata_set_void(pd->data, entry, SOLVABLE_SOURCENAME);
196   else
197     repodata_set_id(pd->data, entry, SOLVABLE_SOURCENAME, name);
198   if (evr == s->evr)
199     repodata_set_void(pd->data, entry, SOLVABLE_SOURCEEVR);
200   else
201     repodata_set_id(pd->data, entry, SOLVABLE_SOURCEEVR, evr);
202   repodata_set_constantid(pd->data, entry, SOLVABLE_SOURCEARCH, arch);
203 }
204   
205 /*
206  * add_dirline
207  * add a line with directory information
208  * 
209  */
210
211 static void
212 add_dirline (struct parsedata *pd, char *line)
213 {
214   char *sp[6];
215   if (split(line, sp, 6) != 5)
216     return;
217   pd->dirs = sat_extend(pd->dirs, pd->ndirs, 1, sizeof(pd->dirs[0]), 31);
218   long filesz = strtol (sp[1], 0, 0);
219   filesz += strtol (sp[2], 0, 0);
220   long filenum = strtol (sp[3], 0, 0);
221   filenum += strtol (sp[4], 0, 0);
222   /* hack: we know that there's room for a / */
223   if (*sp[0] != '/')
224     *--sp[0] = '/';
225   unsigned dirid = repodata_str2dir(pd->data, sp[0], 1);
226 #if 0
227 fprintf(stderr, "%s -> %d\n", sp[0], dirid);
228 #endif
229   pd->dirs[pd->ndirs][0] = dirid;
230   pd->dirs[pd->ndirs][1] = filesz;
231   pd->dirs[pd->ndirs][2] = filenum;
232   pd->ndirs++;
233 }
234
235 static void
236 set_checksum(Repodata *data, int last_found_pack, Id keyname, char *line)
237 {
238   char *sp[3];
239   int l;
240   Id type;
241   if (split(line, sp, 3) != 2)
242     {
243       fprintf(stderr, "Bad source line: %s\n", line);
244       exit(1);
245     }
246   if (!strcasecmp (sp[0], "sha1"))
247     l = SIZEOF_SHA1 * 2, type = REPOKEY_TYPE_SHA1;
248   else if (!strcasecmp (sp[0], "md5"))
249     l = SIZEOF_MD5 * 2, type = REPOKEY_TYPE_MD5;
250   else
251     {
252       fprintf(stderr, "Unknown checksum type: %s\n", sp[0]);
253       exit(1);
254     }
255   if (strlen(sp[1]) != l)
256     {
257       fprintf(stderr, "Invalid checksum length for %s: %s\n", sp[0], sp[1]);
258       exit(1);
259     }
260   repodata_set_checksum(data, last_found_pack, keyname, type, sp[1]);
261 }
262
263 /*
264  * id3_cmp
265  * compare 
266  * 
267  */
268
269 static int
270 id3_cmp (const void *v1, const void *v2)
271 {
272   Id *i1 = (Id*)v1;
273   Id *i2 = (Id*)v2;
274   return i1[0] - i2[0];
275 }
276
277
278 /*
279  * commit_diskusage
280  * 
281  */
282
283 static void
284 commit_diskusage (struct parsedata *pd, unsigned entry)
285 {
286   unsigned i;
287   Dirpool *dp = &pd->data->dirpool;
288   /* Now sort in dirid order.  This ensures that parents come before
289      their children.  */
290   if (pd->ndirs > 1)
291     qsort(pd->dirs, pd->ndirs, sizeof (pd->dirs[0]), id3_cmp);
292   /* Substract leaf numbers from all parents to make the numbers
293      non-cumulative.  This must be done post-order (i.e. all leafs
294      adjusted before parents).  We ensure this by starting at the end of
295      the array moving to the start, hence seeing leafs before parents.  */
296   for (i = pd->ndirs; i--;)
297     {
298       unsigned p = dirpool_parent(dp, pd->dirs[i][0]);
299       unsigned j = i;
300       for (; p; p = dirpool_parent(dp, p))
301         {
302           for (; j--;)
303             if (pd->dirs[j][0] == p)
304               break;
305           if (j < pd->ndirs)
306             {
307               if (pd->dirs[j][1] < pd->dirs[i][1])
308                 pd->dirs[j][1] = 0;
309               else
310                 pd->dirs[j][1] -= pd->dirs[i][1];
311               if (pd->dirs[j][2] < pd->dirs[i][2])
312                 pd->dirs[j][2] = 0;
313               else
314                 pd->dirs[j][2] -= pd->dirs[i][2];
315             }
316           else
317             /* Haven't found this parent in the list, look further if
318                we maybe find the parents parent.  */
319             j = i;
320         }
321     }
322 #if 0
323   char sbuf[1024];
324   char *buf = sbuf;
325   unsigned slen = sizeof (sbuf);
326   for (i = 0; i < pd->ndirs; i++)
327     {
328       dir2str (attr, pd->dirs[i][0], &buf, &slen);
329       fprintf (stderr, "have dir %d %d %d %s\n", pd->dirs[i][0], pd->dirs[i][1], pd->dirs[i][2], buf);
330     }
331   if (buf != sbuf)
332     free (buf);
333 #endif
334   for (i = 0; i < pd->ndirs; i++)
335     if (pd->dirs[i][1] || pd->dirs[i][2])
336       {
337         repodata_add_dirnumnum(pd->data, entry, SOLVABLE_DISKUSAGE, pd->dirs[i][0], pd->dirs[i][1], pd->dirs[i][2]);
338       }
339   pd->ndirs = 0;
340 }
341
342
343 /* Unfortunately "a"[0] is no constant expression in the C languages,
344    so we need to pass the four characters individually :-/  */
345 #define CTAG(a,b,c,d) ((unsigned)(((unsigned char)a) << 24) \
346  | ((unsigned char)b << 16) \
347  | ((unsigned char)c << 8) \
348  | ((unsigned char)d))
349
350 /*
351  * tag_from_string
352  * 
353  */
354
355 static inline unsigned
356 tag_from_string (char *cs)
357 {
358   unsigned char *s = (unsigned char*) cs;
359   return ((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]);
360 }
361
362
363 /*
364  * repo_add_susetags
365  * Parse susetags file passed in fp, fill solvables into repo
366  * 
367  * susetags is key,value based
368  *  for short values
369  *    =key: value
370  *  is used
371  *  for long (multi-line) values,
372  *    +key:
373  *    value
374  *    value
375  *    -key:
376  *  is used
377  *
378  * See http://en.opensuse.org/Standards/YaST2_Repository_Metadata
379  * and http://en.opensuse.org/Standards/YaST2_Repository_Metadata/packages
380  * and http://en.opensuse.org/Standards/YaST2_Repository_Metadata/pattern
381  * 
382  * Assumptions:
383  *   All keys have 3 characters and end in ':'
384  */
385
386 void
387 finish_solvable(struct parsedata *pd, Solvable *s, int last_found_pack)
388 {
389   Pool *pool = pd->repo->pool;
390
391 #if 0
392   /* move file provides to filelist */
393   /* relies on the fact that rpm inserts self-provides at the end */
394   if (s->provides)
395     {
396       Id *p, *lastreal, did;
397       const char *str, *sp;
398       lastreal = pd->repo->idarraydata + s->provides;
399       for (p = lastreal; *p; p++)
400         if (ISRELDEP(*p))
401           lastreal = p + 1;
402       for (p = lastreal; *p; p++)
403         {
404           str = id2str(pool, *p);
405           if (*str != '/')
406             lastreal = p + 1;
407         }
408       if (*lastreal)
409         {
410           for (p = lastreal; *p; p++)
411             {
412               str = id2str(pool, *p);
413               sp = strrchr(str, '/');
414               if (sp - str >= 128)
415                 {
416                   char *sdup = strdup(str);
417                   sdup[sp - str] = 0;
418                   did = repodata_str2dir(pd->data, sdup, 1);
419                   free(sdup);
420                 }
421               else
422                 {
423                   char sdup[128];
424                   strncpy(sdup, str, sp - str);
425                   sdup[sp - str] = 0;
426                   did = repodata_str2dir(pd->data, sdup, 1);
427                 }
428               repodata_add_dirstr(pd->data, last_found_pack, SOLVABLE_FILELIST, did, sp + 1);
429               *p = 0;
430             }
431         }
432     }
433 #endif
434   /* A self provide, except for source packages.  This is harmless
435      to do twice (in case we see the same package twice).  */
436   if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
437     s->provides = repo_addid_dep(pd->repo, s->provides,
438                 rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
439   /* XXX This uses repo_addid_dep internally, so should also be
440      harmless to do twice.  */
441   s->supplements = repo_fix_legacy(pd->repo, s->provides, s->supplements);
442   if (pd->ndirs)
443     commit_diskusage (pd, last_found_pack);
444 }
445
446 void
447 repo_add_susetags(Repo *repo, FILE *fp, Id vendor, const char *language, int flags)
448 {
449   Pool *pool = repo->pool;
450   char *line, *linep;
451   int aline;
452   Solvable *s;
453   int intag = 0;
454   int cummulate = 0;
455   int indesc = 0;
456   int last_found_pack = 0;
457   char *sp[5];
458   struct parsedata pd;
459   Repodata *data = 0;
460
461   if ((flags & SUSETAGS_EXTEND) && repo->nrepodata)
462     indesc = 1;
463   if (repo->nrepodata)
464     /* use last repodata */
465     data = repo->repodata + repo->nrepodata - 1;
466   else
467     data = repo_add_repodata(repo, 0);
468
469   memset(&pd, 0, sizeof(pd));
470   line = malloc(1024);
471   aline = 1024;
472
473   pd.repo = pd.common.repo = repo;
474   pd.data = data;
475   pd.common.pool = pool;
476
477   linep = line;
478   s = 0;
479
480   /*
481    * read complete file
482    * 
483    * collect values in 'struct parsedata pd'
484    * then build .solv (and .attr) file
485    */
486   
487   for (;;)
488     {
489       unsigned tag;
490       if (linep - line + 16 > aline)              /* (re-)alloc buffer */
491         {
492           aline = linep - line;
493           line = realloc(line, aline + 512);
494           linep = line + aline;
495           aline += 512;
496         }
497       if (!fgets(linep, aline - (linep - line), fp)) /* read line */
498         break;
499       linep += strlen(linep);
500       if (linep == line || linep[-1] != '\n')
501         continue;
502       *--linep = 0;
503       
504       if (intag)
505         {
506           /* check for multi-line value tags (+Key:/-Key:) */
507           
508           int is_end = (linep[-intag - 2] == '-')
509                       && (linep[-1] == ':')
510                       && !strncmp(linep - 1 - intag, line + 1, intag)
511                       && (linep == line + 1 + intag + 1 + 1 + 1 + intag + 1 || linep[-intag - 3] == '\n');
512           if (cummulate && !is_end)
513             {
514               *linep++ = '\n';
515               continue;
516             }
517           if (cummulate && is_end)
518             {
519               linep[-intag - 2] = 0;
520               if (linep[-intag - 3] == '\n')
521                 linep[-intag - 3] = 0;
522               linep = line;
523               intag = 0;
524             }
525           if (!cummulate && is_end)
526             {
527               intag = 0;
528               linep = line;
529               continue;
530             }
531           if (!cummulate && !is_end)
532             linep = line + intag + 3;
533         }
534       else
535         linep = line;
536
537       if (!intag && line[0] == '+' && line[1] && line[1] != ':') /* start of +Key:/-Key: tag */
538         {
539           char *tagend = strchr(line, ':');
540           if (!tagend)
541             {
542               fprintf(stderr, "bad line: %s\n", line);
543               exit(1);
544             }
545           intag = tagend - (line + 1);
546           cummulate = 0;
547           switch (tag_from_string (line))       /* check if accumulation is needed */
548             {
549               case CTAG('+', 'P', 'r', 'q'):
550               case CTAG('+', 'P', 'r', 'c'):
551               case CTAG('+', 'P', 's', 'g'):
552                 if (!pd.kind || !(flags & SUSETAGS_KINDS_SEPARATELY))
553                   break;
554               case CTAG('+', 'D', 'e', 's'):
555               case CTAG('+', 'E', 'u', 'l'):
556               case CTAG('+', 'I', 'n', 's'):
557               case CTAG('+', 'D', 'e', 'l'):
558               case CTAG('+', 'A', 'u', 't'):
559                 if (line[4] == ':')
560                   cummulate = 1;
561             }
562           line[0] = '=';                       /* handle lines between +Key:/-Key: as =Key: */
563           line[intag + 2] = ' ';
564           linep = line + intag + 3;
565           continue;
566         }
567       if (*line == '#' || !*line)
568         continue;
569       if (! (line[0] && line[1] && line[2] && line[3] && line[4] == ':'))
570         continue;
571       tag = tag_from_string (line);
572
573
574       /*
575        * start of (next) package or pattern
576        *
577        * =Pkg: <name> <version> <release> <architecture>
578        * (=Pat: ...)
579        */
580       
581       if ((tag == CTAG('=', 'P', 'k', 'g')
582            || tag == CTAG('=', 'P', 'a', 't')))
583         {
584           Id name, evr, arch;
585           /* If we have an old solvable, complete it by filling in some
586              default stuff.  */
587           if (s)
588             finish_solvable(&pd, s, last_found_pack);
589
590           /*
591            * define kind
592            */
593           
594           pd.kind = 0;
595           if (line[3] == 't')
596             pd.kind = "pattern";
597
598           /*
599            * parse nevra
600            */
601           
602           if (split(line + 5, sp, 5) != 4)
603             {
604               fprintf(stderr, "Bad line: %s\n", line);
605               exit(1);
606             }
607           /* Lookup (but don't construct) the name and arch.  */
608           if (pd.kind)
609             name = str2id(pool, join2(pd.kind, ":", sp[0]), 0);
610           else
611             name = str2id(pool, sp[0], 0);
612           arch = str2id(pool, sp[3], 0);
613           evr = makeevr(pool, join2(sp[1], "-", sp[2]));
614
615           s = 0;
616
617           /* Now see if we know this solvable already.  If we found neither
618              the name nor the arch at all in this repo
619              there's no chance of finding the exact solvable either.  */
620           if (indesc >= 2 && name && arch)
621             {
622               int n, nn;
623               /* Now look for a solvable with the given name,evr,arch.
624                  Our input is structured so, that the second set of =Pkg
625                  lines comes in roughly the same order as the first set, so we 
626                  have a hint at where to start our search, namely were we found
627                  the last entry.  */
628               for (n = repo->start, nn = n + last_found_pack; n < repo->end; n++, nn++)
629                 {
630                   if (nn >= repo->end)
631                     nn = repo->start;
632                   s = pool->solvables + nn;
633                   if (s->repo == repo && s->name == name && s->evr == evr && s->arch == arch)
634                     break;
635                 }
636               if (n == repo->end)
637                 s = 0;
638               else
639                 last_found_pack = nn - repo->start;
640             }
641
642           /* And if we still don't have a solvable, create a new one.  */
643           if (!s)
644             {
645               s = pool_id2solvable(pool, repo_add_solvable(repo));
646               last_found_pack = (s - pool->solvables) - repo->start;
647               if (data)
648                 repodata_extend(data, s - pool->solvables);
649               if (name)
650                 s->name = name;
651               else if (pd.kind)
652                 s->name = str2id(pool, join2(pd.kind, ":", sp[0]), 1);
653               else
654                 s->name = str2id(pool, sp[0], 1);
655               s->evr = evr;
656               if (arch)
657                 s->arch = arch;
658               else
659                 s->arch = str2id(pool, sp[3], 1);
660               s->vendor = vendor;
661             }
662         }
663
664       /* If we have no current solvable to add to, ignore all further lines
665          for it.  Probably invalid input data in the second set of
666          solvables.  */
667       if (indesc >= 2 && !s)
668         {
669           fprintf (stderr, "Huh %s?\n", line);
670           continue;
671         }
672       switch (tag)
673         {
674           case CTAG('=', 'P', 'r', 'v'):                                        /* provides */
675             s->provides = adddep(pool, &pd, s->provides, line, 0, pd.kind);
676             continue;
677           case CTAG('=', 'R', 'e', 'q'):                                        /* requires */
678             s->requires = adddep(pool, &pd, s->requires, line, -SOLVABLE_PREREQMARKER, pd.kind);
679             continue;
680           case CTAG('=', 'P', 'r', 'q'):                                        /* pre-requires / packages required */
681             if (pd.kind)
682               {
683                 if (flags & SUSETAGS_KINDS_SEPARATELY)
684                   repodata_set_poolstr(data, last_found_pack, str2id(pool, "solvable:must", 1), line + 6);
685                 else
686                   s->requires = adddep(pool, &pd, s->requires, line, 0, 0);           /* patterns: a required package */
687               }
688             else
689               s->requires = adddep(pool, &pd, s->requires, line, SOLVABLE_PREREQMARKER, 0); /* package: pre-requires */
690             continue;
691           case CTAG('=', 'O', 'b', 's'):                                        /* obsoletes */
692             s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, pd.kind);
693             continue;
694           case CTAG('=', 'C', 'o', 'n'):                                        /* conflicts */
695             s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, pd.kind);
696             continue;
697           case CTAG('=', 'R', 'e', 'c'):                                        /* recommends */
698             s->recommends = adddep(pool, &pd, s->recommends, line, 0, pd.kind);
699             continue;
700           case CTAG('=', 'S', 'u', 'p'):                                        /* supplements */
701             s->supplements = adddep(pool, &pd, s->supplements, line, 0, pd.kind);
702             continue;
703           case CTAG('=', 'E', 'n', 'h'):                                        /* enhances */
704             s->enhances = adddep(pool, &pd, s->enhances, line, 0, pd.kind);
705             continue;
706           case CTAG('=', 'S', 'u', 'g'):                                        /* suggests */
707             s->suggests = adddep(pool, &pd, s->suggests, line, 0, pd.kind);
708             continue;
709           case CTAG('=', 'F', 'r', 'e'):                                        /* freshens */
710             s->freshens = adddep(pool, &pd, s->freshens, line, 0, pd.kind);
711             continue;
712           case CTAG('=', 'P', 'r', 'c'):                                        /* packages recommended */
713             if (flags & SUSETAGS_KINDS_SEPARATELY)
714               repodata_set_poolstr(data, last_found_pack, str2id(pool, "solvable:should", 1), line + 6);
715             else
716               s->recommends = adddep(pool, &pd, s->recommends, line, 0, 0);
717             continue;
718           case CTAG('=', 'P', 's', 'g'):                                        /* packages suggested */
719             if (flags & SUSETAGS_KINDS_SEPARATELY)
720               repodata_set_poolstr(data, last_found_pack, str2id(pool, "solvable:may", 1), line + 6);
721             else
722               s->suggests = adddep(pool, &pd, s->suggests, line, 0, 0);
723             continue;
724           case CTAG('=', 'P', 'c', 'n'):                                        /* pattern: package conflicts */
725             if (flags & SUSETAGS_KINDS_SEPARATELY)
726               fprintf (stderr, "Unsupported: pattern -> package conflicts\n");
727             else
728               s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, 0);
729             continue;
730           case CTAG('=', 'P', 'o', 'b'):                                        /* pattern: package obsoletes */
731             if (flags & SUSETAGS_KINDS_SEPARATELY)
732               fprintf (stderr, "Unsupported: pattern -> package obsoletes\n");
733             else
734               s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, 0);
735             continue;
736           case CTAG('=', 'P', 'f', 'r'):                                        /* pattern: package freshens */
737             if (flags & SUSETAGS_KINDS_SEPARATELY)
738               fprintf (stderr, "Unsupported: pattern -> package freshens\n");
739             else
740               s->freshens = adddep(pool, &pd, s->freshens, line, 0, 0);
741             continue;
742           case CTAG('=', 'P', 's', 'p'):                                        /* pattern: package supplements */
743             if (flags & SUSETAGS_KINDS_SEPARATELY)
744               fprintf (stderr, "Unsupported: pattern -> package supplements\n");
745             else
746               s->supplements = adddep(pool, &pd, s->supplements, line, 0, 0);
747             continue;
748           case CTAG('=', 'P', 'e', 'n'):                                        /* pattern: package enhances */
749             if (flags & SUSETAGS_KINDS_SEPARATELY)
750               fprintf (stderr, "Unsupported: pattern -> package enhances\n");
751             else
752               s->enhances = adddep(pool, &pd, s->enhances, line, 0, 0);
753             continue;
754           case CTAG('=', 'V', 'e', 'r'):                                        /* - version - */
755             last_found_pack = 0;
756             indesc++;
757             continue;
758
759         /* From here it's the attribute tags.  */
760           case CTAG('=', 'G', 'r', 'p'):
761             repodata_set_poolstr(data, last_found_pack, SOLVABLE_GROUP, line + 6);
762             continue;
763           case CTAG('=', 'L', 'i', 'c'):
764             repodata_set_poolstr(data, last_found_pack, SOLVABLE_LICENSE, line + 6);
765             continue;
766           case CTAG('=', 'L', 'o', 'c'):
767             add_location(&pd, line + 6, s, last_found_pack);
768             continue;
769           case CTAG('=', 'S', 'r', 'c'):
770             add_source(&pd, line + 6, s, last_found_pack);
771             continue;
772           case CTAG('=', 'S', 'i', 'z'):
773             if (split (line + 6, sp, 3) == 2)
774               {
775                 repodata_set_num(data, last_found_pack, SOLVABLE_DOWNLOADSIZE, (atoi(sp[0]) + 1023) / 1024);
776                 repodata_set_num(data, last_found_pack, SOLVABLE_INSTALLSIZE, (atoi(sp[1]) + 1023) / 1024);
777               }
778             continue;
779           case CTAG('=', 'T', 'i', 'm'):
780             {
781               unsigned int t = atoi (line + 6);
782               if (t)
783                 repodata_set_num(data, last_found_pack, SOLVABLE_BUILDTIME, t);
784             }
785             continue;
786           case CTAG('=', 'K', 'w', 'd'):
787             repodata_add_poolstr_array(data, last_found_pack, SOLVABLE_KEYWORDS, line + 6);
788             continue;
789           case CTAG('=', 'A', 'u', 't'):
790             repodata_set_str(data, last_found_pack, SOLVABLE_AUTHORS, line + 6);
791             continue;
792           case CTAG('=', 'S', 'u', 'm'):
793             repodata_set_str(data, last_found_pack, langtag(&pd, SOLVABLE_SUMMARY, language), line + 6);
794             continue;
795           case CTAG('=', 'D', 'e', 's'):
796             repodata_set_str(data, last_found_pack, langtag(&pd, SOLVABLE_DESCRIPTION, language), line + 6);
797             continue;
798           case CTAG('=', 'E', 'u', 'l'):
799             repodata_set_str(data, last_found_pack, langtag(&pd, SOLVABLE_EULA, language), line + 6);
800             continue;
801           case CTAG('=', 'I', 'n', 's'):
802             repodata_set_str(data, last_found_pack, langtag(&pd, SOLVABLE_MESSAGEINS, language), line + 6);
803             continue;
804           case CTAG('=', 'D', 'e', 'l'):
805             repodata_set_str(data, last_found_pack, langtag(&pd, SOLVABLE_MESSAGEDEL, language), line + 6);
806             continue;
807           case CTAG('=', 'V', 'i', 's'):
808             {
809               /* Accept numbers and textual bools.  */
810               unsigned k;
811               k = atoi (line + 6);
812               if (k || !strcasecmp (line + 6, "true"))
813                 repodata_set_constant(data, last_found_pack, SOLVABLE_ISVISIBLE, 1);
814             }
815             continue;
816           case CTAG('=', 'S', 'h', 'r'):
817             if (last_found_pack >= pd.nshare)
818               {
819                 if (pd.share_with)
820                   {
821                     pd.share_with = realloc (pd.share_with, (last_found_pack + 256) * sizeof (*pd.share_with));
822                     memset (pd.share_with + pd.nshare, 0, (last_found_pack + 256 - pd.nshare) * sizeof (*pd.share_with));
823                   }
824                 else
825                   pd.share_with = calloc (last_found_pack + 256, sizeof (*pd.share_with));
826                 pd.nshare = last_found_pack + 256;
827               }
828             pd.share_with[last_found_pack] = strdup (line + 6);
829             continue;
830           case CTAG('=', 'D', 'i', 'r'):
831             add_dirline (&pd, line + 6);
832             continue;
833           case CTAG('=', 'C', 'a', 't'):
834             repodata_set_poolstr(data, last_found_pack, SOLVABLE_CATEGORY, line + 6);
835             break;
836           case CTAG('=', 'O', 'r', 'd'):
837             /* Order is a string not a number, so we can retroactively insert
838                new patterns in the middle, i.e. 1 < 15 < 2.  */
839             repodata_set_str(data, last_found_pack, SOLVABLE_ORDER, line + 6);
840             break;
841           case CTAG('=', 'I', 'c', 'o'):
842             repodata_set_str(data, last_found_pack, SOLVABLE_ICON, line + 6);
843             break;
844           case CTAG('=', 'E', 'x', 't'):
845             repodata_add_poolstr_array(data, last_found_pack, SOLVABLE_EXTENDS, line + 6);
846             break;
847           case CTAG('=', 'I', 'n', 'c'):
848             repodata_add_poolstr_array(data, last_found_pack, SOLVABLE_INCLUDES, line + 6);
849             break;
850           case CTAG('=', 'C', 'k', 's'):
851             set_checksum(data, last_found_pack, SOLVABLE_CHECKSUM, line + 6);
852             break;
853
854           case CTAG('=', 'P', 'a', 't'):
855           case CTAG('=', 'P', 'k', 'g'):
856             break;
857
858           default:
859             break;
860         }
861
862     } /* for(;;) */
863
864   if (s)
865     finish_solvable(&pd, s, last_found_pack);
866     
867   /* Shared attributes
868    *  (e.g. multiple binaries built from same source)
869    */
870   if (pd.nshare)
871     {
872       int i, last_found;
873       last_found = 0;
874       for (i = 0; i < pd.nshare; i++)
875         if (pd.share_with[i])
876           {
877             if (split(pd.share_with[i], sp, 5) != 4)
878               {
879                 fprintf(stderr, "Bad =Shr line: %s\n", pd.share_with[i]);
880                 exit(1);
881               }
882
883             Id name = str2id(pool, sp[0], 1);
884             Id evr = makeevr(pool, join2(sp[1], "-", sp[2]));
885             Id arch = str2id(pool, sp[3], 1);
886             unsigned n, nn;
887             Solvable *found = 0;
888             for (n = repo->start, nn = repo->start + last_found;
889                  n < repo->end; n++, nn++)
890               {
891                 if (nn >= repo->end)
892                   nn = repo->start;
893                 found = pool->solvables + nn;
894                 if (found->repo == repo
895                     && found->name == name
896                     && found->evr == evr
897                     && found->arch == arch)
898                   {
899                     last_found = nn - repo->start;
900                     break;
901                   }
902               }
903             if (n != repo->end)
904               repodata_merge_attrs(data, i, last_found);
905           }
906       free (pd.share_with);
907     }
908
909   if (data)
910     repodata_internalize(data);
911
912   if (pd.common.tmp)
913     free(pd.common.tmp);
914   free(line);
915 }
916