- copy parser swtab fix from Matz into other files
[platform/upstream/libsolv.git] / tools / repo_repomdxml.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 DUMPOUT 0
24
25 /*
26 <repomd>
27 <data type="primary">
28 <location href="repodata/primary.xml.gz"/>
29 <checksum type="sha">e9162516fa25fec8d60caaf4682d2e49967786cc</checksum>
30 <timestamp>1215708444</timestamp>
31 <open-checksum type="sha">c796c48184cd5abc260e4ba929bdf01be14778a7</open-checksum>
32 </data>
33 <data type="filelists">
34 <location href="repodata/filelists.xml.gz"/>
35 <checksum type="sha">1c638295c49e9707c22810004ebb0799791fcf45</checksum>
36 <timestamp>1215708445</timestamp>
37 <open-checksum type="sha">54a40d5db3df0813b8acbe58cea616987eb9dc16</open-checksum>
38 </data>
39 <data type="other">
40 <location href="repodata/other.xml.gz"/>
41 <checksum type="sha">a81ef39eaa70e56048f8351055119d8c82af2491</checksum>
42 <timestamp>1215708447</timestamp>
43 <open-checksum type="sha">4d1ee867c8864025575a2fb8fde3b85371d51978</open-checksum>
44 </data>
45 <data type="deltainfo">
46 <location href="repodata/deltainfo.xml.gz"/>
47 <checksum type="sha">5880cfa5187026a24a552d3c0650904a44908c28</checksum>
48 <timestamp>1215708447</timestamp>
49 <open-checksum type="sha">7c964a2c3b17df5bfdd962c3be952c9ca6978d8b</open-checksum>
50 </data>
51 <data type="updateinfo">
52 <location href="repodata/updateinfo.xml.gz"/>
53 <checksum type="sha">4097f7e25c7bb0770ae31b2471a9c8c077ee904b</checksum>
54 <timestamp>1215708447</timestamp>
55 <open-checksum type="sha">24f8252f3dd041e37e7c3feb2d57e02b4422d316</open-checksum>
56 </data>
57 <data type="diskusage">
58 <location href="repodata/diskusage.xml.gz"/>
59 <checksum type="sha">4097f7e25c7bb0770ae31b2471a9c8c077ee904b</checksum>
60 <timestamp>1215708447</timestamp>
61 <open-checksum type="sha">24f8252f3dd041e37e7c3feb2d57e02b4422d316</open-checksum>
62 </data>
63 </repomd>
64 */
65
66 enum state {
67   STATE_START,
68   STATE_REPOMD,       /* 1 */
69   STATE_DATA,         /* 2 */
70   STATE_LOCATION,     /* 3 */
71   STATE_CHECKSUM,     /* 4 */
72   STATE_TIMESTAMP,    /* 5 */
73   STATE_OPENCHECKSUM, /* 6 */
74   NUMSTATES
75 };
76
77 struct stateswitch {
78   enum state from;
79   char *ename;
80   enum state to;
81   int docontent;
82 };
83
84 /* !! must be sorted by first column !! */
85 static struct stateswitch stateswitches[] = {
86   { STATE_START,       "repomd",          STATE_REPOMD, 0 },
87   { STATE_REPOMD,      "data",            STATE_DATA,  0 },
88   { STATE_DATA,        "location",        STATE_LOCATION, 0 },
89   { STATE_DATA,        "checksum",        STATE_CHECKSUM, 1 },  
90   { STATE_DATA,        "timestamp",       STATE_TIMESTAMP, 1 },
91   { STATE_DATA,        "open-checksum",    STATE_OPENCHECKSUM, 1 },
92   { NUMSTATES }
93 };
94
95 /*
96  * split l into m parts, store to sp[]
97  *  split at whitespace
98  */
99
100 static inline int
101 split_comma(char *l, char **sp, int m)
102 {
103   int i;
104   for (i = 0; i < m;)
105     {
106       while (*l == ',')
107         l++;
108       if (!*l)
109         break;
110       sp[i++] = l;
111       if (i == m)
112         break;
113       while (*l && !(*l == ','))
114         l++;
115       if (!*l)
116         break;
117       *l++ = 0;
118     }
119   return i;
120 }
121
122
123 struct parsedata {
124   int depth;
125   enum state state;
126   int statedepth;
127   char *content;
128   int lcontent;
129   int acontent;
130   int docontent;
131   Pool *pool;
132   Repo *repo;
133   Repodata *data;
134   
135   struct stateswitch *swtab[NUMSTATES];
136   enum state sbtab[NUMSTATES];
137   int timestamp;
138 };
139
140 /*
141  * find attribute
142  */
143
144 static inline const char *
145 find_attr(const char *txt, const char **atts)
146 {
147   for (; *atts; atts += 2)
148     {
149       if (!strcmp(*atts, txt))
150         return atts[1];
151     }
152   return 0;
153 }
154
155
156 static void XMLCALL
157 startElement(void *userData, const char *name, const char **atts)
158 {
159   struct parsedata *pd = userData;
160   /*Pool *pool = pd->pool;*/
161   struct stateswitch *sw;
162   const char *expirestr = 0;
163   int expire = 0;
164
165 #if 0
166   fprintf(stderr, "start: [%d]%s\n", pd->state, name);
167 #endif
168   if (pd->depth != pd->statedepth)
169     {
170       pd->depth++;
171       return;
172     }
173
174   pd->depth++;
175   if (!pd->swtab[pd->state])
176     return;
177   for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)  /* find name in statetable */
178     if (!strcmp(sw->ename, name))
179       break;
180   
181   if (sw->from != pd->state)
182     {
183 #if 1
184       fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
185       exit( 1 );
186 #endif
187       return;
188     }
189   pd->state = sw->to;
190   pd->docontent = sw->docontent;
191   pd->statedepth = pd->depth;
192   pd->lcontent = 0;
193   *pd->content = 0;
194
195   switch(pd->state)
196     {
197     case STATE_START: break;
198     case STATE_REPOMD:
199       {
200         const char *updstr;
201         char *value;
202         char *fvalue;
203
204         expirestr = (char*) find_attr("expire", atts);
205         if ( expirestr != NULL )
206           expire = atoi(expirestr);
207         if ( expire > 0 )
208           {
209             /* save the timestamp in the non solvable number 1 */
210             repo_set_num(pd->repo, -1, REPOSITORY_EXPIRE, expire);
211           }
212
213         updstr = find_attr("updates", atts);
214         if ( updstr != NULL )
215           {
216             value = strdup(updstr);
217             fvalue = value; /* save the first */
218             if ( value != NULL )
219               {
220                 char *sp[2];
221                 while (value)
222                   {
223                     int words = split_comma(value, sp, 2);
224                     if (!words)
225                       break;
226                     if (sp[0])
227                       repo_add_poolstr_array(pd->repo, -1, REPOSITORY_UPDATES, sp[0]);
228                     if (words == 1)
229                       break;
230                     value = sp[1];
231                   }
232                 free(fvalue);
233               }
234           }
235           break;
236         }
237     case STATE_DATA: break;
238     case STATE_LOCATION: break;
239     case STATE_CHECKSUM: break;
240     case STATE_TIMESTAMP: break;
241     case STATE_OPENCHECKSUM: break;
242     case NUMSTATES: break;
243     default: break;
244     }
245   return;
246 }
247
248 static void XMLCALL
249 endElement(void *userData, const char *name)
250 {
251   struct parsedata *pd = userData;
252   /* Pool *pool = pd->pool; */
253   int timestamp;
254
255 #if 0
256       fprintf(stderr, "end: %s\n", name);
257 #endif
258   if (pd->depth != pd->statedepth)
259     {
260       pd->depth--;
261 #if 1
262       fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
263 #endif
264       return;
265     }
266
267   pd->depth--;
268   pd->statedepth--;
269   switch (pd->state)
270     {
271     case STATE_START: break;
272     case STATE_REPOMD: break;
273     case STATE_DATA: break;
274     case STATE_LOCATION: break;
275     case STATE_CHECKSUM: break;
276     case STATE_OPENCHECKSUM: break;
277     case STATE_TIMESTAMP:
278       /**
279        * we want to look for the newer timestamp
280        * of all resources to save it as the time
281        * the metadata was generated
282        */
283       timestamp = atoi(pd->content);
284       /** if the timestamp is invalid or just 0 ignore it */
285       if ( timestamp == 0 )
286         break;
287       if ( timestamp > pd->timestamp )
288         {
289           pd->timestamp = timestamp;
290           /* save the timestamp in the non solvable number 1 */
291           repo_set_num(pd->repo, -1, REPOSITORY_TIMESTAMP, pd->timestamp);
292         }
293       break;
294     case NUMSTATES: break;              
295     default:
296       break;
297     }
298
299   pd->state = pd->sbtab[pd->state];
300   pd->docontent = 0;
301   
302   return;
303 }
304
305
306 static void XMLCALL
307 characterData(void *userData, const XML_Char *s, int len)
308 {
309   struct parsedata *pd = userData;
310   int l;
311   char *c;
312   if (!pd->docontent) {
313 #if 0
314     char *dup = strndup( s, len );
315   fprintf(stderr, "Content: [%d]'%s'\n", pd->state, dup );
316   free( dup );
317 #endif
318     return;
319   }
320   l = pd->lcontent + len + 1;
321   if (l > pd->acontent)
322     {
323       pd->content = realloc(pd->content, l + 256);
324       pd->acontent = l + 256;
325     }
326   c = pd->content + pd->lcontent;
327   pd->lcontent += len;
328   while (len-- > 0)
329     *c++ = *s++;
330   *c = 0;
331 }
332
333 #define BUFF_SIZE 8192
334
335 void
336 repo_add_repomdxml(Repo *repo, FILE *fp, int flags)
337 {
338   Pool *pool = repo->pool;
339   struct parsedata pd;
340   pd.timestamp = 0;
341
342   char buf[BUFF_SIZE];
343   int i, l;
344   struct stateswitch *sw;
345
346   memset(&pd, 0, sizeof(pd));
347   for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
348     {
349       if (!pd.swtab[sw->from])
350         pd.swtab[sw->from] = sw;
351       pd.sbtab[sw->to] = sw->from;
352     }
353   pd.pool = pool;
354   pd.repo = repo;
355   pd.data = repo_add_repodata(pd.repo, 0);
356
357   pd.content = malloc(256);
358   pd.acontent = 256;
359   pd.lcontent = 0;
360   XML_Parser parser = XML_ParserCreate(NULL);
361   XML_SetUserData(parser, &pd);
362   XML_SetElementHandler(parser, startElement, endElement);
363   XML_SetCharacterDataHandler(parser, characterData);
364   for (;;)
365     {
366       l = fread(buf, 1, sizeof(buf), fp);
367       if (XML_Parse(parser, buf, l, l == 0) == XML_STATUS_ERROR)
368         {
369           fprintf(stderr, "repo_repomdxml: %s at line %u:%u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
370           exit(1);
371         }
372       if (l == 0)
373         break;
374     }
375   XML_ParserFree(parser);
376
377   if (pd.data)
378     repodata_internalize(pd.data);
379
380   free(pd.content);
381 }
382
383 /* EOF */