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