blob: e681e0c22cd1dbbf9a5dc2a2576fb26a70036227 [file] [log] [blame]
Daniel Veillardd7306b02004-01-05 23:11:54 +00001/**
2 * rngparser.c: parser for the Relax-NG compact syntax.
3 *
Daniel Veillard03c2f0a2004-01-25 19:54:59 +00004 * Based on:
5 * RELAX NG Compact Syntax
6 * Committee Specification 21 November 2002
7 * http://www.oasis-open.org/committees/relax-ng/compact-20021121.html
8 *
Daniel Veillardd7306b02004-01-05 23:11:54 +00009 * See Copyright for the status of this software.
10 *
11 * Daniel Veillard <veillard@redhat.com>
12 */
13
14#include <string.h>
15
16#include <libxml/parser.h>
17#include <libxml/parserInternals.h>
18#include <libxml/relaxng.h>
19#include <libxml/dict.h>
20
Daniel Veillardf8e3db02012-09-11 13:26:36 +080021#define TODO \
Daniel Veillardd7306b02004-01-05 23:11:54 +000022 xmlGenericError(xmlGenericErrorContext, \
23 "Unimplemented block at %s:%d\n", \
24 __FILE__, __LINE__);
25
26#define MAX_TOKEN 10
27
28typedef enum {
29 CRNG_NONE = 0,
30 CRNG_OP = 1,
31 CRNG_KEYWORD,
32 CRNG_IDENTIFIER,
33 CRNG_LITERAL_SEGMENT,
34 CRNG_CNAME,
35 CRNG_QNAME,
36 CRNG_NSNAME,
37 CRNG_DOCUMENTATION
38} xmlCRNGTokType;
39
40typedef enum {
41 CRNG_OKAY = 0,
42 CRNG_MEMORY_ERROR,
43 CRNG_INVALID_CHAR_ERROR,
44 CRNG_END_ERROR,
45 CRNG_ENCODING_ERROR
46} xmlCRNGError;
47
48typedef enum {
49 XML_CRNG_ERROR = -1,
50 XML_CRNG_OK = 0,
51 XML_CRNG_EOF = 1
52} xmlCRelaxNGParserState;
53
54typedef struct _token _token;
55typedef _token *tokenPtr;
56struct _token {
57 xmlCRNGTokType toktype;
58 int toklen;
59 const xmlChar *token;
60 const xmlChar *prefix;
61};
62
63typedef struct _xmlCRelaxNGParserCtxt xmlCRelaxNGParserCtxt;
64typedef xmlCRelaxNGParserCtxt *xmlCRelaxNGParserCtxtPtr;
65struct _xmlCRelaxNGParserCtxt {
66 void *userData; /* user specific data block */
67 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
68 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
69 xmlRelaxNGValidErr err;
70
71 const xmlChar *compact;
72 const xmlChar *end;
73 const xmlChar *cur;
74 int isElem;
75 int lineno;
76 const xmlChar *linestart;
77 const char *filename;
78
79 int nbTokens;
80 int firstToken;
81 _token tokens[MAX_TOKEN];
82 int totalToken;
83
84 xmlCRelaxNGParserState state;
85
86 int nbErrors;
87
88 xmlDocPtr res; /* the result */
89 xmlNodePtr ins; /* the current insertion node */
90
91 xmlNsPtr nsDef;
92 tokenPtr token;
93
94 xmlHashTablePtr namespaces;
95 xmlHashTablePtr datatypes;
96
97 /*
98 * dictionnary and keywords
99 */
100 xmlDictPtr dict;
101 const xmlChar *key_attribute;
102 const xmlChar *key_default;
103 const xmlChar *key_datatypes;
104 const xmlChar *key_div;
105 const xmlChar *key_element;
106 const xmlChar *key_empty;
107 const xmlChar *key_external;
108 const xmlChar *key_grammar;
109 const xmlChar *key_include;
110 const xmlChar *key_inherit;
111 const xmlChar *key_list;
112 const xmlChar *key_mixed;
113 const xmlChar *key_namespace;
114 const xmlChar *key_notAllowed;
115 const xmlChar *key_parent;
116 const xmlChar *key_start;
117 const xmlChar *key_string;
118 const xmlChar *key_text;
119 const xmlChar *key_token;
120 const xmlChar *key_equal;
121 const xmlChar *key_orequal;
122 const xmlChar *key_andequal;
123 const xmlChar *key_combine;
124 const xmlChar *key_or;
125 const xmlChar *key_comma;
126 const xmlChar *key_and;
127 const xmlChar *key_choice;
128 const xmlChar *key_group;
129 const xmlChar *key_interleave;
130 const xmlChar *key_ref;
131 const xmlChar *key_define;
132
133 /* results */
134 xmlDocPtr doc; /* the resulting doc */
135 xmlNodePtr insert; /* the insertion point */
136 xmlAttrPtr attrs; /* pending attributes */
137};
138
139static const xmlChar *xmlCRelaxNGInherit = BAD_CAST "Inherit string";
140static const xmlChar *xmlCRelaxNGDefault = BAD_CAST "Default string";
141
142#define CUR_CHAR(l) xmlXPathCurrentChar(ctxt, &l)
143/**
144 * IS_BLANK:
145 * @c: an UNICODE value (int)
146 *
147 * Macro to check the following production in the XML spec:
148 *
149 * [3] S ::= (#x20 | #x9 | #xD | #xA)+
150 */
151#ifndef IS_BLANK
152#define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \
153 ((c) == 0x0D))
154#endif
155#define IS_SEPARATOR(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \
156 ((c) == 0x0D) || (c == '#'))
157
158#define CRNG_ERROR0(X) \
159 { xmlCRNGErr(ctxt, X, NULL); return(0); }
160#define CRNG_ERROR(X) \
161 { xmlCRNGErr(ctxt, X, NULL); }
162
163#define CRNG_MEM_ERROR0() \
164 { xmlCRNGErr(ctxt, CRNG_MEMORY_ERROR, NULL); return(0); }
165#define CRNG_MEM_ERROR() \
166 { xmlCRNGErr(ctxt, CRNG_MEMORY_ERROR, NULL); }
167
168#define ERROR(str) xmlCRNGErr(ctxt, 0, str);
169
170static void
171xmlCRNGErr(xmlCRelaxNGParserCtxtPtr ctxt, int err_no, const char *err_msg) {
172 const xmlChar *cur;
173 xmlChar buffer[150];
174 int i, l;
175
176 if (ctxt != NULL) {
177 if (ctxt->filename != NULL)
178 fprintf(stderr, "%s:%d ", ctxt->filename, ctxt->lineno);
179 }
180 if (err_msg != NULL) {
181 fprintf(stderr, "error: %s\n", err_msg);
182 } else if (err_no != 0)
183 fprintf(stderr, "error %d\n", err_no);
184 cur = ctxt->cur;
185 while ((*cur != '\n') && (*cur != '\r') && (ctxt->cur - cur < 80)) cur--;
186 l = ctxt->cur - cur;
187 cur++;
188 for (i = 0; i < 100;i++) {
189 if ((*cur == '\n') || (*cur == '\r')) break;
190 buffer[i] = *cur++;
191 }
192 buffer[i] = 0;
193 fprintf(stderr, "%s\n", buffer);
194 for (i = 0; i < l;i++) buffer[i] = ' ';
195 buffer[i++] = '^';
196 buffer[i++] = 0;
197 fprintf(stderr, "%s\n", buffer);
198}
199
200/**
201 * IS_OP
202 * @c: an UNICODE value (int)
203 *
204 * Macro to check for operator value
205 */
206#ifndef IS_OP
207#define IS_OP(c) (((c) == ',') || ((c) == '&') || ((c) == '|') || \
208 ((c) == '?') || ((c) == '-') || ((c) == '*') || \
209 ((c) == '{') || ((c) == '}') || ((c) == '(') || \
210 ((c) == ')') || ((c) == '+') || ((c) == '=') || \
211 ((c) == ':'))
212#endif
213
214static int
215xmlCRNGIsKeyword(xmlCRelaxNGParserCtxtPtr ctxt, const xmlChar *str) {
216 if ((str == ctxt->key_attribute) ||
217 (str == ctxt->key_default) ||
218 (str == ctxt->key_datatypes) ||
219 (str == ctxt->key_div) ||
220 (str == ctxt->key_element) ||
221 (str == ctxt->key_empty) ||
222 (str == ctxt->key_external) ||
223 (str == ctxt->key_grammar) ||
224 (str == ctxt->key_include) ||
225 (str == ctxt->key_inherit) ||
226 (str == ctxt->key_list) ||
227 (str == ctxt->key_mixed) ||
228 (str == ctxt->key_namespace) ||
229 (str == ctxt->key_notAllowed) ||
230 (str == ctxt->key_parent) ||
231 (str == ctxt->key_start) ||
232 (str == ctxt->key_string) ||
233 (str == ctxt->key_text) ||
234 (str == ctxt->key_token))
235 return(1);
236 return(0);
237
238}
239
240/*
241 * xmlCRNGNextToken:
242 * ctxt: a compact RNG parser context
243 *
244 * Scan the schema to get the next token
245 *
246 * Return 0 if success and -1 in case of error
247 */
248
249static int
250xmlCRNGNextToken(xmlCRelaxNGParserCtxtPtr ctxt) {
251 const xmlChar *cur;
252 tokenPtr token;
253
254 if (ctxt == NULL) return(-1);
255 if (ctxt->nbTokens >= MAX_TOKEN) return(-1);
256 token = &(ctxt->tokens[(ctxt->firstToken + ctxt->nbTokens) % MAX_TOKEN]);
257 token->toktype = CRNG_NONE;
258
259 if (ctxt->cur == NULL) {
260 ctxt->cur = ctxt->compact;
261 }
262retry:
263 if (ctxt->cur >= ctxt->end) {
264 ctxt->state = XML_CRNG_EOF;
265 return(-1);
266 }
267 while ((ctxt->cur < ctxt->end) &&
268 (IS_BLANK(*ctxt->cur))) ctxt->cur++;
269 if (ctxt->cur >= ctxt->end) {
270 ctxt->state = XML_CRNG_EOF;
271 return(-1);
272 }
273 if (*ctxt->cur == '#') {
274 cur = ctxt->cur;
275 cur++;
276 while ((cur < ctxt->end) && (*cur != '\n') && (*cur != '\r'))
277 cur++;
278 ctxt->cur = cur;
279 goto retry;
280 } else if (*ctxt->cur == '"') {
281 /* string, check for '"""' */
282 ctxt->cur++;
283 if (ctxt->cur >= ctxt->end) goto eof;
284 cur = ctxt->cur;
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800285 if ((ctxt->end - ctxt->end > 2) &&
Daniel Veillardd7306b02004-01-05 23:11:54 +0000286 (*cur == '"') && (cur[1] == '"')) {
287 TODO
288 } else {
289 while ((cur < ctxt->end) && (*cur != '"')) cur++;
290 if (cur >= ctxt->end) goto eof;
291 token->toklen = cur - ctxt->cur;
292 token->token = xmlDictLookup(ctxt->dict, ctxt->cur, token->toklen);
293 token->toktype = CRNG_LITERAL_SEGMENT;
294 token->prefix = NULL;
295 cur++;
296 ctxt->cur = cur;
297 }
298 } else if (*ctxt->cur == '\'') {
299 /* string, check for "'''" */
300 TODO
301 } else if ((IS_OP(*ctxt->cur)) || (*ctxt->cur == ':')) {
302 cur = ctxt->cur;
303 cur++;
304 if ((cur < ctxt->end) &&
305 (((*cur == '=') &&
306 ((*ctxt->cur == '|') || (*ctxt->cur == '&'))) ||
307 ((*cur == '*') && (*ctxt->cur == ':')))) {
308 token->toklen = 2;
309 } else {
310 token->toklen = 1;
311 }
312 token->token = xmlDictLookup(ctxt->dict, ctxt->cur, token->toklen);
313 token->toktype = CRNG_OP;
314 token->prefix = NULL;
315 ctxt->cur += token->toklen;
316 } else {
317 int escape = 0;
318
319 cur = ctxt->cur;
320 if (*cur == '\\') {
321 escape = 1;
322 cur++;
323 ctxt->cur++;
324 }
325 while ((cur < ctxt->end) &&
326 (!(IS_SEPARATOR(*cur))) && (!(IS_OP(*cur)))) cur++;
327
328 token->toklen = cur - ctxt->cur;
329 token->token = xmlDictLookup(ctxt->dict, ctxt->cur, token->toklen);
330 token->prefix = NULL;
331 ctxt->cur = cur;
332 if ((escape == 0) && (xmlCRNGIsKeyword(ctxt, token->token)))
333 token->toktype = CRNG_KEYWORD;
334 else {
335 token->toktype = CRNG_IDENTIFIER;
336 }
337 if (*ctxt->cur == ':') {
338 ctxt->cur++;
339 if (*ctxt->cur == '*') {
340 ctxt->cur++;
341 token->toktype = CRNG_NSNAME;
342 } else {
343 cur = ctxt->cur;
344 while ((cur < ctxt->end) &&
345 (!(IS_SEPARATOR(*cur))) && (!(IS_OP(*cur)))) cur++;
346 token->prefix = token->token;
347 token->toklen = cur - ctxt->cur;
348 token->token = xmlDictLookup(ctxt->dict, ctxt->cur,
349 token->toklen);
350 ctxt->cur = cur;
351 if (xmlValidateNCName(token->token, 0) == 0)
352 token->toktype = CRNG_QNAME;
353 else {
354 TODO /* sounds like an error ! */
355 token->toktype = CRNG_IDENTIFIER;
356 }
357 }
358 }
359 }
360 ctxt->nbTokens++;
361 return(0);
362eof:
363 ctxt->state = XML_CRNG_EOF;
364 CRNG_ERROR(CRNG_END_ERROR);
365 return(-1);
366}
367
368/**
369 * xmlParseCRNGGetToken:
370 * @ctxt: a compact RNG parser context
371 * @no: the number of the token from 1 for the first one
372 * and 2, 3 ... for read-ahead
373 *
374 * Token reading interface
375 *
376 * returns a pointer to the new token, or NULL in case of error or EOF
377 */
378static tokenPtr
379xmlParseCRNGGetToken(xmlCRelaxNGParserCtxtPtr ctxt, int no) {
380 tokenPtr ret;
381 int res;
382
383 if ((no <= 0) || (no >= MAX_TOKEN)) return(NULL);
384 no--;
385 while (ctxt->nbTokens <= no) {
386 res = xmlCRNGNextToken(ctxt);
387 if (res < 0)
388 return(NULL);
389 }
390 ret = &(ctxt->tokens[(ctxt->firstToken + no) % MAX_TOKEN]);
391 return(ret);
392}
393
394/**
395 * xmlParseCRNGDropTokens:
396 * @ctxt: a compact RNG parser context
397 * @nr: the number of token marked as read
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800398 *
Daniel Veillardd7306b02004-01-05 23:11:54 +0000399 * mark a number of token as read and consumed.
400 *
401 * Returns -1 in case of error and 0 otherwise
402 */
403static int
404xmlParseCRNGDropTokens(xmlCRelaxNGParserCtxtPtr ctxt, int nr) {
405 if ((nr <= 0) || (nr >= MAX_TOKEN)) return(-1);
406 while ((ctxt->nbTokens >0) && (nr > 0)) {
407 ctxt->firstToken++;
408 nr--;
409 ctxt->nbTokens--;
410 ctxt->totalToken++;
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800411 if (ctxt->totalToken == 384)
Daniel Veillardd7306b02004-01-05 23:11:54 +0000412 fprintf(stderr, "found\n");
413 }
414 ctxt->firstToken = ctxt->firstToken % MAX_TOKEN;
415 return(0);
416}
417
418static void
419xmlParseCRNGTokenize(xmlCRelaxNGParserCtxtPtr ctxt) {
420 tokenPtr token;
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800421
Daniel Veillardd7306b02004-01-05 23:11:54 +0000422 token = xmlParseCRNGGetToken(ctxt, 1);
423 while (token != NULL) {
424 switch (token->toktype) {
425 case CRNG_NONE: printf("none"); break;
426 case CRNG_OP: printf("op"); break;
427 case CRNG_KEYWORD: printf("keyword"); break;
428 case CRNG_IDENTIFIER: printf("identifier"); break;
429 case CRNG_LITERAL_SEGMENT: printf("literal"); break;
430 case CRNG_CNAME: printf("cname"); break;
431 case CRNG_QNAME: printf("qname"); break;
432 case CRNG_NSNAME: printf("nsname"); break;
433 case CRNG_DOCUMENTATION: printf("doc"); break;
434 }
435 printf(":%s\n", token->token);
436 xmlParseCRNGDropTokens(ctxt, 1);
437 token = xmlParseCRNGGetToken(ctxt, 1);
438 }
439}
440
441/**
442 * xmlParseCRNG_attribute:
443 * @ctxt: a compact RNG parser context
444 * @name: the attribute name
445 * @ns: the attribute namespace
446 * @value: the attribute value
447 *
448 * implements attribute of the RELAX NG Compact Syntax Appendix A
449 *
450 * Returns 0 in case of success and -1 in case of error
451 */
452static int
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800453xmlParseCRNG_attribute(xmlCRelaxNGParserCtxtPtr ctxt,
Daniel Veillardd7306b02004-01-05 23:11:54 +0000454 const xmlChar *name,
455 xmlNsPtr ns,
456 const xmlChar *value)
457{
458 xmlAttrPtr attr;
459
460 attr = xmlNewNsPropEatName(NULL, ns, (xmlChar *) name, value);
461 if (attr == NULL) CRNG_MEM_ERROR0();
462 attr->next = ctxt->attrs;
463 if (ctxt->attrs != NULL)
464 ctxt->attrs->prev = attr;
465 ctxt->attrs = attr;
466 return(0);
467}
468
469/**
470 * xmlParseCRNG_bindPrefix:
471 * @ctxt: a compact RNG parser context
472 * @prefix: the namespace prefix or NULL
473 * @namespace: the namespace name
474 *
475 * implements bindPrefix of the RELAX NG Compact Syntax Appendix A
476 *
477 * Returns 0 in case of success and -1 in case of error
478 */
479static int
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800480xmlParseCRNG_bindPrefix(xmlCRelaxNGParserCtxtPtr ctxt,
Daniel Veillardd7306b02004-01-05 23:11:54 +0000481 const xmlChar *prefix,
482 const xmlChar *namespace)
483{
484 int ret;
485
486 if ((prefix != NULL) && (xmlStrEqual(prefix, BAD_CAST "xml")) &&
487 (!xmlStrEqual(namespace, XML_XML_NAMESPACE))) {
488 ERROR("The \"xml\" prefix must be bound to \"http://www.w3.org/XML/1998/namespace\"");
489 return(-1);
490 } else if ((xmlStrEqual(namespace, XML_XML_NAMESPACE)) &&
491 (!xmlStrEqual(prefix, BAD_CAST "xml"))) {
492 ERROR("The \"http://www.w3.org/XML/1998/namespace\" name must be bound to \"xml\" prefix");
493 return(-1);
494 }
495 if (ctxt->namespaces == NULL)
496 ctxt->namespaces = xmlHashCreate(10);
497 if (ctxt->namespaces == NULL) {
498 ERROR("Failed to create namespace hash table");
499 return(-1);
500 }
501 if (prefix == NULL)
502 ret = xmlHashAddEntry(ctxt->namespaces, xmlCRelaxNGDefault,
503 (void *) namespace);
504 else
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800505 ret = xmlHashAddEntry(ctxt->namespaces, prefix,
Daniel Veillardd7306b02004-01-05 23:11:54 +0000506 (void *) namespace);
507 if (ret < 0) {
508 if (prefix == NULL) {
509 ERROR("Redefinition of default namespace");
510 } else {
511 ERROR("Redefinition of namespace");
512 }
513 return(-1);
514 }
515
516 return(0);
517}
518
519/**
520 * xmlParseCRNG_bindDatatypePrefix:
521 * @ctxt: a compact RNG parser context
522 * @prefix: the datatype prefix
523 * @namespace: the datatype identifier
524 *
525 * implements bindDatatypePrefix of the RELAX NG Compact Syntax Appendix A
526 *
527 * Returns 0 in case of success and -1 in case of error
528 */
529static int
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800530xmlParseCRNG_bindDatatypePrefix(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
Daniel Veillardd7306b02004-01-05 23:11:54 +0000531 const xmlChar *prefix,
532 const xmlChar *namespace)
533{
534 int ret;
535
536 if ((prefix != NULL) && (xmlStrEqual(prefix, BAD_CAST "xsd")) &&
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800537 (!xmlStrEqual(namespace,
Daniel Veillardd7306b02004-01-05 23:11:54 +0000538 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes"))) {
539 ERROR("The \"xsd\" prefix must be bound to \"http://www.w3.org/2001/XMLSchema-datatypes\"");
540 return(-1);
541 }
542 if (ctxt->datatypes == NULL)
543 ctxt->datatypes = xmlHashCreate(10);
544 if (ctxt->datatypes == NULL) {
545 ERROR("Failed to create namespace hash table");
546 return(-1);
547 }
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800548 ret = xmlHashAddEntry(ctxt->datatypes, prefix,
Daniel Veillardd7306b02004-01-05 23:11:54 +0000549 (void *) namespace);
550 if (ret < 0) {
551 ERROR("Redefinition of datatype");
552 return(-1);
553 }
554 return(0);
555}
556
557/**
558 * xmlParseCRNG_lookupPrefix:
559 * @ctxt: a compact RNG parser context
560 * @prefix: the namespace prefix or NULL
561 *
562 * implements lookupPrefix of the RELAX NG Compact Syntax Appendix A
563 *
564 * Returns the prefix in case of success or NULL in case of error
565 */
566static const xmlChar *
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800567xmlParseCRNG_lookupPrefix(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
Daniel Veillardd7306b02004-01-05 23:11:54 +0000568 const xmlChar *prefix)
569{
570 const xmlChar *ret;
571
572 if (prefix == NULL)
573 ret = xmlHashLookup(ctxt->namespaces, xmlCRelaxNGDefault);
574 else
575 ret = xmlHashLookup(ctxt->namespaces, prefix);
576 return(ret);
577}
578
579/**
580 * xmlParseCRNG_lookupDatatypePrefix:
581 * @ctxt: a compact RNG parser context
582 * @prefix: the namespace prefix or NULL
583 *
584 * implements lookupDatatypePrefix of the RELAX NG Compact Syntax Appendix A
585 *
586 * Returns the prefix in case of success or NULL in case of error
587 */
588static const xmlChar *
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800589xmlParseCRNG_lookupDatatypePrefix(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
Daniel Veillardd7306b02004-01-05 23:11:54 +0000590 const xmlChar *prefix)
591{
592 const xmlChar *ret;
593 ret = xmlHashLookup(ctxt->datatypes, prefix);
594 return(ret);
595}
596
597/**
598 * xmlParseCRNG_datatypeAttributes:
599 * @ctxt: a compact RNG parser context
600 * @prefix: the namespace prefix or NULL
601 *
602 * implements lookupPrefix of the RELAX NG Compact Syntax Appendix A
603 *
604 * Returns the prefix in case of success or NULL in case of error
605 */
606static xmlAttrPtr
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800607xmlParseCRNG_datatypeAttributes(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
Daniel Veillardd7306b02004-01-05 23:11:54 +0000608 const xmlChar *library, const xmlChar *type)
609{
610 xmlAttrPtr lib, typ;
611
612 lib = xmlNewNsProp(NULL, NULL, BAD_CAST "datatypeLibrary", library);
613 if (lib == NULL) {
614 CRNG_MEM_ERROR();
615 return(NULL);
616 }
617 typ = xmlNewNsProp(NULL, NULL, BAD_CAST "type", type);
618 if (typ == NULL) {
619 CRNG_MEM_ERROR();
620 return(lib);
621 }
622 lib->next = typ;
623
624 return(lib);
625}
626
627/**
628 * xmlParseCRNG_XXX:
629 * @ctxt: a compact RNG parser context
630 *
631 * Parse XXX of the RELAX NG Compact Syntax Appendix A
632 *
633 * Returns 0 in case of success and -1 in case of error
634 */
635static int
636xmlParseCRNG_XXX(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
637{
638 return(0);
639}
640
641static int xmlParseCRNG_pattern(xmlCRelaxNGParserCtxtPtr ctxt);
642static int xmlParseCRNG_nameClass(xmlCRelaxNGParserCtxtPtr ctxt);
643
644/**
645 * xmlParseCRNG_params:
646 * @ctxt: a compact RNG parser context
647 *
648 * Parse params of the RELAX NG Compact Syntax Appendix A
649 *
650 * Returns 0 in case of success and -1 in case of error
651 */
652static int
653xmlParseCRNG_params(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
654{
655 TODO
656 return(0);
657}
658
659/**
660 * xmlParseCRNG_exceptNameClass:
661 * @ctxt: a compact RNG parser context
662 *
663 * Parse exceptNameClass of the RELAX NG Compact Syntax Appendix A
664 *
665 * Returns 0 in case of success and -1 in case of error
666 */
667static int
668xmlParseCRNG_exceptNameClass(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
669{
670 tokenPtr token;
671 xmlNodePtr insert = ctxt->insert, cur;
672
673 token = xmlParseCRNGGetToken(ctxt, 1);
674 if ((token->toktype == CRNG_OP) &&
675 (token->token[0] == '-') && (token->token[1] == 0)) {
676 xmlParseCRNGDropTokens(ctxt, 1);
677 cur = xmlNewNode(NULL, BAD_CAST "except");
678 if (cur == NULL) CRNG_MEM_ERROR0();
679 if (ctxt->insert != NULL)
680 xmlAddChild(ctxt->insert, cur);
681 ctxt->insert = cur;
682 xmlParseCRNG_nameClass(ctxt);
683 }
684 ctxt->insert = insert;
685 return(0);
686}
687
688/**
689 * xmlParseCRNG_innerNameClass:
690 * @ctxt: a compact RNG parser context
691 *
692 * Parse innerNameClass of the RELAX NG Compact Syntax Appendix A
693 *
694 * Returns 0 in case of success and -1 in case of error
695 */
696static int
697xmlParseCRNG_innerNameClass(xmlCRelaxNGParserCtxtPtr ctxt)
698{
699 tokenPtr token;
700 xmlNodePtr cur;
701
702 token = xmlParseCRNGGetToken(ctxt, 1);
703 if (token->toktype == CRNG_OP) {
704 if ((token->token[0] == '(') && (token->token[1] == 0)) {
705 xmlParseCRNGDropTokens(ctxt, 1);
706 xmlParseCRNG_nameClass(ctxt);
707 token = xmlParseCRNGGetToken(ctxt, 1);
708 if ((token->toktype != CRNG_OP) ||
709 (token->token[0] != ')') || (token->token[1] != 0)) {
710 ERROR("Expecting \")\" here");
711 }
712 xmlParseCRNGDropTokens(ctxt, 1);
713 } else if ((token->token[0] == '*') && (token->token[1] == 0)) {
714 xmlParseCRNGDropTokens(ctxt, 1);
715 cur = xmlNewNode(NULL, BAD_CAST "anyName");
716 if (cur == NULL) CRNG_MEM_ERROR0();
717 if (ctxt->insert != NULL)
718 xmlAddChild(ctxt->insert, cur);
719 ctxt->insert = cur;
720 xmlParseCRNG_exceptNameClass(ctxt);
721 } else {
722 TODO
723 }
724 } else if ((token->toktype == CRNG_IDENTIFIER) ||
725 (token->toktype == CRNG_KEYWORD)) {
726 cur = xmlNewNode(NULL, BAD_CAST "name");
727 if (cur == NULL) CRNG_MEM_ERROR0();
728 if (ctxt->isElem) {
729 xmlSetProp(cur, BAD_CAST "ns",
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800730 xmlParseCRNG_lookupPrefix(ctxt, NULL));
Daniel Veillardd7306b02004-01-05 23:11:54 +0000731 } else {
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800732 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
Daniel Veillardd7306b02004-01-05 23:11:54 +0000733 }
734 xmlNodeAddContent(cur, token->token);
735 if (ctxt->insert != NULL)
736 xmlAddChild(ctxt->insert, cur);
737 ctxt->insert = cur;
738 xmlParseCRNGDropTokens(ctxt, 1);
739 } else if (token->toktype == CRNG_CNAME) {
740 TODO
741 } else if (token->toktype == CRNG_NSNAME) {
742 cur = xmlNewNode(NULL, BAD_CAST "nsName");
743 if (cur == NULL) CRNG_MEM_ERROR0();
744 xmlSetProp(cur, BAD_CAST "ns",
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800745 xmlParseCRNG_lookupPrefix(ctxt, token->token));
Daniel Veillardd7306b02004-01-05 23:11:54 +0000746 if (ctxt->insert != NULL)
747 xmlAddChild(ctxt->insert, cur);
748 ctxt->insert = cur;
749 xmlParseCRNGDropTokens(ctxt, 1);
750 xmlParseCRNG_exceptNameClass(ctxt);
751 } else {
752 TODO /* probably an error */
753 }
754
755 return(0);
756}
757
758/**
759 * xmlParseCRNG_nameClass:
760 * @ctxt: a compact RNG parser context
761 *
762 * Parse nameClass of the RELAX NG Compact Syntax Appendix A
763 *
764 * Returns 0 in case of success and -1 in case of error
765 */
766static int
767xmlParseCRNG_nameClass(xmlCRelaxNGParserCtxtPtr ctxt)
768{
769 tokenPtr token;
770 xmlNodePtr insert = ctxt->insert, last, choice;
771
772 ctxt->insert = NULL;
773 xmlParseCRNG_innerNameClass(ctxt);
774 last = ctxt->insert;
775 token = xmlParseCRNGGetToken(ctxt, 1);
776 while ((token->toktype == CRNG_OP) &&
777 (token->token[0] == '|') && (token->token[1] == 0)) {
778 choice = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_choice);
779 xmlParseCRNGDropTokens(ctxt, 1);
780 if (choice == NULL) CRNG_MEM_ERROR0();
781 ctxt->insert = NULL;
782 xmlParseCRNG_innerNameClass(ctxt);
783 xmlAddChild(choice, last);
784 xmlAddChild(choice, ctxt->insert);
785 last = choice;
786 token = xmlParseCRNGGetToken(ctxt, 1);
787 }
788 xmlAddChild(insert, last);
789
790 ctxt->insert = insert;
791 return(0);
792}
793
794/**
795 * xmlParseCRNG_patternBlock:
796 * @ctxt: a compact RNG parser context
797 *
798 * Parse a pattern block of the RELAX NG Compact Syntax Appendix A
799 *
800 * Returns 0 in case of success and -1 in case of error
801 */
802static int
803xmlParseCRNG_patternBlock(xmlCRelaxNGParserCtxtPtr ctxt)
804{
805 tokenPtr token;
806
807 token = xmlParseCRNGGetToken(ctxt, 1);
808 if ((token->toktype != CRNG_OP) ||
809 (token->token[0] != '{') || (token->token[1] != 0)) {
810 ERROR("Expecting \"{\" here");
811 }
812 xmlParseCRNGDropTokens(ctxt, 1);
813 xmlParseCRNG_pattern(ctxt);
814 token = xmlParseCRNGGetToken(ctxt, 1);
815 if ((token->toktype != CRNG_OP) ||
816 (token->token[0] != '}') || (token->token[1] != 0)) {
817 ERROR("Expecting \"}\" here");
818 }
819 xmlParseCRNGDropTokens(ctxt, 1);
820 return(0);
821}
822
823/**
824 * xmlParseCRNG_datatype:
825 * @ctxt: a compact RNG parser context
826 *
827 * Parse datatype of the RELAX NG Compact Syntax Appendix A
828 *
829 * Returns 0 in case of success and -1 in case of error
830 */
831static int
832xmlParseCRNG_datatype(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
833{
834 tokenPtr token;
835 xmlAttrPtr attrs = NULL;
836
837 token = xmlParseCRNGGetToken(ctxt, 1);
838 if (token->toktype == CRNG_KEYWORD) {
839 if (token->token == ctxt->key_string) {
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800840 attrs = xmlParseCRNG_datatypeAttributes(ctxt, BAD_CAST "",
Daniel Veillardd7306b02004-01-05 23:11:54 +0000841 token->token);
842 xmlParseCRNGDropTokens(ctxt, 1);
843 } else if (token->token == ctxt->key_token) {
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800844 attrs = xmlParseCRNG_datatypeAttributes(ctxt, BAD_CAST "",
Daniel Veillardd7306b02004-01-05 23:11:54 +0000845 token->token);
846 xmlParseCRNGDropTokens(ctxt, 1);
847 } else {
848 TODO /* probably an error */
849 }
850 } else if (token->toktype == CRNG_LITERAL_SEGMENT) {
851 ctxt->insert = xmlNewNode(NULL, BAD_CAST "value");
852 xmlParseCRNGDropTokens(ctxt, 1);
853 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
854 xmlNodeAddContent(ctxt->insert, token->token);
855 } else if (token->toktype == CRNG_QNAME) {
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800856 attrs = xmlParseCRNG_datatypeAttributes(ctxt,
Daniel Veillardd7306b02004-01-05 23:11:54 +0000857 xmlParseCRNG_lookupDatatypePrefix(ctxt, token->prefix),
858 token->token);
859 } else {
860 TODO
861 }
862 if (attrs != NULL) {
863 token = xmlParseCRNGGetToken(ctxt, 1);
864 if (token->toktype == CRNG_LITERAL_SEGMENT) {
865 ctxt->insert = xmlNewNode(NULL, BAD_CAST "value");
866 xmlParseCRNGDropTokens(ctxt, 1);
867 if (ctxt->insert == NULL) {
868 xmlFreePropList(attrs);
869 CRNG_MEM_ERROR0();
870 }
871 ctxt->insert->properties = attrs;
872 xmlNodeAddContent(ctxt->insert, token->token);
873 } else if ((token->toktype == CRNG_OP) &&
874 (token->token[0] == '{') && (token->token[0] == 0)) {
875 ctxt->insert = xmlNewNode(NULL, BAD_CAST "data");
876 xmlParseCRNGDropTokens(ctxt, 1);
877 if (ctxt->insert == NULL) {
878 xmlFreePropList(attrs);
879 CRNG_MEM_ERROR0();
880 }
881 ctxt->insert->properties = attrs;
882 xmlParseCRNG_params(ctxt);
883 } else {
884 ctxt->insert = xmlNewNode(NULL, BAD_CAST "data");
885 xmlParseCRNGDropTokens(ctxt, 1);
886 if (ctxt->insert == NULL) {
887 xmlFreePropList(attrs);
888 CRNG_MEM_ERROR0();
889 }
890 ctxt->insert->properties = attrs;
891 xmlNodeAddContent(ctxt->insert, token->token);
892 }
893 }
894 return(0);
895}
896
897/**
898 * xmlParseCRNG_primary:
899 * @ctxt: a compact RNG parser context
900 *
901 * Parse primary of the RELAX NG Compact Syntax Appendix A
902 *
903 * Returns 0 in case of success and -1 in case of error
904 */
905static int
906xmlParseCRNG_primary(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
907{
908 tokenPtr token;
909
910 token = xmlParseCRNGGetToken(ctxt, 1);
911 if (token == NULL)
912 return(0);
913 if (token->toktype == CRNG_KEYWORD) {
914 if (token->token == ctxt->key_element) {
915 ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
916 xmlParseCRNGDropTokens(ctxt, 1);
917 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
918 ctxt->isElem = 1;
919 xmlParseCRNG_nameClass(ctxt);
920 xmlParseCRNG_patternBlock(ctxt);
921 } else if (token->token == ctxt->key_attribute) {
922 ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
923 xmlParseCRNGDropTokens(ctxt, 1);
924 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
925 ctxt->isElem = 0;
926 xmlParseCRNG_nameClass(ctxt);
927 xmlParseCRNG_patternBlock(ctxt);
928 } else if (token->token == ctxt->key_mixed) {
929 ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
930 xmlParseCRNGDropTokens(ctxt, 1);
931 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
932 xmlParseCRNG_patternBlock(ctxt);
933 } else if (token->token == ctxt->key_list) {
934 ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
935 xmlParseCRNGDropTokens(ctxt, 1);
936 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
937 xmlParseCRNG_patternBlock(ctxt);
938 } else if (token->token == ctxt->key_empty) {
939 ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
940 xmlParseCRNGDropTokens(ctxt, 1);
941 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
942 } else if (token->token == ctxt->key_notAllowed) {
943 ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
944 xmlParseCRNGDropTokens(ctxt, 1);
945 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
946 } else if (token->token == ctxt->key_text) {
947 ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
948 xmlParseCRNGDropTokens(ctxt, 1);
949 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
950 } else if (token->token == ctxt->key_parent) {
951 ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
952 xmlParseCRNGDropTokens(ctxt, 1);
953 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
954 TODO
955 } else if (token->token == ctxt->key_grammar) {
956 ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
957 xmlParseCRNGDropTokens(ctxt, 1);
958 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
959 TODO
960 } else if (token->token == ctxt->key_external) {
961 ctxt->insert = xmlNewNode(NULL, BAD_CAST "externalRef");
962 xmlParseCRNGDropTokens(ctxt, 1);
963 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
964 TODO
965 } else {
966 TODO
967 }
968 } else if (token->toktype == CRNG_IDENTIFIER) {
969 ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_ref);
970 if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
971 xmlSetProp(ctxt->insert, BAD_CAST "name", token->token);
972 xmlParseCRNGDropTokens(ctxt, 1);
973 } else if (token->toktype == CRNG_QNAME) {
974 xmlParseCRNG_datatype(ctxt);
975 } else if (token->toktype == CRNG_LITERAL_SEGMENT) {
976 xmlParseCRNG_datatype(ctxt);
977 } else if ((token->toktype == CRNG_OP) &&
978 (token->token[0] == '(') && (token->token[1] == 0)) {
979 xmlParseCRNGDropTokens(ctxt, 1);
980 xmlParseCRNG_pattern(ctxt);
981 token = xmlParseCRNGGetToken(ctxt, 1);
982 if ((token->toktype != CRNG_OP) ||
983 (token->token[0] != ')') || (token->token[1] != 0)) {
984 ERROR("Expecting \")\" here");
985 }
986 xmlParseCRNGDropTokens(ctxt, 1);
987 }
988 return(0);
989}
990
991/**
992 * xmlParseCRNG_particle:
993 * @ctxt: a compact RNG parser context
994 *
995 * Parse particle of the RELAX NG Compact Syntax Appendix A
996 *
997 * Returns 0 in case of success and -1 in case of error
998 */
999static int
1000xmlParseCRNG_particle(xmlCRelaxNGParserCtxtPtr ctxt)
1001{
1002 tokenPtr token;
1003 xmlNodePtr insert = ctxt->insert, res, tmp = NULL;
1004
1005 ctxt->insert = NULL;
1006 xmlParseCRNG_primary(ctxt);
1007 res = ctxt->insert;
1008 token = xmlParseCRNGGetToken(ctxt, 1);
1009 if ((token != NULL) && (token->toktype == CRNG_OP)) {
1010 if ((token->token[0] == '*') && (token->token[1] == 0)) {
1011 tmp = xmlNewNode(NULL, BAD_CAST "zeroOrMore");
1012 if (tmp == NULL) CRNG_MEM_ERROR0();
1013 } else if ((token->token[0] == '+') && (token->token[1] == 0)) {
1014 tmp = xmlNewNode(NULL, BAD_CAST "oneOrMore");
1015 if (tmp == NULL) CRNG_MEM_ERROR0();
1016 } else if ((token->token[0] == '?') && (token->token[1] == 0)) {
1017 tmp = xmlNewNode(NULL, BAD_CAST "optional");
1018 if (tmp == NULL) CRNG_MEM_ERROR0();
1019 }
1020 if (tmp != NULL) {
1021 xmlAddChild(tmp, res);
1022 res = tmp;
1023 xmlParseCRNGDropTokens(ctxt, 1);
1024 }
1025 }
1026 if (insert != NULL) {
1027 xmlAddChild(insert, res);
1028 ctxt->insert = insert;
1029 } else
1030 ctxt->insert = res;
1031 return(0);
1032}
1033
1034/**
1035 * xmlParseCRNG_pattern:
1036 * @ctxt: a compact RNG parser context
1037 *
1038 * Parse pattern of the RELAX NG Compact Syntax Appendix A
1039 *
1040 * Returns 0 in case of success and -1 in case of error
1041 */
1042static int
1043xmlParseCRNG_pattern(xmlCRelaxNGParserCtxtPtr ctxt)
1044{
1045 tokenPtr token;
1046 xmlNodePtr insert = ctxt->insert, prev, grp;
1047
1048 ctxt->insert = NULL;
1049 xmlParseCRNG_particle(ctxt);
1050 prev = ctxt->insert;
1051 token = xmlParseCRNGGetToken(ctxt, 1);
1052 while ((prev != NULL) && (token != NULL) && (token->toktype == CRNG_OP)) {
1053 if (token->token == ctxt->key_or) {
1054 grp = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_choice);
1055 if (grp == NULL) CRNG_MEM_ERROR0();
1056 } else if (token->token == ctxt->key_and) {
1057 grp = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_interleave);
1058 if (grp == NULL) CRNG_MEM_ERROR0();
1059 } else if (token->token == ctxt->key_comma) {
1060 grp = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_group);
1061 if (grp == NULL) CRNG_MEM_ERROR0();
1062 } else
1063 break;
1064 xmlParseCRNGDropTokens(ctxt, 1);
1065 ctxt->insert = NULL;
1066 xmlParseCRNG_particle(ctxt);
1067 xmlAddChild(grp, prev);
1068 xmlAddChild(grp, ctxt->insert);
1069 prev = grp;
1070 token = xmlParseCRNGGetToken(ctxt, 1);
1071 }
1072 if (insert != NULL) {
1073 xmlAddChild(insert, prev);
1074 ctxt->insert = insert;
1075 } else {
1076 ctxt->insert = prev;
1077 }
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001078
Daniel Veillardd7306b02004-01-05 23:11:54 +00001079 return(0);
1080}
1081
1082/**
1083 * xmlParseCRNG_component:
1084 * @ctxt: a compact RNG parser context
1085 *
1086 * Parse component of the RELAX NG Compact Syntax Appendix A
1087 *
1088 * Returns 0 in case of success and -1 in case of error
1089 */
1090static int
1091xmlParseCRNG_component(xmlCRelaxNGParserCtxtPtr ctxt)
1092{
1093 tokenPtr token, tok2;
1094 xmlNodePtr insert = ctxt->insert;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001095
Daniel Veillardd7306b02004-01-05 23:11:54 +00001096 token = xmlParseCRNGGetToken(ctxt, 1);
1097 if (token == NULL)
1098 return(0);
1099 if (token->toktype == CRNG_KEYWORD) {
1100 if (token->token == ctxt->key_start) {
1101 xmlNodePtr start;
1102
1103 start = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_start);
1104 if (start == NULL) CRNG_MEM_ERROR0();
1105 if (ctxt->insert != NULL)
1106 xmlAddChild(ctxt->insert, start);
1107 ctxt->insert = start;
1108 xmlParseCRNGDropTokens(ctxt, 1);
1109 token = xmlParseCRNGGetToken(ctxt, 1);
1110
1111 if ((token->toktype == CRNG_OP) &&
1112 (token->token == ctxt->key_equal)) {
1113 } else if ((token->toktype == CRNG_OP) &&
1114 (token->token == ctxt->key_orequal)) {
1115 xmlParseCRNG_attribute(ctxt, ctxt->key_combine, NULL,
1116 BAD_CAST "choice");
1117 } else if ((token->toktype == CRNG_OP) &&
1118 (token->token == ctxt->key_andequal)) {
1119 xmlParseCRNG_attribute(ctxt, ctxt->key_combine, NULL,
1120 BAD_CAST "interleave");
1121 } else {
1122 ERROR("expecting \"=\" or \"&=\" or \"|=\" here")
1123 return(-1);
1124 }
1125 start->properties = ctxt->attrs;
1126 ctxt->attrs = NULL;
1127 xmlParseCRNGDropTokens(ctxt, 1);
1128 xmlParseCRNG_pattern(ctxt);
1129
1130 } else if (token->token == ctxt->key_include) {
1131 TODO
1132 } else if (token->token == ctxt->key_div) {
1133 TODO
1134 } else {
1135 return(-1);
1136 }
1137 } else if (token->toktype == CRNG_IDENTIFIER) {
1138 xmlNodePtr define;
1139 const xmlChar *identifier;
1140
1141 identifier = token->token;
1142 tok2 = xmlParseCRNGGetToken(ctxt, 2);
1143 if ((tok2->toktype == CRNG_OP) &&
1144 (tok2->token == ctxt->key_equal)) {
1145 } else if ((tok2->toktype == CRNG_OP) &&
1146 (tok2->token == ctxt->key_orequal)) {
1147 xmlParseCRNG_attribute(ctxt, ctxt->key_combine, NULL,
1148 BAD_CAST "choice");
1149 } else if ((tok2->toktype == CRNG_OP) &&
1150 (tok2->token == ctxt->key_andequal)) {
1151 xmlParseCRNG_attribute(ctxt, ctxt->key_combine, NULL,
1152 BAD_CAST "interleave");
1153 } else {
1154 ERROR("expecting \"=\" or \"&=\" or \"|=\" here")
1155 return(-1);
1156 }
1157 xmlParseCRNGDropTokens(ctxt, 2);
1158
1159 define = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_define);
1160 if (define == NULL) CRNG_MEM_ERROR0();
1161 define->properties = ctxt->attrs;
1162 ctxt->attrs = NULL;
1163 xmlSetProp(define, BAD_CAST "name", identifier);
1164 if (ctxt->insert != NULL)
1165 xmlAddChild(ctxt->insert, define);
1166 ctxt->insert = define;
1167 xmlParseCRNG_pattern(ctxt);
1168 } else {
1169 return(-1);
1170 }
1171 ctxt->insert = insert;
1172 return(0);
1173}
1174
1175/**
1176 * xmlParseCRNG_grammar:
1177 * @ctxt: a compact RNG parser context
1178 *
1179 * Parse grammar of the RELAX NG Compact Syntax Appendix A
1180 *
1181 * Returns 0 in case of success and -1 in case of error
1182 */
1183static int
1184xmlParseCRNG_grammar(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
1185{
1186 tokenPtr token;
1187 int ret;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001188
Daniel Veillardd7306b02004-01-05 23:11:54 +00001189 token = xmlParseCRNGGetToken(ctxt, 1);
1190 while (token != NULL) {
1191 ret = xmlParseCRNG_component(ctxt);
1192 if (ret != 0)
1193 break;
1194 token = xmlParseCRNGGetToken(ctxt, 1);
1195 }
1196 return(0);
1197}
1198
1199/**
1200 * xmlParseCRNG_topLevelBody:
1201 * @ctxt: a compact RNG parser context
1202 *
1203 * Parse topLevelBody of the RELAX NG Compact Syntax Appendix A
1204 *
1205 * Returns 0 in case of success and -1 in case of error
1206 */
1207static int
1208xmlParseCRNG_topLevelBody(xmlCRelaxNGParserCtxtPtr ctxt)
1209{
1210 tokenPtr token, tok2;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001211
Daniel Veillardd7306b02004-01-05 23:11:54 +00001212 token = xmlParseCRNGGetToken(ctxt, 1);
1213 if (token->toktype == CRNG_KEYWORD) {
1214 if ((token->token == ctxt->key_start) ||
1215 (token->token == ctxt->key_include) ||
1216 (token->token == ctxt->key_div)) {
1217 xmlNodePtr grammar;
1218
1219 grammar = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_grammar);
1220 if (grammar == NULL) CRNG_MEM_ERROR0();
1221 xmlDocSetRootElement(ctxt->doc, grammar);
1222 ctxt->insert = grammar;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001223
Daniel Veillardd7306b02004-01-05 23:11:54 +00001224 xmlParseCRNG_grammar(ctxt);
1225 } else {
1226 xmlParseCRNG_pattern(ctxt);
1227 }
1228 } else {
1229 tok2 = xmlParseCRNGGetToken(ctxt, 2);
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001230 if ((tok2->toktype == CRNG_OP) &&
Daniel Veillardd7306b02004-01-05 23:11:54 +00001231 ((tok2->token == ctxt->key_equal) ||
1232 (tok2->token == ctxt->key_orequal) ||
1233 (tok2->token == ctxt->key_andequal))) {
1234 xmlNodePtr grammar;
1235
1236 grammar = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_grammar);
1237 if (grammar == NULL) CRNG_MEM_ERROR0();
1238 xmlDocSetRootElement(ctxt->doc, grammar);
1239 ctxt->insert = grammar;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001240
Daniel Veillardd7306b02004-01-05 23:11:54 +00001241 xmlParseCRNG_grammar(ctxt);
1242 } else {
1243 xmlParseCRNG_pattern(ctxt);
1244 }
1245 }
1246 return(0);
1247}
1248
1249/**
1250 * xmlParseCRNG_namespacePrefix:
1251 * @ctxt: a compact RNG parser context
1252 *
1253 * Parse namespacePrefix of the RELAX NG Compact Syntax Appendix A
1254 *
1255 * Returns the prefix or NULL in case of error
1256 */
1257static const xmlChar *
1258xmlParseCRNG_namespacePrefix(xmlCRelaxNGParserCtxtPtr ctxt)
1259{
1260 tokenPtr token;
1261 const xmlChar *prefix = NULL;
1262
1263 token = xmlParseCRNGGetToken(ctxt, 1);
1264 if (token->toktype == CRNG_IDENTIFIER) {
1265 prefix = token->token;
1266 } else if (token->toktype == CRNG_OP) {
1267 if ((token->token[0] == '=') && (token->token[1] == 0))
1268 return(NULL);
1269 prefix = token->token;
1270 } else {
1271 ERROR("Expecting a namespace prefix");
1272 return(NULL);
1273 }
1274 xmlParseCRNGDropTokens(ctxt, 1);
1275
1276 if (xmlStrEqual(prefix, BAD_CAST "xmlns")) {
1277 ERROR("Namespace prefix \"xmlns\" is forbidden");
1278 }
1279 return(prefix);
1280}
1281
1282/**
1283 * xmlParseCRNG_decl:
1284 * @ctxt: a compact RNG parser context
1285 *
1286 * Parse decl of the RELAX NG Compact Syntax Appendix A
1287 *
1288 * Returns 0 in case of success and -1 in case of error
1289 */
1290static int
1291xmlParseCRNG_decl(xmlCRelaxNGParserCtxtPtr ctxt)
1292{
1293 const xmlChar *prefix = NULL;
1294 const xmlChar *namespace = NULL;
1295 tokenPtr token;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001296
Daniel Veillardd7306b02004-01-05 23:11:54 +00001297 token = xmlParseCRNGGetToken(ctxt, 1);
1298 if (token->toktype != CRNG_KEYWORD) return(-1);
1299 if (token->token == ctxt->key_default) {
1300 xmlParseCRNGDropTokens(ctxt, 1);
1301 token = xmlParseCRNGGetToken(ctxt, 1);
1302 if ((token->toktype != CRNG_KEYWORD) ||
1303 (token->token != ctxt->key_namespace)) {
1304 ERROR("Expecting keyword \"namespace\" after \"default\"");
1305 }
1306 xmlParseCRNGDropTokens(ctxt, 1);
1307 prefix = xmlParseCRNG_namespacePrefix(ctxt);
1308 token = xmlParseCRNGGetToken(ctxt, 1);
1309 if ((token->toktype != CRNG_OP) ||
1310 (token->token[0] != '=') || (token->token[1] != 0)) {
1311 ERROR("Expecting keyword \"=\" here");
1312 }
1313 xmlParseCRNGDropTokens(ctxt, 1);
1314 token = xmlParseCRNGGetToken(ctxt, 1);
1315 if ((token->toktype == CRNG_KEYWORD) &&
1316 (token->token == ctxt->key_inherit)) {
1317 namespace = xmlCRelaxNGInherit;
1318 } else if (token->toktype == CRNG_LITERAL_SEGMENT) {
1319 namespace = token->token;
1320 } else {
1321 ERROR("Expecting an URI or \"inherit\" value");
1322 }
1323 xmlParseCRNGDropTokens(ctxt, 1);
1324 if (namespace != NULL) {
1325 if (prefix != NULL)
1326 xmlParseCRNG_bindPrefix(ctxt, prefix, namespace);
1327 xmlParseCRNG_bindPrefix(ctxt, NULL, namespace);
1328 }
1329 } else if (token->token == ctxt->key_namespace) {
1330 xmlParseCRNGDropTokens(ctxt, 1);
1331 prefix = xmlParseCRNG_namespacePrefix(ctxt);
1332 token = xmlParseCRNGGetToken(ctxt, 1);
1333 if ((token->toktype != CRNG_OP) ||
1334 (token->token[0] != '=') || (token->token[1] != 0)) {
1335 ERROR("Expecting keyword \"=\" here");
1336 }
1337 xmlParseCRNGDropTokens(ctxt, 1);
1338 token = xmlParseCRNGGetToken(ctxt, 1);
1339 if ((token->toktype == CRNG_KEYWORD) &&
1340 (token->token == ctxt->key_inherit)) {
1341 namespace = xmlCRelaxNGInherit;
1342 } else if (token->toktype == CRNG_LITERAL_SEGMENT) {
1343 namespace = token->token;
1344 } else {
1345 ERROR("Expecting an URI or \"inherit\" value");
1346 }
1347 xmlParseCRNGDropTokens(ctxt, 1);
1348 if (namespace != NULL)
1349 xmlParseCRNG_bindPrefix(ctxt, prefix, namespace);
1350 } else if (token->token == ctxt->key_datatypes) {
1351 xmlParseCRNGDropTokens(ctxt, 1);
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001352
Daniel Veillardd7306b02004-01-05 23:11:54 +00001353 token = xmlParseCRNGGetToken(ctxt, 1);
1354 if ((token->toktype != CRNG_KEYWORD) &&
1355 (token->toktype != CRNG_IDENTIFIER)) {
1356 ERROR("Expecting a datatype prefix identifier here");
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001357 } else
Daniel Veillardd7306b02004-01-05 23:11:54 +00001358 prefix = token->token;
1359 xmlParseCRNGDropTokens(ctxt, 1);
1360 token = xmlParseCRNGGetToken(ctxt, 1);
1361 if ((token->toktype != CRNG_OP) ||
1362 (token->token[0] != '=') || (token->token[1] != 0)) {
1363 ERROR("Expecting keyword \"=\" here");
1364 }
1365 xmlParseCRNGDropTokens(ctxt, 1);
1366 token = xmlParseCRNGGetToken(ctxt, 1);
1367 if (token->toktype == CRNG_LITERAL_SEGMENT) {
1368 namespace = token->token;
1369 } else {
1370 ERROR("Expecting a literal value for the datatype identifier");
1371 }
1372 xmlParseCRNGDropTokens(ctxt, 1);
1373 if ((namespace != NULL) && (prefix != NULL))
1374 xmlParseCRNG_bindDatatypePrefix(ctxt, prefix, namespace);
1375 }
1376
1377 return(0);
1378}
1379
1380/**
1381 * xmlParseCRNG_preamble:
1382 * @ctxt: a compact RNG parser context
1383 *
1384 * Parse preamble of the RELAX NG Compact Syntax Appendix A
1385 *
1386 * Returns 0 in case of success and -1 in case of error
1387 */
1388static int
1389xmlParseCRNG_preamble(xmlCRelaxNGParserCtxtPtr ctxt)
1390{
1391 tokenPtr token;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001392
Daniel Veillardd7306b02004-01-05 23:11:54 +00001393 token = xmlParseCRNGGetToken(ctxt, 1);
1394 while (token != NULL) {
1395 if (token == NULL) return(-1);
1396 if ((token->toktype == CRNG_KEYWORD) &&
1397 ((token->token == ctxt->key_default) ||
1398 (token->token == ctxt->key_namespace) ||
1399 (token->token == ctxt->key_datatypes))) {
1400 xmlParseCRNG_decl(ctxt);
1401 } else
1402 break;
1403 token = xmlParseCRNGGetToken(ctxt, 1);
1404 }
1405 return(0);
1406}
1407
1408/**
1409 * xmlParseCRNG_topLevel:
1410 * @ctxt: a compact RNG parser context
1411 *
1412 * Parse topLevel of the RELAX NG Compact Syntax Appendix A
1413 *
1414 * Returns 0 in case of success and -1 in case of error
1415 */
1416static int
1417xmlParseCRNG_topLevel(xmlCRelaxNGParserCtxtPtr ctxt)
1418{
1419 xmlParseCRNG_preamble(ctxt);
1420 xmlParseCRNG_topLevelBody(ctxt);
1421 return(0);
1422}
1423
1424/**
Daniel Veillard03c2f0a2004-01-25 19:54:59 +00001425 * xmlConvertCRNG:
Daniel Veillardd7306b02004-01-05 23:11:54 +00001426 * @schemas: pointer to the text of the compact schemas
1427 * @len: length of the schemas in bytes (or 0)
Daniel Veillard03c2f0a2004-01-25 19:54:59 +00001428 * @encoding: encoding indicated by the context or NULL
Daniel Veillardd7306b02004-01-05 23:11:54 +00001429 *
1430 * Compiles the schemas into the equivalent Relax-NG XML structure
1431 *
1432 * Returns the xmlDocPtr resulting from the compilation or
1433 * NULL in case of error
1434 */
Daniel Veillard03c2f0a2004-01-25 19:54:59 +00001435xmlDocPtr
1436xmlConvertCRNG(const char *schemas, int len, const char *encoding) {
Daniel Veillardd7306b02004-01-05 23:11:54 +00001437 struct _xmlCRelaxNGParserCtxt ctxt;
1438 xmlDocPtr ret = NULL;
1439
1440 if (schemas == NULL) return(NULL);
Daniel Veillard36711902004-02-11 13:25:26 +00001441 if (len <= 5) len = xmlStrlen((const unsigned char *) schemas);
Daniel Veillardd7306b02004-01-05 23:11:54 +00001442 if (len <= 0) return(NULL);
1443
1444 memset(&ctxt, 0, sizeof(ctxt));
Daniel Veillard36711902004-02-11 13:25:26 +00001445 ctxt.compact = (const unsigned char *) schemas;
1446 ctxt.cur = (const unsigned char *) schemas;
1447 ctxt.end = (const unsigned char *) &schemas[len];
Daniel Veillardd7306b02004-01-05 23:11:54 +00001448 ctxt.dict = xmlDictCreate();
1449 if (ctxt.dict == NULL)
1450 return(NULL);
1451 ctxt.doc = xmlNewDoc(NULL);
1452 if (ctxt.doc == NULL) {
1453 xmlDictFree(ctxt.dict);
1454 return(NULL);
1455 }
1456 ctxt.doc->dict = ctxt.dict;
1457 xmlDictReference(ctxt.dict);
1458
1459 ctxt.nbTokens = 0;
1460 ctxt.firstToken = 0;
1461 ctxt.key_attribute = xmlDictLookup(ctxt.dict, BAD_CAST "attribute", -1);
1462 ctxt.key_default = xmlDictLookup(ctxt.dict, BAD_CAST "default", -1);
1463 ctxt.key_datatypes = xmlDictLookup(ctxt.dict, BAD_CAST "datatypes", -1);
1464 ctxt.key_div = xmlDictLookup(ctxt.dict, BAD_CAST "div", -1);
1465 ctxt.key_element = xmlDictLookup(ctxt.dict, BAD_CAST "element", -1);
1466 ctxt.key_empty = xmlDictLookup(ctxt.dict, BAD_CAST "empty", -1);
1467 ctxt.key_external = xmlDictLookup(ctxt.dict, BAD_CAST "external", -1);
1468 ctxt.key_grammar = xmlDictLookup(ctxt.dict, BAD_CAST "grammar", -1);
1469 ctxt.key_include = xmlDictLookup(ctxt.dict, BAD_CAST "include", -1);
1470 ctxt.key_inherit = xmlDictLookup(ctxt.dict, BAD_CAST "inherit", -1);
1471 ctxt.key_list = xmlDictLookup(ctxt.dict, BAD_CAST "list", -1);
1472 ctxt.key_mixed = xmlDictLookup(ctxt.dict, BAD_CAST "mixed", -1);
1473 ctxt.key_namespace = xmlDictLookup(ctxt.dict, BAD_CAST "namespace", -1);
1474 ctxt.key_notAllowed = xmlDictLookup(ctxt.dict, BAD_CAST "notAllowed", -1);
1475 ctxt.key_parent = xmlDictLookup(ctxt.dict, BAD_CAST "parent", -1);
1476 ctxt.key_start = xmlDictLookup(ctxt.dict, BAD_CAST "start", -1);
1477 ctxt.key_string = xmlDictLookup(ctxt.dict, BAD_CAST "string", -1);
1478 ctxt.key_text = xmlDictLookup(ctxt.dict, BAD_CAST "text", -1);
1479 ctxt.key_token = xmlDictLookup(ctxt.dict, BAD_CAST "token", -1);
1480 ctxt.key_equal = xmlDictLookup(ctxt.dict, BAD_CAST "=", 1);
1481 ctxt.key_orequal = xmlDictLookup(ctxt.dict, BAD_CAST "|=", 2);
1482 ctxt.key_andequal = xmlDictLookup(ctxt.dict, BAD_CAST "&=", 2);
1483 ctxt.key_combine = xmlDictLookup(ctxt.dict, BAD_CAST "&=", 2);
1484 ctxt.key_or = xmlDictLookup(ctxt.dict, BAD_CAST "|", 1);
1485 ctxt.key_comma = xmlDictLookup(ctxt.dict, BAD_CAST ",", 1);
1486 ctxt.key_and = xmlDictLookup(ctxt.dict, BAD_CAST "&", 1);
1487 ctxt.key_choice = xmlDictLookup(ctxt.dict, BAD_CAST "choice", -1);
1488 ctxt.key_group = xmlDictLookup(ctxt.dict, BAD_CAST "group", -1);
1489 ctxt.key_interleave = xmlDictLookup(ctxt.dict, BAD_CAST "interleave", -1);
1490 ctxt.key_ref = xmlDictLookup(ctxt.dict, BAD_CAST "ref", 3);
1491 ctxt.key_define = xmlDictLookup(ctxt.dict, BAD_CAST "define", 6);
1492
Daniel Veillard03c2f0a2004-01-25 19:54:59 +00001493 /* xmlConvertCRNGTokenize(&ctxt); */
1494 xmlConvertCRNG_topLevel(&ctxt);
Daniel Veillardd7306b02004-01-05 23:11:54 +00001495
1496 xmlDictFree(ctxt.dict);
1497
1498 ret = ctxt.doc;
1499 return(ret);
1500}
1501
Daniel Veillard03c2f0a2004-01-25 19:54:59 +00001502/**
1503 * xmlConvertCRNGFile:
1504 * @URL: URL or filename for the resource
1505 * @encoding: encoding indicated by the context or NULL
1506 *
1507 * Compiles the schemas into the equivalent Relax-NG XML structure
1508 *
1509 * Returns the xmlDocPtr resulting from the compilation or
1510 * NULL in case of error
1511 */
1512xmlDocPtr
Daniel Veillardf7627552004-04-22 07:15:40 +00001513xmlConvertCRNGFile(const char *URL, const char *encoding) {
Daniel Veillard03c2f0a2004-01-25 19:54:59 +00001514}
1515
1516#ifdef STANDALONE
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001517const xmlChar *schemas =
Daniel Veillardd7306b02004-01-05 23:11:54 +00001518"# RELAX NG XML syntax specified in compact syntax.\n\
1519\n\
1520default namespace rng = \"http://relaxng.org/ns/structure/1.0\"\n\
1521namespace local = \"\"\n\
1522datatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\
1523\n\
1524start = pattern\n\
1525\n\
1526pattern =\n\
1527 element element { (nameQName | nameClass), (common & pattern+) }\n\
1528 | element attribute { (nameQName | nameClass), (common & pattern?) }\n\
1529 | element group|interleave|choice|optional\n\
1530 |zeroOrMore|oneOrMore|list|mixed { common & pattern+ }\n\
1531 | element ref|parentRef { nameNCName, common }\n\
1532 | element empty|notAllowed|text { common }\n\
1533 | element data { type, param*, (common & exceptPattern?) }\n\
1534 | element value { commonAttributes, type?, xsd:string }\n\
1535 | element externalRef { href, common }\n\
1536 | element grammar { common & grammarContent* }\n\
1537\n\
1538param = element param { commonAttributes, nameNCName, xsd:string }\n\
1539\n\
1540exceptPattern = element except { common & pattern+ }\n\
1541\n\
1542grammarContent =\n\
1543 definition\n\
1544 | element div { common & grammarContent* }\n\
1545 | element include { href, (common & includeContent*) }\n\
1546\n\
1547includeContent =\n\
1548 definition\n\
1549 | element div { common & includeContent* }\n\
1550\n\
1551definition =\n\
1552 element start { combine?, (common & pattern+) }\n\
1553 | element define { nameNCName, combine?, (common & pattern+) }\n\
1554\n\
1555combine = attribute combine { \"choice\" | \"interleave\" }\n\
1556\n\
1557nameClass =\n\
1558 element name { commonAttributes, xsd:QName }\n\
1559 | element anyName { common & exceptNameClass? }\n\
1560 | element nsName { common & exceptNameClass? }\n\
1561 | element choice { common & nameClass+ }\n\
1562\n\
1563exceptNameClass = element except { common & nameClass+ }\n\
1564\n\
1565nameQName = attribute name { xsd:QName }\n\
1566nameNCName = attribute name { xsd:NCName }\n\
1567href = attribute href { xsd:anyURI }\n\
1568type = attribute type { xsd:NCName }\n\
1569\n\
1570common = commonAttributes, foreignElement*\n\
1571\n\
1572commonAttributes =\n\
1573 attribute ns { xsd:string }?,\n\
1574 attribute datatypeLibrary { xsd:anyURI }?,\n\
1575 foreignAttribute*\n\
1576\n\
1577foreignElement = element * - rng:* { (anyAttribute | text | anyElement)* }\n\
1578foreignAttribute = attribute * - (rng:*|local:*) { text }\n\
1579anyElement = element * { (anyAttribute | text | anyElement)* }\n\
1580anyAttribute = attribute * { text }\n\
1581";
1582
1583int main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
1584 xmlDocPtr res;
1585
Daniel Veillard03c2f0a2004-01-25 19:54:59 +00001586 res = xmlConvertCRNG(schemas, -1);
Daniel Veillardd7306b02004-01-05 23:11:54 +00001587 if (res != NULL) {
1588 xmlDocFormatDump(stdout, res, 1);
1589 xmlFreeDoc(res);
1590 }
1591 return(0);
1592}
Daniel Veillard03c2f0a2004-01-25 19:54:59 +00001593#endif
Daniel Veillard5d4644e2005-04-01 13:11:58 +00001594#define bottom_rngparser
1595#include "elfgcchack.h"