- implement product handling using buddy packages
[platform/upstream/libsolv.git] / tools / repo_rpmmd.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 #include <expat.h>
15
16 #include "pool.h"
17 #include "repo.h"
18 #define DISABLE_SPLIT
19 #include "tools_util.h"
20 #include "repo_rpmmd.h"
21
22
23 enum state {
24   STATE_START,
25   STATE_METADATA,
26   STATE_SOLVABLE,
27   STATE_PRODUCT,
28   STATE_PATTERN,
29   STATE_PATCH,
30   STATE_NAME,
31   STATE_ARCH,
32   STATE_VERSION,
33
34   // package rpm-md
35   STATE_LOCATION,
36   STATE_CHECKSUM,
37   STATE_RPM_GROUP,
38   STATE_RPM_LICENSE,
39
40   /* resobject attributes */
41   STATE_SUMMARY,
42   STATE_DESCRIPTION,
43   STATE_DISTRIBUTION,
44   STATE_PACKAGER,
45   STATE_URL,
46   STATE_INSNOTIFY,
47   STATE_DELNOTIFY,
48   STATE_VENDOR,
49   STATE_SIZE,
50   STATE_TIME,
51   STATE_DOWNLOADSIZE,
52   STATE_INSTALLTIME,
53   STATE_INSTALLONLY,
54   
55   /* patch */
56   STATE_ID,
57   STATE_TIMESTAMP,
58   STATE_AFFECTSPKG,
59   STATE_REBOOTNEEDED,
60
61   // pattern attributes
62   STATE_CATEGORY, /* pattern and patches */
63   STATE_SCRIPT,
64   STATE_ICON,
65   STATE_USERVISIBLE,
66   STATE_DEFAULT,
67   STATE_INSTALL_TIME,
68
69   /* product */
70   STATE_SHORTNAME,
71   STATE_DISTNAME, // obsolete
72   STATE_DISTEDITION, // obsolete
73   STATE_SOURCE,
74   STATE_TYPE,
75   STATE_RELNOTESURL,
76   STATE_UPDATEURL,
77   STATE_OPTIONALURL,
78   STATE_FLAG,
79   STATE_FLAVOR,
80   STATE_REFERENCES,
81
82   /* rpm-md dependencies inside the
83      format tag */
84   STATE_PROVIDES,
85   STATE_REQUIRES,
86   STATE_OBSOLETES,
87   STATE_CONFLICTS,
88   STATE_RECOMMENDS,
89   STATE_SUPPLEMENTS,
90   STATE_SUGGESTS,
91   STATE_ENHANCES,
92   STATE_FRESHENS,
93   STATE_SOURCERPM,
94   STATE_HEADERRANGE,
95
96   STATE_PROVIDESENTRY,
97   STATE_REQUIRESENTRY,
98   STATE_OBSOLETESENTRY,
99   STATE_CONFLICTSENTRY,
100   STATE_RECOMMENDSENTRY,
101   STATE_SUPPLEMENTSENTRY,
102   STATE_SUGGESTSENTRY,
103   STATE_ENHANCESENTRY,
104   STATE_FRESHENSENTRY,
105
106   STATE_FILE,
107
108   // general
109   NUMSTATES
110 };
111
112 struct stateswitch {
113   enum state from;
114   char *ename;
115   enum state to;
116   int docontent;
117 };
118
119 static struct stateswitch stateswitches[] = {
120
121   { STATE_START,       "product",         STATE_SOLVABLE, 0 },
122   { STATE_START,       "pattern",         STATE_SOLVABLE, 0 },
123   { STATE_START,       "patch",           STATE_SOLVABLE, 0 },
124   { STATE_START,       "package",         STATE_SOLVABLE, 0 },
125   
126   { STATE_SOLVABLE,    "name",            STATE_NAME, 1 },
127   { STATE_SOLVABLE,    "arch",            STATE_ARCH, 1 },
128   { STATE_SOLVABLE,    "version",         STATE_VERSION, 0 },
129
130   // package attributes rpm-md
131   { STATE_SOLVABLE,    "location",        STATE_LOCATION, 0 },
132   { STATE_SOLVABLE,    "checksum",        STATE_CHECKSUM, 1 },
133   
134   /* resobject attributes */
135
136   { STATE_SOLVABLE,    "summary",         STATE_SUMMARY,      1 },
137   { STATE_SOLVABLE,    "description",     STATE_DESCRIPTION,  1 },
138   { STATE_SOLVABLE,    "distribution",    STATE_DISTRIBUTION, 1 },
139   { STATE_SOLVABLE,    "url",             STATE_URL,          1 },
140   { STATE_SOLVABLE,    "packager",        STATE_PACKAGER,     1 },
141   //{ STATE_SOLVABLE,    "???",         STATE_INSNOTIFY, 1 },
142   //{ STATE_SOLVABLE,    "??",     STATE_DELNOTIFY, 1 },
143   { STATE_SOLVABLE,    "vendor",          STATE_VENDOR,       1 },
144   { STATE_SOLVABLE,    "size",            STATE_SIZE,         0 },
145   { STATE_SOLVABLE,    "archive-size",    STATE_DOWNLOADSIZE, 1 },
146   { STATE_SOLVABLE,    "install-time",    STATE_INSTALLTIME,  1 },
147   { STATE_SOLVABLE,    "install-only",    STATE_INSTALLONLY,  1 },
148   { STATE_SOLVABLE,    "time",            STATE_TIME,         0 },
149
150   // pattern attribute
151   { STATE_SOLVABLE,    "script",          STATE_SCRIPT,        1 },
152   { STATE_SOLVABLE,    "icon",            STATE_ICON,          1 },
153   { STATE_SOLVABLE,    "uservisible",     STATE_USERVISIBLE,   1 },
154   { STATE_SOLVABLE,    "category",        STATE_CATEGORY,      1 },
155   { STATE_SOLVABLE,    "default",         STATE_DEFAULT,       1 },
156   { STATE_SOLVABLE,    "install-time",    STATE_INSTALL_TIME,  1 },
157
158   /* product attributes */
159   /* note the product type is an attribute */
160   { STATE_SOLVABLE,    "release-notes-url", STATE_RELNOTESURL, 1 },
161   { STATE_SOLVABLE,    "update-url",        STATE_UPDATEURL,   1 },
162   { STATE_SOLVABLE,    "optional-url",      STATE_OPTIONALURL, 1 },
163   { STATE_SOLVABLE,    "flag",              STATE_FLAG,        1 },
164   { STATE_SOLVABLE,    "flavor",            STATE_FLAVOR,      1 },
165   { STATE_SOLVABLE,    "references",        STATE_REFERENCES,  1 },
166
167   { STATE_SOLVABLE,      "rpm:vendor",      STATE_VENDOR,      1 },
168   { STATE_SOLVABLE,      "rpm:group",       STATE_RPM_GROUP,   1 },
169   { STATE_SOLVABLE,      "rpm:license",     STATE_RPM_LICENSE, 1 },
170
171   /* rpm-md dependencies */ 
172   { STATE_SOLVABLE,      "rpm:provides",    STATE_PROVIDES,     0 },
173   { STATE_SOLVABLE,      "rpm:requires",    STATE_REQUIRES,     0 },
174   { STATE_SOLVABLE,      "rpm:obsoletes",   STATE_OBSOLETES,    0 },
175   { STATE_SOLVABLE,      "rpm:conflicts",   STATE_CONFLICTS,    0 },
176   { STATE_SOLVABLE,      "rpm:recommends",  STATE_RECOMMENDS ,  0 },
177   { STATE_SOLVABLE,      "rpm:supplements", STATE_SUPPLEMENTS,  0 },
178   { STATE_SOLVABLE,      "rpm:suggests",    STATE_SUGGESTS,     0 },
179   { STATE_SOLVABLE,      "rpm:enhances",    STATE_ENHANCES,     0 },
180   { STATE_SOLVABLE,      "rpm:freshens",    STATE_FRESHENS,     0 },
181   { STATE_SOLVABLE,      "rpm:sourcerpm",   STATE_SOURCERPM,    1 },
182   { STATE_SOLVABLE,      "rpm:header-range", STATE_HEADERRANGE, 0 },
183   { STATE_SOLVABLE,      "file",            STATE_FILE, 1 },
184   
185   { STATE_PROVIDES,    "rpm:entry",       STATE_PROVIDESENTRY, 0 },
186   { STATE_REQUIRES,    "rpm:entry",       STATE_REQUIRESENTRY, 0 },
187   { STATE_OBSOLETES,   "rpm:entry",       STATE_OBSOLETESENTRY, 0 },
188   { STATE_CONFLICTS,   "rpm:entry",       STATE_CONFLICTSENTRY, 0 },
189   { STATE_RECOMMENDS,  "rpm:entry",       STATE_RECOMMENDSENTRY, 0 },
190   { STATE_SUPPLEMENTS, "rpm:entry",       STATE_SUPPLEMENTSENTRY, 0 },
191   { STATE_SUGGESTS,    "rpm:entry",       STATE_SUGGESTSENTRY, 0 },
192   { STATE_ENHANCES,    "rpm:entry",       STATE_ENHANCESENTRY, 0 },
193   { STATE_FRESHENS,    "rpm:entry",       STATE_FRESHENSENTRY, 0 },
194
195   { NUMSTATES}
196 };
197
198
199 struct parsedata {
200   struct parsedata_common common;
201   char *kind;
202   int depth;
203   enum state state;
204   int statedepth;
205   char *content;
206   int lcontent;
207   int acontent;
208   int docontent;
209   Solvable *solvable;
210   Offset freshens;
211   struct stateswitch *swtab[NUMSTATES];
212   enum state sbtab[NUMSTATES];
213   const char *lang;
214   const char *capkind;
215   // used to store tmp attributes
216   // while the tag ends
217   const char *tmpattr;
218   Repodata *data;
219   Id handle;
220   XML_Parser *parser;
221   Id (*dirs)[3]; // dirid, size, nfiles
222   int ndirs;
223   Id langcache[ID_NUM_INTERNAL];
224 };
225
226
227 static Id
228 makeevr_atts(Pool *pool, struct parsedata *pd, const char **atts)
229 {
230   const char *e, *v, *r, *v2;
231   char *c;
232   int l;
233
234   e = v = r = 0;
235   for (; *atts; atts += 2)
236     {
237       if (!strcmp(*atts, "epoch"))
238         e = atts[1];
239       else if (!strcmp(*atts, "ver"))
240         v = atts[1];
241       else if (!strcmp(*atts, "rel"))
242         r = atts[1];
243     }
244   if (e && !strcmp(e, "0"))
245     e = 0;
246   if (v && !e)
247     {
248       for (v2 = v; *v2 >= '0' && *v2 <= '9'; v2++)
249         ;
250       if (v2 > v && *v2 == ':')
251         e = "0";
252     }
253   l = 1;
254   if (e)
255     l += strlen(e) + 1;
256   if (v)
257     l += strlen(v);
258   if (r)
259     l += strlen(r) + 1;
260   if (l > pd->acontent)
261     {
262       pd->content = sat_realloc(pd->content, l + 256);
263       pd->acontent = l + 256;
264     }
265   c = pd->content;
266   if (e)
267     {
268       strcpy(c, e);
269       c += strlen(c);
270       *c++ = ':';
271     }
272   if (v)
273     {
274       strcpy(c, v);
275       c += strlen(c);
276     }
277   if (r)
278     {
279       *c++ = '-';
280       strcpy(c, r);
281       c += strlen(c);
282     }
283   *c = 0;
284   if (!*pd->content)
285     return 0;
286 #if 0
287   fprintf(stderr, "evr: %s\n", pd->content);
288 #endif
289   return str2id(pool, pd->content, 1);
290 }
291
292 static inline const char *
293 find_attr(const char *txt, const char **atts)
294 {
295   for (; *atts; atts += 2)
296     {
297       if (!strcmp(*atts, txt))
298         return atts[1];
299     }
300   return 0;
301 }
302
303 static char *flagtab[] = {
304   "GT",
305   "EQ",
306   "GE",
307   "LT",
308   "NE",
309   "LE"
310 };
311
312 static unsigned int
313 adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, const char **atts, int isreq)
314 {
315   Id id, name, marker;
316   const char *n, *f, *k;
317   const char **a;
318
319   n = f = k = 0;
320   marker = isreq ? -SOLVABLE_PREREQMARKER : 0;
321   for (a = atts; *a; a += 2)
322     {
323       if (!strcmp(*a, "name"))
324         n = a[1];
325       else if (!strcmp(*a, "flags"))
326         f = a[1];
327       else if (!strcmp(*a, "kind"))
328         k = a[1];
329       else if (isreq && !strcmp(*a, "pre") && a[1][0] == '1')
330         marker = SOLVABLE_PREREQMARKER;
331     }
332   if (!n)
333     return olddeps;
334   if (k && !strcmp(k, "package"))
335     k = 0;
336   if (k)
337     {
338       int l = strlen(k) + 1 + strlen(n) + 1;
339       if (l > pd->acontent)
340         {
341           pd->content = sat_realloc(pd->content, l + 256);
342           pd->acontent = l + 256;
343         }
344       sprintf(pd->content, "%s:%s", k, n); 
345       name = str2id(pool, pd->content, 1); 
346     }
347   else
348     name = str2id(pool, (char *)n, 1);
349   if (f)
350     {
351       Id evr = makeevr_atts(pool, pd, atts);
352       int flags;
353       for (flags = 0; flags < 6; flags++)
354         if (!strcmp(f, flagtab[flags]))
355           break;
356       flags = flags < 6 ? flags + 1 : 0;
357       id = rel2id(pool, name, evr, flags, 1);
358     }
359   else
360     id = name;
361 #if 0
362   fprintf(stderr, "new dep %s%s%s\n", id2str(pool, d), id2rel(pool, d), id2evr(pool, d));
363 #endif
364   return repo_addid_dep(pd->common.repo, olddeps, id, marker);
365 }
366
367 static void
368 set_desciption_author(Repodata *data, Id handle, char *str)
369 {
370   char *aut, *p;
371
372   if (!str || !*str)
373     return;
374   for (aut = str; (aut = strchr(aut, '\n')) != 0; aut++)
375     if (!strncmp(aut, "\nAuthors:\n--------\n", 19)) 
376       break;
377   if (aut)
378     {
379       /* oh my, found SUSE special author section */
380       int l = aut - str; 
381       str[l] = 0; 
382       while (l > 0 && str[l - 1] == '\n')
383         str[--l] = 0; 
384       if (l)
385         repodata_set_str(data, handle, SOLVABLE_DESCRIPTION, str);
386       p = aut + 19;
387       aut = str;        /* copy over */
388       while (*p == ' ' || *p == '\n')
389         p++;
390       while (*p) 
391         {
392           if (*p == '\n')
393             {
394               *aut++ = *p++;
395               while (*p == ' ') 
396                 p++;
397               continue;
398             }
399           *aut++ = *p++;
400         }
401       while (aut != str && aut[-1] == '\n')
402         aut--;
403       *aut = 0; 
404       if (*str)
405         repodata_set_str(data, handle, SOLVABLE_AUTHORS, str);
406     }
407   else if (*str)
408     repodata_set_str(data, handle, SOLVABLE_DESCRIPTION, str);
409 }
410
411 static void
412 set_sourcerpm(Repodata *data, Solvable *s, Id handle, char *sourcerpm)
413 {
414   const char *p, *sevr, *sarch, *name, *evr;
415   Pool *pool;
416
417   p = strrchr(sourcerpm, '.');
418   if (!p || strcmp(p, ".rpm") != 0)
419     return;
420   p--;
421   while (p > sourcerpm && *p != '.')
422     p--;
423   if (*p != '.' || p == sourcerpm)
424     return;
425   sarch = p-- + 1;
426   while (p > sourcerpm && *p != '-')
427     p--;
428   if (*p != '-' || p == sourcerpm)
429     return;
430   p--;
431   while (p > sourcerpm && *p != '-')
432     p--;
433   if (*p != '-' || p == sourcerpm)
434     return;
435   sevr = p + 1;
436   pool = s->repo->pool;
437   name = id2str(pool, s->name);
438   evr = id2str(pool, s->evr);
439   if (!strcmp(sarch, "src.rpm"))
440     repodata_set_constantid(data, handle, SOLVABLE_SOURCEARCH, ARCH_SRC);
441   else if (!strcmp(sarch, "nosrc.rpm"))
442     repodata_set_constantid(data, handle, SOLVABLE_SOURCEARCH, ARCH_NOSRC);
443   else
444     repodata_set_constantid(data, handle, SOLVABLE_SOURCEARCH, strn2id(pool, sarch, strlen(sarch) - 4, 1));
445   if (!strncmp(sevr, evr, sarch - sevr - 1) && evr[sarch - sevr - 1] == 0)
446     repodata_set_void(data, handle, SOLVABLE_SOURCEEVR);
447   else
448     repodata_set_id(data, handle, SOLVABLE_SOURCEEVR, strn2id(pool, sevr, sarch - sevr - 1, 1));
449   if (!strncmp(sourcerpm, name, sevr - sourcerpm - 1) && name[sevr - sourcerpm -
450  1] == 0)
451     repodata_set_void(data, handle, SOLVABLE_SOURCENAME);
452   else
453     repodata_set_id(data, handle, SOLVABLE_SOURCENAME, strn2id(pool, sourcerpm, sevr - sourcerpm - 1, 1));
454 }
455
456 static void XMLCALL
457 startElement(void *userData, const char *name, const char **atts)
458 {
459   //fprintf(stderr,"+tag: %s\n", name);
460   struct parsedata *pd = userData;
461   Pool *pool = pd->common.pool;
462   Solvable *s = pd->solvable;
463   struct stateswitch *sw;
464   const char *str;
465   Id handle = pd->handle;
466
467   // fprintf(stderr, "into %s, from %d, depth %d, statedepth %d\n", name, pd->state, pd->depth, pd->statedepth);
468
469   if (pd->depth != pd->statedepth)
470     {
471       pd->depth++;
472       return;
473     }
474
475   if (pd->state == STATE_START && !strcmp(name, "patterns"))
476     return;
477   if (pd->state == STATE_START && !strcmp(name, "metadata"))
478     return;
479   if (pd->state == STATE_SOLVABLE && !strcmp(name, "format"))
480     return;
481
482   pd->depth++;
483   for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)
484     if (!strcmp(sw->ename, name))
485       break;
486   if (sw->from != pd->state)
487     {
488 #if 0
489       fprintf(stderr, "into unknown: %s\n", name);
490 #endif
491       return;
492     }
493   pd->state = sw->to;
494   pd->docontent = sw->docontent;
495   pd->statedepth = pd->depth;
496   pd->lcontent = 0;
497   *pd->content = 0;
498   switch(pd->state)
499     {
500     case STATE_SOLVABLE:
501       pd->kind = 0;
502       if (name[2] == 't' && name[3] == 't')
503         pd->kind = "pattern";
504       else if (name[1] == 'r')
505         pd->kind = "product";
506       else if (name[2] == 't' && name[3] == 'c')
507         pd->kind = "patch";
508       
509       /* this is a new package */
510       /*fprintf(stderr, "new package\n");*/
511       pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->common.repo));
512       pd->freshens = 0;
513       repodata_extend(pd->data, pd->solvable - pool->solvables);
514       pd->handle = repodata_get_handle(pd->data, (pd->solvable - pool->solvables) - pd->data->start);
515 #if 0
516       fprintf(stderr, "package #%d\n", pd->solvable - pool->solvables);
517 #endif
518       break;
519     case STATE_VERSION:
520       s->evr = makeevr_atts(pool, pd, atts);
521       break;
522     case STATE_PROVIDES:
523       s->provides = 0;
524       break;
525     case STATE_PROVIDESENTRY:
526       s->provides = adddep(pool, pd, s->provides, atts, 0);
527       break;
528     case STATE_REQUIRES:
529       s->requires = 0;
530       break;
531     case STATE_REQUIRESENTRY:
532       s->requires = adddep(pool, pd, s->requires, atts, 1);
533       break;
534     case STATE_OBSOLETES:
535       s->obsoletes = 0;
536       break;
537     case STATE_OBSOLETESENTRY:
538       s->obsoletes = adddep(pool, pd, s->obsoletes, atts, 0);
539       break;
540     case STATE_CONFLICTS:
541       s->conflicts = 0;
542       break;
543     case STATE_CONFLICTSENTRY:
544       s->conflicts = adddep(pool, pd, s->conflicts, atts, 0);
545       break;
546     case STATE_RECOMMENDS:
547       s->recommends = 0;
548       break;
549     case STATE_RECOMMENDSENTRY:
550       s->recommends = adddep(pool, pd, s->recommends, atts, 0);
551       break;
552     case STATE_SUPPLEMENTS:
553       s->supplements= 0;
554       break;
555     case STATE_SUPPLEMENTSENTRY:
556       s->supplements = adddep(pool, pd, s->supplements, atts, 0);
557       break;
558     case STATE_SUGGESTS:
559       s->suggests = 0;
560       break;
561     case STATE_SUGGESTSENTRY:
562       s->suggests = adddep(pool, pd, s->suggests, atts, 0);
563       break;
564     case STATE_ENHANCES:
565       s->enhances = 0;
566       break;
567     case STATE_ENHANCESENTRY:
568       s->enhances = adddep(pool, pd, s->enhances, atts, 0);
569       break;
570     case STATE_FRESHENS:
571       pd->freshens = 0;
572       break;
573     case STATE_FRESHENSENTRY:
574       pd->freshens = adddep(pool, pd, pd->freshens, atts, 0);
575       break;
576     case STATE_SUMMARY:
577     case STATE_DESCRIPTION:
578       pd->lang = find_attr("lang", atts);
579       break;
580     case STATE_LOCATION:
581       str = find_attr("href", atts);
582       if (str)
583         {
584           const char *str2 = strrchr(str, '/');
585           if (str2)
586             {
587               char *str3 = strdup(str);
588               str3[str2 - str] = 0;
589               repodata_set_poolstr(pd->data, handle, SOLVABLE_MEDIADIR, str3);
590               free(str3);
591               repodata_set_str(pd->data, handle, SOLVABLE_MEDIAFILE, str2 + 1);
592             }
593           else
594             repodata_set_str(pd->data, handle, SOLVABLE_MEDIAFILE, str);
595         }
596       break;
597     case STATE_CHECKSUM:
598       pd->tmpattr = find_attr("type", atts);
599       break;
600     case STATE_TIME:
601       {
602         unsigned int t;
603         str = find_attr("build", atts);
604         if (str && (t = atoi(str)) != 0)
605           repodata_set_num(pd->data, handle, SOLVABLE_BUILDTIME, t);
606         break;
607       }
608     case STATE_SIZE:
609       {
610         unsigned int k;
611         str = find_attr("installed", atts);
612         if (str && (k = atoi(str)) != 0)
613           repodata_set_num(pd->data, handle, SOLVABLE_INSTALLSIZE, (k + 1023) / 1024);
614         /* XXX the "package" attribute gives the size of the rpm file,
615            i.e. the download size.  Except on packman, there it seems to be
616            something else entirely, it has a value near to the other two
617            values, as if the rpm is uncompressed.  */
618         str = find_attr("package", atts);
619         if (str && (k = atoi(str)) != 0)
620           repodata_set_num(pd->data, handle, SOLVABLE_DOWNLOADSIZE, (k + 1023) / 1024);
621         break;
622       }
623     case STATE_HEADERRANGE:
624       {
625         unsigned int end;
626         str = find_attr("end", atts);
627         if (str && (end = atoi(str)) != 0)
628           repodata_set_num(pd->data, handle, SOLVABLE_HEADEREND, end);
629       }
630     default:
631       break;
632     }
633 }
634
635 static void XMLCALL
636 endElement(void *userData, const char *name)
637 {
638   //fprintf(stderr,"-tag: %s\n", name);
639   struct parsedata *pd = userData;
640   Pool *pool = pd->common.pool;
641   Solvable *s = pd->solvable;
642   Repo *repo = pd->common.repo;
643   Id handle = pd->handle;
644   Id id;
645   char *p;
646
647   if (pd->depth != pd->statedepth)
648     {
649       pd->depth--;
650       // printf("back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
651       return;
652     }
653
654   /* ignore patterns & metadata */
655   if (pd->state == STATE_START && !strcmp(name, "patterns"))
656     return;
657   if (pd->state == STATE_START && !strcmp(name, "metadata"))
658     return;
659   if (pd->state == STATE_SOLVABLE && !strcmp(name, "format"))
660     return;
661
662   pd->depth--;
663   pd->statedepth--;
664   switch (pd->state)
665     {
666     case STATE_PATTERN:
667     case STATE_PRODUCT:
668     case STATE_SOLVABLE:
669       if (!s->arch)
670         s->arch = ARCH_NOARCH;
671       if (!s->evr)
672         s->evr = ID_EMPTY;      /* some patterns have this */
673       if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
674         s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
675       s->supplements = repo_fix_legacy(repo, s->provides, s->supplements, pd->freshens);
676       pd->freshens = 0;
677       pd->kind = 0;
678       break;
679     case STATE_NAME:
680       if ( pd->kind )
681           s->name = str2id(pool, join2( pd->kind, ":", pd->content), 1);
682       else
683           s->name = str2id(pool, pd->content, 1);
684       break;
685     case STATE_ARCH:
686       s->arch = str2id(pool, pd->content, 1);
687       break;
688     case STATE_VENDOR:
689       s->vendor = str2id(pool, pd->content, 1);
690       break;
691     case STATE_RPM_GROUP:
692       repodata_set_poolstr(pd->data, handle, SOLVABLE_GROUP, pd->content);
693       break;
694     case STATE_RPM_LICENSE:
695       repodata_set_poolstr(pd->data, handle, SOLVABLE_LICENSE, pd->content);
696       break;
697     case STATE_CHECKSUM:
698       { 
699         int l;
700         Id type;
701         if (!strcasecmp (pd->tmpattr, "sha") || !strcasecmp (pd->tmpattr, "sha1"))
702           l = SIZEOF_SHA1 * 2, type = REPOKEY_TYPE_SHA1;
703         else if (!strcasecmp (pd->tmpattr, "md5"))
704           l = SIZEOF_MD5 * 2, type = REPOKEY_TYPE_MD5;
705         else
706           {
707             fprintf(stderr, "Unknown checksum type: %d: %s\n", (unsigned int)XML_GetCurrentLineNumber(*pd->parser), pd->tmpattr);
708             exit(1);
709           }
710         if (strlen(pd->content) != l)
711           {
712             fprintf(stderr, "Invalid checksum length: %d: for %s\n", (unsigned int)XML_GetCurrentLineNumber(*pd->parser), pd->tmpattr);
713             exit(1);
714           }
715         repodata_set_checksum(pd->data, handle, SOLVABLE_CHECKSUM, type, pd->content);
716       }
717       break;
718     case STATE_FILE:
719 #if 0
720       id = str2id(pool, pd->content, 1);
721       s->provides = repo_addid_dep(repo, s->provides, id, SOLVABLE_FILEMARKER);
722 #endif
723       if ((p = strrchr(pd->content, '/')) != 0)
724         {
725           *p++ = 0;
726           id = repodata_str2dir(pd->data, pd->content, 1);
727         }
728       else
729         {
730           p = pd->content;
731           id = 0;
732         }
733       if (!id)
734         id = repodata_str2dir(pd->data, "/", 1);
735       repodata_add_dirstr(pd->data, handle, SOLVABLE_FILELIST, id, p);
736       break;
737     case STATE_SUMMARY:
738       pd->lang = 0;
739       repodata_set_str(pd->data, handle, SOLVABLE_SUMMARY, pd->content);
740       break;
741     case STATE_DESCRIPTION:
742       pd->lang = 0;
743       set_desciption_author(pd->data, handle, pd->content);
744       break;
745     case STATE_DISTRIBUTION:
746         repodata_set_poolstr(pd->data, handle, SOLVABLE_DISTRIBUTION, pd->content);
747         break;
748     case STATE_URL:
749       if (pd->content[0])
750         repodata_set_str(pd->data, handle, SOLVABLE_URL, pd->content);
751       break;
752     case STATE_PACKAGER:
753       if (pd->content[0])
754         repodata_set_poolstr(pd->data, handle, SOLVABLE_PACKAGER, pd->content);
755       break;
756     case STATE_SOURCERPM:
757       set_sourcerpm(pd->data, s, handle, pd->content);
758       break;
759     case STATE_RELNOTESURL:
760       if (pd->content[0])
761           repodata_set_poolstr(pd->data, handle, PRODUCT_RELNOTESURL, pd->content);
762       break;
763     case STATE_UPDATEURL:
764       if (pd->content[0])
765           repodata_set_poolstr(pd->data, handle, PRODUCT_EXTRAURLS, pd->content);
766       break;
767     case STATE_OPTIONALURL:
768       if (pd->content[0])
769           repodata_set_poolstr(pd->data, handle, PRODUCT_OPTIONALURLS, pd->content);
770       break;
771     case STATE_FLAG:
772       if (pd->content[0])
773           repodata_set_poolstr(pd->data, handle, PRODUCT_FLAGS, pd->content);
774       break;
775     case STATE_FLAVOR:
776       if (pd->content[0])
777         repodata_set_str(pd->data, handle, PRODUCT_FLAVOR, pd->content);
778       break;
779     case STATE_REFERENCES:
780       if (pd->content[0])
781         repodata_set_str(pd->data, handle, PRODUCT_REFERENCES, pd->content);
782       break;
783     default:
784       break;
785     }
786   pd->state = pd->sbtab[pd->state];
787   pd->docontent = 0;
788   // fprintf(stderr, "back from known %d %d %d\n", pd->state, pd->depth, pd->statedepth);
789 }
790
791 static void XMLCALL
792 characterData(void *userData, const XML_Char *s, int len)
793 {
794   struct parsedata *pd = userData;
795   int l;
796   char *c;
797
798   if (!pd->docontent)
799     return;
800   l = pd->lcontent + len + 1;
801   if (l > pd->acontent)
802     {
803       pd->content = sat_realloc(pd->content, l + 256);
804       pd->acontent = l + 256;
805     }
806   c = pd->content + pd->lcontent;
807   pd->lcontent += len;
808   while (len-- > 0)
809     *c++ = *s++;
810   *c = 0;
811 }
812
813
814 #define BUFF_SIZE 8192
815
816 void
817 repo_add_rpmmd(Repo *repo, FILE *fp, int flags)
818 {
819   Pool *pool = repo->pool;
820   struct parsedata pd;
821   char buf[BUFF_SIZE];
822   int i, l;
823   struct stateswitch *sw;
824
825   memset(&pd, 0, sizeof(pd));
826   for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
827     {
828       if (!pd.swtab[sw->from])
829         pd.swtab[sw->from] = sw;
830       pd.sbtab[sw->to] = sw->from;
831     }
832   pd.common.pool = pool;
833   pd.common.repo = repo;
834
835   pd.data = repo_add_repodata(repo, 0);
836
837   pd.content = sat_malloc(256);
838   pd.acontent = 256;
839   pd.lcontent = 0;
840   pd.common.tmp = 0;
841   pd.common.tmpl = 0;
842   pd.kind = 0;
843   XML_Parser parser = XML_ParserCreate(NULL);
844   XML_SetUserData(parser, &pd);
845   pd.parser = &parser;
846   XML_SetElementHandler(parser, startElement, endElement);
847   XML_SetCharacterDataHandler(parser, characterData);
848   for (;;)
849     {
850       l = fread(buf, 1, sizeof(buf), fp);
851       if (XML_Parse(parser, buf, l, l == 0) == XML_STATUS_ERROR)
852         {
853           fprintf(stderr, "repo_rpmmd: %s at line %u:%u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
854           exit(1);
855         }
856       if (l == 0)
857         break;
858     }
859   XML_ParserFree(parser);
860
861   if (pd.data)
862     repodata_internalize(pd.data);
863   sat_free(pd.content);
864   join_freemem();
865 }