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