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