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