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