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