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