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