Imported Upstream version 0.6.27
[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 <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12
13 #include "pool.h"
14 #include "repo.h"
15 #include "hash.h"
16 #include "chksum.h"
17 #include "tools_util.h"
18 #include "repo_susetags.h"
19 #ifdef ENABLE_COMPLEX_DEPS
20 #include "pool_parserpmrichdep.h"
21 #endif
22 #include "repodata_diskusage.h"
23
24 struct datashare {
25   Id name;
26   Id evr;
27   Id arch;
28 };
29
30 struct parsedata {
31   int ret;
32   Pool *pool;
33   Repo *repo;
34   Repodata *data;
35   char *kind;
36   int flags;
37   int last_found_source;
38   struct datashare *share_with;
39   int nshare;
40   Queue diskusageq;
41   struct joindata jd;
42   char *language;                       /* the default language */
43   Id langcache[ID_NUM_INTERNAL];        /* cache for the default language */
44   int lineno;
45   char *filelist;
46   int afilelist;                        /* allocated */
47   int nfilelist;                        /* used */
48 };
49
50 static char *flagtab[] = {
51   ">",
52   "=",
53   ">=",
54   "<",
55   "!=",
56   "<="
57 };
58
59
60 static Id
61 langtag(struct parsedata *pd, Id tag, const char *language)
62 {
63   if (language && *language)
64     return pool_id2langid(pd->repo->pool, tag, language, 1);
65   if (!pd->language)
66     return tag;
67   if (tag >= ID_NUM_INTERNAL)
68     return pool_id2langid(pd->repo->pool, tag, pd->language, 1);
69   if (!pd->langcache[tag])
70     pd->langcache[tag] = pool_id2langid(pd->repo->pool, tag, pd->language, 1);
71   return pd->langcache[tag];
72 }
73
74 /*
75  * adddep
76  * create and add dependency
77  */
78
79 static unsigned int
80 adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, char *line, Id marker, char *kind)
81 {
82   int i, flags;
83   Id id, evrid;
84   char *sp[4];
85
86   if (line[6] == '/')
87     {
88       /* A file dependency. Do not try to parse it */
89       id = pool_str2id(pool, line + 6, 1);
90     }
91 #ifdef ENABLE_COMPLEX_DEPS
92   else if (line[6] == '(')
93     {
94       id = pool_parserpmrichdep(pool, line + 6);
95       if (!id)
96         {
97           pd->ret = pool_error(pool, -1, "susetags: line %d: bad dependency: '%s'\n", pd->lineno, line);
98           return olddeps;
99         }
100     }
101 #endif
102   else
103     {
104       i = split(line + 6, sp, 4); /* name, <op>, evr, ? */
105       if (i != 1 && i != 3) /* expect either 'name' or 'name' <op> 'evr' */
106         {
107           pd->ret = pool_error(pool, -1, "susetags: line %d: bad dependency: '%s'\n", pd->lineno, line);
108           return olddeps;
109         }
110       if (kind)
111         id = pool_str2id(pool, join2(&pd->jd, kind, ":", sp[0]), 1);
112       else
113         id = pool_str2id(pool, sp[0], 1);
114       if (i == 3)
115         {
116           evrid = makeevr(pool, sp[2]);
117           for (flags = 0; flags < 6; flags++)
118             if (!strcmp(sp[1], flagtab[flags]))
119               break;
120           if (flags == 6)
121             {
122               if (!strcmp(sp[1], "<>"))
123                 flags = 4;
124               else
125                 {
126                   pd->ret = pool_error(pool, -1, "susetags: line %d: unknown relation: '%s'\n", pd->lineno, sp[1]);
127                   return olddeps;
128                 }
129             }
130           id = pool_rel2id(pool, id, evrid, flags + 1, 1);
131         }
132     }
133   return repo_addid_dep(pd->repo, olddeps, id, marker);
134 }
135
136
137 /*
138  * add_source
139  *
140  */
141
142 static void
143 add_source(struct parsedata *pd, char *line, Solvable *s, Id handle)
144 {
145   Pool *pool = s->repo->pool;
146   char *sp[5];
147   Id name;
148   Id arch;
149   const char *evr, *sevr;
150
151   if (split(line, sp, 5) != 4)
152     {
153       pd->ret = pool_error(pool, -1, "susetags: line %d: bad source line '%s'\n", pd->lineno, line);
154       return;
155     }
156
157   name = pool_str2id(pool, sp[0], 1);
158   arch = pool_str2id(pool, sp[3], 1);   /* do this before id2str */
159   evr = join2(&pd->jd, sp[1], "-", sp[2]);
160   sevr = pool_id2str(pool, s->evr);
161   if (sevr)
162     {
163       /* strip epoch */
164       const char *p;
165       for (p = sevr; *p >= '0' && *p <= '9'; p++)
166         ;
167       if (p != sevr && *p == ':' && p[1])
168         sevr = p;
169     }
170   if (name == s->name)
171     repodata_set_void(pd->data, handle, SOLVABLE_SOURCENAME);
172   else
173     repodata_set_id(pd->data, handle, SOLVABLE_SOURCENAME, name);
174   if (sevr && !strcmp(sevr, evr))
175     repodata_set_void(pd->data, handle, SOLVABLE_SOURCEEVR);
176   else
177     repodata_set_id(pd->data, handle, SOLVABLE_SOURCEEVR, pool_str2id(pool, evr, 1));
178   repodata_set_constantid(pd->data, handle, SOLVABLE_SOURCEARCH, arch);
179 }
180
181 static void
182 set_checksum(struct parsedata *pd, Repodata *data, Id handle, Id keyname, char *line)
183 {
184   char *sp[3];
185   Id type;
186   if (split(line, sp, 3) != 2)
187     {
188       pd->ret = pool_error(pd->pool, -1, "susetags: line %d: bad checksum line '%s'\n", pd->lineno, line);
189       return;
190     }
191   type = solv_chksum_str2type(sp[0]);
192   if (!type)
193     {
194       pd->ret = pool_error(pd->pool, -1, "susetags: line %d: unknown checksum type: '%s'\n", pd->lineno, sp[0]);
195       return;
196     }
197   if (strlen(sp[1]) != 2 * solv_chksum_len(type))
198     {
199       pd->ret = pool_error(pd->pool, -1, "susetags: line %d: bad checksum length for type %s: '%s'\n", pd->lineno, sp[0], sp[1]);
200       return;
201     }
202   repodata_set_checksum(data, handle, keyname, type, sp[1]);
203 }
204
205
206 /* Unfortunately "a"[0] is no constant expression in the C languages,
207    so we need to pass the four characters individually :-/  */
208 #define CTAG(a,b,c,d) ((unsigned)(((unsigned char)a) << 24) \
209  | ((unsigned char)b << 16) \
210  | ((unsigned char)c << 8) \
211  | ((unsigned char)d))
212
213 /*
214  * tag_from_string
215  *
216  */
217
218 static inline unsigned int
219 tag_from_string(char *cs)
220 {
221   unsigned char *s = (unsigned char *)cs;
222   return ((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]);
223 }
224
225
226 /*
227  * repo_add_susetags
228  * Parse susetags file passed in fp, fill solvables into repo
229  *
230  * susetags is key,value based
231  *  for short values
232  *    =key: value
233  *  is used
234  *  for long (multi-line) values,
235  *    +key:
236  *    value
237  *    value
238  *    -key:
239  *  is used
240  *
241  * See http://en.opensuse.org/Standards/YaST2_Repository_Metadata
242  * and http://en.opensuse.org/Standards/YaST2_Repository_Metadata/packages
243  * and http://en.opensuse.org/Standards/YaST2_Repository_Metadata/pattern
244  *
245  * Assumptions:
246  *   All keys have 3 characters and end in ':'
247  */
248
249 static void
250 finish_solvable(struct parsedata *pd, Solvable *s, Offset freshens)
251 {
252   Pool *pool = pd->repo->pool;
253   Id handle = s - pool->solvables;
254
255   if (pd->nfilelist)
256     {
257       int l;
258       Id did;
259       for (l = 0; l < pd->nfilelist; l += strlen(pd->filelist + l) + 1)
260         {
261           char *p = strrchr(pd->filelist + l, '/');
262           if (!p)
263             continue;
264           *p++ = 0;
265           did = repodata_str2dir(pd->data, pd->filelist + l, 1);
266           p[-1] = '/';
267           if (!did)
268             did = repodata_str2dir(pd->data, "/", 1);
269           repodata_add_dirstr(pd->data, handle, SOLVABLE_FILELIST, did, p);
270         }
271       pd->nfilelist = 0;
272     }
273   /* Add self provide, except for source packages.  This is harmless
274      to do twice (in case we see the same package twice).  */
275   if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
276     s->provides = repo_addid_dep(pd->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
277   repo_rewrite_suse_deps(s, freshens);
278   if (pd->diskusageq.count)
279     repodata_add_diskusage(pd->data, handle, &pd->diskusageq);
280 }
281
282 static Hashtable
283 joinhash_init(Repo *repo, Hashval *hmp)
284 {
285   Hashval hm = mkmask(repo->nsolvables);
286   Hashtable ht = solv_calloc(hm + 1, sizeof(*ht));
287   Hashval h, hh;
288   Solvable *s;
289   int i;
290
291   FOR_REPO_SOLVABLES(repo, i, s)
292     {
293       hh = HASHCHAIN_START;
294       h = s->name & hm;
295       while (ht[h])
296         h = HASHCHAIN_NEXT(h, hh, hm);
297       ht[h] = i;
298     }
299   *hmp = hm;
300   return ht;
301 }
302
303 static Solvable *
304 joinhash_lookup(Repo *repo, Hashtable ht, Hashval hm, Id name, Id evr, Id arch, Id start)
305 {
306   Hashval h, hh;
307
308   if (!name || !arch || !evr)
309     return 0;
310   hh = HASHCHAIN_START;
311   h = name & hm;
312   while (ht[h])
313     {
314       Solvable *s = repo->pool->solvables + ht[h];
315       if (ht[h] >= start && s->name == name && s->evr == evr && s->arch == arch)
316         return s;
317       h = HASHCHAIN_NEXT(h, hh, hm);
318     }
319   return 0;
320 }
321
322 static Id
323 lookup_shared_id(Repodata *data, Id p, Id keyname, Id voidid, int uninternalized)
324 {
325   Id r;
326   r = repodata_lookup_type(data, p, keyname);
327   if (r)
328     {
329       if (r == REPOKEY_TYPE_VOID)
330         return voidid;
331       r = repodata_lookup_id(data, p, keyname);
332       if (r)
333         return r;
334     }
335   if (uninternalized)
336     return repodata_lookup_id_uninternalized(data, p, keyname, voidid);
337   return 0;
338 }
339
340 static inline Id
341 toevr(Pool *pool, struct parsedata *pd, const char *version, const char *release)
342 {
343   return makeevr(pool, !release || (release[0] == '-' && !release[1]) ?
344         version : join2(&pd->jd, version, "-", release));
345 }
346
347
348 /*
349  * parse susetags
350  *
351  * fp: file to read from
352  * defvendor: default vendor (0 if none)
353  * language: current language (0 if none)
354  * flags: flags
355  */
356
357 int
358 repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int flags)
359 {
360   Pool *pool = repo->pool;
361   char *line, *linep;
362   int aline;
363   Solvable *s;
364   Offset freshens;
365   int intag = 0;
366   int intag_linestart = 0;
367   int cummulate = 0;
368   int indesc = 0;
369   int indelta = 0;
370   int last_found_pack = 0;
371   Id first_new_pkg = 0;
372   char *sp[6];
373   struct parsedata pd;
374   Repodata *data = 0;
375   Id handle = 0;
376   Hashtable joinhash = 0;
377   Hashval joinhashm = 0;
378   int createdpkgs = 0;
379
380   if ((flags & (SUSETAGS_EXTEND|REPO_EXTEND_SOLVABLES)) != 0 && repo->nrepodata)
381     {
382       joinhash = joinhash_init(repo, &joinhashm);
383       indesc = 1;
384     }
385
386   data = repo_add_repodata(repo, flags);
387
388   memset(&pd, 0, sizeof(pd));
389   line = solv_malloc(1024);
390   aline = 1024;
391
392   pd.pool = pool;
393   pd.repo = repo;
394   pd.data = data;
395   pd.flags = flags;
396   pd.language = language && *language ? solv_strdup(language) : 0;
397   queue_init(&pd.diskusageq);
398
399   linep = line;
400   s = 0;
401   freshens = 0;
402
403   /* if this is a join setup the recorded share data */
404   if (joinhash)
405     {
406       Repodata *sdata;
407       int i;
408
409       FOR_REPODATAS(repo, i, sdata)
410         {
411           int p;
412           if (!repodata_has_keyname(sdata, SUSETAGS_SHARE_NAME))
413             continue;
414           for (p = sdata->start; p < sdata->end; p++)
415             {
416               Id name, evr, arch;
417               name = lookup_shared_id(sdata, p, SUSETAGS_SHARE_NAME, pool->solvables[p].name, sdata == data);
418               if (!name)
419                 continue;
420               evr = lookup_shared_id(sdata, p, SUSETAGS_SHARE_EVR, pool->solvables[p].evr, sdata == data);
421               if (!evr)
422                 continue;
423               arch = lookup_shared_id(sdata, p, SUSETAGS_SHARE_ARCH, pool->solvables[p].arch, sdata == data);
424               if (!arch)
425                 continue;
426               if (p - repo->start >= pd.nshare)
427                 {
428                   pd.share_with = solv_realloc2(pd.share_with, p - repo->start + 256, sizeof(*pd.share_with));
429                   memset(pd.share_with + pd.nshare, 0, (p - repo->start + 256 - pd.nshare) * sizeof(*pd.share_with));
430                   pd.nshare = p - repo->start + 256;
431                 }
432               pd.share_with[p - repo->start].name = name;
433               pd.share_with[p - repo->start].evr = evr;
434               pd.share_with[p - repo->start].arch = arch;
435             }
436         }
437     }
438
439   /*
440    * read complete file
441    *
442    * collect values in 'struct parsedata pd'
443    * then build .solv (and .attr) file
444    */
445
446   for (;;)
447     {
448       unsigned int tag;
449       char line_lang[6];
450       int keylen;
451
452       if (pd.ret)
453         break;
454       if (linep - line + 16 > aline)              /* (re-)alloc buffer */
455         {
456           aline = linep - line;
457           line = solv_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       linep += strlen(linep);
464       if (linep == line || linep[-1] != '\n')
465         continue;
466       pd.lineno++;
467       *--linep = 0;
468
469       if (intag)
470         {
471           /* in multi-line value tags (+Key:/-Key:), check for end, cummulate */
472           int is_end = line[intag_linestart] == '-' && linep[-1] == ':' && linep - line == intag_linestart + intag + 2;
473           if (is_end && strncmp(linep - 1 - intag, line + 1, intag))
474             {
475               pool_debug(pool, SOLV_ERROR, "susetags: Nonmatching multi-line tags: %d: '%s' '%.*s'\n", pd.lineno, linep - 1 - intag, intag, line + 1);
476             }
477           if (!is_end)
478             {
479               if (cummulate)
480                 {
481                   *linep++ = '\n';
482                   intag_linestart = linep - line;
483                   continue;
484                 }
485               intag_linestart = intag + 3;
486               linep = line + intag_linestart;
487               if (!*linep)
488                 continue;               /* ignore empty lines, bnc#381828 */
489             }
490           else
491             {
492               intag = 0;
493               linep = line;
494               if (!cummulate)
495                 continue;
496               line[intag_linestart] = 0;
497               if (line[intag_linestart - 1] == '\n')
498                 line[intag_linestart - 1] = 0;          /* strip trailing newline */
499             }
500         }
501       else
502         linep = line;
503
504       /* ignore comments and empty lines */
505       if (!*line || *line == '#')
506         continue;
507
508       /* ignore malformed lines */
509       if (!(line[0] && line[1] && line[2] && line[3] && (line[4] == ':' || line[4] == '.')))
510         continue;
511
512       if (!intag && line[0] == '+' && line[1] != ':') /* start of +Key:/-Key: tag */
513         {
514           char *tagend = strchr(line, ':');
515           if (!tagend || tagend - line > 100)
516             {
517               pd.ret = pool_error(pool, -1, "susetags: line %d: bad line '%s'\n", pd.lineno, line);
518               break;
519             }
520           intag = tagend - (line + 1);          /* set to tagsize */
521           cummulate = 0;
522           switch (tag_from_string(line))        /* check if accumulation is needed */
523             {
524               case CTAG('+', 'D', 'e', 's'):
525               case CTAG('+', 'E', 'u', 'l'):
526               case CTAG('+', 'I', 'n', 's'):
527               case CTAG('+', 'D', 'e', 'l'):
528               case CTAG('+', 'A', 'u', 't'):
529                 cummulate = 1;
530                 break;
531               default:
532                 break;
533             }
534           line[0] = '=';                        /* handle lines between +Key:/-Key: as =Key: */
535           line[intag + 2] = ' ';
536           intag_linestart = intag + 3;
537           linep = line + intag_linestart;
538           continue;
539         }
540
541       /* support language suffix */
542       keylen = 3;
543       line_lang[0] = 0;
544       if (line[4] == '.')
545         {
546           char *endlang;
547           endlang = strchr(line + 5, ':');
548           if (endlang)
549             {
550               int langsize = endlang - (line + 5);
551               keylen = endlang - line - 1;
552               if (langsize > 5)
553                 langsize = 5;
554               strncpy(line_lang, line + 5, langsize);
555               line_lang[langsize] = 0;
556             }
557         }
558
559       tag = tag_from_string(line);
560
561       if (indelta)
562         {
563           /* Example:
564             =Dlt: subversion 1.6.16 1.3.1 i586
565             =Dsq: subversion 1.6.15 4.2 i586 d57b3fc86e7a2f73796e8e35b96fa86212c910
566             =Cks: SHA1 14a8410cf741856a5d70d89dab62984dba6a1ca7
567             =Loc: 1 subversion-1.6.15_1.6.16-4.2_1.3.1.i586.delta.rpm
568             =Siz: 81558
569            */
570           switch (tag)
571             {
572             case CTAG('=', 'D', 's', 'q'):
573               {
574                 Id evr;
575                 if (split(line + 5, sp, 5) != 5)
576                   continue;
577                 repodata_set_id(data, handle, DELTA_SEQ_NAME, pool_str2id(pool, sp[0], 1));
578                 evr = toevr(pool, &pd, sp[1], sp[2]);
579                 repodata_set_id(data, handle, DELTA_SEQ_EVR, evr);
580                 /* repodata_set_id(data, handle, DELTA_SEQ_ARCH, pool_str2id(pool, sp[3], 1)); */
581                 repodata_set_str(data, handle, DELTA_SEQ_NUM, sp[4]);
582                 repodata_set_id(data, handle, DELTA_BASE_EVR, evr);
583                 continue;
584               }
585             case CTAG('=', 'C', 'k', 's'):
586               set_checksum(&pd, data, handle, DELTA_CHECKSUM, line + 6);
587               continue;
588             case CTAG('=', 'L', 'o', 'c'):
589               {
590                 int i = split(line + 6, sp, 3);
591                 if (i != 2 && i != 3)
592                   {
593                     pd.ret = pool_error(pool, -1, "susetags: line %d: bad location line '%s'\n", pd.lineno, line);
594                     continue;
595                   }
596                 repodata_set_deltalocation(data, handle, atoi(sp[0]), i == 3 ? sp[2] : 0, sp[1]);
597                 continue;
598               }
599             case CTAG('=', 'S', 'i', 'z'):
600               if (split(line + 6, sp, 3) == 2)
601                 repodata_set_num(data, handle, DELTA_DOWNLOADSIZE, strtoull(sp[0], 0, 10));
602               continue;
603             case CTAG('=', 'P', 'k', 'g'):
604             case CTAG('=', 'P', 'a', 't'):
605             case CTAG('=', 'D', 'l', 't'):
606               handle = 0;
607               indelta = 0;
608               break;
609             default:
610               pool_debug(pool, SOLV_ERROR, "susetags: unknown line: %d: %s\n", pd.lineno, line);
611               continue;
612             }
613         }
614
615       /*
616        * start of (next) package or pattern or delta
617        *
618        * =Pkg: <name> <version> <release> <architecture>
619        * (=Pat: ...)
620        */
621       if (tag == CTAG('=', 'D', 'l', 't'))
622         {
623           if (s)
624             finish_solvable(&pd, s, freshens);
625           s = 0;
626           pd.kind = 0;
627           if (split(line + 5, sp, 5) != 4)
628             {
629               pd.ret = pool_error(pool, -1, "susetags: line %d: bad line '%s'\n", pd.lineno, line);
630               break;
631             }
632           handle = repodata_new_handle(data);
633           repodata_set_id(data, handle, DELTA_PACKAGE_NAME, pool_str2id(pool, sp[0], 1));
634           repodata_set_id(data, handle, DELTA_PACKAGE_EVR, toevr(pool, &pd, sp[1], sp[2]));
635           repodata_set_id(data, handle, DELTA_PACKAGE_ARCH, pool_str2id(pool, sp[3], 1));
636           repodata_add_flexarray(data, SOLVID_META, REPOSITORY_DELTAINFO, handle);
637           indelta = 1;
638           continue;
639         }
640       if (tag == CTAG('=', 'P', 'k', 'g')
641            || tag == CTAG('=', 'P', 'a', 't'))
642         {
643           /* If we have an old solvable, complete it by filling in some
644              default stuff.  */
645           if (s)
646             finish_solvable(&pd, s, freshens);
647
648           /*
649            * define kind
650            */
651
652           pd.kind = 0;
653           if (line[3] == 't')
654             pd.kind = "pattern";
655
656           /*
657            * parse nevra
658            */
659
660           if (split(line + 5, sp, 5) != 4)
661             {
662               pd.ret = pool_error(pool, -1, "susetags: line %d: bad line '%s'\n", pd.lineno, line);
663               break;
664             }
665           s = 0;
666           freshens = 0;
667
668           if (joinhash)
669             {
670               /* data join operation. find solvable matching name/arch/evr and
671                * add data to it */
672               Id name, evr, arch;
673               /* we don't use the create flag here as a simple pre-check for existance */
674               if (pd.kind)
675                 name = pool_str2id(pool, join2(&pd.jd, pd.kind, ":", sp[0]), 0);
676               else
677                 name = pool_str2id(pool, sp[0], 0);
678               evr = toevr(pool, &pd, sp[1], sp[2]);
679               arch = pool_str2id(pool, sp[3], 0);
680               if (name && arch)
681                 {
682                   Id start = (flags & REPO_EXTEND_SOLVABLES) ? 0 : first_new_pkg;
683                   if (repo->start + last_found_pack + 1 >= start && repo->start + last_found_pack + 1 < repo->end)
684                     {
685                       s = pool->solvables + repo->start + last_found_pack + 1;
686                       if (s->repo != repo || s->name != name || s->evr != evr || s->arch != arch)
687                         s = 0;
688                     }
689                   if (!s)
690                     s = joinhash_lookup(repo, joinhash, joinhashm, name, evr, arch, start);
691                 }
692               /* do not create new packages in EXTEND_SOLVABLES mode */
693               if (!s && (flags & REPO_EXTEND_SOLVABLES) != 0)
694                 continue;
695               /* fallthrough to package creation */
696             }
697           if (!s)
698             {
699               /* normal operation. create a new solvable. */
700               s = pool_id2solvable(pool, repo_add_solvable(repo));
701               if (pd.kind)
702                 s->name = pool_str2id(pool, join2(&pd.jd, pd.kind, ":", sp[0]), 1);
703               else
704                 s->name = pool_str2id(pool, sp[0], 1);
705               s->evr = toevr(pool, &pd, sp[1], sp[2]);
706               s->arch = pool_str2id(pool, sp[3], 1);
707               s->vendor = defvendor;
708               if (!first_new_pkg)
709                 first_new_pkg = s - pool->solvables;
710               createdpkgs = 1;
711             }
712           last_found_pack = (s - pool->solvables) - repo->start;
713           if (data)
714             handle = s - pool->solvables;
715         }
716
717       /* If we have no current solvable to add to, ignore all further lines
718          for it.  Probably invalid input data in the second set of
719          solvables.  */
720       if (indesc >= 2 && !s)
721         {
722 #if 0
723           pool_debug(pool, SOLV_ERROR, "susetags: huh %d: %s?\n", pd.lineno, line);
724 #endif
725           continue;
726         }
727       switch (tag)
728         {
729           case CTAG('=', 'P', 'r', 'v'):                                        /* provides */
730             if (line[6] == '/')
731               {
732                 /* probably a filelist entry. stash it away for now */
733                 int l = strlen(line + 6) + 1;
734                 if (pd.nfilelist + l > pd.afilelist)
735                   {
736                     pd.afilelist = pd.nfilelist + l + 512;
737                     pd.filelist = solv_realloc(pd.filelist, pd.afilelist);
738                   }
739                 memcpy(pd.filelist + pd.nfilelist, line + 6, l);
740                 pd.nfilelist += l;
741                 break;
742               }
743             if (pd.nfilelist)
744               {
745                 int l;
746                 for (l = 0; l < pd.nfilelist; l += strlen(pd.filelist + l) + 1)
747                   s->provides = repo_addid_dep(pd.repo, s->provides, pool_str2id(pool, pd.filelist + l, 1), 0);
748                 pd.nfilelist = 0;
749               }
750             s->provides = adddep(pool, &pd, s->provides, line, 0, pd.kind);
751             continue;
752           case CTAG('=', 'R', 'e', 'q'):                                        /* requires */
753             s->requires = adddep(pool, &pd, s->requires, line, -SOLVABLE_PREREQMARKER, pd.kind);
754             continue;
755           case CTAG('=', 'P', 'r', 'q'):                                        /* pre-requires / packages required */
756             if (pd.kind)
757               {
758                 s->requires = adddep(pool, &pd, s->requires, line, 0, 0);           /* patterns: a required package */
759               }
760             else
761               s->requires = adddep(pool, &pd, s->requires, line, SOLVABLE_PREREQMARKER, 0); /* package: pre-requires */
762             continue;
763           case CTAG('=', 'O', 'b', 's'):                                        /* obsoletes */
764             s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, pd.kind);
765             continue;
766           case CTAG('=', 'C', 'o', 'n'):                                        /* conflicts */
767             s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, pd.kind);
768             continue;
769           case CTAG('=', 'R', 'e', 'c'):                                        /* recommends */
770             s->recommends = adddep(pool, &pd, s->recommends, line, 0, pd.kind);
771             continue;
772           case CTAG('=', 'S', 'u', 'p'):                                        /* supplements */
773             s->supplements = adddep(pool, &pd, s->supplements, line, 0, pd.kind);
774             continue;
775           case CTAG('=', 'E', 'n', 'h'):                                        /* enhances */
776             s->enhances = adddep(pool, &pd, s->enhances, line, 0, pd.kind);
777             continue;
778           case CTAG('=', 'S', 'u', 'g'):                                        /* suggests */
779             s->suggests = adddep(pool, &pd, s->suggests, line, 0, pd.kind);
780             continue;
781           case CTAG('=', 'F', 'r', 'e'):                                        /* freshens */
782             freshens = adddep(pool, &pd, freshens, line, 0, pd.kind);
783             continue;
784           case CTAG('=', 'P', 'r', 'c'):                                        /* packages recommended */
785             s->recommends = adddep(pool, &pd, s->recommends, line, 0, 0);
786             continue;
787           case CTAG('=', 'P', 's', 'g'):                                        /* packages suggested */
788             s->suggests = adddep(pool, &pd, s->suggests, line, 0, 0);
789             continue;
790           case CTAG('=', 'P', 'c', 'n'):                                        /* pattern: package conflicts */
791             s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, 0);
792             continue;
793           case CTAG('=', 'P', 'o', 'b'):                                        /* pattern: package obsoletes */
794             s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, 0);
795             continue;
796           case CTAG('=', 'P', 'f', 'r'):                                        /* pattern: package freshens */
797             freshens = adddep(pool, &pd, freshens, line, 0, 0);
798             continue;
799           case CTAG('=', 'P', 's', 'p'):                                        /* pattern: package supplements */
800             s->supplements = adddep(pool, &pd, s->supplements, line, 0, 0);
801             continue;
802           case CTAG('=', 'P', 'e', 'n'):                                        /* pattern: package enhances */
803             s->enhances = adddep(pool, &pd, s->enhances, line, 0, 0);
804             continue;
805           case CTAG('=', 'V', 'e', 'r'):                                        /* - version - */
806             last_found_pack = 0;
807             handle = 0;
808             indesc++;
809             if (createdpkgs)
810               {
811                 solv_free(joinhash);
812                 joinhash = joinhash_init(repo, &joinhashm);
813                 createdpkgs = 0;
814               }
815             continue;
816           case CTAG('=', 'V', 'n', 'd'):                                        /* vendor */
817             s->vendor = pool_str2id(pool, line + 6, 1);
818             continue;
819
820         /* From here it's the attribute tags.  */
821           case CTAG('=', 'G', 'r', 'p'):
822             repodata_set_poolstr(data, handle, SOLVABLE_GROUP, line + 6);
823             continue;
824           case CTAG('=', 'L', 'i', 'c'):
825             repodata_set_poolstr(data, handle, SOLVABLE_LICENSE, line + 6);
826             continue;
827           case CTAG('=', 'L', 'o', 'c'):
828             {
829               int i = split(line + 6, sp, 3);
830               if (i != 2 && i != 3)
831                 {
832                   pd.ret = pool_error(pool, -1, "susetags: line %d: bad location line '%s'\n", pd.lineno, line);
833                   continue;
834                 }
835               repodata_set_location(data, handle, atoi(sp[0]), i == 3 ? sp[2] : pool_id2str(pool, s->arch), sp[1]);
836             }
837             continue;
838           case CTAG('=', 'S', 'r', 'c'):
839             add_source(&pd, line + 6, s, handle);
840             continue;
841           case CTAG('=', 'S', 'i', 'z'):
842             if (split(line + 6, sp, 3) == 2)
843               {
844                 repodata_set_num(data, handle, SOLVABLE_DOWNLOADSIZE, strtoull(sp[0], 0, 10));
845                 repodata_set_num(data, handle, SOLVABLE_INSTALLSIZE, strtoull(sp[1], 0, 10));
846               }
847             continue;
848           case CTAG('=', 'T', 'i', 'm'):
849             {
850               unsigned int t = atoi(line + 6);
851               if (t)
852                 repodata_set_num(data, handle, SOLVABLE_BUILDTIME, t);
853             }
854             continue;
855           case CTAG('=', 'K', 'w', 'd'):
856             repodata_add_poolstr_array(data, handle, SOLVABLE_KEYWORDS, line + 6);
857             continue;
858           case CTAG('=', 'A', 'u', 't'):
859             repodata_set_str(data, handle, SOLVABLE_AUTHORS, line + 6);
860             continue;
861           case CTAG('=', 'S', 'u', 'm'):
862             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_SUMMARY, line_lang), line + 3 + keylen);
863             continue;
864           case CTAG('=', 'D', 'e', 's'):
865             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_DESCRIPTION, line_lang), line + 3 + keylen);
866             continue;
867           case CTAG('=', 'E', 'u', 'l'):
868             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_EULA, line_lang), line + 6);
869             continue;
870           case CTAG('=', 'I', 'n', 's'):
871             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_MESSAGEINS, line_lang), line + 6);
872             continue;
873           case CTAG('=', 'D', 'e', 'l'):
874             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_MESSAGEDEL, line_lang), line + 6);
875             continue;
876           case CTAG('=', 'V', 'i', 's'):
877             {
878               /* Accept numbers and textual bools.  */
879               int k;
880               k = atoi(line + 6);
881               if (k || !strcasecmp(line + 6, "true"))
882                 repodata_set_void(data, handle, SOLVABLE_ISVISIBLE);
883             }
884             continue;
885           case CTAG('=', 'S', 'h', 'r'):
886             {
887               Id name, evr, arch;
888               if (split(line + 6, sp, 5) != 4)
889                 {
890                   pd.ret = pool_error(pool, -1, "susetags: line %d: bad =Shr line '%s'\n", pd.lineno, line);
891                   continue;
892                 }
893               name = pool_str2id(pool, sp[0], 1);
894               evr = toevr(pool, &pd, sp[1], sp[2]);
895               arch = pool_str2id(pool, sp[3], 1);
896               if (last_found_pack >= pd.nshare)
897                 {
898                   pd.share_with = solv_realloc2(pd.share_with, last_found_pack + 256, sizeof(*pd.share_with));
899                   memset(pd.share_with + pd.nshare, 0, (last_found_pack + 256 - pd.nshare) * sizeof(*pd.share_with));
900                   pd.nshare = last_found_pack + 256;
901                 }
902               pd.share_with[last_found_pack].name = name;
903               pd.share_with[last_found_pack].evr = evr;
904               pd.share_with[last_found_pack].arch = arch;
905               if ((flags & SUSETAGS_RECORD_SHARES) != 0)
906                 {
907                   if (s->name == name)
908                     repodata_set_void(data, handle, SUSETAGS_SHARE_NAME);
909                   else
910                     repodata_set_id(data, handle, SUSETAGS_SHARE_NAME, name);
911                   if (s->evr == evr)
912                     repodata_set_void(data, handle, SUSETAGS_SHARE_EVR);
913                   else
914                     repodata_set_id(data, handle, SUSETAGS_SHARE_EVR, evr);
915                   if (s->arch == arch)
916                     repodata_set_void(data, handle, SUSETAGS_SHARE_ARCH);
917                   else
918                     repodata_set_id(data, handle, SUSETAGS_SHARE_ARCH, arch);
919                 }
920               continue;
921             }
922           case CTAG('=', 'D', 'i', 'r'):
923             if (split(line + 6, sp, 6) == 5)
924               {
925                 long filesz, filenum;
926                 Id did;
927
928                 filesz = strtol(sp[1], 0, 0);
929                 filesz += strtol(sp[2], 0, 0);
930                 filenum = strtol(sp[3], 0, 0);
931                 filenum += strtol(sp[4], 0, 0);
932                 if (*sp[0] != '/')
933                   *--sp[0] = '/';       /* hack: we know that there's room for a / */
934                 did = repodata_str2dir(data, sp[0], 1);
935                 queue_push(&pd.diskusageq, did);
936                 queue_push2(&pd.diskusageq, (Id)filesz, (Id)filenum);
937               }
938             break;
939           case CTAG('=', 'C', 'a', 't'):
940             repodata_set_poolstr(data, handle, langtag(&pd, SOLVABLE_CATEGORY, line_lang), line + 3 + keylen);
941             break;
942           case CTAG('=', 'O', 'r', 'd'):
943             /* Order is a string not a number, so we can retroactively insert
944                new patterns in the middle, i.e. 1 < 15 < 2.  */
945             repodata_set_str(data, handle, SOLVABLE_ORDER, line + 6);
946             break;
947           case CTAG('=', 'I', 'c', 'o'):
948             repodata_set_str(data, handle, SOLVABLE_ICON, line + 6);
949             break;
950           case CTAG('=', 'E', 'x', 't'):
951             repodata_add_poolstr_array(data, handle, SOLVABLE_EXTENDS, join2(&pd.jd, "pattern", ":", line + 6));
952             break;
953           case CTAG('=', 'I', 'n', 'c'):
954             repodata_add_poolstr_array(data, handle, SOLVABLE_INCLUDES, join2(&pd.jd, "pattern", ":", line + 6));
955             break;
956           case CTAG('=', 'C', 'k', 's'):
957             set_checksum(&pd, data, handle, SOLVABLE_CHECKSUM, line + 6);
958             break;
959           case CTAG('=', 'L', 'a', 'n'):
960             pd.language = solv_free(pd.language);
961             memset(pd.langcache, 0, sizeof(pd.langcache));
962             if (line[6])
963               pd.language = solv_strdup(line + 6);
964             break;
965
966           case CTAG('=', 'F', 'l', 's'):
967             {
968               char *p, *file = line + 6;
969               Id did;
970
971               if (*file != '/')
972                 *--file = '/';          /* hack: we know there is room */
973               p  = strrchr(file, '/');
974               /* strip trailing slashes */
975               while (p != file && !p[1])
976                 {
977                   *p = 0;
978                   p = strrchr(file, '/');
979                 }
980               *p++ = 0;
981               did = repodata_str2dir(data, *file ? file : "/", 1);
982               repodata_add_dirstr(data, handle, SOLVABLE_FILELIST, did, p);
983               line[5] = ' ';
984               break;
985             }
986           case CTAG('=', 'H', 'd', 'r'):
987             /* rpm header range */
988             if (split(line + 6, sp, 3) == 2)
989               {
990                 /* we ignore the start value */
991                 unsigned int end = (unsigned int)atoi(sp[1]);
992                 if (end)
993                   repodata_set_num(data, handle, SOLVABLE_HEADEREND, end);
994               }
995             break;
996
997           case CTAG('=', 'P', 'a', 't'):
998           case CTAG('=', 'P', 'k', 'g'):
999             break;
1000
1001           default:
1002 #if 0
1003             pool_debug(pool, SOLV_WARN, "susetags: unknown line: %d: %s\n", pd.lineno, line);
1004 #endif
1005             break;
1006         }
1007
1008     }
1009
1010   if (s)
1011     finish_solvable(&pd, s, freshens);
1012   solv_free(pd.filelist);
1013
1014   /* Shared attributes
1015    *  (e.g. multiple binaries built from same source)
1016    */
1017   if (pd.nshare)
1018     {
1019       int i, last_found;
1020       Map keyidmap;
1021
1022       map_init(&keyidmap, data->nkeys);
1023       for (i = 1; i < data->nkeys; i++)
1024         {
1025           Id keyname = data->keys[i].name;
1026           if (keyname == SOLVABLE_INSTALLSIZE || keyname == SOLVABLE_DISKUSAGE || keyname == SOLVABLE_FILELIST)
1027             continue;
1028           if (keyname == SOLVABLE_MEDIADIR || keyname == SOLVABLE_MEDIAFILE || keyname == SOLVABLE_MEDIANR)
1029             continue;
1030           if (keyname == SOLVABLE_DOWNLOADSIZE || keyname == SOLVABLE_CHECKSUM)
1031             continue;
1032           if (keyname == SOLVABLE_SOURCENAME || keyname == SOLVABLE_SOURCEARCH || keyname == SOLVABLE_SOURCEEVR)
1033             continue;
1034           if (keyname == SOLVABLE_PKGID || keyname == SOLVABLE_HDRID || keyname == SOLVABLE_LEADSIGID)
1035             continue;
1036           if (keyname == SUSETAGS_SHARE_NAME || keyname == SUSETAGS_SHARE_EVR || keyname == SUSETAGS_SHARE_ARCH)
1037             continue;
1038           MAPSET(&keyidmap, i);
1039         }
1040       last_found = 0;
1041       for (i = 0; i < pd.nshare; i++)
1042         {
1043           unsigned int n, nn;
1044           Solvable *found = 0;
1045           if (!pd.share_with[i].name)
1046             continue;
1047           for (n = repo->start, nn = repo->start + last_found; n < repo->end; n++, nn++)
1048             {
1049               if (nn >= repo->end)
1050                 nn = repo->start;
1051               found = pool->solvables + nn;
1052               if (found->repo == repo
1053                   && found->name == pd.share_with[i].name
1054                   && found->evr == pd.share_with[i].evr
1055                   && found->arch == pd.share_with[i].arch)
1056                 {
1057                   last_found = nn - repo->start;
1058                   break;
1059                 }
1060             }
1061           if (n != repo->end)
1062             repodata_merge_some_attrs(data, repo->start + i, repo->start + last_found, &keyidmap, 0);
1063         }
1064       free(pd.share_with);
1065       map_free(&keyidmap);
1066     }
1067
1068   solv_free(joinhash);
1069   repodata_free_dircache(data);
1070   if (!(flags & REPO_NO_INTERNALIZE))
1071     repodata_internalize(data);
1072
1073   solv_free(pd.language);
1074   solv_free(line);
1075   join_freemem(&pd.jd);
1076   queue_free(&pd.diskusageq);
1077   return pd.ret;
1078 }