1 /* Read an XML document from standard input and print 2 element declarations (if any) to standard output. 3 It must be used with Expat compiled for UTF-8 output. 4 __ __ _ 5 ___\ \/ /_ __ __ _| |_ 6 / _ \\ /| '_ \ / _` | __| 7 | __// \| |_) | (_| | |_ 8 \___/_/\_\ .__/ \__,_|\__| 9 |_| XML parser 10 11 Copyright (c) 1997-2000 Thai Open Source Software Center Ltd 12 Copyright (c) 2001-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net> 13 Copyright (c) 2004-2006 Karl Waclawek <karl@waclawek.net> 14 Copyright (c) 2005-2007 Steven Solie <steven@solie.ca> 15 Copyright (c) 2016-2026 Sebastian Pipping <sebastian@pipping.org> 16 Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk> 17 Copyright (c) 2019 Zhongyuan Zhou <zhouzhongyuan@huawei.com> 18 Copyright (c) 2024 Hanno Böck <hanno@gentoo.org> 19 Copyright (c) 2026 Matthew Fernandez <matthew.fernandez@gmail.com> 20 Licensed under the MIT license: 21 22 Permission is hereby granted, free of charge, to any person obtaining 23 a copy of this software and associated documentation files (the 24 "Software"), to deal in the Software without restriction, including 25 without limitation the rights to use, copy, modify, merge, publish, 26 distribute, sublicense, and/or sell copies of the Software, and to permit 27 persons to whom the Software is furnished to do so, subject to the 28 following conditions: 29 30 The above copyright notice and this permission notice shall be included 31 in all copies or substantial portions of the Software. 32 33 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 34 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 35 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 36 NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 37 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 38 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 39 USE OR OTHER DEALINGS IN THE SOFTWARE. 40 41 SPDX-License-Identifier: MIT 42 */ 43 44 #include <inttypes.h> // PRIu64 45 #include <stdbool.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <expat.h> 49 50 #ifdef XML_UNICODE_WCHAR_T 51 # define XML_FMT_STR "ls" 52 #else 53 # define XML_FMT_STR "s" 54 #endif 55 56 // While traversing the XML_Content tree, we avoid recursion 57 // to not be vulnerable to a denial of service attack. 58 typedef struct StackStruct { 59 const XML_Content *model; 60 unsigned level; 61 struct StackStruct *prev; 62 } Stack; 63 64 static Stack * 65 stackPushMalloc(Stack *stackTop, const XML_Content *model, unsigned level) { 66 Stack *const newStackTop = malloc(sizeof(Stack)); 67 if (! newStackTop) { 68 return NULL; 69 } 70 newStackTop->model = model; 71 newStackTop->level = level; 72 newStackTop->prev = stackTop; 73 return newStackTop; 74 } 75 76 static Stack * 77 stackPopFree(Stack *stackTop) { 78 Stack *const newStackTop = stackTop->prev; 79 free(stackTop); 80 return newStackTop; 81 } 82 83 static const char * 84 contentTypeName(enum XML_Content_Type contentType) { 85 switch (contentType) { 86 case XML_CTYPE_EMPTY: 87 return "EMPTY"; 88 case XML_CTYPE_ANY: 89 return "ANY"; 90 case XML_CTYPE_MIXED: 91 return "MIXED"; 92 case XML_CTYPE_NAME: 93 return "NAME"; 94 case XML_CTYPE_CHOICE: 95 return "CHOICE"; 96 case XML_CTYPE_SEQ: 97 return "SEQ"; 98 default: 99 return "???"; 100 } 101 } 102 103 static const char * 104 contentQuantName(enum XML_Content_Quant contentQuant) { 105 switch (contentQuant) { 106 case XML_CQUANT_NONE: 107 return "NONE"; 108 case XML_CQUANT_OPT: 109 return "OPT"; 110 case XML_CQUANT_REP: 111 return "REP"; 112 case XML_CQUANT_PLUS: 113 return "PLUS"; 114 default: 115 return "???"; 116 } 117 } 118 119 static void 120 dumpContentModelElement(const XML_Content *model, unsigned level, 121 const XML_Content *root) { 122 // Indent 123 unsigned u = 0; 124 for (; u < level; u++) { 125 printf(" "); 126 } 127 128 // Node 129 printf("[%u] type=%s(%u), quant=%s(%u)", (unsigned)(model - root), 130 contentTypeName(model->type), (unsigned int)model->type, 131 contentQuantName(model->quant), (unsigned int)model->quant); 132 if (model->name) { 133 printf(", name=\"%" XML_FMT_STR "\"", model->name); 134 } else { 135 printf(", name=NULL"); 136 } 137 printf(", numchildren=%u", model->numchildren); 138 printf("\n"); 139 } 140 141 static bool 142 dumpContentModel(const XML_Char *name, const XML_Content *root) { 143 printf("Element \"%" XML_FMT_STR "\":\n", name); 144 Stack *stackTop = stackPushMalloc(NULL, root, 1); 145 if (! stackTop) { 146 return false; 147 } 148 149 while (stackTop) { 150 const XML_Content *const model = stackTop->model; 151 const unsigned level = stackTop->level; 152 153 dumpContentModelElement(model, level, root); 154 155 stackTop = stackPopFree(stackTop); 156 157 for (size_t u = model->numchildren; u >= 1; u--) { 158 Stack *const newStackTop 159 = stackPushMalloc(stackTop, model->children + (u - 1), level + 1); 160 if (! newStackTop) { 161 // We ran out of memory, so let's free all memory allocated 162 // earlier in this function, to be leak-clean: 163 while (stackTop != NULL) { 164 stackTop = stackPopFree(stackTop); 165 } 166 return false; 167 } 168 stackTop = newStackTop; 169 } 170 } 171 172 printf("\n"); 173 return true; 174 } 175 176 static void XMLCALL 177 handleElementDeclaration(void *userData, const XML_Char *name, 178 XML_Content *model) { 179 XML_Parser parser = (XML_Parser)userData; 180 const bool success = dumpContentModel(name, model); 181 XML_FreeContentModel(parser, model); 182 if (! success) { 183 XML_StopParser(parser, /* resumable= */ XML_FALSE); 184 } 185 } 186 187 int 188 main(void) { 189 XML_Parser parser = XML_ParserCreate(NULL); 190 int done; 191 192 if (! parser) { 193 fprintf(stderr, "Couldn't allocate memory for parser\n"); 194 return 1; 195 } 196 197 XML_SetUserData(parser, parser); 198 XML_SetElementDeclHandler(parser, handleElementDeclaration); 199 200 do { 201 void *const buf = XML_GetBuffer(parser, BUFSIZ); 202 if (! buf) { 203 fprintf(stderr, "Couldn't allocate memory for buffer\n"); 204 XML_ParserFree(parser); 205 return 1; 206 } 207 208 const size_t len = fread(buf, 1, BUFSIZ, stdin); 209 210 if (ferror(stdin)) { 211 fprintf(stderr, "Read error\n"); 212 XML_ParserFree(parser); 213 return 1; 214 } 215 216 done = feof(stdin); 217 218 if (XML_ParseBuffer(parser, (int)len, done) == XML_STATUS_ERROR) { 219 enum XML_Error errorCode = XML_GetErrorCode(parser); 220 if (errorCode == XML_ERROR_ABORTED) { 221 errorCode = XML_ERROR_NO_MEMORY; 222 } 223 fprintf(stderr, "Parse error at line %" PRIu64 ":\n%" XML_FMT_STR "\n", 224 XML_GetCurrentLineNumber64(parser), XML_ErrorString(errorCode)); 225 XML_ParserFree(parser); 226 return 1; 227 } 228 } while (! done); 229 230 XML_ParserFree(parser); 231 return 0; 232 } 233