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