- clean up repo parsing code
[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  
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(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(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 /*
544  * parse susetags
545  *
546  * fp: file to read from
547  * defvendor: default vendor (0 if none)
548  * language: current language (0 if none)
549  * flags: flags
550  */
551
552 int
553 repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int flags)
554 {
555   Pool *pool = repo->pool;
556   char *line, *linep;
557   int aline;
558   Solvable *s;
559   Offset freshens;
560   int intag = 0;
561   int cummulate = 0;
562   int indesc = 0;
563   int indelta = 0;
564   int last_found_pack = 0;
565   char *sp[5];
566   struct parsedata pd;
567   Repodata *data = 0;
568   Id handle = 0;
569   Hashtable joinhash = 0;
570   Hashmask joinhashm = 0;
571   int createdpkgs = 0;
572
573   if ((flags & (SUSETAGS_EXTEND|REPO_EXTEND_SOLVABLES)) != 0 && repo->nrepodata)
574     {
575       joinhash = joinhash_init(repo, &joinhashm);
576       indesc = 1;
577     }
578
579   data = repo_add_repodata(repo, flags);
580
581   memset(&pd, 0, sizeof(pd));
582   line = malloc(1024);
583   aline = 1024;
584
585   pd.pool = pool;
586   pd.repo = repo;
587   pd.data = data;
588   pd.flags = flags;
589   pd.language = language && *language ? solv_strdup(language) : 0;
590
591   linep = line;
592   s = 0;
593   freshens = 0;
594
595   /* if this is a join setup the recorded share data */
596   if (joinhash)
597     {
598       Repodata *sdata;
599       int i;
600       
601       FOR_REPODATAS(repo, i, sdata)
602         {
603           int p;
604           if (!repodata_has_keyname(sdata, SUSETAGS_SHARE_NAME))
605             continue;
606           for (p = sdata->start; p < sdata->end; p++)
607             {
608               Id name, evr, arch;
609               name = lookup_shared_id(sdata, p, SUSETAGS_SHARE_NAME, pool->solvables[p].name, sdata == data);
610               if (!name)
611                 continue;
612               evr = lookup_shared_id(sdata, p, SUSETAGS_SHARE_EVR, pool->solvables[p].evr, sdata == data);
613               if (!evr)
614                 continue;
615               arch = lookup_shared_id(sdata, p, SUSETAGS_SHARE_ARCH, pool->solvables[p].arch, sdata == data);
616               if (!arch)
617                 continue;
618               if (p - repo->start >= pd.nshare)
619                 {
620                   pd.share_with = solv_realloc2(pd.share_with, p - repo->start + 256, sizeof(*pd.share_with));
621                   memset(pd.share_with + pd.nshare, 0, (p - repo->start + 256 - pd.nshare) * sizeof(*pd.share_with));
622                   pd.nshare = p - repo->start + 256;
623                 }
624               pd.share_with[p - repo->start].name = name;
625               pd.share_with[p - repo->start].evr = evr;
626               pd.share_with[p - repo->start].arch = arch;
627             }
628         }
629     }
630   
631   /*
632    * read complete file
633    *
634    * collect values in 'struct parsedata pd'
635    * then build .solv (and .attr) file
636    */
637
638   for (;;)
639     {
640       unsigned tag;
641       char *olinep; /* old line pointer */
642       char line_lang[6];
643       int keylen = 3;
644       if (linep - line + 16 > aline)              /* (re-)alloc buffer */
645         {
646           aline = linep - line;
647           line = realloc(line, aline + 512);
648           linep = line + aline;
649           aline += 512;
650         }
651       if (!fgets(linep, aline - (linep - line), fp)) /* read line */
652         break;
653       olinep = linep;
654       linep += strlen(linep);
655       if (linep == line || linep[-1] != '\n')
656         continue;
657       pd.lineno++;
658       *--linep = 0;
659       if (linep == olinep)
660         continue;
661
662       if (intag)
663         {
664           /* check for multi-line value tags (+Key:/-Key:) */
665
666           int is_end = (linep[-intag - keylen + 1] == '-')
667                       && (linep[-1] == ':')
668                       && (linep == line + 1 + intag + 1 + 1 + 1 + intag + 1 || linep[-intag - keylen] == '\n');
669           if (is_end
670               && strncmp(linep - 1 - intag, line + 1, intag))
671             {
672               pool_debug(pool, SOLV_ERROR, "susetags: Nonmatching multi-line tags: %d: '%s' '%s' %d\n", pd.lineno, linep - 1 - intag, line + 1, intag);
673             }
674           if (cummulate && !is_end)
675             {
676               *linep++ = '\n';
677               continue;
678             }
679           if (cummulate && is_end)
680             {
681               linep[-intag - keylen + 1] = 0;
682               if (linep[-intag - keylen] == '\n')
683                 linep[-intag - keylen] = 0;
684               linep = line;
685               intag = 0;
686             }
687           if (!cummulate && is_end)
688             {
689               intag = 0;
690               linep = line;
691               continue;
692             }
693           if (!cummulate && !is_end)
694             linep = line + intag + keylen;
695         }
696       else
697         linep = line;
698
699       if (!intag && line[0] == '+' && line[1] && line[1] != ':') /* start of +Key:/-Key: tag */
700         {
701           char *tagend = strchr(line, ':');
702           if (!tagend)
703             {
704               pool_debug(pool, SOLV_FATAL, "susetags: bad line: %d: %s\n", pd.lineno, line);
705               exit(1);
706             }
707           intag = tagend - (line + 1);
708           cummulate = 0;
709           switch (tag_from_string(line))       /* check if accumulation is needed */
710             {
711               case CTAG('+', 'D', 'e', 's'):
712               case CTAG('+', 'E', 'u', 'l'):
713               case CTAG('+', 'I', 'n', 's'):
714               case CTAG('+', 'D', 'e', 'l'):
715               case CTAG('+', 'A', 'u', 't'):
716                 if (line[4] == ':' || line[4] == '.')
717                   cummulate = 1;
718                 break;
719               default:
720                 break;
721             }
722           line[0] = '=';                       /* handle lines between +Key:/-Key: as =Key: */
723           line[intag + keylen - 1] = ' ';
724           linep = line + intag + keylen;
725           continue;
726         }
727       if (*line == '#' || !*line)
728         continue;
729       if (! (line[0] && line[1] && line[2] && line[3] && (line[4] == ':' || line[4] == '.')))
730         continue;
731       line_lang[0] = 0;
732       if (line[4] == '.')
733         {
734           char *endlang;
735           endlang = strchr(line + 5, ':');
736           if (endlang)
737             {
738               int langsize = endlang - (line + 5);
739               keylen = endlang - line - 1;
740               if (langsize > 5)
741                 langsize = 5;
742               strncpy(line_lang, line + 5, langsize);
743               line_lang[langsize] = 0;
744             }
745         }
746       tag = tag_from_string(line);
747
748       if (indelta)
749         {
750           /* Example:
751             =Dlt: subversion 1.6.16 1.3.1 i586
752             =Dsq: subversion 1.6.15 4.2 i586 d57b3fc86e7a2f73796e8e35b96fa86212c910
753             =Cks: SHA1 14a8410cf741856a5d70d89dab62984dba6a1ca7
754             =Loc: 1 subversion-1.6.15_1.6.16-4.2_1.3.1.i586.delta.rpm
755             =Siz: 81558
756            */
757           switch (tag)
758             {
759             case CTAG('=', 'D', 's', 'q'):
760               {
761                 Id evr;
762                 if (split(line + 5, sp, 5) != 5)
763                   continue;
764                 repodata_set_id(data, handle, DELTA_SEQ_NAME, pool_str2id(pool, sp[0], 1));
765                 evr = makeevr(pool, join2(sp[1], "-", sp[2]));
766                 repodata_set_id(data, handle, DELTA_SEQ_EVR, evr);
767                 /* repodata_set_id(data, handle, DELTA_SEQ_ARCH, pool_str2id(pool, sp[3], 1)); */
768                 repodata_set_str(data, handle, DELTA_SEQ_NUM, sp[4]);
769                 repodata_set_id(data, handle, DELTA_BASE_EVR, evr);
770                 continue;
771               }
772             case CTAG('=', 'C', 'k', 's'):
773               set_checksum(&pd, data, handle, DELTA_CHECKSUM, line + 6);
774               continue;
775             case CTAG('=', 'L', 'o', 'c'):
776               {
777                 int i = split(line + 6, sp, 3);
778                 if (i != 2 && i != 3)
779                   {
780                     pool_debug(pool, SOLV_FATAL, "susetags: bad location line: %d: %s\n", pd.lineno, line);
781                     exit(1);
782                   }
783                 set_delta_location(data, handle, atoi(sp[0]), i == 3 ? sp[2] : 0, sp[1]);
784                 continue;
785               }
786             case CTAG('=', 'S', 'i', 'z'):
787               if (split(line + 6, sp, 3) == 2)
788                 repodata_set_num(data, handle, DELTA_DOWNLOADSIZE, (unsigned int)(atoi(sp[0]) + 1023) / 1024);
789               continue;
790             case CTAG('=', 'P', 'k', 'g'):
791             case CTAG('=', 'P', 'a', 't'):
792             case CTAG('=', 'D', 'l', 't'):
793               handle = 0;
794               indelta = 0;
795               break;
796             default:
797               pool_debug(pool, SOLV_ERROR, "susetags: unknown line: %d: %s\n", pd.lineno, line);
798               continue;
799             }
800         }
801
802       /*
803        * start of (next) package or pattern or delta
804        *
805        * =Pkg: <name> <version> <release> <architecture>
806        * (=Pat: ...)
807        */
808       if (tag == CTAG('=', 'D', 'l', 't'))
809         {
810           if (s)
811             finish_solvable(&pd, s, handle, freshens);
812           s = 0;
813           pd.kind = 0;
814           if (split(line + 5, sp, 5) != 4)
815             {
816               pool_debug(pool, SOLV_FATAL, "susetags: bad line: %d: %s\n", pd.lineno, line);
817               exit(1);
818             }
819           handle = repodata_new_handle(data);
820           repodata_set_id(data, handle, DELTA_PACKAGE_NAME, pool_str2id(pool, sp[0], 1));
821           repodata_set_id(data, handle, DELTA_PACKAGE_EVR, makeevr(pool, join2(sp[1], "-", sp[2])));
822           repodata_set_id(data, handle, DELTA_PACKAGE_ARCH, pool_str2id(pool, sp[3], 1));
823           repodata_add_flexarray(data, SOLVID_META, REPOSITORY_DELTAINFO, handle);
824           indelta = 1;
825           continue;
826         }
827       if (tag == CTAG('=', 'P', 'k', 'g')
828            || tag == CTAG('=', 'P', 'a', 't'))
829         {
830           /* If we have an old solvable, complete it by filling in some
831              default stuff.  */
832           if (s)
833             finish_solvable(&pd, s, handle, freshens);
834
835           /*
836            * define kind
837            */
838
839           pd.kind = 0;
840           if (line[3] == 't')
841             pd.kind = "pattern";
842
843           /*
844            * parse nevra
845            */
846
847           if (split(line + 5, sp, 5) != 4)
848             {
849               pool_debug(pool, SOLV_FATAL, "susetags: bad line: %d: %s\n", pd.lineno, line);
850               exit(1);
851             }
852           s = 0;
853           freshens = 0;
854
855           if (joinhash)
856             {
857               /* data join operation. find solvable matching name/arch/evr and
858                * add data to it */
859               Id name, evr, arch;
860               /* we don't use the create flag here as a simple pre-check for existance */
861               if (pd.kind)
862                 name = pool_str2id(pool, join2(pd.kind, ":", sp[0]), 0);
863               else
864                 name = pool_str2id(pool, sp[0], 0);
865               evr = makeevr(pool, join2(sp[1], "-", sp[2]));
866               arch = pool_str2id(pool, sp[3], 0);
867               if (name && arch)
868                 {
869                   if (repo->start + last_found_pack + 1 < repo->end)
870                     {
871                       s = pool->solvables + repo->start + last_found_pack + 1;
872                       if (s->repo != repo || s->name != name || s->evr != evr || s->arch != arch)
873                         s = 0;
874                     }
875                   if (!s)
876                     s = joinhash_lookup(repo, joinhash, joinhashm, name, evr, arch);
877                 }
878               if (!s && (flags & REPO_EXTEND_SOLVABLES) != 0)
879                 continue;
880               /* fallthrough to package creation */
881             }
882           if (!s)
883             {
884               /* normal operation. create a new solvable. */
885               s = pool_id2solvable(pool, repo_add_solvable(repo));
886               if (pd.kind)
887                 s->name = pool_str2id(pool, join2(pd.kind, ":", sp[0]), 1);
888               else
889                 s->name = pool_str2id(pool, sp[0], 1);
890               s->evr = makeevr(pool, join2(sp[1], "-", sp[2]));
891               s->arch = pool_str2id(pool, sp[3], 1);
892               s->vendor = defvendor;
893               createdpkgs = 1;
894             }
895           last_found_pack = (s - pool->solvables) - repo->start;
896           if (data)
897             handle = s - pool->solvables;
898         }
899
900       /* If we have no current solvable to add to, ignore all further lines
901          for it.  Probably invalid input data in the second set of
902          solvables.  */
903       if (indesc >= 2 && !s)
904         {
905 #if 0
906           pool_debug(pool, SOLV_ERROR, "susetags: huh %d: %s?\n", pd.lineno, line);
907 #endif
908           continue;
909         }
910       switch (tag)
911         {
912           case CTAG('=', 'P', 'r', 'v'):                                        /* provides */
913             s->provides = adddep(pool, &pd, s->provides, line, 0, pd.kind);
914             continue;
915           case CTAG('=', 'R', 'e', 'q'):                                        /* requires */
916             s->requires = adddep(pool, &pd, s->requires, line, -SOLVABLE_PREREQMARKER, pd.kind);
917             continue;
918           case CTAG('=', 'P', 'r', 'q'):                                        /* pre-requires / packages required */
919             if (pd.kind)
920               {
921                 s->requires = adddep(pool, &pd, s->requires, line, 0, 0);           /* patterns: a required package */
922               }
923             else
924               s->requires = adddep(pool, &pd, s->requires, line, SOLVABLE_PREREQMARKER, 0); /* package: pre-requires */
925             continue;
926           case CTAG('=', 'O', 'b', 's'):                                        /* obsoletes */
927             s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, pd.kind);
928             continue;
929           case CTAG('=', 'C', 'o', 'n'):                                        /* conflicts */
930             s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, pd.kind);
931             continue;
932           case CTAG('=', 'R', 'e', 'c'):                                        /* recommends */
933             s->recommends = adddep(pool, &pd, s->recommends, line, 0, pd.kind);
934             continue;
935           case CTAG('=', 'S', 'u', 'p'):                                        /* supplements */
936             s->supplements = adddep(pool, &pd, s->supplements, line, 0, pd.kind);
937             continue;
938           case CTAG('=', 'E', 'n', 'h'):                                        /* enhances */
939             s->enhances = adddep(pool, &pd, s->enhances, line, 0, pd.kind);
940             continue;
941           case CTAG('=', 'S', 'u', 'g'):                                        /* suggests */
942             s->suggests = adddep(pool, &pd, s->suggests, line, 0, pd.kind);
943             continue;
944           case CTAG('=', 'F', 'r', 'e'):                                        /* freshens */
945             freshens = adddep(pool, &pd, freshens, line, 0, pd.kind);
946             continue;
947           case CTAG('=', 'P', 'r', 'c'):                                        /* packages recommended */
948             s->recommends = adddep(pool, &pd, s->recommends, line, 0, 0);
949             continue;
950           case CTAG('=', 'P', 's', 'g'):                                        /* packages suggested */
951             s->suggests = adddep(pool, &pd, s->suggests, line, 0, 0);
952             continue;
953           case CTAG('=', 'P', 'c', 'n'):                                        /* pattern: package conflicts */
954             s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, 0);
955             continue;
956           case CTAG('=', 'P', 'o', 'b'):                                        /* pattern: package obsoletes */
957             s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, 0);
958             continue;
959           case CTAG('=', 'P', 'f', 'r'):                                        /* pattern: package freshens */
960             freshens = adddep(pool, &pd, freshens, line, 0, 0);
961             continue;
962           case CTAG('=', 'P', 's', 'p'):                                        /* pattern: package supplements */
963             s->supplements = adddep(pool, &pd, s->supplements, line, 0, 0);
964             continue;
965           case CTAG('=', 'P', 'e', 'n'):                                        /* pattern: package enhances */
966             s->enhances = adddep(pool, &pd, s->enhances, line, 0, 0);
967             continue;
968           case CTAG('=', 'V', 'e', 'r'):                                        /* - version - */
969             last_found_pack = 0;
970             handle = 0;
971             indesc++;
972             if (createdpkgs)
973               {
974                 solv_free(joinhash);
975                 joinhash = joinhash_init(repo, &joinhashm);
976                 createdpkgs = 0;
977               }
978             continue;
979           case CTAG('=', 'V', 'n', 'd'):                                        /* vendor */
980             s->vendor = pool_str2id(pool, line + 6, 1);
981             continue;
982
983         /* From here it's the attribute tags.  */
984           case CTAG('=', 'G', 'r', 'p'):
985             repodata_set_poolstr(data, handle, SOLVABLE_GROUP, line + 6);
986             continue;
987           case CTAG('=', 'L', 'i', 'c'):
988             repodata_set_poolstr(data, handle, SOLVABLE_LICENSE, line + 6);
989             continue;
990           case CTAG('=', 'L', 'o', 'c'):
991             {
992               int i = split(line + 6, sp, 3);
993               if (i != 2 && i != 3)
994                 {
995                   pool_debug(pool, SOLV_FATAL, "susetags: bad location line: %d: %s\n", pd.lineno, line);
996                   exit(1);
997                 }
998               repodata_set_location(data, handle, atoi(sp[0]), i == 3 ? sp[2] : pool_id2str(pool, s->arch), sp[1]);
999             }
1000             continue;
1001           case CTAG('=', 'S', 'r', 'c'):
1002             add_source(&pd, line + 6, s, handle);
1003             continue;
1004           case CTAG('=', 'S', 'i', 'z'):
1005             if (split(line + 6, sp, 3) == 2)
1006               {
1007                 repodata_set_num(data, handle, SOLVABLE_DOWNLOADSIZE, (unsigned int)(atoi(sp[0]) + 1023) / 1024);
1008                 repodata_set_num(data, handle, SOLVABLE_INSTALLSIZE, (unsigned int)(atoi(sp[1]) + 1023) / 1024);
1009               }
1010             continue;
1011           case CTAG('=', 'T', 'i', 'm'):
1012             {
1013               unsigned int t = atoi(line + 6);
1014               if (t)
1015                 repodata_set_num(data, handle, SOLVABLE_BUILDTIME, t);
1016             }
1017             continue;
1018           case CTAG('=', 'K', 'w', 'd'):
1019             repodata_add_poolstr_array(data, handle, SOLVABLE_KEYWORDS, line + 6);
1020             continue;
1021           case CTAG('=', 'A', 'u', 't'):
1022             repodata_set_str(data, handle, SOLVABLE_AUTHORS, line + 6);
1023             continue;
1024           case CTAG('=', 'S', 'u', 'm'):
1025             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_SUMMARY, line_lang), line + 3 + keylen);
1026             continue;
1027           case CTAG('=', 'D', 'e', 's'):
1028             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_DESCRIPTION, line_lang), line + 3 + keylen);
1029             continue;
1030           case CTAG('=', 'E', 'u', 'l'):
1031             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_EULA, line_lang), line + 6);
1032             continue;
1033           case CTAG('=', 'I', 'n', 's'):
1034             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_MESSAGEINS, line_lang), line + 6);
1035             continue;
1036           case CTAG('=', 'D', 'e', 'l'):
1037             repodata_set_str(data, handle, langtag(&pd, SOLVABLE_MESSAGEDEL, line_lang), line + 6);
1038             continue;
1039           case CTAG('=', 'V', 'i', 's'):
1040             {
1041               /* Accept numbers and textual bools.  */
1042               unsigned k;
1043               k = atoi(line + 6);
1044               if (k || !strcasecmp(line + 6, "true"))
1045                 repodata_set_void(data, handle, SOLVABLE_ISVISIBLE);
1046             }
1047             continue;
1048           case CTAG('=', 'S', 'h', 'r'):
1049             {
1050               Id name, evr, arch;
1051               if (split(line + 6, sp, 5) != 4)
1052                 {
1053                   pool_debug(pool, SOLV_FATAL, "susetags: bad =Shr line: %s\n", line + 6);
1054                   exit(1);
1055                 }
1056               name = pool_str2id(pool, sp[0], 1);
1057               evr = makeevr(pool, join2(sp[1], "-", sp[2]));
1058               arch = pool_str2id(pool, sp[3], 1);
1059               if (last_found_pack >= pd.nshare)
1060                 {
1061                   pd.share_with = solv_realloc2(pd.share_with, last_found_pack + 256, sizeof(*pd.share_with));
1062                   memset(pd.share_with + pd.nshare, 0, (last_found_pack + 256 - pd.nshare) * sizeof(*pd.share_with));
1063                   pd.nshare = last_found_pack + 256;
1064                 }
1065               pd.share_with[last_found_pack].name = name;
1066               pd.share_with[last_found_pack].evr = evr;
1067               pd.share_with[last_found_pack].arch = arch;
1068               if ((flags & SUSETAGS_RECORD_SHARES) != 0)
1069                 {
1070                   if (s->name == name)
1071                     repodata_set_void(data, handle, SUSETAGS_SHARE_NAME);
1072                   else
1073                     repodata_set_id(data, handle, SUSETAGS_SHARE_NAME, name);
1074                   if (s->evr == evr)
1075                     repodata_set_void(data, handle, SUSETAGS_SHARE_EVR);
1076                   else
1077                     repodata_set_id(data, handle, SUSETAGS_SHARE_EVR, evr);
1078                   if (s->arch == arch)
1079                     repodata_set_void(data, handle, SUSETAGS_SHARE_ARCH);
1080                   else
1081                     repodata_set_id(data, handle, SUSETAGS_SHARE_ARCH, arch);
1082                 }
1083               continue;
1084             }
1085           case CTAG('=', 'D', 'i', 'r'):
1086             add_dirline(&pd, line + 6);
1087             continue;
1088           case CTAG('=', 'C', 'a', 't'):
1089             repodata_set_poolstr(data, handle, langtag(&pd, SOLVABLE_CATEGORY, line_lang), line + 3 + keylen);
1090             break;
1091           case CTAG('=', 'O', 'r', 'd'):
1092             /* Order is a string not a number, so we can retroactively insert
1093                new patterns in the middle, i.e. 1 < 15 < 2.  */
1094             repodata_set_str(data, handle, SOLVABLE_ORDER, line + 6);
1095             break;
1096           case CTAG('=', 'I', 'c', 'o'):
1097             repodata_set_str(data, handle, SOLVABLE_ICON, line + 6);
1098             break;
1099           case CTAG('=', 'E', 'x', 't'):
1100             repodata_add_poolstr_array(data, handle, SOLVABLE_EXTENDS, join2("pattern", ":", line + 6));
1101             break;
1102           case CTAG('=', 'I', 'n', 'c'):
1103             repodata_add_poolstr_array(data, handle, SOLVABLE_INCLUDES, join2("pattern", ":", line + 6));
1104             break;
1105           case CTAG('=', 'C', 'k', 's'):
1106             set_checksum(&pd, data, handle, SOLVABLE_CHECKSUM, line + 6);
1107             break;
1108           case CTAG('=', 'L', 'a', 'n'):
1109             pd.language = solv_free(pd.language);
1110             if (line[6])
1111               pd.language = solv_strdup(line + 6);
1112             break;
1113
1114           case CTAG('=', 'F', 'l', 's'):
1115             {
1116               char *p = strrchr(line + 6, '/');
1117               Id did;
1118               if (p && p != line + 6 && !p[1])
1119                 {
1120                   *p = 0;
1121                   p = strrchr(line + 6, '/');
1122                 }
1123               if (p)
1124                 {
1125                   *p++ = 0;
1126                   did = repodata_str2dir(data, line + 6, 1);
1127                 }
1128               else
1129                 {
1130                   p = line + 6;
1131                   did = 0;
1132                 }
1133               if (!did)
1134                 did = repodata_str2dir(data, "/", 1);
1135               repodata_add_dirstr(data, handle, SOLVABLE_FILELIST, did, p);
1136               break;
1137             }
1138           case CTAG('=', 'H', 'd', 'r'):
1139             /* rpm header range */
1140             if (split(line + 6, sp, 3) == 2)
1141               {
1142                 /* we ignore the start value */
1143                 unsigned int end = (unsigned int)atoi(sp[1]);
1144                 if (end)
1145                   repodata_set_num(data, handle, SOLVABLE_HEADEREND, end);
1146               }
1147             break;
1148
1149           case CTAG('=', 'P', 'a', 't'):
1150           case CTAG('=', 'P', 'k', 'g'):
1151             break;
1152
1153           default:
1154             pool_debug(pool, SOLV_ERROR, "susetags: unknown line: %d: %s\n", pd.lineno, line);
1155             break;
1156         }
1157
1158     } /* for(;;) */
1159
1160   if (s)
1161     finish_solvable(&pd, s, handle, freshens);
1162
1163   /* Shared attributes
1164    *  (e.g. multiple binaries built from same source)
1165    */
1166   if (pd.nshare)
1167     {
1168       int i, last_found;
1169       Map keyidmap;
1170
1171       map_init(&keyidmap, data->nkeys);
1172       for (i = 1; i < data->nkeys; i++)
1173         {
1174           Id keyname = data->keys[i].name;
1175           if (keyname == SOLVABLE_INSTALLSIZE || keyname == SOLVABLE_DISKUSAGE || keyname == SOLVABLE_FILELIST)
1176             continue;
1177           if (keyname == SOLVABLE_MEDIADIR || keyname == SOLVABLE_MEDIAFILE || keyname == SOLVABLE_MEDIANR)
1178             continue;
1179           if (keyname == SOLVABLE_DOWNLOADSIZE || keyname == SOLVABLE_CHECKSUM)
1180             continue;
1181           if (keyname == SOLVABLE_SOURCENAME || keyname == SOLVABLE_SOURCEARCH || keyname == SOLVABLE_SOURCEEVR)
1182             continue;
1183           if (keyname == SOLVABLE_PKGID || keyname == SOLVABLE_HDRID || keyname == SOLVABLE_LEADSIGID)
1184             continue;
1185           if (keyname == SUSETAGS_SHARE_NAME || keyname == SUSETAGS_SHARE_EVR || keyname == SUSETAGS_SHARE_ARCH)
1186             continue;
1187           MAPSET(&keyidmap, i);
1188         }
1189       last_found = 0;
1190       for (i = 0; i < pd.nshare; i++)
1191         {
1192           unsigned n, nn;
1193           Solvable *found = 0;
1194           if (!pd.share_with[i].name)
1195             continue;
1196           for (n = repo->start, nn = repo->start + last_found; n < repo->end; n++, nn++)
1197             {
1198               if (nn >= repo->end)
1199                 nn = repo->start;
1200               found = pool->solvables + nn;
1201               if (found->repo == repo
1202                   && found->name == pd.share_with[i].name
1203                   && found->evr == pd.share_with[i].evr
1204                   && found->arch == pd.share_with[i].arch)
1205                 {
1206                   last_found = nn - repo->start;
1207                   break;
1208                 }
1209             }
1210           if (n != repo->end)
1211             repodata_merge_some_attrs(data, repo->start + i, repo->start + last_found, &keyidmap, 0);
1212         }
1213       free(pd.share_with);
1214       map_free(&keyidmap);
1215     }
1216
1217   solv_free(joinhash);
1218   if (!(flags & REPO_NO_INTERNALIZE))
1219     repodata_internalize(data);
1220
1221   solv_free(pd.language);
1222   free(line);
1223   join_freemem();
1224   return 0;
1225 }