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