Preparation for structured types. The repodata_set_* functions now take
[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 handle)
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, handle, SOLVABLE_MEDIANR, atoi(sp[0]));
128       repodata_set_poolstr(pd->data, handle, SOLVABLE_MEDIADIR, sp[2]);
129       repodata_set_str(pd->data, handle, 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, handle, SOLVABLE_MEDIANR, medianr);
160       repodata_set_void(pd->data, handle, SOLVABLE_MEDIADIR);
161       repodata_set_void(pd->data, handle, SOLVABLE_MEDIAFILE);
162       return;
163
164 nontrivial:
165       repodata_set_constant(pd->data, handle, SOLVABLE_MEDIANR, medianr);
166       repodata_set_void(pd->data, handle, SOLVABLE_MEDIADIR);
167       repodata_set_str(pd->data, handle, 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 handle)
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, handle, SOLVABLE_SOURCENAME);
196   else
197     repodata_set_id(pd->data, handle, SOLVABLE_SOURCENAME, name);
198   if (evr == s->evr)
199     repodata_set_void(pd->data, handle, SOLVABLE_SOURCEEVR);
200   else
201     repodata_set_id(pd->data, handle, SOLVABLE_SOURCEEVR, evr);
202   repodata_set_constantid(pd->data, handle, 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 handle, 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, handle, 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 handle)
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, handle, 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 handle)
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, handle, 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, handle);
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   Id blanr = -1;
461   Id handle = 0;
462
463   if ((flags & SUSETAGS_EXTEND) && repo->nrepodata)
464     indesc = 1;
465   if (repo->nrepodata)
466     /* use last repodata */
467     data = repo->repodata + repo->nrepodata - 1;
468   else
469     data = repo_add_repodata(repo, 0);
470
471   memset(&pd, 0, sizeof(pd));
472   line = malloc(1024);
473   aline = 1024;
474
475   pd.repo = pd.common.repo = repo;
476   pd.data = data;
477   pd.common.pool = pool;
478
479   linep = line;
480   s = 0;
481
482   /* XXX deactivate test code */
483   blanr = 0;
484   /*
485    * read complete file
486    * 
487    * collect values in 'struct parsedata pd'
488    * then build .solv (and .attr) file
489    */
490   
491   for (;;)
492     {
493       unsigned tag;
494       if (linep - line + 16 > aline)              /* (re-)alloc buffer */
495         {
496           aline = linep - line;
497           line = realloc(line, aline + 512);
498           linep = line + aline;
499           aline += 512;
500         }
501       if (!fgets(linep, aline - (linep - line), fp)) /* read line */
502         break;
503       linep += strlen(linep);
504       if (linep == line || linep[-1] != '\n')
505         continue;
506       *--linep = 0;
507       
508       if (intag)
509         {
510           /* check for multi-line value tags (+Key:/-Key:) */
511           
512           int is_end = (linep[-intag - 2] == '-')
513                       && (linep[-1] == ':')
514                       && !strncmp(linep - 1 - intag, line + 1, intag)
515                       && (linep == line + 1 + intag + 1 + 1 + 1 + intag + 1 || linep[-intag - 3] == '\n');
516           if (cummulate && !is_end)
517             {
518               *linep++ = '\n';
519               continue;
520             }
521           if (cummulate && is_end)
522             {
523               linep[-intag - 2] = 0;
524               if (linep[-intag - 3] == '\n')
525                 linep[-intag - 3] = 0;
526               linep = line;
527               intag = 0;
528             }
529           if (!cummulate && is_end)
530             {
531               intag = 0;
532               linep = line;
533               continue;
534             }
535           if (!cummulate && !is_end)
536             linep = line + intag + 3;
537         }
538       else
539         linep = line;
540
541       if (!intag && line[0] == '+' && line[1] && line[1] != ':') /* start of +Key:/-Key: tag */
542         {
543           char *tagend = strchr(line, ':');
544           if (!tagend)
545             {
546               fprintf(stderr, "bad line: %s\n", line);
547               exit(1);
548             }
549           intag = tagend - (line + 1);
550           cummulate = 0;
551           switch (tag_from_string (line))       /* check if accumulation is needed */
552             {
553               case CTAG('+', 'P', 'r', 'q'):
554               case CTAG('+', 'P', 'r', 'c'):
555               case CTAG('+', 'P', 's', 'g'):
556                 if (!pd.kind || !(flags & SUSETAGS_KINDS_SEPARATELY))
557                   break;
558               case CTAG('+', 'D', 'e', 's'):
559               case CTAG('+', 'E', 'u', 'l'):
560               case CTAG('+', 'I', 'n', 's'):
561               case CTAG('+', 'D', 'e', 'l'):
562               case CTAG('+', 'A', 'u', 't'):
563                 if (line[4] == ':')
564                   cummulate = 1;
565             }
566           line[0] = '=';                       /* handle lines between +Key:/-Key: as =Key: */
567           line[intag + 2] = ' ';
568           linep = line + intag + 3;
569           continue;
570         }
571       if (*line == '#' || !*line)
572         continue;
573       if (! (line[0] && line[1] && line[2] && line[3] && line[4] == ':'))
574         continue;
575       tag = tag_from_string (line);
576
577
578       /*
579        * start of (next) package or pattern
580        *
581        * =Pkg: <name> <version> <release> <architecture>
582        * (=Pat: ...)
583        */
584       
585       if ((tag == CTAG('=', 'P', 'k', 'g')
586            || tag == CTAG('=', 'P', 'a', 't')))
587         {
588           Id name, evr, arch;
589           /* If we have an old solvable, complete it by filling in some
590              default stuff.  */
591           if (s)
592             finish_solvable(&pd, s, handle);
593
594           /*
595            * define kind
596            */
597           
598           pd.kind = 0;
599           if (line[3] == 't')
600             pd.kind = "pattern";
601
602           /*
603            * parse nevra
604            */
605           
606           if (split(line + 5, sp, 5) != 4)
607             {
608               fprintf(stderr, "Bad line: %s\n", line);
609               exit(1);
610             }
611           /* Lookup (but don't construct) the name and arch.  */
612           if (pd.kind)
613             name = str2id(pool, join2(pd.kind, ":", sp[0]), 0);
614           else
615             name = str2id(pool, sp[0], 0);
616           arch = str2id(pool, sp[3], 0);
617           evr = makeevr(pool, join2(sp[1], "-", sp[2]));
618
619           s = 0;
620
621           /* Now see if we know this solvable already.  If we found neither
622              the name nor the arch at all in this repo
623              there's no chance of finding the exact solvable either.  */
624           if (indesc >= 2 && name && arch)
625             {
626               int n, nn;
627               /* Now look for a solvable with the given name,evr,arch.
628                  Our input is structured so, that the second set of =Pkg
629                  lines comes in roughly the same order as the first set, so we 
630                  have a hint at where to start our search, namely were we found
631                  the last entry.  */
632               for (n = repo->start, nn = n + last_found_pack; n < repo->end; n++, nn++)
633                 {
634                   if (nn >= repo->end)
635                     nn = repo->start;
636                   s = pool->solvables + nn;
637                   if (s->repo == repo && s->name == name && s->evr == evr && s->arch == arch)
638                     break;
639                 }
640               if (n == repo->end)
641                 s = 0;
642               else
643                 {
644                   last_found_pack = nn - repo->start;
645                   handle = repodata_get_handle(data, last_found_pack);
646                 }
647             }
648
649           /* And if we still don't have a solvable, create a new one.  */
650           if (!s)
651             {
652               s = pool_id2solvable(pool, repo_add_solvable(repo));
653               last_found_pack = (s - pool->solvables) - repo->start;
654               if (data)
655                 {
656                   repodata_extend(data, s - pool->solvables);
657                   handle = repodata_get_handle(data, last_found_pack);
658                 }
659               if (name)
660                 s->name = name;
661               else if (pd.kind)
662                 s->name = str2id(pool, join2(pd.kind, ":", sp[0]), 1);
663               else
664                 s->name = str2id(pool, sp[0], 1);
665               s->evr = evr;
666               if (arch)
667                 s->arch = arch;
668               else
669                 s->arch = str2id(pool, sp[3], 1);
670               s->vendor = vendor;
671             }
672         }
673
674       /* If we have no current solvable to add to, ignore all further lines
675          for it.  Probably invalid input data in the second set of
676          solvables.  */
677       if (indesc >= 2 && !s)
678         {
679           fprintf (stderr, "Huh %s?\n", line);
680           continue;
681         }
682       switch (tag)
683         {
684           case CTAG('=', 'P', 'r', 'v'):                                        /* provides */
685             s->provides = adddep(pool, &pd, s->provides, line, 0, pd.kind);
686             continue;
687           case CTAG('=', 'R', 'e', 'q'):                                        /* requires */
688             s->requires = adddep(pool, &pd, s->requires, line, -SOLVABLE_PREREQMARKER, pd.kind);
689             continue;
690           case CTAG('=', 'P', 'r', 'q'):                                        /* pre-requires / packages required */
691             if (pd.kind)
692               {
693                 if (flags & SUSETAGS_KINDS_SEPARATELY)
694                   repodata_set_poolstr(data, handle, str2id(pool, "solvable:must", 1), line + 6);
695                 else
696                   s->requires = adddep(pool, &pd, s->requires, line, 0, 0);           /* patterns: a required package */
697               }
698             else
699               s->requires = adddep(pool, &pd, s->requires, line, SOLVABLE_PREREQMARKER, 0); /* package: pre-requires */
700             continue;
701           case CTAG('=', 'O', 'b', 's'):                                        /* obsoletes */
702             s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, pd.kind);
703             continue;
704           case CTAG('=', 'C', 'o', 'n'):                                        /* conflicts */
705             s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, pd.kind);
706             continue;
707           case CTAG('=', 'R', 'e', 'c'):                                        /* recommends */
708             s->recommends = adddep(pool, &pd, s->recommends, line, 0, pd.kind);
709             continue;
710           case CTAG('=', 'S', 'u', 'p'):                                        /* supplements */
711             s->supplements = adddep(pool, &pd, s->supplements, line, 0, pd.kind);
712             continue;
713           case CTAG('=', 'E', 'n', 'h'):                                        /* enhances */
714             s->enhances = adddep(pool, &pd, s->enhances, line, 0, pd.kind);
715             continue;
716           case CTAG('=', 'S', 'u', 'g'):                                        /* suggests */
717             s->suggests = adddep(pool, &pd, s->suggests, line, 0, pd.kind);
718             continue;
719           case CTAG('=', 'F', 'r', 'e'):                                        /* freshens */
720             s->freshens = adddep(pool, &pd, s->freshens, line, 0, pd.kind);
721             continue;
722           case CTAG('=', 'P', 'r', 'c'):                                        /* packages recommended */
723             if (flags & SUSETAGS_KINDS_SEPARATELY)
724               repodata_set_poolstr(data, handle, str2id(pool, "solvable:should", 1), line + 6);
725             else
726               s->recommends = adddep(pool, &pd, s->recommends, line, 0, 0);
727             continue;
728           case CTAG('=', 'P', 's', 'g'):                                        /* packages suggested */
729             if (flags & SUSETAGS_KINDS_SEPARATELY)
730               repodata_set_poolstr(data, handle, str2id(pool, "solvable:may", 1), line + 6);
731             else
732               s->suggests = adddep(pool, &pd, s->suggests, line, 0, 0);
733             continue;
734           case CTAG('=', 'P', 'c', 'n'):                                        /* pattern: package conflicts */
735             if (flags & SUSETAGS_KINDS_SEPARATELY)
736               fprintf (stderr, "Unsupported: pattern -> package conflicts\n");
737             else
738               s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, 0);
739             continue;
740           case CTAG('=', 'P', 'o', 'b'):                                        /* pattern: package obsoletes */
741             if (flags & SUSETAGS_KINDS_SEPARATELY)
742               fprintf (stderr, "Unsupported: pattern -> package obsoletes\n");
743             else
744               s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, 0);
745             continue;
746           case CTAG('=', 'P', 'f', 'r'):                                        /* pattern: package freshens */
747             if (flags & SUSETAGS_KINDS_SEPARATELY)
748               fprintf (stderr, "Unsupported: pattern -> package freshens\n");
749             else
750               s->freshens = adddep(pool, &pd, s->freshens, line, 0, 0);
751             continue;
752           case CTAG('=', 'P', 's', 'p'):                                        /* pattern: package supplements */
753             if (flags & SUSETAGS_KINDS_SEPARATELY)
754               fprintf (stderr, "Unsupported: pattern -> package supplements\n");
755             else
756               s->supplements = adddep(pool, &pd, s->supplements, line, 0, 0);
757             continue;
758           case CTAG('=', 'P', 'e', 'n'):                                        /* pattern: package enhances */
759             if (flags & SUSETAGS_KINDS_SEPARATELY)
760               fprintf (stderr, "Unsupported: pattern -> package enhances\n");
761             else
762               s->enhances = adddep(pool, &pd, s->enhances, line, 0, 0);
763             continue;
764           case CTAG('=', 'V', 'e', 'r'):                                        /* - version - */
765             last_found_pack = 0;
766             handle = 0;
767             indesc++;
768             continue;
769
770         /* From here it's the attribute tags.  */
771           case CTAG('=', 'G', 'r', 'p'):
772             repodata_set_poolstr(data, handle, SOLVABLE_GROUP, line + 6);
773             continue;
774           case CTAG('=', 'L', 'i', 'c'):
775             repodata_set_poolstr(data, handle, SOLVABLE_LICENSE, line + 6);
776             continue;
777           case CTAG('=', 'L', 'o', 'c'):
778             add_location(&pd, line + 6, s, handle);
779             continue;
780           case CTAG('=', 'S', 'r', 'c'):
781             add_source(&pd, line + 6, s, handle);
782             continue;
783           case CTAG('=', 'S', 'i', 'z'):
784             if (split (line + 6, sp, 3) == 2)
785               {
786                 repodata_set_num(data, handle, SOLVABLE_DOWNLOADSIZE, (atoi(sp[0]) + 1023) / 1024);
787                 repodata_set_num(data, handle, SOLVABLE_INSTALLSIZE, (atoi(sp[1]) + 1023) / 1024);
788               }
789             continue;
790           case CTAG('=', 'T', 'i', 'm'):
791             {
792               unsigned int t = atoi (line + 6);
793               if (t)
794                 repodata_set_num(data, handle, SOLVABLE_BUILDTIME, t);
795             }
796             continue;
797           case CTAG('=', 'K', 'w', 'd'):
798             repodata_add_poolstr_array(data, handle, SOLVABLE_KEYWORDS, line + 6);
799             continue;
800           case CTAG('=', 'A', 'u', 't'):
801             repodata_set_str(data, handle, SOLVABLE_AUTHORS, line + 6);
802             continue;
803           case CTAG('=', 'S', 'u', 'm'):
804             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_SUMMARY, language), line + 6);
805             continue;
806           case CTAG('=', 'D', 'e', 's'):
807             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_DESCRIPTION, language), line + 6);
808             continue;
809           case CTAG('=', 'E', 'u', 'l'):
810             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_EULA, language), line + 6);
811             continue;
812           case CTAG('=', 'I', 'n', 's'):
813             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_MESSAGEINS, language), line + 6);
814             if (blanr)
815               {
816                 /* XXX testcode */
817                 repo_set_str(repo, blanr, SOLVABLE_MESSAGEINS, line + 6);
818                 blanr--;
819               }
820             continue;
821           case CTAG('=', 'D', 'e', 'l'):
822             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_MESSAGEDEL, language), line + 6);
823             continue;
824           case CTAG('=', 'V', 'i', 's'):
825             {
826               /* Accept numbers and textual bools.  */
827               unsigned k;
828               k = atoi (line + 6);
829               if (k || !strcasecmp (line + 6, "true"))
830                 repodata_set_constant(data, handle, SOLVABLE_ISVISIBLE, 1);
831             }
832             continue;
833           case CTAG('=', 'S', 'h', 'r'):
834             if (last_found_pack >= pd.nshare)
835               {
836                 if (pd.share_with)
837                   {
838                     pd.share_with = realloc (pd.share_with, (last_found_pack + 256) * sizeof (*pd.share_with));
839                     memset (pd.share_with + pd.nshare, 0, (last_found_pack + 256 - pd.nshare) * sizeof (*pd.share_with));
840                   }
841                 else
842                   pd.share_with = calloc (last_found_pack + 256, sizeof (*pd.share_with));
843                 pd.nshare = last_found_pack + 256;
844               }
845             pd.share_with[last_found_pack] = strdup (line + 6);
846             continue;
847           case CTAG('=', 'D', 'i', 'r'):
848             add_dirline (&pd, line + 6);
849             continue;
850           case CTAG('=', 'C', 'a', 't'):
851             repodata_set_poolstr(data, handle, SOLVABLE_CATEGORY, line + 6);
852             break;
853           case CTAG('=', 'O', 'r', 'd'):
854             /* Order is a string not a number, so we can retroactively insert
855                new patterns in the middle, i.e. 1 < 15 < 2.  */
856             repodata_set_str(data, handle, SOLVABLE_ORDER, line + 6);
857             break;
858           case CTAG('=', 'I', 'c', 'o'):
859             repodata_set_str(data, handle, SOLVABLE_ICON, line + 6);
860             break;
861           case CTAG('=', 'E', 'x', 't'):
862             repodata_add_poolstr_array(data, handle, SOLVABLE_EXTENDS, join2("pattern", ":", line + 6));
863             break;
864           case CTAG('=', 'I', 'n', 'c'):
865             repodata_add_poolstr_array(data, handle, SOLVABLE_INCLUDES, join2("pattern", ":", line + 6));
866             break;
867           case CTAG('=', 'C', 'k', 's'):
868             set_checksum(data, handle, SOLVABLE_CHECKSUM, line + 6);
869             break;
870           case CTAG('=', 'L', 'a', 'n'):
871             language = strdup(line + 6);
872             break;
873
874           case CTAG('=', 'P', 'a', 't'):
875           case CTAG('=', 'P', 'k', 'g'):
876             break;
877
878           default:
879             break;
880         }
881
882     } /* for(;;) */
883
884   if (s)
885     finish_solvable(&pd, s, handle);
886     
887   /* Shared attributes
888    *  (e.g. multiple binaries built from same source)
889    */
890   if (pd.nshare)
891     {
892       int i, last_found;
893       last_found = 0;
894       for (i = 0; i < pd.nshare; i++)
895         if (pd.share_with[i])
896           {
897             if (split(pd.share_with[i], sp, 5) != 4)
898               {
899                 fprintf(stderr, "Bad =Shr line: %s\n", pd.share_with[i]);
900                 exit(1);
901               }
902
903             Id name = str2id(pool, sp[0], 1);
904             Id evr = makeevr(pool, join2(sp[1], "-", sp[2]));
905             Id arch = str2id(pool, sp[3], 1);
906             unsigned n, nn;
907             Solvable *found = 0;
908             for (n = repo->start, nn = repo->start + last_found;
909                  n < repo->end; n++, nn++)
910               {
911                 if (nn >= repo->end)
912                   nn = repo->start;
913                 found = pool->solvables + nn;
914                 if (found->repo == repo
915                     && found->name == name
916                     && found->evr == evr
917                     && found->arch == arch)
918                   {
919                     last_found = nn - repo->start;
920                     break;
921                   }
922               }
923             if (n != repo->end)
924               repodata_merge_attrs(data, i, last_found);
925           }
926       free (pd.share_with);
927     }
928
929   if (data)
930     repodata_internalize(data);
931
932   if (pd.common.tmp)
933     free(pd.common.tmp);
934   free(line);
935 }