170520ff3901c4abf0c380875addd964d722eb8a
[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
58 start_element(void *userData, const xmlChar *name, const xmlChar **atts)
59 #else
60 static void XMLCALL
61 start_element(void *userData, const char *name, const char **atts)
62 #endif
63 {
64   struct solv_xmlparser *xmlp = userData;
65   struct solv_xmlparser_element *elements;
66   Id *elementhelper;
67   struct solv_xmlparser_element *el;
68   int i, oldstate;
69
70   if (xmlp->unknowncnt)
71     {
72       xmlp->unknowncnt++;
73       return;
74     }
75   elementhelper = xmlp->elementhelper;
76   elements = xmlp->elements;
77   oldstate = xmlp->state;
78   for (i = elementhelper[xmlp->nelements + oldstate]; i; i = elementhelper[i - 1])
79     if (!strcmp(elements[i - 1].element, (char *)name))
80       break;
81   if (!i)
82     {
83 #if 0
84       fprintf(stderr, "into unknown: %s\n", name);
85 #endif
86       xmlp->unknowncnt++;
87       return;
88     }
89   el = xmlp->elements + i - 1;
90   queue_push(&xmlp->elementq, xmlp->state);
91   xmlp->state = el->tostate;
92   xmlp->docontent = el->docontent;
93   xmlp->lcontent = 0;
94 #ifdef WITH_LIBXML2
95   if (!atts)
96     {
97       static const char *nullattr;
98       atts = (const xmlChar **)&nullattr;
99     }
100 #endif
101   if (xmlp->state != oldstate)
102     xmlp->startelement(xmlp, xmlp->state, el->element, (const char **)atts);
103 }
104
105 #ifdef WITH_LIBXML2
106 static void
107 end_element(void *userData, const xmlChar *name)
108 #else
109 static void XMLCALL
110 end_element(void *userData, const char *name)
111 #endif
112 {
113   struct solv_xmlparser *xmlp = userData;
114
115   if (xmlp->unknowncnt)
116     {
117       xmlp->unknowncnt--;
118       xmlp->lcontent = 0;
119       xmlp->docontent = 0;
120       return;
121     }
122   xmlp->content[xmlp->lcontent] = 0;
123   if (xmlp->elementq.count && xmlp->state != xmlp->elementq.elements[xmlp->elementq.count - 1])
124     xmlp->endelement(xmlp, xmlp->state, xmlp->content);
125   xmlp->state = queue_pop(&xmlp->elementq);
126   xmlp->docontent = 0;
127   xmlp->lcontent = 0;
128 }
129
130 void
131 solv_xmlparser_init(struct solv_xmlparser *xmlp,
132     struct solv_xmlparser_element *elements,
133     void *userdata,
134     void (*startelement)(struct solv_xmlparser *, int state, const char *name, const char **atts),
135     void (*endelement)(struct solv_xmlparser *, int state, char *content),
136     void (*errorhandler)(struct solv_xmlparser *, const char *errstr, unsigned int line, unsigned int column))
137 {
138   int i, nstates, nelements;
139   struct solv_xmlparser_element *el;
140   Id *elementhelper;
141
142   memset(xmlp, 0, sizeof(*xmlp));
143   nstates = 0;
144   nelements = 0;
145   for (el = elements; el->element; el++)
146     {
147       nelements++;
148       if (el->fromstate > nstates)
149         nstates = el->fromstate;
150       if (el->tostate > nstates)
151         nstates = el->tostate;
152     }
153   nstates++;
154
155   xmlp->elements = elements;
156   xmlp->nelements = nelements;
157   elementhelper = solv_calloc(nelements + nstates, sizeof(Id));
158   for (i = nelements - 1; i >= 0; i--)
159     {
160       int fromstate = elements[i].fromstate;
161       elementhelper[i] = elementhelper[nelements + fromstate];
162       elementhelper[nelements + fromstate] = i + 1;
163     }
164   xmlp->elementhelper = elementhelper;
165   queue_init(&xmlp->elementq);
166   xmlp->acontent = 256;
167   xmlp->content = solv_malloc(xmlp->acontent);
168
169   xmlp->userdata = userdata;
170   xmlp->startelement = startelement;
171   xmlp->endelement = endelement;
172   xmlp->errorhandler = errorhandler;
173 }
174
175 void
176 solv_xmlparser_free(struct solv_xmlparser *xmlp)
177 {
178   xmlp->elementhelper = solv_free(xmlp->elementhelper);
179   queue_free(&xmlp->elementq);
180   xmlp->content = solv_free(xmlp->content);
181 }
182
183 #ifdef WITH_LIBXML2
184
185 static inline int
186 create_parser(struct solv_xmlparser *xmlp)
187 {
188   /* delayed to parse_block so that we have the first bytes */
189   return 1;
190 }
191
192 static inline void
193 free_parser(struct solv_xmlparser *xmlp)
194 {
195   if (xmlp->parser)
196     xmlFreeParserCtxt(xmlp->parser);
197   xmlp->parser = 0;
198 }
199
200 static xmlParserCtxtPtr create_parser_ctx(struct solv_xmlparser *xmlp, char *buf, int l)
201 {
202   xmlSAXHandler sax;
203   memset(&sax, 0, sizeof(sax));
204   sax.startElement = start_element;
205   sax.endElement = end_element;
206   sax.characters = character_data;
207   return xmlCreatePushParserCtxt(&sax, xmlp, buf, l, NULL);
208 }
209
210 static inline int
211 parse_block(struct solv_xmlparser *xmlp, char *buf, int l)
212 {
213   if (!xmlp->parser)
214     {
215       int l2 = l > 4 ? 4 : 0;
216       xmlp->parser = create_parser_ctx(xmlp, buf, l2);
217       if (!xmlp->parser)
218         {
219           xmlp->errorhandler(xmlp, "could not create parser", 0, 0);
220           return 0;
221         }
222       buf += l2;
223       l -= l2;
224       if (l2 && !l)
225         return 1;
226     }
227   if (xmlParseChunk(xmlp->parser, buf, l, l == 0 ? 1 : 0))
228     {
229       xmlErrorPtr err = xmlCtxtGetLastError(xmlp->parser);
230       xmlp->errorhandler(xmlp, err->message, err->line, err->int2);
231       return 0;
232     }
233   return 1;
234 }
235
236 unsigned int
237 solv_xmlparser_lineno(struct solv_xmlparser *xmlp)
238 {
239   return (unsigned int)xmlSAX2GetLineNumber(xmlp->parser);
240 }
241
242 #else
243
244 static inline int
245 create_parser(struct solv_xmlparser *xmlp)
246 {
247   xmlp->parser = XML_ParserCreate(NULL);
248   if (!xmlp->parser)
249     return 0;
250   XML_SetUserData(xmlp->parser, xmlp);
251   XML_SetElementHandler(xmlp->parser, start_element, end_element);
252   XML_SetCharacterDataHandler(xmlp->parser, character_data);
253   return 1;
254 }
255
256 static inline void
257 free_parser(struct solv_xmlparser *xmlp)
258 {
259   XML_ParserFree(xmlp->parser);
260   xmlp->parser = 0;
261 }
262
263 static inline int
264 parse_block(struct solv_xmlparser *xmlp, char *buf, int l)
265 {
266   if (XML_Parse(xmlp->parser, buf, l, l == 0) == XML_STATUS_ERROR)
267     {
268       unsigned int line = XML_GetCurrentLineNumber(xmlp->parser);
269       unsigned int column = XML_GetCurrentColumnNumber(xmlp->parser);
270       xmlp->errorhandler(xmlp, XML_ErrorString(XML_GetErrorCode(xmlp->parser)), line, column);
271       return 0;
272     }
273   return 1;
274 }
275
276 unsigned int
277 solv_xmlparser_lineno(struct solv_xmlparser *xmlp)
278 {
279   return (unsigned int)XML_GetCurrentLineNumber(xmlp->parser);
280 }
281
282 #endif
283
284 void
285 solv_xmlparser_parse(struct solv_xmlparser *xmlp, FILE *fp)
286 {
287   char buf[8192];
288   int l;
289
290   xmlp->state = 0;
291   xmlp->unknowncnt = 0;
292   xmlp->docontent = 0;
293   xmlp->lcontent = 0;
294   queue_empty(&xmlp->elementq);
295
296   if (!create_parser(xmlp))
297     {
298       xmlp->errorhandler(xmlp, "could not create xml parser", 0, 0);
299       return;
300     }
301   for (;;)
302     {
303       l = fread(buf, 1, sizeof(buf), fp);
304       if (!parse_block(xmlp, buf, l) || !l)
305         break;
306     }
307   free_parser(xmlp);
308 }
309
310 char *
311 solv_xmlparser_contentspace(struct solv_xmlparser *xmlp, int l)
312 {
313   xmlp->lcontent = 0;
314   if (l > xmlp->acontent)
315     {    
316       xmlp->acontent = l + 256; 
317       xmlp->content = solv_realloc(xmlp->content, xmlp->acontent);
318     }    
319   return xmlp->content;
320 }
321