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