- allow <> dep in susetags, special case pattern: in packageand(), add SOLVER_TRANSAC...
[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)
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 (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   char *sp[5];
527   struct parsedata pd;
528   Repodata *data = 0;
529   Id handle = 0;
530   Hashtable joinhash = 0;
531   Hashmask joinhashm = 0;
532   int createdpkgs = 0;
533
534   if ((flags & (SUSETAGS_EXTEND|REPO_EXTEND_SOLVABLES)) != 0 && repo->nrepodata)
535     {
536       joinhash = joinhash_init(repo, &joinhashm);
537       indesc = 1;
538     }
539
540   data = repo_add_repodata(repo, flags);
541
542   memset(&pd, 0, sizeof(pd));
543   line = solv_malloc(1024);
544   aline = 1024;
545
546   pd.pool = pool;
547   pd.repo = repo;
548   pd.data = data;
549   pd.flags = flags;
550   pd.language = language && *language ? solv_strdup(language) : 0;
551
552   linep = line;
553   s = 0;
554   freshens = 0;
555
556   /* if this is a join setup the recorded share data */
557   if (joinhash)
558     {
559       Repodata *sdata;
560       int i;
561       
562       FOR_REPODATAS(repo, i, sdata)
563         {
564           int p;
565           if (!repodata_has_keyname(sdata, SUSETAGS_SHARE_NAME))
566             continue;
567           for (p = sdata->start; p < sdata->end; p++)
568             {
569               Id name, evr, arch;
570               name = lookup_shared_id(sdata, p, SUSETAGS_SHARE_NAME, pool->solvables[p].name, sdata == data);
571               if (!name)
572                 continue;
573               evr = lookup_shared_id(sdata, p, SUSETAGS_SHARE_EVR, pool->solvables[p].evr, sdata == data);
574               if (!evr)
575                 continue;
576               arch = lookup_shared_id(sdata, p, SUSETAGS_SHARE_ARCH, pool->solvables[p].arch, sdata == data);
577               if (!arch)
578                 continue;
579               if (p - repo->start >= pd.nshare)
580                 {
581                   pd.share_with = solv_realloc2(pd.share_with, p - repo->start + 256, sizeof(*pd.share_with));
582                   memset(pd.share_with + pd.nshare, 0, (p - repo->start + 256 - pd.nshare) * sizeof(*pd.share_with));
583                   pd.nshare = p - repo->start + 256;
584                 }
585               pd.share_with[p - repo->start].name = name;
586               pd.share_with[p - repo->start].evr = evr;
587               pd.share_with[p - repo->start].arch = arch;
588             }
589         }
590     }
591   
592   /*
593    * read complete file
594    *
595    * collect values in 'struct parsedata pd'
596    * then build .solv (and .attr) file
597    */
598
599   for (;;)
600     {
601       unsigned tag;
602       char *olinep; /* old line pointer */
603       char line_lang[6];
604       int keylen = 3;
605       if (linep - line + 16 > aline)              /* (re-)alloc buffer */
606         {
607           aline = linep - line;
608           line = solv_realloc(line, aline + 512);
609           linep = line + aline;
610           aline += 512;
611         }
612       if (!fgets(linep, aline - (linep - line), fp)) /* read line */
613         break;
614       olinep = linep;
615       linep += strlen(linep);
616       if (linep == line || linep[-1] != '\n')
617         continue;
618       pd.lineno++;
619       *--linep = 0;
620       if (linep == olinep)
621         continue;
622
623       if (intag)
624         {
625           /* check for multi-line value tags (+Key:/-Key:) */
626
627           int is_end = (linep[-intag - keylen + 1] == '-')
628                       && (linep[-1] == ':')
629                       && (linep == line + 1 + intag + 1 + 1 + 1 + intag + 1 || linep[-intag - keylen] == '\n');
630           if (is_end
631               && strncmp(linep - 1 - intag, line + 1, intag))
632             {
633               pool_debug(pool, SOLV_ERROR, "susetags: Nonmatching multi-line tags: %d: '%s' '%s' %d\n", pd.lineno, linep - 1 - intag, line + 1, intag);
634             }
635           if (cummulate && !is_end)
636             {
637               *linep++ = '\n';
638               continue;
639             }
640           if (cummulate && is_end)
641             {
642               linep[-intag - keylen + 1] = 0;
643               if (linep[-intag - keylen] == '\n')
644                 linep[-intag - keylen] = 0;
645               linep = line;
646               intag = 0;
647             }
648           if (!cummulate && is_end)
649             {
650               intag = 0;
651               linep = line;
652               continue;
653             }
654           if (!cummulate && !is_end)
655             linep = line + intag + keylen;
656         }
657       else
658         linep = line;
659
660       if (!intag && line[0] == '+' && line[1] && line[1] != ':') /* start of +Key:/-Key: tag */
661         {
662           char *tagend = strchr(line, ':');
663           if (!tagend)
664             {
665               pool_debug(pool, SOLV_FATAL, "susetags: bad line: %d: %s\n", pd.lineno, line);
666               exit(1);
667             }
668           intag = tagend - (line + 1);
669           cummulate = 0;
670           switch (tag_from_string(line))       /* check if accumulation is needed */
671             {
672               case CTAG('+', 'D', 'e', 's'):
673               case CTAG('+', 'E', 'u', 'l'):
674               case CTAG('+', 'I', 'n', 's'):
675               case CTAG('+', 'D', 'e', 'l'):
676               case CTAG('+', 'A', 'u', 't'):
677                 if (line[4] == ':' || line[4] == '.')
678                   cummulate = 1;
679                 break;
680               default:
681                 break;
682             }
683           line[0] = '=';                       /* handle lines between +Key:/-Key: as =Key: */
684           line[intag + keylen - 1] = ' ';
685           linep = line + intag + keylen;
686           continue;
687         }
688       if (*line == '#' || !*line)
689         continue;
690       if (! (line[0] && line[1] && line[2] && line[3] && (line[4] == ':' || line[4] == '.')))
691         continue;
692       line_lang[0] = 0;
693       if (line[4] == '.')
694         {
695           char *endlang;
696           endlang = strchr(line + 5, ':');
697           if (endlang)
698             {
699               int langsize = endlang - (line + 5);
700               keylen = endlang - line - 1;
701               if (langsize > 5)
702                 langsize = 5;
703               strncpy(line_lang, line + 5, langsize);
704               line_lang[langsize] = 0;
705             }
706         }
707       tag = tag_from_string(line);
708
709       if (indelta)
710         {
711           /* Example:
712             =Dlt: subversion 1.6.16 1.3.1 i586
713             =Dsq: subversion 1.6.15 4.2 i586 d57b3fc86e7a2f73796e8e35b96fa86212c910
714             =Cks: SHA1 14a8410cf741856a5d70d89dab62984dba6a1ca7
715             =Loc: 1 subversion-1.6.15_1.6.16-4.2_1.3.1.i586.delta.rpm
716             =Siz: 81558
717            */
718           switch (tag)
719             {
720             case CTAG('=', 'D', 's', 'q'):
721               {
722                 Id evr;
723                 if (split(line + 5, sp, 5) != 5)
724                   continue;
725                 repodata_set_id(data, handle, DELTA_SEQ_NAME, pool_str2id(pool, sp[0], 1));
726                 evr = toevr(pool, &pd, sp[1], sp[2]);
727                 repodata_set_id(data, handle, DELTA_SEQ_EVR, evr);
728                 /* repodata_set_id(data, handle, DELTA_SEQ_ARCH, pool_str2id(pool, sp[3], 1)); */
729                 repodata_set_str(data, handle, DELTA_SEQ_NUM, sp[4]);
730                 repodata_set_id(data, handle, DELTA_BASE_EVR, evr);
731                 continue;
732               }
733             case CTAG('=', 'C', 'k', 's'):
734               set_checksum(&pd, data, handle, DELTA_CHECKSUM, line + 6);
735               continue;
736             case CTAG('=', 'L', 'o', 'c'):
737               {
738                 int i = split(line + 6, sp, 3);
739                 if (i != 2 && i != 3)
740                   {
741                     pool_debug(pool, SOLV_FATAL, "susetags: bad location line: %d: %s\n", pd.lineno, line);
742                     exit(1);
743                   }
744                 set_delta_location(data, handle, atoi(sp[0]), i == 3 ? sp[2] : 0, sp[1]);
745                 continue;
746               }
747             case CTAG('=', 'S', 'i', 'z'):
748               if (split(line + 6, sp, 3) == 2)
749                 repodata_set_num(data, handle, DELTA_DOWNLOADSIZE, (unsigned int)(atoi(sp[0]) + 1023) / 1024);
750               continue;
751             case CTAG('=', 'P', 'k', 'g'):
752             case CTAG('=', 'P', 'a', 't'):
753             case CTAG('=', 'D', 'l', 't'):
754               handle = 0;
755               indelta = 0;
756               break;
757             default:
758               pool_debug(pool, SOLV_ERROR, "susetags: unknown line: %d: %s\n", pd.lineno, line);
759               continue;
760             }
761         }
762
763       /*
764        * start of (next) package or pattern or delta
765        *
766        * =Pkg: <name> <version> <release> <architecture>
767        * (=Pat: ...)
768        */
769       if (tag == CTAG('=', 'D', 'l', 't'))
770         {
771           if (s)
772             finish_solvable(&pd, s, handle, freshens);
773           s = 0;
774           pd.kind = 0;
775           if (split(line + 5, sp, 5) != 4)
776             {
777               pool_debug(pool, SOLV_FATAL, "susetags: bad line: %d: %s\n", pd.lineno, line);
778               exit(1);
779             }
780           handle = repodata_new_handle(data);
781           repodata_set_id(data, handle, DELTA_PACKAGE_NAME, pool_str2id(pool, sp[0], 1));
782           repodata_set_id(data, handle, DELTA_PACKAGE_EVR, toevr(pool, &pd, sp[1], sp[2]));
783           repodata_set_id(data, handle, DELTA_PACKAGE_ARCH, pool_str2id(pool, sp[3], 1));
784           repodata_add_flexarray(data, SOLVID_META, REPOSITORY_DELTAINFO, handle);
785           indelta = 1;
786           continue;
787         }
788       if (tag == CTAG('=', 'P', 'k', 'g')
789            || tag == CTAG('=', 'P', 'a', 't'))
790         {
791           /* If we have an old solvable, complete it by filling in some
792              default stuff.  */
793           if (s)
794             finish_solvable(&pd, s, handle, freshens);
795
796           /*
797            * define kind
798            */
799
800           pd.kind = 0;
801           if (line[3] == 't')
802             pd.kind = "pattern";
803
804           /*
805            * parse nevra
806            */
807
808           if (split(line + 5, sp, 5) != 4)
809             {
810               pool_debug(pool, SOLV_FATAL, "susetags: bad line: %d: %s\n", pd.lineno, line);
811               exit(1);
812             }
813           s = 0;
814           freshens = 0;
815
816           if (joinhash)
817             {
818               /* data join operation. find solvable matching name/arch/evr and
819                * add data to it */
820               Id name, evr, arch;
821               /* we don't use the create flag here as a simple pre-check for existance */
822               if (pd.kind)
823                 name = pool_str2id(pool, join2(&pd.jd, pd.kind, ":", sp[0]), 0);
824               else
825                 name = pool_str2id(pool, sp[0], 0);
826               evr = toevr(pool, &pd, sp[1], sp[2]);
827               arch = pool_str2id(pool, sp[3], 0);
828               if (name && arch)
829                 {
830                   if (repo->start + last_found_pack + 1 < repo->end)
831                     {
832                       s = pool->solvables + repo->start + last_found_pack + 1;
833                       if (s->repo != repo || s->name != name || s->evr != evr || s->arch != arch)
834                         s = 0;
835                     }
836                   if (!s)
837                     s = joinhash_lookup(repo, joinhash, joinhashm, name, evr, arch);
838                 }
839               if (!s && (flags & REPO_EXTEND_SOLVABLES) != 0)
840                 continue;
841               /* fallthrough to package creation */
842             }
843           if (!s)
844             {
845               /* normal operation. create a new solvable. */
846               s = pool_id2solvable(pool, repo_add_solvable(repo));
847               if (pd.kind)
848                 s->name = pool_str2id(pool, join2(&pd.jd, pd.kind, ":", sp[0]), 1);
849               else
850                 s->name = pool_str2id(pool, sp[0], 1);
851               s->evr = toevr(pool, &pd, sp[1], sp[2]);
852               s->arch = pool_str2id(pool, sp[3], 1);
853               s->vendor = defvendor;
854               createdpkgs = 1;
855             }
856           last_found_pack = (s - pool->solvables) - repo->start;
857           if (data)
858             handle = s - pool->solvables;
859         }
860
861       /* If we have no current solvable to add to, ignore all further lines
862          for it.  Probably invalid input data in the second set of
863          solvables.  */
864       if (indesc >= 2 && !s)
865         {
866 #if 0
867           pool_debug(pool, SOLV_ERROR, "susetags: huh %d: %s?\n", pd.lineno, line);
868 #endif
869           continue;
870         }
871       switch (tag)
872         {
873           case CTAG('=', 'P', 'r', 'v'):                                        /* provides */
874             if (line[6] == '/')
875               {
876                 /* probably a filelist entry. stash it away for now */
877                 int l = strlen(line + 6) + 1;
878                 if (pd.nfilelist + l > pd.afilelist)
879                   {
880                     pd.afilelist = pd.nfilelist + l + 512;
881                     pd.filelist = solv_realloc(pd.filelist, pd.afilelist);
882                   }
883                 memcpy(pd.filelist + pd.nfilelist, line + 6, l);
884                 pd.nfilelist += l;
885                 break;
886               }
887             if (pd.nfilelist)
888               {
889                 int l;
890                 for (l = 0; l < pd.nfilelist; l += strlen(pd.filelist + l) + 1)
891                   s->provides = repo_addid_dep(pd.repo, s->provides, pool_str2id(pool, pd.filelist + l, 1), 0);
892                 pd.nfilelist = 0;
893               }
894             s->provides = adddep(pool, &pd, s->provides, line, 0, pd.kind);
895             continue;
896           case CTAG('=', 'R', 'e', 'q'):                                        /* requires */
897             s->requires = adddep(pool, &pd, s->requires, line, -SOLVABLE_PREREQMARKER, pd.kind);
898             continue;
899           case CTAG('=', 'P', 'r', 'q'):                                        /* pre-requires / packages required */
900             if (pd.kind)
901               {
902                 s->requires = adddep(pool, &pd, s->requires, line, 0, 0);           /* patterns: a required package */
903               }
904             else
905               s->requires = adddep(pool, &pd, s->requires, line, SOLVABLE_PREREQMARKER, 0); /* package: pre-requires */
906             continue;
907           case CTAG('=', 'O', 'b', 's'):                                        /* obsoletes */
908             s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, pd.kind);
909             continue;
910           case CTAG('=', 'C', 'o', 'n'):                                        /* conflicts */
911             s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, pd.kind);
912             continue;
913           case CTAG('=', 'R', 'e', 'c'):                                        /* recommends */
914             s->recommends = adddep(pool, &pd, s->recommends, line, 0, pd.kind);
915             continue;
916           case CTAG('=', 'S', 'u', 'p'):                                        /* supplements */
917             s->supplements = adddep(pool, &pd, s->supplements, line, 0, pd.kind);
918             continue;
919           case CTAG('=', 'E', 'n', 'h'):                                        /* enhances */
920             s->enhances = adddep(pool, &pd, s->enhances, line, 0, pd.kind);
921             continue;
922           case CTAG('=', 'S', 'u', 'g'):                                        /* suggests */
923             s->suggests = adddep(pool, &pd, s->suggests, line, 0, pd.kind);
924             continue;
925           case CTAG('=', 'F', 'r', 'e'):                                        /* freshens */
926             freshens = adddep(pool, &pd, freshens, line, 0, pd.kind);
927             continue;
928           case CTAG('=', 'P', 'r', 'c'):                                        /* packages recommended */
929             s->recommends = adddep(pool, &pd, s->recommends, line, 0, 0);
930             continue;
931           case CTAG('=', 'P', 's', 'g'):                                        /* packages suggested */
932             s->suggests = adddep(pool, &pd, s->suggests, line, 0, 0);
933             continue;
934           case CTAG('=', 'P', 'c', 'n'):                                        /* pattern: package conflicts */
935             s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, 0);
936             continue;
937           case CTAG('=', 'P', 'o', 'b'):                                        /* pattern: package obsoletes */
938             s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, 0);
939             continue;
940           case CTAG('=', 'P', 'f', 'r'):                                        /* pattern: package freshens */
941             freshens = adddep(pool, &pd, freshens, line, 0, 0);
942             continue;
943           case CTAG('=', 'P', 's', 'p'):                                        /* pattern: package supplements */
944             s->supplements = adddep(pool, &pd, s->supplements, line, 0, 0);
945             continue;
946           case CTAG('=', 'P', 'e', 'n'):                                        /* pattern: package enhances */
947             s->enhances = adddep(pool, &pd, s->enhances, line, 0, 0);
948             continue;
949           case CTAG('=', 'V', 'e', 'r'):                                        /* - version - */
950             last_found_pack = 0;
951             handle = 0;
952             indesc++;
953             if (createdpkgs)
954               {
955                 solv_free(joinhash);
956                 joinhash = joinhash_init(repo, &joinhashm);
957                 createdpkgs = 0;
958               }
959             continue;
960           case CTAG('=', 'V', 'n', 'd'):                                        /* vendor */
961             s->vendor = pool_str2id(pool, line + 6, 1);
962             continue;
963
964         /* From here it's the attribute tags.  */
965           case CTAG('=', 'G', 'r', 'p'):
966             repodata_set_poolstr(data, handle, SOLVABLE_GROUP, line + 6);
967             continue;
968           case CTAG('=', 'L', 'i', 'c'):
969             repodata_set_poolstr(data, handle, SOLVABLE_LICENSE, line + 6);
970             continue;
971           case CTAG('=', 'L', 'o', 'c'):
972             {
973               int i = split(line + 6, sp, 3);
974               if (i != 2 && i != 3)
975                 {
976                   pool_debug(pool, SOLV_FATAL, "susetags: bad location line: %d: %s\n", pd.lineno, line);
977                   exit(1);
978                 }
979               repodata_set_location(data, handle, atoi(sp[0]), i == 3 ? sp[2] : pool_id2str(pool, s->arch), sp[1]);
980             }
981             continue;
982           case CTAG('=', 'S', 'r', 'c'):
983             add_source(&pd, line + 6, s, handle);
984             continue;
985           case CTAG('=', 'S', 'i', 'z'):
986             if (split(line + 6, sp, 3) == 2)
987               {
988                 repodata_set_num(data, handle, SOLVABLE_DOWNLOADSIZE, (unsigned int)(atoi(sp[0]) + 1023) / 1024);
989                 repodata_set_num(data, handle, SOLVABLE_INSTALLSIZE, (unsigned int)(atoi(sp[1]) + 1023) / 1024);
990               }
991             continue;
992           case CTAG('=', 'T', 'i', 'm'):
993             {
994               unsigned int t = atoi(line + 6);
995               if (t)
996                 repodata_set_num(data, handle, SOLVABLE_BUILDTIME, t);
997             }
998             continue;
999           case CTAG('=', 'K', 'w', 'd'):
1000             repodata_add_poolstr_array(data, handle, SOLVABLE_KEYWORDS, line + 6);
1001             continue;
1002           case CTAG('=', 'A', 'u', 't'):
1003             repodata_set_str(data, handle, SOLVABLE_AUTHORS, line + 6);
1004             continue;
1005           case CTAG('=', 'S', 'u', 'm'):
1006             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_SUMMARY, line_lang), line + 3 + keylen);
1007             continue;
1008           case CTAG('=', 'D', 'e', 's'):
1009             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_DESCRIPTION, line_lang), line + 3 + keylen);
1010             continue;
1011           case CTAG('=', 'E', 'u', 'l'):
1012             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_EULA, line_lang), line + 6);
1013             continue;
1014           case CTAG('=', 'I', 'n', 's'):
1015             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_MESSAGEINS, line_lang), line + 6);
1016             continue;
1017           case CTAG('=', 'D', 'e', 'l'):
1018             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_MESSAGEDEL, line_lang), line + 6);
1019             continue;
1020           case CTAG('=', 'V', 'i', 's'):
1021             {
1022               /* Accept numbers and textual bools.  */
1023               int k;
1024               k = atoi(line + 6);
1025               if (k || !strcasecmp(line + 6, "true"))
1026                 repodata_set_void(data, handle, SOLVABLE_ISVISIBLE);
1027             }
1028             continue;
1029           case CTAG('=', 'S', 'h', 'r'):
1030             {
1031               Id name, evr, arch;
1032               if (split(line + 6, sp, 5) != 4)
1033                 {
1034                   pool_debug(pool, SOLV_FATAL, "susetags: bad =Shr line: %s\n", line + 6);
1035                   exit(1);
1036                 }
1037               name = pool_str2id(pool, sp[0], 1);
1038               evr = toevr(pool, &pd, sp[1], sp[2]);
1039               arch = pool_str2id(pool, sp[3], 1);
1040               if (last_found_pack >= pd.nshare)
1041                 {
1042                   pd.share_with = solv_realloc2(pd.share_with, last_found_pack + 256, sizeof(*pd.share_with));
1043                   memset(pd.share_with + pd.nshare, 0, (last_found_pack + 256 - pd.nshare) * sizeof(*pd.share_with));
1044                   pd.nshare = last_found_pack + 256;
1045                 }
1046               pd.share_with[last_found_pack].name = name;
1047               pd.share_with[last_found_pack].evr = evr;
1048               pd.share_with[last_found_pack].arch = arch;
1049               if ((flags & SUSETAGS_RECORD_SHARES) != 0)
1050                 {
1051                   if (s->name == name)
1052                     repodata_set_void(data, handle, SUSETAGS_SHARE_NAME);
1053                   else
1054                     repodata_set_id(data, handle, SUSETAGS_SHARE_NAME, name);
1055                   if (s->evr == evr)
1056                     repodata_set_void(data, handle, SUSETAGS_SHARE_EVR);
1057                   else
1058                     repodata_set_id(data, handle, SUSETAGS_SHARE_EVR, evr);
1059                   if (s->arch == arch)
1060                     repodata_set_void(data, handle, SUSETAGS_SHARE_ARCH);
1061                   else
1062                     repodata_set_id(data, handle, SUSETAGS_SHARE_ARCH, arch);
1063                 }
1064               continue;
1065             }
1066           case CTAG('=', 'D', 'i', 'r'):
1067             add_dirline(&pd, line + 6);
1068             continue;
1069           case CTAG('=', 'C', 'a', 't'):
1070             repodata_set_poolstr(data, handle, langtag(&pd, SOLVABLE_CATEGORY, line_lang), line + 3 + keylen);
1071             break;
1072           case CTAG('=', 'O', 'r', 'd'):
1073             /* Order is a string not a number, so we can retroactively insert
1074                new patterns in the middle, i.e. 1 < 15 < 2.  */
1075             repodata_set_str(data, handle, SOLVABLE_ORDER, line + 6);
1076             break;
1077           case CTAG('=', 'I', 'c', 'o'):
1078             repodata_set_str(data, handle, SOLVABLE_ICON, line + 6);
1079             break;
1080           case CTAG('=', 'E', 'x', 't'):
1081             repodata_add_poolstr_array(data, handle, SOLVABLE_EXTENDS, join2(&pd.jd, "pattern", ":", line + 6));
1082             break;
1083           case CTAG('=', 'I', 'n', 'c'):
1084             repodata_add_poolstr_array(data, handle, SOLVABLE_INCLUDES, join2(&pd.jd, "pattern", ":", line + 6));
1085             break;
1086           case CTAG('=', 'C', 'k', 's'):
1087             set_checksum(&pd, data, handle, SOLVABLE_CHECKSUM, line + 6);
1088             break;
1089           case CTAG('=', 'L', 'a', 'n'):
1090             pd.language = solv_free(pd.language);
1091             memset(pd.langcache, 0, sizeof(pd.langcache));
1092             if (line[6])
1093               pd.language = solv_strdup(line + 6);
1094             break;
1095
1096           case CTAG('=', 'F', 'l', 's'):
1097             {
1098               char *p = strrchr(line + 6, '/');
1099               Id did;
1100               /* strip trailing slash */
1101               if (p && p != line + 6 && !p[1])
1102                 {
1103                   *p = 0;
1104                   p = strrchr(line + 6, '/');
1105                 }
1106               if (p)
1107                 {
1108                   *p++ = 0;
1109                   did = repodata_str2dir(data, line + 6, 1);
1110                 }
1111               else
1112                 {
1113                   p = line + 6;
1114                   did = 0;
1115                 }
1116               if (!did)
1117                 did = repodata_str2dir(data, "/", 1);
1118               repodata_add_dirstr(data, handle, SOLVABLE_FILELIST, did, p);
1119               break;
1120             }
1121           case CTAG('=', 'H', 'd', 'r'):
1122             /* rpm header range */
1123             if (split(line + 6, sp, 3) == 2)
1124               {
1125                 /* we ignore the start value */
1126                 unsigned int end = (unsigned int)atoi(sp[1]);
1127                 if (end)
1128                   repodata_set_num(data, handle, SOLVABLE_HEADEREND, end);
1129               }
1130             break;
1131
1132           case CTAG('=', 'P', 'a', 't'):
1133           case CTAG('=', 'P', 'k', 'g'):
1134             break;
1135
1136           default:
1137             pool_debug(pool, SOLV_ERROR, "susetags: unknown line: %d: %s\n", pd.lineno, line);
1138             break;
1139         }
1140
1141     } /* for(;;) */
1142
1143   if (s)
1144     finish_solvable(&pd, s, handle, freshens);
1145   solv_free(pd.filelist);
1146
1147   /* Shared attributes
1148    *  (e.g. multiple binaries built from same source)
1149    */
1150   if (pd.nshare)
1151     {
1152       int i, last_found;
1153       Map keyidmap;
1154
1155       map_init(&keyidmap, data->nkeys);
1156       for (i = 1; i < data->nkeys; i++)
1157         {
1158           Id keyname = data->keys[i].name;
1159           if (keyname == SOLVABLE_INSTALLSIZE || keyname == SOLVABLE_DISKUSAGE || keyname == SOLVABLE_FILELIST)
1160             continue;
1161           if (keyname == SOLVABLE_MEDIADIR || keyname == SOLVABLE_MEDIAFILE || keyname == SOLVABLE_MEDIANR)
1162             continue;
1163           if (keyname == SOLVABLE_DOWNLOADSIZE || keyname == SOLVABLE_CHECKSUM)
1164             continue;
1165           if (keyname == SOLVABLE_SOURCENAME || keyname == SOLVABLE_SOURCEARCH || keyname == SOLVABLE_SOURCEEVR)
1166             continue;
1167           if (keyname == SOLVABLE_PKGID || keyname == SOLVABLE_HDRID || keyname == SOLVABLE_LEADSIGID)
1168             continue;
1169           if (keyname == SUSETAGS_SHARE_NAME || keyname == SUSETAGS_SHARE_EVR || keyname == SUSETAGS_SHARE_ARCH)
1170             continue;
1171           MAPSET(&keyidmap, i);
1172         }
1173       last_found = 0;
1174       for (i = 0; i < pd.nshare; i++)
1175         {
1176           unsigned int n, nn;
1177           Solvable *found = 0;
1178           if (!pd.share_with[i].name)
1179             continue;
1180           for (n = repo->start, nn = repo->start + last_found; n < repo->end; n++, nn++)
1181             {
1182               if (nn >= repo->end)
1183                 nn = repo->start;
1184               found = pool->solvables + nn;
1185               if (found->repo == repo
1186                   && found->name == pd.share_with[i].name
1187                   && found->evr == pd.share_with[i].evr
1188                   && found->arch == pd.share_with[i].arch)
1189                 {
1190                   last_found = nn - repo->start;
1191                   break;
1192                 }
1193             }
1194           if (n != repo->end)
1195             repodata_merge_some_attrs(data, repo->start + i, repo->start + last_found, &keyidmap, 0);
1196         }
1197       free(pd.share_with);
1198       map_free(&keyidmap);
1199     }
1200
1201   solv_free(joinhash);
1202   repodata_free_dircache(data);
1203   if (!(flags & REPO_NO_INTERNALIZE))
1204     repodata_internalize(data);
1205
1206   solv_free(pd.language);
1207   solv_free(line);
1208   join_freemem(&pd.jd);
1209   return 0;
1210 }