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