- also disable update rules for "keep installed" jobs.
[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           pool_debug(pool, SAT_FATAL, "susetags: 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               pool_debug(pool, SAT_FATAL, "susetags: unknown relation in %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, Id 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       pool_debug(pool, SAT_FATAL, "susetags: 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, Id 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       pool_debug(pool, SAT_FATAL, "susetags: 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, Id 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       pool_debug(pd->repo->pool, SAT_FATAL, "susetags: bad checksum 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       pool_debug(pd->repo->pool, SAT_FATAL, "susetags: unknown checksum type: %d: %s\n", pd->lineno, sp[0]);
257       exit(1);
258     }
259   if (strlen(sp[1]) != l)
260     {
261       pool_debug(pd->repo->pool, SAT_FATAL, "susetags: bad 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, Id 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, Id 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 handle = 0;
493   Id vendor = 0;
494
495   if ((flags & SUSETAGS_EXTEND) && repo->nrepodata)
496     indesc = 1;
497
498   if (!(flags & REPO_REUSE_REPODATA))
499     data = repo_add_repodata(repo, 0);
500   else
501     data = repo_last_repodata(repo);
502
503   if (product)
504     {
505       if (!strncmp(id2str(pool, pool->solvables[product].name), "product:", 8))
506         vendor = pool->solvables[product].vendor;
507     }
508
509   memset(&pd, 0, sizeof(pd));
510   line = malloc(1024);
511   aline = 1024;
512
513   pd.repo = pd.common.repo = repo;
514   pd.data = data;
515   pd.common.pool = pool;
516
517   linep = line;
518   s = 0;
519   freshens = 0;
520
521   /*
522    * read complete file
523    *
524    * collect values in 'struct parsedata pd'
525    * then build .solv (and .attr) file
526    */
527
528   for (;;)
529     {
530       unsigned tag;
531       char *olinep; /* old line pointer */
532       char line_lang[6];
533       int keylen = 3;
534       if (linep - line + 16 > aline)              /* (re-)alloc buffer */
535         {
536           aline = linep - line;
537           line = realloc(line, aline + 512);
538           linep = line + aline;
539           aline += 512;
540         }
541       if (!fgets(linep, aline - (linep - line), fp)) /* read line */
542         break;
543       olinep = linep;
544       linep += strlen(linep);
545       if (linep == line || linep[-1] != '\n')
546         continue;
547       pd.lineno++;
548       *--linep = 0;
549       if (linep == olinep)
550         continue;
551
552       if (intag)
553         {
554           /* check for multi-line value tags (+Key:/-Key:) */
555
556           int is_end = (linep[-intag - keylen + 1] == '-')
557                       && (linep[-1] == ':')
558                       && (linep == line + 1 + intag + 1 + 1 + 1 + intag + 1 || linep[-intag - keylen] == '\n');
559           if (is_end
560               && strncmp(linep - 1 - intag, line + 1, intag))
561             {
562               pool_debug(pool, SAT_ERROR, "susetags: Nonmatching multi-line tags: %d: '%s' '%s' %d\n", pd.lineno, linep - 1 - intag, line + 1, intag);
563             }
564           if (cummulate && !is_end)
565             {
566               *linep++ = '\n';
567               continue;
568             }
569           if (cummulate && is_end)
570             {
571               linep[-intag - keylen + 1] = 0;
572               if (linep[-intag - keylen] == '\n')
573                 linep[-intag - keylen] = 0;
574               linep = line;
575               intag = 0;
576             }
577           if (!cummulate && is_end)
578             {
579               intag = 0;
580               linep = line;
581               continue;
582             }
583           if (!cummulate && !is_end)
584             linep = line + intag + keylen;
585         }
586       else
587         linep = line;
588
589       if (!intag && line[0] == '+' && line[1] && line[1] != ':') /* start of +Key:/-Key: tag */
590         {
591           char *tagend = strchr(line, ':');
592           if (!tagend)
593             {
594               pool_debug(pool, SAT_FATAL, "susetags: bad line: %d: %s\n", pd.lineno, line);
595               exit(1);
596             }
597           intag = tagend - (line + 1);
598           cummulate = 0;
599           switch (tag_from_string (line))       /* check if accumulation is needed */
600             {
601               case CTAG('+', 'P', 'r', 'q'):
602               case CTAG('+', 'P', 'r', 'c'):
603               case CTAG('+', 'P', 's', 'g'):
604                 if (!pd.kind || !(flags & SUSETAGS_KINDS_SEPARATELY))
605                   break;
606               case CTAG('+', 'D', 'e', 's'):
607               case CTAG('+', 'E', 'u', 'l'):
608               case CTAG('+', 'I', 'n', 's'):
609               case CTAG('+', 'D', 'e', 'l'):
610               case CTAG('+', 'A', 'u', 't'):
611                 if (line[4] == ':')
612                   cummulate = 1;
613             }
614           line[0] = '=';                       /* handle lines between +Key:/-Key: as =Key: */
615           line[intag + keylen - 1] = ' ';
616           linep = line + intag + keylen;
617           continue;
618         }
619       if (*line == '#' || !*line)
620         continue;
621       if (! (line[0] && line[1] && line[2] && line[3] && (line[4] == ':' || line[4] == '.')))
622         continue;
623       if ( line[4] == '.')
624         {
625           char *endlang;
626           endlang = strchr(line + 5, ':');
627           if (endlang)
628             {
629               keylen = endlang - line - 1;
630               strncpy(line_lang, line + 5, 5);
631               line_lang[endlang - line - 5] = 0;
632             }
633         }
634       else
635         line_lang[0] = 0;
636       tag = tag_from_string (line);
637
638
639       /*
640        * start of (next) package or pattern
641        *
642        * =Pkg: <name> <version> <release> <architecture>
643        * (=Pat: ...)
644        */
645
646       if ((tag == CTAG('=', 'P', 'k', 'g')
647            || tag == CTAG('=', 'P', 'a', 't')))
648         {
649           Id name, evr, arch;
650           /* If we have an old solvable, complete it by filling in some
651              default stuff.  */
652           if (s)
653             finish_solvable(&pd, s, handle, freshens);
654
655           /*
656            * define kind
657            */
658
659           pd.kind = 0;
660           if (line[3] == 't')
661             pd.kind = "pattern";
662
663           /*
664            * parse nevra
665            */
666
667           if (split(line + 5, sp, 5) != 4)
668             {
669               pool_debug(pool, SAT_FATAL, "susetags: bad line: %d: %s\n", pd.lineno, line);
670               exit(1);
671             }
672           /* Lookup (but don't construct) the name and arch.  */
673           if (pd.kind)
674             name = str2id(pool, join2(pd.kind, ":", sp[0]), 0);
675           else
676             name = str2id(pool, sp[0], 0);
677           arch = str2id(pool, sp[3], 0);
678           evr = makeevr(pool, join2(sp[1], "-", sp[2]));
679
680           s = 0;
681           freshens = 0;
682
683           /* Now see if we know this solvable already.  If we found neither
684              the name nor the arch at all in this repo
685              there's no chance of finding the exact solvable either.  */
686           if (name && arch && (indesc >= 2))
687             {
688               int n, nn;
689               /* Now look for a solvable with the given name,evr,arch.
690                Our input is structured so, that the second set of =Pkg
691                lines comes in roughly the same order as the first set, so we
692                have a hint at where to start our search, namely were we found
693                the last entry.  */
694               for (n = repo->start, nn = n + last_found_pack; n < repo->end; n++, nn++)
695                 {
696                   if (nn >= repo->end)
697                     nn = repo->start;
698                   s = pool->solvables + nn;
699                   if (s->repo == repo && s->name == name && s->evr == evr && s->arch == arch)
700                     break;
701                 }
702               if (n == repo->end)
703                 s = 0;
704               else
705                 {
706                   last_found_pack = nn - repo->start;
707                   handle = nn;
708                 }
709             }
710
711
712           /* And if we still don't have a solvable, create a new one.  */
713           if (!s)
714             {
715               s = pool_id2solvable(pool, repo_add_solvable(repo));
716               last_found_pack = (s - pool->solvables) - repo->start;
717               if (data)
718                 handle = s - pool->solvables;
719               if (name)
720                 s->name = name;
721               else if (pd.kind)
722                 s->name = str2id(pool, join2(pd.kind, ":", sp[0]), 1);
723               else
724                 s->name = str2id(pool, sp[0], 1);
725               s->evr = evr;
726               if (arch)
727                 s->arch = arch;
728               else
729                 s->arch = str2id(pool, sp[3], 1);
730               s->vendor = vendor; /* default to product vendor */
731             }
732         }
733
734       /* If we have no current solvable to add to, ignore all further lines
735          for it.  Probably invalid input data in the second set of
736          solvables.  */
737       if (indesc >= 2 && !s)
738         {
739           fprintf (stderr, "Huh %d: %s?\n", pd.lineno, line);
740           continue;
741         }
742       switch (tag)
743         {
744           case CTAG('=', 'P', 'r', 'v'):                                        /* provides */
745             s->provides = adddep(pool, &pd, s->provides, line, 0, pd.kind);
746             continue;
747           case CTAG('=', 'R', 'e', 'q'):                                        /* requires */
748             s->requires = adddep(pool, &pd, s->requires, line, -SOLVABLE_PREREQMARKER, pd.kind);
749             continue;
750           case CTAG('=', 'P', 'r', 'q'):                                        /* pre-requires / packages required */
751             if (pd.kind)
752               {
753                 if (flags & SUSETAGS_KINDS_SEPARATELY)
754                   repodata_set_poolstr(data, handle, str2id(pool, "solvable:must", 1), line + 6);
755                 else
756                   s->requires = adddep(pool, &pd, s->requires, line, 0, 0);           /* patterns: a required package */
757               }
758             else
759               s->requires = adddep(pool, &pd, s->requires, line, SOLVABLE_PREREQMARKER, 0); /* package: pre-requires */
760             continue;
761           case CTAG('=', 'O', 'b', 's'):                                        /* obsoletes */
762             s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, pd.kind);
763             continue;
764           case CTAG('=', 'C', 'o', 'n'):                                        /* conflicts */
765             s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, pd.kind);
766             continue;
767           case CTAG('=', 'R', 'e', 'c'):                                        /* recommends */
768             s->recommends = adddep(pool, &pd, s->recommends, line, 0, pd.kind);
769             continue;
770           case CTAG('=', 'S', 'u', 'p'):                                        /* supplements */
771             s->supplements = adddep(pool, &pd, s->supplements, line, 0, pd.kind);
772             continue;
773           case CTAG('=', 'E', 'n', 'h'):                                        /* enhances */
774             s->enhances = adddep(pool, &pd, s->enhances, line, 0, pd.kind);
775             continue;
776           case CTAG('=', 'S', 'u', 'g'):                                        /* suggests */
777             s->suggests = adddep(pool, &pd, s->suggests, line, 0, pd.kind);
778             continue;
779           case CTAG('=', 'F', 'r', 'e'):                                        /* freshens */
780             freshens = adddep(pool, &pd, freshens, line, 0, pd.kind);
781             continue;
782           case CTAG('=', 'P', 'r', 'c'):                                        /* packages recommended */
783             if (flags & SUSETAGS_KINDS_SEPARATELY)
784               repodata_set_poolstr(data, handle, str2id(pool, "solvable:should", 1), line + 6);
785             else
786               s->recommends = adddep(pool, &pd, s->recommends, line, 0, 0);
787             continue;
788           case CTAG('=', 'P', 's', 'g'):                                        /* packages suggested */
789             if (flags & SUSETAGS_KINDS_SEPARATELY)
790               repodata_set_poolstr(data, handle, str2id(pool, "solvable:may", 1), line + 6);
791             else
792               s->suggests = adddep(pool, &pd, s->suggests, line, 0, 0);
793             continue;
794           case CTAG('=', 'P', 'c', 'n'):                                        /* pattern: package conflicts */
795             if (flags & SUSETAGS_KINDS_SEPARATELY)
796               fprintf (stderr, "Unsupported: pattern -> package conflicts\n");
797             else
798               s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, 0);
799             continue;
800           case CTAG('=', 'P', 'o', 'b'):                                        /* pattern: package obsoletes */
801             if (flags & SUSETAGS_KINDS_SEPARATELY)
802               fprintf (stderr, "Unsupported: pattern -> package obsoletes\n");
803             else
804               s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, 0);
805             continue;
806           case CTAG('=', 'P', 'f', 'r'):                                        /* pattern: package freshens */
807             if (flags & SUSETAGS_KINDS_SEPARATELY)
808               fprintf (stderr, "Unsupported: pattern -> package freshens\n");
809             else
810               freshens = adddep(pool, &pd, freshens, line, 0, 0);
811             continue;
812           case CTAG('=', 'P', 's', 'p'):                                        /* pattern: package supplements */
813             if (flags & SUSETAGS_KINDS_SEPARATELY)
814               fprintf (stderr, "Unsupported: pattern -> package supplements\n");
815             else
816               s->supplements = adddep(pool, &pd, s->supplements, line, 0, 0);
817             continue;
818           case CTAG('=', 'P', 'e', 'n'):                                        /* pattern: package enhances */
819             if (flags & SUSETAGS_KINDS_SEPARATELY)
820               fprintf (stderr, "Unsupported: pattern -> package enhances\n");
821             else
822               s->enhances = adddep(pool, &pd, s->enhances, line, 0, 0);
823             continue;
824           case CTAG('=', 'V', 'e', 'r'):                                        /* - version - */
825             last_found_pack = 0;
826             handle = 0;
827             indesc++;
828             continue;
829           case CTAG('=', 'V', 'n', 'd'):                                        /* vendor */
830             s->vendor = str2id(pool, line + 6, 1);
831             continue;
832
833         /* From here it's the attribute tags.  */
834           case CTAG('=', 'G', 'r', 'p'):
835             repodata_set_poolstr(data, handle, SOLVABLE_GROUP, line + 6);
836             continue;
837           case CTAG('=', 'L', 'i', 'c'):
838             repodata_set_poolstr(data, handle, SOLVABLE_LICENSE, line + 6);
839             continue;
840           case CTAG('=', 'L', 'o', 'c'):
841             add_location(&pd, line + 6, s, handle);
842             continue;
843           case CTAG('=', 'S', 'r', 'c'):
844             add_source(&pd, line + 6, s, handle);
845             continue;
846           case CTAG('=', 'S', 'i', 'z'):
847             if (split (line + 6, sp, 3) == 2)
848               {
849                 repodata_set_num(data, handle, SOLVABLE_DOWNLOADSIZE, (unsigned int)(atoi(sp[0]) + 1023) / 1024);
850                 repodata_set_num(data, handle, SOLVABLE_INSTALLSIZE, (unsigned int)(atoi(sp[1]) + 1023) / 1024);
851               }
852             continue;
853           case CTAG('=', 'T', 'i', 'm'):
854             {
855               unsigned int t = atoi (line + 6);
856               if (t)
857                 repodata_set_num(data, handle, SOLVABLE_BUILDTIME, t);
858             }
859             continue;
860           case CTAG('=', 'K', 'w', 'd'):
861             repodata_add_poolstr_array(data, handle, SOLVABLE_KEYWORDS, line + 6);
862             continue;
863           case CTAG('=', 'A', 'u', 't'):
864             repodata_set_str(data, handle, SOLVABLE_AUTHORS, line + 6);
865             continue;
866           case CTAG('=', 'S', 'u', 'm'):
867             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_SUMMARY, language ? language : line_lang), line + 3 + keylen );
868             continue;
869           case CTAG('=', 'D', 'e', 's'):
870             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_DESCRIPTION, language ? language : line_lang), line + 3 + keylen );
871             continue;
872           case CTAG('=', 'E', 'u', 'l'):
873             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_EULA, language), line + 6);
874             continue;
875           case CTAG('=', 'I', 'n', 's'):
876             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_MESSAGEINS, language), line + 6);
877             continue;
878           case CTAG('=', 'D', 'e', 'l'):
879             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_MESSAGEDEL, language), line + 6);
880             continue;
881           case CTAG('=', 'V', 'i', 's'):
882             {
883               /* Accept numbers and textual bools.  */
884               unsigned k;
885               k = atoi (line + 6);
886               if (k || !strcasecmp (line + 6, "true"))
887                 repodata_set_void(data, handle, SOLVABLE_ISVISIBLE );
888             }
889             continue;
890           case CTAG('=', 'S', 'h', 'r'):
891             if (last_found_pack >= pd.nshare)
892               {
893                 pd.share_with = sat_realloc2(pd.share_with, last_found_pack + 256, sizeof (*pd.share_with));
894                 memset(pd.share_with + pd.nshare, 0, (last_found_pack + 256 - pd.nshare) * sizeof (*pd.share_with));
895                 pd.nshare = last_found_pack + 256;
896               }
897             pd.share_with[last_found_pack] = strdup(line + 6);
898             continue;
899           case CTAG('=', 'D', 'i', 'r'):
900             add_dirline (&pd, line + 6);
901             continue;
902           case CTAG('=', 'C', 'a', 't'):
903             repodata_set_poolstr(data, handle, langtag(&pd, SOLVABLE_CATEGORY, line_lang), line + 3 + keylen);
904             break;
905           case CTAG('=', 'O', 'r', 'd'):
906             /* Order is a string not a number, so we can retroactively insert
907                new patterns in the middle, i.e. 1 < 15 < 2.  */
908             repodata_set_str(data, handle, SOLVABLE_ORDER, line + 6);
909             break;
910           case CTAG('=', 'I', 'c', 'o'):
911             repodata_set_str(data, handle, SOLVABLE_ICON, line + 6);
912             break;
913           case CTAG('=', 'E', 'x', 't'):
914             repodata_add_poolstr_array(data, handle, SOLVABLE_EXTENDS, join2("pattern", ":", line + 6));
915             break;
916           case CTAG('=', 'I', 'n', 'c'):
917             repodata_add_poolstr_array(data, handle, SOLVABLE_INCLUDES, join2("pattern", ":", line + 6));
918             break;
919           case CTAG('=', 'C', 'k', 's'):
920             set_checksum(&pd, data, handle, SOLVABLE_CHECKSUM, line + 6);
921 #if 0
922             if (0)
923               {
924                 Id sub = repodata_create_struct(data, handle, str2id(pool, "solvable:komisch", 1));
925                 repodata_set_poolstr(data, sub, str2id(pool, "sub:key1", 1), line + 6);
926                 repodata_set_num(data, sub, str2id(pool, "sub:key2", 1), last_found_pack);
927                 sub = repodata_create_struct(data, handle, str2id(pool, "solvable:komisch", 1));
928                 repodata_set_poolstr(data, sub, str2id(pool, "sub:key1", 1), line + 7);
929                 repodata_set_num(data, sub, str2id(pool, "sub:key2", 1), last_found_pack+1);
930               }
931 #endif
932             break;
933           case CTAG('=', 'L', 'a', 'n'):
934             language = strdup(line + 6);
935             break;
936
937           case CTAG('=', 'F', 'l', 's'):
938             {
939               char *p = strrchr(line + 6, '/');
940               Id did;
941               if (p && p != line + 6 && !p[1])
942                 {
943                   *p = 0;
944                   p = strrchr(line + 6, '/');
945                 }
946               if (p)
947                 {
948                   *p++ = 0;
949                   did = repodata_str2dir(data, line + 6, 1);
950                 }
951               else
952                 {
953                   p = line + 6;
954                   did = 0;
955                 }
956               if (!did)
957                 did = repodata_str2dir(data, "/", 1);
958               repodata_add_dirstr(data, handle, SOLVABLE_FILELIST, did, p);
959               continue;
960             }
961
962           case CTAG('=', 'P', 'a', 't'):
963           case CTAG('=', 'P', 'k', 'g'):
964             break;
965
966           default:
967             pool_debug(pool, SAT_ERROR, "susetags: unknown line: %d: %s\n", pd.lineno, line);
968             break;
969         }
970
971     } /* for(;;) */
972
973   if (s)
974     finish_solvable(&pd, s, handle, freshens);
975
976   /* Shared attributes
977    *  (e.g. multiple binaries built from same source)
978    */
979   if (pd.nshare)
980     {
981       int i, last_found;
982       last_found = 0;
983       for (i = 0; i < pd.nshare; i++)
984         if (pd.share_with[i])
985           {
986             if (split(pd.share_with[i], sp, 5) != 4)
987               {
988                 pool_debug(pool, SAT_FATAL, "susetags: bad =Shr line: %s\n", pd.share_with[i]);
989                 exit(1);
990               }
991
992             Id name = str2id(pool, sp[0], 1);
993             Id evr = makeevr(pool, join2(sp[1], "-", sp[2]));
994             Id arch = str2id(pool, sp[3], 1);
995             unsigned n, nn;
996             Solvable *found = 0;
997             for (n = repo->start, nn = repo->start + last_found;
998                  n < repo->end; n++, nn++)
999               {
1000                 if (nn >= repo->end)
1001                   nn = repo->start;
1002                 found = pool->solvables + nn;
1003                 if (found->repo == repo
1004                     && found->name == name
1005                     && found->evr == evr
1006                     && found->arch == arch)
1007                   {
1008                     last_found = nn - repo->start;
1009                     break;
1010                   }
1011               }
1012             if (n != repo->end)
1013               repodata_merge_attrs(data, repo->start + i, repo->start + last_found);
1014             free(pd.share_with[i]);
1015           }
1016       free(pd.share_with);
1017     }
1018
1019   if (!(flags & REPO_NO_INTERNALIZE))
1020     repodata_internalize(data);
1021
1022   if (pd.common.tmp)
1023     free(pd.common.tmp);
1024   free(line);
1025   join_freemem();
1026 }