f387406dfb77ff93a6b50716177f5d271c533c80
[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   for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)  /* find name in statetable */
176     if (!strcmp(sw->ename, name))
177       break;
178   
179   if (sw->from != pd->state)
180     {
181 #if 1
182       fprintf(stderr, "into unknown: [%d]%s (from: %d)\n", sw->to, name, sw->from);
183       exit( 1 );
184 #endif
185       return;
186     }
187   pd->state = sw->to;
188   pd->docontent = sw->docontent;
189   pd->statedepth = pd->depth;
190   pd->lcontent = 0;
191   *pd->content = 0;
192
193   switch(pd->state)
194     {
195     case STATE_START: break;
196     case STATE_REPOMD:
197       {
198         const char *updstr;
199         char *value;
200         char *fvalue;
201
202         expirestr = (char*) find_attr("expire", atts);
203         if ( expirestr != NULL )
204           expire = atoi(expirestr);
205         if ( expire > 0 )
206           {
207             /* save the timestamp in the non solvable number 1 */
208             repo_set_num(pd->repo, -1, REPOSITORY_EXPIRE, expire);
209           }
210
211         updstr = find_attr("updates", atts);
212         if ( updstr != NULL )
213           {
214             value = strdup(updstr);
215             fvalue = value; /* save the first */
216             if ( value != NULL )
217               {
218                 char *sp[2];
219                 while (value)
220                   {
221                     int words = split_comma(value, sp, 2);
222                     if (!words)
223                       break;
224                     if (sp[0])
225                       repo_add_poolstr_array(pd->repo, -1, REPOSITORY_UPDATES, sp[0]);
226                     if (words == 1)
227                       break;
228                     value = sp[1];
229                   }
230                 free(fvalue);
231               }
232           }
233           break;
234         }
235     case STATE_DATA: break;
236     case STATE_LOCATION: break;
237     case STATE_CHECKSUM: break;
238     case STATE_TIMESTAMP: break;
239     case STATE_OPENCHECKSUM: break;
240     case NUMSTATES: break;
241     default: break;
242     }
243   return;
244 }
245
246 static void XMLCALL
247 endElement(void *userData, const char *name)
248 {
249   struct parsedata *pd = userData;
250   /* Pool *pool = pd->pool; */
251   int timestamp;
252
253 #if 0
254       fprintf(stderr, "end: %s\n", name);
255 #endif
256   if (pd->depth != pd->statedepth)
257     {
258       pd->depth--;
259 #if 1
260       fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
261 #endif
262       return;
263     }
264
265   pd->depth--;
266   pd->statedepth--;
267   switch (pd->state)
268     {
269     case STATE_START: break;
270     case STATE_REPOMD: break;
271     case STATE_DATA: break;
272     case STATE_LOCATION: break;
273     case STATE_CHECKSUM: break;
274     case STATE_OPENCHECKSUM: break;
275     case STATE_TIMESTAMP:
276       /**
277        * we want to look for the newer timestamp
278        * of all resources to save it as the time
279        * the metadata was generated
280        */
281       timestamp = atoi(pd->content);
282       /** if the timestamp is invalid or just 0 ignore it */
283       if ( timestamp == 0 )
284         break;
285       if ( timestamp > pd->timestamp )
286         {
287           pd->timestamp = timestamp;
288           /* save the timestamp in the non solvable number 1 */
289           repo_set_num(pd->repo, -1, REPOSITORY_TIMESTAMP, pd->timestamp);
290         }
291       break;
292     case NUMSTATES: break;              
293     default:
294       break;
295     }
296
297   pd->state = pd->sbtab[pd->state];
298   pd->docontent = 0;
299   
300   return;
301 }
302
303
304 static void XMLCALL
305 characterData(void *userData, const XML_Char *s, int len)
306 {
307   struct parsedata *pd = userData;
308   int l;
309   char *c;
310   if (!pd->docontent) {
311 #if 0
312     char *dup = strndup( s, len );
313   fprintf(stderr, "Content: [%d]'%s'\n", pd->state, dup );
314   free( dup );
315 #endif
316     return;
317   }
318   l = pd->lcontent + len + 1;
319   if (l > pd->acontent)
320     {
321       pd->content = realloc(pd->content, l + 256);
322       pd->acontent = l + 256;
323     }
324   c = pd->content + pd->lcontent;
325   pd->lcontent += len;
326   while (len-- > 0)
327     *c++ = *s++;
328   *c = 0;
329 }
330
331 #define BUFF_SIZE 8192
332
333 void
334 repo_add_repomdxml(Repo *repo, FILE *fp, int flags)
335 {
336   Pool *pool = repo->pool;
337   struct parsedata pd;
338   pd.timestamp = 0;
339
340   char buf[BUFF_SIZE];
341   int i, l;
342   struct stateswitch *sw;
343
344   memset(&pd, 0, sizeof(pd));
345   for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
346     {
347       if (!pd.swtab[sw->from])
348         pd.swtab[sw->from] = sw;
349       pd.sbtab[sw->to] = sw->from;
350     }
351   pd.pool = pool;
352   pd.repo = repo;
353   pd.data = repo_add_repodata(pd.repo, 0);
354
355   pd.content = malloc(256);
356   pd.acontent = 256;
357   pd.lcontent = 0;
358   XML_Parser parser = XML_ParserCreate(NULL);
359   XML_SetUserData(parser, &pd);
360   XML_SetElementHandler(parser, startElement, endElement);
361   XML_SetCharacterDataHandler(parser, characterData);
362   for (;;)
363     {
364       l = fread(buf, 1, sizeof(buf), fp);
365       if (XML_Parse(parser, buf, l, l == 0) == XML_STATUS_ERROR)
366         {
367           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));
368           exit(1);
369         }
370       if (l == 0)
371         break;
372     }
373   XML_ParserFree(parser);
374
375   if (pd.data)
376     repodata_internalize(pd.data);
377
378   free(pd.content);
379 }
380
381 /* EOF */