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