1 /* libcomps - C alternative to yum.comps library
2  * Copyright (C) 2013 Jindrich Luza
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to  Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
17  * USA
18  */
19 
20 #include <stdio.h>
21 #include <ctype.h>
22 #include <signal.h>
23 
24 #include "comps_types.h"
25 #include "comps_parse.h"
26 #include "comps_elem.h"
27 
28 #define BUFF_SIZE 1024
29 
30 #define XML_DTD
31 void comps_parse_check_attributes(COMPS_Parsed *parsed, COMPS_Elem* elem);
32 
__comps_is_whitespace_only(const char * s,int len)33 inline unsigned __comps_is_whitespace_only(const char * s, int len)
34 {
35     int i;
36     for (i = 0; i<len; i++) {
37         if (!isspace(s[i])) return 0;
38     }
39     return 1;
40 }
comps_parse_parsed_create()41 COMPS_Parsed* comps_parse_parsed_create() {
42     COMPS_Parsed *ret;
43     ret =  malloc(sizeof(COMPS_Parsed));
44     if (ret == NULL) return NULL;
45     return ret;
46 }
47 
comps_parse_parsed_init(COMPS_Parsed * parsed,const char * encoding,char log_stdout)48 unsigned comps_parse_parsed_init(COMPS_Parsed * parsed, const char * encoding,
49                                  char log_stdout) {
50     if (parsed == NULL)
51         return 0;
52     parsed->parser = XML_ParserCreate((const XML_Char*)encoding);
53 
54     XML_SetDefaultHandler(parsed->parser, &comps_parse_def_handler);
55     XML_SetElementHandler(parsed->parser, &comps_parse_start_elem_handler,
56                               &comps_parse_end_elem_handler);
57     XML_SetCharacterDataHandler(parsed->parser, &comps_parse_char_data_handler);
58     XML_SetStartDoctypeDeclHandler(parsed->parser, &comps_parse_start_doctype);
59 
60     parsed->enc = encoding;
61     parsed->elem_stack = comps_hslist_create();
62     parsed->text_buffer = comps_hslist_create();
63     parsed->text_buffer_len = 0;
64     parsed->text_buffer_pt = NULL;
65     parsed->tmp_buffer = NULL;
66     parsed->log = COMPS_OBJECT_CREATE(COMPS_Log, NULL);
67     parsed->log->std_out = log_stdout;
68     parsed->comps_doc = NULL;
69     parsed->doctype_name = NULL;
70     parsed->doctype_sysid = NULL;
71     parsed->doctype_pubid = NULL;
72     parsed->fatal_error = 0;
73     if (parsed->elem_stack == NULL || parsed->text_buffer == NULL) {
74         if (!parsed->elem_stack)
75             comps_hslist_destroy(&parsed->elem_stack);
76         if (!parsed->text_buffer)
77             comps_hslist_destroy(&parsed->text_buffer);
78         COMPS_OBJECT_DESTROY(parsed->log);
79         XML_ParserFree(parsed->parser);
80         free(parsed);
81         return 0;
82     }
83     comps_hslist_init(parsed->elem_stack, NULL, NULL, &comps_elem_destroy);
84     comps_hslist_init(parsed->text_buffer, NULL, NULL, &free);
85     XML_SetUserData(parsed->parser, parsed);
86     return 1;
87 }
88 
comps_parse_parsed_reinit(COMPS_Parsed * parsed)89 void comps_parse_parsed_reinit(COMPS_Parsed *parsed) {
90     parsed->fatal_error = 0;
91     XML_ParserReset(parsed->parser, parsed->enc);
92     XML_SetDefaultHandler(parsed->parser, &comps_parse_def_handler);
93     XML_SetElementHandler(parsed->parser, &comps_parse_start_elem_handler,
94                                           &comps_parse_end_elem_handler);
95     XML_SetCharacterDataHandler(parsed->parser, &comps_parse_char_data_handler);
96     XML_SetStartDoctypeDeclHandler(parsed->parser, &comps_parse_start_doctype);
97 
98 
99     XML_SetUserData(parsed->parser, parsed);
100     comps_hslist_clear(parsed->elem_stack);
101     comps_hslist_clear(parsed->text_buffer);
102     comps_hslist_clear(parsed->log->entries);
103     COMPS_OBJECT_DESTROY(parsed->comps_doc);
104     COMPS_OBJECT_DESTROY(parsed->doctype_name);
105     COMPS_OBJECT_DESTROY(parsed->doctype_sysid);
106     COMPS_OBJECT_DESTROY(parsed->doctype_pubid);
107     parsed->doctype_name = NULL;
108     parsed->doctype_sysid = NULL;
109     parsed->doctype_pubid = NULL;
110 }
111 
comps_parse_parsed_destroy(COMPS_Parsed * parsed)112 void comps_parse_parsed_destroy(COMPS_Parsed *parsed) {
113     comps_hslist_destroy(&parsed->elem_stack);
114     comps_hslist_destroy(&parsed->text_buffer);
115     COMPS_OBJECT_DESTROY(parsed->log);
116     COMPS_OBJECT_DESTROY(parsed->comps_doc);
117     COMPS_OBJECT_DESTROY(parsed->doctype_name);
118     COMPS_OBJECT_DESTROY(parsed->doctype_sysid);
119     COMPS_OBJECT_DESTROY(parsed->doctype_pubid);
120     XML_ParserFree(parsed->parser);
121     free(parsed);
122 }
123 
empty_xmlGenericErrorFunc(void * ctx,const char * msg,...)124 void empty_xmlGenericErrorFunc(void * ctx, const char * msg, ...) {
125     (void) ctx;
126     (void) msg;
127 }
128 
comps_parse_start_doctype(void * userData,const XML_Char * doctypeName,const XML_Char * sysid,const XML_Char * pubid,int standalone)129 void comps_parse_start_doctype(void *userData,
130                                const XML_Char *doctypeName,
131                                const XML_Char *sysid,
132                                const XML_Char *pubid,
133                                int standalone) {
134     #define parsed ((COMPS_Parsed*)userData)
135     parsed->doctype_name = comps_str(doctypeName);
136     parsed->doctype_sysid = comps_str(sysid);
137     parsed->doctype_pubid = comps_str(pubid);
138     #undef parsed
139 }
140 
comps_parse_validate_dtd(char * filename,char * dtd_file)141 int comps_parse_validate_dtd(char *filename, char *dtd_file) {
142     xmlDocPtr fptr;
143     xmlDtdPtr dtd_ptr;
144     xmlValidCtxtPtr vctxt;
145     int ret;
146     xmlErrorPtr err;
147 
148     fptr = xmlReadFile(filename, NULL, 0);
149     if (fptr == NULL) {
150         return -1;
151     }
152 
153     dtd_ptr = xmlParseDTD(NULL, (const xmlChar*)dtd_file);
154     if (dtd_ptr == NULL) {
155         xmlFreeDoc(fptr);
156         return -2;
157     }
158 
159     vctxt = xmlNewValidCtxt();
160     xmlSetGenericErrorFunc(vctxt, empty_xmlGenericErrorFunc);
161     if (vctxt == NULL) {
162         xmlFreeDoc(fptr);
163         xmlFreeDtd(dtd_ptr);
164         return -3;
165     }
166     ret = xmlValidateDtd(vctxt, fptr, dtd_ptr);
167     if (!ret) {
168         err = xmlGetLastError();
169         printf("%s\n", err->message);
170         ret = -err->code;
171     }
172     xmlFreeDoc(fptr);
173     xmlFreeDtd(dtd_ptr);
174     xmlFreeValidCtxt(vctxt);
175     return ret;
176 }
177 
__comps_after_parse(COMPS_Parsed * parsed)178 void __comps_after_parse(COMPS_Parsed *parsed) {
179     if (parsed->doctype_name) {
180         COMPS_OBJECT_DESTROY(parsed->comps_doc->doctype_name);
181         parsed->comps_doc->doctype_name = (COMPS_Str*)
182                                   COMPS_OBJECT_INCREF(parsed->doctype_name);
183     } else {
184         //parsed->comps_doc->doctype_name = comps_str(comps_default_doctype_name);
185     }
186     if (parsed->doctype_sysid) {
187         COMPS_OBJECT_DESTROY(parsed->comps_doc->doctype_sysid);
188         parsed->comps_doc->doctype_sysid = (COMPS_Str*)
189                                   COMPS_OBJECT_INCREF(parsed->doctype_sysid);
190     } else {
191         //parsed->comps_doc->doctype_sysid = comps_str(comps_default_doctype_sysid);
192     }
193     if (parsed->doctype_pubid) {
194         COMPS_OBJECT_DESTROY(parsed->comps_doc->doctype_pubid);
195         parsed->comps_doc->doctype_pubid = (COMPS_Str*)
196                                   COMPS_OBJECT_INCREF(parsed->doctype_pubid);
197     } else {
198         //parsed->comps_doc->doctype_pubid = comps_str(comps_default_doctype_pubid);
199     }
200 }
201 
comps_parse_file(COMPS_Parsed * parsed,FILE * f,COMPS_DefaultsOptions * options)202 signed char comps_parse_file(COMPS_Parsed *parsed, FILE *f,
203                              COMPS_DefaultsOptions *options) {
204     void *buff;
205     int bytes_read;
206 
207     if (!f) {
208         comps_log_error(parsed->log, COMPS_ERR_READFD, 0);
209         parsed->fatal_error = 1;
210         return -1;
211     }
212     comps_parse_parsed_reinit(parsed);
213     if (options)
214         parsed->def_options = options;
215     else
216         parsed->def_options = &COMPS_DDefaultsOptions;
217 
218     for (;;) {
219         buff = XML_GetBuffer(parsed->parser, BUFF_SIZE);
220         if (buff == NULL) {
221             comps_log_error(parsed->log, COMPS_ERR_MALLOC, 0);
222             fclose(f);
223             raise(SIGABRT);
224             return -1;
225         }
226         bytes_read = fread(buff, sizeof(char), BUFF_SIZE, f);
227         if (bytes_read < 0)
228             comps_log_error(parsed->log, COMPS_ERR_READFD, 0);
229         if (!XML_ParseBuffer(parsed->parser, bytes_read, bytes_read == 0)) {
230             comps_log_error_x(parsed->log, COMPS_ERR_PARSER, 3,
231                           comps_num(XML_GetCurrentLineNumber(parsed->parser)),
232                           comps_num(XML_GetCurrentColumnNumber(parsed->parser)),
233                           comps_str(XML_ErrorString(
234                                     XML_GetErrorCode(parsed->parser))));
235             parsed->fatal_error = 1;
236         }
237         if (bytes_read == 0) break;
238     }
239     fclose(f);
240     __comps_after_parse(parsed);
241 
242     if (parsed->fatal_error == 0 && parsed->log->entries->first == NULL)
243         return 0;
244     else if (parsed->fatal_error != 1)
245         return 1;
246     else
247         return -1;
248 }
249 
comps_parse_str(COMPS_Parsed * parsed,char * str,COMPS_DefaultsOptions * options)250 signed char comps_parse_str(COMPS_Parsed *parsed, char *str,
251                             COMPS_DefaultsOptions *options) {
252     if (options)
253         parsed->def_options = options;
254     else
255         parsed->def_options = &COMPS_DDefaultsOptions;
256 
257     if (!XML_Parse(parsed->parser, str, strlen(str), 1)) {
258         comps_log_error_x(parsed->log, COMPS_ERR_PARSER, 3,
259                           comps_num(XML_GetCurrentLineNumber(parsed->parser)),
260                           comps_num(XML_GetCurrentColumnNumber(parsed->parser)),
261                           comps_str(XML_ErrorString(
262                                             XML_GetErrorCode(parsed->parser))));
263         parsed->fatal_error = 1;
264     }
265     __comps_after_parse(parsed);
266 
267     if (parsed->fatal_error == 0 && parsed->log->entries->first == NULL)
268         return 0;
269     else if (parsed->fatal_error != 1)
270         return 1;
271     else
272         return -1;
273 }
274 
comps_parse_end_elem_handler(void * userData,const XML_Char * s)275 void comps_parse_end_elem_handler(void *userData, const XML_Char *s) {
276     //COMPS_ListItem * item;
277     char * alltext = NULL;
278     //size_t item_len;
279     //int index=0;
280     void *data;
281     #define parser_line XML_GetCurrentLineNumber(((COMPS_Parsed*)userData)->parser)
282     #define parser_col XML_GetCurrentColumnNumber(((COMPS_Parsed*)userData)->parser)
283     #define parsed ((COMPS_Parsed*)userData)
284     #define last_elem ((COMPS_Elem*)parsed->elem_stack->last->data)
285 
286     /* check if there's some text in recent element - are we interested in?*/
287     if (parsed->text_buffer_len) {
288         alltext = malloc(sizeof(char)*(parsed->text_buffer_len + 1));
289         if (alltext == NULL) {
290             comps_log_error(parsed->log, COMPS_ERR_MALLOC, 0);
291             raise(SIGABRT);
292         }
293         alltext[0]=0;
294     }
295     while ((data = comps_hslist_shift(parsed->text_buffer)) != NULL) {
296         //item_len = strlen((char*)data);
297         alltext = strcat(alltext, data);
298         free(data);
299         //index += item_len;
300     }
301     /* set zero char at the end of string */
302     if (alltext)
303         memset(alltext+parsed->text_buffer_len, 0, sizeof(char));
304     parsed->tmp_buffer = alltext;
305 
306     /* start postprocess for currently processed elements */
307     if (comps_elem_get_type(s) == last_elem->type) {
308         if (last_elem->valid && COMPS_ElemInfos[last_elem->type]->postproc) {
309             COMPS_ElemInfos[last_elem->type]->postproc((COMPS_Parsed*)userData,
310                                                        last_elem);
311         }
312         if (last_elem->valid && parsed->tmp_buffer) {
313             comps_log_error_x(parsed->log, COMPS_ERR_TEXT_BETWEEN, 3,
314                               comps_str(parsed->tmp_buffer), comps_num(parser_line),
315                               comps_num(parser_col));
316         }
317         /* finaly, remove element from element stack */
318         data = comps_hslist_pop(parsed->elem_stack);
319         comps_elem_destroy(data);
320     }
321     free(alltext);
322     parsed->text_buffer_len = 0;
323     #undef parsed
324     #undef parser_line
325     #undef parser_col
326 }
327 
comps_parse_def_handler(void * userData,const XML_Char * s,int len)328 void comps_parse_def_handler(void *userData, const XML_Char *s, int len) {
329     (void) userData;
330     (void) s;
331     (void) len;
332 }
333 
comps_parse_start_elem_handler(void * userData,const XML_Char * s,const XML_Char ** attrs)334 void comps_parse_start_elem_handler(void *userData,
335                               const XML_Char *s,
336                               const XML_Char **attrs) {
337     #define parser_line XML_GetCurrentLineNumber(((COMPS_Parsed*)userData)->parser)
338     #define parser_col XML_GetCurrentColumnNumber(((COMPS_Parsed*)userData)->parser)
339     #define ELEMINFO  COMPS_ElemInfos[elem->type]
340     #define LAST ((COMPS_Parsed*)userData)->elem_stack->last
341     #define LASTELEM  ((COMPS_Elem*)((COMPS_Parsed*)userData)->elem_stack->last->data)
342 
343     COMPS_Elem * elem = NULL;
344     COMPS_ElemType type;
345 
346     /* create new element */
347     type = comps_elem_get_type(s);
348     elem = comps_elem_create(s, attrs, type);
349     if (elem == NULL) {
350         comps_log_error_x(((COMPS_Parsed*)userData)->log, COMPS_ERR_MALLOC, 0);
351         raise(SIGABRT);
352         return;
353     }
354     elem->valid = 0;
355 
356     if (ELEMINFO->ancestors[0] != COMPS_ELEM_NONE) {
357         if (!LAST) {
358 
359         } else {
360             elem->ancestor = LASTELEM;
361             for (int x = 0; ELEMINFO->ancestors[x] != COMPS_ELEM_SENTINEL; x++){
362                 if (ELEMINFO->ancestors[x] == LASTELEM->type && \
363                     LASTELEM->valid) {
364                     elem->valid = 1;
365                     break;
366                 }
367             }
368         }
369     } else if (elem->type != COMPS_ELEM_UNKNOWN){
370         elem->valid = 1;
371     }
372     if (!elem->valid) {
373         comps_log_error_x(((COMPS_Parsed*)userData)->log,
374                           COMPS_ERR_NOPARENT, 3, comps_str(s),
375                           comps_num(parser_line),
376                           comps_num(parser_col));
377     }
378     if (((COMPS_Parsed*)userData)->text_buffer->first) {
379         comps_log_error_x(((COMPS_Parsed*)userData)->log,
380                           COMPS_ERR_TEXT_BETWEEN, 3,
381                           comps_str((char*)((COMPS_Parsed*)userData)
382                                          ->text_buffer->first->data),
383                           comps_num(parser_line), comps_num(parser_col));
384         comps_hslist_clear(((COMPS_Parsed*)userData)->text_buffer);
385         ((COMPS_Parsed*)userData)->text_buffer_len = 0;
386     }
387 
388     /* end append it to element stack */
389     comps_hslist_append(((COMPS_Parsed*)userData)->elem_stack, elem, 0);
390     if (ELEMINFO->attributes) {
391        comps_parse_check_attributes(((COMPS_Parsed*)userData), elem);
392     }
393 
394 
395     /* preprocess new element */
396     if (ELEMINFO->preproc && elem->valid) {
397         ELEMINFO->preproc((COMPS_Parsed*)userData, elem);
398     } else {
399 
400     }
401 
402     #undef parser_line
403     #undef parser_col
404     #undef ELEMINFO
405     #undef LAST
406     #undef LASTELEM
407 }
408 
409 
comps_parse_char_data_handler(void * userData,const XML_Char * s,int len)410 void comps_parse_char_data_handler(void *userData,
411                             const XML_Char *s,
412                             int len) {
413 
414     char * c = NULL;
415     //COMPS_ListItem * it;
416 
417     /* skip whitespace data */
418     if (__comps_is_whitespace_only(s, len)) {
419         return;
420     }
421     if ((c = malloc(sizeof(char) * (len+1))) == NULL) {
422         comps_log_error(((COMPS_Parsed*)userData)->log, COMPS_ERR_MALLOC, 0);
423         raise(SIGABRT);
424         return;
425     }
426 
427     /* copy text data of element to temporal string and append it to text_buffer
428        list*/
429     memcpy(c, s, sizeof(char) * len);
430     memset(c+len, 0, sizeof(char));
431     /* and increment total lenght of strings in text_buffer */
432     ((COMPS_Parsed*)userData)->text_buffer_len += len;
433 
434     comps_hslist_append(((COMPS_Parsed*)userData)->text_buffer, c, 0);
435 
436 }
437 
comps_parse_check_attributes(COMPS_Parsed * parsed,COMPS_Elem * elem)438 void comps_parse_check_attributes(COMPS_Parsed *parsed, COMPS_Elem* elem) {
439     #define parser_line XML_GetCurrentLineNumber(parsed->parser)
440     #define parser_col XML_GetCurrentColumnNumber(parsed->parser)
441     const COMPS_ElemInfo *info;
442     info = COMPS_ElemInfos[elem->type];
443     int attr_count;
444     COMPS_HSList *keys;
445     char *val;
446     COMPS_HSListItem *it;
447 
448     for (attr_count = 0; info->attributes[attr_count] != NULL; attr_count++);
449     keys = comps_dict_keys(elem->attrs);
450     for (int x =0; x<attr_count; x++) {
451         for (it = keys->first; it != NULL; it = it->next) {
452             if (strcmp((char*)it->data, info->attributes[x]->name) == 0) {
453                 if (info->attributes[x]->val_check) {
454                     val = comps_dict_get(elem->attrs, it->data);
455                     if (!info->attributes[x]->val_check(val)) {
456                         ///error
457                     }
458 
459                 }
460                 comps_hslist_remove(keys, it);
461                 keys->data_destructor(it->data);
462                 free(it);
463                 break;
464             }
465         }
466     }
467     for (it = keys->first; it != NULL; it = it->next) {
468         comps_log_warning_x(parsed->log, COMPS_ERR_ATTR_UNKNOWN, 4,
469                             comps_str(it->data), comps_str(info->name),
470                             comps_num(parser_line), comps_num(parser_col));
471     }
472     comps_hslist_destroy(&keys);
473     #undef parser_line
474     #undef parser_col
475 }
476