diff --git a/ChangeLog b/ChangeLog index eaedc793..68f16354 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +Sun Sep 7 11:11:45 CEST 2003 Daniel Veillard + + * dict.c: allow to give -1 for undefined length in lookups + * include/libxml/parser.h parser.c parserInternals.c testSAX.c: + first round of work on the new SAX2 interfaces, the API + will change but commiting before changing for historical + reference. + Sat Sep 6 10:55:01 PTD 2003 William Brack * SAX2.c, xmlIO.c: fixed bug #121210 (callback to sax->error, diff --git a/dict.c b/dict.c index 2dd759d1..aa299768 100644 --- a/dict.c +++ b/dict.c @@ -256,7 +256,7 @@ xmlDictFree(xmlDictPtr dict) { * xmlDictLookup: * @dict: the dictionnary * @name: the name of the userdata - * @len: the length of the name + * @len: the length of the name, if -1 it is recomputed * * Add the @name to the hash @dict if not present. * @@ -269,7 +269,12 @@ xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len) { xmlDictEntryPtr insert; const xmlChar *ret; - if ((dict == NULL) || (name == NULL) || (len <= 0)) + if ((dict == NULL) || (name == NULL)) + return(NULL); + + if (len < 0) + len = xmlStrlen(name); + if (len <= 0) return(NULL); /* diff --git a/include/libxml/parser.h b/include/libxml/parser.h index e52013b9..8ab2033c 100644 --- a/include/libxml/parser.h +++ b/include/libxml/parser.h @@ -240,6 +240,20 @@ struct _xmlParserCtxt { const xmlChar * *atts; /* array for the attributes callbacks */ int maxatts; /* the size of the array */ int docdict; /* use strings from dict to build tree */ + + /* + * pre-interned strings + */ + const xmlChar *str_xml; + const xmlChar *str_xmlns; + + /* + * Everything below is related to the new SAX mode + */ + int sax2; /* operating in the new SAX mode */ + int nsNr; /* the number of inherited namespaces */ + int nsMax; /* the size of the arrays */ + const xmlChar * *nsTab; /* the array of prefix/namespace name */ }; /** diff --git a/parser.c b/parser.c index c2c4ff6b..dafebb63 100644 --- a/parser.c +++ b/parser.c @@ -85,6 +85,8 @@ */ #define MAX_DEPTH 1024 +#define SAX2 1 + #define XML_PARSER_BIG_BUFFER_SIZE 300 #define XML_PARSER_BUFFER_SIZE 100 @@ -125,6 +127,114 @@ xmlParseBalancedChunkMemoryInternal(xmlParserCtxtPtr oldctxt, xmlEntityPtr xmlParseStringEntityRef(xmlParserCtxtPtr ctxt, const xmlChar ** str); +#ifdef SAX2 +/** + * nsPush: + * @ctxt: an XML parser context + * @prefix: the namespace prefix or NULL + * @URL: the namespace name + * + * Pushes a new parser namespace on top of the ns stack + * + * Returns -1 in case of error, the index in the stack otherwise + */ +static int +nsPush(xmlParserCtxtPtr ctxt, const xmlChar *prefix, const xmlChar *URL) +{ + if ((ctxt->nsMax == 0) || (ctxt->nsTab == NULL)) { + ctxt->nsMax = 10; + ctxt->nsNr = 0; + ctxt->nsTab = (const xmlChar **) + xmlMalloc(ctxt->nsMax * sizeof(xmlChar *)); + if (ctxt->nsTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); + ctxt->nsMax = 0; + return (-1); + } + } else if (ctxt->nsNr >= ctxt->nsMax) { + ctxt->nsMax *= 2; + ctxt->nsTab = (const xmlChar **) + xmlRealloc(ctxt->nsTab, + ctxt->nsMax * sizeof(ctxt->nsTab[0])); + if (ctxt->nsTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); + ctxt->nsMax /= 2; + return (-1); + } + } + ctxt->nsTab[ctxt->nsNr++] = prefix; + ctxt->nsTab[ctxt->nsNr++] = URL; + return (ctxt->nsNr); +} +/** + * nsPop: + * @ctxt: an XML parser context + * @nr: the number to pop + * + * Pops the top @nr parser prefix/namespace from the ns stack + * + * Returns the number of namespaces removed + */ +static int +nsPop(xmlParserCtxtPtr ctxt, int nr) +{ + int i; + + if (ctxt->nsTab == NULL) return(0); + if (ctxt->nsNr < nr) { + xmlGenericError(xmlGenericErrorContext, "Pbm popping %d NS\n", nr); + nr = ctxt->nsNr; + } + if (ctxt->nsNr <= 0) + return (0); + + for (i = 0;i < nr;i++) { + ctxt->nsNr--; + ctxt->nsTab[ctxt->nsNr] = NULL; + } + return(nr); +} +#endif + +static int +xmlCtxtGrowAttrs(xmlParserCtxtPtr ctxt, int nr) { + const xmlChar **atts; + int maxatts; + + if (ctxt->atts == NULL) { + maxatts = 44; /* allow for 10 attrs by default */ + atts = (const xmlChar **) + xmlMalloc(maxatts * sizeof(xmlChar *)); + if (atts == NULL) { + xmlGenericError(xmlGenericErrorContext, + "malloc of %ld byte failed\n", + maxatts * (long)sizeof(xmlChar *)); + ctxt->errNo = XML_ERR_NO_MEMORY; + ctxt->instate = XML_PARSER_EOF; + ctxt->disableSAX = 1; + return(-1); + } + ctxt->atts = atts; + ctxt->maxatts = maxatts; + } else if (nr + 4 > ctxt->maxatts) { + maxatts = (nr + 4) * 2; + atts = (const xmlChar **) xmlRealloc((void *) ctxt->atts, + maxatts * sizeof(const xmlChar *)); + if (atts == NULL) { + xmlGenericError(xmlGenericErrorContext, + "realloc of %ld byte failed\n", + maxatts * (long)sizeof(xmlChar *)); + ctxt->errNo = XML_ERR_NO_MEMORY; + ctxt->instate = XML_PARSER_EOF; + ctxt->disableSAX = 1; + return(-1); + } + ctxt->atts = atts; + ctxt->maxatts = maxatts; + } + return(ctxt->maxatts); +} + /** * inputPush: * @ctxt: an XML parser context @@ -1870,6 +1980,8 @@ xmlSplitQName(xmlParserCtxtPtr ctxt, const xmlChar *name, xmlChar **prefix) { ************************************************************************/ static const xmlChar * xmlParseNameComplex(xmlParserCtxtPtr ctxt); +static xmlChar * xmlParseAttValueInternal(xmlParserCtxtPtr ctxt, int *len); + /** * xmlParseName: * @ctxt: an XML parser context @@ -1935,7 +2047,6 @@ xmlParseName(xmlParserCtxtPtr ctxt) { * parse an XML name and compares for match * (specialized for endtag parsing) * - * * Returns NULL for an illegal name, (xmlChar*) 1 for success * and the name for mismatch */ @@ -1960,7 +2071,8 @@ xmlParseNameAndCompare(xmlParserCtxtPtr ctxt, xmlChar const *other) { } /* failure (or end of input buffer), check with full function */ ret = xmlParseName (ctxt); - if ((ret != NULL) && (xmlStrEqual (ret, other))) { + /* strings coming from the dictionnary direct compare possible */ + if (ret == other) { return (const xmlChar*) 1; } return ret; @@ -2322,80 +2434,10 @@ xmlParseEntityValue(xmlParserCtxtPtr ctxt, xmlChar **orig) { return(ret); } -/** - * xmlParseAttValue: - * @ctxt: an XML parser context - * - * parse a value for an attribute - * Note: the parser won't do substitution of entities here, this - * will be handled later in xmlStringGetNodeList - * - * [10] AttValue ::= '"' ([^<&"] | Reference)* '"' | - * "'" ([^<&'] | Reference)* "'" - * - * 3.3.3 Attribute-Value Normalization: - * Before the value of an attribute is passed to the application or - * checked for validity, the XML processor must normalize it as follows: - * - a character reference is processed by appending the referenced - * character to the attribute value - * - an entity reference is processed by recursively processing the - * replacement text of the entity - * - a whitespace character (#x20, #xD, #xA, #x9) is processed by - * appending #x20 to the normalized value, except that only a single - * #x20 is appended for a "#xD#xA" sequence that is part of an external - * parsed entity or the literal entity value of an internal parsed entity - * - other characters are processed by appending them to the normalized value - * If the declared value is not CDATA, then the XML processor must further - * process the normalized attribute value by discarding any leading and - * trailing space (#x20) characters, and by replacing sequences of space - * (#x20) characters by a single space (#x20) character. - * All attributes for which no declaration has been read should be treated - * by a non-validating parser as if declared CDATA. - * - * Returns the AttValue parsed or NULL. The value has to be freed by the caller. - */ - -xmlChar * -xmlParseAttValueComplex(xmlParserCtxtPtr ctxt); - -xmlChar * -xmlParseAttValue(xmlParserCtxtPtr ctxt) { - xmlChar limit = 0; - const xmlChar *in = NULL; - xmlChar *ret = NULL; - - SHRINK; - GROW; - in = (xmlChar *) CUR_PTR; - if (*in != '"' && *in != '\'') { - ctxt->errNo = XML_ERR_ATTRIBUTE_NOT_STARTED; - if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL)) - ctxt->sax->error(ctxt->userData, "AttValue: \" or ' expected\n"); - ctxt->wellFormed = 0; - if (ctxt->recovery == 0) ctxt->disableSAX = 1; - return(NULL); - } - ctxt->instate = XML_PARSER_ATTRIBUTE_VALUE; - limit = *in; - ++in; - - while (*in != limit && *in >= 0x20 && *in <= 0x7f && - *in != '&' && *in != '<' - ) { - ++in; - } - if (*in != limit) { - return xmlParseAttValueComplex(ctxt); - } - ++in; - ret = xmlStrndup (CUR_PTR + 1, in - CUR_PTR - 2); - CUR_PTR = in; - return ret; -} - /** * xmlParseAttValueComplex: * @ctxt: an XML parser context + * @len: the resulting attribute len * * parse a value for an attribute, this is the fallback function * of xmlParseAttValue() when the attribute parsing requires handling @@ -2403,8 +2445,8 @@ xmlParseAttValue(xmlParserCtxtPtr ctxt) { * * Returns the AttValue parsed or NULL. The value has to be freed by the caller. */ -xmlChar * -xmlParseAttValueComplex(xmlParserCtxtPtr ctxt) { +static xmlChar * +xmlParseAttValueComplex(xmlParserCtxtPtr ctxt, int *attlen) { xmlChar limit = 0; xmlChar *buf = NULL; int len = 0; @@ -2566,9 +2608,49 @@ xmlParseAttValueComplex(xmlParserCtxtPtr ctxt) { if (ctxt->recovery == 0) ctxt->disableSAX = 1; } else NEXT; + if (attlen != NULL) *attlen = len; return(buf); } +/** + * xmlParseAttValue: + * @ctxt: an XML parser context + * + * parse a value for an attribute + * Note: the parser won't do substitution of entities here, this + * will be handled later in xmlStringGetNodeList + * + * [10] AttValue ::= '"' ([^<&"] | Reference)* '"' | + * "'" ([^<&'] | Reference)* "'" + * + * 3.3.3 Attribute-Value Normalization: + * Before the value of an attribute is passed to the application or + * checked for validity, the XML processor must normalize it as follows: + * - a character reference is processed by appending the referenced + * character to the attribute value + * - an entity reference is processed by recursively processing the + * replacement text of the entity + * - a whitespace character (#x20, #xD, #xA, #x9) is processed by + * appending #x20 to the normalized value, except that only a single + * #x20 is appended for a "#xD#xA" sequence that is part of an external + * parsed entity or the literal entity value of an internal parsed entity + * - other characters are processed by appending them to the normalized value + * If the declared value is not CDATA, then the XML processor must further + * process the normalized attribute value by discarding any leading and + * trailing space (#x20) characters, and by replacing sequences of space + * (#x20) characters by a single space (#x20) character. + * All attributes for which no declaration has been read should be treated + * by a non-validating parser as if declared CDATA. + * + * Returns the AttValue parsed or NULL. The value has to be freed by the caller. + */ + + +xmlChar * +xmlParseAttValue(xmlParserCtxtPtr ctxt) { + return(xmlParseAttValueInternal(ctxt, NULL)); +} + /** * xmlParseSystemLiteral: * @ctxt: an XML parser context @@ -2841,7 +2923,7 @@ get_more: SHRINK; GROW; in = ctxt->input->cur; - } while ((*in >= 0x20) && (*in <= 0x7F)); + } while ((*in >= 0x20) && (*in <= 0x7F) || (*in == 0x09)); nbchar = 0; } ctxt->input->line = line; @@ -6664,7 +6746,6 @@ xmlParseStartTag(xmlParserCtxtPtr ctxt) { goto failed; } } - /* * Add the pair to atts */ @@ -6764,8 +6845,10 @@ failed: } /** - * xmlParseEndTagInternal: + * xmlParseEndTag1: * @ctxt: an XML parser context + * @line: line of the start tag + * @nsNr: number of namespaces on the start tag * * parse an end of tag * @@ -6777,7 +6860,7 @@ failed: */ static void -xmlParseEndTagInternal(xmlParserCtxtPtr ctxt, int line) { +xmlParseEndTag1(xmlParserCtxtPtr ctxt, int line) { const xmlChar *name; const xmlChar *oldname; @@ -6863,7 +6946,696 @@ xmlParseEndTagInternal(xmlParserCtxtPtr ctxt, int line) { void xmlParseEndTag(xmlParserCtxtPtr ctxt) { - xmlParseEndTagInternal(ctxt, 0); + xmlParseEndTag1(ctxt, 0); +} + +/************************************************************************ + * * + * SAX 2 specific operations * + * * + ************************************************************************/ + +static const xmlChar * +xmlParseNCNameComplex(xmlParserCtxtPtr ctxt) { + int len = 0, l; + int c; + int count = 0; + + /* + * Handler for more complex cases + */ + GROW; + c = CUR_CHAR(l); + if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */ + (!IS_LETTER(c) && (c != '_') && + (c != ':'))) { + return(NULL); + } + + while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */ + ((IS_LETTER(c)) || (IS_DIGIT(c)) || + (c == '.') || (c == '-') || + (c == '_') || (c == ':') || + (IS_COMBINING(c)) || + (IS_EXTENDER(c)))) { + if (count++ > 100) { + count = 0; + GROW; + } + len += l; + NEXTL(l); + c = CUR_CHAR(l); + } + return(xmlDictLookup(ctxt->dict, ctxt->input->cur - len, len)); +} + +/* + * xmlGetNamespace: + * @ctxt: an XML parser context + * @prefix: the prefix to lookup + * + * Lookup the namespace name for the @prefix (which ca be NULL) + * The prefix must come from the @ctxt->dict dictionnary + * + * Returns the namespace name or NULL if not bound + */ +static const xmlChar * +xmlGetNamespace(xmlParserCtxtPtr ctxt, const xmlChar *prefix) { + int i; + + for (i = ctxt->nsNr - 2;i >= 0;i-=2) + if (ctxt->nsTab[i] == prefix) + return(ctxt->nsTab[i + 1]); + return(NULL); +} + +/** + * xmlParseNCName: + * @ctxt: an XML parser context + * + * parse an XML name. + * + * [4NS] NCNameChar ::= Letter | Digit | '.' | '-' | '_' | + * CombiningChar | Extender + * + * [5NS] NCName ::= (Letter | '_') (NCNameChar)* + * + * Returns the Name parsed or NULL + */ + +static const xmlChar * +xmlParseNCName(xmlParserCtxtPtr ctxt) { + const xmlChar *in; + const xmlChar *ret; + int count = 0; + + /* + * Accelerator for simple ASCII names + */ + in = ctxt->input->cur; + if (((*in >= 0x61) && (*in <= 0x7A)) || + ((*in >= 0x41) && (*in <= 0x5A)) || + (*in == '_')) { + in++; + while (((*in >= 0x61) && (*in <= 0x7A)) || + ((*in >= 0x41) && (*in <= 0x5A)) || + ((*in >= 0x30) && (*in <= 0x39)) || + (*in == '_') || (*in == '-') || + (*in == '.')) + in++; + if ((*in > 0) && (*in < 0x80)) { + count = in - ctxt->input->cur; + ret = xmlDictLookup(ctxt->dict, ctxt->input->cur, count); + ctxt->input->cur = in; + ctxt->nbChars += count; + ctxt->input->col += count; + if (ret == NULL) { + if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL)) + ctxt->sax->error(ctxt->userData, + "XML parser: out of memory\n"); + ctxt->errNo = XML_ERR_NO_MEMORY; + ctxt->instate = XML_PARSER_EOF; + ctxt->disableSAX = 1; + } + return(ret); + } + } + return(xmlParseNCNameComplex(ctxt)); +} + +/** + * xmlParseQName: + * @ctxt: an XML parser context + * @prefix: pointer to store the prefix part + * + * parse an XML Namespace QName + * + * [6] QName ::= (Prefix ':')? LocalPart + * [7] Prefix ::= NCName + * [8] LocalPart ::= NCName + * + * Returns the Name parsed or NULL + */ + +static const xmlChar * +xmlParseQName(xmlParserCtxtPtr ctxt, const xmlChar **prefix) { + const xmlChar *l, *p; + + GROW; + + l = xmlParseNCName(ctxt); + if (l == NULL) return(NULL); + if (CUR == ':') { + NEXT; + p = l; + l = xmlParseNCName(ctxt); + if (l == NULL) { + if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL)) { + ctxt->sax->error(ctxt->userData, + "Failed to parse QName '%s:'\n", p); + } + return(NULL); + } + if (CUR == ':') { + if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL)) { + ctxt->sax->error(ctxt->userData, + "Failed to parse QName '%s:%s:'\n", p, l); + } + return(NULL); + } + *prefix = p; + } else + *prefix = NULL; + return(l); +} + +/** + * xmlParseQNameAndCompare: + * @ctxt: an XML parser context + * @name: the localname + * @prefix: the prefix, if any. + * + * parse an XML name and compares for match + * (specialized for endtag parsing) + * + * Returns NULL for an illegal name, (xmlChar*) 1 for success + * and the name for mismatch + */ + +static const xmlChar * +xmlParseQNameAndCompare(xmlParserCtxtPtr ctxt, xmlChar const *name, + xmlChar const *prefix) { + const xmlChar *cmp = name; + const xmlChar *in; + const xmlChar *ret; + const xmlChar *prefix2; + + if (prefix == NULL) return(xmlParseNameAndCompare(ctxt, name)); + + GROW; + in = ctxt->input->cur; + + cmp = prefix; + while (*in != 0 && *in == *cmp) { + ++in; + ++cmp; + } + if ((*cmp == 0) && (*in == ':')) { + in++; + cmp = name; + while (*in != 0 && *in == *cmp) { + ++in; + ++cmp; + } + if (*cmp == 0 && (*in == '>' || IS_BLANK (*in))) { + /* success */ + ctxt->input->cur = in; + return((const xmlChar*) 1); + } + } + /* + * all strings coms from the dictionary, equality can be done directly + */ + ret = xmlParseQName (ctxt, &prefix2); + if ((ret == name) && (prefix == prefix2)) + return((const xmlChar*) 1); + return ret; +} + +/** + * xmlParseAttValueInternal: + * @ctxt: an XML parser context + * @len: attribute len result + * + * parse a value for an attribute. + * NOTE: if no normalization is needed, the routine will return pointers + * directly from the data buffer. + * + * 3.3.3 Attribute-Value Normalization: + * Before the value of an attribute is passed to the application or + * checked for validity, the XML processor must normalize it as follows: + * - a character reference is processed by appending the referenced + * character to the attribute value + * - an entity reference is processed by recursively processing the + * replacement text of the entity + * - a whitespace character (#x20, #xD, #xA, #x9) is processed by + * appending #x20 to the normalized value, except that only a single + * #x20 is appended for a "#xD#xA" sequence that is part of an external + * parsed entity or the literal entity value of an internal parsed entity + * - other characters are processed by appending them to the normalized value + * If the declared value is not CDATA, then the XML processor must further + * process the normalized attribute value by discarding any leading and + * trailing space (#x20) characters, and by replacing sequences of space + * (#x20) characters by a single space (#x20) character. + * All attributes for which no declaration has been read should be treated + * by a non-validating parser as if declared CDATA. + * + * Returns the AttValue parsed or NULL. The value has to be freed by the + * caller if it was copied, this can be detected by val[*len] == 0. + */ + +static xmlChar * +xmlParseAttValueInternal(xmlParserCtxtPtr ctxt, int *len) { + xmlChar limit = 0; + const xmlChar *in = NULL; + xmlChar *ret = NULL; + + GROW; + in = (xmlChar *) CUR_PTR; + if (*in != '"' && *in != '\'') { + ctxt->errNo = XML_ERR_ATTRIBUTE_NOT_STARTED; + if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL)) + ctxt->sax->error(ctxt->userData, "AttValue: \" or ' expected\n"); + ctxt->wellFormed = 0; + if (ctxt->recovery == 0) ctxt->disableSAX = 1; + return(NULL); + } + ctxt->instate = XML_PARSER_ATTRIBUTE_VALUE; + limit = *in; + ++in; + + while (*in != limit && *in >= 0x20 && *in <= 0x7f && + *in != '&' && *in != '<' + ) { + ++in; + } + if (*in != limit) { + return xmlParseAttValueComplex(ctxt, len); + } + ++in; + if (len != NULL) { + *len = in - CUR_PTR - 2; + ret = (xmlChar *) CUR_PTR + 1; + } else { + ret = xmlStrndup (CUR_PTR + 1, in - CUR_PTR - 2); + } + CUR_PTR = in; + return ret; +} + +/** + * xmlParseAttribute2: + * @ctxt: an XML parser context + * @value: a xmlChar ** used to store the value of the attribute + * + * parse an attribute in the new SAX2 framework. + * + * Returns the attribute name, and the value in *value, . + */ + +static const xmlChar * +xmlParseAttribute2(xmlParserCtxtPtr ctxt, const xmlChar **prefix, + xmlChar **value, int *len) { + const xmlChar *name; + xmlChar *val; + + *value = NULL; + GROW; + name = xmlParseQName(ctxt, prefix); + if (name == NULL) { + ctxt->errNo = XML_ERR_NAME_REQUIRED; + if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL)) + ctxt->sax->error(ctxt->userData, "error parsing attribute name\n"); + ctxt->wellFormed = 0; + if (ctxt->recovery == 0) ctxt->disableSAX = 1; + return(NULL); + } + + /* + * read the value + */ + SKIP_BLANKS; + if (RAW == '=') { + NEXT; + SKIP_BLANKS; + val = xmlParseAttValueInternal(ctxt, len); + ctxt->instate = XML_PARSER_CONTENT; + } else { + ctxt->errNo = XML_ERR_ATTRIBUTE_WITHOUT_VALUE; + if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL)) + ctxt->sax->error(ctxt->userData, + "Specification mandate value for attribute %s\n", name); + ctxt->wellFormed = 0; + if (ctxt->recovery == 0) ctxt->disableSAX = 1; + return(NULL); + } + + /* + * Check that xml:lang conforms to the specification + * No more registered as an error, just generate a warning now + * since this was deprecated in XML second edition + */ + if ((ctxt->pedantic) && (xmlStrEqual(name, BAD_CAST "xml:lang"))) { + if (!xmlCheckLanguageID(val)) { + if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL)) + ctxt->sax->warning(ctxt->userData, + "Malformed value for xml:lang : %s\n", val); + } + } + + /* + * Check that xml:space conforms to the specification + */ + if (xmlStrEqual(name, BAD_CAST "xml:space")) { + if (xmlStrEqual(val, BAD_CAST "default")) + *(ctxt->space) = 0; + else if (xmlStrEqual(val, BAD_CAST "preserve")) + *(ctxt->space) = 1; + else { + ctxt->errNo = XML_ERR_ATTRIBUTE_WITHOUT_VALUE; + if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL)) + ctxt->sax->error(ctxt->userData, +"Invalid value \"%s\" for xml:space : \"default\" or \"preserve\" expected\n", + val); + ctxt->wellFormed = 0; + if (ctxt->recovery == 0) ctxt->disableSAX = 1; + } + } + + *value = val; + return(name); +} + +/** + * xmlParseStartTag2: + * @ctxt: an XML parser context + * + * parse a start of tag either for rule element or + * EmptyElement. In both case we don't parse the tag closing chars. + * This routine is called when running SAX2 parsing + * + * [40] STag ::= '<' Name (S Attribute)* S? '>' + * + * [ WFC: Unique Att Spec ] + * No attribute name may appear more than once in the same start-tag or + * empty-element tag. + * + * [44] EmptyElemTag ::= '<' Name (S Attribute)* S? '/>' + * + * [ WFC: Unique Att Spec ] + * No attribute name may appear more than once in the same start-tag or + * empty-element tag. + * + * With namespace: + * + * [NS 8] STag ::= '<' QName (S Attribute)* S? '>' + * + * [NS 10] EmptyElement ::= '<' QName (S Attribute)* S? '/>' + * + * Returns the element name parsed + */ + +static const xmlChar * +xmlParseStartTag2(xmlParserCtxtPtr ctxt, const xmlChar **pref, + const xmlChar **URI) { + const xmlChar *localname; + const xmlChar *prefix; + const xmlChar *attname; + const xmlChar *nsname; + xmlChar *attvalue; + const xmlChar **atts = ctxt->atts; + int nbatts = 0; + int maxatts = ctxt->maxatts; + int i, j, nbNs = 0, attval = 0; + + if (RAW != '<') return(NULL); + NEXT1; + + /* + * NOTE: it is crucial with the SAX2 API to never call SHRINK beyond that + * point since the attribute values may be stored as pointers to + * the buffer and calling SHRINK would destroy them ! + * The Shrinking is only possible once the full set of attribute + * callbacks have been done. + */ + SHRINK; + + localname = xmlParseQName(ctxt, &prefix); + if (localname == NULL) { + ctxt->errNo = XML_ERR_NAME_REQUIRED; + if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL)) + ctxt->sax->error(ctxt->userData, + "xmlParseStartTag: invalid element name\n"); + ctxt->wellFormed = 0; + if (ctxt->recovery == 0) ctxt->disableSAX = 1; + return(NULL); + } + + /* + * Now parse the attributes, it ends up with the ending + * + * (S Attribute)* S? + */ + SKIP_BLANKS; + GROW; + + while ((RAW != '>') && + ((RAW != '/') || (NXT(1) != '>')) && + (IS_CHAR((unsigned int) RAW))) { + const xmlChar *q = CUR_PTR; + unsigned int cons = ctxt->input->consumed; + const xmlChar *aprefix; + int len = -1; + + attname = xmlParseAttribute2(ctxt, &aprefix, &attvalue, &len); + if ((attname != NULL) && (attvalue != NULL)) { + if (len < 0) len = xmlStrlen(attvalue); + if ((attname == ctxt->str_xmlns) && (aprefix == NULL)) { + /* + * TODO: [ WFC: Unique Att Spec ] + */ + if (nsPush(ctxt, NULL, + xmlDictLookup(ctxt->dict, attvalue, len)) > 0) + nbNs++; + if (attvalue[len] == 0) + xmlFree(attvalue); + continue; + } + if (aprefix == ctxt->str_xmlns) { + /* + * TODO: [ WFC: Unique Att Spec ] + */ + if (nsPush(ctxt, attname, + xmlDictLookup(ctxt->dict, attvalue, len)) > 0) + nbNs++; + if (attvalue[len] == 0) + xmlFree(attvalue); + continue; + } + + /* + * Add the pair to atts + */ + if ((atts == NULL) || (nbatts + 4 > maxatts)) { + if (xmlCtxtGrowAttrs(ctxt, nbatts + 4) < 0) { + if (attvalue[len] == 0) + xmlFree(attvalue); + goto failed; + } + maxatts = ctxt->maxatts; + atts = ctxt->atts; + } + atts[nbatts++] = attname; + atts[nbatts++] = aprefix; + atts[nbatts++] = attvalue; + attvalue += len; + atts[nbatts++] = attvalue; + /* + * tag if some deallocation is needed + */ + if (*attvalue != 0) attval = 1; + } else { + if ((attvalue != NULL) && (attvalue[len] == 0)) + xmlFree(attvalue); + } + +failed: + + GROW + if ((RAW == '>') || (((RAW == '/') && (NXT(1) == '>')))) + break; + if (!IS_BLANK(RAW)) { + ctxt->errNo = XML_ERR_SPACE_REQUIRED; + if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL)) + ctxt->sax->error(ctxt->userData, + "attributes construct error\n"); + ctxt->wellFormed = 0; + if (ctxt->recovery == 0) ctxt->disableSAX = 1; + } + SKIP_BLANKS; + if ((cons == ctxt->input->consumed) && (q == CUR_PTR) && + (attname == NULL) && (attvalue == NULL)) { + ctxt->errNo = XML_ERR_INTERNAL_ERROR; + if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL)) + ctxt->sax->error(ctxt->userData, + "xmlParseStartTag: problem parsing attributes\n"); + ctxt->wellFormed = 0; + if (ctxt->recovery == 0) ctxt->disableSAX = 1; + break; + } + SHRINK; + GROW; + } + + nsname = xmlGetNamespace(ctxt, prefix); + if ((prefix != NULL) && (nsname == NULL)) { + if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL)) + ctxt->sax->error(ctxt->userData, + "Namespace prefix %s on %d is not defined\n", prefix, localname); + } + *pref = prefix; + *URI = nsname; + /* + * SAX: Start of Element ! + */ + if ((ctxt->sax != NULL) && (ctxt->sax->startElementNs != NULL) && + (!ctxt->disableSAX)) { + if (nbNs > 0) + ctxt->sax->startElementNs(ctxt->userData, localname, prefix, + nsname, nbNs, &ctxt->nsTab[ctxt->nsNr - 2 * nbNs], + nbatts / 4); + else + ctxt->sax->startElementNs(ctxt->userData, localname, prefix, + nsname, 0, NULL, nbatts / 4); + } + /* + * The attributes callbacks + */ + for (i = 0; i < nbatts;i += 4) { + nsname = xmlGetNamespace(ctxt, atts[i + 1]); + if ((atts[i + 1] != NULL) && (nsname == NULL)) { + if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL)) + ctxt->sax->error(ctxt->userData, + "Namespace prefix %s for %s on %d is not defined\n", + atts[i + 1], atts[i], localname); + } + /* + * [ WFC: Unique Att Spec ] + * No attribute name may appear more than once in the same + * start-tag or empty-element tag. + * As extended by the Namespace in XML REC. + */ + for (j = 0; j < i;j += 4) { + if (atts[i] == atts[j]) { + if (atts[i+1] == atts[j+1]) { + ctxt->errNo = XML_ERR_ATTRIBUTE_REDEFINED; + if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL)) { + if (atts[i+1] == NULL) + ctxt->sax->error(ctxt->userData, + "Attribute %s redefined\n", + atts[i]); + else + ctxt->sax->error(ctxt->userData, + "Attribute %s:%s redefined\n", + atts[i+1], atts[i]); + } + ctxt->wellFormed = 0; + if (ctxt->recovery == 0) ctxt->disableSAX = 1; + goto skip_attribute; + } + if ((nsname != NULL) && + (xmlGetNamespace(ctxt, atts[j + 1]) == nsname)) { + ctxt->sax->error(ctxt->userData, + "Attribute %s in %s redefined\n", + atts[i], nsname); + goto skip_attribute; + } + } + } + if ((ctxt->sax != NULL) && (ctxt->sax->attributeNs != NULL)) + ctxt->sax->attributeNs(ctxt->userData, + atts[i], atts[i + 1], nsname, + atts[i + 2], atts[i + 3] - atts[i + 2]); +skip_attribute: + if ((attval != 0) && (*atts[i+3] == 0)) + xmlFree((xmlChar *) atts[i+3]); + } + + return(localname); +} + +/** + * xmlParseEndTag2: + * @ctxt: an XML parser context + * @line: line of the start tag + * @nsNr: number of namespaces on the start tag + * + * parse an end of tag + * + * [42] ETag ::= '' + * + * With namespace + * + * [NS 9] ETag ::= '' + */ + +static void +xmlParseEndTag2(xmlParserCtxtPtr ctxt, const xmlChar *prefix, + const xmlChar *URI, int line, int nsNr) { + const xmlChar *name; + + GROW; + if ((RAW != '<') || (NXT(1) != '/')) { + ctxt->errNo = XML_ERR_LTSLASH_REQUIRED; + if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL)) + ctxt->sax->error(ctxt->userData, "xmlParseEndTag: 'wellFormed = 0; + if (ctxt->recovery == 0) ctxt->disableSAX = 1; + return; + } + SKIP(2); + + name = xmlParseQNameAndCompare(ctxt, ctxt->name, prefix); + + /* + * We should definitely be at the ending "S? '>'" part + */ + GROW; + SKIP_BLANKS; + if ((!IS_CHAR((unsigned int) RAW)) || (RAW != '>')) { + ctxt->errNo = XML_ERR_GT_REQUIRED; + if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL)) + ctxt->sax->error(ctxt->userData, "End tag : expected '>'\n"); + ctxt->wellFormed = 0; + if (ctxt->recovery == 0) ctxt->disableSAX = 1; + } else + NEXT1; + + /* + * [ WFC: Element Type Match ] + * The Name in an element's end-tag must match the element type in the + * start-tag. + * + */ + if (name != (xmlChar*)1) { + ctxt->errNo = XML_ERR_TAG_NAME_MISMATCH; + if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL)) { + if (name != NULL) { + ctxt->sax->error(ctxt->userData, + "Opening and ending tag mismatch: %s line %d and %s\n", + ctxt->name, line, name); + } else { + ctxt->sax->error(ctxt->userData, + "Ending tag error for: %s line %d\n", ctxt->name, line); + } + + } + ctxt->wellFormed = 0; + if (ctxt->recovery == 0) ctxt->disableSAX = 1; + } + + /* + * SAX: End of Tag + */ + if ((ctxt->sax != NULL) && (ctxt->sax->endElementNs != NULL) && + (!ctxt->disableSAX)) + ctxt->sax->endElementNs(ctxt->userData, ctxt->name, prefix, URI); + + namePop(ctxt); + spacePop(ctxt); + if (nsNr != 0) + nsPop(ctxt, nsNr); + return; } /** @@ -7082,27 +7854,17 @@ xmlParseContent(xmlParserCtxtPtr ctxt) { * The Name in an element's end-tag must match the element type in the * start-tag. * - * [ VC: Element Valid ] - * An element is valid if there is a declaration matching elementdecl - * where the Name matches the element type and one of the following holds: - * - The declaration matches EMPTY and the element has no content. - * - The declaration matches children and the sequence of child elements - * belongs to the language generated by the regular expression in the - * content model, with optional white space (characters matching the - * nonterminal S) between each pair of child elements. - * - The declaration matches Mixed and the content consists of character - * data and child elements whose types match names in the content model. - * - The declaration matches ANY, and the types of any child elements have - * been declared. */ void xmlParseElement(xmlParserCtxtPtr ctxt) { const xmlChar *name; - const xmlChar *oldname; + const xmlChar *prefix; + const xmlChar *URI; xmlParserNodeInfo node_info; int line; xmlNodePtr ret; + int nsNr = ctxt->nsNr; /* Capture start position */ if (ctxt->record_info) { @@ -7117,7 +7879,10 @@ xmlParseElement(xmlParserCtxtPtr ctxt) { spacePush(ctxt, *ctxt->space); line = ctxt->input->line; - name = xmlParseStartTag(ctxt); + if (ctxt->sax2) + name = xmlParseStartTag2(ctxt, &prefix, &URI); + else + name = xmlParseStartTag(ctxt); if (name == NULL) { spacePop(ctxt); return; @@ -7139,16 +7904,19 @@ xmlParseElement(xmlParserCtxtPtr ctxt) { */ if ((RAW == '/') && (NXT(1) == '>')) { SKIP(2); - if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL) && - (!ctxt->disableSAX)) - ctxt->sax->endElement(ctxt->userData, name); - oldname = namePop(ctxt); - spacePop(ctxt); -#ifdef DEBUG_STACK - if (oldname != NULL) { - xmlGenericError(xmlGenericErrorContext,"Close: popped %s\n", oldname); + if (ctxt->sax2) { + if ((ctxt->sax != NULL) && (ctxt->sax->endElementNs != NULL) && + (!ctxt->disableSAX)) + ctxt->sax->endElementNs(ctxt->userData, name, prefix, URI); + } else { + if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL) && + (!ctxt->disableSAX)) + ctxt->sax->endElement(ctxt->userData, name); } -#endif + namePop(ctxt); + spacePop(ctxt); + if (nsNr != ctxt->nsNr) + nsPop(ctxt, ctxt->nsNr - nsNr); if ( ret != NULL && ctxt->record_info ) { node_info.end_pos = ctxt->input->consumed + (CUR_PTR - ctxt->input->base); @@ -7173,13 +7941,10 @@ xmlParseElement(xmlParserCtxtPtr ctxt) { * end of parsing of this node. */ nodePop(ctxt); - oldname = namePop(ctxt); + namePop(ctxt); spacePop(ctxt); -#ifdef DEBUG_STACK - if (oldname != NULL) { - xmlGenericError(xmlGenericErrorContext,"Close: popped %s\n", oldname); - } -#endif + if (nsNr != ctxt->nsNr) + nsPop(ctxt, ctxt->nsNr - nsNr); /* * Capture end position and add node @@ -7210,20 +7975,20 @@ xmlParseElement(xmlParserCtxtPtr ctxt) { * end of parsing of this node. */ nodePop(ctxt); - oldname = namePop(ctxt); + namePop(ctxt); spacePop(ctxt); -#ifdef DEBUG_STACK - if (oldname != NULL) { - xmlGenericError(xmlGenericErrorContext,"Close: popped %s\n", oldname); - } -#endif + if (nsNr != ctxt->nsNr) + nsPop(ctxt, ctxt->nsNr - nsNr); return; } /* * parse the end of tag: 'sax2) + xmlParseEndTag2(ctxt, prefix, URI, line, ctxt->nsNr - nsNr); + else + xmlParseEndTag1(ctxt, line); /* * Capture end position and add node @@ -7806,6 +8571,17 @@ xmlParseDocument(xmlParserCtxtPtr ctxt) { GROW; + /* + * SAX: detecting the level. + */ + if ((ctxt->sax) && + ((ctxt->sax->startElementNs != NULL) || + (ctxt->sax->endElementNs != NULL) || + (ctxt->sax->attributeNs != NULL))) ctxt->sax2 = 1; + + ctxt->str_xml = xmlDictLookup(ctxt->dict, BAD_CAST "xml", 3); + ctxt->str_xmlns = xmlDictLookup(ctxt->dict, BAD_CAST "xmlns", 5); + /* * SAX: beginning of the document processing. */ diff --git a/parserInternals.c b/parserInternals.c index 27e865f7..5328b238 100644 --- a/parserInternals.c +++ b/parserInternals.c @@ -2373,6 +2373,8 @@ xmlFreeParserCtxt(xmlParserCtxtPtr ctxt) if (ctxt->vctxt.nodeTab != NULL) xmlFree(ctxt->vctxt.nodeTab); if (ctxt->atts != NULL) xmlFree((xmlChar * *)ctxt->atts); if (ctxt->dict != NULL) xmlDictFree(ctxt->dict); + if (ctxt->nsTab != NULL) xmlFree(ctxt->nsTab); + #ifdef LIBXML_CATALOG_ENABLED if (ctxt->catalogs != NULL) xmlCatalogFreeLocal(ctxt->catalogs); diff --git a/testSAX.c b/testSAX.c index cf284678..80334d68 100644 --- a/testSAX.c +++ b/testSAX.c @@ -46,6 +46,8 @@ static int push = 0; static int speed = 0; static int noent = 0; static int quiet = 0; +static int nonull = 0; +static int sax2 = 0; static int callbacks = 0; xmlSAXHandler emptySAXHandlerStruct = { @@ -75,8 +77,12 @@ xmlSAXHandler emptySAXHandlerStruct = { NULL, /* xmlParserError */ NULL, /* getParameterEntity */ NULL, /* cdataBlock; */ - NULL, /* externalSubset; */ - 1 + NULL, /* externalSubset; */ + 1, + NULL, + NULL, /* startElementNs */ + NULL, /* endElementNs */ + NULL /* attributeNs */ }; xmlSAXHandlerPtr emptySAXHandler = &emptySAXHandlerStruct; @@ -684,11 +690,159 @@ xmlSAXHandler debugSAXHandlerStruct = { getParameterEntityDebug, cdataBlockDebug, externalSubsetDebug, - 1 + 1, + NULL, + NULL, + NULL, + NULL }; xmlSAXHandlerPtr debugSAXHandler = &debugSAXHandlerStruct; +/* + * SAX2 specific callbacks + */ +/** + * startElementDebug: + * @ctxt: An XML parser context + * @name: The element name + * + * called when an opening tag has been processed. + */ +static void +startElementNsDebug(void *ctx ATTRIBUTE_UNUSED, + const xmlChar *localname, + const xmlChar *prefix, + const xmlChar *URI, + int nb_namespaces, + const xmlChar **namespaces, + int nb_attributes) +{ + int i; + + callbacks++; + if (quiet) + return; + fprintf(stdout, "SAX.startElementNs(%s", (char *) localname); + if (prefix == NULL) + fprintf(stdout, ", NULL"); + else + fprintf(stdout, ", %s", (char *) prefix); + if (URI == NULL) + fprintf(stdout, ", NULL"); + else + fprintf(stdout, ", '%s'", (char *) URI); + fprintf(stdout, ", %d", nb_namespaces); + + if (namespaces != NULL) { + for (i = 0;i < nb_namespaces * 2;i++) { + fprintf(stdout, ", xmlns"); + if (namespaces[i] != NULL) + fprintf(stdout, ":%s", namespaces[i]); + i++; + fprintf(stdout, "='%s'", namespaces[i]); + } + } + fprintf(stdout, ", %d)\n", nb_attributes); +} + +/** + * endElementDebug: + * @ctxt: An XML parser context + * @name: The element name + * + * called when the end of an element has been detected. + */ +static void +endElementNsDebug(void *ctx ATTRIBUTE_UNUSED, + const xmlChar *localname, + const xmlChar *prefix, + const xmlChar *URI) +{ + callbacks++; + if (quiet) + return; + fprintf(stdout, "SAX.endElementNs(%s", (char *) localname); + if (prefix == NULL) + fprintf(stdout, ", NULL"); + else + fprintf(stdout, ", %s", (char *) prefix); + if (URI == NULL) + fprintf(stdout, ", NULL)\n"); + else + fprintf(stdout, ", '%s')\n", (char *) URI); +} + +/** + * attributeNsDebug: + * @ctxt: An XML parser context + * @name: The element name + * + * called when the end of an element has been detected. + */ +static void +attributeNsDebug(void *ctx ATTRIBUTE_UNUSED, + const xmlChar *localname, + const xmlChar *prefix, + const xmlChar *URI, + const xmlChar *value, + int valuelen) +{ + callbacks++; + if (quiet) + return; + fprintf(stdout, "SAX.attributeNs(%s", (char *) localname); + if (prefix == NULL) + fprintf(stdout, ", NULL"); + else + fprintf(stdout, ", %s", (char *) prefix); + if (URI == NULL) + fprintf(stdout, ", NULL"); + else + fprintf(stdout, ", '%s'", (char *) URI); + if (valuelen > 13) + fprintf(stdout, ", %10s..., %d)\n", value, valuelen); + else + fprintf(stdout, ", %s, %d)\n", value, valuelen); +} + +xmlSAXHandler debugSAX2HandlerStruct = { + internalSubsetDebug, + isStandaloneDebug, + hasInternalSubsetDebug, + hasExternalSubsetDebug, + resolveEntityDebug, + getEntityDebug, + entityDeclDebug, + notationDeclDebug, + attributeDeclDebug, + elementDeclDebug, + unparsedEntityDeclDebug, + setDocumentLocatorDebug, + startDocumentDebug, + endDocumentDebug, + NULL, + NULL, + referenceDebug, + charactersDebug, + ignorableWhitespaceDebug, + processingInstructionDebug, + commentDebug, + warningDebug, + errorDebug, + fatalErrorDebug, + getParameterEntityDebug, + cdataBlockDebug, + externalSubsetDebug, + 1, + NULL, + startElementNsDebug, + endElementNsDebug, + attributeNsDebug +}; + +xmlSAXHandlerPtr debugSAX2Handler = &debugSAX2HandlerStruct; + /************************************************************************ * * * Debug * @@ -702,29 +856,31 @@ parseAndPrintFile(char *filename) { if (push) { FILE *f; - /* - * Empty callbacks for checking - */ - f = fopen(filename, "r"); - if (f != NULL) { - int ret; - char chars[10]; - xmlParserCtxtPtr ctxt; + if ((!quiet) && (!nonull)) { + /* + * Empty callbacks for checking + */ + f = fopen(filename, "r"); + if (f != NULL) { + int ret; + char chars[10]; + xmlParserCtxtPtr ctxt; - ret = fread(chars, 1, 4, f); - if (ret > 0) { - ctxt = xmlCreatePushParserCtxt(emptySAXHandler, NULL, - chars, ret, filename); - while ((ret = fread(chars, 1, 3, f)) > 0) { - xmlParseChunk(ctxt, chars, ret, 0); + ret = fread(chars, 1, 4, f); + if (ret > 0) { + ctxt = xmlCreatePushParserCtxt(emptySAXHandler, NULL, + chars, ret, filename); + while ((ret = fread(chars, 1, 3, f)) > 0) { + xmlParseChunk(ctxt, chars, ret, 0); + } + xmlParseChunk(ctxt, chars, 0, 1); + xmlFreeParserCtxt(ctxt); } - xmlParseChunk(ctxt, chars, 0, 1); - xmlFreeParserCtxt(ctxt); + fclose(f); + } else { + xmlGenericError(xmlGenericErrorContext, + "Cannot read file %s\n", filename); } - fclose(f); - } else { - xmlGenericError(xmlGenericErrorContext, - "Cannot read file %s\n", filename); } /* * Debug callback @@ -737,8 +893,12 @@ parseAndPrintFile(char *filename) { ret = fread(chars, 1, 4, f); if (ret > 0) { - ctxt = xmlCreatePushParserCtxt(debugSAXHandler, NULL, - chars, ret, filename); + if (sax2) + ctxt = xmlCreatePushParserCtxt(debugSAX2Handler, NULL, + chars, ret, filename); + else + ctxt = xmlCreatePushParserCtxt(debugSAXHandler, NULL, + chars, ret, filename); while ((ret = fread(chars, 1, 3, f)) > 0) { xmlParseChunk(ctxt, chars, ret, 0); } @@ -756,7 +916,7 @@ parseAndPrintFile(char *filename) { /* * Empty callbacks for checking */ - if (!quiet) { + if ((!quiet) && (!nonull)) { res = xmlSAXUserParseFile(emptySAXHandler, NULL, filename); if (res != 0) { fprintf(stdout, "xmlSAXUserParseFile returned error %d\n", res); @@ -767,7 +927,10 @@ parseAndPrintFile(char *filename) { * Debug callback */ callbacks = 0; - res = xmlSAXUserParseFile(debugSAXHandler, NULL, filename); + if (sax2) + res = xmlSAXUserParseFile(debugSAX2Handler, NULL, filename); + else + res = xmlSAXUserParseFile(debugSAXHandler, NULL, filename); if (res != 0) { fprintf(stdout, "xmlSAXUserParseFile returned error %d\n", res); } @@ -813,6 +976,12 @@ int main(int argc, char **argv) { else if ((!strcmp(argv[i], "-quiet")) || (!strcmp(argv[i], "--quiet"))) quiet++; + else if ((!strcmp(argv[i], "-sax2")) || + (!strcmp(argv[i], "--sax2"))) + sax2++; + else if ((!strcmp(argv[i], "-nonull")) || + (!strcmp(argv[i], "--nonull"))) + nonull++; } if (noent != 0) xmlSubstituteEntitiesDefault(1); for (i = 1; i < argc ; i++) {