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