re-add parsing of /etc/<xyz>-release (got lost in transistion from
[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 support also extension suseinfo format
66 <suseinfo>
67   <expire>timestamp</expire>
68   <products>
69     <id>...</id>
70   </products>
71   <kewwords>
72     <k>...</k>
73   </keywords>
74 </suseinfo>
75
76 */
77
78 enum state {
79   STATE_START,
80   /* extension tags */
81   STATE_SUSEINFO,
82   STATE_EXPIRE,
83   STATE_PRODUCTS,
84   STATE_PRODUCT,
85   STATE_KEYWORDS,
86   STATE_KEYWORD,
87   /* normal repomd.xml */
88   STATE_REPOMD,
89   STATE_DATA,
90   STATE_LOCATION,
91   STATE_CHECKSUM,
92   STATE_TIMESTAMP,
93   STATE_OPENCHECKSUM,
94   NUMSTATES
95 };
96
97 struct stateswitch {
98   enum state from;
99   char *ename;
100   enum state to;
101   int docontent;
102 };
103
104 /* !! must be sorted by first column !! */
105 static struct stateswitch stateswitches[] = {
106   /* suseinfo tags */
107   { STATE_START,       "repomd",          STATE_REPOMD, 0 },
108   { STATE_START,       "suseinfo",        STATE_SUSEINFO, 0 },  
109   { STATE_SUSEINFO,    "expire",          STATE_EXPIRE, 1 },  
110   { STATE_SUSEINFO,    "products",        STATE_PRODUCTS, 0 },  
111   { STATE_SUSEINFO,    "keywords",        STATE_KEYWORDS, 0 },  
112   { STATE_PRODUCTS,    "id",              STATE_PRODUCT, 1 },  
113   { STATE_KEYWORDS,    "k",               STATE_KEYWORD, 1 },  
114   /* standard tags */
115   { STATE_REPOMD,      "data",            STATE_DATA,  0 },
116   { STATE_DATA,        "location",        STATE_LOCATION, 0 },
117   { STATE_DATA,        "checksum",        STATE_CHECKSUM, 1 },  
118   { STATE_DATA,        "timestamp",       STATE_TIMESTAMP, 1 },
119   { STATE_DATA,        "open-checksum",    STATE_OPENCHECKSUM, 1 },
120   { NUMSTATES }
121 };
122
123 /*
124  * split l into m parts, store to sp[]
125  *  split at whitespace
126  */
127
128 static inline int
129 split_comma(char *l, char **sp, int m)
130 {
131   int i;
132   for (i = 0; i < m;)
133     {
134       while (*l == ',')
135         l++;
136       if (!*l)
137         break;
138       sp[i++] = l;
139       if (i == m)
140         break;
141       while (*l && !(*l == ','))
142         l++;
143       if (!*l)
144         break;
145       *l++ = 0;
146     }
147   return i;
148 }
149
150
151 struct parsedata {
152   int depth;
153   enum state state;
154   int statedepth;
155   char *content;
156   int lcontent;
157   int acontent;
158   int docontent;
159   Pool *pool;
160   Repo *repo;
161   Repodata *data;
162   
163   struct stateswitch *swtab[NUMSTATES];
164   enum state sbtab[NUMSTATES];
165   int timestamp;
166 };
167
168 /*
169  * find attribute
170  */
171
172 static inline const char *
173 find_attr(const char *txt, const char **atts)
174 {
175   for (; *atts; atts += 2)
176     {
177       if (!strcmp(*atts, txt))
178         return atts[1];
179     }
180   return 0;
181 }
182
183
184 static void XMLCALL
185 startElement(void *userData, const char *name, const char **atts)
186 {
187   struct parsedata *pd = userData;
188   /*Pool *pool = pd->pool;*/
189   struct stateswitch *sw;
190
191 #if 0
192   fprintf(stderr, "start: [%d]%s\n", pd->state, name);
193 #endif
194   if (pd->depth != pd->statedepth)
195     {
196       pd->depth++;
197       return;
198     }
199
200   pd->depth++;
201   if (!pd->swtab[pd->state])
202     return;
203   for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)  /* find name in statetable */
204     if (!strcmp(sw->ename, name))
205       break;
206   
207   if (sw->from != pd->state)
208     {
209 #if 1
210       fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
211       exit( 1 );
212 #endif
213       return;
214     }
215   pd->state = sw->to;
216   pd->docontent = sw->docontent;
217   pd->statedepth = pd->depth;
218   pd->lcontent = 0;
219   *pd->content = 0;
220
221   switch(pd->state)
222     {
223     case STATE_START: break;
224     case STATE_REPOMD:
225       {
226         const char *updstr;
227         char *value;
228         char *fvalue;
229
230         /* this should be OBSOLETE soon */
231         updstr = find_attr("updates", atts);
232         if ( updstr != NULL )
233           {
234             value = strdup(updstr);
235             fvalue = value; /* save the first */
236             if ( value != NULL )
237               {
238                 char *sp[2];
239                 while (value)
240                   {
241                     int words = split_comma(value, sp, 2);
242                     if (!words)
243                       break;
244                     if (sp[0])
245                       repo_add_poolstr_array(pd->repo, -1, REPOSITORY_UPDATES, sp[0]);
246                     if (words == 1)
247                       break;
248                     value = sp[1];
249                   }
250                 free(fvalue);
251               }
252           }
253           break;
254         }
255     case STATE_SUSEINFO: break;
256     case STATE_EXPIRE: break;
257     case STATE_PRODUCTS: break;
258     case STATE_PRODUCT: break;
259     case STATE_KEYWORDS: break;
260     case STATE_KEYWORD: break;
261     case STATE_DATA: break;
262     case STATE_LOCATION: break;
263     case STATE_CHECKSUM: break;
264     case STATE_TIMESTAMP: break;
265     case STATE_OPENCHECKSUM: break;
266     case NUMSTATES: break;
267     default: break;
268     }
269   return;
270 }
271
272 static void XMLCALL
273 endElement(void *userData, const char *name)
274 {
275   struct parsedata *pd = userData;
276   /* Pool *pool = pd->pool; */
277   int timestamp;
278
279 #if 0
280       fprintf(stderr, "end: %s\n", name);
281 #endif
282   if (pd->depth != pd->statedepth)
283     {
284       pd->depth--;
285 #if 1
286       fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
287 #endif
288       return;
289     }
290
291   pd->depth--;
292   pd->statedepth--;
293   switch (pd->state)
294     {
295     case STATE_START: break;
296     case STATE_REPOMD: 
297       /* save the timestamp in the non solvable number 1 */
298       if ( pd->timestamp > 0 )
299         repo_set_num(pd->repo, -1, REPOSITORY_TIMESTAMP, pd->timestamp);
300       break;
301     case STATE_DATA: break;
302     case STATE_LOCATION: break;
303     case STATE_CHECKSUM: break;
304     case STATE_OPENCHECKSUM: break;
305     case STATE_TIMESTAMP:
306       {
307         /**
308          * we want to look for the newer timestamp
309          * of all resources to save it as the time
310          * the metadata was generated
311          */
312         timestamp = atoi(pd->content);
313         /** if the timestamp is invalid or just 0 ignore it */
314         if ( timestamp == 0 )
315           break;
316         if ( timestamp > pd->timestamp )
317           {
318             pd->timestamp = timestamp;
319           }
320         break;
321       }
322     case STATE_EXPIRE:
323       {
324         int expire = 0;
325         if ( pd->content )
326           {
327             expire = atoi(pd->content);
328             if ( expire > 0 )
329               {
330                 /* save the timestamp in the non solvable number 1 */
331                 repo_set_num(pd->repo, -1, REPOSITORY_EXPIRE, expire);
332               }
333           }
334         break;
335       }
336     case STATE_PRODUCT:
337       {
338         if ( pd->content )
339           repo_add_poolstr_array(pd->repo, -1, REPOSITORY_PRODUCTS, pd->content);
340         break;
341       }
342     case STATE_KEYWORD:
343       {
344         if ( pd->content )
345           repo_add_poolstr_array(pd->repo, -1, REPOSITORY_KEYWORDS, pd->content);
346         break;
347       }
348     case STATE_SUSEINFO: break;
349     case STATE_PRODUCTS: break;
350     case STATE_KEYWORDS: break;
351     case NUMSTATES: break;              
352     default:
353       break;
354     }
355
356   pd->state = pd->sbtab[pd->state];
357   pd->docontent = 0;
358   
359   return;
360 }
361
362
363 static void XMLCALL
364 characterData(void *userData, const XML_Char *s, int len)
365 {
366   struct parsedata *pd = userData;
367   int l;
368   char *c;
369   if (!pd->docontent) {
370 #if 0
371     char *dup = strndup( s, len );
372   fprintf(stderr, "Content: [%d]'%s'\n", pd->state, dup );
373   free( dup );
374 #endif
375     return;
376   }
377   l = pd->lcontent + len + 1;
378   if (l > pd->acontent)
379     {
380       pd->content = realloc(pd->content, l + 256);
381       pd->acontent = l + 256;
382     }
383   c = pd->content + pd->lcontent;
384   pd->lcontent += len;
385   while (len-- > 0)
386     *c++ = *s++;
387   *c = 0;
388 }
389
390 #define BUFF_SIZE 8192
391
392 void
393 repo_add_repomdxml(Repo *repo, FILE *fp, int flags)
394 {
395   Pool *pool = repo->pool;
396   struct parsedata pd;
397   pd.timestamp = 0;
398
399   char buf[BUFF_SIZE];
400   int i, l;
401   struct stateswitch *sw;
402
403   memset(&pd, 0, sizeof(pd));
404   for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
405     {
406       if (!pd.swtab[sw->from])
407         pd.swtab[sw->from] = sw;
408       pd.sbtab[sw->to] = sw->from;
409     }
410   pd.pool = pool;
411   pd.repo = repo;
412   pd.data = repo_add_repodata(pd.repo, 0);
413
414   pd.content = malloc(256);
415   pd.acontent = 256;
416   pd.lcontent = 0;
417   XML_Parser parser = XML_ParserCreate(NULL);
418   XML_SetUserData(parser, &pd);
419   XML_SetElementHandler(parser, startElement, endElement);
420   XML_SetCharacterDataHandler(parser, characterData);
421   for (;;)
422     {
423       l = fread(buf, 1, sizeof(buf), fp);
424       if (XML_Parse(parser, buf, l, l == 0) == XML_STATUS_ERROR)
425         {
426           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));
427           exit(1);
428         }
429       if (l == 0)
430         break;
431     }
432   XML_ParserFree(parser);
433
434   if (pd.data)
435     repodata_internalize(pd.data);
436
437   free(pd.content);
438 }
439
440 /* EOF */