Add ENABLE_COMPLEX_DEPS flag
[platform/upstream/libsolv.git] / ext / solv_xmlparser.c
1 /*
2  * solv_xmlparser.c
3  *
4  * XML parser abstraction
5  *
6  * Copyright (c) 2017, Novell Inc.
7  *
8  * This program is licensed under the BSD license, read LICENSE.BSD
9  * for further information
10  */
11
12 #include <sys/types.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16
17 #ifdef WITH_LIBXML2
18 #include <libxml/parser.h>
19 #else
20 #include <expat.h>
21 #endif
22
23 #include "util.h"
24 #include "queue.h"
25 #include "solv_xmlparser.h"
26
27 static inline void
28 add_contentspace(struct solv_xmlparser *xmlp, int l)
29 {
30   l += xmlp->lcontent + 1;      /* plus room for trailing zero */
31   if (l > xmlp->acontent)
32     {    
33       xmlp->acontent = l + 256; 
34       xmlp->content = solv_realloc(xmlp->content, xmlp->acontent);
35     }    
36 }
37
38
39 #ifdef WITH_LIBXML2
40 static void
41 character_data(void *userData, const xmlChar *s, int len)
42 #else
43 static void XMLCALL
44 character_data(void *userData, const XML_Char *s, int len) 
45 #endif
46 {
47   struct solv_xmlparser *xmlp = userData;
48
49   if (!xmlp->docontent || !len)
50     return;
51   add_contentspace(xmlp, len);
52   memcpy(xmlp->content + xmlp->lcontent, s, len);
53   xmlp->lcontent += len; 
54 }
55
56 #ifdef WITH_LIBXML2
57 static void fixup_att_inplace(char *at)
58 {
59   while ((at = strchr(at, '&')) != 0)
60     {
61       at++;
62       if (!memcmp(at, "#38;", 4))
63         memmove(at, at + 4, strlen(at + 4) + 1);
64     }
65 }
66
67 static const xmlChar **fixup_atts(struct solv_xmlparser *xmlp, const xmlChar **atts)
68 {
69   size_t needsize = 0;
70   size_t natts;
71   char **at;
72
73   for (natts = 0; atts[natts]; natts++)
74     if (strchr((char *)atts[natts], '&'))
75       needsize += strlen((const char *)atts[natts]) + 1;
76   if (!needsize)
77     return atts;
78   at = xmlp->attsdata = solv_realloc(xmlp->attsdata, (natts + 1) * sizeof(xmlChar *) + needsize);
79   needsize = (natts + 1) * sizeof(xmlChar *);
80   for (natts = 0; atts[natts]; natts++)
81     {
82       at[natts] = (char *)atts[natts];
83       if (strchr(at[natts], '&'))
84         {
85           size_t l = strlen(at[natts]) + 1;
86           memcpy((char *)at + needsize, at[natts], l);
87           at[natts] = (char *)at + needsize;
88           needsize += l;
89           fixup_att_inplace(at[natts]);
90         }
91     }
92   at[natts] = 0;
93   return (const xmlChar **)at;
94 }
95 #endif
96
97 #ifdef WITH_LIBXML2
98 static void
99 start_element(void *userData, const xmlChar *name, const xmlChar **atts)
100 #else
101 static void XMLCALL
102 start_element(void *userData, const char *name, const char **atts)
103 #endif
104 {
105   struct solv_xmlparser *xmlp = userData;
106   struct solv_xmlparser_element *elements;
107   Id *elementhelper;
108   struct solv_xmlparser_element *el;
109   int i, oldstate;
110
111   if (xmlp->unknowncnt)
112     {
113       xmlp->unknowncnt++;
114       return;
115     }
116   elementhelper = xmlp->elementhelper;
117   elements = xmlp->elements;
118   oldstate = xmlp->state;
119   for (i = elementhelper[xmlp->nelements + oldstate]; i; i = elementhelper[i - 1])
120     if (!strcmp(elements[i - 1].element, (char *)name))
121       break;
122   if (!i)
123     {
124 #if 0
125       fprintf(stderr, "into unknown: %s\n", name);
126 #endif
127       xmlp->unknowncnt++;
128       return;
129     }
130   el = xmlp->elements + i - 1;
131   queue_push(&xmlp->elementq, xmlp->state);
132   xmlp->state = el->tostate;
133   xmlp->docontent = el->docontent;
134   xmlp->lcontent = 0;
135 #ifdef WITH_LIBXML2
136   if (!atts)
137     {
138       static const char *nullattr;
139       atts = (const xmlChar **)&nullattr;
140     }
141   else if (xmlp->state != oldstate)
142     atts = fixup_atts(xmlp, atts);
143 #endif
144   if (xmlp->state != oldstate)
145     xmlp->startelement(xmlp, xmlp->state, el->element, (const char **)atts);
146 }
147
148 #ifdef WITH_LIBXML2
149 static void
150 end_element(void *userData, const xmlChar *name)
151 #else
152 static void XMLCALL
153 end_element(void *userData, const char *name)
154 #endif
155 {
156   struct solv_xmlparser *xmlp = userData;
157
158   if (xmlp->unknowncnt)
159     {
160       xmlp->unknowncnt--;
161       xmlp->lcontent = 0;
162       xmlp->docontent = 0;
163       return;
164     }
165   xmlp->content[xmlp->lcontent] = 0;
166   if (xmlp->elementq.count && xmlp->state != xmlp->elementq.elements[xmlp->elementq.count - 1])
167     xmlp->endelement(xmlp, xmlp->state, xmlp->content);
168   xmlp->state = queue_pop(&xmlp->elementq);
169   xmlp->docontent = 0;
170   xmlp->lcontent = 0;
171 }
172
173 void
174 solv_xmlparser_init(struct solv_xmlparser *xmlp,
175     struct solv_xmlparser_element *elements,
176     void *userdata,
177     void (*startelement)(struct solv_xmlparser *, int state, const char *name, const char **atts),
178     void (*endelement)(struct solv_xmlparser *, int state, char *content))
179 {
180   int i, nstates, nelements;
181   struct solv_xmlparser_element *el;
182   Id *elementhelper;
183
184   memset(xmlp, 0, sizeof(*xmlp));
185   nstates = 0;
186   nelements = 0;
187   for (el = elements; el->element; el++)
188     {
189       nelements++;
190       if (el->fromstate > nstates)
191         nstates = el->fromstate;
192       if (el->tostate > nstates)
193         nstates = el->tostate;
194     }
195   nstates++;
196
197   xmlp->elements = elements;
198   xmlp->nelements = nelements;
199   elementhelper = solv_calloc(nelements + nstates, sizeof(Id));
200   for (i = nelements - 1; i >= 0; i--)
201     {
202       int fromstate = elements[i].fromstate;
203       elementhelper[i] = elementhelper[nelements + fromstate];
204       elementhelper[nelements + fromstate] = i + 1;
205     }
206   xmlp->elementhelper = elementhelper;
207   queue_init(&xmlp->elementq);
208   xmlp->acontent = 256;
209   xmlp->content = solv_malloc(xmlp->acontent);
210
211   xmlp->userdata = userdata;
212   xmlp->startelement = startelement;
213   xmlp->endelement = endelement;
214 }
215
216 void
217 solv_xmlparser_free(struct solv_xmlparser *xmlp)
218 {
219   xmlp->elementhelper = solv_free(xmlp->elementhelper);
220   queue_free(&xmlp->elementq);
221   xmlp->content = solv_free(xmlp->content);
222   xmlp->errstr = solv_free(xmlp->errstr);
223   xmlp->attsdata = solv_free(xmlp->attsdata);
224 }
225
226 static void
227 set_error(struct solv_xmlparser *xmlp, const char *errstr, unsigned int line, unsigned int column)
228 {
229   solv_free(xmlp->errstr);
230   xmlp->errstr = solv_strdup(errstr);
231   xmlp->line = line;
232   xmlp->column = column;
233 }
234
235 #ifdef WITH_LIBXML2
236
237 static inline int
238 create_parser(struct solv_xmlparser *xmlp)
239 {
240   /* delayed to parse_block so that we have the first bytes */
241   return 1;
242 }
243
244 static inline void
245 free_parser(struct solv_xmlparser *xmlp)
246 {
247   if (xmlp->parser)
248     xmlFreeParserCtxt(xmlp->parser);
249   xmlp->parser = 0;
250 }
251
252 static xmlParserCtxtPtr
253 create_parser_ctx(struct solv_xmlparser *xmlp, char *buf, int l)
254 {
255   xmlSAXHandler sax;
256   memset(&sax, 0, sizeof(sax));
257   sax.startElement = start_element;
258   sax.endElement = end_element;
259   sax.characters = character_data;
260   return xmlCreatePushParserCtxt(&sax, xmlp, buf, l, NULL);
261 }
262
263 static inline int
264 parse_block(struct solv_xmlparser *xmlp, char *buf, int l)
265 {
266   if (!xmlp->parser)
267     {
268       int l2 = l > 4 ? 4 : 0;
269       xmlp->parser = create_parser_ctx(xmlp, buf, l2);
270       if (!xmlp->parser)
271         {
272           set_error(xmlp, "could not create parser", 0, 0);
273           return 0;
274         }
275       buf += l2;
276       l -= l2;
277       if (l2 && !l)
278         return 1;
279     }
280   if (xmlParseChunk(xmlp->parser, buf, l, l == 0 ? 1 : 0))
281     {
282       xmlErrorPtr err = xmlCtxtGetLastError(xmlp->parser);
283       set_error(xmlp, err->message, err->line, err->int2);
284       return 0;
285     }
286   return 1;
287 }
288
289 unsigned int
290 solv_xmlparser_lineno(struct solv_xmlparser *xmlp)
291 {
292   return (unsigned int)xmlSAX2GetLineNumber(xmlp->parser);
293 }
294
295 #else
296
297 static inline int
298 create_parser(struct solv_xmlparser *xmlp)
299 {
300   xmlp->parser = XML_ParserCreate(NULL);
301   if (!xmlp->parser)
302     return 0;
303   XML_SetUserData(xmlp->parser, xmlp);
304   XML_SetElementHandler(xmlp->parser, start_element, end_element);
305   XML_SetCharacterDataHandler(xmlp->parser, character_data);
306   return 1;
307 }
308
309 static inline void
310 free_parser(struct solv_xmlparser *xmlp)
311 {
312   XML_ParserFree(xmlp->parser);
313   xmlp->parser = 0;
314 }
315
316 static inline int
317 parse_block(struct solv_xmlparser *xmlp, char *buf, int l)
318 {
319   if (XML_Parse(xmlp->parser, buf, l, l == 0) == XML_STATUS_ERROR)
320     {
321       set_error(xmlp, XML_ErrorString(XML_GetErrorCode(xmlp->parser)), XML_GetCurrentLineNumber(xmlp->parser), XML_GetCurrentColumnNumber(xmlp->parser));
322       return 0;
323     }
324   return 1;
325 }
326
327 unsigned int
328 solv_xmlparser_lineno(struct solv_xmlparser *xmlp)
329 {
330   return (unsigned int)XML_GetCurrentLineNumber(xmlp->parser);
331 }
332
333 #endif
334
335 int
336 solv_xmlparser_parse(struct solv_xmlparser *xmlp, FILE *fp)
337 {
338   char buf[8192];
339   int l, ret = SOLV_XMLPARSER_OK;
340
341   xmlp->state = 0;
342   xmlp->unknowncnt = 0;
343   xmlp->docontent = 0;
344   xmlp->lcontent = 0;
345   queue_empty(&xmlp->elementq);
346
347   if (!create_parser(xmlp))
348     {
349       set_error(xmlp, "could not create parser", 0, 0);
350       return SOLV_XMLPARSER_ERROR;
351     }
352   for (;;)
353     {
354       l = fread(buf, 1, sizeof(buf), fp);
355       if (!parse_block(xmlp, buf, l))
356         {
357           ret = SOLV_XMLPARSER_ERROR;
358           break;
359         }
360       if (!l)
361         break;
362     }
363   free_parser(xmlp);
364   return ret;
365 }
366
367 char *
368 solv_xmlparser_contentspace(struct solv_xmlparser *xmlp, int l)
369 {
370   xmlp->lcontent = 0;
371   if (l > xmlp->acontent)
372     {    
373       xmlp->acontent = l + 256; 
374       xmlp->content = solv_realloc(xmlp->content, xmlp->acontent);
375     }    
376   return xmlp->content;
377 }
378