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