- deltainfoxml2solv now writes the information out
[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 };
120
121 /*
122  * find attribute
123  */
124
125 static const char *
126 find_attr(const char *txt, const char **atts)
127 {
128   for (; *atts; atts += 2)
129     {
130       if (!strcmp(*atts, txt))
131         return atts[1];
132     }
133   return 0;
134 }
135
136
137 /*
138  * create evr (as Id) from 'epoch', 'version' and 'release' attributes
139  */
140
141 static Id
142 makeevr_atts(Pool *pool, struct parsedata *pd, const char **atts)
143 {
144   const char *e, *v, *r, *v2;
145   char *c;
146   int l;
147
148   e = v = r = 0;
149   for (; *atts; atts += 2)
150     {
151       if (!strcmp(*atts, "oldepoch"))
152         e = atts[1];
153       else if (!strcmp(*atts, "epoch"))
154         e = atts[1];
155       else if (!strcmp(*atts, "version"))
156         v = atts[1];
157       else if (!strcmp(*atts, "oldversion"))
158         v = atts[1];
159       else if (!strcmp(*atts, "release"))
160         r = atts[1];
161       else if (!strcmp(*atts, "oldrelease"))
162         r = atts[1];
163     }
164   if (e && !strcmp(e, "0"))
165     e = 0;
166   if (v && !e)
167     {
168       for (v2 = v; *v2 >= '0' && *v2 <= '9'; v2++)
169         ;
170       if (v2 > v && *v2 == ':')
171         e = "0";
172     }
173   l = 1;
174   if (e)
175     l += strlen(e) + 1;
176   if (v)
177     l += strlen(v);
178   if (r)
179     l += strlen(r) + 1;
180   if (l > pd->acontent)
181     {
182       pd->content = realloc(pd->content, l + 256);
183       pd->acontent = l + 256;
184     }
185   c = pd->content;
186   if (e)
187     {
188       strcpy(c, e);
189       c += strlen(c);
190       *c++ = ':';
191     }
192   if (v)
193     {
194       strcpy(c, v);
195       c += strlen(c);
196     }
197   if (r)
198     {
199       *c++ = '-';
200       strcpy(c, r);
201       c += strlen(c);
202     }
203   *c = 0;
204   if (!*pd->content)
205     return 0;
206 #if 0
207   fprintf(stderr, "evr: %s\n", pd->content);
208 #endif
209   return str2id(pool, pd->content, 1);
210 }
211
212 static void parse_delta_location( struct parsedata *pd, 
213                                   const char* str )
214 {
215     Pool *pool = pd->pool;
216     if (str)
217     {
218         /* Separate the filename into its different parts.
219            rpm/x86_64/alsa-1.0.14-31_31.2.x86_64.delta.rpm
220            --> dir = rpm/x86_64
221            name = alsa
222            evr = 1.0.14-31_31.2
223            suffix = x86_64.delta.rpm.  */
224         char *real_str = strdup(str);
225         char *s = real_str;
226         char *s1, *s2;
227         s1 = strrchr (s, '/');
228         if (s1)
229         {
230             pd->delta.locdir = strn2id(pool, s, s1 - s, 1);
231             s = s1 + 1;
232         }
233         /* Guess suffix.  */
234         s1 = strrchr (s, '.');
235         if (s1)
236         {
237             for (s2 = s1 - 1; s2 > s; s2--)
238                 if (*s2 == '.')
239                     break;
240             if (!strcmp (s2, ".delta.rpm") || !strcmp (s2, ".patch.rpm"))
241             {
242                 s1 = s2;
243                 /* We accept one more item as suffix.  */
244                 for (s2 = s1 - 1; s2 > s; s2--)
245                     if (*s2 == '.')
246                         break;
247                 s1 = s2;
248                   }
249             if (*s1 == '.')
250                 *s1++ = 0;
251             pd->delta.locsuffix = str2id(pool, s1, 1); 
252         }
253         /* Last '-'.  */
254         s1 = strrchr (s, '-');
255         if (s1)
256         {
257                   /* Second to last '-'.  */
258             for (s2 = s1 - 1; s2 > s; s2--)
259                 if (*s2 == '-')
260                     break;
261         }
262         else
263             s2 = 0;
264         if (s2 > s && *s2 == '-')
265         {
266             *s2++ = 0;
267             pd->delta.locevr = str2id(pool, s2, 1);
268         }
269         pd->delta.locname = str2id(pool, s, 1);
270         free(real_str);
271     }
272 }
273                                  
274 static void XMLCALL
275 startElement(void *userData, const char *name, const char **atts)
276 {
277   struct parsedata *pd = userData;
278   Pool *pool = pd->pool;
279   struct stateswitch *sw;
280   const char *str;
281
282 #if 0
283       fprintf(stderr, "start: [%d]%s\n", pd->state, name);
284 #endif
285   if (pd->depth != pd->statedepth)
286     {
287       pd->depth++;
288       return;
289     }
290
291   pd->depth++;
292   for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)  /* find name in statetable */
293     if (!strcmp(sw->ename, name))
294       break;
295   
296   if (sw->from != pd->state)
297     {
298 #if 1
299       fprintf(stderr, "into unknown: [%d]%s (from: %d)\n", sw->to, name, sw->from);
300       exit( 1 );
301 #endif
302       return;
303     }
304   pd->state = sw->to;
305   pd->docontent = sw->docontent;
306   pd->statedepth = pd->depth;
307   pd->lcontent = 0;
308   *pd->content = 0;
309
310   switch(pd->state)
311     {
312       case STATE_START:
313           break;
314       case STATE_NEWPACKAGE:
315           if ( (str = find_attr("name", atts)) )
316           {
317               pd->newpkgname = str2id(pool, str, 1);
318           }
319           break;
320           
321       case STATE_DELTA:
322           memset(&pd->delta, 0, sizeof (pd->delta));
323           *pd->tempstr = 0;
324           pd->ltemp = 0;
325           pd->delta.nbevr++;
326           pd->delta.bevr = sat_realloc (pd->delta.bevr, pd->delta.nbevr * sizeof(Id));
327           pd->delta.bevr[pd->delta.nbevr - 1] = makeevr_atts(pool, pd, atts);
328           --(pd->datanum);
329           break;
330       case STATE_FILENAME:
331           break;
332       case STATE_LOCATION:
333           parse_delta_location( pd, find_attr("href", atts));
334           break;
335     case STATE_SIZE:
336       break;
337     case STATE_SEQUENCE:
338       break;
339
340       case NUMSTATES+1:
341         split(NULL, NULL, 0); /* just to keep gcc happy about tools_util.h: static ... split() {...}  Urgs!*/
342       break;
343       default:
344       break;
345     }
346   return;
347 }
348
349
350 static void XMLCALL
351 endElement(void *userData, const char *name)
352 {
353   struct parsedata *pd = userData;
354   Pool *pool = pd->pool;
355   const char *str;
356
357 #if 0
358       fprintf(stderr, "end: %s\n", name);
359 #endif
360   if (pd->depth != pd->statedepth)
361     {
362       pd->depth--;
363 #if 1
364       fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
365 #endif
366       return;
367     }
368
369   pd->depth--;
370   pd->statedepth--;
371   switch (pd->state)
372   {
373       case STATE_START:
374           break;
375       case STATE_NEWPACKAGE:
376           break;
377       case STATE_DELTA:
378       {
379 #ifdef DUMPOUT
380           int i;
381 #endif
382           struct deltarpm *d = &pd->delta;
383
384 #ifdef DUMPOUT
385
386           fprintf (stderr, "found deltarpm for %s:\n", id2str(pool, pd->newpkgname));
387 #endif
388           repo_set_id(pd->repo, pd->datanum, DELTA_PACKAGE_NAME, pd->newpkgname);
389           repo_set_id(pd->repo, pd->datanum, DELTA_LOCATION_NAME, d->locname);
390           repo_set_id(pd->repo, pd->datanum, DELTA_LOCATION_DIR, d->locdir);
391           repo_set_id(pd->repo, pd->datanum, DELTA_LOCATION_EVR, d->locevr);
392           repo_set_id(pd->repo, pd->datanum, DELTA_LOCATION_SUFFIX, d->locsuffix);
393
394 #ifdef DUMPOUT
395           fprintf (stderr, "   loc: %s %s %s %s\n", id2str(pool, d->locdir),
396                    id2str(pool, d->locname), id2str(pool, d->locevr),
397                    id2str(pool, d->locsuffix));
398           fprintf (stderr, "  size: %d down\n", d->downloadsize);
399           fprintf (stderr, "  chek: %s\n", d->filechecksum);
400 #endif
401
402           repo_set_num(pd->repo, pd->datanum, DELTA_DOWNLOADSIZE, d->downloadsize);
403           repo_set_str(pd->repo, pd->datanum, DELTA_CHECKSUM, d->filechecksum);
404
405
406           if (d->seqnum)
407           {
408 #ifdef DUMPOUT
409               fprintf (stderr, "  base: %s\n",
410                        id2str(pool, d->bevr[0]));
411               fprintf (stderr, "            seq: %s\n",
412                        id2str(pool, d->seqname));
413               fprintf (stderr, "                 %s\n",
414                        id2str(pool, d->seqevr));
415               fprintf (stderr, "                 %s\n",
416                        d->seqnum);
417 #endif
418               repo_set_id(pd->repo, pd->datanum, DELTA_BASE_EVR, d->bevr[0]);
419               repo_set_id(pd->repo, pd->datanum, DELTA_SEQ_NAME, d->seqname);
420               repo_set_id(pd->repo, pd->datanum, DELTA_SEQ_EVR, d->seqevr);
421               repo_set_str(pd->repo, pd->datanum, DELTA_SEQ_NUM, d->seqnum);
422
423 #ifdef DUMPOUT
424               fprintf(stderr, "OK\n");
425 #endif
426
427 #ifdef DUMPOUT              
428               if (d->seqevr != d->bevr[0])
429                   fprintf (stderr, "XXXXX evr\n");
430               /* Name of package ("atom:xxxx") should match the sequence info
431                  name.  */
432               if (strcmp(id2str(pool, d->seqname), id2str(pool, pd->newpkgname) + 5))
433                   fprintf (stderr, "XXXXX name\n");
434 #endif
435           }
436           else
437           {
438
439 #ifdef DUMPOUT                          
440               fprintf (stderr, "  base:");
441               for (i = 0; i < d->nbevr; i++)
442                   fprintf (stderr, " %s", id2str(pool, d->bevr[i]));
443               fprintf (stderr, "\n");
444 #endif
445           }
446
447       }
448       free(pd->delta.filechecksum);
449       free(pd->delta.bevr);
450       free(pd->delta.seqnum);
451       break;
452       case STATE_FILENAME:
453           parse_delta_location(pd, pd->content);
454           break;
455       case STATE_CHECKSUM:
456       pd->delta.filechecksum = strdup(pd->content);
457       break;
458       case STATE_SIZE:
459           pd->delta.downloadsize = atoi(pd->content);
460           break;
461       case STATE_SEQUENCE:
462       if ((str = pd->content))
463       {
464           const char *s1, *s2;
465           s1 = strrchr(str, '-');
466           if (s1)
467           {
468               for (s2 = s1 - 1; s2 > str; s2--)
469                 if (*s2 == '-')
470                     break;
471               if (*s2 == '-')
472               {
473                   for (s2 = s2 - 1; s2 > str; s2--)
474                       if (*s2 == '-')
475                           break;
476                   if (*s2 == '-')
477                   {
478                       pd->delta.seqevr = strn2id(pool, s2 + 1, s1 - s2 - 1, 1);
479                       pd->delta.seqname = strn2id(pool, str, s2 - str, 1);
480                       str = s1 + 1;
481                   }
482               }
483           }
484           pd->delta.seqnum = strdup(str);
485       }
486       default:
487       break;
488   }
489
490   pd->state = pd->sbtab[pd->state];
491   pd->docontent = 0;
492   
493   return;
494 }
495
496
497 static void XMLCALL
498 characterData(void *userData, const XML_Char *s, int len)
499 {
500   struct parsedata *pd = userData;
501   int l;
502   char *c;
503   if (!pd->docontent) {
504 #if 0
505     char *dup = strndup( s, len );
506   fprintf(stderr, "Content: [%d]'%s'\n", pd->state, dup );
507   free( dup );
508 #endif
509     return;
510   }
511   l = pd->lcontent + len + 1;
512   if (l > pd->acontent)
513     {
514       pd->content = realloc(pd->content, l + 256);
515       pd->acontent = l + 256;
516     }
517   c = pd->content + pd->lcontent;
518   pd->lcontent += len;
519   while (len-- > 0)
520     *c++ = *s++;
521   *c = 0;
522 }
523
524 #define BUFF_SIZE 8192
525
526 void
527 repo_add_deltainfoxml(Repo *repo, FILE *fp, int flags)
528 {
529   Pool *pool = repo->pool;
530   struct parsedata pd;
531   char buf[BUFF_SIZE];
532   int i, l;
533   struct stateswitch *sw;
534
535   memset(&pd, 0, sizeof(pd));
536   for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
537     {
538       if (!pd.swtab[sw->from])
539         pd.swtab[sw->from] = sw;
540       pd.sbtab[sw->to] = sw->from;
541     }
542   pd.pool = pool;
543   pd.repo = repo;
544   pd.data = repo_add_repodata(pd.repo, 0);
545
546   pd.content = malloc(256);
547   pd.acontent = 256;
548   pd.lcontent = 0;
549   pd.tempstr = malloc(256);
550   pd.atemp = 256;
551   pd.ltemp = 0;
552   pd.datanum = 0;
553   XML_Parser parser = XML_ParserCreate(NULL);
554   XML_SetUserData(parser, &pd);
555   XML_SetElementHandler(parser, startElement, endElement);
556   XML_SetCharacterDataHandler(parser, characterData);
557   for (;;)
558     {
559       l = fread(buf, 1, sizeof(buf), fp);
560       if (XML_Parse(parser, buf, l, l == 0) == XML_STATUS_ERROR)
561         {
562           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));
563           exit(1);
564         }
565       if (l == 0)
566         break;
567     }
568   XML_ParserFree(parser);
569
570   if (pd.data)
571     repodata_internalize(pd.data);
572
573   free(pd.content);
574   join_freemem();
575 }
576
577 /* EOF */