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