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