xref: /freebsd/contrib/expat/xmlwf/xmlwf.c (revision 207d96dabfec14d7b3699747abb539ab3c1118ab)
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("&amp;"), fp);
110       break;
111     case T('<'):
112       fputts(T("&lt;"), fp);
113       break;
114     case T('>'):
115       fputts(T("&gt;"), fp);
116       break;
117 #ifdef W3C14N
118     case 13:
119       fputts(T("&#xD;"), fp);
120       break;
121 #else
122     case T('"'):
123       fputts(T("&quot;"), 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("&amp;"), fp);
151       break;
152     case T('<'):
153       fputts(T("&lt;"), fp);
154       break;
155     case T('"'):
156       fputts(T("&quot;"), fp);
157       break;
158 #ifdef W3C14N
159     case 9:
160       fputts(T("&#x9;"), fp);
161       break;
162     case 10:
163       fputts(T("&#xA;"), fp);
164       break;
165     case 13:
166       fputts(T("&#xD;"), fp);
167       break;
168 #else
169     case T('>'):
170       fputts(T("&gt;"), 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