add column to error output
[platform/upstream/libsolv.git] / tools / repo_patchxml.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 "repo_patchxml.h"
19 #include "repo_rpmmd.h"
20
21 //#define TESTMM
22
23 enum state {
24   STATE_START,
25   STATE_PATCH,
26   STATE_ATOM,
27   STATE_NAME,
28   STATE_ARCH,
29   STATE_VERSION,
30   STATE_PKGFILES,
31   STATE_DELTARPM,
32   STATE_DLOCATION,
33   STATE_DCHECKSUM,
34   STATE_DTIME,
35   STATE_DSIZE,
36   STATE_DBASEVERSION,
37   STATE_REQUIRES,
38   STATE_REQUIRESENTRY,
39   STATE_PROVIDES,
40   STATE_PROVIDESENTRY,
41   STATE_OBSOLETES,
42   STATE_OBSOLETESENTRY,
43   STATE_CONFLICTS,
44   STATE_CONFLICTSENTRY,
45   STATE_RECOMMENDS,
46   STATE_RECOMMENDSENTRY,
47   STATE_SUPPLEMENTS,
48   STATE_SUPPLEMENTSENTRY,
49   STATE_SUGGESTS,
50   STATE_SUGGESTSENTRY,
51   STATE_ENHANCES,
52   STATE_ENHANCESENTRY,
53   STATE_FRESHENS,
54   STATE_FRESHENSENTRY,
55   NUMSTATES
56 };
57
58
59 struct stateswitch {
60   enum state from;
61   char *ename;
62   enum state to;
63   int docontent;
64 };
65
66 static struct stateswitch stateswitches[] = {
67   { STATE_START,       "patch",           STATE_PATCH, 0 },
68   { STATE_START,       "package",         STATE_ATOM, 0 },
69   { STATE_START,       "patches",         STATE_START, 0},
70   { STATE_PATCH,       "yum:name",        STATE_NAME, 1 },
71   { STATE_PATCH,       "yum:arch",        STATE_ARCH, 1 },
72   { STATE_PATCH,       "yum:version",     STATE_VERSION, 0 },
73   { STATE_PATCH,       "name",            STATE_NAME, 1 },
74   { STATE_PATCH,       "arch",            STATE_ARCH, 1 },
75   { STATE_PATCH,       "version",         STATE_VERSION, 0 },
76   { STATE_PATCH,       "rpm:requires",    STATE_REQUIRES, 0 },
77   { STATE_PATCH,       "rpm:provides",    STATE_PROVIDES, 0 },
78   { STATE_PATCH,       "rpm:requires",    STATE_REQUIRES, 0 },
79   { STATE_PATCH,       "rpm:obsoletes",   STATE_OBSOLETES , 0 },
80   { STATE_PATCH,       "rpm:conflicts",   STATE_CONFLICTS , 0 },
81   { STATE_PATCH,       "rpm:recommends" , STATE_RECOMMENDS , 0 },
82   { STATE_PATCH,       "rpm:supplements", STATE_SUPPLEMENTS, 0 },
83   { STATE_PATCH,       "rpm:suggests",    STATE_SUGGESTS, 0 },
84   { STATE_PATCH,       "rpm:enhances",    STATE_ENHANCES, 0 },
85   { STATE_PATCH,       "rpm:freshens",    STATE_FRESHENS, 0 },
86   { STATE_PATCH,       "suse:freshens",   STATE_FRESHENS, 0 },
87   { STATE_PATCH,       "atoms",           STATE_START, 0 },
88   { STATE_PATCH,       "pkgfiles",        STATE_PKGFILES, 0 },
89   { STATE_PKGFILES,    "deltarpm",        STATE_DELTARPM, 0 },
90   { STATE_PKGFILES,    "patchrpm",        STATE_DELTARPM, 0 },
91   { STATE_DELTARPM,    "location",        STATE_DLOCATION, 0 },
92   { STATE_DELTARPM,    "checksum",        STATE_DCHECKSUM, 1 },
93   { STATE_DELTARPM,    "time",            STATE_DTIME, 0 },
94   { STATE_DELTARPM,    "size",            STATE_DSIZE, 0 },
95   { STATE_DELTARPM,    "base-version",    STATE_DBASEVERSION, 0 },
96   { STATE_PROVIDES,    "rpm:entry",       STATE_PROVIDESENTRY, 0 },
97   { STATE_REQUIRES,    "rpm:entry",       STATE_REQUIRESENTRY, 0 },
98   { STATE_OBSOLETES,   "rpm:entry",       STATE_OBSOLETESENTRY, 0 },
99   { STATE_CONFLICTS,   "rpm:entry",       STATE_CONFLICTSENTRY, 0 },
100   { STATE_RECOMMENDS,  "rpm:entry",       STATE_RECOMMENDSENTRY, 0 },
101   { STATE_SUPPLEMENTS, "rpm:entry",       STATE_SUPPLEMENTSENTRY, 0 },
102   { STATE_SUGGESTS,    "rpm:entry",       STATE_SUGGESTSENTRY, 0 },
103   { STATE_ENHANCES,    "rpm:entry",       STATE_ENHANCESENTRY, 0 },
104   { STATE_FRESHENS,    "rpm:entry",       STATE_FRESHENSENTRY, 0 },
105   { STATE_FRESHENS,    "suse:entry",      STATE_FRESHENSENTRY, 0 },
106   { NUMSTATES}
107 };
108
109 /* Cumulated info about the current deltarpm or patchrpm */
110 struct deltarpm {
111   Id locdir;
112   Id locname;
113   Id locevr;
114   Id locsuffix;
115   unsigned buildtime;
116   unsigned downloadsize, archivesize;
117   char *filechecksum;
118   /* Baseversions.  deltarpm only has one, patchrpm may have more.  */
119   Id *bevr;
120   unsigned nbevr;
121   /* If deltarpm, then this is filled.  */
122   Id seqname;
123   Id seqevr;
124   char *seqnum;
125 };
126
127 struct parsedata {
128   int depth;
129   enum state state;
130   int statedepth;
131   char *content;
132   int lcontent;
133   int acontent;
134   int docontent;
135   Pool *pool;
136   Repo *repo;
137   Solvable *solvable;
138   char *kind;
139
140   struct stateswitch *swtab[NUMSTATES];
141   enum state sbtab[NUMSTATES];
142   char *tempstr;
143   int ltemp;
144   int atemp;
145   struct deltarpm delta;
146 };
147
148 #if 0
149 static void
150 append_str(struct parsedata *pd, const char *s)
151 {
152   if (!s)
153     return;
154   int l = pd->ltemp + strlen(s) + 1;
155   if (l > pd->atemp)
156     {
157       pd->tempstr = realloc(pd->tempstr, l + 256);
158       pd->atemp = l + 256;
159     }
160   strcpy(pd->tempstr + pd->ltemp, s);
161   pd->ltemp += strlen(s);
162 }
163 #endif
164
165 static Id
166 makeevr_atts(Pool *pool, struct parsedata *pd, const char **atts)
167 {
168   const char *e, *v, *r, *v2;
169   char *c;
170   int l;
171
172   e = v = r = 0;
173   for (; *atts; atts += 2)
174     {
175       if (!strcmp(*atts, "epoch"))
176         e = atts[1];
177       else if (!strcmp(*atts, "ver"))
178         v = atts[1];
179       else if (!strcmp(*atts, "rel"))
180         r = atts[1];
181     }
182   if (e && !strcmp(e, "0"))
183     e = 0;
184   if (v && !e)
185     {
186       for (v2 = v; *v2 >= '0' && *v2 <= '9'; v2++)
187         ;
188       if (v2 > v && *v2 == ':')
189         e = "0";
190     }
191   l = 1;
192   if (e)
193     l += strlen(e) + 1;
194   if (v)
195     l += strlen(v);
196   if (r)
197     l += strlen(r) + 1;
198   if (l > pd->acontent)
199     {
200       pd->content = realloc(pd->content, l + 256);
201       pd->acontent = l + 256;
202     }
203   c = pd->content;
204   if (e)
205     {
206       strcpy(c, e);
207       c += strlen(c);
208       *c++ = ':';
209     }
210   if (v)
211     {
212       strcpy(c, v);
213       c += strlen(c);
214     }
215   if (r)
216     {
217       *c++ = '-';
218       strcpy(c, r);
219       c += strlen(c);
220     }
221   *c = 0;
222   if (!*pd->content)
223     return 0;
224 #if 0
225   fprintf(stderr, "evr: %s\n", pd->content);
226 #endif
227   return str2id(pool, pd->content, 1);
228 }
229
230 static const char *
231 find_attr(const char *txt, const char **atts)
232 {
233   for (; *atts; atts += 2)
234     {
235       if (!strcmp(*atts, txt))
236         return atts[1];
237     }
238   return 0;
239 }
240
241 static char *flagtab[] = {
242   "GT",
243   "EQ",
244   "GE",
245   "LT",
246   "NE",
247   "LE"
248 };
249
250 static unsigned int
251 adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, const char **atts, int isreq)
252 {
253   Id id, name, marker;
254   const char *n, *f, *k;
255   const char **a;
256
257   n = f = k = 0;
258   marker = isreq ? -SOLVABLE_PREREQMARKER : 0;
259   for (a = atts; *a; a += 2)
260     {
261       if (!strcmp(*a, "name"))
262         n = a[1];
263       else if (!strcmp(*a, "flags"))
264         f = a[1];
265       else if (!strcmp(*a, "kind"))
266         k = a[1];
267       else if (isreq && !strcmp(*a, "pre") && a[1][0] == '1')
268         marker = SOLVABLE_PREREQMARKER;
269     }
270   if (!n)
271     return olddeps;
272   if (k && !strcmp(k, "package"))
273     k = 0;
274   if (k)
275     {
276       int l = strlen(k) + 1 + strlen(n) + 1;
277       if (l > pd->acontent)
278         {
279           pd->content = realloc(pd->content, l + 256);
280           pd->acontent = l + 256;
281         }
282       sprintf(pd->content, "%s:%s", k, n); 
283       name = str2id(pool, pd->content, 1); 
284     }
285   else
286     name = str2id(pool, (char *)n, 1);
287   if (f)
288     {
289       Id evr = makeevr_atts(pool, pd, atts);
290       int flags;
291       for (flags = 0; flags < 6; flags++)
292         if (!strcmp(f, flagtab[flags]))
293           break;
294       flags = flags < 6 ? flags + 1 : 0;
295       id = rel2id(pool, name, evr, flags, 1);
296     }
297   else
298     id = name;
299 #if 0
300   fprintf(stderr, "new dep %s%s%s\n", id2str(pool, d), id2rel(pool, d), id2evr(pool, d));
301 #endif
302   return repo_addid_dep(pd->repo, olddeps, id, marker);
303 }
304
305
306 static void XMLCALL
307 startElement(void *userData, const char *name, const char **atts)
308 {
309   struct parsedata *pd = userData;
310   Pool *pool = pd->pool;
311   Solvable *s = pd->solvable;
312   struct stateswitch *sw;
313   const char *str;
314
315   if (pd->depth != pd->statedepth)
316     {
317       pd->depth++;
318       return;
319     }
320
321   if (pd->state == STATE_PATCH && !strcmp(name, "format"))
322     return;
323
324   pd->depth++;
325   for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)
326     if (!strcmp(sw->ename, name))
327       break;
328   if (sw->from != pd->state)
329     {
330 #if 0
331       fprintf(stderr, "into unknown: %s\n", name);
332 #endif
333       return;
334     }
335   pd->state = sw->to;
336   pd->docontent = sw->docontent;
337   pd->statedepth = pd->depth;
338   pd->lcontent = 0;
339   *pd->content = 0;
340   switch(pd->state)
341     {
342     case STATE_NAME:
343       if (pd->kind)
344         {
345           strcpy(pd->content, pd->kind);
346           pd->lcontent = strlen(pd->content);
347           pd->content[pd->lcontent++] = ':';
348           pd->content[pd->lcontent] = 0;
349         }
350       break;
351     case STATE_PATCH:
352     case STATE_ATOM:
353       if (pd->state == STATE_ATOM)
354         {
355           /* HACK: close patch */
356           if (pd->kind && !strcmp(pd->kind, "patch"))
357             {
358               if (!s->arch)
359                 s->arch = ARCH_NOARCH;
360               s->provides = repo_addid_dep(pd->repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
361             }
362           pd->kind = "atom";
363           pd->state = STATE_PATCH;
364         }
365       else
366         pd->kind = "patch";
367       
368       pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
369 #if 0
370       fprintf(stderr, "package #%d\n", pd->solvable - pool->solvables);
371 #endif
372       break;
373     case STATE_DELTARPM:
374       memset(&pd->delta, 0, sizeof (pd->delta));
375       *pd->tempstr = 0;
376       pd->ltemp = 0;
377       break;
378     case STATE_DLOCATION:
379       if ((str = find_attr("href", atts)))
380         {
381           /* Separate the filename into its different parts.
382              rpm/x86_64/alsa-1.0.14-31_31.2.x86_64.delta.rpm
383              --> dir = rpm/x86_64
384                  name = alsa
385                  evr = 1.0.14-31_31.2
386                  suffix = x86_64.delta.rpm.  */
387           char *real_str = strdup(str);
388           char *s = real_str;
389           char *s1, *s2;
390           s1 = strrchr (s, '/');
391           if (s1)
392             {
393               pd->delta.locdir = strn2id(pool, s, s1 - s, 1);
394               s = s1 + 1;
395             }
396           /* Guess suffix.  */
397           s1 = strrchr (s, '.');
398           if (s1)
399             {
400               for (s2 = s1 - 1; s2 > s; s2--)
401                 if (*s2 == '.')
402                   break;
403               if (!strcmp (s2, ".delta.rpm") || !strcmp (s2, ".patch.rpm"))
404                 {
405                   s1 = s2;
406                   /* We accept one more item as suffix.  */
407                   for (s2 = s1 - 1; s2 > s; s2--)
408                     if (*s2 == '.')
409                       break;
410                   s1 = s2;
411                 }
412               if (*s1 == '.')
413                 *s1++ = 0;
414               pd->delta.locsuffix = str2id(pool, s1, 1); 
415             }
416           /* Last '-'.  */
417           s1 = strrchr (s, '-');
418           if (s1)
419             {
420               /* Second to last '-'.  */
421               for (s2 = s1 - 1; s2 > s; s2--)
422                 if (*s2 == '-')
423                   break;
424             }
425           else
426             s2 = 0;
427           if (s2 > s && *s2 == '-')
428             {
429               *s2++ = 0;
430               pd->delta.locevr = str2id(pool, s2, 1);
431             }
432           pd->delta.locname = str2id(pool, s, 1);
433           free(real_str);
434         }
435       break;
436     case STATE_DTIME:
437       str = find_attr("build", atts);
438       if (str)
439         pd->delta.buildtime = atoi(str);
440       break;
441     case STATE_DSIZE:
442       if ((str = find_attr("package", atts)))
443         pd->delta.downloadsize = atoi(str);
444       if ((str = find_attr("archive", atts)))
445         pd->delta.archivesize = atoi(str);
446       break;
447     case STATE_DBASEVERSION:
448       if ((str = find_attr("sequence_info", atts)))
449         {
450           const char *s1, *s2;
451           s1 = strrchr(str, '-');
452           if (s1)
453             {
454               for (s2 = s1 - 1; s2 > str; s2--)
455                 if (*s2 == '-')
456                   break;
457               if (*s2 == '-')
458                 {
459                   for (s2 = s2 - 1; s2 > str; s2--)
460                     if (*s2 == '-')
461                       break;
462                   if (*s2 == '-')
463                     {
464                       pd->delta.seqevr = strn2id(pool, s2 + 1, s1 - s2 - 1, 1);
465                       pd->delta.seqname = strn2id(pool, str, s2 - str, 1);
466                       str = s1 + 1;
467                     }
468                 }
469             }
470           pd->delta.seqnum = strdup(str);
471         }
472       pd->delta.nbevr++;
473       pd->delta.bevr = sat_realloc (pd->delta.bevr, pd->delta.nbevr * sizeof(Id));
474       pd->delta.bevr[pd->delta.nbevr - 1] = makeevr_atts(pool, pd, atts);
475       break;
476     case STATE_VERSION:
477       s->evr = makeevr_atts(pool, pd, atts);
478       break;
479     case STATE_PROVIDES:
480       s->provides = 0;
481       break;
482     case STATE_PROVIDESENTRY:
483       s->provides = adddep(pool, pd, s->provides, atts, 0);
484       break;
485     case STATE_REQUIRES:
486       s->requires = 0;
487       break;
488     case STATE_REQUIRESENTRY:
489       s->requires = adddep(pool, pd, s->requires, atts, 1);
490       break;
491     case STATE_OBSOLETES:
492       s->obsoletes = 0;
493       break;
494     case STATE_OBSOLETESENTRY:
495       s->obsoletes = adddep(pool, pd, s->obsoletes, atts, 0);
496       break;
497     case STATE_CONFLICTS:
498       s->conflicts = 0;
499       break;
500     case STATE_CONFLICTSENTRY:
501       s->conflicts = adddep(pool, pd, s->conflicts, atts, 0);
502       break;
503     case STATE_RECOMMENDS:
504       s->recommends = 0;
505       break;
506     case STATE_RECOMMENDSENTRY:
507       s->recommends = adddep(pool, pd, s->recommends, atts, 0);
508       break;
509     case STATE_SUPPLEMENTS:
510       s->supplements= 0;
511       break;
512     case STATE_SUPPLEMENTSENTRY:
513       s->supplements = adddep(pool, pd, s->supplements, atts, 0);
514       break;
515     case STATE_SUGGESTS:
516       s->suggests = 0;
517       break;
518     case STATE_SUGGESTSENTRY:
519       s->suggests = adddep(pool, pd, s->suggests, atts, 0);
520       break;
521     case STATE_ENHANCES:
522       s->enhances = 0;
523       break;
524     case STATE_ENHANCESENTRY:
525       s->enhances = adddep(pool, pd, s->enhances, atts, 0);
526       break;
527     case STATE_FRESHENS:
528       s->freshens = 0;
529       break;
530     case STATE_FRESHENSENTRY:
531       s->freshens = adddep(pool, pd, s->freshens, atts, 0);
532       break;
533     default:
534       break;
535     }
536 }
537
538 static void XMLCALL
539 endElement(void *userData, const char *name)
540 {
541   struct parsedata *pd = userData;
542   Pool *pool = pd->pool;
543   Solvable *s = pd->solvable;
544
545   if (pd->depth != pd->statedepth)
546     {
547       pd->depth--;
548       // printf("back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
549       return;
550     }
551
552   if (pd->state == STATE_PATCH && !strcmp(name, "format"))
553     return;
554
555   pd->depth--;
556   pd->statedepth--;
557   switch (pd->state)
558     {
559     case STATE_PATCH:
560       if (!strcmp(name, "patch") && strcmp(pd->kind, "patch"))
561         break;  /* already closed */
562       if (!s->arch)
563         s->arch = ARCH_NOARCH;
564       if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
565         s->provides = repo_addid_dep(pd->repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
566       s->supplements = repo_fix_legacy(pd->repo, s->provides, s->supplements);
567       break;
568     case STATE_NAME:
569       s->name = str2id(pool, pd->content, 1);
570       break;
571     case STATE_ARCH:
572       s->arch = str2id(pool, pd->content, 1);
573       break;
574     case STATE_DELTARPM:
575 #ifdef TESTMM
576       {
577         int i;
578         struct deltarpm *d = &pd->delta;
579         fprintf (stderr, "found deltarpm for %s:\n", id2str(pool, s->name));
580         fprintf (stderr, "   loc: %s %s %s %s\n", id2str(pool, d->locdir),
581                  id2str(pool, d->locname), id2str(pool, d->locevr),
582                  id2str(pool, d->locsuffix));
583         fprintf (stderr, "  time: %u\n", d->buildtime);
584         fprintf (stderr, "  size: %d down, %d archive\n", d->downloadsize,
585                  d->archivesize);
586         fprintf (stderr, "  chek: %s\n", d->filechecksum);
587         if (d->seqnum)
588           {
589             fprintf (stderr, "  base: %s, seq: %s %s %s\n",
590                      id2str(pool, d->bevr[0]), id2str(pool, d->seqname),
591                      id2str(pool, d->seqevr), d->seqnum);
592             if (d->seqevr != d->bevr[0])
593               fprintf (stderr, "XXXXX evr\n");
594             /* Name of package ("atom:xxxx") should match the sequence info
595                name.  */
596             if (strcmp(id2str(pool, d->seqname), id2str(pool, s->name) + 5))
597               fprintf (stderr, "XXXXX name\n");
598           }
599         else
600           {
601             fprintf (stderr, "  base:");
602             for (i = 0; i < d->nbevr; i++)
603               fprintf (stderr, " %s", id2str(pool, d->bevr[i]));
604             fprintf (stderr, "\n");
605           }
606       }
607 #endif
608       free(pd->delta.filechecksum);
609       free(pd->delta.bevr);
610       free(pd->delta.seqnum);
611       break;
612     case STATE_DCHECKSUM:
613       pd->delta.filechecksum = strdup(pd->content);
614       break;
615     default:
616       break;
617     }
618   pd->state = pd->sbtab[pd->state];
619   pd->docontent = 0;
620   // printf("back from known %d %d %d\n", pd->state, pd->depth, pd->statedepth);
621 }
622
623 static void XMLCALL
624 characterData(void *userData, const XML_Char *s, int len)
625 {
626   struct parsedata *pd = userData;
627   int l;
628   char *c;
629
630   if (!pd->docontent)
631     return;
632   l = pd->lcontent + len + 1;
633   if (l > pd->acontent)
634     {
635       pd->content = realloc(pd->content, l + 256);
636       pd->acontent = l + 256;
637     }
638   c = pd->content + pd->lcontent;
639   pd->lcontent += len;
640   while (len-- > 0)
641     *c++ = *s++;
642   *c = 0;
643 }
644
645
646 #define BUFF_SIZE 8192
647
648 void
649 repo_add_patchxml(Repo *repo, FILE *fp, int flags)
650 {
651   Pool *pool = repo->pool;
652   struct parsedata pd;
653   char buf[BUFF_SIZE];
654   int i, l;
655   struct stateswitch *sw;
656
657   memset(&pd, 0, sizeof(pd));
658   for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
659     {
660       if (!pd.swtab[sw->from])
661         pd.swtab[sw->from] = sw;
662       pd.sbtab[sw->to] = sw->from;
663     }
664   pd.pool = pool;
665   pd.repo = repo;
666   pd.content = malloc(256);
667   pd.acontent = 256;
668   pd.lcontent = 0;
669   pd.tempstr = malloc(256);
670   pd.atemp = 256;
671   pd.ltemp = 0;
672   XML_Parser parser = XML_ParserCreate(NULL);
673   XML_SetUserData(parser, &pd);
674   XML_SetElementHandler(parser, startElement, endElement);
675   XML_SetCharacterDataHandler(parser, characterData);
676   for (;;)
677     {
678       l = fread(buf, 1, sizeof(buf), fp);
679       if (XML_Parse(parser, buf, l, l == 0) == XML_STATUS_ERROR)
680         {
681           fprintf(stderr, "repo_patchxml: %s at line %u:%u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
682           exit(1);
683         }
684       if (l == 0)
685         break;
686     }
687   XML_ParserFree(parser);
688
689   free(pd.content);
690 }