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