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