/* Copyright (C) 2006-2008 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of that license. Refer to licensing information at http://www.artifex.com/ or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. */ /* Recursive descent XML parsing API on top of Expat. */ #include "ghostsvg.h" #include #define XMLBUFLEN 4096 static void on_open_tag(void *zp, const char *name, const char **atts) { svg_context_t *ctx = zp; svg_item_t *item; svg_item_t *tail; int namelen; int attslen; int textlen; char *p; int i; if (ctx->error) return; /* count size to alloc */ namelen = strlen(name) + 1; /* zero terminated */ attslen = sizeof(char*); /* with space for sentinel */ textlen = 0; for (i = 0; atts[i]; i++) { attslen += sizeof(char*); textlen += strlen(atts[i]) + 1; } item = svg_alloc(ctx, sizeof(svg_item_t) + attslen + namelen + textlen); if (!item) { ctx->error = "out of memory"; } /* copy strings to new memory */ item->atts = (char**) (((char*)item) + sizeof(svg_item_t)); item->name = ((char*)item) + sizeof(svg_item_t) + attslen; p = ((char*)item) + sizeof(svg_item_t) + attslen + namelen; strcpy(item->name, name); for (i = 0; atts[i]; i++) { item->atts[i] = p; strcpy(item->atts[i], atts[i]); p += strlen(p) + 1; } item->atts[i] = 0; /* link item into tree */ item->up = ctx->head; item->down = NULL; item->next = NULL; if (!ctx->head) { ctx->root = item; ctx->head = item; return; } if (!ctx->head->down) { ctx->head->down = item; ctx->head = item; return; } tail = ctx->head->down; while (tail->next) tail = tail->next; tail->next = item; ctx->head = item; } static void on_close_tag(void *zp, const char *name) { svg_context_t *ctx = zp; if (ctx->error) return; if (ctx->head) ctx->head = ctx->head->up; } static inline int is_svg_space(int c) { return c == ' ' || c == '\t' || c == '\r' || c == '\n'; } static void on_text(void *zp, const char *buf, int len) { svg_context_t *ctx = zp; const char *atts[3]; int i; if (ctx->error) return; for (i = 0; i < len; i++) { if (!is_svg_space(buf[i])) { char *tmp = svg_alloc(ctx, len + 1); if (!tmp) { ctx->error = "out of memory"; return; } atts[0] = ""; atts[1] = tmp; atts[2] = NULL; memcpy(tmp, buf, len); tmp[len] = 0; on_open_tag(zp, "", atts); on_close_tag(zp, ""); svg_free(ctx, tmp); return; } } } int svg_open_xml_parser(svg_context_t *ctx) { XML_Parser xp; /* xp = XML_ParserCreateNS(NULL, ns); */ xp = XML_ParserCreate(NULL); if (!xp) return gs_throw(-1, "xml error: could not create expat parser"); XML_SetUserData(xp, ctx); XML_SetParamEntityParsing(xp, XML_PARAM_ENTITY_PARSING_NEVER); XML_SetStartElementHandler(xp, on_open_tag); XML_SetEndElementHandler(xp, on_close_tag); XML_SetCharacterDataHandler(xp, on_text); ctx->root = NULL; ctx->head = NULL; ctx->error = NULL; ctx->parser = xp; return 0; } int svg_feed_xml_parser(svg_context_t *ctx, const char *buf, int len) { XML_Parser xp = ctx->parser; int code = XML_Parse(xp, buf, len, 0); if (code == 0) { ctx->root = NULL; ctx->head = NULL; ctx->error = NULL; ctx->parser = NULL; gs_throw3(-1, "xml error: %s (line %d, column %d)", XML_ErrorString(XML_GetErrorCode(xp)), XML_GetCurrentLineNumber(xp), XML_GetCurrentColumnNumber(xp)); XML_ParserFree(xp); return -1; } return 0; } svg_item_t * svg_close_xml_parser(svg_context_t *ctx) { XML_Parser xp = ctx->parser; svg_item_t *root = ctx->root; int code = XML_Parse(xp, "", 0, 1); /* finish parsing */ if (code == 0) { gs_throw3(-1, "xml error: %s (line %d, column %d)", XML_ErrorString(XML_GetErrorCode(xp)), XML_GetCurrentLineNumber(xp), XML_GetCurrentColumnNumber(xp)); root = NULL; } ctx->root = NULL; ctx->head = NULL; ctx->error = NULL; ctx->parser = NULL; XML_ParserFree(xp); return root; } svg_item_t * svg_next(svg_item_t *item) { return item->next; } svg_item_t * svg_down(svg_item_t *item) { return item->down; } char * svg_tag(svg_item_t *item) { return item->name; } char * svg_att(svg_item_t *item, const char *att) { int i; for (i = 0; item->atts[i]; i += 2) if (!strcmp(item->atts[i], att)) return item->atts[i + 1]; return NULL; } void svg_free_item(svg_context_t *ctx, svg_item_t *item) { svg_item_t *next; while (item) { next = item->next; if (item->down) svg_free_item(ctx, item->down); svg_free(ctx, item); item = next; } } static void indent(int n) { while (n--) printf(" "); } static void svg_debug_item_imp(svg_item_t *item, int level, int loop) { int i; while (item) { indent(level); if (strlen(item->name) == 0) printf("%s\n", item->atts[1]); else { printf("<%s", item->name); for (i = 0; item->atts[i]; i += 2) printf(" %s=\"%s\"", item->atts[i], item->atts[i+1]); if (item->down) { printf(">\n"); svg_debug_item_imp(item->down, level + 1, 1); indent(level); printf("\n", item->name); } else printf(" />\n"); } item = item->next; if (!loop) return; } } void svg_debug_item(svg_item_t *item, int level) { svg_debug_item_imp(item, level, 0); }