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-2024 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 <stdbool.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <expat.h> 48 49 #ifdef XML_LARGE_SIZE 50 # define XML_FMT_INT_MOD "ll" 51 #else 52 # define XML_FMT_INT_MOD "l" 53 #endif 54 55 #ifdef XML_UNICODE_WCHAR_T 56 # define XML_FMT_STR "ls" 57 #else 58 # define XML_FMT_STR "s" 59 #endif 60 61 // While traversing the XML_Content tree, we avoid recursion 62 // to not be vulnerable to a denial of service attack. 63 typedef struct StackStruct { 64 const XML_Content *model; 65 unsigned level; 66 struct StackStruct *prev; 67 } Stack; 68 69 static Stack * 70 stackPushMalloc(Stack *stackTop, const XML_Content *model, unsigned level) { 71 Stack *const newStackTop = malloc(sizeof(Stack)); 72 if (! newStackTop) { 73 return NULL; 74 } 75 newStackTop->model = model; 76 newStackTop->level = level; 77 newStackTop->prev = stackTop; 78 return newStackTop; 79 } 80 81 static Stack * 82 stackPopFree(Stack *stackTop) { 83 Stack *const newStackTop = stackTop->prev; 84 free(stackTop); 85 return newStackTop; 86 } 87 88 static const char * 89 contentTypeName(enum XML_Content_Type contentType) { 90 switch (contentType) { 91 case XML_CTYPE_EMPTY: 92 return "EMPTY"; 93 case XML_CTYPE_ANY: 94 return "ANY"; 95 case XML_CTYPE_MIXED: 96 return "MIXED"; 97 case XML_CTYPE_NAME: 98 return "NAME"; 99 case XML_CTYPE_CHOICE: 100 return "CHOICE"; 101 case XML_CTYPE_SEQ: 102 return "SEQ"; 103 default: 104 return "???"; 105 } 106 } 107 108 static const char * 109 contentQuantName(enum XML_Content_Quant contentQuant) { 110 switch (contentQuant) { 111 case XML_CQUANT_NONE: 112 return "NONE"; 113 case XML_CQUANT_OPT: 114 return "OPT"; 115 case XML_CQUANT_REP: 116 return "REP"; 117 case XML_CQUANT_PLUS: 118 return "PLUS"; 119 default: 120 return "???"; 121 } 122 } 123 124 static void 125 dumpContentModelElement(const XML_Content *model, unsigned level, 126 const XML_Content *root) { 127 // Indent 128 unsigned u = 0; 129 for (; u < level; u++) { 130 printf(" "); 131 } 132 133 // Node 134 printf("[%u] type=%s(%u), quant=%s(%u)", (unsigned)(model - root), 135 contentTypeName(model->type), (unsigned int)model->type, 136 contentQuantName(model->quant), (unsigned int)model->quant); 137 if (model->name) { 138 printf(", name=\"%" XML_FMT_STR "\"", model->name); 139 } else { 140 printf(", name=NULL"); 141 } 142 printf(", numchildren=%u", model->numchildren); 143 printf("\n"); 144 } 145 146 static bool 147 dumpContentModel(const XML_Char *name, const XML_Content *root) { 148 printf("Element \"%" XML_FMT_STR "\":\n", name); 149 Stack *stackTop = stackPushMalloc(NULL, root, 1); 150 if (! stackTop) { 151 return false; 152 } 153 154 while (stackTop) { 155 const XML_Content *const model = stackTop->model; 156 const unsigned level = stackTop->level; 157 158 dumpContentModelElement(model, level, root); 159 160 stackTop = stackPopFree(stackTop); 161 162 for (size_t u = model->numchildren; u >= 1; u--) { 163 Stack *const newStackTop 164 = stackPushMalloc(stackTop, model->children + (u - 1), level + 1); 165 if (! newStackTop) { 166 // We ran out of memory, so let's free all memory allocated 167 // earlier in this function, to be leak-clean: 168 while (stackTop != NULL) { 169 stackTop = stackPopFree(stackTop); 170 } 171 return false; 172 } 173 stackTop = newStackTop; 174 } 175 } 176 177 printf("\n"); 178 return true; 179 } 180 181 static void XMLCALL 182 handleElementDeclaration(void *userData, const XML_Char *name, 183 XML_Content *model) { 184 XML_Parser parser = (XML_Parser)userData; 185 const bool success = dumpContentModel(name, model); 186 XML_FreeContentModel(parser, model); 187 if (! success) { 188 XML_StopParser(parser, /* resumable= */ XML_FALSE); 189 } 190 } 191 192 int 193 main(void) { 194 XML_Parser parser = XML_ParserCreate(NULL); 195 int done; 196 197 if (! parser) { 198 fprintf(stderr, "Couldn't allocate memory for parser\n"); 199 return 1; 200 } 201 202 XML_SetUserData(parser, parser); 203 XML_SetElementDeclHandler(parser, handleElementDeclaration); 204 205 do { 206 void *const buf = XML_GetBuffer(parser, BUFSIZ); 207 if (! buf) { 208 fprintf(stderr, "Couldn't allocate memory for buffer\n"); 209 XML_ParserFree(parser); 210 return 1; 211 } 212 213 const size_t len = fread(buf, 1, BUFSIZ, stdin); 214 215 if (ferror(stdin)) { 216 fprintf(stderr, "Read error\n"); 217 XML_ParserFree(parser); 218 return 1; 219 } 220 221 done = feof(stdin); 222 223 if (XML_ParseBuffer(parser, (int)len, done) == XML_STATUS_ERROR) { 224 enum XML_Error errorCode = XML_GetErrorCode(parser); 225 if (errorCode == XML_ERROR_ABORTED) { 226 errorCode = XML_ERROR_NO_MEMORY; 227 } 228 fprintf(stderr, 229 "Parse error at line %" XML_FMT_INT_MOD "u:\n%" XML_FMT_STR "\n", 230 XML_GetCurrentLineNumber(parser), XML_ErrorString(errorCode)); 231 XML_ParserFree(parser); 232 return 1; 233 } 234 } while (! done); 235 236 XML_ParserFree(parser); 237 return 0; 238 } 239