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