]> mj.ucw.cz Git - libucw.git/blob - sherlock/xml/xml-test.c
XML: Implemented detection and validation of ignorable whitespace
[libucw.git] / sherlock / xml / xml-test.c
1 /*
2  *      Sherlock Library -- A simple XML parser
3  *
4  *      (c) 2007--2008 Pavel Charvat <pchar@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #include "sherlock/sherlock.h"
11 #include "sherlock/xml/xml.h"
12 #include "sherlock/xml/dtd.h"
13 #include "lib/getopt.h"
14 #include "lib/fastbuf.h"
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <fcntl.h>
19
20 enum {
21   WANT_FIRST = 0x100,
22   WANT_HIDE_ERRORS,
23   WANT_IGNORE_COMMENTS,
24   WANT_IGNORE_PIS,
25   WANT_REPORT_BLOCKS,
26   WANT_REPORT_IGNORABLE,
27   WANT_FILE_ENTITIES,
28 };
29
30 static char *shortopts = "spdt" CF_SHORT_OPTS;
31 static struct option longopts[] = {
32   CF_LONG_OPTS
33   { "sax",              0, 0, 's' },
34   { "pull",             0, 0, 'p' },
35   { "dom",              0, 0, 't' },
36   { "dtd",              0, 0, 'd' },
37   { "hide-errors",      0, 0, WANT_HIDE_ERRORS },
38   { "ignore-comments",  0, 0, WANT_IGNORE_COMMENTS },
39   { "ignore-pis",       0, 0, WANT_IGNORE_PIS },
40   { "report-blocks",    0, 0, WANT_REPORT_BLOCKS },
41   { "report-ignorable", 0, 0, WANT_REPORT_IGNORABLE },
42   { "file-entities",    0, 0, WANT_FILE_ENTITIES },
43   { NULL,               0, 0, 0 }
44 };
45
46 static void NONRET
47 usage(void)
48 {
49   fputs("\
50 Usage: xml-test [options] < input.xml\n\
51 \n\
52 Options:\n"
53 CF_USAGE
54 "\
55 -p, --pull              Test PULL interface\n\
56 -s, --sax               Test SAX interface\n\
57 -t, --dom               Test DOM interface\n\
58 -d, --dtd               Enable parsing of DTD\n\
59     --hide-errors       Hide warnings and error messages\n\
60     --ignore-comments   Ignore comments\n\
61     --ignore-pis        Ignore processing instructions\n\
62     --report-blocks     Report blocks or characters and CDATA sections\n\
63     --report-ignorable  Report ignorable whitespace\n\
64     --file-entities     Resolve file external entities (not fully normative)\n\
65 \n", stderr);
66   exit(1);
67 }
68
69 static uns want_sax;
70 static uns want_pull;
71 static uns want_dom;
72 static uns want_parse_dtd;
73 static uns want_hide_errors;
74 static uns want_ignore_comments;
75 static uns want_ignore_pis;
76 static uns want_report_blocks;
77 static uns want_report_ignorable;
78 static uns want_file_entities;
79
80 static struct fastbuf *out;
81
82 static char *
83 node_type(struct xml_node *node)
84 {
85   switch (node->type)
86     {
87       case XML_NODE_ELEM: return "element";
88       case XML_NODE_COMMENT: return "comment";
89       case XML_NODE_PI: return "pi";
90       case XML_NODE_CHARS: return "chars";
91       default: return "unknown";
92     }
93 }
94
95 static void
96 show_node(struct xml_node *node)
97 {
98   switch (node->type)
99     {
100       case XML_NODE_ELEM:
101         bprintf(out, " <%s>", node->name);
102         XML_ATTR_FOR_EACH(a, node)
103           bprintf(out, " %s='%s'", a->name, a->val);
104         bputc(out, '\n');
105         break;
106       case XML_NODE_COMMENT:
107         bprintf(out, " text='%s'\n", node->text);
108         break;
109       case XML_NODE_PI:
110         bprintf(out, " target=%s text='%s'\n", node->name, node->text);
111         break;
112       case XML_NODE_CHARS:
113         bprintf(out, " text='%s'\n", node->text);
114         break;
115       default:
116         bputc(out, '\n');
117     }
118 }
119
120 static void
121 show_tree(struct xml_node *node, uns level)
122 {
123   if (!node)
124     return;
125   bputs(out, "DOM:  ");
126   for (uns i = 0; i < level; i++)
127     bputs(out, "    ");
128   bputs(out, node_type(node));
129   show_node(node);
130   if (node->type == XML_NODE_ELEM)
131     XML_NODE_FOR_EACH(son, node)
132       show_tree(son, level + 1);
133 }
134
135 static void
136 h_error(struct xml_context *ctx)
137 {
138   bprintf(out, "SAX:  %s at %u: %s\n", (ctx->err_code < XML_ERR_ERROR) ? "warn" : "error", xml_row(ctx), ctx->err_msg);
139 }
140
141 static void
142 h_document_start(struct xml_context *ctx UNUSED)
143 {
144   bputs(out, "SAX:  document_start\n");
145 }
146
147 static void
148 h_document_end(struct xml_context *ctx UNUSED)
149 {
150   bputs(out, "SAX:  document_end\n");
151 }
152
153 static void
154 h_xml_decl(struct xml_context *ctx)
155 {
156   bprintf(out, "SAX:  xml_decl version=%s standalone=%d fb_encoding=%s\n", ctx->version_str, ctx->standalone, ctx->src->fb_encoding);
157 }
158
159 static void
160 h_doctype_decl(struct xml_context *ctx)
161 {
162   bprintf(out, "SAX:  doctype_decl type=%s public='%s' system='%s' extsub=%d intsub=%d\n",
163     ctx->doctype, ctx->public_id ? : "", ctx->system_id ? : "",
164     !!(ctx->flags & XML_HAS_EXTERNAL_SUBSET), !!(ctx->flags & XML_HAS_INTERNAL_SUBSET));
165 }
166
167 static void
168 h_comment(struct xml_context *ctx)
169 {
170   bputs(out, "SAX:  comment");
171   show_node(ctx->node);
172 }
173
174 static void
175 h_pi(struct xml_context *ctx)
176 {
177   bputs(out, "SAX:  pi");
178   show_node(ctx->node);
179 }
180
181 static void
182 h_stag(struct xml_context *ctx)
183 {
184   bputs(out, "SAX:  stag");
185   show_node(ctx->node);
186 }
187
188 static void
189 h_etag(struct xml_context *ctx)
190 {
191   bprintf(out, "SAX:  etag </%s>\n", ctx->node->name);
192 }
193
194 static void
195 h_chars(struct xml_context *ctx)
196 {
197   bputs(out, "SAX:  chars");
198   show_node(ctx->node);
199 }
200
201 static void
202 h_block(struct xml_context *ctx UNUSED, char *text, uns len UNUSED)
203 {
204   bprintf(out, "SAX:  block text='%s'\n", text);
205 }
206
207 static void
208 h_cdata(struct xml_context *ctx UNUSED, char *text, uns len UNUSED)
209 {
210   bprintf(out, "SAX:  cdata text='%s'\n", text);
211 }
212
213 static void
214 h_ignorable(struct xml_context *ctx UNUSED, char *text, uns len UNUSED)
215 {
216   bprintf(out, "SAX:  ignorable text='%s'\n", text);
217 }
218
219 static void
220 h_dtd_start(struct xml_context *ctx UNUSED)
221 {
222   bputs(out, "SAX:  dtd_start\n");
223 }
224
225 static void
226 h_dtd_end(struct xml_context *ctx UNUSED)
227 {
228   bputs(out, "SAX:  dtd_end\n");
229 }
230
231 static void
232 h_resolve_entity(struct xml_context *ctx, struct xml_dtd_entity *e)
233 {
234   xml_push_fastbuf(ctx, bopen(e->system_id, O_RDONLY, 4096));
235 }
236
237 int
238 main(int argc, char **argv)
239 {
240   int opt;
241   cf_def_file = NULL;
242   log_init(argv[0]);
243   while ((opt = cf_getopt(argc, argv, shortopts, longopts, NULL)) >= 0)
244     switch (opt)
245       {
246         case 's':
247           want_sax++;
248           break;
249         case 'p':
250           want_pull++;
251           break;
252         case 't':
253           want_dom++;
254           break;
255         case 'd':
256           want_parse_dtd++;
257           break;
258         case WANT_HIDE_ERRORS:
259           want_hide_errors++;
260           break;
261         case WANT_IGNORE_COMMENTS:
262           want_ignore_comments++;
263           break;
264         case WANT_IGNORE_PIS:
265           want_ignore_pis++;
266           break;
267         case WANT_REPORT_BLOCKS:
268           want_report_blocks++;
269           break;
270         case WANT_REPORT_IGNORABLE:
271           want_report_ignorable++;
272           break;
273         case WANT_FILE_ENTITIES:
274           want_file_entities++;
275           break;
276         default:
277           usage();
278       }
279   if (optind != argc)
280     usage();
281
282   out = bfdopen_shared(1, 4096);
283   struct xml_context ctx;
284   xml_init(&ctx);
285   if (!want_hide_errors)
286     ctx.h_warn = ctx.h_error = ctx.h_fatal = h_error;
287   if (want_sax)
288     {
289       ctx.h_document_start = h_document_start;
290       ctx.h_document_end = h_document_end;
291       ctx.h_xml_decl = h_xml_decl;
292       ctx.h_doctype_decl = h_doctype_decl;
293       ctx.h_comment = h_comment;
294       ctx.h_pi = h_pi;
295       ctx.h_stag = h_stag;
296       ctx.h_etag = h_etag;
297       ctx.h_chars = h_chars;
298       if (want_report_blocks)
299         {
300           ctx.h_block = h_block;
301           ctx.h_cdata = h_cdata;
302         }
303       if (want_report_ignorable)
304         ctx.h_ignorable = h_ignorable;
305       ctx.h_dtd_start = h_dtd_start;
306       ctx.h_dtd_end = h_dtd_end;
307     }
308   if (want_dom)
309     ctx.flags |= XML_ALLOC_ALL;
310   if (want_parse_dtd)
311     ctx.flags |= XML_PARSE_DTD;
312   if (want_ignore_comments)
313     ctx.flags &= ~(XML_REPORT_COMMENTS | XML_ALLOC_COMMENTS);
314   if (want_ignore_pis)
315     ctx.flags &= ~(XML_REPORT_PIS | XML_ALLOC_PIS);
316   if (want_file_entities)
317     ctx.h_resolve_entity = h_resolve_entity;
318   xml_push_fastbuf(&ctx, bfdopen_shared(0, 4096));
319   bputs(out, "PULL: start\n");
320   if (want_pull)
321     {
322       ctx.pull = XML_PULL_CHARS | XML_PULL_STAG | XML_PULL_ETAG | XML_PULL_COMMENT | XML_PULL_PI;
323       uns state;
324       while (state = xml_next(&ctx))
325         switch (state)
326           {
327             case XML_STATE_CHARS:
328               bputs(out, "PULL: chars");
329               show_node(ctx.node);
330               break;
331             case XML_STATE_STAG:
332               bputs(out, "PULL: stag");
333               show_node(ctx.node);
334               break;
335             case XML_STATE_ETAG:
336               bprintf(out, "PULL: etag </%s>\n", ctx.node->name);
337               break;
338             case XML_STATE_COMMENT:
339               bputs(out, "PULL: comment");
340               show_node(ctx.node);
341               break;
342             case XML_STATE_PI:
343               bputs(out, "PULL: pi");
344               show_node(ctx.node);
345               break;
346             default:
347               bputs(out, "PULL: unknown\n");
348               break;
349           }
350     }
351   else
352     xml_parse(&ctx);
353   if (ctx.err_code)
354     bprintf(out, "PULL: fatal error at %u: %s\n", xml_row(&ctx), ctx.err_msg);
355   else
356     {
357       bputs(out, "PULL: eof\n");
358       if (want_dom)
359         show_tree(ctx.dom, 0);
360     }
361
362   xml_cleanup(&ctx);
363   bclose(out);
364   return 0;
365 }