blob: 32b9bb639bad8b22ea0df80f9cb15a1a9ceaf3f8 [file] [log] [blame]
Daniel Veillard4255d502002-04-16 15:50:10 +00001/*
2 * schemastypes.c : implementation of the XML Schema Datatypes
3 * definition and validity checking
4 *
5 * See Copyright for the status of this software.
6 *
7 * Daniel Veillard <veillard@redhat.com>
8 */
9
10#define IN_LIBXML
11#include "libxml.h"
12
13#ifdef LIBXML_SCHEMAS_ENABLED
14
15#include <string.h>
16#include <libxml/xmlmemory.h>
17#include <libxml/parser.h>
18#include <libxml/parserInternals.h>
19#include <libxml/hash.h>
20#include <libxml/valid.h>
21
22#include <libxml/xmlschemas.h>
23#include <libxml/schemasInternals.h>
24#include <libxml/xmlschemastypes.h>
25
Daniel Veillard070803b2002-05-03 07:29:38 +000026#ifdef HAVE_MATH_H
27#include <math.h>
28#endif
29
Daniel Veillard4255d502002-04-16 15:50:10 +000030#define DEBUG
31
32#define TODO \
33 xmlGenericError(xmlGenericErrorContext, \
34 "Unimplemented block at %s:%d\n", \
35 __FILE__, __LINE__);
36
37#define XML_SCHEMAS_NAMESPACE_NAME \
38 (const xmlChar *)"http://www.w3.org/2001/XMLSchema"
39
40typedef enum {
41 XML_SCHEMAS_UNKNOWN = 0,
42 XML_SCHEMAS_STRING,
43 XML_SCHEMAS_NMTOKEN,
44 XML_SCHEMAS_DECIMAL,
Daniel Veillard070803b2002-05-03 07:29:38 +000045 XML_SCHEMAS_TIME,
46 XML_SCHEMAS_GDAY,
47 XML_SCHEMAS_GMONTH,
48 XML_SCHEMAS_GMONTHDAY,
49 XML_SCHEMAS_GYEAR,
50 XML_SCHEMAS_GYEARMONTH,
51 XML_SCHEMAS_DATE,
52 XML_SCHEMAS_DATETIME,
53 XML_SCHEMAS_DURATION,
Daniel Veillard4255d502002-04-16 15:50:10 +000054 XML_SCHEMAS_,
55 XML_SCHEMAS_XXX
56} xmlSchemaValType;
57
58unsigned long powten[10] = {
59 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000L,
60 100000000L, 1000000000L
61};
62
Daniel Veillard070803b2002-05-03 07:29:38 +000063/* Date value */
64typedef struct _xmlSchemaValDate xmlSchemaValDate;
65typedef xmlSchemaValDate *xmlSchemaValDatePtr;
66struct _xmlSchemaValDate {
67 long year;
68 unsigned int mon :4; /* 1 <= mon <= 12 */
69 unsigned int day :5; /* 1 <= day <= 31 */
70 unsigned int hour :5; /* 0 <= hour <= 23 */
71 unsigned int min :6; /* 0 <= min <= 59 */
72 double sec;
73 int tz_flag :1; /* is tzo explicitely set? */
74 int tzo :11; /* -1440 <= tzo <= 1440 */
75};
76
77/* Duration value */
78typedef struct _xmlSchemaValDuration xmlSchemaValDuration;
79typedef xmlSchemaValDuration *xmlSchemaValDurationPtr;
80struct _xmlSchemaValDuration {
81 long mon; /* mon stores years also */
82 long day;
83 double sec; /* sec stores min and hour also */
84};
85
Daniel Veillard4255d502002-04-16 15:50:10 +000086typedef struct _xmlSchemaValDecimal xmlSchemaValDecimal;
87typedef xmlSchemaValDecimal *xmlSchemaValDecimalPtr;
88struct _xmlSchemaValDecimal {
89 /* would use long long but not portable */
90 unsigned long base;
91 unsigned int extra;
92 int sign:1;
93 int frac:7;
94 int total:8;
95};
96
97struct _xmlSchemaVal {
98 xmlSchemaValType type;
99 union {
100 xmlSchemaValDecimal decimal;
Daniel Veillard070803b2002-05-03 07:29:38 +0000101 xmlSchemaValDate date;
102 xmlSchemaValDuration dur;
Daniel Veillard4255d502002-04-16 15:50:10 +0000103 } value;
104};
105
106static int xmlSchemaTypesInitialized = 0;
107static xmlHashTablePtr xmlSchemaTypesBank = NULL;
108
109static xmlSchemaTypePtr xmlSchemaTypeStringDef = NULL;
110static xmlSchemaTypePtr xmlSchemaTypeAnyTypeDef = NULL;
111static xmlSchemaTypePtr xmlSchemaTypeAnySimpleTypeDef = NULL;
112static xmlSchemaTypePtr xmlSchemaTypeDecimalDef = NULL;
Daniel Veillard070803b2002-05-03 07:29:38 +0000113static xmlSchemaTypePtr xmlSchemaTypeDatetimeDef = NULL;
Daniel Veillard4255d502002-04-16 15:50:10 +0000114static xmlSchemaTypePtr xmlSchemaTypeDateDef = NULL;
Daniel Veillard070803b2002-05-03 07:29:38 +0000115static xmlSchemaTypePtr xmlSchemaTypeTimeDef = NULL;
116static xmlSchemaTypePtr xmlSchemaTypeGYearDef = NULL;
117static xmlSchemaTypePtr xmlSchemaTypeGYearMonthDef = NULL;
118static xmlSchemaTypePtr xmlSchemaTypeGDayDef = NULL;
119static xmlSchemaTypePtr xmlSchemaTypeGMonthDayDef = NULL;
120static xmlSchemaTypePtr xmlSchemaTypeGMonthDef = NULL;
121static xmlSchemaTypePtr xmlSchemaTypeDurationDef = NULL;
Daniel Veillard4255d502002-04-16 15:50:10 +0000122static xmlSchemaTypePtr xmlSchemaTypePositiveIntegerDef = NULL;
123static xmlSchemaTypePtr xmlSchemaTypeNonNegativeIntegerDef = NULL;
124static xmlSchemaTypePtr xmlSchemaTypeNmtoken = NULL;
125
126/*
127 * xmlSchemaInitBasicType:
128 * @name: the type name
129 *
130 * Initialize one default type
131 */
132static xmlSchemaTypePtr
133xmlSchemaInitBasicType(const char *name) {
134 xmlSchemaTypePtr ret;
135
136 ret = (xmlSchemaTypePtr) xmlMalloc(sizeof(xmlSchemaType));
137 if (ret == NULL) {
138 xmlGenericError(xmlGenericErrorContext,
139 "Could not initilize type %s: out of memory\n", name);
140 return(NULL);
141 }
142 memset(ret, 0, sizeof(xmlSchemaType));
143 ret->name = xmlStrdup((const xmlChar *)name);
144 ret->type = XML_SCHEMA_TYPE_BASIC;
145 ret->contentType = XML_SCHEMA_CONTENT_BASIC;
146 xmlHashAddEntry2(xmlSchemaTypesBank, ret->name,
147 XML_SCHEMAS_NAMESPACE_NAME, ret);
148 return(ret);
149}
150
151/*
152 * xmlSchemaInitTypes:
153 *
154 * Initialize the default XML Schemas type library
155 */
156void
157xmlSchemaInitTypes(void) {
158 if (xmlSchemaTypesInitialized != 0)
159 return;
160 xmlSchemaTypesBank = xmlHashCreate(40);
161
162 xmlSchemaTypeStringDef = xmlSchemaInitBasicType("string");
163 xmlSchemaTypeAnyTypeDef = xmlSchemaInitBasicType("anyType");
164 xmlSchemaTypeAnySimpleTypeDef = xmlSchemaInitBasicType("anySimpleType");
165 xmlSchemaTypeDecimalDef = xmlSchemaInitBasicType("decimal");
166 xmlSchemaTypeDateDef = xmlSchemaInitBasicType("date");
Daniel Veillard070803b2002-05-03 07:29:38 +0000167 xmlSchemaTypeDatetimeDef = xmlSchemaInitBasicType("dateTime");
168 xmlSchemaTypeTimeDef = xmlSchemaInitBasicType("time");
169 xmlSchemaTypeGYearDef = xmlSchemaInitBasicType("gYear");
170 xmlSchemaTypeGYearMonthDef = xmlSchemaInitBasicType("gYearMonth");
171 xmlSchemaTypeGMonthDef = xmlSchemaInitBasicType("gMonth");
172 xmlSchemaTypeGMonthDayDef = xmlSchemaInitBasicType("gMonthDay");
173 xmlSchemaTypeGDayDef = xmlSchemaInitBasicType("gDay");
174 xmlSchemaTypeDurationDef = xmlSchemaInitBasicType("duration");
Daniel Veillard4255d502002-04-16 15:50:10 +0000175 xmlSchemaTypePositiveIntegerDef = xmlSchemaInitBasicType("positiveInteger");
176 xmlSchemaTypeNonNegativeIntegerDef =
177 xmlSchemaInitBasicType("nonNegativeInteger");
178 xmlSchemaTypeNmtoken = xmlSchemaInitBasicType("NMTOKEN");
179
180 xmlSchemaTypesInitialized = 1;
181}
182
183/**
184 * xmlSchemaCleanupTypes:
185 *
186 * Cleanup the default XML Schemas type library
187 */
188void
189xmlSchemaCleanupTypes(void) {
190 if (xmlSchemaTypesInitialized == 0)
191 return;
192 xmlHashFree(xmlSchemaTypesBank, (xmlHashDeallocator) xmlSchemaFreeType);
193 xmlSchemaTypesInitialized = 0;
194}
195
196/**
197 * xmlSchemaNewValue:
198 * @type: the value type
199 *
200 * Allocate a new simple type value
201 *
202 * Returns a pointer to the new value or NULL in case of error
203 */
204static xmlSchemaValPtr
205xmlSchemaNewValue(xmlSchemaValType type) {
206 xmlSchemaValPtr value;
207
208 value = (xmlSchemaValPtr) xmlMalloc(sizeof(xmlSchemaVal));
209 if (value == NULL) {
210 return(NULL);
211 }
212 memset(value, 0, sizeof(xmlSchemaVal));
213 value->type = type;
214 return(value);
215}
216
217/**
218 * xmlSchemaFreeValue:
219 * @value: the value to free
220 *
221 * Cleanup the default XML Schemas type library
222 */
223void
224xmlSchemaFreeValue(xmlSchemaValPtr value) {
225 if (value == NULL)
226 return;
227 xmlFree(value);
228}
229
230/**
231 * xmlSchemaGetPredefinedType:
232 * @name: the type name
233 * @ns: the URI of the namespace usually "http://www.w3.org/2001/XMLSchema"
234 *
235 * Lookup a type in the default XML Schemas type library
236 *
237 * Returns the type if found, NULL otherwise
238 */
239xmlSchemaTypePtr
240xmlSchemaGetPredefinedType(const xmlChar *name, const xmlChar *ns) {
241 if (xmlSchemaTypesInitialized == 0)
242 xmlSchemaInitTypes();
243 if (name == NULL)
244 return(NULL);
245 return((xmlSchemaTypePtr) xmlHashLookup2(xmlSchemaTypesBank, name, ns));
246}
Daniel Veillard070803b2002-05-03 07:29:38 +0000247
248/****************************************************************
249 * *
250 * Convenience macros and functions *
251 * *
252 ****************************************************************/
253
254#define IS_TZO_CHAR(c) \
255 ((c == 0) || (c == 'Z') || (c == '+') || (c == '-'))
256
257#define VALID_YEAR(yr) (yr != 0)
258#define VALID_MONTH(mon) ((mon >= 1) && (mon <= 12))
259/* VALID_DAY should only be used when month is unknown */
260#define VALID_DAY(day) ((day >= 1) && (day <= 31))
261#define VALID_HOUR(hr) ((hr >= 0) && (hr <= 23))
262#define VALID_MIN(min) ((min >= 0) && (min <= 59))
263#define VALID_SEC(sec) ((sec >= 0) && (sec < 60))
264#define VALID_TZO(tzo) ((tzo > -1440) && (tzo < 1440))
265#define IS_LEAP(y) \
266 (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
267
268static const long daysInMonth[12] =
269 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
270static const long daysInMonthLeap[12] =
271 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
272
273#define VALID_MDAY(dt) \
274 (IS_LEAP(dt->year) ? \
275 (dt->day <= daysInMonthLeap[dt->mon - 1]) : \
276 (dt->day <= daysInMonth[dt->mon - 1]))
277
278#define VALID_DATE(dt) \
279 (VALID_YEAR(dt->year) && VALID_MONTH(dt->mon) && VALID_MDAY(dt))
280
281#define VALID_TIME(dt) \
282 (VALID_HOUR(dt->hour) && VALID_MIN(dt->min) && \
283 VALID_SEC(dt->sec) && VALID_TZO(dt->tzo))
284
285#define VALID_DATETIME(dt) \
286 (VALID_DATE(dt) && VALID_TIME(dt))
287
288#define SECS_PER_MIN (60)
289#define SECS_PER_HOUR (60 * SECS_PER_MIN)
290#define SECS_PER_DAY (24 * SECS_PER_HOUR)
291
292/**
293 * _xmlSchemaParseGYear:
294 * @dt: pointer to a date structure
295 * @str: pointer to the string to analyze
296 *
297 * Parses a xs:gYear without time zone and fills in the appropriate
298 * field of the @dt structure. @str is updated to point just after the
299 * xs:gYear. It is supposed that @dt->year is big enough to contain
300 * the year.
301 *
302 * Returns 0 or the error code
303 */
304static int
305_xmlSchemaParseGYear (xmlSchemaValDatePtr dt, const xmlChar **str) {
306 const xmlChar *cur = *str, *firstChar;
307 int isneg = 0, digcnt = 0;
308
309 if (((*cur < '0') || (*cur > '9')) &&
310 (*cur != '-') && (*cur != '+'))
311 return -1;
312
313 if (*cur == '-') {
314 isneg = 1;
315 cur++;
316 }
317
318 firstChar = cur;
319
320 while ((*cur >= '0') && (*cur <= '9')) {
321 dt->year = dt->year * 10 + (*cur - '0');
322 cur++;
323 digcnt++;
324 }
325
326 /* year must be at least 4 digits (CCYY); over 4
327 * digits cannot have a leading zero. */
328 if ((digcnt < 4) || ((digcnt > 4) && (*firstChar == '0')))
329 return 1;
330
331 if (isneg)
332 dt->year = - dt->year;
333
334 if (!VALID_YEAR(dt->year))
335 return 2;
336
337 *str = cur;
338 return 0;
339}
340
341/**
342 * PARSE_2_DIGITS:
343 * @num: the integer to fill in
344 * @cur: an #xmlChar *
345 * @invalid: an integer
346 *
347 * Parses a 2-digits integer and updates @num with the value. @cur is
348 * updated to point just after the integer.
349 * In case of error, @invalid is set to %TRUE, values of @num and
350 * @cur are undefined.
351 */
352#define PARSE_2_DIGITS(num, cur, invalid) \
353 if ((cur[0] < '0') || (cur[0] > '9') || \
354 (cur[1] < '0') || (cur[1] > '9')) \
355 invalid = 1; \
356 else \
357 num = (cur[0] - '0') * 10 + (cur[1] - '0'); \
358 cur += 2;
359
360/**
361 * PARSE_FLOAT:
362 * @num: the double to fill in
363 * @cur: an #xmlChar *
364 * @invalid: an integer
365 *
366 * Parses a float and updates @num with the value. @cur is
367 * updated to point just after the float. The float must have a
368 * 2-digits integer part and may or may not have a decimal part.
369 * In case of error, @invalid is set to %TRUE, values of @num and
370 * @cur are undefined.
371 */
372#define PARSE_FLOAT(num, cur, invalid) \
373 PARSE_2_DIGITS(num, cur, invalid); \
374 if (!invalid && (*cur == '.')) { \
375 double mult = 1; \
376 cur++; \
377 if ((*cur < '0') || (*cur > '9')) \
378 invalid = 1; \
379 while ((*cur >= '0') && (*cur <= '9')) { \
380 mult /= 10; \
381 num += (*cur - '0') * mult; \
382 cur++; \
383 } \
384 }
385
386/**
387 * _xmlSchemaParseGMonth:
388 * @dt: pointer to a date structure
389 * @str: pointer to the string to analyze
390 *
391 * Parses a xs:gMonth without time zone and fills in the appropriate
392 * field of the @dt structure. @str is updated to point just after the
393 * xs:gMonth.
394 *
395 * Returns 0 or the error code
396 */
397static int
398_xmlSchemaParseGMonth (xmlSchemaValDatePtr dt, const xmlChar **str) {
399 const xmlChar *cur = *str;
400 int ret = 0;
401
402 PARSE_2_DIGITS(dt->mon, cur, ret);
403 if (ret != 0)
404 return ret;
405
406 if (!VALID_MONTH(dt->mon))
407 return 2;
408
409 *str = cur;
410 return 0;
411}
412
413/**
414 * _xmlSchemaParseGDay:
415 * @dt: pointer to a date structure
416 * @str: pointer to the string to analyze
417 *
418 * Parses a xs:gDay without time zone and fills in the appropriate
419 * field of the @dt structure. @str is updated to point just after the
420 * xs:gDay.
421 *
422 * Returns 0 or the error code
423 */
424static int
425_xmlSchemaParseGDay (xmlSchemaValDatePtr dt, const xmlChar **str) {
426 const xmlChar *cur = *str;
427 int ret = 0;
428
429 PARSE_2_DIGITS(dt->day, cur, ret);
430 if (ret != 0)
431 return ret;
432
433 if (!VALID_DAY(dt->day))
434 return 2;
435
436 *str = cur;
437 return 0;
438}
439
440/**
441 * _xmlSchemaParseTime:
442 * @dt: pointer to a date structure
443 * @str: pointer to the string to analyze
444 *
445 * Parses a xs:time without time zone and fills in the appropriate
446 * fields of the @dt structure. @str is updated to point just after the
447 * xs:time.
448 * In case of error, values of @dt fields are undefined.
449 *
450 * Returns 0 or the error code
451 */
452static int
453_xmlSchemaParseTime (xmlSchemaValDatePtr dt, const xmlChar **str) {
454 const xmlChar *cur = *str;
455 unsigned int hour = 0; /* use temp var in case str is not xs:time */
456 int ret = 0;
457
458 PARSE_2_DIGITS(hour, cur, ret);
459 if (ret != 0)
460 return ret;
461
462 if (*cur != ':')
463 return 1;
464 cur++;
465
466 /* the ':' insures this string is xs:time */
467 dt->hour = hour;
468
469 PARSE_2_DIGITS(dt->min, cur, ret);
470 if (ret != 0)
471 return ret;
472
473 if (*cur != ':')
474 return 1;
475 cur++;
476
477 PARSE_FLOAT(dt->sec, cur, ret);
478 if (ret != 0)
479 return ret;
480
481 if (!VALID_TIME(dt))
482 return 2;
483
484 *str = cur;
485 return 0;
486}
487
488/**
489 * _xmlSchemaParseTimeZone:
490 * @dt: pointer to a date structure
491 * @str: pointer to the string to analyze
492 *
493 * Parses a time zone without time zone and fills in the appropriate
494 * field of the @dt structure. @str is updated to point just after the
495 * time zone.
496 *
497 * Returns 0 or the error code
498 */
499static int
500_xmlSchemaParseTimeZone (xmlSchemaValDatePtr dt, const xmlChar **str) {
501 const xmlChar *cur = *str;
502 int ret = 0;
503
504 if (str == NULL)
505 return -1;
506
507 switch (*cur) {
508 case 0:
509 dt->tz_flag = 0;
510 dt->tzo = 0;
511 break;
512
513 case 'Z':
514 dt->tz_flag = 1;
515 dt->tzo = 0;
516 cur++;
517 break;
518
519 case '+':
520 case '-': {
521 int isneg = 0, tmp = 0;
522 isneg = (*cur == '-');
523
524 cur++;
525
526 PARSE_2_DIGITS(tmp, cur, ret);
527 if (ret != 0)
528 return ret;
529 if (!VALID_HOUR(tmp))
530 return 2;
531
532 if (*cur != ':')
533 return 1;
534 cur++;
535
536 dt->tzo = tmp * 60;
537
538 PARSE_2_DIGITS(tmp, cur, ret);
539 if (ret != 0)
540 return ret;
541 if (!VALID_MIN(tmp))
542 return 2;
543
544 dt->tzo += tmp;
545 if (isneg)
546 dt->tzo = - dt->tzo;
547
548 if (!VALID_TZO(dt->tzo))
549 return 2;
550
551 break;
552 }
553 default:
554 return 1;
555 }
556
557 *str = cur;
558 return 0;
559}
560
561/****************************************************************
562 * *
563 * XML Schema Dates/Times Datatypes Handling *
564 * *
565 ****************************************************************/
566
567/**
568 * PARSE_DIGITS:
569 * @num: the integer to fill in
570 * @cur: an #xmlChar *
571 * @num_type: an integer flag
572 *
573 * Parses a digits integer and updates @num with the value. @cur is
574 * updated to point just after the integer.
575 * In case of error, @num_type is set to -1, values of @num and
576 * @cur are undefined.
577 */
578#define PARSE_DIGITS(num, cur, num_type) \
579 if ((*cur < '0') || (*cur > '9')) \
580 num_type = -1; \
581 else \
582 while ((*cur >= '0') && (*cur <= '9')) { \
583 num = num * 10 + (*cur - '0'); \
584 cur++; \
585 }
586
587/**
588 * PARSE_NUM:
589 * @num: the double to fill in
590 * @cur: an #xmlChar *
591 * @num_type: an integer flag
592 *
593 * Parses a float or integer and updates @num with the value. @cur is
594 * updated to point just after the number. If the number is a float,
595 * then it must have an integer part and a decimal part; @num_type will
596 * be set to 1. If there is no decimal part, @num_type is set to zero.
597 * In case of error, @num_type is set to -1, values of @num and
598 * @cur are undefined.
599 */
600#define PARSE_NUM(num, cur, num_type) \
601 num = 0; \
602 PARSE_DIGITS(num, cur, num_type); \
603 if (!num_type && (*cur == '.')) { \
604 double mult = 1; \
605 cur++; \
606 if ((*cur < '0') || (*cur > '9')) \
607 num_type = -1; \
608 else \
609 num_type = 1; \
610 while ((*cur >= '0') && (*cur <= '9')) { \
611 mult /= 10; \
612 num += (*cur - '0') * mult; \
613 cur++; \
614 } \
615 }
616
617/**
618 * xmlSchemaParseDates:
619 * @type: the predefined type
620 * @dateTime: string to analyze
621 * @val: the return computed value
622 *
623 * Check that @dateTime conforms to the lexical space of one of the date types.
624 * if true a value is computed and returned in @val.
625 *
626 * Returns 0 if this validates, a positive error code number otherwise
627 * and -1 in case of internal or API error.
628 */
629static int
630xmlSchemaParseDates (xmlSchemaTypePtr type, const xmlChar *dateTime,
631 xmlSchemaValPtr *val) {
632 xmlSchemaValPtr dt;
633 int ret;
634 const xmlChar *cur = dateTime;
635
636#define RETURN_TYPE_IF_VALID(t) \
637 if (IS_TZO_CHAR(*cur)) { \
638 ret = _xmlSchemaParseTimeZone(&(dt->value.date), &cur); \
639 if (ret == 0) { \
640 if (*cur != 0) \
641 goto error; \
642 dt->type = t; \
643 if (val != NULL) \
644 *val = dt; \
645 return 0; \
646 } \
647 }
648
649 if (dateTime == NULL)
650 return -1;
651
652 if ((*cur != '-') && (*cur < '0') && (*cur > '9'))
653 return 1;
654
655 dt = xmlSchemaNewValue(XML_SCHEMAS_UNKNOWN);
656 if (dt == NULL)
657 return -1;
658
659 if ((cur[0] == '-') && (cur[1] == '-')) {
660 /*
661 * It's an incomplete date (xs:gMonthDay, xs:gMonth or
662 * xs:gDay)
663 */
664 cur += 2;
665
666 /* is it an xs:gDay? */
667 if (*cur == '-') {
668 ++cur;
669 ret = _xmlSchemaParseGDay(&(dt->value.date), &cur);
670 if (ret != 0)
671 goto error;
672
673 RETURN_TYPE_IF_VALID(XML_SCHEMAS_GDAY);
674
675 goto error;
676 }
677
678 /*
679 * it should be an xs:gMonthDay or xs:gMonth
680 */
681 ret = _xmlSchemaParseGMonth(&(dt->value.date), &cur);
682 if (ret != 0)
683 goto error;
684
685 if (*cur != '-')
686 goto error;
687 cur++;
688
689 /* is it an xs:gMonth? */
690 if (*cur == '-') {
691 cur++;
692 RETURN_TYPE_IF_VALID(XML_SCHEMAS_GMONTH);
693 goto error;
694 }
695
696 /* it should be an xs:gMonthDay */
697 ret = _xmlSchemaParseGDay(&(dt->value.date), &cur);
698 if (ret != 0)
699 goto error;
700
701 RETURN_TYPE_IF_VALID(XML_SCHEMAS_GMONTHDAY);
702
703 goto error;
704 }
705
706 /*
707 * It's a right-truncated date or an xs:time.
708 * Try to parse an xs:time then fallback on right-truncated dates.
709 */
710 if ((*cur >= '0') && (*cur <= '9')) {
711 ret = _xmlSchemaParseTime(&(dt->value.date), &cur);
712 if (ret == 0) {
713 /* it's an xs:time */
714 RETURN_TYPE_IF_VALID(XML_SCHEMAS_TIME);
715 }
716 }
717
718 /* fallback on date parsing */
719 cur = dateTime;
720
721 ret = _xmlSchemaParseGYear(&(dt->value.date), &cur);
722 if (ret != 0)
723 goto error;
724
725 /* is it an xs:gYear? */
726 RETURN_TYPE_IF_VALID(XML_SCHEMAS_GYEAR);
727
728 if (*cur != '-')
729 goto error;
730 cur++;
731
732 ret = _xmlSchemaParseGMonth(&(dt->value.date), &cur);
733 if (ret != 0)
734 goto error;
735
736 /* is it an xs:gYearMonth? */
737 RETURN_TYPE_IF_VALID(XML_SCHEMAS_GYEARMONTH);
738
739 if (*cur != '-')
740 goto error;
741 cur++;
742
743 ret = _xmlSchemaParseGDay(&(dt->value.date), &cur);
744 if ((ret != 0) || !VALID_DATE((&(dt->value.date))))
745 goto error;
746
747 /* is it an xs:date? */
748 RETURN_TYPE_IF_VALID(XML_SCHEMAS_DATE);
749
750 if (*cur != 'T')
751 goto error;
752 cur++;
753
754 /* it should be an xs:dateTime */
755 ret = _xmlSchemaParseTime(&(dt->value.date), &cur);
756 if (ret != 0)
757 goto error;
758
759 ret = _xmlSchemaParseTimeZone(&(dt->value.date), &cur);
760 if ((ret != 0) || (*cur != 0) || !VALID_DATETIME((&(dt->value.date))))
761 goto error;
762
763 dt->type = XML_SCHEMAS_DATETIME;
764
765 if (val != NULL)
766 *val = dt;
767
768 return 0;
769
770error:
771 if (dt != NULL)
772 xmlSchemaFreeValue(dt);
773 return 1;
774}
775
776/**
777 * xmlSchemaParseDuration:
778 * @type: the predefined type
779 * @duration: string to analyze
780 * @val: the return computed value
781 *
782 * Check that @duration conforms to the lexical space of the duration type.
783 * if true a value is computed and returned in @val.
784 *
785 * Returns 0 if this validates, a positive error code number otherwise
786 * and -1 in case of internal or API error.
787 */
788static int
789xmlSchemaParseDuration (xmlSchemaTypePtr type, const xmlChar *duration,
790 xmlSchemaValPtr *val) {
791 const xmlChar *cur = duration;
792 xmlSchemaValPtr dur;
793 int isneg = 0;
794 unsigned int seq = 0;
795
796 if (duration == NULL)
797 return -1;
798
799 if (*cur == '-') {
800 isneg = 1;
801 cur++;
802 }
803
804 /* duration must start with 'P' (after sign) */
805 if (*cur++ != 'P')
806 return 1;
807
808 dur = xmlSchemaNewValue(XML_SCHEMAS_DURATION);
809 if (dur == NULL)
810 return -1;
811
812 while (*cur != 0) {
813 double num;
814 int num_type = 0; /* -1 = invalid, 0 = int, 1 = floating */
815 const xmlChar desig[] = {'Y', 'M', 'D', 'H', 'M', 'S'};
816 const double multi[] = { 0.0, 0.0, 86400.0, 3600.0, 60.0, 1.0, 0.0};
817
818 /* input string should be empty or invalid date/time item */
819 if (seq >= sizeof(desig))
820 goto error;
821
822 /* T designator must be present for time items */
823 if (*cur == 'T') {
824 if (seq <= 3) {
825 seq = 3;
826 cur++;
827 } else
828 return 1;
829 } else if (seq == 3)
830 goto error;
831
832 /* parse the number portion of the item */
833 PARSE_NUM(num, cur, num_type);
834
835 if ((num_type == -1) || (*cur == 0))
836 goto error;
837
838 /* update duration based on item type */
839 while (seq < sizeof(desig)) {
840 if (*cur == desig[seq]) {
841
842 /* verify numeric type; only seconds can be float */
843 if ((num_type != 0) && (seq < (sizeof(desig)-1)))
844 goto error;
845
846 switch (seq) {
847 case 0:
848 dur->value.dur.mon = (long)num * 12;
849 break;
850 case 1:
851 dur->value.dur.mon += (long)num;
852 break;
853 default:
854 /* convert to seconds using multiplier */
855 dur->value.dur.sec += num * multi[seq];
856 seq++;
857 break;
858 }
859
860 break; /* exit loop */
861 }
862 /* no date designators found? */
863 if (++seq == 3)
864 goto error;
865 }
866 cur++;
867 }
868
869 if (isneg) {
870 dur->value.dur.mon = -dur->value.dur.mon;
871 dur->value.dur.day = -dur->value.dur.day;
872 dur->value.dur.sec = -dur->value.dur.sec;
873 }
874
875 if (val != NULL)
876 *val = dur;
877
878 return 0;
879
880error:
881 if (dur != NULL)
882 xmlSchemaFreeValue(dur);
883 return 1;
884}
885
Daniel Veillard4255d502002-04-16 15:50:10 +0000886/**
887 * xmlSchemaValidatePredefinedType:
888 * @type: the predefined type
889 * @value: the value to check
890 * @val: the return computed value
891 *
892 * Check that a value conforms to the lexical space of the predefined type.
893 * if true a value is computed and returned in @val.
894 *
895 * Returns 0 if this validates, a positive error code number otherwise
896 * and -1 in case of internal or API error.
897 */
898int
899xmlSchemaValidatePredefinedType(xmlSchemaTypePtr type, const xmlChar *value,
900 xmlSchemaValPtr *val) {
901 xmlSchemaValPtr v;
902
903 if (xmlSchemaTypesInitialized == 0)
904 return(-1);
905 if (type == NULL)
906 return(-1);
907 if (val != NULL)
908 *val = NULL;
909 if (type == xmlSchemaTypeStringDef) {
910 return(0);
911 } else if (type == xmlSchemaTypeAnyTypeDef) {
912 return(0);
913 } else if (type == xmlSchemaTypeAnySimpleTypeDef) {
914 return(0);
915 } else if (type == xmlSchemaTypeNmtoken) {
916 if (xmlValidateNmtokenValue(value))
917 return(0);
918 return(1);
919 } else if (type == xmlSchemaTypeDecimalDef) {
920 const xmlChar *cur = value, *tmp;
921 int frac = 0, main, neg = 0;
922 unsigned long base = 0;
923 if (cur == NULL)
924 return(1);
925 if (*cur == '+')
926 cur++;
927 else if (*cur == '-') {
928 neg = 1;
929 cur++;
930 }
931 tmp = cur;
932 while ((*cur >= '0') && (*cur <= '9')) {
933 base = base * 10 + (*cur - '0');
934 cur++;
935 }
936 main = cur - tmp;
937 if (*cur == '.') {
938 cur++;
939 tmp = cur;
940 while ((*cur >= '0') && (*cur <= '9')) {
941 base = base * 10 + (*cur - '0');
942 cur++;
943 }
944 frac = cur - tmp;
945 }
946 if (*cur != 0)
947 return(1);
948 if (val != NULL) {
949 v = xmlSchemaNewValue(XML_SCHEMAS_DECIMAL);
950 if (v != NULL) {
951 v->value.decimal.base = base;
952 v->value.decimal.sign = neg;
953 v->value.decimal.frac = frac;
954 v->value.decimal.total = frac + main;
955 *val = v;
956 }
957 }
958 return(0);
Daniel Veillard070803b2002-05-03 07:29:38 +0000959 } else if (type == xmlSchemaTypeDurationDef) {
960 return xmlSchemaParseDuration(type, value, val);
961 } else if ((type == xmlSchemaTypeDatetimeDef) ||
962 (type == xmlSchemaTypeTimeDef) ||
963 (type == xmlSchemaTypeDateDef) ||
964 (type == xmlSchemaTypeGYearDef) ||
965 (type == xmlSchemaTypeGYearMonthDef) ||
966 (type == xmlSchemaTypeGMonthDef) ||
967 (type == xmlSchemaTypeGMonthDayDef) ||
968 (type == xmlSchemaTypeGDayDef)) {
969 return xmlSchemaParseDates(type, value, val);
Daniel Veillard4255d502002-04-16 15:50:10 +0000970 } else if (type == xmlSchemaTypePositiveIntegerDef) {
971 const xmlChar *cur = value;
972 unsigned long base = 0;
973 int total = 0;
974 if (cur == NULL)
975 return(1);
976 if (*cur == '+')
977 cur++;
978 while ((*cur >= '0') && (*cur <= '9')) {
979 base = base * 10 + (*cur - '0');
980 total++;
981 cur++;
982 }
983 if (*cur != 0)
984 return(1);
985 if (val != NULL) {
986 v = xmlSchemaNewValue(XML_SCHEMAS_DECIMAL);
987 if (v != NULL) {
988 v->value.decimal.base = base;
989 v->value.decimal.sign = 0;
990 v->value.decimal.frac = 0;
991 v->value.decimal.total = total;
992 *val = v;
993 }
994 }
995 return(0);
996 } else if (type == xmlSchemaTypeNonNegativeIntegerDef) {
997 const xmlChar *cur = value;
998 unsigned long base = 0;
999 int total = 0;
1000 int sign = 0;
1001 if (cur == NULL)
1002 return(1);
1003 if (*cur == '-') {
1004 sign = 1;
1005 cur++;
1006 } else if (*cur == '+')
1007 cur++;
1008 while ((*cur >= '0') && (*cur <= '9')) {
1009 base = base * 10 + (*cur - '0');
1010 total++;
1011 cur++;
1012 }
1013 if (*cur != 0)
1014 return(1);
1015 if ((sign == 1) && (base != 0))
1016 return(1);
1017 if (val != NULL) {
1018 v = xmlSchemaNewValue(XML_SCHEMAS_DECIMAL);
1019 if (v != NULL) {
1020 v->value.decimal.base = base;
1021 v->value.decimal.sign = 0;
1022 v->value.decimal.frac = 0;
1023 v->value.decimal.total = total;
1024 *val = v;
1025 }
1026 }
1027 return(0);
1028 } else {
1029 TODO
1030 return(0);
1031 }
1032}
1033
1034/**
1035 * xmlSchemaCompareDecimals:
1036 * @x: a first decimal value
1037 * @y: a second decimal value
1038 *
1039 * Compare 2 decimals
1040 *
1041 * Returns -1 if x < y, 0 if x == y, 1 if x > y and -2 in case of error
1042 */
1043static int
1044xmlSchemaCompareDecimals(xmlSchemaValPtr x, xmlSchemaValPtr y)
1045{
1046 xmlSchemaValPtr swp;
1047 int order = 1;
1048 unsigned long tmp;
1049
1050 if ((x->value.decimal.sign) && (x->value.decimal.sign))
1051 order = -1;
1052 else if (x->value.decimal.sign)
1053 return (-1);
1054 else if (y->value.decimal.sign)
1055 return (1);
1056 if (x->value.decimal.frac == y->value.decimal.frac) {
1057 if (x->value.decimal.base < y->value.decimal.base)
1058 return (-1);
1059 return (x->value.decimal.base > y->value.decimal.base);
1060 }
1061 if (y->value.decimal.frac > x->value.decimal.frac) {
1062 swp = y;
1063 y = x;
1064 x = swp;
1065 order = -order;
1066 }
1067 tmp =
1068 x->value.decimal.base / powten[x->value.decimal.frac -
1069 y->value.decimal.frac];
1070 if (tmp > y->value.decimal.base)
1071 return (order);
1072 if (tmp < y->value.decimal.base)
1073 return (-order);
1074 tmp =
1075 y->value.decimal.base * powten[x->value.decimal.frac -
1076 y->value.decimal.frac];
1077 if (x->value.decimal.base < tmp)
1078 return (-order);
1079 if (x->value.decimal.base == tmp)
1080 return (0);
1081 return (order);
1082}
1083
1084/**
Daniel Veillard070803b2002-05-03 07:29:38 +00001085 * xmlSchemaCompareDurations:
1086 * @x: a first duration value
1087 * @y: a second duration value
1088 *
1089 * Compare 2 durations
1090 *
1091 * Returns -1 if x < y, 0 if x == y, 1 if x > y, 2 if x <> y, and -2 in
1092 * case of error
1093 */
1094static int
1095xmlSchemaCompareDurations(xmlSchemaValPtr x, xmlSchemaValPtr y)
1096{
1097 long carry, mon, day;
1098 double sec;
1099 long xmon, xday, myear, lyear, minday, maxday;
1100 static const long dayRange [2][12] = {
1101 { 0, 28, 59, 89, 120, 150, 181, 212, 242, 273, 303, 334, },
1102 { 0, 31, 62, 92, 123, 153, 184, 215, 245, 276, 306, 337} };
1103
1104 if ((x == NULL) || (y == NULL))
1105 return NULL;
1106
1107 /* months */
1108 mon = x->value.dur.mon - y->value.dur.mon;
1109
1110 /* seconds */
1111 sec = x->value.dur.sec - y->value.dur.sec;
1112 carry = (long)sec / SECS_PER_DAY;
1113 sec -= (double)(carry * SECS_PER_DAY);
1114
1115 /* days */
1116 day = x->value.dur.day - y->value.dur.day + carry;
1117
1118 /* easy test */
1119 if (mon == 0) {
1120 if (day == 0)
1121 if (sec == 0.0)
1122 return 0;
1123 else if (sec < 0.0)
1124 return -1;
1125 else
1126 return 1;
1127 else if (day < 0)
1128 return -1;
1129 else
1130 return 1;
1131 }
1132
1133 if (mon > 0) {
1134 if ((day >= 0) && (sec >= 0.0))
1135 return 1;
1136 else {
1137 xmon = mon;
1138 xday = -day;
1139 }
1140 } else if ((day <= 0) && (sec <= 0.0)) {
1141 return -1;
1142 } else {
1143 xmon = -mon;
1144 xday = day;
1145 }
1146
1147 myear = xmon / 12;
1148 lyear = myear / 4;
1149 minday = (myear * 365) + (lyear != 0 ? lyear - 1 : 0);
1150 maxday = (myear * 365) + (lyear != 0 ? lyear + 1 : 0);
1151
1152 xmon = xmon % 12;
1153 minday += dayRange[0][xmon];
1154 maxday += dayRange[1][xmon];
1155
1156 if (maxday < xday)
1157 return 1;
1158 else if (minday > xday)
1159 return -1;
1160
1161 /* indeterminate */
1162 return 0;
1163}
1164
1165/**
Daniel Veillard4255d502002-04-16 15:50:10 +00001166 * xmlSchemaCompareValues:
1167 * @x: a first value
1168 * @y: a second value
1169 *
1170 * Compare 2 values
1171 *
1172 * Returns -1 if x < y, 0 if x == y, 1 if x > y and -2 in case of error
1173 */
Daniel Veillarde19fc232002-04-22 16:01:24 +00001174static int
Daniel Veillard4255d502002-04-16 15:50:10 +00001175xmlSchemaCompareValues(xmlSchemaValPtr x, xmlSchemaValPtr y) {
1176 if ((x == NULL) || (y == NULL))
1177 return(-2);
1178
1179 switch (x->type) {
1180 case XML_SCHEMAS_STRING:
1181 TODO
1182 case XML_SCHEMAS_DECIMAL:
1183 if (y->type == XML_SCHEMAS_DECIMAL)
1184 return(xmlSchemaCompareDecimals(x, y));
1185 else
1186 return(-2);
Daniel Veillard070803b2002-05-03 07:29:38 +00001187 case XML_SCHEMAS_DURATION:
1188 if (y->type == XML_SCHEMAS_DURATION)
1189 return(xmlSchemaCompareDurations(x, y));
1190 else
1191 return(-2);
Daniel Veillard4255d502002-04-16 15:50:10 +00001192 default:
1193 TODO
1194 }
1195}
1196
1197/**
1198 * xmlSchemaValidateFacet:
1199 * @type: the type declaration
1200 * @facet: the facet to check
1201 * @value: the lexical repr of the value to validate
1202 * @val: the precomputed value
1203 *
1204 * Check a value against a facet condition
1205 *
1206 * Returns 0 if the element is schemas valid, a positive error code
1207 * number otherwise and -1 in case of internal or API error.
1208 */
1209int
1210xmlSchemaValidateFacet(xmlSchemaTypePtr base, xmlSchemaFacetPtr facet,
1211 const xmlChar *value, xmlSchemaValPtr val)
1212{
1213 int ret;
1214
1215 switch (facet->type) {
1216 case XML_SCHEMA_FACET_PATTERN:
1217 ret = xmlRegexpExec(facet->regexp, value);
1218 if (ret == 1)
1219 return(0);
1220 if (ret == 0) {
1221 TODO /* error code */
1222 return(1);
1223 }
1224 return(ret);
1225 case XML_SCHEMA_FACET_MAXEXCLUSIVE:
1226 ret = xmlSchemaCompareValues(val, facet->val);
1227 if (ret == -2) {
1228 TODO /* error code */
1229 return(-1);
1230 }
1231 if (ret == -1)
1232 return(0);
1233 TODO /* error code */
1234 return(1);
Daniel Veillard070803b2002-05-03 07:29:38 +00001235 case XML_SCHEMA_FACET_MAXINCLUSIVE:
1236 ret = xmlSchemaCompareValues(val, facet->val);
1237 if (ret == -2) {
1238 TODO /* error code */
1239 return(-1);
1240 }
1241 if ((ret == -1) || (ret == 0))
1242 return(0);
1243 TODO /* error code */
1244 return(1);
1245 case XML_SCHEMA_FACET_MINEXCLUSIVE:
1246 ret = xmlSchemaCompareValues(val, facet->val);
1247 if (ret == -2) {
1248 TODO /* error code */
1249 return(-1);
1250 }
1251 if (ret == 1)
1252 return(0);
1253 TODO /* error code */
1254 return(1);
1255 case XML_SCHEMA_FACET_MININCLUSIVE:
1256 ret = xmlSchemaCompareValues(val, facet->val);
1257 if (ret == -2) {
1258 TODO /* error code */
1259 return(-1);
1260 }
1261 if ((ret == 1) || (ret == 0))
1262 return(0);
1263 TODO /* error code */
1264 return(1);
Daniel Veillard8651f532002-04-17 09:06:27 +00001265 case XML_SCHEMA_FACET_WHITESPACE:
1266 TODO /* whitespaces */
1267 return(0);
Daniel Veillarde19fc232002-04-22 16:01:24 +00001268 case XML_SCHEMA_FACET_MAXLENGTH:
1269 if ((facet->val != NULL) &&
1270 (facet->val->type == XML_SCHEMAS_DECIMAL) &&
1271 (facet->val->value.decimal.frac == 0)) {
1272 int len;
1273
1274 if (facet->val->value.decimal.sign == 1)
1275 return(1);
1276 len = xmlUTF8Strlen(value);
1277 if (len > facet->val->value.decimal.base)
1278 return(1);
1279 return(0);
1280 }
1281 TODO /* error code */
1282 return(1);
Daniel Veillard88c58912002-04-23 07:12:20 +00001283 case XML_SCHEMA_FACET_ENUMERATION:
1284 if ((facet->value != NULL) &&
1285 (xmlStrEqual(facet->value, value)))
1286 return(0);
1287 return(1);
Daniel Veillard4255d502002-04-16 15:50:10 +00001288 default:
1289 TODO
1290 }
1291 return(0);
1292}
1293
1294#endif /* LIBXML_SCHEMAS_ENABLED */