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 *
stackPushMalloc(Stack * stackTop,const XML_Content * model,unsigned level)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 *
stackPopFree(Stack * stackTop)82 stackPopFree(Stack *stackTop) {
83 Stack *const newStackTop = stackTop->prev;
84 free(stackTop);
85 return newStackTop;
86 }
87
88 static const char *
contentTypeName(enum XML_Content_Type contentType)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 *
contentQuantName(enum XML_Content_Quant contentQuant)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
dumpContentModelElement(const XML_Content * model,unsigned level,const XML_Content * root)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
dumpContentModel(const XML_Char * name,const XML_Content * root)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
handleElementDeclaration(void * userData,const XML_Char * name,XML_Content * model)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
main(void)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