- don't abort on unknown elements
[platform/upstream/libsolv.git] / tools / repo_zyppdb.c
1 /*
2  * repo_zyppdb.c
3  *
4  * Parses /var/lib/zypp/db/products/...
5  * The are old (pre Code11) products. See bnc#429177
6  *
7  *
8  * Copyright (c) 2008, Novell Inc.
9  *
10  * This program is licensed under the BSD license, read LICENSE.BSD
11  * for further information
12  */
13
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <unistd.h>
17 #include <limits.h>
18 #include <fcntl.h>
19 #include <ctype.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <assert.h>
24 #include <dirent.h>
25 #include <expat.h>
26
27 #include "pool.h"
28 #include "repo.h"
29 #include "util.h"
30 #define DISABLE_SPLIT
31 #include "tools_util.h"
32 #include "repo_content.h"
33
34
35 //#define DUMPOUT 0
36
37 enum state {
38   STATE_START,           // 0
39   STATE_PRODUCT,         // 1
40   STATE_NAME,            // 2
41   STATE_VERSION,         // 3
42   STATE_ARCH,            // 4
43   STATE_SUMMARY,         // 5
44   STATE_VENDOR,          // 6
45   STATE_INSTALLTIME,     // 7
46   NUMSTATES              // 0
47 };
48
49 struct stateswitch {
50   enum state from;
51   char *ename;
52   enum state to;
53   int docontent;
54 };
55
56 /* !! must be sorted by first column !! */
57 static struct stateswitch stateswitches[] = {
58   { STATE_START,     "product",       STATE_PRODUCT,       0 },
59   { STATE_PRODUCT,   "name",          STATE_NAME,          1 },
60   { STATE_PRODUCT,   "version",       STATE_VERSION,       0 },
61   { STATE_PRODUCT,   "arch",          STATE_ARCH,          1 },
62   { STATE_PRODUCT,   "summary",       STATE_SUMMARY,       1 },
63   { STATE_PRODUCT,   "install-time",  STATE_INSTALLTIME,   1 },
64   { STATE_PRODUCT,   "vendor",        STATE_VENDOR,        1 },
65   { NUMSTATES }
66 };
67
68 struct parsedata {
69   int depth;
70   enum state state;
71   int statedepth;
72   char *content;
73   int lcontent;
74   int acontent;
75   int docontent;
76   Pool *pool;
77   Repo *repo;
78   Repodata *data;
79
80   struct stateswitch *swtab[NUMSTATES];
81   enum state sbtab[NUMSTATES];
82
83   const char *tmplang;
84
85   Solvable *solvable;
86   Id handle;
87
88   Id langcache[ID_NUM_INTERNAL];
89 };
90
91
92 /*
93  * find_attr
94  * find value for xml attribute
95  * I: txt, name of attribute
96  * I: atts, list of key/value attributes
97  * I: dup, strdup it
98  * O: pointer to value of matching key, or NULL
99  *
100  */
101
102 static inline const char *
103 find_attr(const char *txt, const char **atts, int dup)
104 {
105   for (; *atts; atts += 2)
106     {
107       if (!strcmp(*atts, txt))
108         return dup ? strdup(atts[1]) : atts[1];
109     }
110   return 0;
111 }
112
113
114 /*
115  * create localized tag
116  */
117
118 static Id
119 langtag(struct parsedata *pd, Id tag, const char *language)
120 {
121   if (language && !language[0])
122     language = 0;
123   if (!language || tag >= ID_NUM_INTERNAL)
124     return pool_id2langid(pd->repo->pool, tag, language, 1);
125   if (!pd->langcache[tag])
126     pd->langcache[tag] = pool_id2langid(pd->repo->pool, tag, language, 1);
127   return pd->langcache[tag];
128 }
129
130
131 /*
132  * XML callback: startElement
133  */
134
135 static void XMLCALL
136 startElement(void *userData, const char *name, const char **atts)
137 {
138   struct parsedata *pd = userData;
139   Pool *pool = pd->pool;
140   Solvable *s = pd->solvable;
141   struct stateswitch *sw;
142
143 #if 0
144       fprintf(stderr, "start: [%d]%s\n", pd->state, name);
145 #endif
146   if (pd->depth != pd->statedepth)
147     {
148       pd->depth++;
149       return;
150     }
151
152   pd->depth++;
153   if (!pd->swtab[pd->state])    /* no statetable -> no substates */
154     {
155 #if 0
156       fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
157 #endif
158       return;
159     }
160   for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)  /* find name in statetable */
161     if (!strcmp(sw->ename, name))
162       break;
163
164   if (sw->from != pd->state)
165     {
166 #if 0
167       fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
168 #endif
169       return;
170     }
171   pd->state = sw->to;
172   pd->docontent = sw->docontent;
173   pd->statedepth = pd->depth;
174   pd->lcontent = 0;
175   *pd->content = 0;
176
177   switch(pd->state)
178     {
179     case STATE_PRODUCT:
180       {
181         /* parse 'type' */
182         const char *type = find_attr("type", atts, 0);
183         s = pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
184         repodata_extend(pd->data, s - pool->solvables);
185         pd->handle = s - pool->solvables;
186         if (type)
187           {
188             repodata_set_str(pd->data, pd->handle, PRODUCT_TYPE, type);
189           }
190       }
191       break;
192     case STATE_VERSION:
193       {
194         const char *ver = find_attr("ver", atts, 0);
195         const char *rel = find_attr("rel", atts, 0);
196         /* const char *epoch = find_attr("epoch", atts, 1); ignored */
197         s->evr = makeevr(pd->pool, join2(ver, "-", rel));
198       }
199       break;
200       /* <summary lang="xy">... */
201     case STATE_SUMMARY:
202       pd->tmplang = find_attr("lang", atts, 1);
203       break;
204     default:
205       break;
206     }
207 }
208
209
210 static void XMLCALL
211 endElement(void *userData, const char *name)
212 {
213   struct parsedata *pd = userData;
214   Solvable *s = pd->solvable;
215
216 #if 0
217       fprintf(stderr, "end: [%d]%s\n", pd->state, name);
218 #endif
219   if (pd->depth != pd->statedepth)
220     {
221       pd->depth--;
222 #if 0
223       fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
224 #endif
225       return;
226     }
227
228   pd->depth--;
229   pd->statedepth--;
230
231   switch (pd->state)
232     {
233     case STATE_PRODUCT:
234
235       if (!s->arch)
236         s->arch = ARCH_NOARCH;
237       if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
238         s->provides = repo_addid_dep(pd->repo, s->provides, rel2id(pd->pool, s->name, s->evr, REL_EQ, 1), 0);
239       pd->solvable = 0;
240       break;
241     case STATE_NAME:
242       s->name = str2id(pd->pool, join2("product", ":", pd->content), 1);
243       break;
244     case STATE_ARCH:
245       s->arch = str2id(pd->pool, pd->content, 1);
246       break;
247     case STATE_SUMMARY:
248       repodata_set_str(pd->data, pd->handle, langtag(pd, SOLVABLE_SUMMARY, pd->tmplang), pd->content);
249       pd->tmplang = sat_free((void *)pd->tmplang);
250       break;
251     case STATE_VENDOR:
252       s->vendor = str2id(pd->pool, pd->content, 1);
253       break;
254     case STATE_INSTALLTIME:
255       repodata_set_num(pd->data, pd->handle, SOLVABLE_INSTALLTIME, atol(pd->content));
256     default:
257       break;
258     }
259
260   pd->state = pd->sbtab[pd->state];
261   pd->docontent = 0;
262
263 #if 0
264       fprintf(stderr, "end: [%s] -> %d\n", name, pd->state);
265 #endif
266 }
267
268
269 static void XMLCALL
270 characterData(void *userData, const XML_Char *s, int len)
271 {
272   struct parsedata *pd = userData;
273   int l;
274   char *c;
275   if (!pd->docontent) {
276 #if 0
277     char *dup = strndup( s, len );
278   fprintf(stderr, "Content: [%d]'%s'\n", pd->state, dup );
279   free( dup );
280 #endif
281     return;
282   }
283   l = pd->lcontent + len + 1;
284   if (l > pd->acontent)
285     {
286       pd->content = realloc(pd->content, l + 256);
287       pd->acontent = l + 256;
288     }
289   c = pd->content + pd->lcontent;
290   pd->lcontent += len;
291   while (len-- > 0)
292     *c++ = *s++;
293   *c = 0;
294 }
295
296 #define BUFF_SIZE 8192
297
298
299 /*
300  * add single product to repo
301  *
302  */
303
304 static void
305 repo_add_product(struct parsedata *pd, Repodata *data, FILE *fp)
306 {
307   char buf[BUFF_SIZE];
308   int l;
309
310   XML_Parser parser = XML_ParserCreate(NULL);
311   XML_SetUserData(parser, pd);
312   XML_SetElementHandler(parser, startElement, endElement);
313   XML_SetCharacterDataHandler(parser, characterData);
314
315   for (;;)
316     {
317       l = fread(buf, 1, sizeof(buf), fp);
318       if (XML_Parse(parser, buf, l, l == 0) == XML_STATUS_ERROR)
319         {
320           fprintf(stderr, "repo_zyppdb: %s at line %u:%u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
321           exit(1);
322         }
323       if (l == 0)
324         break;
325     }
326   XML_ParserFree(parser);
327   return;
328 }
329
330
331
332 /*
333  * parse dir for products
334  */
335
336 static void
337 parse_dir(DIR *dir, const char *path, struct parsedata *pd, Repodata *repodata)
338 {
339   struct dirent *entry;
340
341   while ((entry = readdir(dir)))
342     {
343       int len = strlen(entry->d_name);
344       if (len < 3)   /* skip '.' and '..' */
345         continue;
346       char *fullpath = join2(path, "/", entry->d_name);
347       FILE *fp = fopen(fullpath, "r");
348       if (!fp)
349         {
350           perror(fullpath);
351           break;
352         }
353       repo_add_product(pd, repodata, fp);
354       fclose(fp);
355     }
356 }
357
358
359 /*
360  * read all installed products
361  *
362  * parse each one as a product
363  */
364
365 void
366 repo_add_zyppdb_products(Repo *repo, Repodata *repodata, const char *fullpath, DIR *dir)
367 {
368   int i;
369   struct parsedata pd;
370   struct stateswitch *sw;
371
372   memset(&pd, 0, sizeof(pd));
373   pd.repo = repo;
374   pd.pool = repo->pool;
375   pd.data = repodata;
376
377   pd.content = malloc(256);
378   pd.acontent = 256;
379
380   for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
381     {
382       if (!pd.swtab[sw->from])
383         pd.swtab[sw->from] = sw;
384       pd.sbtab[sw->to] = sw->from;
385     }
386
387   parse_dir(dir, fullpath, &pd, repodata);
388
389   sat_free((void *)pd.tmplang);
390   free(pd.content);
391   join_freemem();
392 }
393
394 /* EOF */