Coverage Report

Created: 2026-04-09 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/xps/xpsxml.c
Line
Count
Source
1
/* Copyright (C) 2001-2025 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* Simple XML document object model on top of Expat. */
18
19
#include "ghostxps.h"
20
21
#include <expat.h>
22
23
#define XMLBUFLEN 4096
24
25
23.5k
#define NS_XPS "http://schemas.microsoft.com/xps/2005/06"
26
23.5k
#define NS_MC "http://schemas.openxmlformats.org/markup-compatibility/2006"
27
23.5k
#define NS_OXPS "http://schemas.openxps.org/oxps/v1.0"
28
typedef struct xps_parser_s xps_parser_t;
29
30
struct xps_parser_s
31
{
32
    xps_context_t *ctx;
33
    xps_item_t *root;
34
    xps_item_t *head;
35
    const char *error;
36
    char *base; /* base of relative URIs */
37
};
38
39
struct xps_item_s
40
{
41
    char *name;
42
    char **atts;
43
    xps_item_t *up;
44
    xps_item_t *down;
45
    xps_item_t *tail;
46
    xps_item_t *next;
47
};
48
49
static const char *
50
skip_namespace(const char *s)
51
94.7k
{
52
94.7k
    const char *p = strchr(s, ' ');
53
94.7k
    if (p)
54
128
        return p + 1;
55
94.6k
    return s;
56
94.7k
}
57
58
static void
59
on_open_tag(void *zp, const char *ns_name, const char **atts)
60
23.5k
{
61
23.5k
    xps_parser_t *parser = zp;
62
23.5k
    xps_context_t *ctx = parser->ctx;
63
23.5k
    xps_item_t *item;
64
23.5k
    xps_item_t *tail;
65
23.5k
    int namelen;
66
23.5k
    int attslen;
67
23.5k
    int textlen;
68
23.5k
    const char *name;
69
23.5k
    char *p;
70
23.5k
    int i;
71
72
23.5k
    if (parser->error)
73
0
        return;
74
75
    /* check namespace */
76
77
23.5k
    name = NULL;
78
79
23.5k
    p = strstr(ns_name, NS_XPS);
80
23.5k
    if (p == ns_name)
81
21.1k
    {
82
21.1k
        name = strchr(ns_name, ' ') + 1;
83
21.1k
    }
84
85
23.5k
    p = strstr(ns_name, NS_MC);
86
23.5k
    if (p == ns_name)
87
0
    {
88
0
        name = strchr(ns_name, ' ') + 1;
89
0
    }
90
91
23.5k
    p = strstr(ns_name, NS_OXPS);
92
23.5k
    if (p == ns_name)
93
0
    {
94
0
        name = strchr(ns_name, ' ') + 1;
95
0
    }
96
97
23.5k
    if (!name)
98
2.38k
    {
99
2.38k
        dmprintf1(ctx->memory, "unknown namespace: %s\n", ns_name);
100
2.38k
        name = ns_name;
101
2.38k
    }
102
103
    /* count size to alloc */
104
105
23.5k
    namelen = strlen(name) + 1; /* zero terminated */
106
23.5k
    attslen = sizeof(char*); /* with space for sentinel */
107
23.5k
    textlen = 0;
108
118k
    for (i = 0; atts[i]; i++)
109
94.7k
    {
110
94.7k
        attslen += sizeof(char*);
111
94.7k
        if ((i & 1) == 0)
112
47.3k
            textlen += strlen(skip_namespace(atts[i])) + 1;
113
47.3k
        else
114
47.3k
            textlen += strlen(atts[i]) + 1;
115
94.7k
    }
116
117
23.5k
    item = xps_alloc(ctx, (size_t)sizeof(xps_item_t) + attslen + namelen + textlen);
118
23.5k
    if (!item) {
119
0
        parser->error = "out of memory";
120
0
        gs_throw(gs_error_VMerror, "out of memory.\n");
121
0
        return;
122
0
    }
123
124
    /* copy strings to new memory */
125
126
23.5k
    item->atts = (char**) (((char*)item) + sizeof(xps_item_t));
127
23.5k
    item->name = ((char*)item) + sizeof(xps_item_t) + attslen;
128
23.5k
    p = ((char*)item) + sizeof(xps_item_t) + attslen + namelen;
129
130
23.5k
    strcpy(item->name, name);
131
118k
    for (i = 0; atts[i]; i++)
132
94.7k
    {
133
94.7k
        item->atts[i] = p;
134
94.7k
        if ((i & 1) == 0)
135
47.3k
            strcpy(item->atts[i], skip_namespace(atts[i]));
136
47.3k
        else
137
47.3k
            strcpy(item->atts[i], atts[i]);
138
94.7k
        p += strlen(p) + 1;
139
94.7k
    }
140
141
23.5k
    item->atts[i] = 0;
142
143
    /* link item into tree */
144
145
23.5k
    item->up = parser->head;
146
23.5k
    item->down = NULL;
147
23.5k
    item->next = NULL;
148
149
23.5k
    if (!parser->head)
150
64
    {
151
64
        parser->root = item;
152
64
        parser->head = item;
153
64
        return;
154
64
    }
155
156
23.4k
    if (!parser->head->down)
157
127
    {
158
127
        parser->head->down = item;
159
127
        parser->head->tail = item;
160
127
        parser->head = item;
161
127
        return;
162
127
    }
163
164
23.3k
    tail = parser->head->tail;
165
23.3k
    tail->next = item;
166
23.3k
    parser->head->tail = item;
167
23.3k
    parser->head = item;
168
23.3k
}
169
170
static void
171
on_close_tag(void *zp, const char *name)
172
23.4k
{
173
23.4k
    xps_parser_t *parser = zp;
174
175
23.4k
    if (parser->error)
176
0
        return;
177
178
23.4k
    if (parser->head)
179
23.4k
        parser->head = parser->head->up;
180
23.4k
}
181
182
static inline int
183
is_xml_space(int c)
184
58.9k
{
185
58.9k
    return c == ' ' || c == '\t' || c == '\r' || c == '\n';
186
58.9k
}
187
188
static void
189
on_text(void *zp, char *buf, int len)
190
40.4k
{
191
40.4k
    xps_parser_t *parser = zp;
192
40.4k
    xps_context_t *ctx = parser->ctx;
193
40.4k
    const char *atts[3];
194
40.4k
    int i;
195
196
40.4k
    if (parser->error)
197
0
        return;
198
199
97.0k
    for (i = 0; i < len; i++)
200
58.9k
    {
201
58.9k
        if (!is_xml_space(buf[i]))
202
2.38k
        {
203
2.38k
            char *tmp = xps_alloc(ctx, (size_t)len + 1);
204
2.38k
            if (!tmp)
205
0
            {
206
0
                parser->error = "out of memory";
207
0
                gs_throw(gs_error_VMerror, "out of memory.\n");
208
0
                return;
209
0
            }
210
211
2.38k
            atts[0] = "";
212
2.38k
            atts[1] = tmp;
213
2.38k
            atts[2] = NULL;
214
215
2.38k
            memcpy(tmp, buf, len);
216
2.38k
            tmp[len] = 0;
217
2.38k
            on_open_tag(zp, "", atts);
218
2.38k
            on_close_tag(zp, "");
219
2.38k
            xps_free(ctx, tmp);
220
2.38k
            return;
221
2.38k
        }
222
58.9k
    }
223
40.4k
}
224
225
xps_item_t *
226
xps_parse_xml(xps_context_t *ctx, byte *buf, int len)
227
65
{
228
65
    xps_parser_t parser;
229
65
    XML_Parser xp;
230
65
    int code;
231
232
65
    parser.ctx = ctx;
233
65
    parser.root = NULL;
234
65
    parser.head = NULL;
235
65
    parser.error = NULL;
236
237
65
    xp = XML_ParserCreateNS(NULL, ' ');
238
65
    if (!xp)
239
0
    {
240
0
        gs_throw(-1, "xml error: could not create expat parser");
241
0
        return NULL;
242
0
    }
243
244
65
    XML_SetUserData(xp, &parser);
245
65
    XML_SetParamEntityParsing(xp, XML_PARAM_ENTITY_PARSING_NEVER);
246
65
    XML_SetStartElementHandler(xp, (XML_StartElementHandler)on_open_tag);
247
65
    XML_SetEndElementHandler(xp, (XML_EndElementHandler)on_close_tag);
248
65
    XML_SetCharacterDataHandler(xp, (XML_CharacterDataHandler)on_text);
249
250
65
    code = XML_Parse(xp, (char*)buf, len, 1);
251
65
    if (code == 0 || parser.error != NULL)
252
60
    {
253
60
        if (parser.root)
254
59
            xps_free_item(ctx, parser.root);
255
60
        if (XML_ErrorString(XML_GetErrorCode(xp)) != 0)
256
60
            emprintf1(parser.ctx->memory, "XML_Error: %s\n", XML_ErrorString(XML_GetErrorCode(xp)));
257
60
        XML_ParserFree(xp);
258
60
        gs_throw1(-1, "parser error: %s", parser.error);
259
60
        return NULL;
260
60
    }
261
262
5
    XML_ParserFree(xp);
263
264
5
    return parser.root;
265
65
}
266
267
xps_item_t *
268
xps_next(xps_item_t *item)
269
13.7k
{
270
13.7k
    return item->next;
271
13.7k
}
272
273
xps_item_t *
274
xps_down(xps_item_t *item)
275
9.60k
{
276
9.60k
    return item->down;
277
9.60k
}
278
279
char *
280
xps_tag(xps_item_t *item)
281
47.8k
{
282
47.8k
    return item->name;
283
47.8k
}
284
285
char *
286
xps_att(xps_item_t *item, const char *att)
287
88.1k
{
288
88.1k
    int i;
289
250k
    for (i = 0; item->atts[i]; i += 2)
290
176k
        if (!strcmp(item->atts[i], att))
291
14.0k
            return item->atts[i + 1];
292
74.0k
    return NULL;
293
88.1k
}
294
295
void
296
xps_detach_and_free_remainder(xps_context_t *ctx, xps_item_t *root, xps_item_t *item)
297
0
{
298
0
    if (item->up)
299
0
        item->up->down = NULL;
300
301
0
    xps_free_item(ctx, item->next);
302
0
    item->next = NULL;
303
304
0
    xps_free_item(ctx, root);
305
0
}
306
307
void
308
xps_free_item(xps_context_t *ctx, xps_item_t *item)
309
191
{
310
191
    xps_item_t *next;
311
23.7k
    while (item)
312
23.5k
    {
313
23.5k
        next = item->next;
314
23.5k
        if (item->down)
315
127
            xps_free_item(ctx, item->down);
316
23.5k
        xps_free(ctx, item);
317
23.5k
        item = next;
318
23.5k
    }
319
191
}
320
321
static void indent(int n)
322
0
{
323
0
    while (n--)
324
0
        dlprintf("  ");
325
0
}
326
327
static void
328
xps_debug_item_imp(xps_item_t *item, int level, int loop)
329
0
{
330
0
    int i;
331
332
0
    while (item)
333
0
    {
334
0
        indent(level);
335
336
0
        if (strlen(item->name) == 0)
337
0
            dlprintf1("%s\n", item->atts[1]);
338
0
        else
339
0
        {
340
0
            dlprintf1("<%s", item->name);
341
342
0
            for (i = 0; item->atts[i]; i += 2)
343
0
                dlprintf2(" %s=\"%s\"", item->atts[i], item->atts[i+1]);
344
345
0
            if (item->down)
346
0
            {
347
0
                dlprintf(">\n");
348
0
                xps_debug_item_imp(item->down, level + 1, 1);
349
0
                indent(level);
350
0
                dlprintf1("</%s>\n", item->name);
351
0
            }
352
0
            else
353
0
                dlprintf(" />\n");
354
0
        }
355
356
0
        item = item->next;
357
358
0
        if (!loop)
359
0
            return;
360
0
    }
361
0
}
362
363
void
364
xps_debug_item(xps_item_t *item, int level)
365
0
{
366
0
    xps_debug_item_imp(item, level, 0);
367
0
}