1596 lines
46 KiB
C
1596 lines
46 KiB
C
/**
|
|
* rngparser.c: parser for the Relax-NG compact syntax.
|
|
*
|
|
* Based on:
|
|
* RELAX NG Compact Syntax
|
|
* Committee Specification 21 November 2002
|
|
* http://www.oasis-open.org/committees/relax-ng/compact-20021121.html
|
|
*
|
|
* See Copyright for the status of this software.
|
|
*
|
|
* Daniel Veillard <veillard@redhat.com>
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include <libxml/parser.h>
|
|
#include <libxml/parserInternals.h>
|
|
#include <libxml/relaxng.h>
|
|
#include <libxml/dict.h>
|
|
|
|
#define TODO \
|
|
xmlGenericError(xmlGenericErrorContext, \
|
|
"Unimplemented block at %s:%d\n", \
|
|
__FILE__, __LINE__);
|
|
|
|
#define MAX_TOKEN 10
|
|
|
|
typedef enum {
|
|
CRNG_NONE = 0,
|
|
CRNG_OP = 1,
|
|
CRNG_KEYWORD,
|
|
CRNG_IDENTIFIER,
|
|
CRNG_LITERAL_SEGMENT,
|
|
CRNG_CNAME,
|
|
CRNG_QNAME,
|
|
CRNG_NSNAME,
|
|
CRNG_DOCUMENTATION
|
|
} xmlCRNGTokType;
|
|
|
|
typedef enum {
|
|
CRNG_OKAY = 0,
|
|
CRNG_MEMORY_ERROR,
|
|
CRNG_INVALID_CHAR_ERROR,
|
|
CRNG_END_ERROR,
|
|
CRNG_ENCODING_ERROR
|
|
} xmlCRNGError;
|
|
|
|
typedef enum {
|
|
XML_CRNG_ERROR = -1,
|
|
XML_CRNG_OK = 0,
|
|
XML_CRNG_EOF = 1
|
|
} xmlCRelaxNGParserState;
|
|
|
|
typedef struct _token _token;
|
|
typedef _token *tokenPtr;
|
|
struct _token {
|
|
xmlCRNGTokType toktype;
|
|
int toklen;
|
|
const xmlChar *token;
|
|
const xmlChar *prefix;
|
|
};
|
|
|
|
typedef struct _xmlCRelaxNGParserCtxt xmlCRelaxNGParserCtxt;
|
|
typedef xmlCRelaxNGParserCtxt *xmlCRelaxNGParserCtxtPtr;
|
|
struct _xmlCRelaxNGParserCtxt {
|
|
void *userData; /* user specific data block */
|
|
xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
|
|
xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
|
|
xmlRelaxNGValidErr err;
|
|
|
|
const xmlChar *compact;
|
|
const xmlChar *end;
|
|
const xmlChar *cur;
|
|
int isElem;
|
|
int lineno;
|
|
const xmlChar *linestart;
|
|
const char *filename;
|
|
|
|
int nbTokens;
|
|
int firstToken;
|
|
_token tokens[MAX_TOKEN];
|
|
int totalToken;
|
|
|
|
xmlCRelaxNGParserState state;
|
|
|
|
int nbErrors;
|
|
|
|
xmlDocPtr res; /* the result */
|
|
xmlNodePtr ins; /* the current insertion node */
|
|
|
|
xmlNsPtr nsDef;
|
|
tokenPtr token;
|
|
|
|
xmlHashTablePtr namespaces;
|
|
xmlHashTablePtr datatypes;
|
|
|
|
/*
|
|
* dictionary and keywords
|
|
*/
|
|
xmlDictPtr dict;
|
|
const xmlChar *key_attribute;
|
|
const xmlChar *key_default;
|
|
const xmlChar *key_datatypes;
|
|
const xmlChar *key_div;
|
|
const xmlChar *key_element;
|
|
const xmlChar *key_empty;
|
|
const xmlChar *key_external;
|
|
const xmlChar *key_grammar;
|
|
const xmlChar *key_include;
|
|
const xmlChar *key_inherit;
|
|
const xmlChar *key_list;
|
|
const xmlChar *key_mixed;
|
|
const xmlChar *key_namespace;
|
|
const xmlChar *key_notAllowed;
|
|
const xmlChar *key_parent;
|
|
const xmlChar *key_start;
|
|
const xmlChar *key_string;
|
|
const xmlChar *key_text;
|
|
const xmlChar *key_token;
|
|
const xmlChar *key_equal;
|
|
const xmlChar *key_orequal;
|
|
const xmlChar *key_andequal;
|
|
const xmlChar *key_combine;
|
|
const xmlChar *key_or;
|
|
const xmlChar *key_comma;
|
|
const xmlChar *key_and;
|
|
const xmlChar *key_choice;
|
|
const xmlChar *key_group;
|
|
const xmlChar *key_interleave;
|
|
const xmlChar *key_ref;
|
|
const xmlChar *key_define;
|
|
|
|
/* results */
|
|
xmlDocPtr doc; /* the resulting doc */
|
|
xmlNodePtr insert; /* the insertion point */
|
|
xmlAttrPtr attrs; /* pending attributes */
|
|
};
|
|
|
|
static const xmlChar *xmlCRelaxNGInherit = BAD_CAST "Inherit string";
|
|
static const xmlChar *xmlCRelaxNGDefault = BAD_CAST "Default string";
|
|
|
|
#define CUR_CHAR(l) xmlXPathCurrentChar(ctxt, &l)
|
|
/**
|
|
* IS_BLANK:
|
|
* @c: an UNICODE value (int)
|
|
*
|
|
* Macro to check the following production in the XML spec:
|
|
*
|
|
* [3] S ::= (#x20 | #x9 | #xD | #xA)+
|
|
*/
|
|
#ifndef IS_BLANK
|
|
#define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \
|
|
((c) == 0x0D))
|
|
#endif
|
|
#define IS_SEPARATOR(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \
|
|
((c) == 0x0D) || (c == '#'))
|
|
|
|
#define CRNG_ERROR0(X) \
|
|
{ xmlCRNGErr(ctxt, X, NULL); return(0); }
|
|
#define CRNG_ERROR(X) \
|
|
{ xmlCRNGErr(ctxt, X, NULL); }
|
|
|
|
#define CRNG_MEM_ERROR0() \
|
|
{ xmlCRNGErr(ctxt, CRNG_MEMORY_ERROR, NULL); return(0); }
|
|
#define CRNG_MEM_ERROR() \
|
|
{ xmlCRNGErr(ctxt, CRNG_MEMORY_ERROR, NULL); }
|
|
|
|
#define ERROR(str) xmlCRNGErr(ctxt, 0, str);
|
|
|
|
static void
|
|
xmlCRNGErr(xmlCRelaxNGParserCtxtPtr ctxt, int err_no, const char *err_msg) {
|
|
const xmlChar *cur;
|
|
xmlChar buffer[150];
|
|
int i, l;
|
|
|
|
if (ctxt != NULL) {
|
|
if (ctxt->filename != NULL)
|
|
fprintf(stderr, "%s:%d ", ctxt->filename, ctxt->lineno);
|
|
}
|
|
if (err_msg != NULL) {
|
|
fprintf(stderr, "error: %s\n", err_msg);
|
|
} else if (err_no != 0)
|
|
fprintf(stderr, "error %d\n", err_no);
|
|
cur = ctxt->cur;
|
|
while ((*cur != '\n') && (*cur != '\r') && (ctxt->cur - cur < 80)) cur--;
|
|
l = ctxt->cur - cur;
|
|
cur++;
|
|
for (i = 0; i < 100;i++) {
|
|
if ((*cur == '\n') || (*cur == '\r')) break;
|
|
buffer[i] = *cur++;
|
|
}
|
|
buffer[i] = 0;
|
|
fprintf(stderr, "%s\n", buffer);
|
|
for (i = 0; i < l;i++) buffer[i] = ' ';
|
|
buffer[i++] = '^';
|
|
buffer[i++] = 0;
|
|
fprintf(stderr, "%s\n", buffer);
|
|
}
|
|
|
|
/**
|
|
* IS_OP
|
|
* @c: an UNICODE value (int)
|
|
*
|
|
* Macro to check for operator value
|
|
*/
|
|
#ifndef IS_OP
|
|
#define IS_OP(c) (((c) == ',') || ((c) == '&') || ((c) == '|') || \
|
|
((c) == '?') || ((c) == '-') || ((c) == '*') || \
|
|
((c) == '{') || ((c) == '}') || ((c) == '(') || \
|
|
((c) == ')') || ((c) == '+') || ((c) == '=') || \
|
|
((c) == ':'))
|
|
#endif
|
|
|
|
static int
|
|
xmlCRNGIsKeyword(xmlCRelaxNGParserCtxtPtr ctxt, const xmlChar *str) {
|
|
if ((str == ctxt->key_attribute) ||
|
|
(str == ctxt->key_default) ||
|
|
(str == ctxt->key_datatypes) ||
|
|
(str == ctxt->key_div) ||
|
|
(str == ctxt->key_element) ||
|
|
(str == ctxt->key_empty) ||
|
|
(str == ctxt->key_external) ||
|
|
(str == ctxt->key_grammar) ||
|
|
(str == ctxt->key_include) ||
|
|
(str == ctxt->key_inherit) ||
|
|
(str == ctxt->key_list) ||
|
|
(str == ctxt->key_mixed) ||
|
|
(str == ctxt->key_namespace) ||
|
|
(str == ctxt->key_notAllowed) ||
|
|
(str == ctxt->key_parent) ||
|
|
(str == ctxt->key_start) ||
|
|
(str == ctxt->key_string) ||
|
|
(str == ctxt->key_text) ||
|
|
(str == ctxt->key_token))
|
|
return(1);
|
|
return(0);
|
|
|
|
}
|
|
|
|
/*
|
|
* xmlCRNGNextToken:
|
|
* ctxt: a compact RNG parser context
|
|
*
|
|
* Scan the schema to get the next token
|
|
*
|
|
* Return 0 if success and -1 in case of error
|
|
*/
|
|
|
|
static int
|
|
xmlCRNGNextToken(xmlCRelaxNGParserCtxtPtr ctxt) {
|
|
const xmlChar *cur;
|
|
tokenPtr token;
|
|
|
|
if (ctxt == NULL) return(-1);
|
|
if (ctxt->nbTokens >= MAX_TOKEN) return(-1);
|
|
token = &(ctxt->tokens[(ctxt->firstToken + ctxt->nbTokens) % MAX_TOKEN]);
|
|
token->toktype = CRNG_NONE;
|
|
|
|
if (ctxt->cur == NULL) {
|
|
ctxt->cur = ctxt->compact;
|
|
}
|
|
retry:
|
|
if (ctxt->cur >= ctxt->end) {
|
|
ctxt->state = XML_CRNG_EOF;
|
|
return(-1);
|
|
}
|
|
while ((ctxt->cur < ctxt->end) &&
|
|
(IS_BLANK(*ctxt->cur))) ctxt->cur++;
|
|
if (ctxt->cur >= ctxt->end) {
|
|
ctxt->state = XML_CRNG_EOF;
|
|
return(-1);
|
|
}
|
|
if (*ctxt->cur == '#') {
|
|
cur = ctxt->cur;
|
|
cur++;
|
|
while ((cur < ctxt->end) && (*cur != '\n') && (*cur != '\r'))
|
|
cur++;
|
|
ctxt->cur = cur;
|
|
goto retry;
|
|
} else if (*ctxt->cur == '"') {
|
|
/* string, check for '"""' */
|
|
ctxt->cur++;
|
|
if (ctxt->cur >= ctxt->end) goto eof;
|
|
cur = ctxt->cur;
|
|
if ((ctxt->end - ctxt->end > 2) &&
|
|
(*cur == '"') && (cur[1] == '"')) {
|
|
TODO
|
|
} else {
|
|
while ((cur < ctxt->end) && (*cur != '"')) cur++;
|
|
if (cur >= ctxt->end) goto eof;
|
|
token->toklen = cur - ctxt->cur;
|
|
token->token = xmlDictLookup(ctxt->dict, ctxt->cur, token->toklen);
|
|
token->toktype = CRNG_LITERAL_SEGMENT;
|
|
token->prefix = NULL;
|
|
cur++;
|
|
ctxt->cur = cur;
|
|
}
|
|
} else if (*ctxt->cur == '\'') {
|
|
/* string, check for "'''" */
|
|
TODO
|
|
} else if ((IS_OP(*ctxt->cur)) || (*ctxt->cur == ':')) {
|
|
cur = ctxt->cur;
|
|
cur++;
|
|
if ((cur < ctxt->end) &&
|
|
(((*cur == '=') &&
|
|
((*ctxt->cur == '|') || (*ctxt->cur == '&'))) ||
|
|
((*cur == '*') && (*ctxt->cur == ':')))) {
|
|
token->toklen = 2;
|
|
} else {
|
|
token->toklen = 1;
|
|
}
|
|
token->token = xmlDictLookup(ctxt->dict, ctxt->cur, token->toklen);
|
|
token->toktype = CRNG_OP;
|
|
token->prefix = NULL;
|
|
ctxt->cur += token->toklen;
|
|
} else {
|
|
int escape = 0;
|
|
|
|
cur = ctxt->cur;
|
|
if (*cur == '\\') {
|
|
escape = 1;
|
|
cur++;
|
|
ctxt->cur++;
|
|
}
|
|
while ((cur < ctxt->end) &&
|
|
(!(IS_SEPARATOR(*cur))) && (!(IS_OP(*cur)))) cur++;
|
|
|
|
token->toklen = cur - ctxt->cur;
|
|
token->token = xmlDictLookup(ctxt->dict, ctxt->cur, token->toklen);
|
|
token->prefix = NULL;
|
|
ctxt->cur = cur;
|
|
if ((escape == 0) && (xmlCRNGIsKeyword(ctxt, token->token)))
|
|
token->toktype = CRNG_KEYWORD;
|
|
else {
|
|
token->toktype = CRNG_IDENTIFIER;
|
|
}
|
|
if (*ctxt->cur == ':') {
|
|
ctxt->cur++;
|
|
if (*ctxt->cur == '*') {
|
|
ctxt->cur++;
|
|
token->toktype = CRNG_NSNAME;
|
|
} else {
|
|
cur = ctxt->cur;
|
|
while ((cur < ctxt->end) &&
|
|
(!(IS_SEPARATOR(*cur))) && (!(IS_OP(*cur)))) cur++;
|
|
token->prefix = token->token;
|
|
token->toklen = cur - ctxt->cur;
|
|
token->token = xmlDictLookup(ctxt->dict, ctxt->cur,
|
|
token->toklen);
|
|
ctxt->cur = cur;
|
|
if (xmlValidateNCName(token->token, 0) == 0)
|
|
token->toktype = CRNG_QNAME;
|
|
else {
|
|
TODO /* sounds like an error ! */
|
|
token->toktype = CRNG_IDENTIFIER;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ctxt->nbTokens++;
|
|
return(0);
|
|
eof:
|
|
ctxt->state = XML_CRNG_EOF;
|
|
CRNG_ERROR(CRNG_END_ERROR);
|
|
return(-1);
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNGGetToken:
|
|
* @ctxt: a compact RNG parser context
|
|
* @no: the number of the token from 1 for the first one
|
|
* and 2, 3 ... for read-ahead
|
|
*
|
|
* Token reading interface
|
|
*
|
|
* returns a pointer to the new token, or NULL in case of error or EOF
|
|
*/
|
|
static tokenPtr
|
|
xmlParseCRNGGetToken(xmlCRelaxNGParserCtxtPtr ctxt, int no) {
|
|
tokenPtr ret;
|
|
int res;
|
|
|
|
if ((no <= 0) || (no >= MAX_TOKEN)) return(NULL);
|
|
no--;
|
|
while (ctxt->nbTokens <= no) {
|
|
res = xmlCRNGNextToken(ctxt);
|
|
if (res < 0)
|
|
return(NULL);
|
|
}
|
|
ret = &(ctxt->tokens[(ctxt->firstToken + no) % MAX_TOKEN]);
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNGDropTokens:
|
|
* @ctxt: a compact RNG parser context
|
|
* @nr: the number of token marked as read
|
|
*
|
|
* mark a number of token as read and consumed.
|
|
*
|
|
* Returns -1 in case of error and 0 otherwise
|
|
*/
|
|
static int
|
|
xmlParseCRNGDropTokens(xmlCRelaxNGParserCtxtPtr ctxt, int nr) {
|
|
if ((nr <= 0) || (nr >= MAX_TOKEN)) return(-1);
|
|
while ((ctxt->nbTokens >0) && (nr > 0)) {
|
|
ctxt->firstToken++;
|
|
nr--;
|
|
ctxt->nbTokens--;
|
|
ctxt->totalToken++;
|
|
if (ctxt->totalToken == 384)
|
|
fprintf(stderr, "found\n");
|
|
}
|
|
ctxt->firstToken = ctxt->firstToken % MAX_TOKEN;
|
|
return(0);
|
|
}
|
|
|
|
static void
|
|
xmlParseCRNGTokenize(xmlCRelaxNGParserCtxtPtr ctxt) {
|
|
tokenPtr token;
|
|
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
while (token != NULL) {
|
|
switch (token->toktype) {
|
|
case CRNG_NONE: printf("none"); break;
|
|
case CRNG_OP: printf("op"); break;
|
|
case CRNG_KEYWORD: printf("keyword"); break;
|
|
case CRNG_IDENTIFIER: printf("identifier"); break;
|
|
case CRNG_LITERAL_SEGMENT: printf("literal"); break;
|
|
case CRNG_CNAME: printf("cname"); break;
|
|
case CRNG_QNAME: printf("qname"); break;
|
|
case CRNG_NSNAME: printf("nsname"); break;
|
|
case CRNG_DOCUMENTATION: printf("doc"); break;
|
|
}
|
|
printf(":%s\n", token->token);
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNG_attribute:
|
|
* @ctxt: a compact RNG parser context
|
|
* @name: the attribute name
|
|
* @ns: the attribute namespace
|
|
* @value: the attribute value
|
|
*
|
|
* implements attribute of the RELAX NG Compact Syntax Appendix A
|
|
*
|
|
* Returns 0 in case of success and -1 in case of error
|
|
*/
|
|
static int
|
|
xmlParseCRNG_attribute(xmlCRelaxNGParserCtxtPtr ctxt,
|
|
const xmlChar *name,
|
|
xmlNsPtr ns,
|
|
const xmlChar *value)
|
|
{
|
|
xmlAttrPtr attr;
|
|
|
|
attr = xmlNewNsPropEatName(NULL, ns, (xmlChar *) name, value);
|
|
if (attr == NULL) CRNG_MEM_ERROR0();
|
|
attr->next = ctxt->attrs;
|
|
if (ctxt->attrs != NULL)
|
|
ctxt->attrs->prev = attr;
|
|
ctxt->attrs = attr;
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNG_bindPrefix:
|
|
* @ctxt: a compact RNG parser context
|
|
* @prefix: the namespace prefix or NULL
|
|
* @namespace: the namespace name
|
|
*
|
|
* implements bindPrefix of the RELAX NG Compact Syntax Appendix A
|
|
*
|
|
* Returns 0 in case of success and -1 in case of error
|
|
*/
|
|
static int
|
|
xmlParseCRNG_bindPrefix(xmlCRelaxNGParserCtxtPtr ctxt,
|
|
const xmlChar *prefix,
|
|
const xmlChar *namespace)
|
|
{
|
|
int ret;
|
|
|
|
if ((prefix != NULL) && (xmlStrEqual(prefix, BAD_CAST "xml")) &&
|
|
(!xmlStrEqual(namespace, XML_XML_NAMESPACE))) {
|
|
ERROR("The \"xml\" prefix must be bound to \"http://www.w3.org/XML/1998/namespace\"");
|
|
return(-1);
|
|
} else if ((xmlStrEqual(namespace, XML_XML_NAMESPACE)) &&
|
|
(!xmlStrEqual(prefix, BAD_CAST "xml"))) {
|
|
ERROR("The \"http://www.w3.org/XML/1998/namespace\" name must be bound to \"xml\" prefix");
|
|
return(-1);
|
|
}
|
|
if (ctxt->namespaces == NULL)
|
|
ctxt->namespaces = xmlHashCreate(10);
|
|
if (ctxt->namespaces == NULL) {
|
|
ERROR("Failed to create namespace hash table");
|
|
return(-1);
|
|
}
|
|
if (prefix == NULL)
|
|
ret = xmlHashAddEntry(ctxt->namespaces, xmlCRelaxNGDefault,
|
|
(void *) namespace);
|
|
else
|
|
ret = xmlHashAddEntry(ctxt->namespaces, prefix,
|
|
(void *) namespace);
|
|
if (ret < 0) {
|
|
if (prefix == NULL) {
|
|
ERROR("Redefinition of default namespace");
|
|
} else {
|
|
ERROR("Redefinition of namespace");
|
|
}
|
|
return(-1);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNG_bindDatatypePrefix:
|
|
* @ctxt: a compact RNG parser context
|
|
* @prefix: the datatype prefix
|
|
* @namespace: the datatype identifier
|
|
*
|
|
* implements bindDatatypePrefix of the RELAX NG Compact Syntax Appendix A
|
|
*
|
|
* Returns 0 in case of success and -1 in case of error
|
|
*/
|
|
static int
|
|
xmlParseCRNG_bindDatatypePrefix(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
|
|
const xmlChar *prefix,
|
|
const xmlChar *namespace)
|
|
{
|
|
int ret;
|
|
|
|
if ((prefix != NULL) && (xmlStrEqual(prefix, BAD_CAST "xsd")) &&
|
|
(!xmlStrEqual(namespace,
|
|
BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes"))) {
|
|
ERROR("The \"xsd\" prefix must be bound to \"http://www.w3.org/2001/XMLSchema-datatypes\"");
|
|
return(-1);
|
|
}
|
|
if (ctxt->datatypes == NULL)
|
|
ctxt->datatypes = xmlHashCreate(10);
|
|
if (ctxt->datatypes == NULL) {
|
|
ERROR("Failed to create namespace hash table");
|
|
return(-1);
|
|
}
|
|
ret = xmlHashAddEntry(ctxt->datatypes, prefix,
|
|
(void *) namespace);
|
|
if (ret < 0) {
|
|
ERROR("Redefinition of datatype");
|
|
return(-1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNG_lookupPrefix:
|
|
* @ctxt: a compact RNG parser context
|
|
* @prefix: the namespace prefix or NULL
|
|
*
|
|
* implements lookupPrefix of the RELAX NG Compact Syntax Appendix A
|
|
*
|
|
* Returns the prefix in case of success or NULL in case of error
|
|
*/
|
|
static const xmlChar *
|
|
xmlParseCRNG_lookupPrefix(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
|
|
const xmlChar *prefix)
|
|
{
|
|
const xmlChar *ret;
|
|
|
|
if (prefix == NULL)
|
|
ret = xmlHashLookup(ctxt->namespaces, xmlCRelaxNGDefault);
|
|
else
|
|
ret = xmlHashLookup(ctxt->namespaces, prefix);
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNG_lookupDatatypePrefix:
|
|
* @ctxt: a compact RNG parser context
|
|
* @prefix: the namespace prefix or NULL
|
|
*
|
|
* implements lookupDatatypePrefix of the RELAX NG Compact Syntax Appendix A
|
|
*
|
|
* Returns the prefix in case of success or NULL in case of error
|
|
*/
|
|
static const xmlChar *
|
|
xmlParseCRNG_lookupDatatypePrefix(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
|
|
const xmlChar *prefix)
|
|
{
|
|
const xmlChar *ret;
|
|
ret = xmlHashLookup(ctxt->datatypes, prefix);
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNG_datatypeAttributes:
|
|
* @ctxt: a compact RNG parser context
|
|
* @prefix: the namespace prefix or NULL
|
|
*
|
|
* implements lookupPrefix of the RELAX NG Compact Syntax Appendix A
|
|
*
|
|
* Returns the prefix in case of success or NULL in case of error
|
|
*/
|
|
static xmlAttrPtr
|
|
xmlParseCRNG_datatypeAttributes(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
|
|
const xmlChar *library, const xmlChar *type)
|
|
{
|
|
xmlAttrPtr lib, typ;
|
|
|
|
lib = xmlNewNsProp(NULL, NULL, BAD_CAST "datatypeLibrary", library);
|
|
if (lib == NULL) {
|
|
CRNG_MEM_ERROR();
|
|
return(NULL);
|
|
}
|
|
typ = xmlNewNsProp(NULL, NULL, BAD_CAST "type", type);
|
|
if (typ == NULL) {
|
|
CRNG_MEM_ERROR();
|
|
return(lib);
|
|
}
|
|
lib->next = typ;
|
|
|
|
return(lib);
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNG_XXX:
|
|
* @ctxt: a compact RNG parser context
|
|
*
|
|
* Parse XXX of the RELAX NG Compact Syntax Appendix A
|
|
*
|
|
* Returns 0 in case of success and -1 in case of error
|
|
*/
|
|
static int
|
|
xmlParseCRNG_XXX(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
static int xmlParseCRNG_pattern(xmlCRelaxNGParserCtxtPtr ctxt);
|
|
static int xmlParseCRNG_nameClass(xmlCRelaxNGParserCtxtPtr ctxt);
|
|
|
|
/**
|
|
* xmlParseCRNG_params:
|
|
* @ctxt: a compact RNG parser context
|
|
*
|
|
* Parse params of the RELAX NG Compact Syntax Appendix A
|
|
*
|
|
* Returns 0 in case of success and -1 in case of error
|
|
*/
|
|
static int
|
|
xmlParseCRNG_params(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
|
|
{
|
|
TODO
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNG_exceptNameClass:
|
|
* @ctxt: a compact RNG parser context
|
|
*
|
|
* Parse exceptNameClass of the RELAX NG Compact Syntax Appendix A
|
|
*
|
|
* Returns 0 in case of success and -1 in case of error
|
|
*/
|
|
static int
|
|
xmlParseCRNG_exceptNameClass(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
|
|
{
|
|
tokenPtr token;
|
|
xmlNodePtr insert = ctxt->insert, cur;
|
|
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
if ((token->toktype == CRNG_OP) &&
|
|
(token->token[0] == '-') && (token->token[1] == 0)) {
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
cur = xmlNewNode(NULL, BAD_CAST "except");
|
|
if (cur == NULL) CRNG_MEM_ERROR0();
|
|
if (ctxt->insert != NULL)
|
|
xmlAddChild(ctxt->insert, cur);
|
|
ctxt->insert = cur;
|
|
xmlParseCRNG_nameClass(ctxt);
|
|
}
|
|
ctxt->insert = insert;
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNG_innerNameClass:
|
|
* @ctxt: a compact RNG parser context
|
|
*
|
|
* Parse innerNameClass of the RELAX NG Compact Syntax Appendix A
|
|
*
|
|
* Returns 0 in case of success and -1 in case of error
|
|
*/
|
|
static int
|
|
xmlParseCRNG_innerNameClass(xmlCRelaxNGParserCtxtPtr ctxt)
|
|
{
|
|
tokenPtr token;
|
|
xmlNodePtr cur;
|
|
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
if (token->toktype == CRNG_OP) {
|
|
if ((token->token[0] == '(') && (token->token[1] == 0)) {
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
xmlParseCRNG_nameClass(ctxt);
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
if ((token->toktype != CRNG_OP) ||
|
|
(token->token[0] != ')') || (token->token[1] != 0)) {
|
|
ERROR("Expecting \")\" here");
|
|
}
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
} else if ((token->token[0] == '*') && (token->token[1] == 0)) {
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
cur = xmlNewNode(NULL, BAD_CAST "anyName");
|
|
if (cur == NULL) CRNG_MEM_ERROR0();
|
|
if (ctxt->insert != NULL)
|
|
xmlAddChild(ctxt->insert, cur);
|
|
ctxt->insert = cur;
|
|
xmlParseCRNG_exceptNameClass(ctxt);
|
|
} else {
|
|
TODO
|
|
}
|
|
} else if ((token->toktype == CRNG_IDENTIFIER) ||
|
|
(token->toktype == CRNG_KEYWORD)) {
|
|
cur = xmlNewNode(NULL, BAD_CAST "name");
|
|
if (cur == NULL) CRNG_MEM_ERROR0();
|
|
if (ctxt->isElem) {
|
|
xmlSetProp(cur, BAD_CAST "ns",
|
|
xmlParseCRNG_lookupPrefix(ctxt, NULL));
|
|
} else {
|
|
xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
|
|
}
|
|
xmlNodeAddContent(cur, token->token);
|
|
if (ctxt->insert != NULL)
|
|
xmlAddChild(ctxt->insert, cur);
|
|
ctxt->insert = cur;
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
} else if (token->toktype == CRNG_CNAME) {
|
|
TODO
|
|
} else if (token->toktype == CRNG_NSNAME) {
|
|
cur = xmlNewNode(NULL, BAD_CAST "nsName");
|
|
if (cur == NULL) CRNG_MEM_ERROR0();
|
|
xmlSetProp(cur, BAD_CAST "ns",
|
|
xmlParseCRNG_lookupPrefix(ctxt, token->token));
|
|
if (ctxt->insert != NULL)
|
|
xmlAddChild(ctxt->insert, cur);
|
|
ctxt->insert = cur;
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
xmlParseCRNG_exceptNameClass(ctxt);
|
|
} else {
|
|
TODO /* probably an error */
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNG_nameClass:
|
|
* @ctxt: a compact RNG parser context
|
|
*
|
|
* Parse nameClass of the RELAX NG Compact Syntax Appendix A
|
|
*
|
|
* Returns 0 in case of success and -1 in case of error
|
|
*/
|
|
static int
|
|
xmlParseCRNG_nameClass(xmlCRelaxNGParserCtxtPtr ctxt)
|
|
{
|
|
tokenPtr token;
|
|
xmlNodePtr insert = ctxt->insert, last, choice;
|
|
|
|
ctxt->insert = NULL;
|
|
xmlParseCRNG_innerNameClass(ctxt);
|
|
last = ctxt->insert;
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
while ((token->toktype == CRNG_OP) &&
|
|
(token->token[0] == '|') && (token->token[1] == 0)) {
|
|
choice = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_choice);
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
if (choice == NULL) CRNG_MEM_ERROR0();
|
|
ctxt->insert = NULL;
|
|
xmlParseCRNG_innerNameClass(ctxt);
|
|
xmlAddChild(choice, last);
|
|
xmlAddChild(choice, ctxt->insert);
|
|
last = choice;
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
}
|
|
xmlAddChild(insert, last);
|
|
|
|
ctxt->insert = insert;
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNG_patternBlock:
|
|
* @ctxt: a compact RNG parser context
|
|
*
|
|
* Parse a pattern block of the RELAX NG Compact Syntax Appendix A
|
|
*
|
|
* Returns 0 in case of success and -1 in case of error
|
|
*/
|
|
static int
|
|
xmlParseCRNG_patternBlock(xmlCRelaxNGParserCtxtPtr ctxt)
|
|
{
|
|
tokenPtr token;
|
|
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
if ((token->toktype != CRNG_OP) ||
|
|
(token->token[0] != '{') || (token->token[1] != 0)) {
|
|
ERROR("Expecting \"{\" here");
|
|
}
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
xmlParseCRNG_pattern(ctxt);
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
if ((token->toktype != CRNG_OP) ||
|
|
(token->token[0] != '}') || (token->token[1] != 0)) {
|
|
ERROR("Expecting \"}\" here");
|
|
}
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNG_datatype:
|
|
* @ctxt: a compact RNG parser context
|
|
*
|
|
* Parse datatype of the RELAX NG Compact Syntax Appendix A
|
|
*
|
|
* Returns 0 in case of success and -1 in case of error
|
|
*/
|
|
static int
|
|
xmlParseCRNG_datatype(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
|
|
{
|
|
tokenPtr token;
|
|
xmlAttrPtr attrs = NULL;
|
|
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
if (token->toktype == CRNG_KEYWORD) {
|
|
if (token->token == ctxt->key_string) {
|
|
attrs = xmlParseCRNG_datatypeAttributes(ctxt, BAD_CAST "",
|
|
token->token);
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
} else if (token->token == ctxt->key_token) {
|
|
attrs = xmlParseCRNG_datatypeAttributes(ctxt, BAD_CAST "",
|
|
token->token);
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
} else {
|
|
TODO /* probably an error */
|
|
}
|
|
} else if (token->toktype == CRNG_LITERAL_SEGMENT) {
|
|
ctxt->insert = xmlNewNode(NULL, BAD_CAST "value");
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
|
|
xmlNodeAddContent(ctxt->insert, token->token);
|
|
} else if (token->toktype == CRNG_QNAME) {
|
|
attrs = xmlParseCRNG_datatypeAttributes(ctxt,
|
|
xmlParseCRNG_lookupDatatypePrefix(ctxt, token->prefix),
|
|
token->token);
|
|
} else {
|
|
TODO
|
|
}
|
|
if (attrs != NULL) {
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
if (token->toktype == CRNG_LITERAL_SEGMENT) {
|
|
ctxt->insert = xmlNewNode(NULL, BAD_CAST "value");
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
if (ctxt->insert == NULL) {
|
|
xmlFreePropList(attrs);
|
|
CRNG_MEM_ERROR0();
|
|
}
|
|
ctxt->insert->properties = attrs;
|
|
xmlNodeAddContent(ctxt->insert, token->token);
|
|
} else if ((token->toktype == CRNG_OP) &&
|
|
(token->token[0] == '{') && (token->token[0] == 0)) {
|
|
ctxt->insert = xmlNewNode(NULL, BAD_CAST "data");
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
if (ctxt->insert == NULL) {
|
|
xmlFreePropList(attrs);
|
|
CRNG_MEM_ERROR0();
|
|
}
|
|
ctxt->insert->properties = attrs;
|
|
xmlParseCRNG_params(ctxt);
|
|
} else {
|
|
ctxt->insert = xmlNewNode(NULL, BAD_CAST "data");
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
if (ctxt->insert == NULL) {
|
|
xmlFreePropList(attrs);
|
|
CRNG_MEM_ERROR0();
|
|
}
|
|
ctxt->insert->properties = attrs;
|
|
xmlNodeAddContent(ctxt->insert, token->token);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNG_primary:
|
|
* @ctxt: a compact RNG parser context
|
|
*
|
|
* Parse primary of the RELAX NG Compact Syntax Appendix A
|
|
*
|
|
* Returns 0 in case of success and -1 in case of error
|
|
*/
|
|
static int
|
|
xmlParseCRNG_primary(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
|
|
{
|
|
tokenPtr token;
|
|
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
if (token == NULL)
|
|
return(0);
|
|
if (token->toktype == CRNG_KEYWORD) {
|
|
if (token->token == ctxt->key_element) {
|
|
ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
|
|
ctxt->isElem = 1;
|
|
xmlParseCRNG_nameClass(ctxt);
|
|
xmlParseCRNG_patternBlock(ctxt);
|
|
} else if (token->token == ctxt->key_attribute) {
|
|
ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
|
|
ctxt->isElem = 0;
|
|
xmlParseCRNG_nameClass(ctxt);
|
|
xmlParseCRNG_patternBlock(ctxt);
|
|
} else if (token->token == ctxt->key_mixed) {
|
|
ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
|
|
xmlParseCRNG_patternBlock(ctxt);
|
|
} else if (token->token == ctxt->key_list) {
|
|
ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
|
|
xmlParseCRNG_patternBlock(ctxt);
|
|
} else if (token->token == ctxt->key_empty) {
|
|
ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
|
|
} else if (token->token == ctxt->key_notAllowed) {
|
|
ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
|
|
} else if (token->token == ctxt->key_text) {
|
|
ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
|
|
} else if (token->token == ctxt->key_parent) {
|
|
ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
|
|
TODO
|
|
} else if (token->token == ctxt->key_grammar) {
|
|
ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) token->token);
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
|
|
TODO
|
|
} else if (token->token == ctxt->key_external) {
|
|
ctxt->insert = xmlNewNode(NULL, BAD_CAST "externalRef");
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
|
|
TODO
|
|
} else {
|
|
TODO
|
|
}
|
|
} else if (token->toktype == CRNG_IDENTIFIER) {
|
|
ctxt->insert = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_ref);
|
|
if (ctxt->insert == NULL) CRNG_MEM_ERROR0();
|
|
xmlSetProp(ctxt->insert, BAD_CAST "name", token->token);
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
} else if (token->toktype == CRNG_QNAME) {
|
|
xmlParseCRNG_datatype(ctxt);
|
|
} else if (token->toktype == CRNG_LITERAL_SEGMENT) {
|
|
xmlParseCRNG_datatype(ctxt);
|
|
} else if ((token->toktype == CRNG_OP) &&
|
|
(token->token[0] == '(') && (token->token[1] == 0)) {
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
xmlParseCRNG_pattern(ctxt);
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
if ((token->toktype != CRNG_OP) ||
|
|
(token->token[0] != ')') || (token->token[1] != 0)) {
|
|
ERROR("Expecting \")\" here");
|
|
}
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNG_particle:
|
|
* @ctxt: a compact RNG parser context
|
|
*
|
|
* Parse particle of the RELAX NG Compact Syntax Appendix A
|
|
*
|
|
* Returns 0 in case of success and -1 in case of error
|
|
*/
|
|
static int
|
|
xmlParseCRNG_particle(xmlCRelaxNGParserCtxtPtr ctxt)
|
|
{
|
|
tokenPtr token;
|
|
xmlNodePtr insert = ctxt->insert, res, tmp = NULL;
|
|
|
|
ctxt->insert = NULL;
|
|
xmlParseCRNG_primary(ctxt);
|
|
res = ctxt->insert;
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
if ((token != NULL) && (token->toktype == CRNG_OP)) {
|
|
if ((token->token[0] == '*') && (token->token[1] == 0)) {
|
|
tmp = xmlNewNode(NULL, BAD_CAST "zeroOrMore");
|
|
if (tmp == NULL) CRNG_MEM_ERROR0();
|
|
} else if ((token->token[0] == '+') && (token->token[1] == 0)) {
|
|
tmp = xmlNewNode(NULL, BAD_CAST "oneOrMore");
|
|
if (tmp == NULL) CRNG_MEM_ERROR0();
|
|
} else if ((token->token[0] == '?') && (token->token[1] == 0)) {
|
|
tmp = xmlNewNode(NULL, BAD_CAST "optional");
|
|
if (tmp == NULL) CRNG_MEM_ERROR0();
|
|
}
|
|
if (tmp != NULL) {
|
|
xmlAddChild(tmp, res);
|
|
res = tmp;
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
}
|
|
}
|
|
if (insert != NULL) {
|
|
xmlAddChild(insert, res);
|
|
ctxt->insert = insert;
|
|
} else
|
|
ctxt->insert = res;
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNG_pattern:
|
|
* @ctxt: a compact RNG parser context
|
|
*
|
|
* Parse pattern of the RELAX NG Compact Syntax Appendix A
|
|
*
|
|
* Returns 0 in case of success and -1 in case of error
|
|
*/
|
|
static int
|
|
xmlParseCRNG_pattern(xmlCRelaxNGParserCtxtPtr ctxt)
|
|
{
|
|
tokenPtr token;
|
|
xmlNodePtr insert = ctxt->insert, prev, grp;
|
|
|
|
ctxt->insert = NULL;
|
|
xmlParseCRNG_particle(ctxt);
|
|
prev = ctxt->insert;
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
while ((prev != NULL) && (token != NULL) && (token->toktype == CRNG_OP)) {
|
|
if (token->token == ctxt->key_or) {
|
|
grp = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_choice);
|
|
if (grp == NULL) CRNG_MEM_ERROR0();
|
|
} else if (token->token == ctxt->key_and) {
|
|
grp = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_interleave);
|
|
if (grp == NULL) CRNG_MEM_ERROR0();
|
|
} else if (token->token == ctxt->key_comma) {
|
|
grp = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_group);
|
|
if (grp == NULL) CRNG_MEM_ERROR0();
|
|
} else
|
|
break;
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
ctxt->insert = NULL;
|
|
xmlParseCRNG_particle(ctxt);
|
|
xmlAddChild(grp, prev);
|
|
xmlAddChild(grp, ctxt->insert);
|
|
prev = grp;
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
}
|
|
if (insert != NULL) {
|
|
xmlAddChild(insert, prev);
|
|
ctxt->insert = insert;
|
|
} else {
|
|
ctxt->insert = prev;
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNG_component:
|
|
* @ctxt: a compact RNG parser context
|
|
*
|
|
* Parse component of the RELAX NG Compact Syntax Appendix A
|
|
*
|
|
* Returns 0 in case of success and -1 in case of error
|
|
*/
|
|
static int
|
|
xmlParseCRNG_component(xmlCRelaxNGParserCtxtPtr ctxt)
|
|
{
|
|
tokenPtr token, tok2;
|
|
xmlNodePtr insert = ctxt->insert;
|
|
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
if (token == NULL)
|
|
return(0);
|
|
if (token->toktype == CRNG_KEYWORD) {
|
|
if (token->token == ctxt->key_start) {
|
|
xmlNodePtr start;
|
|
|
|
start = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_start);
|
|
if (start == NULL) CRNG_MEM_ERROR0();
|
|
if (ctxt->insert != NULL)
|
|
xmlAddChild(ctxt->insert, start);
|
|
ctxt->insert = start;
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
|
|
if ((token->toktype == CRNG_OP) &&
|
|
(token->token == ctxt->key_equal)) {
|
|
} else if ((token->toktype == CRNG_OP) &&
|
|
(token->token == ctxt->key_orequal)) {
|
|
xmlParseCRNG_attribute(ctxt, ctxt->key_combine, NULL,
|
|
BAD_CAST "choice");
|
|
} else if ((token->toktype == CRNG_OP) &&
|
|
(token->token == ctxt->key_andequal)) {
|
|
xmlParseCRNG_attribute(ctxt, ctxt->key_combine, NULL,
|
|
BAD_CAST "interleave");
|
|
} else {
|
|
ERROR("expecting \"=\" or \"&=\" or \"|=\" here")
|
|
return(-1);
|
|
}
|
|
start->properties = ctxt->attrs;
|
|
ctxt->attrs = NULL;
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
xmlParseCRNG_pattern(ctxt);
|
|
|
|
} else if (token->token == ctxt->key_include) {
|
|
TODO
|
|
} else if (token->token == ctxt->key_div) {
|
|
TODO
|
|
} else {
|
|
return(-1);
|
|
}
|
|
} else if (token->toktype == CRNG_IDENTIFIER) {
|
|
xmlNodePtr define;
|
|
const xmlChar *identifier;
|
|
|
|
identifier = token->token;
|
|
tok2 = xmlParseCRNGGetToken(ctxt, 2);
|
|
if ((tok2->toktype == CRNG_OP) &&
|
|
(tok2->token == ctxt->key_equal)) {
|
|
} else if ((tok2->toktype == CRNG_OP) &&
|
|
(tok2->token == ctxt->key_orequal)) {
|
|
xmlParseCRNG_attribute(ctxt, ctxt->key_combine, NULL,
|
|
BAD_CAST "choice");
|
|
} else if ((tok2->toktype == CRNG_OP) &&
|
|
(tok2->token == ctxt->key_andequal)) {
|
|
xmlParseCRNG_attribute(ctxt, ctxt->key_combine, NULL,
|
|
BAD_CAST "interleave");
|
|
} else {
|
|
ERROR("expecting \"=\" or \"&=\" or \"|=\" here")
|
|
return(-1);
|
|
}
|
|
xmlParseCRNGDropTokens(ctxt, 2);
|
|
|
|
define = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_define);
|
|
if (define == NULL) CRNG_MEM_ERROR0();
|
|
define->properties = ctxt->attrs;
|
|
ctxt->attrs = NULL;
|
|
xmlSetProp(define, BAD_CAST "name", identifier);
|
|
if (ctxt->insert != NULL)
|
|
xmlAddChild(ctxt->insert, define);
|
|
ctxt->insert = define;
|
|
xmlParseCRNG_pattern(ctxt);
|
|
} else {
|
|
return(-1);
|
|
}
|
|
ctxt->insert = insert;
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNG_grammar:
|
|
* @ctxt: a compact RNG parser context
|
|
*
|
|
* Parse grammar of the RELAX NG Compact Syntax Appendix A
|
|
*
|
|
* Returns 0 in case of success and -1 in case of error
|
|
*/
|
|
static int
|
|
xmlParseCRNG_grammar(xmlCRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED)
|
|
{
|
|
tokenPtr token;
|
|
int ret;
|
|
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
while (token != NULL) {
|
|
ret = xmlParseCRNG_component(ctxt);
|
|
if (ret != 0)
|
|
break;
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNG_topLevelBody:
|
|
* @ctxt: a compact RNG parser context
|
|
*
|
|
* Parse topLevelBody of the RELAX NG Compact Syntax Appendix A
|
|
*
|
|
* Returns 0 in case of success and -1 in case of error
|
|
*/
|
|
static int
|
|
xmlParseCRNG_topLevelBody(xmlCRelaxNGParserCtxtPtr ctxt)
|
|
{
|
|
tokenPtr token, tok2;
|
|
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
if (token->toktype == CRNG_KEYWORD) {
|
|
if ((token->token == ctxt->key_start) ||
|
|
(token->token == ctxt->key_include) ||
|
|
(token->token == ctxt->key_div)) {
|
|
xmlNodePtr grammar;
|
|
|
|
grammar = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_grammar);
|
|
if (grammar == NULL) CRNG_MEM_ERROR0();
|
|
xmlDocSetRootElement(ctxt->doc, grammar);
|
|
ctxt->insert = grammar;
|
|
|
|
xmlParseCRNG_grammar(ctxt);
|
|
} else {
|
|
xmlParseCRNG_pattern(ctxt);
|
|
}
|
|
} else {
|
|
tok2 = xmlParseCRNGGetToken(ctxt, 2);
|
|
if ((tok2->toktype == CRNG_OP) &&
|
|
((tok2->token == ctxt->key_equal) ||
|
|
(tok2->token == ctxt->key_orequal) ||
|
|
(tok2->token == ctxt->key_andequal))) {
|
|
xmlNodePtr grammar;
|
|
|
|
grammar = xmlNewNodeEatName(NULL, (xmlChar *) ctxt->key_grammar);
|
|
if (grammar == NULL) CRNG_MEM_ERROR0();
|
|
xmlDocSetRootElement(ctxt->doc, grammar);
|
|
ctxt->insert = grammar;
|
|
|
|
xmlParseCRNG_grammar(ctxt);
|
|
} else {
|
|
xmlParseCRNG_pattern(ctxt);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNG_namespacePrefix:
|
|
* @ctxt: a compact RNG parser context
|
|
*
|
|
* Parse namespacePrefix of the RELAX NG Compact Syntax Appendix A
|
|
*
|
|
* Returns the prefix or NULL in case of error
|
|
*/
|
|
static const xmlChar *
|
|
xmlParseCRNG_namespacePrefix(xmlCRelaxNGParserCtxtPtr ctxt)
|
|
{
|
|
tokenPtr token;
|
|
const xmlChar *prefix = NULL;
|
|
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
if (token->toktype == CRNG_IDENTIFIER) {
|
|
prefix = token->token;
|
|
} else if (token->toktype == CRNG_OP) {
|
|
if ((token->token[0] == '=') && (token->token[1] == 0))
|
|
return(NULL);
|
|
prefix = token->token;
|
|
} else {
|
|
ERROR("Expecting a namespace prefix");
|
|
return(NULL);
|
|
}
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
|
|
if (xmlStrEqual(prefix, BAD_CAST "xmlns")) {
|
|
ERROR("Namespace prefix \"xmlns\" is forbidden");
|
|
}
|
|
return(prefix);
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNG_decl:
|
|
* @ctxt: a compact RNG parser context
|
|
*
|
|
* Parse decl of the RELAX NG Compact Syntax Appendix A
|
|
*
|
|
* Returns 0 in case of success and -1 in case of error
|
|
*/
|
|
static int
|
|
xmlParseCRNG_decl(xmlCRelaxNGParserCtxtPtr ctxt)
|
|
{
|
|
const xmlChar *prefix = NULL;
|
|
const xmlChar *namespace = NULL;
|
|
tokenPtr token;
|
|
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
if (token->toktype != CRNG_KEYWORD) return(-1);
|
|
if (token->token == ctxt->key_default) {
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
if ((token->toktype != CRNG_KEYWORD) ||
|
|
(token->token != ctxt->key_namespace)) {
|
|
ERROR("Expecting keyword \"namespace\" after \"default\"");
|
|
}
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
prefix = xmlParseCRNG_namespacePrefix(ctxt);
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
if ((token->toktype != CRNG_OP) ||
|
|
(token->token[0] != '=') || (token->token[1] != 0)) {
|
|
ERROR("Expecting keyword \"=\" here");
|
|
}
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
if ((token->toktype == CRNG_KEYWORD) &&
|
|
(token->token == ctxt->key_inherit)) {
|
|
namespace = xmlCRelaxNGInherit;
|
|
} else if (token->toktype == CRNG_LITERAL_SEGMENT) {
|
|
namespace = token->token;
|
|
} else {
|
|
ERROR("Expecting an URI or \"inherit\" value");
|
|
}
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
if (namespace != NULL) {
|
|
if (prefix != NULL)
|
|
xmlParseCRNG_bindPrefix(ctxt, prefix, namespace);
|
|
xmlParseCRNG_bindPrefix(ctxt, NULL, namespace);
|
|
}
|
|
} else if (token->token == ctxt->key_namespace) {
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
prefix = xmlParseCRNG_namespacePrefix(ctxt);
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
if ((token->toktype != CRNG_OP) ||
|
|
(token->token[0] != '=') || (token->token[1] != 0)) {
|
|
ERROR("Expecting keyword \"=\" here");
|
|
}
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
if ((token->toktype == CRNG_KEYWORD) &&
|
|
(token->token == ctxt->key_inherit)) {
|
|
namespace = xmlCRelaxNGInherit;
|
|
} else if (token->toktype == CRNG_LITERAL_SEGMENT) {
|
|
namespace = token->token;
|
|
} else {
|
|
ERROR("Expecting an URI or \"inherit\" value");
|
|
}
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
if (namespace != NULL)
|
|
xmlParseCRNG_bindPrefix(ctxt, prefix, namespace);
|
|
} else if (token->token == ctxt->key_datatypes) {
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
if ((token->toktype != CRNG_KEYWORD) &&
|
|
(token->toktype != CRNG_IDENTIFIER)) {
|
|
ERROR("Expecting a datatype prefix identifier here");
|
|
} else
|
|
prefix = token->token;
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
if ((token->toktype != CRNG_OP) ||
|
|
(token->token[0] != '=') || (token->token[1] != 0)) {
|
|
ERROR("Expecting keyword \"=\" here");
|
|
}
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
if (token->toktype == CRNG_LITERAL_SEGMENT) {
|
|
namespace = token->token;
|
|
} else {
|
|
ERROR("Expecting a literal value for the datatype identifier");
|
|
}
|
|
xmlParseCRNGDropTokens(ctxt, 1);
|
|
if ((namespace != NULL) && (prefix != NULL))
|
|
xmlParseCRNG_bindDatatypePrefix(ctxt, prefix, namespace);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNG_preamble:
|
|
* @ctxt: a compact RNG parser context
|
|
*
|
|
* Parse preamble of the RELAX NG Compact Syntax Appendix A
|
|
*
|
|
* Returns 0 in case of success and -1 in case of error
|
|
*/
|
|
static int
|
|
xmlParseCRNG_preamble(xmlCRelaxNGParserCtxtPtr ctxt)
|
|
{
|
|
tokenPtr token;
|
|
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
while (token != NULL) {
|
|
if (token == NULL) return(-1);
|
|
if ((token->toktype == CRNG_KEYWORD) &&
|
|
((token->token == ctxt->key_default) ||
|
|
(token->token == ctxt->key_namespace) ||
|
|
(token->token == ctxt->key_datatypes))) {
|
|
xmlParseCRNG_decl(ctxt);
|
|
} else
|
|
break;
|
|
token = xmlParseCRNGGetToken(ctxt, 1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
* xmlParseCRNG_topLevel:
|
|
* @ctxt: a compact RNG parser context
|
|
*
|
|
* Parse topLevel of the RELAX NG Compact Syntax Appendix A
|
|
*
|
|
* Returns 0 in case of success and -1 in case of error
|
|
*/
|
|
static int
|
|
xmlParseCRNG_topLevel(xmlCRelaxNGParserCtxtPtr ctxt)
|
|
{
|
|
xmlParseCRNG_preamble(ctxt);
|
|
xmlParseCRNG_topLevelBody(ctxt);
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
* xmlConvertCRNG:
|
|
* @schemas: pointer to the text of the compact schemas
|
|
* @len: length of the schemas in bytes (or 0)
|
|
* @encoding: encoding indicated by the context or NULL
|
|
*
|
|
* Compiles the schemas into the equivalent Relax-NG XML structure
|
|
*
|
|
* Returns the xmlDocPtr resulting from the compilation or
|
|
* NULL in case of error
|
|
*/
|
|
xmlDocPtr
|
|
xmlConvertCRNG(const char *schemas, int len, const char *encoding) {
|
|
struct _xmlCRelaxNGParserCtxt ctxt;
|
|
xmlDocPtr ret = NULL;
|
|
|
|
if (schemas == NULL) return(NULL);
|
|
if (len <= 5) len = xmlStrlen((const unsigned char *) schemas);
|
|
if (len <= 0) return(NULL);
|
|
|
|
memset(&ctxt, 0, sizeof(ctxt));
|
|
ctxt.compact = (const unsigned char *) schemas;
|
|
ctxt.cur = (const unsigned char *) schemas;
|
|
ctxt.end = (const unsigned char *) &schemas[len];
|
|
ctxt.dict = xmlDictCreate();
|
|
if (ctxt.dict == NULL)
|
|
return(NULL);
|
|
ctxt.doc = xmlNewDoc(NULL);
|
|
if (ctxt.doc == NULL) {
|
|
xmlDictFree(ctxt.dict);
|
|
return(NULL);
|
|
}
|
|
ctxt.doc->dict = ctxt.dict;
|
|
xmlDictReference(ctxt.dict);
|
|
|
|
ctxt.nbTokens = 0;
|
|
ctxt.firstToken = 0;
|
|
ctxt.key_attribute = xmlDictLookup(ctxt.dict, BAD_CAST "attribute", -1);
|
|
ctxt.key_default = xmlDictLookup(ctxt.dict, BAD_CAST "default", -1);
|
|
ctxt.key_datatypes = xmlDictLookup(ctxt.dict, BAD_CAST "datatypes", -1);
|
|
ctxt.key_div = xmlDictLookup(ctxt.dict, BAD_CAST "div", -1);
|
|
ctxt.key_element = xmlDictLookup(ctxt.dict, BAD_CAST "element", -1);
|
|
ctxt.key_empty = xmlDictLookup(ctxt.dict, BAD_CAST "empty", -1);
|
|
ctxt.key_external = xmlDictLookup(ctxt.dict, BAD_CAST "external", -1);
|
|
ctxt.key_grammar = xmlDictLookup(ctxt.dict, BAD_CAST "grammar", -1);
|
|
ctxt.key_include = xmlDictLookup(ctxt.dict, BAD_CAST "include", -1);
|
|
ctxt.key_inherit = xmlDictLookup(ctxt.dict, BAD_CAST "inherit", -1);
|
|
ctxt.key_list = xmlDictLookup(ctxt.dict, BAD_CAST "list", -1);
|
|
ctxt.key_mixed = xmlDictLookup(ctxt.dict, BAD_CAST "mixed", -1);
|
|
ctxt.key_namespace = xmlDictLookup(ctxt.dict, BAD_CAST "namespace", -1);
|
|
ctxt.key_notAllowed = xmlDictLookup(ctxt.dict, BAD_CAST "notAllowed", -1);
|
|
ctxt.key_parent = xmlDictLookup(ctxt.dict, BAD_CAST "parent", -1);
|
|
ctxt.key_start = xmlDictLookup(ctxt.dict, BAD_CAST "start", -1);
|
|
ctxt.key_string = xmlDictLookup(ctxt.dict, BAD_CAST "string", -1);
|
|
ctxt.key_text = xmlDictLookup(ctxt.dict, BAD_CAST "text", -1);
|
|
ctxt.key_token = xmlDictLookup(ctxt.dict, BAD_CAST "token", -1);
|
|
ctxt.key_equal = xmlDictLookup(ctxt.dict, BAD_CAST "=", 1);
|
|
ctxt.key_orequal = xmlDictLookup(ctxt.dict, BAD_CAST "|=", 2);
|
|
ctxt.key_andequal = xmlDictLookup(ctxt.dict, BAD_CAST "&=", 2);
|
|
ctxt.key_combine = xmlDictLookup(ctxt.dict, BAD_CAST "&=", 2);
|
|
ctxt.key_or = xmlDictLookup(ctxt.dict, BAD_CAST "|", 1);
|
|
ctxt.key_comma = xmlDictLookup(ctxt.dict, BAD_CAST ",", 1);
|
|
ctxt.key_and = xmlDictLookup(ctxt.dict, BAD_CAST "&", 1);
|
|
ctxt.key_choice = xmlDictLookup(ctxt.dict, BAD_CAST "choice", -1);
|
|
ctxt.key_group = xmlDictLookup(ctxt.dict, BAD_CAST "group", -1);
|
|
ctxt.key_interleave = xmlDictLookup(ctxt.dict, BAD_CAST "interleave", -1);
|
|
ctxt.key_ref = xmlDictLookup(ctxt.dict, BAD_CAST "ref", 3);
|
|
ctxt.key_define = xmlDictLookup(ctxt.dict, BAD_CAST "define", 6);
|
|
|
|
/* xmlConvertCRNGTokenize(&ctxt); */
|
|
xmlConvertCRNG_topLevel(&ctxt);
|
|
|
|
xmlDictFree(ctxt.dict);
|
|
|
|
ret = ctxt.doc;
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* xmlConvertCRNGFile:
|
|
* @URL: URL or filename for the resource
|
|
* @encoding: encoding indicated by the context or NULL
|
|
*
|
|
* Compiles the schemas into the equivalent Relax-NG XML structure
|
|
*
|
|
* Returns the xmlDocPtr resulting from the compilation or
|
|
* NULL in case of error
|
|
*/
|
|
xmlDocPtr
|
|
xmlConvertCRNGFile(const char *URL, const char *encoding) {
|
|
}
|
|
|
|
#ifdef STANDALONE
|
|
const xmlChar *schemas =
|
|
"# RELAX NG XML syntax specified in compact syntax.\n\
|
|
\n\
|
|
default namespace rng = \"http://relaxng.org/ns/structure/1.0\"\n\
|
|
namespace local = \"\"\n\
|
|
datatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\
|
|
\n\
|
|
start = pattern\n\
|
|
\n\
|
|
pattern =\n\
|
|
element element { (nameQName | nameClass), (common & pattern+) }\n\
|
|
| element attribute { (nameQName | nameClass), (common & pattern?) }\n\
|
|
| element group|interleave|choice|optional\n\
|
|
|zeroOrMore|oneOrMore|list|mixed { common & pattern+ }\n\
|
|
| element ref|parentRef { nameNCName, common }\n\
|
|
| element empty|notAllowed|text { common }\n\
|
|
| element data { type, param*, (common & exceptPattern?) }\n\
|
|
| element value { commonAttributes, type?, xsd:string }\n\
|
|
| element externalRef { href, common }\n\
|
|
| element grammar { common & grammarContent* }\n\
|
|
\n\
|
|
param = element param { commonAttributes, nameNCName, xsd:string }\n\
|
|
\n\
|
|
exceptPattern = element except { common & pattern+ }\n\
|
|
\n\
|
|
grammarContent =\n\
|
|
definition\n\
|
|
| element div { common & grammarContent* }\n\
|
|
| element include { href, (common & includeContent*) }\n\
|
|
\n\
|
|
includeContent =\n\
|
|
definition\n\
|
|
| element div { common & includeContent* }\n\
|
|
\n\
|
|
definition =\n\
|
|
element start { combine?, (common & pattern+) }\n\
|
|
| element define { nameNCName, combine?, (common & pattern+) }\n\
|
|
\n\
|
|
combine = attribute combine { \"choice\" | \"interleave\" }\n\
|
|
\n\
|
|
nameClass =\n\
|
|
element name { commonAttributes, xsd:QName }\n\
|
|
| element anyName { common & exceptNameClass? }\n\
|
|
| element nsName { common & exceptNameClass? }\n\
|
|
| element choice { common & nameClass+ }\n\
|
|
\n\
|
|
exceptNameClass = element except { common & nameClass+ }\n\
|
|
\n\
|
|
nameQName = attribute name { xsd:QName }\n\
|
|
nameNCName = attribute name { xsd:NCName }\n\
|
|
href = attribute href { xsd:anyURI }\n\
|
|
type = attribute type { xsd:NCName }\n\
|
|
\n\
|
|
common = commonAttributes, foreignElement*\n\
|
|
\n\
|
|
commonAttributes =\n\
|
|
attribute ns { xsd:string }?,\n\
|
|
attribute datatypeLibrary { xsd:anyURI }?,\n\
|
|
foreignAttribute*\n\
|
|
\n\
|
|
foreignElement = element * - rng:* { (anyAttribute | text | anyElement)* }\n\
|
|
foreignAttribute = attribute * - (rng:*|local:*) { text }\n\
|
|
anyElement = element * { (anyAttribute | text | anyElement)* }\n\
|
|
anyAttribute = attribute * { text }\n\
|
|
";
|
|
|
|
int main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
|
|
xmlDocPtr res;
|
|
|
|
res = xmlConvertCRNG(schemas, -1);
|
|
if (res != NULL) {
|
|
xmlDocFormatDump(stdout, res, 1);
|
|
xmlFreeDoc(res);
|
|
}
|
|
return(0);
|
|
}
|
|
#endif
|
|
#define bottom_rngparser
|
|
#include "elfgcchack.h"
|