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