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