Annotation of embedaddon/libpdel/structs/structs_xml.c, revision 1.1.1.1
1.1 misho 1:
2: /*
3: * Copyright (c) 2001-2002 Packet Design, LLC.
4: * All rights reserved.
5: *
6: * Subject to the following obligations and disclaimer of warranty,
7: * use and redistribution of this software, in source or object code
8: * forms, with or without modifications are expressly permitted by
9: * Packet Design; provided, however, that:
10: *
11: * (i) Any and all reproductions of the source or object code
12: * must include the copyright notice above and the following
13: * disclaimer of warranties; and
14: * (ii) No rights are granted, in any manner or form, to use
15: * Packet Design trademarks, including the mark "PACKET DESIGN"
16: * on advertising, endorsements, or otherwise except as such
17: * appears in the above copyright notice or in the software.
18: *
19: * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
20: * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
21: * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
22: * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
23: * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
24: * OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
25: * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
26: * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
27: * RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE
28: * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
29: * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
30: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
31: * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
32: * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
33: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
35: * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
36: * THE POSSIBILITY OF SUCH DAMAGE.
37: *
38: * Author: Archie Cobbs <archie@freebsd.org>
39: */
40:
41: #include <sys/types.h>
42: #include <net/ethernet.h>
43: #include <netinet/in.h>
44:
45: #include <stdlib.h>
46: #include <stdio.h>
47: #include <stdarg.h>
48: #include <assert.h>
49: #include <ctype.h>
50: #include <syslog.h>
51: #include <string.h>
52: #include <errno.h>
53: #include <pthread.h>
54:
55: #include <expat.h>
56:
57: #include "structs/structs.h"
58: #include "structs/xml.h"
59: #include "structs/type/array.h"
60: #include "structs/type/struct.h"
61: #include "structs/type/union.h"
62: #include "util/typed_mem.h"
63: #include "sys/alog.h"
64:
65: /* Standard XML header */
66: #define XML_HEADER "<?xml version=\"1.0\" standalone=\"yes\"?>\n"
67:
68: /* Max parse depth */
69: #define MAX_XML_STACK 128
70:
71: #define INPUT_MEM_TYPE "structs_xml_input"
72: #define INFO_MEM_TYPE "structs_xml_input.info"
73: #define CHARDATA_MEM_TYPE "structs_xml_input.chardata"
74: #define OUTPUT_MEM_TYPE "structs_xml_output"
75:
76: /* Parse private context */
77: struct xmlinput_stackframe {
78: const struct structs_type *type; /* type we're parsing */
79: void *data; /* data pointer */
80: char *s; /* character data */
81: u_int s_len; /* strlen(s) */
82: u_int index; /* fixed array index */
83: u_char combined; /* was a combined tag */
84: };
85:
86: struct xml_input_info {
87: XML_Parser p;
88: int error;
89: int depth;
90: int flags;
91: u_int skip;
92: const char *elem_tag;
93: char **attrp;
94: const char *attr_mtype;
95: int attr_len;
96: struct xmlinput_stackframe stack[MAX_XML_STACK];
97: structs_xmllog_t *logger;
98: };
99:
100: /*
101: * Internal functions
102: */
103: static void *structs_xml_malloc(size_t size);
104: static void *structs_xml_realloc(void *ptr, size_t size);
105: static void structs_xml_free(void *ptr);
106:
107: static void structs_xml_output_cleanup(void *arg);
108: static void structs_xml_output_prefix(FILE *fp, int depth);
109: static void structs_xml_encode(FILE *fp, const char *s);
110:
111: static structs_xmllog_t structs_xml_null_logger;
112: static structs_xmllog_t structs_xml_stderr_logger;
113: static structs_xmllog_t structs_xml_alog_logger;
114:
115: /*
116: * Internal variables
117: */
118: static const XML_Memory_Handling_Suite memsuite = {
119: structs_xml_malloc,
120: structs_xml_realloc,
121: structs_xml_free
122: };
123:
124: static const char separator_string[] = { STRUCTS_SEPARATOR, '\0' };
125: static const char double_separator_string[] = {
126: STRUCTS_SEPARATOR, STRUCTS_SEPARATOR, '\0'
127: };
128:
129: /*********************************************************************
130: XML INPUT ROUTINES
131: *********************************************************************/
132:
133: /*
134: * Internal functions
135: */
136: static void structs_xml_input_cleanup(void *arg);
137: static void structs_xml_input_start(void *userData,
138: const XML_Char *name, const XML_Char **atts);
139: static void structs_xml_input_end(void *userData, const XML_Char *name);
140: static void structs_xml_input_chardata(void *userData,
141: const XML_Char *s, int len);
142: static void structs_xml_input_nest(struct xml_input_info *info,
143: const char *name, const struct structs_type **typep,
144: void **datap);
145: static void structs_xml_input_prep(struct xml_input_info *info,
146: const struct structs_type *type, void *data,
147: int combined);
148: static void structs_xml_unnest(struct xml_input_info *info,
149: const XML_Char *name);
150: static void structs_xml_pop(struct xml_input_info *info);
151:
152: /* Context for one XML parse run */
153: struct structs_xml_input_ctx {
154: struct xml_input_info *info;
155: XML_Parser p;
156: int data_init;
157: int rtn;
158: const struct structs_type *type;
159: void *data;
160: char **attrp;
161: const char *attr_mtype;
162: };
163:
164: /*
165: * Input a type from XML.
166: *
167: * Note: it is safe for the calling thread to be canceled.
168: */
169: int
170: structs_xml_input(const struct structs_type *type,
171: const char *elem_tag, char **attrp, const char *attr_mtype,
172: FILE *fp, void *data, int flags, structs_xmllog_t *logger)
173: {
174: struct structs_xml_input_ctx *ctx;
175: int esave;
176: int r;
177:
178: /* Special cases for logger */
179: if (logger == STRUCTS_LOGGER_NONE)
180: logger = structs_xml_null_logger;
181: else if (logger == STRUCTS_LOGGER_STDERR)
182: logger = structs_xml_stderr_logger;
183: else if (logger == STRUCTS_LOGGER_ALOG)
184: logger = structs_xml_alog_logger;
185:
186: /* Create context */
187: if ((ctx = MALLOC(INPUT_MEM_TYPE, sizeof(*ctx))) == NULL)
188: return (-1);
189: memset(ctx, 0, sizeof(*ctx));
190: ctx->attrp = attrp;
191: ctx->attr_mtype = attr_mtype;
192: ctx->type = type;
193: ctx->data = data;
194: ctx->rtn = -1;
195: pthread_cleanup_push(structs_xml_input_cleanup, ctx);
196:
197: /* Initialize attributes */
198: if (ctx->attrp != NULL
199: && (*ctx->attrp = STRDUP(ctx->attr_mtype, "")) == NULL)
200: goto done;
201:
202: /* Initialize data object if desired */
203: if (ctx->type != NULL && (flags & STRUCTS_XML_UNINIT) != 0) {
204: if ((*type->init)(ctx->type, ctx->data) == -1) {
205: esave = errno;
206: (*logger)(LOG_ERR, "error initializing data: %s",
207: strerror(errno));
208: errno = esave;
209: goto done;
210: }
211: ctx->data_init = 1;
212: }
213:
214: /* Allocate info structure */
215: if ((ctx->info = MALLOC(INFO_MEM_TYPE, sizeof(*ctx->info))) == NULL) {
216: esave = errno;
217: (*logger)(LOG_ERR, "%s: %s", "malloc", strerror(errno));
218: errno = esave;
219: goto done;
220: }
221: memset(ctx->info, 0, sizeof(*ctx->info));
222: ctx->info->logger = logger;
223: ctx->info->attrp = attrp;
224: ctx->info->attr_len = 0;
225: ctx->info->attr_mtype = attr_mtype;
226: ctx->info->elem_tag = elem_tag;
227: ctx->info->stack[0].type = type;
228: ctx->info->stack[0].data = data;
229: ctx->info->flags = flags;
230:
231: /* Create a new parser */
232: if ((ctx->p = XML_ParserCreate_MM(NULL, &memsuite, NULL)) == NULL) {
233: esave = errno;
234: (*logger)(LOG_ERR,
235: "error creating XML parser: %s", strerror(errno));
236: errno = esave;
237: goto done;
238: }
239: ctx->info->p = ctx->p;
240: XML_SetUserData(ctx->p, ctx->info);
241: XML_SetElementHandler(ctx->p,
242: structs_xml_input_start, structs_xml_input_end);
243: XML_SetCharacterDataHandler(ctx->p, structs_xml_input_chardata);
244:
245: /* Parse it */
246: while (1) {
247: const int bufsize = 1024;
248: size_t len;
249: void *buf;
250:
251: /* Get buffer */
252: if ((buf = XML_GetBuffer(ctx->p, bufsize)) == NULL) {
253: esave = errno;
254: (*logger)(LOG_ERR,
255: "error from XML_GetBuffer: %s", strerror(errno));
256: errno = esave;
257: break;
258: }
259:
260: /* Read more bytes. Note: we could get canceled here. */
261: len = fread(buf, 1, bufsize, fp);
262:
263: /* Check for error */
264: if (ferror(fp)) {
265: esave = errno;
266: (*logger)(LOG_ERR, "read error: %s", strerror(errno));
267: errno = esave;
268: break;
269: }
270:
271: /* Process them */
272: if (len > 0 && !XML_ParseBuffer(ctx->p, len, feof(fp))) {
273: (*logger)(LOG_ERR, "line %d:%d: %s",
274: XML_GetCurrentLineNumber(ctx->p),
275: XML_GetCurrentColumnNumber(ctx->p),
276: XML_ErrorString(XML_GetErrorCode(ctx->p)));
277: errno = EINVAL;
278: break;
279: }
280:
281: /* Done? */
282: if (feof(fp)) {
283: if (ctx->info->error != 0)
284: errno = ctx->info->error;
285: else
286: ctx->rtn = 0;
287: break;
288: }
289: }
290:
291: done:
292: /* Clean up and exit */
293: r = ctx->rtn;
294: pthread_cleanup_pop(1);
295: return (r);
296: }
297:
298: /*
299: * Cleanup for structs_xml_input()
300: */
301: static void
302: structs_xml_input_cleanup(void *arg)
303: {
304: struct structs_xml_input_ctx *const ctx = arg;
305: const int esave = errno;
306:
307: /* Free private parse info */
308: if (ctx->info != NULL) {
309: while (ctx->info->depth >= 0)
310: structs_xml_pop(ctx->info);
311: FREE(INFO_MEM_TYPE, ctx->info);
312: }
313:
314: /* Free parser */
315: if (ctx->p != NULL)
316: XML_ParserFree(ctx->p);
317:
318: /* If error, free returned attributes and initialized data */
319: if (ctx->rtn != 0) {
320: if (ctx->attrp != NULL && *ctx->attrp != NULL) {
321: FREE(ctx->attr_mtype, *ctx->attrp);
322: *ctx->attrp = NULL;
323: }
324: if (ctx->data_init)
325: (*ctx->type->uninit)(ctx->type, ctx->data);
326: }
327:
328: /* Free context */
329: FREE(INPUT_MEM_TYPE, ctx);
330: errno = esave;
331: }
332:
333: /*
334: * Start tag handler
335: */
336: static void
337: structs_xml_input_start(void *userData,
338: const XML_Char *name, const XML_Char **attrs)
339: {
340: struct xml_input_info *const info = userData;
341: struct xmlinput_stackframe *const frame = &info->stack[info->depth];
342: const struct structs_type *type = frame->type;
343: void *data = frame->data;
344: char *namebuf;
345: char *ctx;
346: int first;
347: char *s;
348: int sev;
349:
350: /* Skip if any errors */
351: if (info->error)
352: return;
353: if (info->skip) {
354: info->skip++;
355: return;
356: }
357:
358: /* Handle the top level tag specially */
359: if (info->depth == 0) {
360: int i;
361:
362: /* The top level tag must match what we expect */
363: if (strcmp(name, info->elem_tag) != 0) {
364: (*info->logger)(LOG_ERR,
365: "line %d:%d: expecting element \"%s\" here",
366: XML_GetCurrentLineNumber(info->p),
367: XML_GetCurrentColumnNumber(info->p),
368: info->elem_tag);
369: info->error = EINVAL;
370: return;
371: }
372:
373: /* Extract attributes */
374: for (i = 0; info->attrp != NULL && attrs[i] != NULL; i += 2) {
375: const char *const name = attrs[i];
376: const char *const value = attrs[i + 1];
377: void *mem;
378:
379: if ((mem = REALLOC(info->attr_mtype,
380: *info->attrp, info->attr_len + strlen(name) + 1
381: + strlen(value) + 1 + 1)) == NULL) {
382: info->error = errno;
383: (*info->logger)(LOG_ERR, "line %d:%d: %s: %s",
384: XML_GetCurrentLineNumber(info->p),
385: XML_GetCurrentColumnNumber(info->p),
386: "malloc", strerror(errno));
387: return;
388: }
389: *info->attrp = mem;
390: strcpy(*info->attrp + info->attr_len, name);
391: strcpy(*info->attrp + info->attr_len
392: + strlen(name) + 1, value);
393: (*info->attrp)[info->attr_len + strlen(name) + 1
394: + strlen(value) + 1] = '\0';
395: info->attr_len += strlen(name) + 1 + strlen(value) + 1;
396: }
397:
398: /* Are we only scanning? */
399: if ((info->flags & STRUCTS_XML_SCAN) != 0) {
400: info->skip++;
401: return;
402: }
403:
404: /* Prep the top level data structure */
405: structs_xml_input_prep(info, type, data, 0);
406: return;
407: }
408:
409: /* We don't allow attributes with non top level elements */
410: if (attrs[0] != NULL) {
411: sev = (info->flags & STRUCTS_XML_LOOSE) == 0 ?
412: LOG_ERR : LOG_WARNING;
413: (*info->logger)(sev, "line %d:%d: element \"%s\""
414: " contains attributes (not allowed)",
415: XML_GetCurrentLineNumber(info->p),
416: XML_GetCurrentColumnNumber(info->p), name);
417: if (sev == LOG_ERR) {
418: info->error = EINVAL;
419: return;
420: }
421: }
422:
423: /*
424: * Check the case of a structure or union field name
425: * containing the separator character. Such fields take
426: * precedence over combined XML tags.
427: */
428: if (strchr(name, STRUCTS_SEPARATOR) != NULL) {
429: switch (type->tclass) {
430: case STRUCTS_TYPE_STRUCTURE:
431: {
432: const struct structs_field *field;
433:
434: for (field = frame->type->args[0].v;
435: field->name != NULL; field++) {
436: if (strcmp(field->name, name) == 0)
437: goto not_combined;
438: }
439: }
440: case STRUCTS_TYPE_UNION:
441: {
442: const struct structs_ufield *field;
443:
444: for (field = frame->type->args[0].v;
445: field->name != NULL; field++) {
446: if (strcmp(field->name, name) == 0)
447: goto not_combined;
448: }
449: }
450: default:
451: break;
452: }
453: }
454:
455: /* Check whether we need to consider this as a combined tag */
456: if ((info->flags & STRUCTS_XML_COMB_TAGS) == 0
457: || strchr(name, STRUCTS_SEPARATOR) == NULL)
458: goto not_combined;
459:
460: /* Check that the combined XML tag is well-formed */
461: if (name[0] == STRUCTS_SEPARATOR
462: || (name[0] != '\0' && name[strlen(name) - 1] == STRUCTS_SEPARATOR)
463: || strstr(name, double_separator_string) != NULL) {
464: (*info->logger)(LOG_ERR, "line %d:%d: invalid combined"
465: " element tag \"%s\"", XML_GetCurrentLineNumber(info->p),
466: XML_GetCurrentColumnNumber(info->p), name);
467: info->error = EINVAL;
468: return;
469: }
470:
471: /* Copy XML tag so we can parse it */
472: if ((namebuf = STRDUP(TYPED_MEM_TEMP, name)) == NULL) {
473: info->error = errno;
474: return;
475: }
476:
477: /* Parse combined XML tag into individual tag names */
478: for (first = 1, s = strtok_r(namebuf, separator_string, &ctx);
479: s != NULL; first = 0, s = strtok_r(NULL, separator_string, &ctx)) {
480: struct xmlinput_stackframe *const frame
481: = &info->stack[info->depth];
482:
483: type = frame->type;
484: data = frame->data;
485: structs_xml_input_nest(info, s, &type, &data);
486: if (info->error != 0) {
487: FREE(TYPED_MEM_TEMP, namebuf);
488: return;
489: }
490: structs_xml_input_prep(info, type, data, !first);
491: if (info->error != 0) {
492: FREE(TYPED_MEM_TEMP, namebuf);
493: return;
494: }
495: }
496: FREE(TYPED_MEM_TEMP, namebuf);
497: return;
498:
499: not_combined:
500: /* Handle a non-combined tag */
501: structs_xml_input_nest(info, name, &type, &data);
502: if (info->error != 0)
503: return;
504: structs_xml_input_prep(info, type, data, 0);
505: if (info->error != 0)
506: return;
507: }
508:
509: /*
510: * Nest one level deeper into the data structure we're inputting
511: * using the sub-field "name".
512: */
513: static void
514: structs_xml_input_nest(struct xml_input_info *info, const char *name,
515: const struct structs_type **typep, void **datap)
516: {
517: struct xmlinput_stackframe *const frame = &info->stack[info->depth];
518: const struct structs_type *type;
519: void *data;
520: int sev;
521:
522: /* Check type type */
523: switch (frame->type->tclass) {
524: case STRUCTS_TYPE_STRUCTURE:
525: case STRUCTS_TYPE_UNION:
526: {
527: /* Find field; for unions, adjust the field type if necessary */
528: type = frame->type;
529: data = frame->data;
530: if ((type = structs_find(type, name, &data, 1)) == NULL) {
531: if (errno == ENOENT) {
532: sev = (info->flags & STRUCTS_XML_LOOSE) == 0 ?
533: LOG_ERR : LOG_WARNING;
534: (*info->logger)(sev,
535: "line %d:%d: element \"%s\" is not"
536: " expected here",
537: XML_GetCurrentLineNumber(info->p),
538: XML_GetCurrentColumnNumber(info->p), name);
539: if (sev == LOG_ERR) {
540: info->error = EINVAL;
541: return;
542: }
543: info->skip++;
544: return;
545: }
546: (*info->logger)(LOG_ERR, "line %d:%d: error"
547: " initializing union field \"%s\": %s",
548: XML_GetCurrentLineNumber(info->p),
549: XML_GetCurrentColumnNumber(info->p),
550: name, strerror(errno));
551: info->error = errno;
552: return;
553: }
554: break;
555: }
556:
557: case STRUCTS_TYPE_ARRAY:
558: {
559: const struct structs_type *const etype = frame->type->args[0].v;
560: const char *mtype = frame->type->args[1].s;
561: const char *elem_name = frame->type->args[2].s;
562: struct structs_array *const ary = frame->data;
563: void *mem;
564:
565: /* Check tag name */
566: if (strcmp(name, elem_name) != 0) {
567: (*info->logger)(LOG_ERR, "line %d:%d: expected element"
568: " \"%s\" instead of \"%s\"",
569: XML_GetCurrentLineNumber(info->p),
570: XML_GetCurrentColumnNumber(info->p),
571: elem_name, name);
572: info->error = EINVAL;
573: return;
574: }
575:
576: /* Expand the array by one */
577: if ((mem = REALLOC(mtype,
578: ary->elems, (ary->length + 1) * etype->size)) == NULL) {
579: info->error = errno;
580: (*info->logger)(LOG_ERR, "line %d:%d: %s: %s",
581: XML_GetCurrentLineNumber(info->p),
582: XML_GetCurrentColumnNumber(info->p),
583: "realloc", strerror(errno));
584: return;
585: }
586: ary->elems = mem;
587:
588: /* Initialize the new element */
589: memset((char *)ary->elems + (ary->length * etype->size),
590: 0, etype->size);
591: if ((*etype->init)(etype,
592: (char *)ary->elems + (ary->length * etype->size)) == -1) {
593: info->error = errno;
594: (*info->logger)(LOG_ERR, "line %d:%d: %s: %s",
595: XML_GetCurrentLineNumber(info->p),
596: XML_GetCurrentColumnNumber(info->p),
597: "error initializing new array element",
598: strerror(errno));
599: return;
600: }
601:
602: /* Parse the element next */
603: type = etype;
604: data = (char *)ary->elems + (ary->length * etype->size);
605: ary->length++;
606: break;
607: }
608:
609: case STRUCTS_TYPE_FIXEDARRAY:
610: {
611: const struct structs_type *const etype = frame->type->args[0].v;
612: const char *elem_name = frame->type->args[1].s;
613: const u_int length = frame->type->args[2].i;
614:
615: /* Check tag name */
616: if (strcmp(name, elem_name) != 0) {
617: (*info->logger)(LOG_ERR, "line %d:%d: expected element"
618: " \"%s\" instead of \"%s\"",
619: XML_GetCurrentLineNumber(info->p),
620: XML_GetCurrentColumnNumber(info->p),
621: elem_name, name);
622: info->error = EINVAL;
623: return;
624: }
625:
626: /* Check index vs. array length */
627: if (frame->index >= length) {
628: sev = (info->flags & STRUCTS_XML_LOOSE) == 0 ?
629: LOG_ERR : LOG_WARNING;
630: (*info->logger)(sev, "line %d:%d: too many"
631: " elements in fixed array (length %u)",
632: XML_GetCurrentLineNumber(info->p),
633: XML_GetCurrentColumnNumber(info->p), length);
634: if (sev == LOG_ERR) {
635: info->error = EINVAL;
636: return;
637: }
638: info->skip++;
639: return;
640: }
641:
642: /* Parse the element next */
643: type = etype;
644: data = (char *)frame->data + (frame->index * etype->size);
645: frame->index++;
646: break;
647: }
648:
649: case STRUCTS_TYPE_PRIMITIVE:
650: sev = (info->flags & STRUCTS_XML_LOOSE) == 0 ?
651: LOG_ERR : LOG_WARNING;
652: (*info->logger)(sev,
653: "line %d:%d: element \"%s\" is not expected here",
654: XML_GetCurrentLineNumber(info->p),
655: XML_GetCurrentColumnNumber(info->p), name);
656: if (sev == LOG_ERR) {
657: info->error = EINVAL;
658: return;
659: }
660: info->skip++;
661: return;
662:
663: default:
664: assert(0);
665: return;
666: }
667:
668: /* Done */
669: *typep = type;
670: *datap = data;
671: }
672:
673: /*
674: * Prepare the next level of nesting for parsing.
675: */
676: static void
677: structs_xml_input_prep(struct xml_input_info *info,
678: const struct structs_type *type, void *data, int combined)
679: {
680: /* Dereference through pointer(s) */
681: while (type->tclass == STRUCTS_TYPE_POINTER) {
682: type = type->args[0].v;
683: data = *((void **)data);
684: }
685:
686: /* If next item is an array, re-initialize it */
687: switch (type->tclass) {
688: case STRUCTS_TYPE_ARRAY:
689: (*type->uninit)(type, data);
690: memset(data, 0, type->size);
691: break;
692: case STRUCTS_TYPE_FIXEDARRAY:
693: {
694: void *mem;
695:
696: /* Get temporary region for newly initialized array */
697: if ((mem = MALLOC(TYPED_MEM_TEMP, type->size)) == NULL) {
698: info->error = errno;
699: (*info->logger)(LOG_ERR, "line %d:%d: %s: %s",
700: XML_GetCurrentLineNumber(info->p),
701: XML_GetCurrentColumnNumber(info->p),
702: "error initializing new array", strerror(errno));
703: return;
704: }
705:
706: /* Initialize new array */
707: if ((*type->init)(type, mem) == -1) {
708: info->error = errno;
709: (*info->logger)(LOG_ERR, "line %d:%d: %s: %s",
710: XML_GetCurrentLineNumber(info->p),
711: XML_GetCurrentColumnNumber(info->p),
712: "error initializing new array", strerror(errno));
713: FREE(TYPED_MEM_TEMP, mem);
714: return;
715: }
716:
717: /* Replace existing array with fresh one */
718: (*type->uninit)(type, data);
719: memcpy(data, mem, type->size);
720: FREE(TYPED_MEM_TEMP, mem);
721:
722: /* Remember that we're on the first element */
723: info->stack[info->depth + 1].index = 0;
724: break;
725: }
726: default:
727: break;
728: }
729:
730: /* Check stack overflow */
731: if (info->depth == MAX_XML_STACK - 1) {
732: (*info->logger)(LOG_ERR,
733: "line %d:%d: maximum parse stack depth (%d) exceeded",
734: XML_GetCurrentLineNumber(info->p),
735: XML_GetCurrentColumnNumber(info->p), MAX_XML_STACK);
736: info->error = EMLINK;
737: return;
738: }
739:
740: /* Continue in a new stack frame */
741: info->depth++;
742: info->stack[info->depth].type = type;
743: info->stack[info->depth].data = data;
744: info->stack[info->depth].combined = combined;
745: }
746:
747: /*
748: * Character data handler
749: */
750: static void
751: structs_xml_input_chardata(void *userData, const XML_Char *s, int len)
752: {
753: struct xml_input_info *const info = userData;
754: struct xmlinput_stackframe *const frame = &info->stack[info->depth];
755: void *mem;
756:
757: /* Skip if any errors */
758: if (info->error || info->skip)
759: return;
760:
761: /* Expand buffer and append character data */
762: if ((mem = REALLOC(CHARDATA_MEM_TYPE,
763: frame->s, frame->s_len + len + 1)) == NULL) {
764: info->error = errno;
765: (*info->logger)(LOG_ERR, "%s: %s",
766: "realloc", strerror(errno));
767: return;
768: }
769: frame->s = mem;
770: memcpy(frame->s + frame->s_len, (char *)s, len);
771: frame->s[frame->s_len + len] = '\0';
772: frame->s_len += len;
773: }
774:
775: /*
776: * End tag handler
777: */
778: static void
779: structs_xml_input_end(void *userData, const XML_Char *name)
780: {
781: struct xml_input_info *const info = userData;
782: struct xmlinput_stackframe *frame;
783: int was_combined;
784:
785: /* Un-nest once for each structs tag */
786: do {
787: frame = &info->stack[info->depth];
788: was_combined = frame->combined;
789: structs_xml_unnest(info, name);
790: } while (was_combined);
791: }
792:
793: /*
794: * Unnest one level
795: */
796: static void
797: structs_xml_unnest(struct xml_input_info *info, const XML_Char *name)
798: {
799: struct xmlinput_stackframe *const frame = &info->stack[info->depth];
800: const struct structs_type *type;
801: const char *s;
802: char ebuf[64];
803: void *data;
804:
805: /* Skip if any errors */
806: if (info->error)
807: return;
808: if (info->skip) {
809: info->skip--;
810: return;
811: }
812:
813: /* Get current type and data */
814: data = frame->data;
815: type = frame->type;
816:
817: /*
818: * Convert from ASCII if possible, otherwise check only whitespace.
819: * For unions, we allow the field name tag to be omitted if you
820: * want to use the default field, which must have primitive type.
821: */
822: switch (type->tclass) {
823: case STRUCTS_TYPE_UNION:
824: {
825: const struct structs_ufield *const field = type->args[0].v;
826:
827: /* Check to see if there's any non-whitespace text */
828: if (frame->s == NULL)
829: goto done;
830: for (s = frame->s; *s != '\0' && isspace(*s); s++);
831: if (*s == '\0')
832: goto done;
833:
834: /* Default field must have primitive type */
835: if (field->type->tclass != STRUCTS_TYPE_PRIMITIVE)
836: break;
837:
838: /* Switch the union to the default field */
839: if (structs_union_set(type, NULL, data, field->name) == -1) {
840: info->error = errno;
841: (*info->logger)(LOG_ERR,
842: "%s: %s", "structs_union_set", strerror(errno));
843: return;
844: }
845:
846: /* Point at the field instead of the union */
847: type = field->type;
848: data = ((const struct structs_union *)data)->un;
849:
850: /* FALLTHROUGH */
851: }
852: case STRUCTS_TYPE_PRIMITIVE:
853: if (structs_set_string(type, NULL,
854: frame->s, data, ebuf, sizeof(ebuf)) == -1) {
855: info->error = errno;
856: (*info->logger)(LOG_ERR,
857: "line %d:%d: error in \"%s\" element data"
858: " \"%s\": %s",
859: XML_GetCurrentLineNumber(info->p),
860: XML_GetCurrentColumnNumber(info->p),
861: name, frame->s == NULL ? "" : frame->s, ebuf);
862: return;
863: }
864: goto done;
865: default:
866: break;
867: }
868:
869: /* There shouldn't be any non-whitespace text here */
870: if (frame->s != NULL) {
871: for (s = frame->s; *s != '\0' && isspace(*s); s++);
872: if (*s != '\0') {
873: (*info->logger)(LOG_ERR, "line %d:%d:"
874: " extra garbage within \"%s\" element",
875: XML_GetCurrentLineNumber(info->p),
876: XML_GetCurrentColumnNumber(info->p),
877: name);
878: info->error = EINVAL;
879: return;
880: }
881: }
882:
883: done:
884: /* Pop stack frame */
885: structs_xml_pop(info);
886: }
887:
888: /*
889: * Pop the XML parse stack
890: */
891: static void
892: structs_xml_pop(struct xml_input_info *info)
893: {
894: struct xmlinput_stackframe *const frame = &info->stack[info->depth];
895:
896: assert(info->depth >= 0);
897: if (frame->s != NULL)
898: FREE(CHARDATA_MEM_TYPE, frame->s);
899: memset(frame, 0, sizeof(*frame));
900: info->depth--;
901: }
902:
903: /*********************************************************************
904: XML OUTPUT ROUTINES
905: *********************************************************************/
906:
907: #define STRUCTS_XML_SHOWONE 0x0100 /* show elem for next level only */
908: #define STRUCTS_XML_SHOWALL 0x0200 /* show elem and all sub elems */
909:
910: /*
911: * Internal functions
912: */
913: static int structs_xml_output_sub(const struct structs_type *type,
914: const void *data, const char *tag, const char *attrs,
915: FILE *fp, const char **elems, const char *posn,
916: int flags, int depth);
917: static void structs_xml_output_openelem(FILE *fp,
918: int depth, const char *tag, const char *attrs);
919:
920: /*
921: * Output a structure in XML
922: *
923: * Note: it is safe for the calling thread to be canceled.
924: */
925: int
926: structs_xml_output(const struct structs_type *type, const char *elem_tag,
927: const char *attrs, const void *data, FILE *fp, const char **elems,
928: int flags)
929: {
930: static const char *all[] = { "", NULL };
931:
932: /* Output standard XML header */
933: fputs(XML_HEADER, fp);
934:
935: /* NULL elems list means "show everything" */
936: if (elems == NULL)
937: elems = all;
938:
939: /* Output structure, and always show the opening and closing tags */
940: return (structs_xml_output_sub(type, data, elem_tag, attrs, fp,
941: elems, "", flags | STRUCTS_XML_SHOWONE, 0));
942: }
943:
944: /*
945: * Output a sub-structure in XML
946: */
947: static int
948: structs_xml_output_sub(const struct structs_type *type, const void *data,
949: const char *tag, const char *attrs, FILE *fp, const char **elems,
950: const char *posn, int flags, int depth)
951: {
952: int r = 0;
953: int i;
954:
955: /* Dereference through pointer(s) */
956: while (type->tclass == STRUCTS_TYPE_POINTER) {
957: type = type->args[0].v;
958: data = *((void **)data);
959: }
960:
961: /* Determine whether to show this element */
962: if ((flags & STRUCTS_XML_SHOWALL) == 0) {
963: const size_t plen = strlen(posn);
964:
965: for (i = 0; elems[i] != NULL; i++) {
966: if (strncmp(elems[i], posn, plen) == 0
967: && (elems[i][plen] == '\0'
968: || elems[i][plen] == STRUCTS_SEPARATOR)) {
969: if (elems[i][plen] == '\0')
970: flags |= STRUCTS_XML_SHOWALL;
971: break;
972: }
973: }
974: if (elems[i] == NULL && depth > 0
975: && (flags & STRUCTS_XML_SHOWONE) == 0)
976: return (0); /* not matched, skip element */
977: }
978:
979: /* If doing abbreviated version, compare with default value */
980: if (depth > 0
981: && (flags & (STRUCTS_XML_FULL|STRUCTS_XML_SHOWONE)) == 0) {
982: void *init_value;
983: int equal;
984:
985: if ((init_value = MALLOC(TYPED_MEM_TEMP, type->size)) == NULL)
986: return (-1);
987: if ((*type->init)(type, init_value) == -1) {
988: FREE(TYPED_MEM_TEMP, init_value);
989: return (-1);
990: }
991: equal = (*type->equal)(type, data, init_value);
992: (*type->uninit)(type, init_value);
993: FREE(TYPED_MEM_TEMP, init_value);
994: if (equal)
995: return (0);
996: }
997:
998: /* The STRUCTS_XML_SHOWONE flag only applies to the next level down */
999: flags &= ~STRUCTS_XML_SHOWONE;
1000:
1001: /* Output element */
1002: switch (type->tclass) {
1003: case STRUCTS_TYPE_UNION:
1004: {
1005: const struct structs_union *const un = data;
1006: const struct structs_ufield *const fields = type->args[0].v;
1007: const struct structs_ufield *field;
1008: char *sposn;
1009:
1010: /* Find field */
1011: for (field = fields; field->name != NULL
1012: && strcmp(un->field_name, field->name) != 0; field++);
1013: if (field->name == NULL)
1014: assert(0);
1015:
1016: /* Generate new position tag */
1017: ASPRINTF(OUTPUT_MEM_TYPE, &sposn, "%s%s%s", posn,
1018: *posn != '\0' ? separator_string : "", field->name);
1019: if (sposn == NULL)
1020: return (-1);
1021: pthread_cleanup_push(structs_xml_output_cleanup, sposn);
1022:
1023: /* Opening tag */
1024: structs_xml_output_openelem(fp, depth, tag, attrs);
1025: fprintf(fp, "\n");
1026:
1027: /*
1028: * If the union field is not the default choice for this union,
1029: * then it must always be shown so the recipient knows that.
1030: */
1031: if (strcmp(un->field_name, fields[0].name) != 0)
1032: flags |= STRUCTS_XML_SHOWONE;
1033:
1034: /* Output chosen union field */
1035: r = structs_xml_output_sub(field->type, un->un, field->name,
1036: NULL, fp, elems, sposn, flags, depth + 1);
1037:
1038: /* Free position tag */
1039: pthread_cleanup_pop(1);
1040:
1041: /* Bail out if there was an error */
1042: if (r == -1)
1043: break;
1044:
1045: /* Closing tag */
1046: structs_xml_output_prefix(fp, depth);
1047: fprintf(fp, "</%s>\n", tag);
1048: break;
1049: }
1050:
1051: case STRUCTS_TYPE_STRUCTURE:
1052: {
1053: const struct structs_field *field;
1054:
1055: /* Opening tag */
1056: structs_xml_output_openelem(fp, depth, tag, attrs);
1057: fprintf(fp, "\n");
1058:
1059: /* Do each structure field */
1060: for (field = type->args[0].v; field->name != NULL; field++) {
1061: char *sposn;
1062:
1063: /* Generate new position tag */
1064: ASPRINTF(OUTPUT_MEM_TYPE, &sposn, "%s%s%s", posn,
1065: *posn != '\0' ? separator_string : "", field->name);
1066: if (sposn == NULL)
1067: return (-1);
1068: pthread_cleanup_push(structs_xml_output_cleanup, sposn);
1069:
1070: /* Do structure field */
1071: r = structs_xml_output_sub(field->type,
1072: (char *)data + field->offset, field->name, NULL,
1073: fp, elems, sposn, flags, depth + 1);
1074:
1075: /* Free position tag */
1076: pthread_cleanup_pop(1);
1077:
1078: /* Bail out if there was an error */
1079: if (r == -1)
1080: break;
1081: }
1082:
1083: /* Closing tag */
1084: structs_xml_output_prefix(fp, depth);
1085: fprintf(fp, "</%s>\n", tag);
1086: break;
1087: }
1088:
1089: case STRUCTS_TYPE_ARRAY:
1090: {
1091: const struct structs_type *const etype = type->args[0].v;
1092: const char *elem_name = type->args[2].s;
1093: const struct structs_array *const ary = data;
1094: int i;
1095:
1096: /* Opening tag */
1097: structs_xml_output_openelem(fp, depth, tag, attrs);
1098: fprintf(fp, "\n");
1099:
1100: /* All array elements must be shown to keep proper ordering */
1101: flags |= STRUCTS_XML_SHOWONE;
1102:
1103: /* Do elements in order */
1104: for (i = 0; i < ary->length; i++) {
1105: char *sposn;
1106:
1107: /* Generate new position tag */
1108: ASPRINTF(OUTPUT_MEM_TYPE, &sposn, "%s%s%u",
1109: posn, *posn != '\0' ? separator_string : "", i);
1110: if (sposn == NULL)
1111: return (-1);
1112: pthread_cleanup_push(structs_xml_output_cleanup, sposn);
1113:
1114: /* Output array element */
1115: r = structs_xml_output_sub(etype, (char *)ary->elems
1116: + (i * etype->size), elem_name, NULL, fp, elems,
1117: sposn, flags, depth + 1);
1118:
1119: /* Free position tag */
1120: pthread_cleanup_pop(1);
1121:
1122: /* Bail out if there was an error */
1123: if (r == -1)
1124: break;
1125: }
1126:
1127: /* Closing tag */
1128: structs_xml_output_prefix(fp, depth);
1129: fprintf(fp, "</%s>\n", tag);
1130: break;
1131: }
1132:
1133: case STRUCTS_TYPE_FIXEDARRAY:
1134: {
1135: const struct structs_type *const etype = type->args[0].v;
1136: const char *elem_name = type->args[1].s;
1137: const u_int length = type->args[2].i;
1138: u_int i;
1139:
1140: /* Opening tag */
1141: structs_xml_output_openelem(fp, depth, tag, attrs);
1142: fprintf(fp, "\n");
1143:
1144: /* All array elements must be shown to keep proper ordering */
1145: flags |= STRUCTS_XML_SHOWONE;
1146:
1147: /* Do elements in order */
1148: for (i = 0; i < length; i++) {
1149: char *sposn;
1150:
1151: /* Generate new position tag */
1152: ASPRINTF(OUTPUT_MEM_TYPE, &sposn, "%s%s%u",
1153: posn, *posn != '\0' ? separator_string : "", i);
1154: if (sposn == NULL)
1155: return (-1);
1156: pthread_cleanup_push(structs_xml_output_cleanup, sposn);
1157:
1158: /* Output array element */
1159: r = structs_xml_output_sub(etype, (char *)data
1160: + (i * etype->size), elem_name, NULL, fp, elems,
1161: sposn, flags, depth + 1);
1162:
1163: /* Free position tag */
1164: pthread_cleanup_pop(1);
1165:
1166: /* Bail out if there was an error */
1167: if (r == -1)
1168: break;
1169: }
1170:
1171: /* Closing tag */
1172: structs_xml_output_prefix(fp, depth);
1173: fprintf(fp, "</%s>\n", tag);
1174: break;
1175: }
1176:
1177: case STRUCTS_TYPE_PRIMITIVE:
1178: {
1179: char *ascii;
1180:
1181: /* Get ascii string */
1182: if ((ascii = (*type->ascify)(type,
1183: OUTPUT_MEM_TYPE, data)) == NULL)
1184: return (-1);
1185:
1186: /* Push cleanup hook to handle cancellation */
1187: pthread_cleanup_push(structs_xml_output_cleanup, ascii);
1188:
1189: /* Output element */
1190: structs_xml_output_openelem(fp, depth, tag, attrs);
1191: structs_xml_encode(fp, ascii);
1192: fprintf(fp, "</%s>\n", tag);
1193:
1194: /* Free ascii string */
1195: pthread_cleanup_pop(1);
1196: break;
1197: }
1198:
1199: default:
1200: assert(0);
1201: }
1202: return (r);
1203: }
1204:
1205: /*
1206: * Cleanup for structs_xml_output_sub()
1207: */
1208: static void
1209: structs_xml_output_cleanup(void *arg)
1210: {
1211: FREE(OUTPUT_MEM_TYPE, arg);
1212: }
1213:
1214: /*
1215: * Output opening element tag with optional attributes
1216: */
1217: static void
1218: structs_xml_output_openelem(FILE *fp,
1219: int depth, const char *tag, const char *attrs)
1220: {
1221: structs_xml_output_prefix(fp, depth);
1222: fprintf(fp, "<%s", tag);
1223: if (attrs != NULL) {
1224: while (*attrs != '\0') {
1225: fprintf(fp, " ");
1226: structs_xml_encode(fp, attrs);
1227: attrs += strlen(attrs) + 1;
1228: fprintf(fp, "=\"");
1229: structs_xml_encode(fp, attrs);
1230: attrs += strlen(attrs) + 1;
1231: fprintf(fp, "\"");
1232: }
1233: }
1234: fprintf(fp, ">");
1235: }
1236:
1237: /*********************************************************************
1238: UTILITY STUFF
1239: *********************************************************************/
1240:
1241: /*
1242: * Output whitespace prefix
1243: */
1244: static void
1245: structs_xml_output_prefix(FILE *fp, int depth)
1246: {
1247: while (depth >= 2) {
1248: putc('\t', fp);
1249: depth -= 2;
1250: }
1251: if (depth > 0) {
1252: fputs(" ", fp);
1253: }
1254: }
1255:
1256: /*
1257: * Output text XML-encoded
1258: */
1259: static void
1260: structs_xml_encode(FILE *fp, const char *s)
1261: {
1262: for ( ; *s != '\0'; s++) {
1263: switch (*s) {
1264: case '<':
1265: fprintf(fp, "<");
1266: break;
1267: case '>':
1268: fprintf(fp, ">");
1269: break;
1270: case '"':
1271: fprintf(fp, """);
1272: break;
1273: case '&':
1274: fprintf(fp, "&");
1275: break;
1276: break;
1277: default:
1278: if (!isprint(*s)) {
1279: fprintf(fp, "&#%d;", (u_char)*s);
1280: break;
1281: }
1282: /* fall through */
1283: case '\n':
1284: case '\t':
1285: putc(*s, fp);
1286: break;
1287: }
1288: }
1289: }
1290:
1291: /*********************************************************************
1292: BUILT-IN LOGGERS
1293: *********************************************************************/
1294:
1295: static void
1296: structs_xml_null_logger(int sev, const char *fmt, ...)
1297: {
1298: }
1299:
1300: static void
1301: structs_xml_stderr_logger(int sev, const char *fmt, ...)
1302: {
1303: static const char *const sevs[] = {
1304: "emerg", "alert", "crit", "err",
1305: "warning", "notice", "info", "debug"
1306: };
1307: static const int num_sevs = sizeof(sevs) / sizeof(*sevs);
1308: va_list args;
1309:
1310: va_start(args, fmt);
1311: if (sev < 0)
1312: sev = 0;
1313: if (sev >= num_sevs)
1314: sev = num_sevs - 1;
1315: fprintf(stderr, "%s: ", sevs[sev]);
1316: vfprintf(stderr, fmt, args);
1317: fprintf(stderr, "\n");
1318: va_end(args);
1319: }
1320:
1321: static void
1322: structs_xml_alog_logger(int sev, const char *fmt, ...)
1323: {
1324: va_list args;
1325:
1326: va_start(args, fmt);
1327: valog(sev, fmt, args);
1328: va_end(args);
1329: }
1330:
1331: /*********************************************************************
1332: MEMORY WRAPPERS
1333: *********************************************************************/
1334:
1335: #define EXPAT_MEM_TYPE "structs_xml_input.expat"
1336:
1337: static void *
1338: structs_xml_malloc(size_t size)
1339: {
1340: return (MALLOC(EXPAT_MEM_TYPE, size));
1341: }
1342:
1343: static void *
1344: structs_xml_realloc(void *ptr, size_t size)
1345: {
1346: return (REALLOC(EXPAT_MEM_TYPE, ptr, size));
1347: }
1348:
1349: static void
1350: structs_xml_free(void *ptr)
1351: {
1352: FREE(EXPAT_MEM_TYPE, ptr);
1353: }
1354:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>