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