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 the license contained in the file LICENSE in this distribution.
Refer to licensing information at http://www.artifex.com or contact
Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
CA 94129, USA, for further information.
*/
#include "ghostxps.h"
#include <expat.h>
#define XMLBUFLEN 4096
#define NS_XPS "http://schemas.microsoft.com/xps/2005/06"
#define NS_MC "http://schemas.openxmlformats.org/markup-compatibility/2006"
#define NS_OXPS "http://schemas.openxps.org/oxps/v1.0"
typedef struct xps_parser_s xps_parser_t;
struct xps_parser_s
{
xps_context_t *ctx;
xps_item_t *root;
xps_item_t *head;
const char *error;
char *base;
};
struct xps_item_s
{
char *name;
char **atts;
xps_item_t *up;
xps_item_t *down;
xps_item_t *tail;
xps_item_t *next;
};
static const char *
skip_namespace(const char *s)
{
const char *p = strchr(s, ' ');
if (p)
return p + 1;
return s;
}
static void
on_open_tag(void *zp, const char *ns_name, const char **atts)
{
xps_parser_t *parser = zp;
xps_context_t *ctx = parser->ctx;
xps_item_t *item;
xps_item_t *tail;
int namelen;
int attslen;
int textlen;
const char *name;
char *p;
int i;
if (parser->error)
return;
name = NULL;
p = strstr(ns_name, NS_XPS);
if (p == ns_name)
{
name = strchr(ns_name, ' ') + 1;
}
p = strstr(ns_name, NS_MC);
if (p == ns_name)
{
name = strchr(ns_name, ' ') + 1;
}
p = strstr(ns_name, NS_OXPS);
if (p == ns_name)
{
name = strchr(ns_name, ' ') + 1;
}
if (!name)
{
dmprintf1(ctx->memory, "unknown namespace: %s\n", ns_name);
name = ns_name;
}
namelen = strlen(name) + 1;
attslen = sizeof(char*);
textlen = 0;
for (i = 0; atts[i]; i++)
{
attslen += sizeof(char*);
if ((i & 1) == 0)
textlen += strlen(skip_namespace(atts[i])) + 1;
else
textlen += strlen(atts[i]) + 1;
}
item = xps_alloc(ctx, sizeof(xps_item_t) + attslen + namelen + textlen);
if (!item) {
parser->error = "out of memory";
gs_throw(gs_error_VMerror, "out of memory.\n");
return;
}
item->atts = (char**) (((char*)item) + sizeof(xps_item_t));
item->name = ((char*)item) + sizeof(xps_item_t) + attslen;
p = ((char*)item) + sizeof(xps_item_t) + attslen + namelen;
strcpy(item->name, name);
for (i = 0; atts[i]; i++)
{
item->atts[i] = p;
if ((i & 1) == 0)
strcpy(item->atts[i], skip_namespace(atts[i]));
else
strcpy(item->atts[i], atts[i]);
p += strlen(p) + 1;
}
item->atts[i] = 0;
item->up = parser->head;
item->down = NULL;
item->next = NULL;
if (!parser->head)
{
parser->root = item;
parser->head = item;
return;
}
if (!parser->head->down)
{
parser->head->down = item;
parser->head->tail = item;
parser->head = item;
return;
}
tail = parser->head->tail;
tail->next = item;
parser->head->tail = item;
parser->head = item;
}
static void
on_close_tag(void *zp, const char *name)
{
xps_parser_t *parser = zp;
if (parser->error)
return;
if (parser->head)
parser->head = parser->head->up;
}
static inline int
is_xml_space(int c)
{
return c == ' ' || c == '\t' || c == '\r' || c == '\n';
}
static void
on_text(void *zp, char *buf, int len)
{
xps_parser_t *parser = zp;
xps_context_t *ctx = parser->ctx;
const char *atts[3];
int i;
if (parser->error)
return;
for (i = 0; i < len; i++)
{
if (!is_xml_space(buf[i]))
{
char *tmp = xps_alloc(ctx, len + 1);
if (!tmp)
{
parser->error = "out of memory";
gs_throw(gs_error_VMerror, "out of memory.\n");
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, "");
xps_free(ctx, tmp);
return;
}
}
}
xps_item_t *
xps_parse_xml(xps_context_t *ctx, byte *buf, int len)
{
xps_parser_t parser;
XML_Parser xp;
int code;
parser.ctx = ctx;
parser.root = NULL;
parser.head = NULL;
parser.error = NULL;
xp = XML_ParserCreateNS(NULL, ' ');
if (!xp)
{
gs_throw(-1, "xml error: could not create expat parser");
return NULL;
}
XML_SetUserData(xp, &parser);
XML_SetParamEntityParsing(xp, XML_PARAM_ENTITY_PARSING_NEVER);
XML_SetStartElementHandler(xp, (XML_StartElementHandler)on_open_tag);
XML_SetEndElementHandler(xp, (XML_EndElementHandler)on_close_tag);
XML_SetCharacterDataHandler(xp, (XML_CharacterDataHandler)on_text);
code = XML_Parse(xp, (char*)buf, len, 1);
if (code == 0 || parser.error != NULL)
{
if (parser.root)
xps_free_item(ctx, parser.root);
if (XML_ErrorString(XML_GetErrorCode(xp)) != 0)
emprintf1(parser.ctx->memory, "XML_Error: %s\n", XML_ErrorString(XML_GetErrorCode(xp)));
XML_ParserFree(xp);
gs_throw1(-1, "parser error: %s", parser.error);
return NULL;
}
XML_ParserFree(xp);
return parser.root;
}
xps_item_t *
xps_next(xps_item_t *item)
{
return item->next;
}
xps_item_t *
xps_down(xps_item_t *item)
{
return item->down;
}
char *
xps_tag(xps_item_t *item)
{
return item->name;
}
char *
xps_att(xps_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
xps_detach_and_free_remainder(xps_context_t *ctx, xps_item_t *root, xps_item_t *item)
{
if (item->up)
item->up->down = NULL;
xps_free_item(ctx, item->next);
item->next = NULL;
xps_free_item(ctx, root);
}
void
xps_free_item(xps_context_t *ctx, xps_item_t *item)
{
xps_item_t *next;
while (item)
{
next = item->next;
if (item->down)
xps_free_item(ctx, item->down);
xps_free(ctx, item);
item = next;
}
}
static void indent(int n)
{
while (n--)
dlprintf(" ");
}
static void
xps_debug_item_imp(xps_item_t *item, int level, int loop)
{
int i;
while (item)
{
indent(level);
if (strlen(item->name) == 0)
dlprintf1("%s\n", item->atts[1]);
else
{
dlprintf1("<%s", item->name);
for (i = 0; item->atts[i]; i += 2)
dlprintf2(" %s=\"%s\"", item->atts[i], item->atts[i+1]);
if (item->down)
{
dlprintf(">\n");
xps_debug_item_imp(item->down, level + 1, 1);
indent(level);
dlprintf1("</%s>\n", item->name);
}
else
dlprintf(" />\n");
}
item = item->next;
if (!loop)
return;
}
}
void
xps_debug_item(xps_item_t *item, int level)
{
xps_debug_item_imp(item, level, 0);
}