- copy parser swtab fix from Matz into other files
[platform/upstream/libsolv.git] / tools / repo_deltainfoxml.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 #define DO_ARRAY 1
9
10 #define _GNU_SOURCE
11 #include <sys/types.h>
12 #include <limits.h>
13 #include <fcntl.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <expat.h>
18
19 #include "pool.h"
20 #include "repo.h"
21 #include "repo_updateinfoxml.h"
22 #include "tools_util.h"
23
24 //#define DUMPOUT 0
25
26 /*
27  * <deltainfo>
28  *   <newpackage name="libtool" epoch="0" version="1.5.24" release="6.fc9" arch="i386">
29  *     <delta oldepoch="0" oldversion="1.5.24" oldrelease="3.fc8">
30  *       <filename>DRPMS/libtool-1.5.24-3.fc8_1.5.24-6.fc9.i386.drpm</filename>
31  *       <sequence>libtool-1.5.24-3.fc8-d3571f98b048b1a870e40241bb46c67ab4</sequence>
32  *       <size>22452</size>
33  *       <checksum type="sha">8f05394695dee9399c204614e21e5f6848990ab7</checksum>
34  *     </delta>
35  *     <delta oldepoch="0" oldversion="1.5.22" oldrelease="11.fc7">
36  *       <filename>DRPMS/libtool-1.5.22-11.fc7_1.5.24-6.fc9.i386.drpm</filename>
37  *        <sequence>libtool-1.5.22-11.fc7-e82691677eee1e83b4812572c5c9ce8eb</sequence>
38  *        <size>110362</size>
39  *        <checksum type="sha">326658fee45c0baec1e70231046dbaf560f941ce</checksum>
40  *      </delta>
41  *    </newpackage>
42  *  </deltainfo>
43  */
44
45 enum state {
46   STATE_START,
47   STATE_NEWPACKAGE,     /* 1 */
48   STATE_DELTA,          /* 2 */
49   STATE_FILENAME,       /* 3 */
50   STATE_SEQUENCE,       /* 4 */
51   STATE_SIZE,           /* 5 */
52   STATE_CHECKSUM,       /* 6 */
53   STATE_LOCATION,       /* 7 */
54   NUMSTATES
55 };
56
57 struct stateswitch {
58   enum state from;
59   char *ename;
60   enum state to;
61   int docontent;
62 };
63
64 /* !! must be sorted by first column !! */
65 static struct stateswitch stateswitches[] = {
66   /* compatibility with old yum-presto */
67   { STATE_START,       "prestodelta",     STATE_START, 0 },
68   { STATE_START,       "deltainfo",       STATE_START, 0 },
69   { STATE_START,       "newpackage",      STATE_NEWPACKAGE,  0 },
70   { STATE_NEWPACKAGE,  "delta",           STATE_DELTA,       0 },
71   /* compatibility with yum-presto */
72   { STATE_DELTA,       "filename",        STATE_FILENAME,    1 },
73   { STATE_DELTA,       "location",        STATE_LOCATION,    0 },
74   { STATE_DELTA,       "sequence",        STATE_SEQUENCE,    1 },
75   { STATE_DELTA,       "size",            STATE_SIZE,        1 },
76   { STATE_DELTA,       "checksum",        STATE_CHECKSUM,    1 },
77   { NUMSTATES }
78 };
79
80 /* Cumulated info about the current deltarpm or patchrpm */
81 struct deltarpm {
82   Id locdir;
83   Id locname;
84   Id locevr;
85   Id locsuffix;
86   unsigned buildtime;
87   unsigned downloadsize, archivesize;
88   char *filechecksum;
89   
90   /* Baseversion.  deltarpm only has one. */
91   Id *bevr;
92   unsigned nbevr;
93   Id seqname;
94   Id seqevr;
95   char *seqnum;
96 };
97
98 struct parsedata {
99   int depth;
100   enum state state;
101   int statedepth;
102   char *content;
103   int lcontent;
104   int acontent;
105   int docontent;
106   Pool *pool;
107   Repo *repo;
108   Repodata *data;
109   int datanum;
110   
111   struct stateswitch *swtab[NUMSTATES];
112   enum state sbtab[NUMSTATES];
113   char *tempstr;
114   int ltemp;
115   int atemp;
116   struct deltarpm delta;
117   Id newpkgevr;
118   Id newpkgname;
119   Id newpkgarch;
120 };
121
122 /*
123  * find attribute
124  */
125
126 static const char *
127 find_attr(const char *txt, const char **atts)
128 {
129   for (; *atts; atts += 2)
130     {
131       if (!strcmp(*atts, txt))
132         return atts[1];
133     }
134   return 0;
135 }
136
137
138 /*
139  * create evr (as Id) from 'epoch', 'version' and 'release' attributes
140  */
141
142 static Id
143 makeevr_atts(Pool *pool, struct parsedata *pd, const char **atts)
144 {
145   const char *e, *v, *r, *v2;
146   char *c;
147   int l;
148
149   e = v = r = 0;
150   for (; *atts; atts += 2)
151     {
152       if (!strcmp(*atts, "oldepoch"))
153         e = atts[1];
154       else if (!strcmp(*atts, "epoch"))
155         e = atts[1];
156       else if (!strcmp(*atts, "version"))
157         v = atts[1];
158       else if (!strcmp(*atts, "oldversion"))
159         v = atts[1];
160       else if (!strcmp(*atts, "release"))
161         r = atts[1];
162       else if (!strcmp(*atts, "oldrelease"))
163         r = atts[1];
164     }
165   if (e && !strcmp(e, "0"))
166     e = 0;
167   if (v && !e)
168     {
169       for (v2 = v; *v2 >= '0' && *v2 <= '9'; v2++)
170         ;
171       if (v2 > v && *v2 == ':')
172         e = "0";
173     }
174   l = 1;
175   if (e)
176     l += strlen(e) + 1;
177   if (v)
178     l += strlen(v);
179   if (r)
180     l += strlen(r) + 1;
181   if (l > pd->acontent)
182     {
183       pd->content = realloc(pd->content, l + 256);
184       pd->acontent = l + 256;
185     }
186   c = pd->content;
187   if (e)
188     {
189       strcpy(c, e);
190       c += strlen(c);
191       *c++ = ':';
192     }
193   if (v)
194     {
195       strcpy(c, v);
196       c += strlen(c);
197     }
198   if (r)
199     {
200       *c++ = '-';
201       strcpy(c, r);
202       c += strlen(c);
203     }
204   *c = 0;
205   if (!*pd->content)
206     return 0;
207 #if 0
208   fprintf(stderr, "evr: %s\n", pd->content);
209 #endif
210   return str2id(pool, pd->content, 1);
211 }
212
213 static void parse_delta_location( struct parsedata *pd, 
214                                   const char* str )
215 {
216     Pool *pool = pd->pool;
217     if (str)
218     {
219         /* Separate the filename into its different parts.
220            rpm/x86_64/alsa-1.0.14-31_31.2.x86_64.delta.rpm
221            --> dir = rpm/x86_64
222            name = alsa
223            evr = 1.0.14-31_31.2
224            suffix = x86_64.delta.rpm.  */
225         char *real_str = strdup(str);
226         char *s = real_str;
227         char *s1, *s2;
228         s1 = strrchr (s, '/');
229         if (s1)
230         {
231             pd->delta.locdir = strn2id(pool, s, s1 - s, 1);
232             s = s1 + 1;
233         }
234         /* Guess suffix.  */
235         s1 = strrchr (s, '.');
236         if (s1)
237         {
238             for (s2 = s1 - 1; s2 > s; s2--)
239                 if (*s2 == '.')
240                     break;
241             if (!strcmp (s2, ".delta.rpm") || !strcmp (s2, ".patch.rpm"))
242             {
243                 s1 = s2;
244                 /* We accept one more item as suffix.  */
245                 for (s2 = s1 - 1; s2 > s; s2--)
246                     if (*s2 == '.')
247                         break;
248                 s1 = s2;
249                   }
250             if (*s1 == '.')
251                 *s1++ = 0;
252             pd->delta.locsuffix = str2id(pool, s1, 1); 
253         }
254         /* Last '-'.  */
255         s1 = strrchr (s, '-');
256         if (s1)
257         {
258                   /* Second to last '-'.  */
259             for (s2 = s1 - 1; s2 > s; s2--)
260                 if (*s2 == '-')
261                     break;
262         }
263         else
264             s2 = 0;
265         if (s2 > s && *s2 == '-')
266         {
267             *s2++ = 0;
268             pd->delta.locevr = str2id(pool, s2, 1);
269         }
270         pd->delta.locname = str2id(pool, s, 1);
271         free(real_str);
272     }
273 }
274                                  
275 static void XMLCALL
276 startElement(void *userData, const char *name, const char **atts)
277 {
278   struct parsedata *pd = userData;
279   Pool *pool = pd->pool;
280   struct stateswitch *sw;
281   const char *str;
282
283 #if 0
284       fprintf(stderr, "start: [%d]%s\n", pd->state, name);
285 #endif
286   if (pd->depth != pd->statedepth)
287     {
288       pd->depth++;
289       return;
290     }
291
292   pd->depth++;
293   if (!pd->swtab[pd->state])
294     return;
295   for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)  /* find name in statetable */
296     if (!strcmp(sw->ename, name))
297       break;
298   if (sw->from != pd->state)
299     {
300 #if 1
301       fprintf(stderr, "into unknown: [%d]%s (from: %d)\n", sw->to, name, sw->from);
302       exit( 1 );
303 #endif
304       return;
305     }
306   pd->state = sw->to;
307   pd->docontent = sw->docontent;
308   pd->statedepth = pd->depth;
309   pd->lcontent = 0;
310   *pd->content = 0;
311
312   switch(pd->state)
313     {
314       case STATE_START:
315           break;
316       case STATE_NEWPACKAGE:
317           if ( (str = find_attr("name", atts)) )
318             {
319               pd->newpkgname = str2id(pool, str, 1);
320             }
321           pd->newpkgevr = makeevr_atts(pool, pd, atts);
322           if ( (str = find_attr("arch", atts)) )
323             {
324               pd->newpkgarch = str2id(pool, str, 1);
325             }
326           break;
327
328       case STATE_DELTA:
329           memset(&pd->delta, 0, sizeof (pd->delta));
330           *pd->tempstr = 0;
331           pd->ltemp = 0;
332           pd->delta.nbevr++;
333           pd->delta.bevr = sat_realloc (pd->delta.bevr, pd->delta.nbevr * sizeof(Id));
334           pd->delta.bevr[pd->delta.nbevr - 1] = makeevr_atts(pool, pd, atts);
335           --(pd->datanum);
336           break;
337       case STATE_FILENAME:
338           break;
339       case STATE_LOCATION:
340           parse_delta_location( pd, find_attr("href", atts));
341           break;
342     case STATE_SIZE:
343       break;
344     case STATE_SEQUENCE:
345       break;
346
347       case NUMSTATES+1:
348         split(NULL, NULL, 0); /* just to keep gcc happy about tools_util.h: static ... split() {...}  Urgs!*/
349       break;
350       default:
351       break;
352     }
353   return;
354 }
355
356
357 static void XMLCALL
358 endElement(void *userData, const char *name)
359 {
360   struct parsedata *pd = userData;
361   Pool *pool = pd->pool;
362   const char *str;
363
364 #if 0
365       fprintf(stderr, "end: %s\n", name);
366 #endif
367   if (pd->depth != pd->statedepth)
368     {
369       pd->depth--;
370 #if 1
371       fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
372 #endif
373       return;
374     }
375
376   pd->depth--;
377   pd->statedepth--;
378   switch (pd->state)
379   {
380       case STATE_START:
381           break;
382       case STATE_NEWPACKAGE:
383           break;
384       case STATE_DELTA:
385       {
386 #ifdef DUMPOUT
387           int i;
388 #endif
389           struct deltarpm *d = &pd->delta;
390
391 #ifdef DUMPOUT
392
393           fprintf (stderr, "found deltarpm for %s:\n", id2str(pool, pd->newpkgname));
394 #endif
395           repo_set_id(pd->repo, pd->datanum, DELTA_PACKAGE_NAME, pd->newpkgname);
396           repo_set_id(pd->repo, pd->datanum, DELTA_PACKAGE_EVR, pd->newpkgevr);
397           repo_set_id(pd->repo, pd->datanum, DELTA_PACKAGE_ARCH, pd->newpkgarch);
398           repo_set_id(pd->repo, pd->datanum, DELTA_LOCATION_NAME, d->locname);
399           repo_set_id(pd->repo, pd->datanum, DELTA_LOCATION_DIR, d->locdir);
400           repo_set_id(pd->repo, pd->datanum, DELTA_LOCATION_EVR, d->locevr);
401           repo_set_id(pd->repo, pd->datanum, DELTA_LOCATION_SUFFIX, d->locsuffix);
402
403 #ifdef DUMPOUT
404           fprintf (stderr, "   loc: %s %s %s %s\n", id2str(pool, d->locdir),
405                    id2str(pool, d->locname), id2str(pool, d->locevr),
406                    id2str(pool, d->locsuffix));
407           fprintf (stderr, "  size: %d down\n", d->downloadsize);
408           fprintf (stderr, "  chek: %s\n", d->filechecksum);
409 #endif
410
411           repo_set_num(pd->repo, pd->datanum, DELTA_DOWNLOADSIZE, d->downloadsize);
412           repo_set_str(pd->repo, pd->datanum, DELTA_CHECKSUM, d->filechecksum);
413
414
415           if (d->seqnum)
416           {
417 #ifdef DUMPOUT
418               fprintf (stderr, "  base: %s\n",
419                        id2str(pool, d->bevr[0]));
420               fprintf (stderr, "            seq: %s\n",
421                        id2str(pool, d->seqname));
422               fprintf (stderr, "                 %s\n",
423                        id2str(pool, d->seqevr));
424               fprintf (stderr, "                 %s\n",
425                        d->seqnum);
426 #endif
427               repo_set_id(pd->repo, pd->datanum, DELTA_BASE_EVR, d->bevr[0]);
428               repo_set_id(pd->repo, pd->datanum, DELTA_SEQ_NAME, d->seqname);
429               repo_set_id(pd->repo, pd->datanum, DELTA_SEQ_EVR, d->seqevr);
430               repo_set_str(pd->repo, pd->datanum, DELTA_SEQ_NUM, d->seqnum);
431
432 #ifdef DUMPOUT
433               fprintf(stderr, "OK\n");
434 #endif
435
436 #ifdef DUMPOUT              
437               if (d->seqevr != d->bevr[0])
438                   fprintf (stderr, "XXXXX evr\n");
439               /* Name of package ("atom:xxxx") should match the sequence info
440                  name.  */
441               if (strcmp(id2str(pool, d->seqname), id2str(pool, pd->newpkgname) + 5))
442                   fprintf (stderr, "XXXXX name\n");
443 #endif
444           }
445           else
446           {
447
448 #ifdef DUMPOUT                          
449               fprintf (stderr, "  base:");
450               for (i = 0; i < d->nbevr; i++)
451                   fprintf (stderr, " %s", id2str(pool, d->bevr[i]));
452               fprintf (stderr, "\n");
453 #endif
454           }
455
456       }
457       free(pd->delta.filechecksum);
458       free(pd->delta.bevr);
459       free(pd->delta.seqnum);
460       break;
461       case STATE_FILENAME:
462           parse_delta_location(pd, pd->content);
463           break;
464       case STATE_CHECKSUM:
465       pd->delta.filechecksum = strdup(pd->content);
466       break;
467       case STATE_SIZE:
468           pd->delta.downloadsize = atoi(pd->content);
469           break;
470       case STATE_SEQUENCE:
471       if ((str = pd->content))
472       {
473           const char *s1, *s2;
474           s1 = strrchr(str, '-');
475           if (s1)
476           {
477               for (s2 = s1 - 1; s2 > str; s2--)
478                 if (*s2 == '-')
479                     break;
480               if (*s2 == '-')
481               {
482                   for (s2 = s2 - 1; s2 > str; s2--)
483                       if (*s2 == '-')
484                           break;
485                   if (*s2 == '-')
486                   {
487                       pd->delta.seqevr = strn2id(pool, s2 + 1, s1 - s2 - 1, 1);
488                       pd->delta.seqname = strn2id(pool, str, s2 - str, 1);
489                       str = s1 + 1;
490                   }
491               }
492           }
493           pd->delta.seqnum = strdup(str);
494       }
495       default:
496       break;
497   }
498
499   pd->state = pd->sbtab[pd->state];
500   pd->docontent = 0;
501   
502   return;
503 }
504
505
506 static void XMLCALL
507 characterData(void *userData, const XML_Char *s, int len)
508 {
509   struct parsedata *pd = userData;
510   int l;
511   char *c;
512   if (!pd->docontent) {
513 #if 0
514     char *dup = strndup( s, len );
515   fprintf(stderr, "Content: [%d]'%s'\n", pd->state, dup );
516   free( dup );
517 #endif
518     return;
519   }
520   l = pd->lcontent + len + 1;
521   if (l > pd->acontent)
522     {
523       pd->content = realloc(pd->content, l + 256);
524       pd->acontent = l + 256;
525     }
526   c = pd->content + pd->lcontent;
527   pd->lcontent += len;
528   while (len-- > 0)
529     *c++ = *s++;
530   *c = 0;
531 }
532
533 #define BUFF_SIZE 8192
534
535 void
536 repo_add_deltainfoxml(Repo *repo, FILE *fp, int flags)
537 {
538   Pool *pool = repo->pool;
539   struct parsedata pd;
540   char buf[BUFF_SIZE];
541   int i, l;
542   struct stateswitch *sw;
543
544   memset(&pd, 0, sizeof(pd));
545   for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
546     {
547       if (!pd.swtab[sw->from])
548         pd.swtab[sw->from] = sw;
549       pd.sbtab[sw->to] = sw->from;
550     }
551   pd.pool = pool;
552   pd.repo = repo;
553   pd.data = repo_add_repodata(pd.repo, 0);
554
555   pd.content = malloc(256);
556   pd.acontent = 256;
557   pd.lcontent = 0;
558   pd.tempstr = malloc(256);
559   pd.atemp = 256;
560   pd.ltemp = 0;
561   pd.datanum = 0;
562   XML_Parser parser = XML_ParserCreate(NULL);
563   XML_SetUserData(parser, &pd);
564   XML_SetElementHandler(parser, startElement, endElement);
565   XML_SetCharacterDataHandler(parser, characterData);
566   for (;;)
567     {
568       l = fread(buf, 1, sizeof(buf), fp);
569       if (XML_Parse(parser, buf, l, l == 0) == XML_STATUS_ERROR)
570         {
571           fprintf(stderr, "repo_updateinfoxml: %s at line %u:%u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
572           exit(1);
573         }
574       if (l == 0)
575         break;
576     }
577   XML_ParserFree(parser);
578
579   if (pd.data)
580     repodata_internalize(pd.data);
581
582   free(pd.content);
583   join_freemem();
584 }
585
586 /* EOF */