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