1 /*
2 __ __ _
3 ___\ \/ /_ __ __ _| |_
4 / _ \\ /| '_ \ / _` | __|
5 | __// \| |_) | (_| | |_
6 \___/_/\_\ .__/ \__,_|\__|
7 |_| XML parser
8
9 Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
10 Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
11 Copyright (c) 2001-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
12 Copyright (c) 2004-2009 Karl Waclawek <karl@waclawek.net>
13 Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
14 Copyright (c) 2016-2026 Sebastian Pipping <sebastian@pipping.org>
15 Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk>
16 Copyright (c) 2019 David Loffredo <loffredo@steptools.com>
17 Copyright (c) 2020 Joe Orton <jorton@redhat.com>
18 Copyright (c) 2020 Kleber Tarcísio <klebertarcisio@yahoo.com.br>
19 Copyright (c) 2021 Tim Bray <tbray@textuality.com>
20 Copyright (c) 2022 Martin Ettl <ettl.martin78@googlemail.com>
21 Copyright (c) 2022 Sean McBride <sean@rogue-research.com>
22 Copyright (c) 2025 Alfonso Gregory <gfunni234@gmail.com>
23 Copyright (c) 2026 Matthew Fernandez <matthew.fernandez@gmail.com>
24 Copyright (c) 2026 Nick Begg <nick@stunttruck.net>
25 Copyright (c) 2026 Kartik Kenchi <netliomax25@gmail.com>
26 Licensed under the MIT license:
27
28 Permission is hereby granted, free of charge, to any person obtaining
29 a copy of this software and associated documentation files (the
30 "Software"), to deal in the Software without restriction, including
31 without limitation the rights to use, copy, modify, merge, publish,
32 distribute, sublicense, and/or sell copies of the Software, and to permit
33 persons to whom the Software is furnished to do so, subject to the
34 following conditions:
35
36 The above copyright notice and this permission notice shall be included
37 in all copies or substantial portions of the Software.
38
39 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
40 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
41 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
42 NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
43 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
44 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
45 USE OR OTHER DEALINGS IN THE SOFTWARE.
46
47 SPDX-License-Identifier: MIT
48 */
49
50 #include "expat_config.h"
51
52 #include <assert.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <stddef.h>
56 #include <string.h>
57 #include <math.h> /* for isnan */
58 #include <errno.h>
59
60 #include "expat.h"
61 #include "codepage.h"
62 #include "internal.h" /* for UNUSED_P only */
63 #include "fallthrough.h"
64 #include "xmlfile.h"
65 #include "xmltchar.h"
66
67 #ifdef _MSC_VER
68 # include <crtdbg.h>
69 #endif
70
71 #ifdef XML_UNICODE
72 # include <wchar.h>
73 #endif
74
75 #include "../lib/xcsinc.c"
76
77 enum ExitCode {
78 XMLWF_EXIT_SUCCESS = 0,
79 XMLWF_EXIT_INTERNAL_ERROR = 1,
80 XMLWF_EXIT_NOT_WELLFORMED = 2,
81 XMLWF_EXIT_OUTPUT_ERROR = 3,
82 XMLWF_EXIT_USAGE_ERROR = 4,
83 };
84
85 /* Structures for handler user data */
86 typedef struct NotationList {
87 struct NotationList *next;
88 const XML_Char *notationName;
89 const XML_Char *systemId;
90 const XML_Char *publicId;
91 } NotationList;
92
93 typedef struct xmlwfUserData {
94 FILE *fp;
95 NotationList *notationListHead;
96 const XML_Char *currentDoctypeName;
97 } XmlwfUserData;
98
99 /* This ensures proper sorting. */
100
101 #define NSSEP T('\001')
102
103 static void XMLCALL
characterData(void * userData,const XML_Char * s,int len)104 characterData(void *userData, const XML_Char *s, int len) {
105 FILE *fp = ((XmlwfUserData *)userData)->fp;
106 for (; len > 0; --len, ++s) {
107 switch (*s) {
108 case T('&'):
109 fputts(T("&"), fp);
110 break;
111 case T('<'):
112 fputts(T("<"), fp);
113 break;
114 case T('>'):
115 fputts(T(">"), fp);
116 break;
117 #ifdef W3C14N
118 case 13:
119 fputts(T("
"), fp);
120 break;
121 #else
122 case T('"'):
123 fputts(T("""), fp);
124 break;
125 case 9:
126 case 10:
127 case 13:
128 ftprintf(fp, T("&#%d;"), *s);
129 break;
130 #endif
131 default:
132 puttc(*s, fp);
133 break;
134 }
135 }
136 }
137
138 static void
attributeValue(FILE * fp,const XML_Char * s)139 attributeValue(FILE *fp, const XML_Char *s) {
140 puttc(T('='), fp);
141 puttc(T('"'), fp);
142 assert(s);
143 for (;;) {
144 switch (*s) {
145 case 0:
146 case NSSEP:
147 puttc(T('"'), fp);
148 return;
149 case T('&'):
150 fputts(T("&"), fp);
151 break;
152 case T('<'):
153 fputts(T("<"), fp);
154 break;
155 case T('"'):
156 fputts(T("""), fp);
157 break;
158 #ifdef W3C14N
159 case 9:
160 fputts(T("	"), fp);
161 break;
162 case 10:
163 fputts(T("
"), fp);
164 break;
165 case 13:
166 fputts(T("
"), fp);
167 break;
168 #else
169 case T('>'):
170 fputts(T(">"), fp);
171 break;
172 case 9:
173 case 10:
174 case 13:
175 ftprintf(fp, T("&#%d;"), *s);
176 break;
177 #endif
178 default:
179 puttc(*s, fp);
180 break;
181 }
182 s++;
183 }
184 }
185
186 /* Lexicographically comparing UTF-8 encoded attribute values,
187 is equivalent to lexicographically comparing based on the character number. */
188
189 static int
attcmp(const void * att1,const void * att2)190 attcmp(const void *att1, const void *att2) {
191 return tcscmp(*(const XML_Char *const *)att1, *(const XML_Char *const *)att2);
192 }
193
194 static void XMLCALL
startElement(void * userData,const XML_Char * name,const XML_Char ** atts)195 startElement(void *userData, const XML_Char *name, const XML_Char **atts) {
196 int nAtts;
197 const XML_Char **p;
198 FILE *fp = ((XmlwfUserData *)userData)->fp;
199 puttc(T('<'), fp);
200 fputts(name, fp);
201
202 p = atts;
203 while (*p)
204 ++p;
205 nAtts = (int)((p - atts) >> 1);
206 if (nAtts > 1)
207 qsort(atts, nAtts, sizeof(XML_Char *) * 2, attcmp);
208 while (*atts) {
209 puttc(T(' '), fp);
210 fputts(*atts++, fp);
211 attributeValue(fp, *atts);
212 atts++;
213 }
214 puttc(T('>'), fp);
215 }
216
217 static void XMLCALL
endElement(void * userData,const XML_Char * name)218 endElement(void *userData, const XML_Char *name) {
219 FILE *fp = ((XmlwfUserData *)userData)->fp;
220 puttc(T('<'), fp);
221 puttc(T('/'), fp);
222 fputts(name, fp);
223 puttc(T('>'), fp);
224 }
225
226 static int
nsattcmp(const void * p1,const void * p2)227 nsattcmp(const void *p1, const void *p2) {
228 const XML_Char *att1 = *(const XML_Char *const *)p1;
229 const XML_Char *att2 = *(const XML_Char *const *)p2;
230 int sep1 = (tcsrchr(att1, NSSEP) != 0);
231 int sep2 = (tcsrchr(att2, NSSEP) != 0);
232 if (sep1 != sep2)
233 return sep1 - sep2;
234 return tcscmp(att1, att2);
235 }
236
237 static void XMLCALL
startElementNS(void * userData,const XML_Char * name,const XML_Char ** atts)238 startElementNS(void *userData, const XML_Char *name, const XML_Char **atts) {
239 int nAtts;
240 int nsi;
241 const XML_Char **p;
242 FILE *fp = ((XmlwfUserData *)userData)->fp;
243 const XML_Char *sep;
244 puttc(T('<'), fp);
245
246 sep = tcsrchr(name, NSSEP);
247 if (sep) {
248 fputts(T("n1:"), fp);
249 fputts(sep + 1, fp);
250 fputts(T(" xmlns:n1"), fp);
251 attributeValue(fp, name);
252 nsi = 2;
253 } else {
254 fputts(name, fp);
255 nsi = 1;
256 }
257
258 p = atts;
259 while (*p)
260 ++p;
261 nAtts = (int)((p - atts) >> 1);
262 if (nAtts > 1)
263 qsort(atts, nAtts, sizeof(XML_Char *) * 2, nsattcmp);
264 while (*atts) {
265 name = *atts++;
266 sep = tcsrchr(name, NSSEP);
267 puttc(T(' '), fp);
268 if (sep) {
269 ftprintf(fp, T("n%d:"), nsi);
270 fputts(sep + 1, fp);
271 } else
272 fputts(name, fp);
273 attributeValue(fp, *atts);
274 if (sep) {
275 ftprintf(fp, T(" xmlns:n%d"), nsi++);
276 attributeValue(fp, name);
277 }
278 atts++;
279 }
280 puttc(T('>'), fp);
281 }
282
283 static void XMLCALL
endElementNS(void * userData,const XML_Char * name)284 endElementNS(void *userData, const XML_Char *name) {
285 FILE *fp = ((XmlwfUserData *)userData)->fp;
286 const XML_Char *sep;
287 puttc(T('<'), fp);
288 puttc(T('/'), fp);
289 sep = tcsrchr(name, NSSEP);
290 if (sep) {
291 fputts(T("n1:"), fp);
292 fputts(sep + 1, fp);
293 } else
294 fputts(name, fp);
295 puttc(T('>'), fp);
296 }
297
298 #ifndef W3C14N
299
300 static void XMLCALL
processingInstruction(void * userData,const XML_Char * target,const XML_Char * data)301 processingInstruction(void *userData, const XML_Char *target,
302 const XML_Char *data) {
303 FILE *fp = ((XmlwfUserData *)userData)->fp;
304 puttc(T('<'), fp);
305 puttc(T('?'), fp);
306 fputts(target, fp);
307 puttc(T(' '), fp);
308 fputts(data, fp);
309 puttc(T('?'), fp);
310 puttc(T('>'), fp);
311 }
312
313 static XML_Char *
xcsdup(const XML_Char * s)314 xcsdup(const XML_Char *s) {
315 const size_t count = xcslen(s) + /* null terminator */ 1;
316
317 // Detect and prevent integer overflow
318 if (count > SIZE_MAX / sizeof(XML_Char))
319 return NULL;
320
321 const size_t numBytes = count * sizeof(XML_Char);
322 XML_Char *const result = malloc(numBytes);
323 if (result == NULL)
324 return NULL;
325 memcpy(result, s, numBytes);
326 return result;
327 }
328
329 static void XMLCALL
startDoctypeDecl(void * userData,const XML_Char * doctypeName,const XML_Char * sysid,const XML_Char * publid,int has_internal_subset)330 startDoctypeDecl(void *userData, const XML_Char *doctypeName,
331 const XML_Char *sysid, const XML_Char *publid,
332 int has_internal_subset) {
333 XmlwfUserData *data = userData;
334 UNUSED_P(sysid);
335 UNUSED_P(publid);
336 UNUSED_P(has_internal_subset);
337 data->currentDoctypeName = xcsdup(doctypeName);
338 }
339
340 static void
freeNotations(XmlwfUserData * data)341 freeNotations(XmlwfUserData *data) {
342 NotationList *notationListHead = data->notationListHead;
343
344 while (notationListHead != NULL) {
345 NotationList *next = notationListHead->next;
346 free((void *)notationListHead->notationName);
347 free((void *)notationListHead->systemId);
348 free((void *)notationListHead->publicId);
349 free(notationListHead);
350 notationListHead = next;
351 }
352 data->notationListHead = NULL;
353 }
354
355 static void
cleanupUserData(XmlwfUserData * userData)356 cleanupUserData(XmlwfUserData *userData) {
357 free((void *)userData->currentDoctypeName);
358 userData->currentDoctypeName = NULL;
359 freeNotations(userData);
360 }
361
362 static int
xcscmp(const XML_Char * xs,const XML_Char * xt)363 xcscmp(const XML_Char *xs, const XML_Char *xt) {
364 while (*xs != 0 && *xt != 0) {
365 if (*xs < *xt)
366 return -1;
367 if (*xs > *xt)
368 return 1;
369 xs++;
370 xt++;
371 }
372 if (*xs < *xt)
373 return -1;
374 if (*xs > *xt)
375 return 1;
376 return 0;
377 }
378
379 static int
notationCmp(const void * a,const void * b)380 notationCmp(const void *a, const void *b) {
381 const NotationList *const n1 = *(const NotationList *const *)a;
382 const NotationList *const n2 = *(const NotationList *const *)b;
383
384 return xcscmp(n1->notationName, n2->notationName);
385 }
386
387 /* Write a SystemLiteral/PubidLiteral, choosing a delimiter that does not
388 occur in the value. The grammar forbids a literal from containing its
389 own delimiter, so a value reported by Expat never holds both quote
390 characters and a safe delimiter always exists. */
391 static void
writeLiteral(FILE * fp,const XML_Char * value)392 writeLiteral(FILE *fp, const XML_Char *value) {
393 const XML_Char quote = (tcschr(value, T('\'')) != NULL) ? T('"') : T('\'');
394 puttc(quote, fp);
395 fputts(value, fp);
396 puttc(quote, fp);
397 }
398
399 static void XMLCALL
endDoctypeDecl(void * userData)400 endDoctypeDecl(void *userData) {
401 XmlwfUserData *data = userData;
402 NotationList **notations;
403 size_t notationCount = 0;
404 NotationList *p;
405 size_t i;
406
407 /* How many notations do we have? */
408 for (p = data->notationListHead; p != NULL; p = p->next)
409 notationCount++;
410 if (notationCount == 0) {
411 /* Nothing to report */
412 goto cleanUp;
413 }
414
415 /* Detect and prevent integer overflow in the multiplication, mirroring
416 the guards in xcsdup() and resolveSystemId() */
417 if (notationCount > SIZE_MAX / sizeof(NotationList *)) {
418 fprintf(stderr, "Unable to sort notations");
419 goto cleanUp;
420 }
421
422 notations = malloc(notationCount * sizeof(NotationList *));
423 if (notations == NULL) {
424 fprintf(stderr, "Unable to sort notations");
425 goto cleanUp;
426 }
427
428 for (p = data->notationListHead, i = 0; i < notationCount; p = p->next, i++) {
429 notations[i] = p;
430 }
431 qsort(notations, notationCount, sizeof(NotationList *), notationCmp);
432
433 /* Output the DOCTYPE header */
434 fputts(T("<!DOCTYPE "), data->fp);
435 fputts(data->currentDoctypeName, data->fp);
436 fputts(T(" [\n"), data->fp);
437
438 /* Now the NOTATIONs */
439 for (i = 0; i < notationCount; i++) {
440 fputts(T("<!NOTATION "), data->fp);
441 fputts(notations[i]->notationName, data->fp);
442 if (notations[i]->publicId != NULL) {
443 fputts(T(" PUBLIC "), data->fp);
444 writeLiteral(data->fp, notations[i]->publicId);
445 if (notations[i]->systemId != NULL) {
446 puttc(T(' '), data->fp);
447 writeLiteral(data->fp, notations[i]->systemId);
448 }
449 } else if (notations[i]->systemId != NULL) {
450 fputts(T(" SYSTEM "), data->fp);
451 writeLiteral(data->fp, notations[i]->systemId);
452 }
453 puttc(T('>'), data->fp);
454 puttc(T('\n'), data->fp);
455 }
456
457 /* Finally end the DOCTYPE */
458 fputts(T("]>\n"), data->fp);
459
460 free(notations);
461
462 cleanUp:
463 freeNotations(data);
464 free((void *)data->currentDoctypeName);
465 data->currentDoctypeName = NULL;
466 }
467
468 static void XMLCALL
notationDecl(void * userData,const XML_Char * notationName,const XML_Char * base,const XML_Char * systemId,const XML_Char * publicId)469 notationDecl(void *userData, const XML_Char *notationName, const XML_Char *base,
470 const XML_Char *systemId, const XML_Char *publicId) {
471 XmlwfUserData *data = userData;
472 NotationList *entry = malloc(sizeof(NotationList));
473 const char *errorMessage = "Unable to store NOTATION for output\n";
474
475 UNUSED_P(base);
476 if (entry == NULL) {
477 fputs(errorMessage, stderr);
478 return; /* Nothing we can really do about this */
479 }
480 entry->notationName = xcsdup(notationName);
481 if (entry->notationName == NULL) {
482 fputs(errorMessage, stderr);
483 free(entry);
484 return;
485 }
486 if (systemId != NULL) {
487 entry->systemId = xcsdup(systemId);
488 if (entry->systemId == NULL) {
489 fputs(errorMessage, stderr);
490 free((void *)entry->notationName);
491 free(entry);
492 return;
493 }
494 } else {
495 entry->systemId = NULL;
496 }
497 if (publicId != NULL) {
498 entry->publicId = xcsdup(publicId);
499 if (entry->publicId == NULL) {
500 fputs(errorMessage, stderr);
501 free((void *)entry->systemId); /* Safe if it's NULL */
502 free((void *)entry->notationName);
503 free(entry);
504 return;
505 }
506 } else {
507 entry->publicId = NULL;
508 }
509
510 entry->next = data->notationListHead;
511 data->notationListHead = entry;
512 }
513
514 #endif /* not W3C14N */
515
516 static void XMLCALL
defaultCharacterData(void * userData,const XML_Char * s,int len)517 defaultCharacterData(void *userData, const XML_Char *s, int len) {
518 UNUSED_P(s);
519 UNUSED_P(len);
520 XML_DefaultCurrent(userData);
521 }
522
523 static void XMLCALL
defaultStartElement(void * userData,const XML_Char * name,const XML_Char ** atts)524 defaultStartElement(void *userData, const XML_Char *name,
525 const XML_Char **atts) {
526 UNUSED_P(name);
527 UNUSED_P(atts);
528 XML_DefaultCurrent(userData);
529 }
530
531 static void XMLCALL
defaultEndElement(void * userData,const XML_Char * name)532 defaultEndElement(void *userData, const XML_Char *name) {
533 UNUSED_P(name);
534 XML_DefaultCurrent(userData);
535 }
536
537 static void XMLCALL
defaultProcessingInstruction(void * userData,const XML_Char * target,const XML_Char * data)538 defaultProcessingInstruction(void *userData, const XML_Char *target,
539 const XML_Char *data) {
540 UNUSED_P(target);
541 UNUSED_P(data);
542 XML_DefaultCurrent(userData);
543 }
544
545 static void XMLCALL
nopCharacterData(void * userData,const XML_Char * s,int len)546 nopCharacterData(void *userData, const XML_Char *s, int len) {
547 UNUSED_P(userData);
548 UNUSED_P(s);
549 UNUSED_P(len);
550 }
551
552 static void XMLCALL
nopStartElement(void * userData,const XML_Char * name,const XML_Char ** atts)553 nopStartElement(void *userData, const XML_Char *name, const XML_Char **atts) {
554 UNUSED_P(userData);
555 UNUSED_P(name);
556 UNUSED_P(atts);
557 }
558
559 static void XMLCALL
nopEndElement(void * userData,const XML_Char * name)560 nopEndElement(void *userData, const XML_Char *name) {
561 UNUSED_P(userData);
562 UNUSED_P(name);
563 }
564
565 static void XMLCALL
nopProcessingInstruction(void * userData,const XML_Char * target,const XML_Char * data)566 nopProcessingInstruction(void *userData, const XML_Char *target,
567 const XML_Char *data) {
568 UNUSED_P(userData);
569 UNUSED_P(target);
570 UNUSED_P(data);
571 }
572
573 static void XMLCALL
markup(void * userData,const XML_Char * s,int len)574 markup(void *userData, const XML_Char *s, int len) {
575 FILE *fp = ((XmlwfUserData *)XML_GetUserData(userData))->fp;
576 for (; len > 0; --len, ++s)
577 puttc(*s, fp);
578 }
579
580 static void
metaLocation(XML_Parser parser)581 metaLocation(XML_Parser parser) {
582 const XML_Char *uri = XML_GetBase(parser);
583 FILE *fp = ((XmlwfUserData *)XML_GetUserData(parser))->fp;
584 if (uri) {
585 fputts(T(" uri=\""), fp);
586 characterData(XML_GetUserData(parser), uri, (int)tcslen(uri));
587 puttc(T('"'), fp);
588 }
589 ftprintf(fp,
590 T(" byte=\"%") T(XML_FMT_INT_MOD) T("d\"") T(" nbytes=\"%d\"")
591 T(" line=\"%") T(XML_FMT_INT_MOD) T("u\"") T(" col=\"%")
592 T(XML_FMT_INT_MOD) T("u\""),
593 XML_GetCurrentByteIndex(parser), XML_GetCurrentByteCount(parser),
594 XML_GetCurrentLineNumber(parser),
595 XML_GetCurrentColumnNumber(parser));
596 }
597
598 static void
metaStartDocument(void * userData)599 metaStartDocument(void *userData) {
600 fputts(T("<document>\n"), ((XmlwfUserData *)XML_GetUserData(userData))->fp);
601 }
602
603 static void
metaEndDocument(void * userData)604 metaEndDocument(void *userData) {
605 fputts(T("</document>\n"), ((XmlwfUserData *)XML_GetUserData(userData))->fp);
606 }
607
608 static void XMLCALL
metaStartElement(void * userData,const XML_Char * name,const XML_Char ** atts)609 metaStartElement(void *userData, const XML_Char *name, const XML_Char **atts) {
610 XML_Parser parser = userData;
611 XmlwfUserData *data = XML_GetUserData(parser);
612 FILE *fp = data->fp;
613 const XML_Char **specifiedAttsEnd
614 = atts + XML_GetSpecifiedAttributeCount(parser);
615 const XML_Char **idAttPtr;
616 int idAttIndex = XML_GetIdAttributeIndex(parser);
617 if (idAttIndex < 0)
618 idAttPtr = 0;
619 else
620 idAttPtr = atts + idAttIndex;
621
622 fputts(T("<starttag name=\""), fp);
623 characterData(data, name, (int)tcslen(name));
624 puttc(T('"'), fp);
625 metaLocation(parser);
626 if (*atts) {
627 fputts(T(">\n"), fp);
628 do {
629 fputts(T("<attribute name=\""), fp);
630 characterData(data, atts[0], (int)tcslen(atts[0]));
631 fputts(T("\" value=\""), fp);
632 characterData(data, atts[1], (int)tcslen(atts[1]));
633 if (atts >= specifiedAttsEnd)
634 fputts(T("\" defaulted=\"yes\"/>\n"), fp);
635 else if (atts == idAttPtr)
636 fputts(T("\" id=\"yes\"/>\n"), fp);
637 else
638 fputts(T("\"/>\n"), fp);
639 } while (*(atts += 2));
640 fputts(T("</starttag>\n"), fp);
641 } else
642 fputts(T("/>\n"), fp);
643 }
644
645 static void XMLCALL
metaEndElement(void * userData,const XML_Char * name)646 metaEndElement(void *userData, const XML_Char *name) {
647 XML_Parser parser = userData;
648 XmlwfUserData *data = XML_GetUserData(parser);
649 FILE *fp = data->fp;
650 fputts(T("<endtag name=\""), fp);
651 characterData(data, name, (int)tcslen(name));
652 puttc(T('"'), fp);
653 metaLocation(parser);
654 fputts(T("/>\n"), fp);
655 }
656
657 static void XMLCALL
metaProcessingInstruction(void * userData,const XML_Char * target,const XML_Char * data)658 metaProcessingInstruction(void *userData, const XML_Char *target,
659 const XML_Char *data) {
660 XML_Parser parser = userData;
661 XmlwfUserData *usrData = XML_GetUserData(parser);
662 FILE *fp = usrData->fp;
663 ftprintf(fp, T("<pi target=\"%s\" data=\""), target);
664 characterData(usrData, data, (int)tcslen(data));
665 puttc(T('"'), fp);
666 metaLocation(parser);
667 fputts(T("/>\n"), fp);
668 }
669
670 static void XMLCALL
metaComment(void * userData,const XML_Char * data)671 metaComment(void *userData, const XML_Char *data) {
672 XML_Parser parser = userData;
673 XmlwfUserData *usrData = XML_GetUserData(parser);
674 FILE *fp = usrData->fp;
675 fputts(T("<comment data=\""), fp);
676 characterData(usrData, data, (int)tcslen(data));
677 puttc(T('"'), fp);
678 metaLocation(parser);
679 fputts(T("/>\n"), fp);
680 }
681
682 static void XMLCALL
metaStartCdataSection(void * userData)683 metaStartCdataSection(void *userData) {
684 XML_Parser parser = userData;
685 XmlwfUserData *data = XML_GetUserData(parser);
686 FILE *fp = data->fp;
687 fputts(T("<startcdata"), fp);
688 metaLocation(parser);
689 fputts(T("/>\n"), fp);
690 }
691
692 static void XMLCALL
metaEndCdataSection(void * userData)693 metaEndCdataSection(void *userData) {
694 XML_Parser parser = userData;
695 XmlwfUserData *data = XML_GetUserData(parser);
696 FILE *fp = data->fp;
697 fputts(T("<endcdata"), fp);
698 metaLocation(parser);
699 fputts(T("/>\n"), fp);
700 }
701
702 static void XMLCALL
metaCharacterData(void * userData,const XML_Char * s,int len)703 metaCharacterData(void *userData, const XML_Char *s, int len) {
704 XML_Parser parser = userData;
705 XmlwfUserData *data = XML_GetUserData(parser);
706 FILE *fp = data->fp;
707 fputts(T("<chars str=\""), fp);
708 characterData(data, s, len);
709 puttc(T('"'), fp);
710 metaLocation(parser);
711 fputts(T("/>\n"), fp);
712 }
713
714 static void XMLCALL
metaStartDoctypeDecl(void * userData,const XML_Char * doctypeName,const XML_Char * sysid,const XML_Char * pubid,int has_internal_subset)715 metaStartDoctypeDecl(void *userData, const XML_Char *doctypeName,
716 const XML_Char *sysid, const XML_Char *pubid,
717 int has_internal_subset) {
718 XML_Parser parser = userData;
719 XmlwfUserData *data = XML_GetUserData(parser);
720 FILE *fp = data->fp;
721 UNUSED_P(sysid);
722 UNUSED_P(pubid);
723 UNUSED_P(has_internal_subset);
724 ftprintf(fp, T("<startdoctype name=\"%s\""), doctypeName);
725 metaLocation(parser);
726 fputts(T("/>\n"), fp);
727 }
728
729 static void XMLCALL
metaEndDoctypeDecl(void * userData)730 metaEndDoctypeDecl(void *userData) {
731 XML_Parser parser = userData;
732 XmlwfUserData *data = XML_GetUserData(parser);
733 FILE *fp = data->fp;
734 fputts(T("<enddoctype"), fp);
735 metaLocation(parser);
736 fputts(T("/>\n"), fp);
737 }
738
739 static void XMLCALL
metaNotationDecl(void * userData,const XML_Char * notationName,const XML_Char * base,const XML_Char * systemId,const XML_Char * publicId)740 metaNotationDecl(void *userData, const XML_Char *notationName,
741 const XML_Char *base, const XML_Char *systemId,
742 const XML_Char *publicId) {
743 XML_Parser parser = userData;
744 XmlwfUserData *data = XML_GetUserData(parser);
745 FILE *fp = data->fp;
746 UNUSED_P(base);
747 ftprintf(fp, T("<notation name=\"%s\""), notationName);
748 if (publicId)
749 ftprintf(fp, T(" public=\"%s\""), publicId);
750 if (systemId) {
751 fputts(T(" system=\""), fp);
752 characterData(data, systemId, (int)tcslen(systemId));
753 puttc(T('"'), fp);
754 }
755 metaLocation(parser);
756 fputts(T("/>\n"), fp);
757 }
758
759 static void XMLCALL
metaEntityDecl(void * userData,const XML_Char * entityName,int is_param,const XML_Char * value,int value_length,const XML_Char * base,const XML_Char * systemId,const XML_Char * publicId,const XML_Char * notationName)760 metaEntityDecl(void *userData, const XML_Char *entityName, int is_param,
761 const XML_Char *value, int value_length, const XML_Char *base,
762 const XML_Char *systemId, const XML_Char *publicId,
763 const XML_Char *notationName) {
764 XML_Parser parser = userData;
765 XmlwfUserData *data = XML_GetUserData(parser);
766 FILE *fp = data->fp;
767
768 UNUSED_P(is_param);
769 UNUSED_P(base);
770 if (value) {
771 ftprintf(fp, T("<entity name=\"%s\""), entityName);
772 metaLocation(parser);
773 puttc(T('>'), fp);
774 characterData(data, value, value_length);
775 fputts(T("</entity/>\n"), fp);
776 } else if (notationName) {
777 ftprintf(fp, T("<entity name=\"%s\""), entityName);
778 if (publicId)
779 ftprintf(fp, T(" public=\"%s\""), publicId);
780 fputts(T(" system=\""), fp);
781 characterData(data, systemId, (int)tcslen(systemId));
782 puttc(T('"'), fp);
783 ftprintf(fp, T(" notation=\"%s\""), notationName);
784 metaLocation(parser);
785 fputts(T("/>\n"), fp);
786 } else {
787 ftprintf(fp, T("<entity name=\"%s\""), entityName);
788 if (publicId)
789 ftprintf(fp, T(" public=\"%s\""), publicId);
790 fputts(T(" system=\""), fp);
791 characterData(data, systemId, (int)tcslen(systemId));
792 puttc(T('"'), fp);
793 metaLocation(parser);
794 fputts(T("/>\n"), fp);
795 }
796 }
797
798 static void XMLCALL
metaStartNamespaceDecl(void * userData,const XML_Char * prefix,const XML_Char * uri)799 metaStartNamespaceDecl(void *userData, const XML_Char *prefix,
800 const XML_Char *uri) {
801 XML_Parser parser = userData;
802 XmlwfUserData *data = XML_GetUserData(parser);
803 FILE *fp = data->fp;
804 fputts(T("<startns"), fp);
805 if (prefix)
806 ftprintf(fp, T(" prefix=\"%s\""), prefix);
807 if (uri) {
808 fputts(T(" ns=\""), fp);
809 characterData(data, uri, (int)tcslen(uri));
810 fputts(T("\"/>\n"), fp);
811 } else
812 fputts(T("/>\n"), fp);
813 }
814
815 static void XMLCALL
metaEndNamespaceDecl(void * userData,const XML_Char * prefix)816 metaEndNamespaceDecl(void *userData, const XML_Char *prefix) {
817 XML_Parser parser = userData;
818 XmlwfUserData *data = XML_GetUserData(parser);
819 FILE *fp = data->fp;
820 if (! prefix)
821 fputts(T("<endns/>\n"), fp);
822 else
823 ftprintf(fp, T("<endns prefix=\"%s\"/>\n"), prefix);
824 }
825
826 static int XMLCALL
unknownEncodingConvert(void * data,const char * p)827 unknownEncodingConvert(void *data, const char *p) {
828 return codepageConvert(*(int *)data, p);
829 }
830
831 static int XMLCALL
unknownEncoding(void * userData,const XML_Char * name,XML_Encoding * info)832 unknownEncoding(void *userData, const XML_Char *name, XML_Encoding *info) {
833 int cp;
834 static const XML_Char prefixL[] = T("windows-");
835 static const XML_Char prefixU[] = T("WINDOWS-");
836 int i;
837
838 UNUSED_P(userData);
839 for (i = 0; prefixU[i]; i++)
840 if (name[i] != prefixU[i] && name[i] != prefixL[i])
841 return 0;
842
843 cp = 0;
844 for (; name[i]; i++) {
845 static const XML_Char digits[] = T("0123456789");
846 const XML_Char *s = tcschr(digits, name[i]);
847 if (! s)
848 return 0;
849 cp *= 10;
850 cp += (int)(s - digits);
851 if (cp >= 0x10000)
852 return 0;
853 }
854 if (! codepageMap(cp, info->map))
855 return 0;
856 info->convert = unknownEncodingConvert;
857 /* We could just cast the code page integer to a void *,
858 and avoid the use of release. */
859 info->release = free;
860 info->data = malloc(sizeof(int));
861 if (! info->data)
862 return 0;
863 *(int *)info->data = cp;
864 return 1;
865 }
866
867 static int XMLCALL
notStandalone(void * userData)868 notStandalone(void *userData) {
869 UNUSED_P(userData);
870 return 0;
871 }
872
873 static void
showVersion(XML_Char * prog)874 showVersion(XML_Char *prog) {
875 XML_Char *s = prog;
876 XML_Char ch;
877 const XML_Feature *features = XML_GetFeatureList();
878 while ((ch = *s) != 0) {
879 if (ch == '/'
880 #if defined(_WIN32)
881 || ch == '\\'
882 #endif
883 )
884 prog = s + 1;
885 ++s;
886 }
887 ftprintf(stdout, T("%s using %s\n"), prog, XML_ExpatVersion());
888 if (features != NULL && features[0].feature != XML_FEATURE_END) {
889 int i = 1;
890 ftprintf(stdout, T("%s"), features[0].name);
891 if (features[0].value)
892 ftprintf(stdout, T("=%ld"), features[0].value);
893 while (features[i].feature != XML_FEATURE_END) {
894 ftprintf(stdout, T(", %s"), features[i].name);
895 if (features[i].value)
896 ftprintf(stdout, T("=%ld"), features[i].value);
897 ++i;
898 }
899 ftprintf(stdout, T("\n"));
900 }
901 }
902
903 #if defined(__GNUC__)
904 __attribute__((noreturn))
905 #endif
906 static void
usage(const XML_Char * prog,int rc)907 usage(const XML_Char *prog, int rc) {
908 ftprintf(
909 stderr,
910 /* Generated with:
911 * $ xmlwf/xmlwf_helpgen.sh
912 * To update, change xmlwf/xmlwf_helpgen.py, then paste the output of
913 * xmlwf/xmlwf_helpgen.sh in here.
914 */
915 /* clang-format off */
916 T("usage:\n")
917 T(" %s [OPTIONS] [FILE ...]\n")
918 T(" %s -h|--help\n")
919 T(" %s -v|--version\n")
920 T("\n")
921 T("xmlwf - Determines if an XML document is well-formed\n")
922 T("\n")
923 T("positional arguments:\n")
924 T(" FILE file to process (default: STDIN)\n")
925 T("\n")
926 T("input control arguments:\n")
927 T(" -s print an error if the document is not [s]tandalone\n")
928 T(" -n enable [n]amespace processing\n")
929 T(" -p enable processing of external DTDs and [p]arameter entities\n")
930 T(" -x enable processing of e[x]ternal entities\n")
931 T(" (CAREFUL! This makes xmlwf vulnerable to external entity attacks (XXE).)\n")
932 T(" -e ENCODING override any in-document [e]ncoding declaration\n")
933 T(" -w enable support for [W]indows code pages\n")
934 T(" -r disable memory-mapping and use [r]ead calls instead\n")
935 T(" -g BYTES buffer size to request per call pair to XML_[G]etBuffer and read (default: 8 KiB)\n")
936 T(" -k when processing multiple files, [k]eep processing after first file with error\n")
937 T("\n")
938 T("output control arguments:\n")
939 T(" -d DIRECTORY output [d]estination directory\n")
940 T(" -c write a [c]opy of input XML, not canonical XML\n")
941 T(" -m write [m]eta XML, not canonical XML\n")
942 T(" -t write no XML output for [t]iming of plain parsing\n")
943 T(" -N enable adding doctype and [n]otation declarations\n")
944 T("\n")
945 T("amplification attack protection (e.g. billion laughs):\n")
946 T(" NOTE: If you ever need to increase these values for non-attack payload, please file a bug report.\n")
947 T("\n")
948 T(" -a FACTOR set maximum tolerated [a]mplification factor (default: 100.0)\n")
949 T(" -b BYTES set number of output [b]ytes needed to activate (default: 8 MiB/64 MiB)\n")
950 T("\n")
951 T("reparse deferral:\n")
952 T(" -q disable reparse deferral, and allow [q]uadratic parse runtime with large tokens\n")
953 T("\n")
954 T("info arguments:\n")
955 T(" -h, --help show this [h]elp message and exit\n")
956 T(" -v, --version show program's [v]ersion number and exit\n")
957 T("\n")
958 T("environment variables:\n")
959 T(" EXPAT_ACCOUNTING_DEBUG=(0|1|2|3)\n")
960 T(" Control verbosity of accounting debugging (default: 0)\n")
961 T(" EXPAT_ENTITY_DEBUG=(0|1|2)\n")
962 T(" Control verbosity of entity debugging (default: 0)\n")
963 T(" EXPAT_ENTROPY_DEBUG=(0|1)\n")
964 T(" Control verbosity of entropy debugging (default: 0)\n")
965 T(" EXPAT_MALLOC_DEBUG=(0|1|2)\n")
966 T(" Control verbosity of allocation tracker (default: 0)\n")
967 T("\n")
968 T("exit status:\n")
969 T(" 0 the input files are well-formed and the output (if requested) was written successfully\n")
970 T(" 1 could not allocate data structures, signals a serious problem with execution environment\n")
971 T(" 2 one or more input files were not well-formed\n")
972 T(" 3 could not create an output file\n")
973 T(" 4 command-line argument error\n")
974 T("\n")
975 T("xmlwf of libexpat is software libre, licensed under the MIT license.\n")
976 T("Please report bugs at https://github.com/libexpat/libexpat/issues -- thank you!\n")
977 , /* clang-format on */
978 prog, prog, prog);
979 exit(rc);
980 }
981
982 #if defined(__MINGW32__) && defined(XML_UNICODE)
983 /* Silence warning about missing prototype */
984 int wmain(int argc, XML_Char **argv);
985 #endif
986
987 #define XMLWF_SHIFT_ARG_INTO(constCharStarTarget, argc, argv, i, j) \
988 { \
989 if (argv[i][j + 1] == T('\0')) { \
990 if (++i == argc) { \
991 usage(argv[0], XMLWF_EXIT_USAGE_ERROR); \
992 /* usage called exit(..), never gets here */ \
993 } \
994 constCharStarTarget = argv[i]; \
995 } else { \
996 constCharStarTarget = argv[i] + j + 1; \
997 } \
998 i++; \
999 j = 0; \
1000 }
1001
1002 int
tmain(int argc,XML_Char ** argv)1003 tmain(int argc, XML_Char **argv) {
1004 int i, j;
1005 const XML_Char *outputDir = NULL;
1006 const XML_Char *encoding = NULL;
1007 unsigned processFlags = XML_MAP_FILE;
1008 int windowsCodePages = 0;
1009 int outputType = 0;
1010 int useNamespaces = 0;
1011 int requireStandalone = 0;
1012 int requiresNotations = 0;
1013 int continueOnError = 0;
1014
1015 float attackMaximumAmplification = -1.0f; /* signaling "not set" */
1016 unsigned long long attackThresholdBytes = 0;
1017 XML_Bool attackThresholdGiven = XML_FALSE;
1018
1019 XML_Bool disableDeferral = XML_FALSE;
1020
1021 int exitCode = XMLWF_EXIT_SUCCESS;
1022 enum XML_ParamEntityParsing paramEntityParsing
1023 = XML_PARAM_ENTITY_PARSING_NEVER;
1024 int useStdin = 0;
1025 XmlwfUserData userData = {NULL, NULL, NULL};
1026
1027 #ifdef _MSC_VER
1028 _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
1029 #endif
1030
1031 i = 1;
1032 j = 0;
1033 while (i < argc) {
1034 if (j == 0) {
1035 if (argv[i][0] != T('-'))
1036 break;
1037 if (argv[i][1] == T('-')) {
1038 if (argv[i][2] == T('\0')) {
1039 i++;
1040 break;
1041 } else if (tcscmp(argv[i] + 2, T("help")) == 0) {
1042 usage(argv[0], XMLWF_EXIT_SUCCESS);
1043 // usage called exit(..), never gets here
1044 } else if (tcscmp(argv[i] + 2, T("version")) == 0) {
1045 showVersion(argv[0]);
1046 return XMLWF_EXIT_SUCCESS;
1047 }
1048 }
1049 j++;
1050 }
1051 switch (argv[i][j]) {
1052 case T('r'):
1053 processFlags &= ~XML_MAP_FILE;
1054 j++;
1055 break;
1056 case T('s'):
1057 requireStandalone = 1;
1058 j++;
1059 break;
1060 case T('n'):
1061 useNamespaces = 1;
1062 j++;
1063 break;
1064 case T('p'):
1065 paramEntityParsing = XML_PARAM_ENTITY_PARSING_ALWAYS;
1066 EXPAT_FALLTHROUGH;
1067 case T('x'):
1068 processFlags |= XML_EXTERNAL_ENTITIES;
1069 j++;
1070 break;
1071 case T('w'):
1072 windowsCodePages = 1;
1073 j++;
1074 break;
1075 case T('m'):
1076 outputType = 'm';
1077 j++;
1078 break;
1079 case T('c'):
1080 outputType = 'c';
1081 useNamespaces = 0;
1082 j++;
1083 break;
1084 case T('t'):
1085 outputType = 't';
1086 j++;
1087 break;
1088 case T('N'):
1089 requiresNotations = 1;
1090 j++;
1091 break;
1092 case T('d'):
1093 XMLWF_SHIFT_ARG_INTO(outputDir, argc, argv, i, j);
1094 break;
1095 case T('e'):
1096 XMLWF_SHIFT_ARG_INTO(encoding, argc, argv, i, j);
1097 break;
1098 case T('h'):
1099 usage(argv[0], XMLWF_EXIT_SUCCESS);
1100 // usage called exit(..), never gets here
1101 case T('v'):
1102 showVersion(argv[0]);
1103 return XMLWF_EXIT_SUCCESS;
1104 case T('g'): {
1105 const XML_Char *valueText = NULL;
1106 XMLWF_SHIFT_ARG_INTO(valueText, argc, argv, i, j);
1107
1108 errno = 0;
1109 XML_Char *afterValueText = (XML_Char *)valueText;
1110 const long long read_size_bytes_candidate
1111 = tcstoull(valueText, &afterValueText, 10);
1112 if ((errno != 0) || (afterValueText[0] != T('\0'))
1113 || (read_size_bytes_candidate < 1)
1114 || (read_size_bytes_candidate > (INT_MAX / 2 + 1))) {
1115 // This prevents tperror(..) from reporting misleading "[..]: Success"
1116 errno = ERANGE;
1117 tperror(T("invalid buffer size") T(
1118 " (needs an integer from 1 to INT_MAX/2+1 i.e. 1,073,741,824 on most platforms)"));
1119 exit(XMLWF_EXIT_USAGE_ERROR);
1120 }
1121 g_read_size_bytes = (int)read_size_bytes_candidate;
1122 break;
1123 }
1124 case T('k'):
1125 continueOnError = 1;
1126 j++;
1127 break;
1128 case T('a'): {
1129 const XML_Char *valueText = NULL;
1130 XMLWF_SHIFT_ARG_INTO(valueText, argc, argv, i, j);
1131
1132 errno = 0;
1133 XML_Char *afterValueText = NULL;
1134 attackMaximumAmplification = tcstof(valueText, &afterValueText);
1135 if ((errno != 0) || (afterValueText[0] != T('\0'))
1136 || isnan(attackMaximumAmplification)
1137 || (attackMaximumAmplification < 1.0f)) {
1138 // This prevents tperror(..) from reporting misleading "[..]: Success"
1139 errno = ERANGE;
1140 tperror(T("invalid amplification limit") T(
1141 " (needs a floating point number greater or equal than 1.0)"));
1142 exit(XMLWF_EXIT_USAGE_ERROR);
1143 }
1144 #if XML_GE == 0
1145 ftprintf(stderr,
1146 T("Warning: Given amplification limit ignored")
1147 T(", xmlwf has been compiled without DTD/GE support.\n"));
1148 #endif
1149 break;
1150 }
1151 case T('b'): {
1152 const XML_Char *valueText = NULL;
1153 XMLWF_SHIFT_ARG_INTO(valueText, argc, argv, i, j);
1154
1155 errno = 0;
1156 XML_Char *afterValueText = (XML_Char *)valueText;
1157 attackThresholdBytes = tcstoull(valueText, &afterValueText, 10);
1158 if ((errno != 0) || (afterValueText[0] != T('\0'))) {
1159 // This prevents tperror(..) from reporting misleading "[..]: Success"
1160 errno = ERANGE;
1161 tperror(T("invalid ignore threshold")
1162 T(" (needs an integer from 0 to 2^64-1)"));
1163 exit(XMLWF_EXIT_USAGE_ERROR);
1164 }
1165 attackThresholdGiven = XML_TRUE;
1166 #if XML_GE == 0
1167 ftprintf(stderr,
1168 T("Warning: Given attack threshold ignored")
1169 T(", xmlwf has been compiled without DTD/GE support.\n"));
1170 #endif
1171 break;
1172 }
1173 case T('q'): {
1174 disableDeferral = XML_TRUE;
1175 j++;
1176 break;
1177 }
1178 case T('\0'):
1179 if (j > 1) {
1180 i++;
1181 j = 0;
1182 break;
1183 }
1184 EXPAT_FALLTHROUGH;
1185 default:
1186 usage(argv[0], XMLWF_EXIT_USAGE_ERROR);
1187 // usage called exit(..), never gets here
1188 }
1189 }
1190 if (i == argc) {
1191 useStdin = 1;
1192 processFlags &= ~XML_MAP_FILE;
1193 i--;
1194 }
1195 for (; i < argc; i++) {
1196 XML_Char *outName = 0;
1197 int result;
1198 XML_Parser parser;
1199 if (useNamespaces)
1200 parser = XML_ParserCreateNS(encoding, NSSEP);
1201 else
1202 parser = XML_ParserCreate(encoding);
1203
1204 if (! parser) {
1205 tperror(T("Could not instantiate parser"));
1206 exit(XMLWF_EXIT_INTERNAL_ERROR);
1207 }
1208
1209 if (attackMaximumAmplification != -1.0f) {
1210 #if XML_GE == 1
1211 XML_SetBillionLaughsAttackProtectionMaximumAmplification(
1212 parser, attackMaximumAmplification);
1213 XML_SetAllocTrackerMaximumAmplification(parser,
1214 attackMaximumAmplification);
1215 #endif
1216 }
1217 if (attackThresholdGiven) {
1218 #if XML_GE == 1
1219 XML_SetBillionLaughsAttackProtectionActivationThreshold(
1220 parser, attackThresholdBytes);
1221 XML_SetAllocTrackerActivationThreshold(parser, attackThresholdBytes);
1222 #else
1223 (void)attackThresholdBytes; // silence -Wunused-but-set-variable
1224 #endif
1225 }
1226
1227 if (disableDeferral) {
1228 const XML_Bool success = XML_SetReparseDeferralEnabled(parser, XML_FALSE);
1229 if (! success) {
1230 // This prevents tperror(..) from reporting misleading "[..]: Success"
1231 errno = EINVAL;
1232 tperror(T("Failed to disable reparse deferral"));
1233 exit(XMLWF_EXIT_INTERNAL_ERROR);
1234 }
1235 }
1236
1237 if (requireStandalone)
1238 XML_SetNotStandaloneHandler(parser, notStandalone);
1239 XML_SetParamEntityParsing(parser, paramEntityParsing);
1240 if (outputType == 't') {
1241 /* This is for doing timings; this gives a more realistic estimate of
1242 the parsing time. */
1243 outputDir = 0;
1244 XML_SetElementHandler(parser, nopStartElement, nopEndElement);
1245 XML_SetCharacterDataHandler(parser, nopCharacterData);
1246 XML_SetProcessingInstructionHandler(parser, nopProcessingInstruction);
1247 } else if (outputDir) {
1248 const XML_Char *delim = T("/");
1249 const XML_Char *file = useStdin ? T("STDIN") : argv[i];
1250 if (! useStdin) {
1251 /* Jump after last (back)slash */
1252 const XML_Char *lastDelim = tcsrchr(file, delim[0]);
1253 if (lastDelim)
1254 file = lastDelim + 1;
1255 #if defined(_WIN32)
1256 else {
1257 const XML_Char *winDelim = T("\\");
1258 lastDelim = tcsrchr(file, winDelim[0]);
1259 if (lastDelim) {
1260 file = lastDelim + 1;
1261 delim = winDelim;
1262 }
1263 }
1264 #endif
1265 }
1266 const size_t outputDirLen = tcslen(outputDir);
1267 const size_t fileLen = tcslen(file);
1268
1269 /* Detect and prevent integer overflow in the addition (without
1270 risking underflow) and the multiplication, mirroring the guards
1271 in xcsdup() and resolveSystemId() */
1272 if (outputDirLen > SIZE_MAX - fileLen
1273 || outputDirLen > SIZE_MAX - fileLen - 2) {
1274 tperror(T("Could not allocate memory"));
1275 exit(XMLWF_EXIT_INTERNAL_ERROR);
1276 }
1277
1278 const size_t charsRequired = outputDirLen + fileLen + 2;
1279
1280 if (charsRequired > SIZE_MAX / sizeof(XML_Char)) {
1281 tperror(T("Could not allocate memory"));
1282 exit(XMLWF_EXIT_INTERNAL_ERROR);
1283 }
1284
1285 outName = malloc(charsRequired * sizeof(XML_Char));
1286 if (! outName) {
1287 tperror(T("Could not allocate memory"));
1288 exit(XMLWF_EXIT_INTERNAL_ERROR);
1289 }
1290 tcscpy(outName, outputDir);
1291 tcscat(outName, delim);
1292 tcscat(outName, file);
1293 userData.fp = tfopen(outName, T("wb"));
1294 if (! userData.fp) {
1295 tperror(outName);
1296 exitCode = XMLWF_EXIT_OUTPUT_ERROR;
1297 free(outName);
1298 XML_ParserFree(parser);
1299 if (continueOnError) {
1300 continue;
1301 } else {
1302 break;
1303 }
1304 }
1305 setvbuf(userData.fp, NULL, _IOFBF, 16384);
1306 #ifdef XML_UNICODE
1307 puttc(0xFEFF, userData.fp);
1308 #endif
1309 XML_SetUserData(parser, &userData);
1310 switch (outputType) {
1311 case 'm':
1312 XML_UseParserAsHandlerArg(parser);
1313 XML_SetElementHandler(parser, metaStartElement, metaEndElement);
1314 XML_SetProcessingInstructionHandler(parser, metaProcessingInstruction);
1315 XML_SetCommentHandler(parser, metaComment);
1316 XML_SetCdataSectionHandler(parser, metaStartCdataSection,
1317 metaEndCdataSection);
1318 XML_SetCharacterDataHandler(parser, metaCharacterData);
1319 XML_SetDoctypeDeclHandler(parser, metaStartDoctypeDecl,
1320 metaEndDoctypeDecl);
1321 XML_SetEntityDeclHandler(parser, metaEntityDecl);
1322 XML_SetNotationDeclHandler(parser, metaNotationDecl);
1323 XML_SetNamespaceDeclHandler(parser, metaStartNamespaceDecl,
1324 metaEndNamespaceDecl);
1325 metaStartDocument(parser);
1326 break;
1327 case 'c':
1328 XML_UseParserAsHandlerArg(parser);
1329 XML_SetDefaultHandler(parser, markup);
1330 XML_SetElementHandler(parser, defaultStartElement, defaultEndElement);
1331 XML_SetCharacterDataHandler(parser, defaultCharacterData);
1332 XML_SetProcessingInstructionHandler(parser,
1333 defaultProcessingInstruction);
1334 break;
1335 default:
1336 if (useNamespaces)
1337 XML_SetElementHandler(parser, startElementNS, endElementNS);
1338 else
1339 XML_SetElementHandler(parser, startElement, endElement);
1340 XML_SetCharacterDataHandler(parser, characterData);
1341 #ifndef W3C14N
1342 XML_SetProcessingInstructionHandler(parser, processingInstruction);
1343 if (requiresNotations) {
1344 XML_SetDoctypeDeclHandler(parser, startDoctypeDecl, endDoctypeDecl);
1345 XML_SetNotationDeclHandler(parser, notationDecl);
1346 }
1347 #endif /* not W3C14N */
1348 break;
1349 }
1350 }
1351 if (windowsCodePages)
1352 XML_SetUnknownEncodingHandler(parser, unknownEncoding, 0);
1353 result = XML_ProcessFile(parser, useStdin ? NULL : argv[i], processFlags);
1354 if (outputDir) {
1355 if (outputType == 'm')
1356 metaEndDocument(parser);
1357 fclose(userData.fp);
1358 if (! result) {
1359 tremove(outName);
1360 }
1361 free(outName);
1362 }
1363 XML_ParserFree(parser);
1364 if (! result) {
1365 exitCode = XMLWF_EXIT_NOT_WELLFORMED;
1366 cleanupUserData(&userData);
1367 if (! continueOnError) {
1368 break;
1369 }
1370 }
1371 }
1372 return exitCode;
1373 }
1374