b14bb9a7d8cfe6a78c44a65d6d581f46254fbaa7
[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   for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)  /* find name in statetable */
294     if (!strcmp(sw->ename, name))
295       break;
296   
297   if (sw->from != pd->state)
298     {
299 #if 1
300       fprintf(stderr, "into unknown: [%d]%s (from: %d)\n", sw->to, name, sw->from);
301       exit( 1 );
302 #endif
303       return;
304     }
305   pd->state = sw->to;
306   pd->docontent = sw->docontent;
307   pd->statedepth = pd->depth;
308   pd->lcontent = 0;
309   *pd->content = 0;
310
311   switch(pd->state)
312     {
313       case STATE_START:
314           break;
315       case STATE_NEWPACKAGE:
316           if ( (str = find_attr("name", atts)) )
317             {
318               pd->newpkgname = str2id(pool, str, 1);
319             }
320           pd->newpkgevr = makeevr_atts(pool, pd, atts);
321           if ( (str = find_attr("arch", atts)) )
322             {
323               pd->newpkgarch = str2id(pool, str, 1);
324             }
325           break;
326
327       case STATE_DELTA:
328           memset(&pd->delta, 0, sizeof (pd->delta));
329           *pd->tempstr = 0;
330           pd->ltemp = 0;
331           pd->delta.nbevr++;
332           pd->delta.bevr = sat_realloc (pd->delta.bevr, pd->delta.nbevr * sizeof(Id));
333           pd->delta.bevr[pd->delta.nbevr - 1] = makeevr_atts(pool, pd, atts);
334           --(pd->datanum);
335           break;
336       case STATE_FILENAME:
337           break;
338       case STATE_LOCATION:
339           parse_delta_location( pd, find_attr("href", atts));
340           break;
341     case STATE_SIZE:
342       break;
343     case STATE_SEQUENCE:
344       break;
345
346       case NUMSTATES+1:
347         split(NULL, NULL, 0); /* just to keep gcc happy about tools_util.h: static ... split() {...}  Urgs!*/
348       break;
349       default:
350       break;
351     }
352   return;
353 }
354
355
356 static void XMLCALL
357 endElement(void *userData, const char *name)
358 {
359   struct parsedata *pd = userData;
360   Pool *pool = pd->pool;
361   const char *str;
362
363 #if 0
364       fprintf(stderr, "end: %s\n", name);
365 #endif
366   if (pd->depth != pd->statedepth)
367     {
368       pd->depth--;
369 #if 1
370       fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
371 #endif
372       return;
373     }
374
375   pd->depth--;
376   pd->statedepth--;
377   switch (pd->state)
378   {
379       case STATE_START:
380           break;
381       case STATE_NEWPACKAGE:
382           break;
383       case STATE_DELTA:
384       {
385 #ifdef DUMPOUT
386           int i;
387 #endif
388           struct deltarpm *d = &pd->delta;
389
390 #ifdef DUMPOUT
391
392           fprintf (stderr, "found deltarpm for %s:\n", id2str(pool, pd->newpkgname));
393 #endif
394           repo_set_id(pd->repo, pd->datanum, DELTA_PACKAGE_NAME, pd->newpkgname);
395           repo_set_id(pd->repo, pd->datanum, DELTA_PACKAGE_EVR, pd->newpkgevr);
396           repo_set_id(pd->repo, pd->datanum, DELTA_PACKAGE_ARCH, pd->newpkgarch);
397           repo_set_id(pd->repo, pd->datanum, DELTA_LOCATION_NAME, d->locname);
398           repo_set_id(pd->repo, pd->datanum, DELTA_LOCATION_DIR, d->locdir);
399           repo_set_id(pd->repo, pd->datanum, DELTA_LOCATION_EVR, d->locevr);
400           repo_set_id(pd->repo, pd->datanum, DELTA_LOCATION_SUFFIX, d->locsuffix);
401
402 #ifdef DUMPOUT
403           fprintf (stderr, "   loc: %s %s %s %s\n", id2str(pool, d->locdir),
404                    id2str(pool, d->locname), id2str(pool, d->locevr),
405                    id2str(pool, d->locsuffix));
406           fprintf (stderr, "  size: %d down\n", d->downloadsize);
407           fprintf (stderr, "  chek: %s\n", d->filechecksum);
408 #endif
409
410           repo_set_num(pd->repo, pd->datanum, DELTA_DOWNLOADSIZE, d->downloadsize);
411           repo_set_str(pd->repo, pd->datanum, DELTA_CHECKSUM, d->filechecksum);
412
413
414           if (d->seqnum)
415           {
416 #ifdef DUMPOUT
417               fprintf (stderr, "  base: %s\n",
418                        id2str(pool, d->bevr[0]));
419               fprintf (stderr, "            seq: %s\n",
420                        id2str(pool, d->seqname));
421               fprintf (stderr, "                 %s\n",
422                        id2str(pool, d->seqevr));
423               fprintf (stderr, "                 %s\n",
424                        d->seqnum);
425 #endif
426               repo_set_id(pd->repo, pd->datanum, DELTA_BASE_EVR, d->bevr[0]);
427               repo_set_id(pd->repo, pd->datanum, DELTA_SEQ_NAME, d->seqname);
428               repo_set_id(pd->repo, pd->datanum, DELTA_SEQ_EVR, d->seqevr);
429               repo_set_str(pd->repo, pd->datanum, DELTA_SEQ_NUM, d->seqnum);
430
431 #ifdef DUMPOUT
432               fprintf(stderr, "OK\n");
433 #endif
434
435 #ifdef DUMPOUT              
436               if (d->seqevr != d->bevr[0])
437                   fprintf (stderr, "XXXXX evr\n");
438               /* Name of package ("atom:xxxx") should match the sequence info
439                  name.  */
440               if (strcmp(id2str(pool, d->seqname), id2str(pool, pd->newpkgname) + 5))
441                   fprintf (stderr, "XXXXX name\n");
442 #endif
443           }
444           else
445           {
446
447 #ifdef DUMPOUT                          
448               fprintf (stderr, "  base:");
449               for (i = 0; i < d->nbevr; i++)
450                   fprintf (stderr, " %s", id2str(pool, d->bevr[i]));
451               fprintf (stderr, "\n");
452 #endif
453           }
454
455       }
456       free(pd->delta.filechecksum);
457       free(pd->delta.bevr);
458       free(pd->delta.seqnum);
459       break;
460       case STATE_FILENAME:
461           parse_delta_location(pd, pd->content);
462           break;
463       case STATE_CHECKSUM:
464       pd->delta.filechecksum = strdup(pd->content);
465       break;
466       case STATE_SIZE:
467           pd->delta.downloadsize = atoi(pd->content);
468           break;
469       case STATE_SEQUENCE:
470       if ((str = pd->content))
471       {
472           const char *s1, *s2;
473           s1 = strrchr(str, '-');
474           if (s1)
475           {
476               for (s2 = s1 - 1; s2 > str; s2--)
477                 if (*s2 == '-')
478                     break;
479               if (*s2 == '-')
480               {
481                   for (s2 = s2 - 1; s2 > str; s2--)
482                       if (*s2 == '-')
483                           break;
484                   if (*s2 == '-')
485                   {
486                       pd->delta.seqevr = strn2id(pool, s2 + 1, s1 - s2 - 1, 1);
487                       pd->delta.seqname = strn2id(pool, str, s2 - str, 1);
488                       str = s1 + 1;
489                   }
490               }
491           }
492           pd->delta.seqnum = strdup(str);
493       }
494       default:
495       break;
496   }
497
498   pd->state = pd->sbtab[pd->state];
499   pd->docontent = 0;
500   
501   return;
502 }
503
504
505 static void XMLCALL
506 characterData(void *userData, const XML_Char *s, int len)
507 {
508   struct parsedata *pd = userData;
509   int l;
510   char *c;
511   if (!pd->docontent) {
512 #if 0
513     char *dup = strndup( s, len );
514   fprintf(stderr, "Content: [%d]'%s'\n", pd->state, dup );
515   free( dup );
516 #endif
517     return;
518   }
519   l = pd->lcontent + len + 1;
520   if (l > pd->acontent)
521     {
522       pd->content = realloc(pd->content, l + 256);
523       pd->acontent = l + 256;
524     }
525   c = pd->content + pd->lcontent;
526   pd->lcontent += len;
527   while (len-- > 0)
528     *c++ = *s++;
529   *c = 0;
530 }
531
532 #define BUFF_SIZE 8192
533
534 void
535 repo_add_deltainfoxml(Repo *repo, FILE *fp, int flags)
536 {
537   Pool *pool = repo->pool;
538   struct parsedata pd;
539   char buf[BUFF_SIZE];
540   int i, l;
541   struct stateswitch *sw;
542
543   memset(&pd, 0, sizeof(pd));
544   for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
545     {
546       if (!pd.swtab[sw->from])
547         pd.swtab[sw->from] = sw;
548       pd.sbtab[sw->to] = sw->from;
549     }
550   pd.pool = pool;
551   pd.repo = repo;
552   pd.data = repo_add_repodata(pd.repo, 0);
553
554   pd.content = malloc(256);
555   pd.acontent = 256;
556   pd.lcontent = 0;
557   pd.tempstr = malloc(256);
558   pd.atemp = 256;
559   pd.ltemp = 0;
560   pd.datanum = 0;
561   XML_Parser parser = XML_ParserCreate(NULL);
562   XML_SetUserData(parser, &pd);
563   XML_SetElementHandler(parser, startElement, endElement);
564   XML_SetCharacterDataHandler(parser, characterData);
565   for (;;)
566     {
567       l = fread(buf, 1, sizeof(buf), fp);
568       if (XML_Parse(parser, buf, l, l == 0) == XML_STATUS_ERROR)
569         {
570           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));
571           exit(1);
572         }
573       if (l == 0)
574         break;
575     }
576   XML_ParserFree(parser);
577
578   if (pd.data)
579     repodata_internalize(pd.data);
580
581   free(pd.content);
582   join_freemem();
583 }
584
585 /* EOF */