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 (at) users.sourceforge.net> 11 Copyright (c) 2001-2003 Fred L. Drake, Jr. <fdrake (at) users.sourceforge.net> 12 Copyright (c) 2004-2009 Karl Waclawek <karl (at) waclawek.net> 13 Copyright (c) 2005-2007 Steven Solie <steven (at) solie.ca> 14 Copyright (c) 2016-2026 Sebastian Pipping <sebastian (at) pipping.org> 15 Copyright (c) 2017 Rhodri James <rhodri (at) wildebeest.org.uk> 16 Copyright (c) 2019 David Loffredo <loffredo (at) steptools.com> 17 Copyright (c) 2020 Joe Orton <jorton (at) redhat.com> 18 Copyright (c) 2020 Kleber Tarcsio <klebertarcisio (at) yahoo.com.br> 19 Copyright (c) 2021 Tim Bray <tbray (at) textuality.com> 20 Copyright (c) 2022 Martin Ettl <ettl.martin78 (at) googlemail.com> 21 Copyright (c) 2022 Sean McBride <sean (at) rogue-research.com> 22 Copyright (c) 2025 Alfonso Gregory <gfunni234 (at) gmail.com> 23 Copyright (c) 2026 Matthew Fernandez <matthew.fernandez (at) gmail.com> 24 Copyright (c) 2026 Nick Begg <nick (at) stunttruck.net> 25 Copyright (c) 2026 Kartik Kenchi <netliomax25 (at) 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 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 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 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 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 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 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 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 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 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 * 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 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 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 356 cleanupUserData(XmlwfUserData *userData) { 357 free((void *)userData->currentDoctypeName); 358 userData->currentDoctypeName = NULL; 359 freeNotations(userData); 360 } 361 362 static int 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 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 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 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 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 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 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 532 defaultEndElement(void *userData, const XML_Char *name) { 533 UNUSED_P(name); 534 XML_DefaultCurrent(userData); 535 } 536 537 static void XMLCALL 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 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 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 560 nopEndElement(void *userData, const XML_Char *name) { 561 UNUSED_P(userData); 562 UNUSED_P(name); 563 } 564 565 static void XMLCALL 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 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 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 599 metaStartDocument(void *userData) { 600 fputts(T("<document>\n"), ((XmlwfUserData *)XML_GetUserData(userData))->fp); 601 } 602 603 static void 604 metaEndDocument(void *userData) { 605 fputts(T("</document>\n"), ((XmlwfUserData *)XML_GetUserData(userData))->fp); 606 } 607 608 static void XMLCALL 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 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 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 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 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 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 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 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 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 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 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 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 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 827 unknownEncodingConvert(void *data, const char *p) { 828 return codepageConvert(*(int *)data, p); 829 } 830 831 static int XMLCALL 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 868 notStandalone(void *userData) { 869 UNUSED_P(userData); 870 return 0; 871 } 872 873 static void 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 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, and make the last error determine the exit code.\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 one or more output files could not be written\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 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 XML_Parser parser; 1198 if (useNamespaces) 1199 parser = XML_ParserCreateNS(encoding, NSSEP); 1200 else 1201 parser = XML_ParserCreate(encoding); 1202 1203 if (! parser) { 1204 tperror(T("Could not instantiate parser")); 1205 exit(XMLWF_EXIT_INTERNAL_ERROR); 1206 } 1207 1208 if (attackMaximumAmplification != -1.0f) { 1209 #if XML_GE == 1 1210 XML_SetBillionLaughsAttackProtectionMaximumAmplification( 1211 parser, attackMaximumAmplification); 1212 XML_SetAllocTrackerMaximumAmplification(parser, 1213 attackMaximumAmplification); 1214 #endif 1215 } 1216 if (attackThresholdGiven) { 1217 #if XML_GE == 1 1218 XML_SetBillionLaughsAttackProtectionActivationThreshold( 1219 parser, attackThresholdBytes); 1220 XML_SetAllocTrackerActivationThreshold(parser, attackThresholdBytes); 1221 #else 1222 (void)attackThresholdBytes; // silence -Wunused-but-set-variable 1223 #endif 1224 } 1225 1226 if (disableDeferral) { 1227 const XML_Bool success = XML_SetReparseDeferralEnabled(parser, XML_FALSE); 1228 if (! success) { 1229 // This prevents tperror(..) from reporting misleading "[..]: Success" 1230 errno = EINVAL; 1231 tperror(T("Failed to disable reparse deferral")); 1232 exit(XMLWF_EXIT_INTERNAL_ERROR); 1233 } 1234 } 1235 1236 if (requireStandalone) 1237 XML_SetNotStandaloneHandler(parser, notStandalone); 1238 XML_SetParamEntityParsing(parser, paramEntityParsing); 1239 if (outputType == 't') { 1240 /* This is for doing timings; this gives a more realistic estimate of 1241 the parsing time. */ 1242 outputDir = 0; 1243 XML_SetElementHandler(parser, nopStartElement, nopEndElement); 1244 XML_SetCharacterDataHandler(parser, nopCharacterData); 1245 XML_SetProcessingInstructionHandler(parser, nopProcessingInstruction); 1246 } else if (outputDir) { 1247 const XML_Char *delim = T("/"); 1248 const XML_Char *file = useStdin ? T("STDIN") : argv[i]; 1249 if (! useStdin) { 1250 /* Jump after last (back)slash */ 1251 const XML_Char *lastDelim = tcsrchr(file, delim[0]); 1252 if (lastDelim) 1253 file = lastDelim + 1; 1254 #if defined(_WIN32) 1255 else { 1256 const XML_Char *winDelim = T("\\"); 1257 lastDelim = tcsrchr(file, winDelim[0]); 1258 if (lastDelim) { 1259 file = lastDelim + 1; 1260 delim = winDelim; 1261 } 1262 } 1263 #endif 1264 } 1265 const size_t outputDirLen = tcslen(outputDir); 1266 const size_t fileLen = tcslen(file); 1267 1268 /* Detect and prevent integer overflow in the addition (without 1269 risking underflow) and the multiplication, mirroring the guards 1270 in xcsdup() and resolveSystemId() */ 1271 if (outputDirLen > SIZE_MAX - fileLen 1272 || outputDirLen > SIZE_MAX - fileLen - 2) { 1273 tperror(T("Could not allocate memory")); 1274 exit(XMLWF_EXIT_INTERNAL_ERROR); 1275 } 1276 1277 const size_t charsRequired = outputDirLen + fileLen + 2; 1278 1279 if (charsRequired > SIZE_MAX / sizeof(XML_Char)) { 1280 tperror(T("Could not allocate memory")); 1281 exit(XMLWF_EXIT_INTERNAL_ERROR); 1282 } 1283 1284 outName = malloc(charsRequired * sizeof(XML_Char)); 1285 if (! outName) { 1286 tperror(T("Could not allocate memory")); 1287 exit(XMLWF_EXIT_INTERNAL_ERROR); 1288 } 1289 tcscpy(outName, outputDir); 1290 tcscat(outName, delim); 1291 tcscat(outName, file); 1292 userData.fp = tfopen(outName, T("wb")); 1293 if (! userData.fp) { 1294 tperror(outName); 1295 exitCode = XMLWF_EXIT_OUTPUT_ERROR; 1296 free(outName); 1297 XML_ParserFree(parser); 1298 if (continueOnError) { 1299 continue; 1300 } else { 1301 break; 1302 } 1303 } 1304 setvbuf(userData.fp, NULL, _IOFBF, 16384); 1305 #ifdef XML_UNICODE 1306 puttc(0xFEFF, userData.fp); 1307 #endif 1308 XML_SetUserData(parser, &userData); 1309 switch (outputType) { 1310 case 'm': 1311 XML_UseParserAsHandlerArg(parser); 1312 XML_SetElementHandler(parser, metaStartElement, metaEndElement); 1313 XML_SetProcessingInstructionHandler(parser, metaProcessingInstruction); 1314 XML_SetCommentHandler(parser, metaComment); 1315 XML_SetCdataSectionHandler(parser, metaStartCdataSection, 1316 metaEndCdataSection); 1317 XML_SetCharacterDataHandler(parser, metaCharacterData); 1318 XML_SetDoctypeDeclHandler(parser, metaStartDoctypeDecl, 1319 metaEndDoctypeDecl); 1320 XML_SetEntityDeclHandler(parser, metaEntityDecl); 1321 XML_SetNotationDeclHandler(parser, metaNotationDecl); 1322 XML_SetNamespaceDeclHandler(parser, metaStartNamespaceDecl, 1323 metaEndNamespaceDecl); 1324 metaStartDocument(parser); 1325 break; 1326 case 'c': 1327 XML_UseParserAsHandlerArg(parser); 1328 XML_SetDefaultHandler(parser, markup); 1329 XML_SetElementHandler(parser, defaultStartElement, defaultEndElement); 1330 XML_SetCharacterDataHandler(parser, defaultCharacterData); 1331 XML_SetProcessingInstructionHandler(parser, 1332 defaultProcessingInstruction); 1333 break; 1334 default: 1335 if (useNamespaces) 1336 XML_SetElementHandler(parser, startElementNS, endElementNS); 1337 else 1338 XML_SetElementHandler(parser, startElement, endElement); 1339 XML_SetCharacterDataHandler(parser, characterData); 1340 #ifndef W3C14N 1341 XML_SetProcessingInstructionHandler(parser, processingInstruction); 1342 if (requiresNotations) { 1343 XML_SetDoctypeDeclHandler(parser, startDoctypeDecl, endDoctypeDecl); 1344 XML_SetNotationDeclHandler(parser, notationDecl); 1345 } 1346 #endif /* not W3C14N */ 1347 break; 1348 } 1349 } 1350 if (windowsCodePages) 1351 XML_SetUnknownEncodingHandler(parser, unknownEncoding, 0); 1352 const bool processingSuccess 1353 = XML_ProcessFile(parser, useStdin ? NULL : argv[i], processFlags); 1354 bool closingSuccess = true; 1355 if (outputDir) { 1356 if (outputType == 'm') 1357 metaEndDocument(parser); 1358 closingSuccess = (fclose(userData.fp) == 0); 1359 if (! closingSuccess) 1360 tperror(outName); 1361 if (! processingSuccess || ! closingSuccess) { 1362 tremove(outName); 1363 } 1364 free(outName); 1365 } 1366 XML_ParserFree(parser); 1367 if (! processingSuccess || ! closingSuccess) { 1368 // NOTE: If both failed, the last error should determine the exit code. 1369 // Failure to close happened after failure to process. 1370 if (! closingSuccess) 1371 exitCode = XMLWF_EXIT_OUTPUT_ERROR; 1372 else if (! processingSuccess) 1373 exitCode = XMLWF_EXIT_NOT_WELLFORMED; 1374 1375 cleanupUserData(&userData); 1376 if (! continueOnError) { 1377 break; 1378 } 1379 } 1380 } 1381 return exitCode; 1382 } 1383