4a417d7d580a62f2084dfd3f686de543fd94aa85
[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
236 /*
237  * id3_cmp
238  * compare 
239  * 
240  */
241
242 static int
243 id3_cmp (const void *v1, const void *v2)
244 {
245   Id *i1 = (Id*)v1;
246   Id *i2 = (Id*)v2;
247   return i1[0] - i2[0];
248 }
249
250
251 /*
252  * commit_diskusage
253  * 
254  */
255
256 static void
257 commit_diskusage (struct parsedata *pd, unsigned entry)
258 {
259   unsigned i;
260   Dirpool *dp = &pd->data->dirpool;
261   /* Now sort in dirid order.  This ensures that parents come before
262      their children.  */
263   if (pd->ndirs > 1)
264     qsort(pd->dirs, pd->ndirs, sizeof (pd->dirs[0]), id3_cmp);
265   /* Substract leaf numbers from all parents to make the numbers
266      non-cumulative.  This must be done post-order (i.e. all leafs
267      adjusted before parents).  We ensure this by starting at the end of
268      the array moving to the start, hence seeing leafs before parents.  */
269   for (i = pd->ndirs; i--;)
270     {
271       unsigned p = dirpool_parent(dp, pd->dirs[i][0]);
272       unsigned j = i;
273       for (; p; p = dirpool_parent(dp, p))
274         {
275           for (; j--;)
276             if (pd->dirs[j][0] == p)
277               break;
278           if (j < pd->ndirs)
279             {
280               if (pd->dirs[j][1] < pd->dirs[i][1])
281                 pd->dirs[j][1] = 0;
282               else
283                 pd->dirs[j][1] -= pd->dirs[i][1];
284               if (pd->dirs[j][2] < pd->dirs[i][2])
285                 pd->dirs[j][2] = 0;
286               else
287                 pd->dirs[j][2] -= pd->dirs[i][2];
288             }
289           else
290             /* Haven't found this parent in the list, look further if
291                we maybe find the parents parent.  */
292             j = i;
293         }
294     }
295 #if 0
296   char sbuf[1024];
297   char *buf = sbuf;
298   unsigned slen = sizeof (sbuf);
299   for (i = 0; i < pd->ndirs; i++)
300     {
301       dir2str (attr, pd->dirs[i][0], &buf, &slen);
302       fprintf (stderr, "have dir %d %d %d %s\n", pd->dirs[i][0], pd->dirs[i][1], pd->dirs[i][2], buf);
303     }
304   if (buf != sbuf)
305     free (buf);
306 #endif
307   for (i = 0; i < pd->ndirs; i++)
308     if (pd->dirs[i][1] || pd->dirs[i][2])
309       {
310         repodata_add_dirnumnum(pd->data, entry, SOLVABLE_DISKUSAGE, pd->dirs[i][0], pd->dirs[i][1], pd->dirs[i][2]);
311       }
312   pd->ndirs = 0;
313 }
314
315
316 /* Unfortunately "a"[0] is no constant expression in the C languages,
317    so we need to pass the four characters individually :-/  */
318 #define CTAG(a,b,c,d) ((unsigned)(((unsigned char)a) << 24) \
319  | ((unsigned char)b << 16) \
320  | ((unsigned char)c << 8) \
321  | ((unsigned char)d))
322
323 /*
324  * tag_from_string
325  * 
326  */
327
328 static inline unsigned
329 tag_from_string (char *cs)
330 {
331   unsigned char *s = (unsigned char*) cs;
332   return ((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]);
333 }
334
335
336 /*
337  * repo_add_susetags
338  * Parse susetags file passed in fp, fill solvables into repo
339  * 
340  * susetags is key,value based
341  *  for short values
342  *    =key: value
343  *  is used
344  *  for long (multi-line) values,
345  *    +key:
346  *    value
347  *    value
348  *    -key:
349  *  is used
350  *
351  * See http://en.opensuse.org/Standards/YaST2_Repository_Metadata
352  * and http://en.opensuse.org/Standards/YaST2_Repository_Metadata/packages
353  * and http://en.opensuse.org/Standards/YaST2_Repository_Metadata/pattern
354  * 
355  * Assumptions:
356  *   All keys have 3 characters and end in ':'
357  */
358
359 void
360 finish_solvable(struct parsedata *pd, Solvable *s, int last_found_pack)
361 {
362   Pool *pool = pd->repo->pool;
363
364 #if 0
365   /* move file provides to filelist */
366   /* relies on the fact that rpm inserts self-provides at the end */
367   if (s->provides)
368     {
369       Id *p, *lastreal, did;
370       const char *str, *sp;
371       lastreal = pd->repo->idarraydata + s->provides;
372       for (p = lastreal; *p; p++)
373         if (ISRELDEP(*p))
374           lastreal = p + 1;
375       for (p = lastreal; *p; p++)
376         {
377           str = id2str(pool, *p);
378           if (*str != '/')
379             lastreal = p + 1;
380         }
381       if (*lastreal)
382         {
383           for (p = lastreal; *p; p++)
384             {
385               str = id2str(pool, *p);
386               sp = strrchr(str, '/');
387               if (sp - str >= 128)
388                 {
389                   char *sdup = strdup(str);
390                   sdup[sp - str] = 0;
391                   did = repodata_str2dir(pd->data, sdup, 1);
392                   free(sdup);
393                 }
394               else
395                 {
396                   char sdup[128];
397                   strncpy(sdup, str, sp - str);
398                   sdup[sp - str] = 0;
399                   did = repodata_str2dir(pd->data, sdup, 1);
400                 }
401               repodata_add_dirstr(pd->data, last_found_pack, SOLVABLE_FILELIST, did, sp + 1);
402               *p = 0;
403             }
404         }
405     }
406 #endif
407   /* A self provide, except for source packages.  This is harmless
408      to do twice (in case we see the same package twice).  */
409   if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
410     s->provides = repo_addid_dep(pd->repo, s->provides,
411                 rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
412   /* XXX This uses repo_addid_dep internally, so should also be
413      harmless to do twice.  */
414   s->supplements = repo_fix_legacy(pd->repo, s->provides, s->supplements);
415   if (pd->ndirs)
416     commit_diskusage (pd, last_found_pack);
417 }
418
419 void
420 repo_add_susetags(Repo *repo, FILE *fp, Id vendor, const char *language, int flags)
421 {
422   Pool *pool = repo->pool;
423   char *line, *linep;
424   int aline;
425   Solvable *s;
426   int intag = 0;
427   int cummulate = 0;
428   int indesc = 0;
429   int last_found_pack = 0;
430   char *sp[5];
431   struct parsedata pd;
432   Repodata *data = 0;
433
434   if ((flags & SUSETAGS_EXTEND) && repo->nrepodata)
435     indesc = 1;
436   if (repo->nrepodata)
437     /* use last repodata */
438     data = repo->repodata + repo->nrepodata - 1;
439   else
440     data = repo_add_repodata(repo, 0);
441
442   memset(&pd, 0, sizeof(pd));
443   line = malloc(1024);
444   aline = 1024;
445
446   pd.repo = pd.common.repo = repo;
447   pd.data = data;
448   pd.common.pool = pool;
449
450   linep = line;
451   s = 0;
452
453   /*
454    * read complete file
455    * 
456    * collect values in 'struct parsedata pd'
457    * then build .solv (and .attr) file
458    */
459   
460   for (;;)
461     {
462       unsigned tag;
463       if (linep - line + 16 > aline)              /* (re-)alloc buffer */
464         {
465           aline = linep - line;
466           line = realloc(line, aline + 512);
467           linep = line + aline;
468           aline += 512;
469         }
470       if (!fgets(linep, aline - (linep - line), fp)) /* read line */
471         break;
472       linep += strlen(linep);
473       if (linep == line || linep[-1] != '\n')
474         continue;
475       *--linep = 0;
476       
477       if (intag)
478         {
479           /* check for multi-line value tags (+Key:/-Key:) */
480           
481           int is_end = (linep[-intag - 2] == '-')
482                       && (linep[-1] == ':')
483                       && !strncmp(linep - 1 - intag, line + 1, intag)
484                       && (linep == line + 1 + intag + 1 + 1 + 1 + intag + 1 || linep[-intag - 3] == '\n');
485           if (cummulate && !is_end)
486             {
487               *linep++ = '\n';
488               continue;
489             }
490           if (cummulate && is_end)
491             {
492               linep[-intag - 2] = 0;
493               if (linep[-intag - 3] == '\n')
494                 linep[-intag - 3] = 0;
495               linep = line;
496               intag = 0;
497             }
498           if (!cummulate && is_end)
499             {
500               intag = 0;
501               linep = line;
502               continue;
503             }
504           if (!cummulate && !is_end)
505             linep = line + intag + 3;
506         }
507       else
508         linep = line;
509
510       if (!intag && line[0] == '+' && line[1] && line[1] != ':') /* start of +Key:/-Key: tag */
511         {
512           char *tagend = strchr(line, ':');
513           if (!tagend)
514             {
515               fprintf(stderr, "bad line: %s\n", line);
516               exit(1);
517             }
518           intag = tagend - (line + 1);
519           cummulate = 0;
520           switch (tag_from_string (line))       /* check if accumulation is needed */
521             {
522               case CTAG('+', 'P', 'r', 'q'):
523               case CTAG('+', 'P', 'r', 'c'):
524               case CTAG('+', 'P', 's', 'g'):
525                 if (!pd.kind || !(flags & SUSETAGS_KINDS_SEPARATELY))
526                   break;
527               case CTAG('+', 'D', 'e', 's'):
528               case CTAG('+', 'E', 'u', 'l'):
529               case CTAG('+', 'I', 'n', 's'):
530               case CTAG('+', 'D', 'e', 'l'):
531               case CTAG('+', 'A', 'u', 't'):
532                 if (line[4] == ':')
533                   cummulate = 1;
534             }
535           line[0] = '=';                       /* handle lines between +Key:/-Key: as =Key: */
536           line[intag + 2] = ' ';
537           linep = line + intag + 3;
538           continue;
539         }
540       if (*line == '#' || !*line)
541         continue;
542       if (! (line[0] && line[1] && line[2] && line[3] && line[4] == ':'))
543         continue;
544       tag = tag_from_string (line);
545
546
547       /*
548        * start of (next) package or pattern
549        *
550        * =Pkg: <name> <version> <release> <architecture>
551        * (=Pat: ...)
552        */
553       
554       if ((tag == CTAG('=', 'P', 'k', 'g')
555            || tag == CTAG('=', 'P', 'a', 't')))
556         {
557           Id name, evr, arch;
558           /* If we have an old solvable, complete it by filling in some
559              default stuff.  */
560           if (s)
561             finish_solvable(&pd, s, last_found_pack);
562
563           /*
564            * define kind
565            */
566           
567           pd.kind = 0;
568           if (line[3] == 't')
569             pd.kind = "pattern";
570
571           /*
572            * parse nevra
573            */
574           
575           if (split(line + 5, sp, 5) != 4)
576             {
577               fprintf(stderr, "Bad line: %s\n", line);
578               exit(1);
579             }
580           /* Lookup (but don't construct) the name and arch.  */
581           if (pd.kind)
582             name = str2id(pool, join2(pd.kind, ":", sp[0]), 0);
583           else
584             name = str2id(pool, sp[0], 0);
585           arch = str2id(pool, sp[3], 0);
586           evr = makeevr(pool, join2(sp[1], "-", sp[2]));
587
588           s = 0;
589
590           /* Now see if we know this solvable already.  If we found neither
591              the name nor the arch at all in this repo
592              there's no chance of finding the exact solvable either.  */
593           if (indesc >= 2 && name && arch)
594             {
595               int n, nn;
596               /* Now look for a solvable with the given name,evr,arch.
597                  Our input is structured so, that the second set of =Pkg
598                  lines comes in roughly the same order as the first set, so we 
599                  have a hint at where to start our search, namely were we found
600                  the last entry.  */
601               for (n = repo->start, nn = n + last_found_pack; n < repo->end; n++, nn++)
602                 {
603                   if (nn >= repo->end)
604                     nn = repo->start;
605                   s = pool->solvables + nn;
606                   if (s->repo == repo && s->name == name && s->evr == evr && s->arch == arch)
607                     break;
608                 }
609               if (n == repo->end)
610                 s = 0;
611               else
612                 last_found_pack = nn - repo->start;
613             }
614
615           /* And if we still don't have a solvable, create a new one.  */
616           if (!s)
617             {
618               s = pool_id2solvable(pool, repo_add_solvable(repo));
619               last_found_pack = (s - pool->solvables) - repo->start;
620               if (data)
621                 repodata_extend(data, s - pool->solvables);
622               if (name)
623                 s->name = name;
624               else if (pd.kind)
625                 s->name = str2id(pool, join2(pd.kind, ":", sp[0]), 1);
626               else
627                 s->name = str2id(pool, sp[0], 1);
628               s->evr = evr;
629               if (arch)
630                 s->arch = arch;
631               else
632                 s->arch = str2id(pool, sp[3], 1);
633               s->vendor = vendor;
634             }
635         }
636
637       /* If we have no current solvable to add to, ignore all further lines
638          for it.  Probably invalid input data in the second set of
639          solvables.  */
640       if (indesc >= 2 && !s)
641         {
642           fprintf (stderr, "Huh %s?\n", line);
643           continue;
644         }
645       switch (tag)
646         {
647           case CTAG('=', 'P', 'r', 'v'):                                        /* provides */
648             s->provides = adddep(pool, &pd, s->provides, line, 0, pd.kind);
649             continue;
650           case CTAG('=', 'R', 'e', 'q'):                                        /* requires */
651             s->requires = adddep(pool, &pd, s->requires, line, -SOLVABLE_PREREQMARKER, pd.kind);
652             continue;
653           case CTAG('=', 'P', 'r', 'q'):                                        /* pre-requires / packages required */
654             if (pd.kind)
655               {
656                 if (flags & SUSETAGS_KINDS_SEPARATELY)
657                   repodata_set_poolstr(data, last_found_pack, str2id(pool, "solvable:must", 1), line + 6);
658                 else
659                   s->requires = adddep(pool, &pd, s->requires, line, 0, 0);           /* patterns: a required package */
660               }
661             else
662               s->requires = adddep(pool, &pd, s->requires, line, SOLVABLE_PREREQMARKER, 0); /* package: pre-requires */
663             continue;
664           case CTAG('=', 'O', 'b', 's'):                                        /* obsoletes */
665             s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, pd.kind);
666             continue;
667           case CTAG('=', 'C', 'o', 'n'):                                        /* conflicts */
668             s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, pd.kind);
669             continue;
670           case CTAG('=', 'R', 'e', 'c'):                                        /* recommends */
671             s->recommends = adddep(pool, &pd, s->recommends, line, 0, pd.kind);
672             continue;
673           case CTAG('=', 'S', 'u', 'p'):                                        /* supplements */
674             s->supplements = adddep(pool, &pd, s->supplements, line, 0, pd.kind);
675             continue;
676           case CTAG('=', 'E', 'n', 'h'):                                        /* enhances */
677             s->enhances = adddep(pool, &pd, s->enhances, line, 0, pd.kind);
678             continue;
679           case CTAG('=', 'S', 'u', 'g'):                                        /* suggests */
680             s->suggests = adddep(pool, &pd, s->suggests, line, 0, pd.kind);
681             continue;
682           case CTAG('=', 'F', 'r', 'e'):                                        /* freshens */
683             s->freshens = adddep(pool, &pd, s->freshens, line, 0, pd.kind);
684             continue;
685           case CTAG('=', 'P', 'r', 'c'):                                        /* packages recommended */
686             if (flags & SUSETAGS_KINDS_SEPARATELY)
687               repodata_set_poolstr(data, last_found_pack, str2id(pool, "solvable:should", 1), line + 6);
688             else
689               s->recommends = adddep(pool, &pd, s->recommends, line, 0, 0);
690             continue;
691           case CTAG('=', 'P', 's', 'g'):                                        /* packages suggested */
692             if (flags & SUSETAGS_KINDS_SEPARATELY)
693               repodata_set_poolstr(data, last_found_pack, str2id(pool, "solvable:may", 1), line + 6);
694             else
695               s->suggests = adddep(pool, &pd, s->suggests, line, 0, 0);
696             continue;
697           case CTAG('=', 'P', 'c', 'n'):                                        /* pattern: package conflicts */
698             if (flags & SUSETAGS_KINDS_SEPARATELY)
699               fprintf (stderr, "Unsupported: pattern -> package conflicts\n");
700             else
701               s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, 0);
702             continue;
703           case CTAG('=', 'P', 'o', 'b'):                                        /* pattern: package obsoletes */
704             if (flags & SUSETAGS_KINDS_SEPARATELY)
705               fprintf (stderr, "Unsupported: pattern -> package obsoletes\n");
706             else
707               s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, 0);
708             continue;
709           case CTAG('=', 'P', 'f', 'r'):                                        /* pattern: package freshens */
710             if (flags & SUSETAGS_KINDS_SEPARATELY)
711               fprintf (stderr, "Unsupported: pattern -> package freshens\n");
712             else
713               s->freshens = adddep(pool, &pd, s->freshens, line, 0, 0);
714             continue;
715           case CTAG('=', 'P', 's', 'p'):                                        /* pattern: package supplements */
716             if (flags & SUSETAGS_KINDS_SEPARATELY)
717               fprintf (stderr, "Unsupported: pattern -> package supplements\n");
718             else
719               s->supplements = adddep(pool, &pd, s->supplements, line, 0, 0);
720             continue;
721           case CTAG('=', 'P', 'e', 'n'):                                        /* pattern: package enhances */
722             if (flags & SUSETAGS_KINDS_SEPARATELY)
723               fprintf (stderr, "Unsupported: pattern -> package enhances\n");
724             else
725               s->enhances = adddep(pool, &pd, s->enhances, line, 0, 0);
726             continue;
727           case CTAG('=', 'V', 'e', 'r'):                                        /* - version - */
728             last_found_pack = 0;
729             indesc++;
730             continue;
731
732         /* From here it's the attribute tags.  */
733           case CTAG('=', 'G', 'r', 'p'):
734             repodata_set_poolstr(data, last_found_pack, SOLVABLE_GROUP, line + 6);
735             continue;
736           case CTAG('=', 'L', 'i', 'c'):
737             repodata_set_poolstr(data, last_found_pack, SOLVABLE_LICENSE, line + 6);
738             continue;
739           case CTAG('=', 'L', 'o', 'c'):
740             add_location(&pd, line + 6, s, last_found_pack);
741             continue;
742           case CTAG('=', 'S', 'r', 'c'):
743             add_source(&pd, line + 6, s, last_found_pack);
744             continue;
745           case CTAG('=', 'S', 'i', 'z'):
746             if (split (line + 6, sp, 3) == 2)
747               {
748                 repodata_set_num(data, last_found_pack, SOLVABLE_DOWNLOADSIZE, (atoi(sp[0]) + 1023) / 1024);
749                 repodata_set_num(data, last_found_pack, SOLVABLE_INSTALLSIZE, (atoi(sp[1]) + 1023) / 1024);
750               }
751             continue;
752           case CTAG('=', 'T', 'i', 'm'):
753             {
754               unsigned int t = atoi (line + 6);
755               if (t)
756                 repodata_set_num(data, last_found_pack, SOLVABLE_BUILDTIME, t);
757             }
758             continue;
759           case CTAG('=', 'K', 'w', 'd'):
760             repodata_add_poolstr_array(data, last_found_pack, SOLVABLE_KEYWORDS, line + 6);
761             continue;
762           case CTAG('=', 'A', 'u', 't'):
763             repodata_set_str(data, last_found_pack, SOLVABLE_AUTHORS, line + 6);
764             continue;
765           case CTAG('=', 'S', 'u', 'm'):
766             repodata_set_str(data, last_found_pack, langtag(&pd, SOLVABLE_SUMMARY, language), line + 6);
767             continue;
768           case CTAG('=', 'D', 'e', 's'):
769             repodata_set_str(data, last_found_pack, langtag(&pd, SOLVABLE_DESCRIPTION, language), line + 6);
770             continue;
771           case CTAG('=', 'E', 'u', 'l'):
772             repodata_set_str(data, last_found_pack, langtag(&pd, SOLVABLE_EULA, language), line + 6);
773             continue;
774           case CTAG('=', 'I', 'n', 's'):
775             repodata_set_str(data, last_found_pack, langtag(&pd, SOLVABLE_MESSAGEINS, language), line + 6);
776             continue;
777           case CTAG('=', 'D', 'e', 'l'):
778             repodata_set_str(data, last_found_pack, langtag(&pd, SOLVABLE_MESSAGEDEL, language), line + 6);
779             continue;
780           case CTAG('=', 'V', 'i', 's'):
781             {
782               /* Accept numbers and textual bools.  */
783               unsigned k;
784               k = atoi (line + 6);
785               if (k || !strcasecmp (line + 6, "true"))
786                 repodata_set_constant(data, last_found_pack, SOLVABLE_ISVISIBLE, 1);
787             }
788             continue;
789           case CTAG('=', 'S', 'h', 'r'):
790             if (last_found_pack >= pd.nshare)
791               {
792                 if (pd.share_with)
793                   {
794                     pd.share_with = realloc (pd.share_with, (last_found_pack + 256) * sizeof (*pd.share_with));
795                     memset (pd.share_with + pd.nshare, 0, (last_found_pack + 256 - pd.nshare) * sizeof (*pd.share_with));
796                   }
797                 else
798                   pd.share_with = calloc (last_found_pack + 256, sizeof (*pd.share_with));
799                 pd.nshare = last_found_pack + 256;
800               }
801             pd.share_with[last_found_pack] = strdup (line + 6);
802             continue;
803           case CTAG('=', 'D', 'i', 'r'):
804             add_dirline (&pd, line + 6);
805             continue;
806           case CTAG('=', 'C', 'a', 't'):
807             repodata_set_poolstr(data, last_found_pack, SOLVABLE_CATEGORY, line + 6);
808             break;
809           case CTAG('=', 'O', 'r', 'd'):
810             /* Order is a string not a number, so we can retroactively insert
811                new patterns in the middle, i.e. 1 < 15 < 2.  */
812             repodata_set_str(data, last_found_pack, SOLVABLE_ORDER, line + 6);
813             break;
814           case CTAG('=', 'I', 'c', 'o'):
815             repodata_set_str(data, last_found_pack, SOLVABLE_ICON, line + 6);
816             break;
817           case CTAG('=', 'E', 'x', 't'):
818             repodata_add_poolstr_array(data, last_found_pack, SOLVABLE_EXTENDS, line + 6);
819             break;
820           case CTAG('=', 'I', 'n', 'c'):
821             repodata_add_poolstr_array(data, last_found_pack, SOLVABLE_INCLUDES, line + 6);
822             break;
823
824           case CTAG('=', 'P', 'a', 't'):
825           case CTAG('=', 'P', 'k', 'g'):
826             break;
827
828           default:
829             break;
830         }
831
832     } /* for(;;) */
833
834   if (s)
835     finish_solvable(&pd, s, last_found_pack);
836     
837   /* Shared attributes
838    *  (e.g. multiple binaries built from same source)
839    */
840   if (pd.nshare)
841     {
842       int i, last_found;
843       last_found = 0;
844       for (i = 0; i < pd.nshare; i++)
845         if (pd.share_with[i])
846           {
847             if (split(pd.share_with[i], sp, 5) != 4)
848               {
849                 fprintf(stderr, "Bad =Shr line: %s\n", pd.share_with[i]);
850                 exit(1);
851               }
852
853             Id name = str2id(pool, sp[0], 1);
854             Id evr = makeevr(pool, join2(sp[1], "-", sp[2]));
855             Id arch = str2id(pool, sp[3], 1);
856             unsigned n, nn;
857             Solvable *found = 0;
858             for (n = repo->start, nn = repo->start + last_found;
859                  n < repo->end; n++, nn++)
860               {
861                 if (nn >= repo->end)
862                   nn = repo->start;
863                 found = pool->solvables + nn;
864                 if (found->repo == repo
865                     && found->name == name
866                     && found->evr == evr
867                     && found->arch == arch)
868                   {
869                     last_found = nn - repo->start;
870                     break;
871                   }
872               }
873             if (n != repo->end)
874               repodata_merge_attrs(data, i, last_found);
875           }
876       free (pd.share_with);
877     }
878
879   if (data)
880     repodata_internalize(data);
881
882   if (pd.common.tmp)
883     free(pd.common.tmp);
884   free(line);
885 }
886