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