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