Some unfinished test code for deltarpm and patchrpm data.
[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, 0 },
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 struct parsedata {
110   int depth;
111   enum state state;
112   int statedepth;
113   char *content;
114   int lcontent;
115   int acontent;
116   int docontent;
117   Pool *pool;
118   Repo *repo;
119   Solvable *solvable;
120   char *kind;
121
122   struct stateswitch *swtab[NUMSTATES];
123   enum state sbtab[NUMSTATES];
124   char *tempstr;
125   int ltemp;
126   int atemp;
127 };
128
129 static void
130 append_str(struct parsedata *pd, const char *s)
131 {
132   if (!s)
133     return;
134   int l = pd->ltemp + strlen(s) + 1;
135   if (l > pd->atemp)
136     {
137       pd->tempstr = realloc(pd->tempstr, l + 256);
138       pd->atemp = l + 256;
139     }
140   strcpy(pd->tempstr + pd->ltemp, s);
141   pd->ltemp += strlen(s);
142 }
143
144 static Id
145 makeevr_atts(Pool *pool, struct parsedata *pd, const char **atts)
146 {
147   const char *e, *v, *r, *v2;
148   char *c;
149   int l;
150
151   e = v = r = 0;
152   for (; *atts; atts += 2)
153     {
154       if (!strcmp(*atts, "epoch"))
155         e = atts[1];
156       else if (!strcmp(*atts, "ver"))
157         v = atts[1];
158       else if (!strcmp(*atts, "rel"))
159         r = atts[1];
160     }
161   if (e && !strcmp(e, "0"))
162     e = 0;
163   if (v && !e)
164     {
165       for (v2 = v; *v2 >= '0' && *v2 <= '9'; v2++)
166         ;
167       if (v2 > v && *v2 == ':')
168         e = "0";
169     }
170   l = 1;
171   if (e)
172     l += strlen(e) + 1;
173   if (v)
174     l += strlen(v);
175   if (r)
176     l += strlen(r) + 1;
177   if (l > pd->acontent)
178     {
179       pd->content = realloc(pd->content, l + 256);
180       pd->acontent = l + 256;
181     }
182   c = pd->content;
183   if (e)
184     {
185       strcpy(c, e);
186       c += strlen(c);
187       *c++ = ':';
188     }
189   if (v)
190     {
191       strcpy(c, v);
192       c += strlen(c);
193     }
194   if (r)
195     {
196       *c++ = '-';
197       strcpy(c, r);
198       c += strlen(c);
199     }
200   *c = 0;
201   if (!*pd->content)
202     return 0;
203 #if 0
204   fprintf(stderr, "evr: %s\n", pd->content);
205 #endif
206   return str2id(pool, pd->content, 1);
207 }
208
209 static const char *
210 find_attr(const char *txt, const char **atts)
211 {
212   for (; *atts; atts += 2)
213     {
214       if (!strcmp(*atts, txt))
215         return atts[1];
216     }
217   return 0;
218 }
219
220 static char *flagtab[] = {
221   "GT",
222   "EQ",
223   "GE",
224   "LT",
225   "NE",
226   "LE"
227 };
228
229 static unsigned int
230 adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, const char **atts, int isreq)
231 {
232   Id id, name, marker;
233   const char *n, *f, *k;
234   const char **a;
235
236   n = f = k = 0;
237   marker = isreq ? -SOLVABLE_PREREQMARKER : 0;
238   for (a = atts; *a; a += 2)
239     {
240       if (!strcmp(*a, "name"))
241         n = a[1];
242       else if (!strcmp(*a, "flags"))
243         f = a[1];
244       else if (!strcmp(*a, "kind"))
245         k = a[1];
246       else if (isreq && !strcmp(*a, "pre") && a[1][0] == '1')
247         marker = SOLVABLE_PREREQMARKER;
248     }
249   if (!n)
250     return olddeps;
251   if (k && !strcmp(k, "package"))
252     k = 0;
253   if (k)
254     {
255       int l = strlen(k) + 1 + strlen(n) + 1;
256       if (l > pd->acontent)
257         {
258           pd->content = realloc(pd->content, l + 256);
259           pd->acontent = l + 256;
260         }
261       sprintf(pd->content, "%s:%s", k, n); 
262       name = str2id(pool, pd->content, 1); 
263     }
264   else
265     name = str2id(pool, (char *)n, 1);
266   if (f)
267     {
268       Id evr = makeevr_atts(pool, pd, atts);
269       int flags;
270       for (flags = 0; flags < 6; flags++)
271         if (!strcmp(f, flagtab[flags]))
272           break;
273       flags = flags < 6 ? flags + 1 : 0;
274       id = rel2id(pool, name, evr, flags, 1);
275     }
276   else
277     id = name;
278 #if 0
279   fprintf(stderr, "new dep %s%s%s\n", id2str(pool, d), id2rel(pool, d), id2evr(pool, d));
280 #endif
281   return repo_addid_dep(pd->repo, olddeps, id, marker);
282 }
283
284
285 static void XMLCALL
286 startElement(void *userData, const char *name, const char **atts)
287 {
288   struct parsedata *pd = userData;
289   Pool *pool = pd->pool;
290   Solvable *s = pd->solvable;
291   struct stateswitch *sw;
292
293   if (pd->depth != pd->statedepth)
294     {
295       pd->depth++;
296       return;
297     }
298
299   if (pd->state == STATE_PATCH && !strcmp(name, "format"))
300     return;
301
302   pd->depth++;
303   for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)
304     if (!strcmp(sw->ename, name))
305       break;
306   if (sw->from != pd->state)
307     {
308 #if 0
309       fprintf(stderr, "into unknown: %s\n", name);
310 #endif
311       return;
312     }
313   pd->state = sw->to;
314   pd->docontent = sw->docontent;
315   pd->statedepth = pd->depth;
316   pd->lcontent = 0;
317   *pd->content = 0;
318   switch(pd->state)
319     {
320     case STATE_NAME:
321       if (pd->kind)
322         {
323           strcpy(pd->content, pd->kind);
324           pd->lcontent = strlen(pd->content);
325           pd->content[pd->lcontent++] = ':';
326           pd->content[pd->lcontent] = 0;
327         }
328       break;
329     case STATE_PATCH:
330     case STATE_ATOM:
331       if (pd->state == STATE_ATOM)
332         {
333           /* HACK: close patch */
334           if (pd->kind && !strcmp(pd->kind, "patch"))
335             {
336               if (!s->arch)
337                 s->arch = ARCH_NOARCH;
338               s->provides = repo_addid_dep(pd->repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
339             }
340           pd->kind = "atom";
341           pd->state = STATE_PATCH;
342         }
343       else
344         pd->kind = "patch";
345       
346       pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
347 #if 0
348       fprintf(stderr, "package #%d\n", pd->solvable - pool->solvables);
349 #endif
350       break;
351     case STATE_DELTARPM:
352       *pd->tempstr = 0;
353       pd->ltemp = 0;
354       break;
355     case STATE_DLOCATION:
356       append_str (pd, "loc:");
357       append_str (pd, find_attr("href", atts));
358       break;
359     case STATE_DCHECKSUM:
360       append_str (pd, "chk:");
361       break;
362     case STATE_DTIME:
363       append_str (pd, "tim:");
364       break;
365     case STATE_DSIZE:
366       append_str (pd, "siz:");
367       break;
368     case STATE_DBASEVERSION:
369       append_str (pd, "bve:");
370       break;
371     case STATE_VERSION:
372       s->evr = makeevr_atts(pool, pd, atts);
373       break;
374     case STATE_PROVIDES:
375       s->provides = 0;
376       break;
377     case STATE_PROVIDESENTRY:
378       s->provides = adddep(pool, pd, s->provides, atts, 0);
379       break;
380     case STATE_REQUIRES:
381       s->requires = 0;
382       break;
383     case STATE_REQUIRESENTRY:
384       s->requires = adddep(pool, pd, s->requires, atts, 1);
385       break;
386     case STATE_OBSOLETES:
387       s->obsoletes = 0;
388       break;
389     case STATE_OBSOLETESENTRY:
390       s->obsoletes = adddep(pool, pd, s->obsoletes, atts, 0);
391       break;
392     case STATE_CONFLICTS:
393       s->conflicts = 0;
394       break;
395     case STATE_CONFLICTSENTRY:
396       s->conflicts = adddep(pool, pd, s->conflicts, atts, 0);
397       break;
398     case STATE_RECOMMENDS:
399       s->recommends = 0;
400       break;
401     case STATE_RECOMMENDSENTRY:
402       s->recommends = adddep(pool, pd, s->recommends, atts, 0);
403       break;
404     case STATE_SUPPLEMENTS:
405       s->supplements= 0;
406       break;
407     case STATE_SUPPLEMENTSENTRY:
408       s->supplements = adddep(pool, pd, s->supplements, atts, 0);
409       break;
410     case STATE_SUGGESTS:
411       s->suggests = 0;
412       break;
413     case STATE_SUGGESTSENTRY:
414       s->suggests = adddep(pool, pd, s->suggests, atts, 0);
415       break;
416     case STATE_ENHANCES:
417       s->enhances = 0;
418       break;
419     case STATE_ENHANCESENTRY:
420       s->enhances = adddep(pool, pd, s->enhances, atts, 0);
421       break;
422     case STATE_FRESHENS:
423       s->freshens = 0;
424       break;
425     case STATE_FRESHENSENTRY:
426       s->freshens = adddep(pool, pd, s->freshens, atts, 0);
427       break;
428     default:
429       break;
430     }
431 }
432
433 static void XMLCALL
434 endElement(void *userData, const char *name)
435 {
436   struct parsedata *pd = userData;
437   Pool *pool = pd->pool;
438   Solvable *s = pd->solvable;
439
440   if (pd->depth != pd->statedepth)
441     {
442       pd->depth--;
443       // printf("back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
444       return;
445     }
446
447   if (pd->state == STATE_PATCH && !strcmp(name, "format"))
448     return;
449
450   pd->depth--;
451   pd->statedepth--;
452   switch (pd->state)
453     {
454     case STATE_PATCH:
455       if (!strcmp(name, "patch") && strcmp(pd->kind, "patch"))
456         break;  /* already closed */
457       if (!s->arch)
458         s->arch = ARCH_NOARCH;
459       if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
460         s->provides = repo_addid_dep(pd->repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
461       s->supplements = repo_fix_legacy(pd->repo, s->provides, s->supplements);
462       break;
463     case STATE_NAME:
464       s->name = str2id(pool, pd->content, 1);
465       break;
466     case STATE_ARCH:
467       s->arch = str2id(pool, pd->content, 1);
468       break;
469     case STATE_DELTARPM:
470 #ifdef TESTMM
471       fprintf (stderr, "found deltarpm for %s: %s\n", id2str(pool, s->name), pd->tempstr);
472 #endif
473       break;
474     default:
475       break;
476     }
477   pd->state = pd->sbtab[pd->state];
478   pd->docontent = 0;
479   // printf("back from known %d %d %d\n", pd->state, pd->depth, pd->statedepth);
480 }
481
482 static void XMLCALL
483 characterData(void *userData, const XML_Char *s, int len)
484 {
485   struct parsedata *pd = userData;
486   int l;
487   char *c;
488
489   if (!pd->docontent)
490     return;
491   l = pd->lcontent + len + 1;
492   if (l > pd->acontent)
493     {
494       pd->content = realloc(pd->content, l + 256);
495       pd->acontent = l + 256;
496     }
497   c = pd->content + pd->lcontent;
498   pd->lcontent += len;
499   while (len-- > 0)
500     *c++ = *s++;
501   *c = 0;
502 }
503
504
505 #define BUFF_SIZE 8192
506
507 void
508 repo_add_patchxml(Repo *repo, FILE *fp)
509 {
510   Pool *pool = repo->pool;
511   struct parsedata pd;
512   char buf[BUFF_SIZE];
513   int i, l;
514   struct stateswitch *sw;
515
516   memset(&pd, 0, sizeof(pd));
517   for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
518     {
519       if (!pd.swtab[sw->from])
520         pd.swtab[sw->from] = sw;
521       pd.sbtab[sw->to] = sw->from;
522     }
523   pd.pool = pool;
524   pd.repo = repo;
525   pd.content = malloc(256);
526   pd.acontent = 256;
527   pd.lcontent = 0;
528   pd.tempstr = malloc(256);
529   pd.atemp = 256;
530   pd.ltemp = 0;
531   XML_Parser parser = XML_ParserCreate(NULL);
532   XML_SetUserData(parser, &pd);
533   XML_SetElementHandler(parser, startElement, endElement);
534   XML_SetCharacterDataHandler(parser, characterData);
535   for (;;)
536     {
537       l = fread(buf, 1, sizeof(buf), fp);
538       if (XML_Parse(parser, buf, l, l == 0) == XML_STATUS_ERROR)
539         {
540           fprintf(stderr, "%s at line %u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser));
541           exit(1);
542         }
543       if (l == 0)
544         break;
545     }
546   XML_ParserFree(parser);
547
548   free(pd.content);
549 }