mirror of
https://gitlab.gnome.org/GNOME/libxml2.git
synced 2024-12-25 23:21:26 +03:00
83c6aeef49
Pass RelaxNG structured error handler to XML parser. Handle malloc failure from xmlRaiseError. Remove argument from memory error handler. Use xmlRaiseMemoryError. Don't use xmlGenericError. Remove TODO macro.
1589 lines
46 KiB
C
1589 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 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
|