patch Charles Bozeman for validation of all the date, time, and duration
* xmlschemastypes.c: patch Charles Bozeman for validation of
all the date, time, and duration types
* test/schemas/dur_0* result/schemas/dur_0*: associated tests
* configure.in: fixed an error pointed by an user
* xml2-config.in: fixed an error pointed by an user
Daniel
diff --git a/ChangeLog b/ChangeLog
index c83cafa..ba6d2a5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+Fri May 3 09:20:41 CEST 2002 Daniel Veillard <daniel@veillard.com>
+
+ * xmlschemastypes.c: patch Charles Bozeman for validation of
+ all the date, time, and duration types
+ * test/schemas/dur_0* result/schemas/dur_0*: associated tests
+ * configure.in: fixed an error pointed by an user
+ * xml2-config.in: fixed an error pointed by an user
+
Wed 01 May 2002 11:29:27 AM PDT Aleksey Sanin <aleksey@aleksey.com>
* include/libxml/xmlIO.h win32/dsp/libxml2.def.src
diff --git a/configure.in b/configure.in
index a026f87..054ed7a 100644
--- a/configure.in
+++ b/configure.in
@@ -425,6 +425,7 @@
echo Disabling XPATH support
with_xptr="no"
with_c14n="no"
+ with_xinclude="no"
WITH_XPATH=0
XPATH_OBJ=
else
diff --git a/result/schemas/dur_0_0 b/result/schemas/dur_0_0
new file mode 100644
index 0000000..5b3a2c9
--- /dev/null
+++ b/result/schemas/dur_0_0
@@ -0,0 +1 @@
+./test/schemas/dur_0.xml validates
diff --git a/result/schemas/dur_0_0.err b/result/schemas/dur_0_0.err
new file mode 100644
index 0000000..d906440
--- /dev/null
+++ b/result/schemas/dur_0_0.err
@@ -0,0 +1,196 @@
+compilation error: file ./test/schemas/dur_0.xsd line 67 element minOccurs
+Restriction restriction 16 has unexpected content
+compilation error
+Schemas: element year2 have both type and subtype
+compilation error
+Schemas: element second1 have both type and subtype
+compilation error
+Schemas: element second2 have both type and subtype
+compilation error
+Schemas: element month1 have both type and subtype
+compilation error
+Schemas: element month2 have both type and subtype
+compilation error
+Schemas: element year1 have both type and subtype
+Type of restriction 15 : ./test/schemas/dur_0.xsd:55 :empty
+Type of restriction 5 : ./test/schemas/dur_0.xsd:14 :empty
+Type of simpletype14 : ./test/schemas/dur_0.xsd:54 :simple
+Type of restriction 16 : ./test/schemas/dur_0.xsd:66 :empty
+Type of choice 3 : ./test/schemas/dur_0.xsd:11 :elements
+Type of simpletype4 : ./test/schemas/dur_0.xsd:13 :simple
+Type of restriction 7 : ./test/schemas/dur_0.xsd:22 :empty
+Type of simpletype6 : ./test/schemas/dur_0.xsd:21 :simple
+Type of restriction 9 : ./test/schemas/dur_0.xsd:30 :empty
+Type of simpletype8 : ./test/schemas/dur_0.xsd:29 :simple
+Type of restriction 11 : ./test/schemas/dur_0.xsd:38 :empty
+Type of sequence 2 : ./test/schemas/dur_0.xsd:10 :elements
+Type of anontype1 : ./test/schemas/dur_0.xsd:9 :elements
+Type of simpletype10 : ./test/schemas/dur_0.xsd:37 :simple
+Type of restriction 13 : ./test/schemas/dur_0.xsd:47 :empty
+Type of MSD : ./test/schemas/dur_0.xsd:65 :simple
+Type of sequence 2 : ./test/schemas/dur_0.xsd:10 :elements
+Type of simpletype12 : ./test/schemas/dur_0.xsd:46 :simple
+Building content model for duration
+Content model of duration:
+ regexp: '(null)'
+7 atoms:
+ 00 atom: string once 'second1'
+ 01 atom: string once 'second2'
+ 02 atom: string once 'month1'
+ 03 atom: string once 'month2'
+ 04 atom: string once 'month3'
+ 05 atom: string once 'year1'
+ 06 atom: string once 'year2'
+10 states:
+ state: 0, 7 transitions:
+ trans: atom 0, to 3
+ trans: atom 1, to 4
+ trans: atom 2, to 5
+ trans: atom 3, to 6
+ trans: atom 4, to 7
+ trans: atom 5, to 8
+ trans: atom 6, to 9
+ state: FINAL 1, 0 transitions:
+ state: 2, 9 transitions:
+ trans: removed
+ trans: count based 0, epsilon to 1
+ trans: counted 0, atom 0, to 3
+ trans: counted 0, atom 1, to 4
+ trans: counted 0, atom 2, to 5
+ trans: counted 0, atom 3, to 6
+ trans: counted 0, atom 4, to 7
+ trans: counted 0, atom 5, to 8
+ trans: counted 0, atom 6, to 9
+ state: 3, 16 transitions:
+ trans: removed
+ trans: counted 0, atom 0, to 3
+ trans: counted 0, atom 1, to 4
+ trans: counted 0, atom 2, to 5
+ trans: counted 0, atom 3, to 6
+ trans: counted 0, atom 4, to 7
+ trans: counted 0, atom 5, to 8
+ trans: counted 0, atom 6, to 9
+ trans: count based 0, epsilon to 1
+ trans: counted 0, atom 0, to 3
+ trans: counted 0, atom 1, to 4
+ trans: counted 0, atom 2, to 5
+ trans: counted 0, atom 3, to 6
+ trans: counted 0, atom 4, to 7
+ trans: counted 0, atom 5, to 8
+ trans: counted 0, atom 6, to 9
+ state: 4, 16 transitions:
+ trans: removed
+ trans: counted 0, atom 0, to 3
+ trans: counted 0, atom 1, to 4
+ trans: counted 0, atom 2, to 5
+ trans: counted 0, atom 3, to 6
+ trans: counted 0, atom 4, to 7
+ trans: counted 0, atom 5, to 8
+ trans: counted 0, atom 6, to 9
+ trans: count based 0, epsilon to 1
+ trans: counted 0, atom 0, to 3
+ trans: counted 0, atom 1, to 4
+ trans: counted 0, atom 2, to 5
+ trans: counted 0, atom 3, to 6
+ trans: counted 0, atom 4, to 7
+ trans: counted 0, atom 5, to 8
+ trans: counted 0, atom 6, to 9
+ state: 5, 16 transitions:
+ trans: removed
+ trans: counted 0, atom 0, to 3
+ trans: counted 0, atom 1, to 4
+ trans: counted 0, atom 2, to 5
+ trans: counted 0, atom 3, to 6
+ trans: counted 0, atom 4, to 7
+ trans: counted 0, atom 5, to 8
+ trans: counted 0, atom 6, to 9
+ trans: count based 0, epsilon to 1
+ trans: counted 0, atom 0, to 3
+ trans: counted 0, atom 1, to 4
+ trans: counted 0, atom 2, to 5
+ trans: counted 0, atom 3, to 6
+ trans: counted 0, atom 4, to 7
+ trans: counted 0, atom 5, to 8
+ trans: counted 0, atom 6, to 9
+ state: 6, 16 transitions:
+ trans: removed
+ trans: counted 0, atom 0, to 3
+ trans: counted 0, atom 1, to 4
+ trans: counted 0, atom 2, to 5
+ trans: counted 0, atom 3, to 6
+ trans: counted 0, atom 4, to 7
+ trans: counted 0, atom 5, to 8
+ trans: counted 0, atom 6, to 9
+ trans: count based 0, epsilon to 1
+ trans: counted 0, atom 0, to 3
+ trans: counted 0, atom 1, to 4
+ trans: counted 0, atom 2, to 5
+ trans: counted 0, atom 3, to 6
+ trans: counted 0, atom 4, to 7
+ trans: counted 0, atom 5, to 8
+ trans: counted 0, atom 6, to 9
+ state: 7, 16 transitions:
+ trans: removed
+ trans: counted 0, atom 0, to 3
+ trans: counted 0, atom 1, to 4
+ trans: counted 0, atom 2, to 5
+ trans: counted 0, atom 3, to 6
+ trans: counted 0, atom 4, to 7
+ trans: counted 0, atom 5, to 8
+ trans: counted 0, atom 6, to 9
+ trans: count based 0, epsilon to 1
+ trans: counted 0, atom 0, to 3
+ trans: counted 0, atom 1, to 4
+ trans: counted 0, atom 2, to 5
+ trans: counted 0, atom 3, to 6
+ trans: counted 0, atom 4, to 7
+ trans: counted 0, atom 5, to 8
+ trans: counted 0, atom 6, to 9
+ state: 8, 16 transitions:
+ trans: removed
+ trans: counted 0, atom 0, to 3
+ trans: counted 0, atom 1, to 4
+ trans: counted 0, atom 2, to 5
+ trans: counted 0, atom 3, to 6
+ trans: counted 0, atom 4, to 7
+ trans: counted 0, atom 5, to 8
+ trans: counted 0, atom 6, to 9
+ trans: count based 0, epsilon to 1
+ trans: counted 0, atom 0, to 3
+ trans: counted 0, atom 1, to 4
+ trans: counted 0, atom 2, to 5
+ trans: counted 0, atom 3, to 6
+ trans: counted 0, atom 4, to 7
+ trans: counted 0, atom 5, to 8
+ trans: counted 0, atom 6, to 9
+ state: 9, 16 transitions:
+ trans: removed
+ trans: counted 0, atom 0, to 3
+ trans: counted 0, atom 1, to 4
+ trans: counted 0, atom 2, to 5
+ trans: counted 0, atom 3, to 6
+ trans: counted 0, atom 4, to 7
+ trans: counted 0, atom 5, to 8
+ trans: counted 0, atom 6, to 9
+ trans: count based 0, epsilon to 1
+ trans: counted 0, atom 0, to 3
+ trans: counted 0, atom 1, to 4
+ trans: counted 0, atom 2, to 5
+ trans: counted 0, atom 3, to 6
+ trans: counted 0, atom 4, to 7
+ trans: counted 0, atom 5, to 8
+ trans: counted 0, atom 6, to 9
+1 counters:
+ 0: min 0 max 1073741823
+xmlSchemaValidateCallback: second1, second1, second1
+xmlSchemaValidateCallback: second2, second2, second2
+xmlSchemaValidateCallback: second2, second2, second2
+xmlSchemaValidateCallback: month1, month1, month1
+xmlSchemaValidateCallback: month1, month1, month1
+xmlSchemaValidateCallback: month2, month2, month2
+xmlSchemaValidateCallback: year1, year1, year1
+xmlSchemaValidateCallback: year1, year1, year1
+Unimplemented block at xmlschemastypes.c:1253
+xmlSchemaValidateCallback: year2, year2, year2
+xmlSchemaValidateCallback: month3, month3, month3
+Element duration content check succeeded
diff --git a/test/schemas/dur_0.xml b/test/schemas/dur_0.xml
new file mode 100644
index 0000000..9e3d2fa
--- /dev/null
+++ b/test/schemas/dur_0.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<duration>
+ <second1>PT0.9S</second1>
+ <second2>PT0.1S</second2>
+ <second2>PT0.999999S</second2>
+ <month1>P0Y27D</month1>
+ <month1>P27DT23H59M59S</month1>
+ <month2>P0Y</month2>
+ <year1>P366DT23H59M59S</year1>
+ <year1>P12M</year1>
+ <year2>P12M</year2>
+ <month3>PT86400S</month3>
+</duration>
diff --git a/test/schemas/dur_0.xsd b/test/schemas/dur_0.xsd
new file mode 100644
index 0000000..5d33fba
--- /dev/null
+++ b/test/schemas/dur_0.xsd
@@ -0,0 +1,73 @@
+<?xml version="1.0"?>
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <xsd:annotation>
+ <xsd:documentation xml:lang="en">
+ Testing duration data types
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:element name="duration">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:choice minOccurs="1" maxOccurs="unbounded">
+ <xsd:element name="second1" type="xsd:duration">
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:duration">
+ <xsd:maxExclusive value="PT1S"/>
+ <xsd:minExclusive value="PT0.1S"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+ <xsd:element name="second2" type="xsd:duration">
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:duration">
+ <xsd:maxInclusive value="PT1S"/>
+ <xsd:minInclusive value="PT0.1S"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+ <xsd:element name="month1" type="xsd:duration">
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:duration">
+ <xsd:maxExclusive value="P1M"/>
+ <xsd:minExclusive value="P0M"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+ <xsd:element name="month2" type="xsd:duration">
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:duration">
+ <xsd:maxInclusive value="P1M"/>
+ <xsd:minInclusive value="P0M"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+ <xsd:element name="month3" type="MSD"/>
+ <xsd:element name="year1" type="xsd:duration">
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:duration">
+ <xsd:maxExclusive value="P2Y"/>
+ <xsd:minExclusive value="P1Y"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+ <xsd:element name="year2" type="xsd:duration">
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:duration">
+ <xsd:maxInclusive value="P2Y"/>
+ <xsd:minInclusive value="P1Y"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:simpleType name="MSD">
+ <xsd:restriction base="xsd:duration">
+ <xsd:minOccurs value="0"/>
+ <xsd:maxExclusive value="PT24H"/>
+ <xsd:minExclusive value="-PT24H"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+</xsd:schema>
+
diff --git a/xml2-config.in b/xml2-config.in
index 768e336..da31e7f 100644
--- a/xml2-config.in
+++ b/xml2-config.in
@@ -8,7 +8,7 @@
usage()
{
cat <<EOF
-Usage: xml-config [OPTION]
+Usage: xml2-config [OPTION]
Known values for OPTION are:
@@ -38,6 +38,8 @@
case "$1" in
--prefix=*)
prefix=$optarg
+ includedir=$prefix/include
+ libdir=$prefix/lib
;;
--prefix)
diff --git a/xmlschemastypes.c b/xmlschemastypes.c
index 92e71ff..32b9bb6 100644
--- a/xmlschemastypes.c
+++ b/xmlschemastypes.c
@@ -23,6 +23,10 @@
#include <libxml/schemasInternals.h>
#include <libxml/xmlschemastypes.h>
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+
#define DEBUG
#define TODO \
@@ -38,6 +42,15 @@
XML_SCHEMAS_STRING,
XML_SCHEMAS_NMTOKEN,
XML_SCHEMAS_DECIMAL,
+ XML_SCHEMAS_TIME,
+ XML_SCHEMAS_GDAY,
+ XML_SCHEMAS_GMONTH,
+ XML_SCHEMAS_GMONTHDAY,
+ XML_SCHEMAS_GYEAR,
+ XML_SCHEMAS_GYEARMONTH,
+ XML_SCHEMAS_DATE,
+ XML_SCHEMAS_DATETIME,
+ XML_SCHEMAS_DURATION,
XML_SCHEMAS_,
XML_SCHEMAS_XXX
} xmlSchemaValType;
@@ -47,6 +60,29 @@
100000000L, 1000000000L
};
+/* Date value */
+typedef struct _xmlSchemaValDate xmlSchemaValDate;
+typedef xmlSchemaValDate *xmlSchemaValDatePtr;
+struct _xmlSchemaValDate {
+ long year;
+ unsigned int mon :4; /* 1 <= mon <= 12 */
+ unsigned int day :5; /* 1 <= day <= 31 */
+ unsigned int hour :5; /* 0 <= hour <= 23 */
+ unsigned int min :6; /* 0 <= min <= 59 */
+ double sec;
+ int tz_flag :1; /* is tzo explicitely set? */
+ int tzo :11; /* -1440 <= tzo <= 1440 */
+};
+
+/* Duration value */
+typedef struct _xmlSchemaValDuration xmlSchemaValDuration;
+typedef xmlSchemaValDuration *xmlSchemaValDurationPtr;
+struct _xmlSchemaValDuration {
+ long mon; /* mon stores years also */
+ long day;
+ double sec; /* sec stores min and hour also */
+};
+
typedef struct _xmlSchemaValDecimal xmlSchemaValDecimal;
typedef xmlSchemaValDecimal *xmlSchemaValDecimalPtr;
struct _xmlSchemaValDecimal {
@@ -62,6 +98,8 @@
xmlSchemaValType type;
union {
xmlSchemaValDecimal decimal;
+ xmlSchemaValDate date;
+ xmlSchemaValDuration dur;
} value;
};
@@ -72,7 +110,15 @@
static xmlSchemaTypePtr xmlSchemaTypeAnyTypeDef = NULL;
static xmlSchemaTypePtr xmlSchemaTypeAnySimpleTypeDef = NULL;
static xmlSchemaTypePtr xmlSchemaTypeDecimalDef = NULL;
+static xmlSchemaTypePtr xmlSchemaTypeDatetimeDef = NULL;
static xmlSchemaTypePtr xmlSchemaTypeDateDef = NULL;
+static xmlSchemaTypePtr xmlSchemaTypeTimeDef = NULL;
+static xmlSchemaTypePtr xmlSchemaTypeGYearDef = NULL;
+static xmlSchemaTypePtr xmlSchemaTypeGYearMonthDef = NULL;
+static xmlSchemaTypePtr xmlSchemaTypeGDayDef = NULL;
+static xmlSchemaTypePtr xmlSchemaTypeGMonthDayDef = NULL;
+static xmlSchemaTypePtr xmlSchemaTypeGMonthDef = NULL;
+static xmlSchemaTypePtr xmlSchemaTypeDurationDef = NULL;
static xmlSchemaTypePtr xmlSchemaTypePositiveIntegerDef = NULL;
static xmlSchemaTypePtr xmlSchemaTypeNonNegativeIntegerDef = NULL;
static xmlSchemaTypePtr xmlSchemaTypeNmtoken = NULL;
@@ -118,6 +164,14 @@
xmlSchemaTypeAnySimpleTypeDef = xmlSchemaInitBasicType("anySimpleType");
xmlSchemaTypeDecimalDef = xmlSchemaInitBasicType("decimal");
xmlSchemaTypeDateDef = xmlSchemaInitBasicType("date");
+ xmlSchemaTypeDatetimeDef = xmlSchemaInitBasicType("dateTime");
+ xmlSchemaTypeTimeDef = xmlSchemaInitBasicType("time");
+ xmlSchemaTypeGYearDef = xmlSchemaInitBasicType("gYear");
+ xmlSchemaTypeGYearMonthDef = xmlSchemaInitBasicType("gYearMonth");
+ xmlSchemaTypeGMonthDef = xmlSchemaInitBasicType("gMonth");
+ xmlSchemaTypeGMonthDayDef = xmlSchemaInitBasicType("gMonthDay");
+ xmlSchemaTypeGDayDef = xmlSchemaInitBasicType("gDay");
+ xmlSchemaTypeDurationDef = xmlSchemaInitBasicType("duration");
xmlSchemaTypePositiveIntegerDef = xmlSchemaInitBasicType("positiveInteger");
xmlSchemaTypeNonNegativeIntegerDef =
xmlSchemaInitBasicType("nonNegativeInteger");
@@ -190,6 +244,645 @@
return(NULL);
return((xmlSchemaTypePtr) xmlHashLookup2(xmlSchemaTypesBank, name, ns));
}
+
+/****************************************************************
+ * *
+ * Convenience macros and functions *
+ * *
+ ****************************************************************/
+
+#define IS_TZO_CHAR(c) \
+ ((c == 0) || (c == 'Z') || (c == '+') || (c == '-'))
+
+#define VALID_YEAR(yr) (yr != 0)
+#define VALID_MONTH(mon) ((mon >= 1) && (mon <= 12))
+/* VALID_DAY should only be used when month is unknown */
+#define VALID_DAY(day) ((day >= 1) && (day <= 31))
+#define VALID_HOUR(hr) ((hr >= 0) && (hr <= 23))
+#define VALID_MIN(min) ((min >= 0) && (min <= 59))
+#define VALID_SEC(sec) ((sec >= 0) && (sec < 60))
+#define VALID_TZO(tzo) ((tzo > -1440) && (tzo < 1440))
+#define IS_LEAP(y) \
+ (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
+
+static const long daysInMonth[12] =
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+static const long daysInMonthLeap[12] =
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+#define VALID_MDAY(dt) \
+ (IS_LEAP(dt->year) ? \
+ (dt->day <= daysInMonthLeap[dt->mon - 1]) : \
+ (dt->day <= daysInMonth[dt->mon - 1]))
+
+#define VALID_DATE(dt) \
+ (VALID_YEAR(dt->year) && VALID_MONTH(dt->mon) && VALID_MDAY(dt))
+
+#define VALID_TIME(dt) \
+ (VALID_HOUR(dt->hour) && VALID_MIN(dt->min) && \
+ VALID_SEC(dt->sec) && VALID_TZO(dt->tzo))
+
+#define VALID_DATETIME(dt) \
+ (VALID_DATE(dt) && VALID_TIME(dt))
+
+#define SECS_PER_MIN (60)
+#define SECS_PER_HOUR (60 * SECS_PER_MIN)
+#define SECS_PER_DAY (24 * SECS_PER_HOUR)
+
+/**
+ * _xmlSchemaParseGYear:
+ * @dt: pointer to a date structure
+ * @str: pointer to the string to analyze
+ *
+ * Parses a xs:gYear without time zone and fills in the appropriate
+ * field of the @dt structure. @str is updated to point just after the
+ * xs:gYear. It is supposed that @dt->year is big enough to contain
+ * the year.
+ *
+ * Returns 0 or the error code
+ */
+static int
+_xmlSchemaParseGYear (xmlSchemaValDatePtr dt, const xmlChar **str) {
+ const xmlChar *cur = *str, *firstChar;
+ int isneg = 0, digcnt = 0;
+
+ if (((*cur < '0') || (*cur > '9')) &&
+ (*cur != '-') && (*cur != '+'))
+ return -1;
+
+ if (*cur == '-') {
+ isneg = 1;
+ cur++;
+ }
+
+ firstChar = cur;
+
+ while ((*cur >= '0') && (*cur <= '9')) {
+ dt->year = dt->year * 10 + (*cur - '0');
+ cur++;
+ digcnt++;
+ }
+
+ /* year must be at least 4 digits (CCYY); over 4
+ * digits cannot have a leading zero. */
+ if ((digcnt < 4) || ((digcnt > 4) && (*firstChar == '0')))
+ return 1;
+
+ if (isneg)
+ dt->year = - dt->year;
+
+ if (!VALID_YEAR(dt->year))
+ return 2;
+
+ *str = cur;
+ return 0;
+}
+
+/**
+ * PARSE_2_DIGITS:
+ * @num: the integer to fill in
+ * @cur: an #xmlChar *
+ * @invalid: an integer
+ *
+ * Parses a 2-digits integer and updates @num with the value. @cur is
+ * updated to point just after the integer.
+ * In case of error, @invalid is set to %TRUE, values of @num and
+ * @cur are undefined.
+ */
+#define PARSE_2_DIGITS(num, cur, invalid) \
+ if ((cur[0] < '0') || (cur[0] > '9') || \
+ (cur[1] < '0') || (cur[1] > '9')) \
+ invalid = 1; \
+ else \
+ num = (cur[0] - '0') * 10 + (cur[1] - '0'); \
+ cur += 2;
+
+/**
+ * PARSE_FLOAT:
+ * @num: the double to fill in
+ * @cur: an #xmlChar *
+ * @invalid: an integer
+ *
+ * Parses a float and updates @num with the value. @cur is
+ * updated to point just after the float. The float must have a
+ * 2-digits integer part and may or may not have a decimal part.
+ * In case of error, @invalid is set to %TRUE, values of @num and
+ * @cur are undefined.
+ */
+#define PARSE_FLOAT(num, cur, invalid) \
+ PARSE_2_DIGITS(num, cur, invalid); \
+ if (!invalid && (*cur == '.')) { \
+ double mult = 1; \
+ cur++; \
+ if ((*cur < '0') || (*cur > '9')) \
+ invalid = 1; \
+ while ((*cur >= '0') && (*cur <= '9')) { \
+ mult /= 10; \
+ num += (*cur - '0') * mult; \
+ cur++; \
+ } \
+ }
+
+/**
+ * _xmlSchemaParseGMonth:
+ * @dt: pointer to a date structure
+ * @str: pointer to the string to analyze
+ *
+ * Parses a xs:gMonth without time zone and fills in the appropriate
+ * field of the @dt structure. @str is updated to point just after the
+ * xs:gMonth.
+ *
+ * Returns 0 or the error code
+ */
+static int
+_xmlSchemaParseGMonth (xmlSchemaValDatePtr dt, const xmlChar **str) {
+ const xmlChar *cur = *str;
+ int ret = 0;
+
+ PARSE_2_DIGITS(dt->mon, cur, ret);
+ if (ret != 0)
+ return ret;
+
+ if (!VALID_MONTH(dt->mon))
+ return 2;
+
+ *str = cur;
+ return 0;
+}
+
+/**
+ * _xmlSchemaParseGDay:
+ * @dt: pointer to a date structure
+ * @str: pointer to the string to analyze
+ *
+ * Parses a xs:gDay without time zone and fills in the appropriate
+ * field of the @dt structure. @str is updated to point just after the
+ * xs:gDay.
+ *
+ * Returns 0 or the error code
+ */
+static int
+_xmlSchemaParseGDay (xmlSchemaValDatePtr dt, const xmlChar **str) {
+ const xmlChar *cur = *str;
+ int ret = 0;
+
+ PARSE_2_DIGITS(dt->day, cur, ret);
+ if (ret != 0)
+ return ret;
+
+ if (!VALID_DAY(dt->day))
+ return 2;
+
+ *str = cur;
+ return 0;
+}
+
+/**
+ * _xmlSchemaParseTime:
+ * @dt: pointer to a date structure
+ * @str: pointer to the string to analyze
+ *
+ * Parses a xs:time without time zone and fills in the appropriate
+ * fields of the @dt structure. @str is updated to point just after the
+ * xs:time.
+ * In case of error, values of @dt fields are undefined.
+ *
+ * Returns 0 or the error code
+ */
+static int
+_xmlSchemaParseTime (xmlSchemaValDatePtr dt, const xmlChar **str) {
+ const xmlChar *cur = *str;
+ unsigned int hour = 0; /* use temp var in case str is not xs:time */
+ int ret = 0;
+
+ PARSE_2_DIGITS(hour, cur, ret);
+ if (ret != 0)
+ return ret;
+
+ if (*cur != ':')
+ return 1;
+ cur++;
+
+ /* the ':' insures this string is xs:time */
+ dt->hour = hour;
+
+ PARSE_2_DIGITS(dt->min, cur, ret);
+ if (ret != 0)
+ return ret;
+
+ if (*cur != ':')
+ return 1;
+ cur++;
+
+ PARSE_FLOAT(dt->sec, cur, ret);
+ if (ret != 0)
+ return ret;
+
+ if (!VALID_TIME(dt))
+ return 2;
+
+ *str = cur;
+ return 0;
+}
+
+/**
+ * _xmlSchemaParseTimeZone:
+ * @dt: pointer to a date structure
+ * @str: pointer to the string to analyze
+ *
+ * Parses a time zone without time zone and fills in the appropriate
+ * field of the @dt structure. @str is updated to point just after the
+ * time zone.
+ *
+ * Returns 0 or the error code
+ */
+static int
+_xmlSchemaParseTimeZone (xmlSchemaValDatePtr dt, const xmlChar **str) {
+ const xmlChar *cur = *str;
+ int ret = 0;
+
+ if (str == NULL)
+ return -1;
+
+ switch (*cur) {
+ case 0:
+ dt->tz_flag = 0;
+ dt->tzo = 0;
+ break;
+
+ case 'Z':
+ dt->tz_flag = 1;
+ dt->tzo = 0;
+ cur++;
+ break;
+
+ case '+':
+ case '-': {
+ int isneg = 0, tmp = 0;
+ isneg = (*cur == '-');
+
+ cur++;
+
+ PARSE_2_DIGITS(tmp, cur, ret);
+ if (ret != 0)
+ return ret;
+ if (!VALID_HOUR(tmp))
+ return 2;
+
+ if (*cur != ':')
+ return 1;
+ cur++;
+
+ dt->tzo = tmp * 60;
+
+ PARSE_2_DIGITS(tmp, cur, ret);
+ if (ret != 0)
+ return ret;
+ if (!VALID_MIN(tmp))
+ return 2;
+
+ dt->tzo += tmp;
+ if (isneg)
+ dt->tzo = - dt->tzo;
+
+ if (!VALID_TZO(dt->tzo))
+ return 2;
+
+ break;
+ }
+ default:
+ return 1;
+ }
+
+ *str = cur;
+ return 0;
+}
+
+/****************************************************************
+ * *
+ * XML Schema Dates/Times Datatypes Handling *
+ * *
+ ****************************************************************/
+
+/**
+ * PARSE_DIGITS:
+ * @num: the integer to fill in
+ * @cur: an #xmlChar *
+ * @num_type: an integer flag
+ *
+ * Parses a digits integer and updates @num with the value. @cur is
+ * updated to point just after the integer.
+ * In case of error, @num_type is set to -1, values of @num and
+ * @cur are undefined.
+ */
+#define PARSE_DIGITS(num, cur, num_type) \
+ if ((*cur < '0') || (*cur > '9')) \
+ num_type = -1; \
+ else \
+ while ((*cur >= '0') && (*cur <= '9')) { \
+ num = num * 10 + (*cur - '0'); \
+ cur++; \
+ }
+
+/**
+ * PARSE_NUM:
+ * @num: the double to fill in
+ * @cur: an #xmlChar *
+ * @num_type: an integer flag
+ *
+ * Parses a float or integer and updates @num with the value. @cur is
+ * updated to point just after the number. If the number is a float,
+ * then it must have an integer part and a decimal part; @num_type will
+ * be set to 1. If there is no decimal part, @num_type is set to zero.
+ * In case of error, @num_type is set to -1, values of @num and
+ * @cur are undefined.
+ */
+#define PARSE_NUM(num, cur, num_type) \
+ num = 0; \
+ PARSE_DIGITS(num, cur, num_type); \
+ if (!num_type && (*cur == '.')) { \
+ double mult = 1; \
+ cur++; \
+ if ((*cur < '0') || (*cur > '9')) \
+ num_type = -1; \
+ else \
+ num_type = 1; \
+ while ((*cur >= '0') && (*cur <= '9')) { \
+ mult /= 10; \
+ num += (*cur - '0') * mult; \
+ cur++; \
+ } \
+ }
+
+/**
+ * xmlSchemaParseDates:
+ * @type: the predefined type
+ * @dateTime: string to analyze
+ * @val: the return computed value
+ *
+ * Check that @dateTime conforms to the lexical space of one of the date types.
+ * if true a value is computed and returned in @val.
+ *
+ * Returns 0 if this validates, a positive error code number otherwise
+ * and -1 in case of internal or API error.
+ */
+static int
+xmlSchemaParseDates (xmlSchemaTypePtr type, const xmlChar *dateTime,
+ xmlSchemaValPtr *val) {
+ xmlSchemaValPtr dt;
+ int ret;
+ const xmlChar *cur = dateTime;
+
+#define RETURN_TYPE_IF_VALID(t) \
+ if (IS_TZO_CHAR(*cur)) { \
+ ret = _xmlSchemaParseTimeZone(&(dt->value.date), &cur); \
+ if (ret == 0) { \
+ if (*cur != 0) \
+ goto error; \
+ dt->type = t; \
+ if (val != NULL) \
+ *val = dt; \
+ return 0; \
+ } \
+ }
+
+ if (dateTime == NULL)
+ return -1;
+
+ if ((*cur != '-') && (*cur < '0') && (*cur > '9'))
+ return 1;
+
+ dt = xmlSchemaNewValue(XML_SCHEMAS_UNKNOWN);
+ if (dt == NULL)
+ return -1;
+
+ if ((cur[0] == '-') && (cur[1] == '-')) {
+ /*
+ * It's an incomplete date (xs:gMonthDay, xs:gMonth or
+ * xs:gDay)
+ */
+ cur += 2;
+
+ /* is it an xs:gDay? */
+ if (*cur == '-') {
+ ++cur;
+ ret = _xmlSchemaParseGDay(&(dt->value.date), &cur);
+ if (ret != 0)
+ goto error;
+
+ RETURN_TYPE_IF_VALID(XML_SCHEMAS_GDAY);
+
+ goto error;
+ }
+
+ /*
+ * it should be an xs:gMonthDay or xs:gMonth
+ */
+ ret = _xmlSchemaParseGMonth(&(dt->value.date), &cur);
+ if (ret != 0)
+ goto error;
+
+ if (*cur != '-')
+ goto error;
+ cur++;
+
+ /* is it an xs:gMonth? */
+ if (*cur == '-') {
+ cur++;
+ RETURN_TYPE_IF_VALID(XML_SCHEMAS_GMONTH);
+ goto error;
+ }
+
+ /* it should be an xs:gMonthDay */
+ ret = _xmlSchemaParseGDay(&(dt->value.date), &cur);
+ if (ret != 0)
+ goto error;
+
+ RETURN_TYPE_IF_VALID(XML_SCHEMAS_GMONTHDAY);
+
+ goto error;
+ }
+
+ /*
+ * It's a right-truncated date or an xs:time.
+ * Try to parse an xs:time then fallback on right-truncated dates.
+ */
+ if ((*cur >= '0') && (*cur <= '9')) {
+ ret = _xmlSchemaParseTime(&(dt->value.date), &cur);
+ if (ret == 0) {
+ /* it's an xs:time */
+ RETURN_TYPE_IF_VALID(XML_SCHEMAS_TIME);
+ }
+ }
+
+ /* fallback on date parsing */
+ cur = dateTime;
+
+ ret = _xmlSchemaParseGYear(&(dt->value.date), &cur);
+ if (ret != 0)
+ goto error;
+
+ /* is it an xs:gYear? */
+ RETURN_TYPE_IF_VALID(XML_SCHEMAS_GYEAR);
+
+ if (*cur != '-')
+ goto error;
+ cur++;
+
+ ret = _xmlSchemaParseGMonth(&(dt->value.date), &cur);
+ if (ret != 0)
+ goto error;
+
+ /* is it an xs:gYearMonth? */
+ RETURN_TYPE_IF_VALID(XML_SCHEMAS_GYEARMONTH);
+
+ if (*cur != '-')
+ goto error;
+ cur++;
+
+ ret = _xmlSchemaParseGDay(&(dt->value.date), &cur);
+ if ((ret != 0) || !VALID_DATE((&(dt->value.date))))
+ goto error;
+
+ /* is it an xs:date? */
+ RETURN_TYPE_IF_VALID(XML_SCHEMAS_DATE);
+
+ if (*cur != 'T')
+ goto error;
+ cur++;
+
+ /* it should be an xs:dateTime */
+ ret = _xmlSchemaParseTime(&(dt->value.date), &cur);
+ if (ret != 0)
+ goto error;
+
+ ret = _xmlSchemaParseTimeZone(&(dt->value.date), &cur);
+ if ((ret != 0) || (*cur != 0) || !VALID_DATETIME((&(dt->value.date))))
+ goto error;
+
+ dt->type = XML_SCHEMAS_DATETIME;
+
+ if (val != NULL)
+ *val = dt;
+
+ return 0;
+
+error:
+ if (dt != NULL)
+ xmlSchemaFreeValue(dt);
+ return 1;
+}
+
+/**
+ * xmlSchemaParseDuration:
+ * @type: the predefined type
+ * @duration: string to analyze
+ * @val: the return computed value
+ *
+ * Check that @duration conforms to the lexical space of the duration type.
+ * if true a value is computed and returned in @val.
+ *
+ * Returns 0 if this validates, a positive error code number otherwise
+ * and -1 in case of internal or API error.
+ */
+static int
+xmlSchemaParseDuration (xmlSchemaTypePtr type, const xmlChar *duration,
+ xmlSchemaValPtr *val) {
+ const xmlChar *cur = duration;
+ xmlSchemaValPtr dur;
+ int isneg = 0;
+ unsigned int seq = 0;
+
+ if (duration == NULL)
+ return -1;
+
+ if (*cur == '-') {
+ isneg = 1;
+ cur++;
+ }
+
+ /* duration must start with 'P' (after sign) */
+ if (*cur++ != 'P')
+ return 1;
+
+ dur = xmlSchemaNewValue(XML_SCHEMAS_DURATION);
+ if (dur == NULL)
+ return -1;
+
+ while (*cur != 0) {
+ double num;
+ int num_type = 0; /* -1 = invalid, 0 = int, 1 = floating */
+ const xmlChar desig[] = {'Y', 'M', 'D', 'H', 'M', 'S'};
+ const double multi[] = { 0.0, 0.0, 86400.0, 3600.0, 60.0, 1.0, 0.0};
+
+ /* input string should be empty or invalid date/time item */
+ if (seq >= sizeof(desig))
+ goto error;
+
+ /* T designator must be present for time items */
+ if (*cur == 'T') {
+ if (seq <= 3) {
+ seq = 3;
+ cur++;
+ } else
+ return 1;
+ } else if (seq == 3)
+ goto error;
+
+ /* parse the number portion of the item */
+ PARSE_NUM(num, cur, num_type);
+
+ if ((num_type == -1) || (*cur == 0))
+ goto error;
+
+ /* update duration based on item type */
+ while (seq < sizeof(desig)) {
+ if (*cur == desig[seq]) {
+
+ /* verify numeric type; only seconds can be float */
+ if ((num_type != 0) && (seq < (sizeof(desig)-1)))
+ goto error;
+
+ switch (seq) {
+ case 0:
+ dur->value.dur.mon = (long)num * 12;
+ break;
+ case 1:
+ dur->value.dur.mon += (long)num;
+ break;
+ default:
+ /* convert to seconds using multiplier */
+ dur->value.dur.sec += num * multi[seq];
+ seq++;
+ break;
+ }
+
+ break; /* exit loop */
+ }
+ /* no date designators found? */
+ if (++seq == 3)
+ goto error;
+ }
+ cur++;
+ }
+
+ if (isneg) {
+ dur->value.dur.mon = -dur->value.dur.mon;
+ dur->value.dur.day = -dur->value.dur.day;
+ dur->value.dur.sec = -dur->value.dur.sec;
+ }
+
+ if (val != NULL)
+ *val = dur;
+
+ return 0;
+
+error:
+ if (dur != NULL)
+ xmlSchemaFreeValue(dur);
+ return 1;
+}
+
/**
* xmlSchemaValidatePredefinedType:
* @type: the predefined type
@@ -263,45 +956,17 @@
}
}
return(0);
- } else if (type == xmlSchemaTypeDateDef) {
- const xmlChar *cur = value;
- if (cur == NULL)
- return(1);
- if (*cur == '-')
- cur++;
- if ((*cur < '0') || (*cur > '9'))
- return(1);
- if ((*cur < '0') || (*cur > '9'))
- return(1);
- if ((*cur < '0') || (*cur > '9'))
- return(1);
- if ((*cur < '0') || (*cur > '9'))
- return(1);
- while ((*cur >= '0') && (*cur <= '9'))
- cur++;
- if (*cur != '-')
- return(1);
- cur++;
- if ((*cur != '0') && (*cur != '1'))
- return(1);
- if ((*cur == '0') && (cur[1] == '0'))
- return(1);
- if ((*cur == '1') && ((cur[1] < '0') || (cur[1] > '2')))
- return(1);
- cur += 2;
- if (*cur != '-')
- return(1);
- cur++;
- if ((*cur < '0') || (*cur > '3'))
- return(1);
- if ((*cur == '0') && (cur[1] == '0'))
- return(1);
- if ((*cur == '3') && ((cur[1] < '0') || (cur[1] > '1')))
- return(1);
- cur += 2;
- if (*cur != 0)
- return(1);
- return(0);
+ } else if (type == xmlSchemaTypeDurationDef) {
+ return xmlSchemaParseDuration(type, value, val);
+ } else if ((type == xmlSchemaTypeDatetimeDef) ||
+ (type == xmlSchemaTypeTimeDef) ||
+ (type == xmlSchemaTypeDateDef) ||
+ (type == xmlSchemaTypeGYearDef) ||
+ (type == xmlSchemaTypeGYearMonthDef) ||
+ (type == xmlSchemaTypeGMonthDef) ||
+ (type == xmlSchemaTypeGMonthDayDef) ||
+ (type == xmlSchemaTypeGDayDef)) {
+ return xmlSchemaParseDates(type, value, val);
} else if (type == xmlSchemaTypePositiveIntegerDef) {
const xmlChar *cur = value;
unsigned long base = 0;
@@ -417,6 +1082,87 @@
}
/**
+ * xmlSchemaCompareDurations:
+ * @x: a first duration value
+ * @y: a second duration value
+ *
+ * Compare 2 durations
+ *
+ * Returns -1 if x < y, 0 if x == y, 1 if x > y, 2 if x <> y, and -2 in
+ * case of error
+ */
+static int
+xmlSchemaCompareDurations(xmlSchemaValPtr x, xmlSchemaValPtr y)
+{
+ long carry, mon, day;
+ double sec;
+ long xmon, xday, myear, lyear, minday, maxday;
+ static const long dayRange [2][12] = {
+ { 0, 28, 59, 89, 120, 150, 181, 212, 242, 273, 303, 334, },
+ { 0, 31, 62, 92, 123, 153, 184, 215, 245, 276, 306, 337} };
+
+ if ((x == NULL) || (y == NULL))
+ return NULL;
+
+ /* months */
+ mon = x->value.dur.mon - y->value.dur.mon;
+
+ /* seconds */
+ sec = x->value.dur.sec - y->value.dur.sec;
+ carry = (long)sec / SECS_PER_DAY;
+ sec -= (double)(carry * SECS_PER_DAY);
+
+ /* days */
+ day = x->value.dur.day - y->value.dur.day + carry;
+
+ /* easy test */
+ if (mon == 0) {
+ if (day == 0)
+ if (sec == 0.0)
+ return 0;
+ else if (sec < 0.0)
+ return -1;
+ else
+ return 1;
+ else if (day < 0)
+ return -1;
+ else
+ return 1;
+ }
+
+ if (mon > 0) {
+ if ((day >= 0) && (sec >= 0.0))
+ return 1;
+ else {
+ xmon = mon;
+ xday = -day;
+ }
+ } else if ((day <= 0) && (sec <= 0.0)) {
+ return -1;
+ } else {
+ xmon = -mon;
+ xday = day;
+ }
+
+ myear = xmon / 12;
+ lyear = myear / 4;
+ minday = (myear * 365) + (lyear != 0 ? lyear - 1 : 0);
+ maxday = (myear * 365) + (lyear != 0 ? lyear + 1 : 0);
+
+ xmon = xmon % 12;
+ minday += dayRange[0][xmon];
+ maxday += dayRange[1][xmon];
+
+ if (maxday < xday)
+ return 1;
+ else if (minday > xday)
+ return -1;
+
+ /* indeterminate */
+ return 0;
+}
+
+/**
* xmlSchemaCompareValues:
* @x: a first value
* @y: a second value
@@ -438,6 +1184,11 @@
return(xmlSchemaCompareDecimals(x, y));
else
return(-2);
+ case XML_SCHEMAS_DURATION:
+ if (y->type == XML_SCHEMAS_DURATION)
+ return(xmlSchemaCompareDurations(x, y));
+ else
+ return(-2);
default:
TODO
}
@@ -481,6 +1232,36 @@
return(0);
TODO /* error code */
return(1);
+ case XML_SCHEMA_FACET_MAXINCLUSIVE:
+ ret = xmlSchemaCompareValues(val, facet->val);
+ if (ret == -2) {
+ TODO /* error code */
+ return(-1);
+ }
+ if ((ret == -1) || (ret == 0))
+ return(0);
+ TODO /* error code */
+ return(1);
+ case XML_SCHEMA_FACET_MINEXCLUSIVE:
+ ret = xmlSchemaCompareValues(val, facet->val);
+ if (ret == -2) {
+ TODO /* error code */
+ return(-1);
+ }
+ if (ret == 1)
+ return(0);
+ TODO /* error code */
+ return(1);
+ case XML_SCHEMA_FACET_MININCLUSIVE:
+ ret = xmlSchemaCompareValues(val, facet->val);
+ if (ret == -2) {
+ TODO /* error code */
+ return(-1);
+ }
+ if ((ret == 1) || (ret == 0))
+ return(0);
+ TODO /* error code */
+ return(1);
case XML_SCHEMA_FACET_WHITESPACE:
TODO /* whitespaces */
return(0);