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