blob: 061502520d5729658f514475542871725b8cd144 [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;
Daniel Veillard5a872412002-05-22 06:40:27 +000092 unsigned int sign:1;
Daniel Veillard4255d502002-04-16 15:50:10 +000093 int frac:7;
94 int total:8;
95};
96
97struct _xmlSchemaVal {
98 xmlSchemaValType type;
99 union {
Daniel Veillard5a872412002-05-22 06:40:27 +0000100 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
Daniel Veillard5a872412002-05-22 06:40:27 +0000273#define MAX_DAYINMONTH(yr,mon) \
274 (IS_LEAP(yr) ? daysInMonthLeap[mon - 1] : daysInMonth[mon - 1])
275
Daniel Veillard070803b2002-05-03 07:29:38 +0000276#define VALID_MDAY(dt) \
277 (IS_LEAP(dt->year) ? \
278 (dt->day <= daysInMonthLeap[dt->mon - 1]) : \
279 (dt->day <= daysInMonth[dt->mon - 1]))
280
281#define VALID_DATE(dt) \
282 (VALID_YEAR(dt->year) && VALID_MONTH(dt->mon) && VALID_MDAY(dt))
283
284#define VALID_TIME(dt) \
285 (VALID_HOUR(dt->hour) && VALID_MIN(dt->min) && \
286 VALID_SEC(dt->sec) && VALID_TZO(dt->tzo))
287
288#define VALID_DATETIME(dt) \
289 (VALID_DATE(dt) && VALID_TIME(dt))
290
291#define SECS_PER_MIN (60)
292#define SECS_PER_HOUR (60 * SECS_PER_MIN)
293#define SECS_PER_DAY (24 * SECS_PER_HOUR)
294
Daniel Veillard5a872412002-05-22 06:40:27 +0000295static const long dayInYearByMonth[12] =
296 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
297static const long dayInLeapYearByMonth[12] =
298 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 };
299
300#define DAY_IN_YEAR(day, month, year) \
301 ((IS_LEAP(year) ? \
302 dayInLeapYearByMonth[month - 1] : \
303 dayInYearByMonth[month - 1]) + day)
304
305#ifdef DEBUG
306#define DEBUG_DATE(dt) \
307 xmlGenericError(xmlGenericErrorContext, \
308 "type=%o %04ld-%02u-%02uT%02u:%02u:%03f", \
309 dt->type,dt->value.date.year,dt->value.date.mon, \
310 dt->value.date.day,dt->value.date.hour,dt->value.date.min, \
311 dt->value.date.sec); \
312 if (dt->value.date.tz_flag) \
313 if (dt->value.date.tzo != 0) \
314 xmlGenericError(xmlGenericErrorContext, \
315 "%+05d\n",dt->value.date.tzo); \
316 else \
317 xmlGenericError(xmlGenericErrorContext, "Z\n"); \
318 else \
319 xmlGenericError(xmlGenericErrorContext,"\n")
320#else
321#define DEBUG_DATE(dt)
322#endif
323
Daniel Veillard070803b2002-05-03 07:29:38 +0000324/**
325 * _xmlSchemaParseGYear:
326 * @dt: pointer to a date structure
327 * @str: pointer to the string to analyze
328 *
329 * Parses a xs:gYear without time zone and fills in the appropriate
330 * field of the @dt structure. @str is updated to point just after the
331 * xs:gYear. It is supposed that @dt->year is big enough to contain
332 * the year.
333 *
334 * Returns 0 or the error code
335 */
336static int
337_xmlSchemaParseGYear (xmlSchemaValDatePtr dt, const xmlChar **str) {
338 const xmlChar *cur = *str, *firstChar;
339 int isneg = 0, digcnt = 0;
340
341 if (((*cur < '0') || (*cur > '9')) &&
342 (*cur != '-') && (*cur != '+'))
343 return -1;
344
345 if (*cur == '-') {
346 isneg = 1;
347 cur++;
348 }
349
350 firstChar = cur;
351
352 while ((*cur >= '0') && (*cur <= '9')) {
353 dt->year = dt->year * 10 + (*cur - '0');
354 cur++;
355 digcnt++;
356 }
357
358 /* year must be at least 4 digits (CCYY); over 4
359 * digits cannot have a leading zero. */
360 if ((digcnt < 4) || ((digcnt > 4) && (*firstChar == '0')))
361 return 1;
362
363 if (isneg)
364 dt->year = - dt->year;
365
366 if (!VALID_YEAR(dt->year))
367 return 2;
368
369 *str = cur;
370 return 0;
371}
372
373/**
374 * PARSE_2_DIGITS:
375 * @num: the integer to fill in
376 * @cur: an #xmlChar *
377 * @invalid: an integer
378 *
379 * Parses a 2-digits integer and updates @num with the value. @cur is
380 * updated to point just after the integer.
381 * In case of error, @invalid is set to %TRUE, values of @num and
382 * @cur are undefined.
383 */
384#define PARSE_2_DIGITS(num, cur, invalid) \
385 if ((cur[0] < '0') || (cur[0] > '9') || \
386 (cur[1] < '0') || (cur[1] > '9')) \
387 invalid = 1; \
388 else \
389 num = (cur[0] - '0') * 10 + (cur[1] - '0'); \
390 cur += 2;
391
392/**
393 * PARSE_FLOAT:
394 * @num: the double to fill in
395 * @cur: an #xmlChar *
396 * @invalid: an integer
397 *
398 * Parses a float and updates @num with the value. @cur is
399 * updated to point just after the float. The float must have a
400 * 2-digits integer part and may or may not have a decimal part.
401 * In case of error, @invalid is set to %TRUE, values of @num and
402 * @cur are undefined.
403 */
404#define PARSE_FLOAT(num, cur, invalid) \
405 PARSE_2_DIGITS(num, cur, invalid); \
406 if (!invalid && (*cur == '.')) { \
407 double mult = 1; \
408 cur++; \
409 if ((*cur < '0') || (*cur > '9')) \
410 invalid = 1; \
411 while ((*cur >= '0') && (*cur <= '9')) { \
412 mult /= 10; \
413 num += (*cur - '0') * mult; \
414 cur++; \
415 } \
416 }
417
418/**
419 * _xmlSchemaParseGMonth:
420 * @dt: pointer to a date structure
421 * @str: pointer to the string to analyze
422 *
423 * Parses a xs:gMonth without time zone and fills in the appropriate
424 * field of the @dt structure. @str is updated to point just after the
425 * xs:gMonth.
426 *
427 * Returns 0 or the error code
428 */
429static int
430_xmlSchemaParseGMonth (xmlSchemaValDatePtr dt, const xmlChar **str) {
431 const xmlChar *cur = *str;
432 int ret = 0;
433
434 PARSE_2_DIGITS(dt->mon, cur, ret);
435 if (ret != 0)
436 return ret;
437
438 if (!VALID_MONTH(dt->mon))
439 return 2;
440
441 *str = cur;
442 return 0;
443}
444
445/**
446 * _xmlSchemaParseGDay:
447 * @dt: pointer to a date structure
448 * @str: pointer to the string to analyze
449 *
450 * Parses a xs:gDay without time zone and fills in the appropriate
451 * field of the @dt structure. @str is updated to point just after the
452 * xs:gDay.
453 *
454 * Returns 0 or the error code
455 */
456static int
457_xmlSchemaParseGDay (xmlSchemaValDatePtr dt, const xmlChar **str) {
458 const xmlChar *cur = *str;
459 int ret = 0;
460
461 PARSE_2_DIGITS(dt->day, cur, ret);
462 if (ret != 0)
463 return ret;
464
465 if (!VALID_DAY(dt->day))
466 return 2;
467
468 *str = cur;
469 return 0;
470}
471
472/**
473 * _xmlSchemaParseTime:
474 * @dt: pointer to a date structure
475 * @str: pointer to the string to analyze
476 *
477 * Parses a xs:time without time zone and fills in the appropriate
478 * fields of the @dt structure. @str is updated to point just after the
479 * xs:time.
480 * In case of error, values of @dt fields are undefined.
481 *
482 * Returns 0 or the error code
483 */
484static int
485_xmlSchemaParseTime (xmlSchemaValDatePtr dt, const xmlChar **str) {
486 const xmlChar *cur = *str;
487 unsigned int hour = 0; /* use temp var in case str is not xs:time */
488 int ret = 0;
489
490 PARSE_2_DIGITS(hour, cur, ret);
491 if (ret != 0)
492 return ret;
493
494 if (*cur != ':')
495 return 1;
496 cur++;
497
498 /* the ':' insures this string is xs:time */
499 dt->hour = hour;
500
501 PARSE_2_DIGITS(dt->min, cur, ret);
502 if (ret != 0)
503 return ret;
504
505 if (*cur != ':')
506 return 1;
507 cur++;
508
509 PARSE_FLOAT(dt->sec, cur, ret);
510 if (ret != 0)
511 return ret;
512
513 if (!VALID_TIME(dt))
514 return 2;
515
516 *str = cur;
517 return 0;
518}
519
520/**
521 * _xmlSchemaParseTimeZone:
522 * @dt: pointer to a date structure
523 * @str: pointer to the string to analyze
524 *
525 * Parses a time zone without time zone and fills in the appropriate
526 * field of the @dt structure. @str is updated to point just after the
527 * time zone.
528 *
529 * Returns 0 or the error code
530 */
531static int
532_xmlSchemaParseTimeZone (xmlSchemaValDatePtr dt, const xmlChar **str) {
533 const xmlChar *cur = *str;
534 int ret = 0;
535
536 if (str == NULL)
537 return -1;
538
539 switch (*cur) {
540 case 0:
541 dt->tz_flag = 0;
542 dt->tzo = 0;
543 break;
544
545 case 'Z':
546 dt->tz_flag = 1;
547 dt->tzo = 0;
548 cur++;
549 break;
550
551 case '+':
552 case '-': {
553 int isneg = 0, tmp = 0;
554 isneg = (*cur == '-');
555
556 cur++;
557
558 PARSE_2_DIGITS(tmp, cur, ret);
559 if (ret != 0)
560 return ret;
561 if (!VALID_HOUR(tmp))
562 return 2;
563
564 if (*cur != ':')
565 return 1;
566 cur++;
567
568 dt->tzo = tmp * 60;
569
570 PARSE_2_DIGITS(tmp, cur, ret);
571 if (ret != 0)
572 return ret;
573 if (!VALID_MIN(tmp))
574 return 2;
575
576 dt->tzo += tmp;
577 if (isneg)
578 dt->tzo = - dt->tzo;
579
580 if (!VALID_TZO(dt->tzo))
581 return 2;
582
Daniel Veillard5a872412002-05-22 06:40:27 +0000583 dt->tz_flag = 1;
Daniel Veillard070803b2002-05-03 07:29:38 +0000584 break;
585 }
586 default:
587 return 1;
588 }
589
590 *str = cur;
591 return 0;
592}
593
594/****************************************************************
595 * *
596 * XML Schema Dates/Times Datatypes Handling *
597 * *
598 ****************************************************************/
599
600/**
601 * PARSE_DIGITS:
602 * @num: the integer to fill in
603 * @cur: an #xmlChar *
604 * @num_type: an integer flag
605 *
606 * Parses a digits integer and updates @num with the value. @cur is
607 * updated to point just after the integer.
608 * In case of error, @num_type is set to -1, values of @num and
609 * @cur are undefined.
610 */
611#define PARSE_DIGITS(num, cur, num_type) \
612 if ((*cur < '0') || (*cur > '9')) \
613 num_type = -1; \
614 else \
615 while ((*cur >= '0') && (*cur <= '9')) { \
616 num = num * 10 + (*cur - '0'); \
617 cur++; \
618 }
619
620/**
621 * PARSE_NUM:
622 * @num: the double to fill in
623 * @cur: an #xmlChar *
624 * @num_type: an integer flag
625 *
626 * Parses a float or integer and updates @num with the value. @cur is
627 * updated to point just after the number. If the number is a float,
628 * then it must have an integer part and a decimal part; @num_type will
629 * be set to 1. If there is no decimal part, @num_type is set to zero.
630 * In case of error, @num_type is set to -1, values of @num and
631 * @cur are undefined.
632 */
633#define PARSE_NUM(num, cur, num_type) \
634 num = 0; \
635 PARSE_DIGITS(num, cur, num_type); \
636 if (!num_type && (*cur == '.')) { \
637 double mult = 1; \
638 cur++; \
639 if ((*cur < '0') || (*cur > '9')) \
640 num_type = -1; \
641 else \
642 num_type = 1; \
643 while ((*cur >= '0') && (*cur <= '9')) { \
644 mult /= 10; \
645 num += (*cur - '0') * mult; \
646 cur++; \
647 } \
648 }
649
650/**
Daniel Veillard5a872412002-05-22 06:40:27 +0000651 * xmlSchemaValidateDates:
Daniel Veillard070803b2002-05-03 07:29:38 +0000652 * @type: the predefined type
653 * @dateTime: string to analyze
654 * @val: the return computed value
655 *
656 * Check that @dateTime conforms to the lexical space of one of the date types.
657 * if true a value is computed and returned in @val.
658 *
659 * Returns 0 if this validates, a positive error code number otherwise
660 * and -1 in case of internal or API error.
661 */
662static int
Daniel Veillard5a872412002-05-22 06:40:27 +0000663xmlSchemaValidateDates (xmlSchemaTypePtr type, const xmlChar *dateTime,
Daniel Veillard070803b2002-05-03 07:29:38 +0000664 xmlSchemaValPtr *val) {
665 xmlSchemaValPtr dt;
666 int ret;
667 const xmlChar *cur = dateTime;
668
669#define RETURN_TYPE_IF_VALID(t) \
670 if (IS_TZO_CHAR(*cur)) { \
671 ret = _xmlSchemaParseTimeZone(&(dt->value.date), &cur); \
672 if (ret == 0) { \
673 if (*cur != 0) \
674 goto error; \
675 dt->type = t; \
676 if (val != NULL) \
677 *val = dt; \
678 return 0; \
679 } \
680 }
681
682 if (dateTime == NULL)
683 return -1;
684
685 if ((*cur != '-') && (*cur < '0') && (*cur > '9'))
686 return 1;
687
688 dt = xmlSchemaNewValue(XML_SCHEMAS_UNKNOWN);
689 if (dt == NULL)
690 return -1;
691
692 if ((cur[0] == '-') && (cur[1] == '-')) {
693 /*
694 * It's an incomplete date (xs:gMonthDay, xs:gMonth or
695 * xs:gDay)
696 */
697 cur += 2;
698
699 /* is it an xs:gDay? */
700 if (*cur == '-') {
701 ++cur;
702 ret = _xmlSchemaParseGDay(&(dt->value.date), &cur);
703 if (ret != 0)
704 goto error;
705
706 RETURN_TYPE_IF_VALID(XML_SCHEMAS_GDAY);
707
708 goto error;
709 }
710
711 /*
712 * it should be an xs:gMonthDay or xs:gMonth
713 */
714 ret = _xmlSchemaParseGMonth(&(dt->value.date), &cur);
715 if (ret != 0)
716 goto error;
717
718 if (*cur != '-')
719 goto error;
720 cur++;
721
722 /* is it an xs:gMonth? */
723 if (*cur == '-') {
724 cur++;
725 RETURN_TYPE_IF_VALID(XML_SCHEMAS_GMONTH);
726 goto error;
727 }
728
729 /* it should be an xs:gMonthDay */
730 ret = _xmlSchemaParseGDay(&(dt->value.date), &cur);
731 if (ret != 0)
732 goto error;
733
734 RETURN_TYPE_IF_VALID(XML_SCHEMAS_GMONTHDAY);
735
736 goto error;
737 }
738
739 /*
740 * It's a right-truncated date or an xs:time.
741 * Try to parse an xs:time then fallback on right-truncated dates.
742 */
743 if ((*cur >= '0') && (*cur <= '9')) {
744 ret = _xmlSchemaParseTime(&(dt->value.date), &cur);
745 if (ret == 0) {
746 /* it's an xs:time */
747 RETURN_TYPE_IF_VALID(XML_SCHEMAS_TIME);
748 }
749 }
750
751 /* fallback on date parsing */
752 cur = dateTime;
753
754 ret = _xmlSchemaParseGYear(&(dt->value.date), &cur);
755 if (ret != 0)
756 goto error;
757
758 /* is it an xs:gYear? */
759 RETURN_TYPE_IF_VALID(XML_SCHEMAS_GYEAR);
760
761 if (*cur != '-')
762 goto error;
763 cur++;
764
765 ret = _xmlSchemaParseGMonth(&(dt->value.date), &cur);
766 if (ret != 0)
767 goto error;
768
769 /* is it an xs:gYearMonth? */
770 RETURN_TYPE_IF_VALID(XML_SCHEMAS_GYEARMONTH);
771
772 if (*cur != '-')
773 goto error;
774 cur++;
775
776 ret = _xmlSchemaParseGDay(&(dt->value.date), &cur);
777 if ((ret != 0) || !VALID_DATE((&(dt->value.date))))
778 goto error;
779
780 /* is it an xs:date? */
781 RETURN_TYPE_IF_VALID(XML_SCHEMAS_DATE);
782
783 if (*cur != 'T')
784 goto error;
785 cur++;
786
787 /* it should be an xs:dateTime */
788 ret = _xmlSchemaParseTime(&(dt->value.date), &cur);
789 if (ret != 0)
790 goto error;
791
792 ret = _xmlSchemaParseTimeZone(&(dt->value.date), &cur);
793 if ((ret != 0) || (*cur != 0) || !VALID_DATETIME((&(dt->value.date))))
794 goto error;
795
796 dt->type = XML_SCHEMAS_DATETIME;
797
798 if (val != NULL)
799 *val = dt;
800
801 return 0;
802
803error:
804 if (dt != NULL)
805 xmlSchemaFreeValue(dt);
806 return 1;
807}
808
809/**
Daniel Veillard5a872412002-05-22 06:40:27 +0000810 * xmlSchemaValidateDuration:
Daniel Veillard070803b2002-05-03 07:29:38 +0000811 * @type: the predefined type
812 * @duration: string to analyze
813 * @val: the return computed value
814 *
815 * Check that @duration conforms to the lexical space of the duration type.
816 * if true a value is computed and returned in @val.
817 *
818 * Returns 0 if this validates, a positive error code number otherwise
819 * and -1 in case of internal or API error.
820 */
821static int
Daniel Veillard5a872412002-05-22 06:40:27 +0000822xmlSchemaValidateDuration (xmlSchemaTypePtr type, const xmlChar *duration,
Daniel Veillard070803b2002-05-03 07:29:38 +0000823 xmlSchemaValPtr *val) {
824 const xmlChar *cur = duration;
825 xmlSchemaValPtr dur;
826 int isneg = 0;
827 unsigned int seq = 0;
828
829 if (duration == NULL)
830 return -1;
831
832 if (*cur == '-') {
833 isneg = 1;
834 cur++;
835 }
836
837 /* duration must start with 'P' (after sign) */
838 if (*cur++ != 'P')
839 return 1;
840
841 dur = xmlSchemaNewValue(XML_SCHEMAS_DURATION);
842 if (dur == NULL)
843 return -1;
844
845 while (*cur != 0) {
846 double num;
847 int num_type = 0; /* -1 = invalid, 0 = int, 1 = floating */
848 const xmlChar desig[] = {'Y', 'M', 'D', 'H', 'M', 'S'};
849 const double multi[] = { 0.0, 0.0, 86400.0, 3600.0, 60.0, 1.0, 0.0};
850
851 /* input string should be empty or invalid date/time item */
852 if (seq >= sizeof(desig))
853 goto error;
854
855 /* T designator must be present for time items */
856 if (*cur == 'T') {
857 if (seq <= 3) {
858 seq = 3;
859 cur++;
860 } else
861 return 1;
862 } else if (seq == 3)
863 goto error;
864
865 /* parse the number portion of the item */
866 PARSE_NUM(num, cur, num_type);
867
868 if ((num_type == -1) || (*cur == 0))
869 goto error;
870
871 /* update duration based on item type */
872 while (seq < sizeof(desig)) {
873 if (*cur == desig[seq]) {
874
875 /* verify numeric type; only seconds can be float */
876 if ((num_type != 0) && (seq < (sizeof(desig)-1)))
877 goto error;
878
879 switch (seq) {
880 case 0:
881 dur->value.dur.mon = (long)num * 12;
882 break;
883 case 1:
884 dur->value.dur.mon += (long)num;
885 break;
886 default:
887 /* convert to seconds using multiplier */
888 dur->value.dur.sec += num * multi[seq];
889 seq++;
890 break;
891 }
892
893 break; /* exit loop */
894 }
895 /* no date designators found? */
896 if (++seq == 3)
897 goto error;
898 }
899 cur++;
900 }
901
902 if (isneg) {
903 dur->value.dur.mon = -dur->value.dur.mon;
904 dur->value.dur.day = -dur->value.dur.day;
905 dur->value.dur.sec = -dur->value.dur.sec;
906 }
907
908 if (val != NULL)
909 *val = dur;
910
911 return 0;
912
913error:
914 if (dur != NULL)
915 xmlSchemaFreeValue(dur);
916 return 1;
917}
918
Daniel Veillard4255d502002-04-16 15:50:10 +0000919/**
920 * xmlSchemaValidatePredefinedType:
921 * @type: the predefined type
922 * @value: the value to check
923 * @val: the return computed value
924 *
925 * Check that a value conforms to the lexical space of the predefined type.
926 * if true a value is computed and returned in @val.
927 *
928 * Returns 0 if this validates, a positive error code number otherwise
929 * and -1 in case of internal or API error.
930 */
931int
932xmlSchemaValidatePredefinedType(xmlSchemaTypePtr type, const xmlChar *value,
933 xmlSchemaValPtr *val) {
934 xmlSchemaValPtr v;
935
936 if (xmlSchemaTypesInitialized == 0)
937 return(-1);
938 if (type == NULL)
939 return(-1);
Daniel Veillard5a872412002-05-22 06:40:27 +0000940
Daniel Veillard4255d502002-04-16 15:50:10 +0000941 if (val != NULL)
942 *val = NULL;
943 if (type == xmlSchemaTypeStringDef) {
944 return(0);
945 } else if (type == xmlSchemaTypeAnyTypeDef) {
946 return(0);
947 } else if (type == xmlSchemaTypeAnySimpleTypeDef) {
948 return(0);
949 } else if (type == xmlSchemaTypeNmtoken) {
950 if (xmlValidateNmtokenValue(value))
951 return(0);
952 return(1);
953 } else if (type == xmlSchemaTypeDecimalDef) {
954 const xmlChar *cur = value, *tmp;
Daniel Veillard5a872412002-05-22 06:40:27 +0000955 int frac = 0, len, neg = 0;
Daniel Veillard4255d502002-04-16 15:50:10 +0000956 unsigned long base = 0;
957 if (cur == NULL)
958 return(1);
959 if (*cur == '+')
960 cur++;
961 else if (*cur == '-') {
962 neg = 1;
963 cur++;
964 }
965 tmp = cur;
966 while ((*cur >= '0') && (*cur <= '9')) {
967 base = base * 10 + (*cur - '0');
968 cur++;
969 }
Daniel Veillard5a872412002-05-22 06:40:27 +0000970 len = cur - tmp;
Daniel Veillard4255d502002-04-16 15:50:10 +0000971 if (*cur == '.') {
972 cur++;
973 tmp = cur;
974 while ((*cur >= '0') && (*cur <= '9')) {
975 base = base * 10 + (*cur - '0');
976 cur++;
977 }
978 frac = cur - tmp;
979 }
980 if (*cur != 0)
981 return(1);
982 if (val != NULL) {
983 v = xmlSchemaNewValue(XML_SCHEMAS_DECIMAL);
984 if (v != NULL) {
985 v->value.decimal.base = base;
986 v->value.decimal.sign = neg;
987 v->value.decimal.frac = frac;
Daniel Veillard5a872412002-05-22 06:40:27 +0000988 v->value.decimal.total = frac + len;
Daniel Veillard4255d502002-04-16 15:50:10 +0000989 *val = v;
990 }
991 }
992 return(0);
Daniel Veillard070803b2002-05-03 07:29:38 +0000993 } else if (type == xmlSchemaTypeDurationDef) {
Daniel Veillard5a872412002-05-22 06:40:27 +0000994 return xmlSchemaValidateDuration(type, value, val);
Daniel Veillard070803b2002-05-03 07:29:38 +0000995 } else if ((type == xmlSchemaTypeDatetimeDef) ||
996 (type == xmlSchemaTypeTimeDef) ||
997 (type == xmlSchemaTypeDateDef) ||
998 (type == xmlSchemaTypeGYearDef) ||
999 (type == xmlSchemaTypeGYearMonthDef) ||
1000 (type == xmlSchemaTypeGMonthDef) ||
1001 (type == xmlSchemaTypeGMonthDayDef) ||
1002 (type == xmlSchemaTypeGDayDef)) {
Daniel Veillard5a872412002-05-22 06:40:27 +00001003 return xmlSchemaValidateDates(type, value, val);
Daniel Veillard4255d502002-04-16 15:50:10 +00001004 } else if (type == xmlSchemaTypePositiveIntegerDef) {
1005 const xmlChar *cur = value;
1006 unsigned long base = 0;
1007 int total = 0;
1008 if (cur == NULL)
1009 return(1);
1010 if (*cur == '+')
1011 cur++;
1012 while ((*cur >= '0') && (*cur <= '9')) {
1013 base = base * 10 + (*cur - '0');
1014 total++;
1015 cur++;
1016 }
1017 if (*cur != 0)
1018 return(1);
1019 if (val != NULL) {
1020 v = xmlSchemaNewValue(XML_SCHEMAS_DECIMAL);
1021 if (v != NULL) {
1022 v->value.decimal.base = base;
1023 v->value.decimal.sign = 0;
1024 v->value.decimal.frac = 0;
1025 v->value.decimal.total = total;
1026 *val = v;
1027 }
1028 }
1029 return(0);
1030 } else if (type == xmlSchemaTypeNonNegativeIntegerDef) {
1031 const xmlChar *cur = value;
1032 unsigned long base = 0;
1033 int total = 0;
1034 int sign = 0;
1035 if (cur == NULL)
1036 return(1);
1037 if (*cur == '-') {
1038 sign = 1;
1039 cur++;
1040 } else if (*cur == '+')
1041 cur++;
1042 while ((*cur >= '0') && (*cur <= '9')) {
1043 base = base * 10 + (*cur - '0');
1044 total++;
1045 cur++;
1046 }
1047 if (*cur != 0)
1048 return(1);
1049 if ((sign == 1) && (base != 0))
1050 return(1);
1051 if (val != NULL) {
1052 v = xmlSchemaNewValue(XML_SCHEMAS_DECIMAL);
1053 if (v != NULL) {
1054 v->value.decimal.base = base;
1055 v->value.decimal.sign = 0;
1056 v->value.decimal.frac = 0;
1057 v->value.decimal.total = total;
1058 *val = v;
1059 }
1060 }
1061 return(0);
1062 } else {
1063 TODO
1064 return(0);
1065 }
1066}
1067
1068/**
1069 * xmlSchemaCompareDecimals:
1070 * @x: a first decimal value
1071 * @y: a second decimal value
1072 *
1073 * Compare 2 decimals
1074 *
1075 * Returns -1 if x < y, 0 if x == y, 1 if x > y and -2 in case of error
1076 */
1077static int
1078xmlSchemaCompareDecimals(xmlSchemaValPtr x, xmlSchemaValPtr y)
1079{
1080 xmlSchemaValPtr swp;
1081 int order = 1;
1082 unsigned long tmp;
1083
1084 if ((x->value.decimal.sign) && (x->value.decimal.sign))
1085 order = -1;
1086 else if (x->value.decimal.sign)
1087 return (-1);
1088 else if (y->value.decimal.sign)
1089 return (1);
1090 if (x->value.decimal.frac == y->value.decimal.frac) {
1091 if (x->value.decimal.base < y->value.decimal.base)
1092 return (-1);
1093 return (x->value.decimal.base > y->value.decimal.base);
1094 }
1095 if (y->value.decimal.frac > x->value.decimal.frac) {
1096 swp = y;
1097 y = x;
1098 x = swp;
1099 order = -order;
1100 }
1101 tmp =
1102 x->value.decimal.base / powten[x->value.decimal.frac -
1103 y->value.decimal.frac];
1104 if (tmp > y->value.decimal.base)
1105 return (order);
1106 if (tmp < y->value.decimal.base)
1107 return (-order);
1108 tmp =
1109 y->value.decimal.base * powten[x->value.decimal.frac -
1110 y->value.decimal.frac];
1111 if (x->value.decimal.base < tmp)
1112 return (-order);
1113 if (x->value.decimal.base == tmp)
1114 return (0);
1115 return (order);
1116}
1117
1118/**
Daniel Veillard070803b2002-05-03 07:29:38 +00001119 * xmlSchemaCompareDurations:
1120 * @x: a first duration value
1121 * @y: a second duration value
1122 *
1123 * Compare 2 durations
1124 *
1125 * Returns -1 if x < y, 0 if x == y, 1 if x > y, 2 if x <> y, and -2 in
1126 * case of error
1127 */
1128static int
1129xmlSchemaCompareDurations(xmlSchemaValPtr x, xmlSchemaValPtr y)
1130{
1131 long carry, mon, day;
1132 double sec;
1133 long xmon, xday, myear, lyear, minday, maxday;
1134 static const long dayRange [2][12] = {
1135 { 0, 28, 59, 89, 120, 150, 181, 212, 242, 273, 303, 334, },
1136 { 0, 31, 62, 92, 123, 153, 184, 215, 245, 276, 306, 337} };
1137
1138 if ((x == NULL) || (y == NULL))
Daniel Veillard5a872412002-05-22 06:40:27 +00001139 return -2;
Daniel Veillard070803b2002-05-03 07:29:38 +00001140
1141 /* months */
1142 mon = x->value.dur.mon - y->value.dur.mon;
1143
1144 /* seconds */
1145 sec = x->value.dur.sec - y->value.dur.sec;
1146 carry = (long)sec / SECS_PER_DAY;
1147 sec -= (double)(carry * SECS_PER_DAY);
1148
1149 /* days */
1150 day = x->value.dur.day - y->value.dur.day + carry;
1151
1152 /* easy test */
1153 if (mon == 0) {
1154 if (day == 0)
1155 if (sec == 0.0)
1156 return 0;
1157 else if (sec < 0.0)
1158 return -1;
1159 else
1160 return 1;
1161 else if (day < 0)
1162 return -1;
1163 else
1164 return 1;
1165 }
1166
1167 if (mon > 0) {
1168 if ((day >= 0) && (sec >= 0.0))
1169 return 1;
1170 else {
1171 xmon = mon;
1172 xday = -day;
1173 }
1174 } else if ((day <= 0) && (sec <= 0.0)) {
1175 return -1;
1176 } else {
1177 xmon = -mon;
1178 xday = day;
1179 }
1180
1181 myear = xmon / 12;
1182 lyear = myear / 4;
1183 minday = (myear * 365) + (lyear != 0 ? lyear - 1 : 0);
1184 maxday = (myear * 365) + (lyear != 0 ? lyear + 1 : 0);
1185
1186 xmon = xmon % 12;
1187 minday += dayRange[0][xmon];
1188 maxday += dayRange[1][xmon];
1189
1190 if (maxday < xday)
1191 return 1;
1192 else if (minday > xday)
1193 return -1;
1194
1195 /* indeterminate */
Daniel Veillard5a872412002-05-22 06:40:27 +00001196 return 2;
1197}
1198
1199/*
1200 * macros for adding date/times and durations
1201 */
1202#define FQUOTIENT(a,b) (floor(((double)a/(double)b)))
1203#define MODULO(a,b) (a - FQUOTIENT(a,b) * b)
1204#define FQUOTIENT_RANGE(a,low,high) (FQUOTIENT((a-low),(high-low)))
1205#define MODULO_RANGE(a,low,high) ((MODULO((a-low),(high-low)))+low)
1206
1207/**
1208 * _xmlSchemaDateAdd:
1209 * @dt: an #xmlSchemaValPtr
1210 * @dur: an #xmlSchemaValPtr of type #XS_DURATION
1211 *
1212 * Compute a new date/time from @dt and @dur. This function assumes @dt
1213 * is either #XML_SCHEMAS_DATETIME, #XML_SCHEMAS_DATE, #XML_SCHEMAS_GYEARMONTH,
1214 * or #XML_SCHEMAS_GYEAR.
1215 *
1216 * Returns date/time pointer or NULL.
1217 */
1218static xmlSchemaValPtr
1219_xmlSchemaDateAdd (xmlSchemaValPtr dt, xmlSchemaValPtr dur)
1220{
1221 xmlSchemaValPtr ret;
1222 long carry, tempdays, temp;
1223 xmlSchemaValDatePtr r, d;
1224 xmlSchemaValDurationPtr u;
1225
1226 if ((dt == NULL) || (dur == NULL))
1227 return NULL;
1228
1229 ret = xmlSchemaNewValue(dt->type);
1230 if (ret == NULL)
1231 return NULL;
1232
1233 r = &(ret->value.date);
1234 d = &(dt->value.date);
1235 u = &(dur->value.dur);
1236
1237 /* normalization */
1238 if (d->mon == 0)
1239 d->mon = 1;
1240
1241 /* normalize for time zone offset */
1242 u->sec -= (d->tzo * 60);
1243 d->tzo = 0;
1244
1245 /* normalization */
1246 if (d->day == 0)
1247 d->day = 1;
1248
1249 /* month */
1250 carry = d->mon + u->mon;
1251 r->mon = MODULO_RANGE(carry, 1, 13);
1252 carry = FQUOTIENT_RANGE(carry, 1, 13);
1253
1254 /* year (may be modified later) */
1255 r->year = d->year + carry;
1256 if (r->year == 0) {
1257 if (d->year > 0)
1258 r->year--;
1259 else
1260 r->year++;
1261 }
1262
1263 /* time zone */
1264 r->tzo = d->tzo;
1265 r->tz_flag = d->tz_flag;
1266
1267 /* seconds */
1268 r->sec = d->sec + u->sec;
1269 carry = FQUOTIENT((long)r->sec, 60);
1270 if (r->sec != 0.0) {
1271 r->sec = MODULO(r->sec, 60.0);
1272 }
1273
1274 /* minute */
1275 carry += d->min;
1276 r->min = MODULO(carry, 60);
1277 carry = FQUOTIENT(carry, 60);
1278
1279 /* hours */
1280 carry += d->hour;
1281 r->hour = MODULO(carry, 24);
1282 carry = FQUOTIENT(carry, 24);
1283
1284 /*
1285 * days
1286 * Note we use tempdays because the temporary values may need more
1287 * than 5 bits
1288 */
1289 if ((VALID_YEAR(r->year)) && (VALID_MONTH(r->mon)) &&
1290 (d->day > MAX_DAYINMONTH(r->year, r->mon)))
1291 tempdays = MAX_DAYINMONTH(r->year, r->mon);
1292 else if (d->day < 1)
1293 tempdays = 1;
1294 else
1295 tempdays = d->day;
1296
1297 tempdays += u->day + carry;
1298
1299 while (1) {
1300 if (tempdays < 1) {
1301 long tmon = MODULO_RANGE(r->mon-1, 1, 13);
1302 long tyr = r->year + FQUOTIENT_RANGE(r->mon-1, 1, 13);
1303 if (tyr == 0)
1304 tyr--;
1305 tempdays += MAX_DAYINMONTH(tyr, tmon);
1306 carry = -1;
1307 } else if (tempdays > MAX_DAYINMONTH(r->year, r->mon)) {
1308 tempdays = tempdays - MAX_DAYINMONTH(r->year, r->mon);
1309 carry = 1;
1310 } else
1311 break;
1312
1313 temp = r->mon + carry;
1314 r->mon = MODULO_RANGE(temp, 1, 13);
1315 r->year = r->year + FQUOTIENT_RANGE(temp, 1, 13);
1316 if (r->year == 0) {
1317 if (temp < 1)
1318 r->year--;
1319 else
1320 r->year++;
1321 }
1322 }
1323
1324 r->day = tempdays;
1325
1326 /*
1327 * adjust the date/time type to the date values
1328 */
1329 if (ret->type != XML_SCHEMAS_DATETIME) {
1330 if ((r->hour) || (r->min) || (r->sec))
1331 ret->type = XML_SCHEMAS_DATETIME;
1332 else if (ret->type != XML_SCHEMAS_DATE) {
1333 if ((r->mon != 1) && (r->day != 1))
1334 ret->type = XML_SCHEMAS_DATE;
1335 else if ((ret->type != XML_SCHEMAS_GYEARMONTH) && (r->mon != 1))
1336 ret->type = XML_SCHEMAS_GYEARMONTH;
1337 }
1338 }
1339
1340 return ret;
1341}
1342
1343/**
1344 * xmlSchemaDupVal:
1345 * @v: value to duplicate
1346 *
1347 * returns a duplicated value.
1348 */
1349static xmlSchemaValPtr
1350xmlSchemaDupVal (xmlSchemaValPtr v)
1351{
1352 xmlSchemaValPtr ret = xmlSchemaNewValue(v->type);
1353 if (ret == NULL)
1354 return ret;
1355
1356 memcpy(ret, v, sizeof(xmlSchemaVal));
1357 return ret;
1358}
1359
1360/**
1361 * xmlSchemaDateNormalize:
1362 * @dt: an #xmlSchemaValPtr
1363 *
1364 * Normalize @dt to GMT time.
1365 *
1366 */
1367static xmlSchemaValPtr
1368xmlSchemaDateNormalize (xmlSchemaValPtr dt, double offset)
1369{
1370 xmlSchemaValPtr dur, ret;
1371
1372 if (dt == NULL)
1373 return NULL;
1374
1375 if (((dt->type != XML_SCHEMAS_TIME) &&
1376 (dt->type != XML_SCHEMAS_DATETIME)) || (dt->value.date.tzo == 0))
1377 return xmlSchemaDupVal(dt);
1378
1379 dur = xmlSchemaNewValue(XML_SCHEMAS_DURATION);
1380 if (dur == NULL)
1381 return NULL;
1382
1383 dur->value.date.sec -= offset;
1384
1385 ret = _xmlSchemaDateAdd(dt, dur);
1386 if (ret == NULL)
1387 return NULL;
1388
1389 xmlSchemaFreeValue(dur);
1390
1391 /* ret->value.date.tzo = 0; */
1392 return ret;
1393}
1394
1395/**
1396 * _xmlSchemaDateCastYMToDays:
1397 * @dt: an #xmlSchemaValPtr
1398 *
1399 * Convert mon and year of @dt to total number of days. Take the
1400 * number of years since (or before) 1 AD and add the number of leap
1401 * years. This is a function because negative
1402 * years must be handled a little differently and there is no zero year.
1403 *
1404 * Returns number of days.
1405 */
1406static long
1407_xmlSchemaDateCastYMToDays (const xmlSchemaValPtr dt)
1408{
1409 long ret;
1410
1411 if (dt->value.date.year < 0)
1412 ret = (dt->value.date.year * 365) +
1413 (((dt->value.date.year+1)/4)-((dt->value.date.year+1)/100)+
1414 ((dt->value.date.year+1)/400)) +
1415 DAY_IN_YEAR(0, dt->value.date.mon, dt->value.date.year);
1416 else
1417 ret = ((dt->value.date.year-1) * 365) +
1418 (((dt->value.date.year-1)/4)-((dt->value.date.year-1)/100)+
1419 ((dt->value.date.year-1)/400)) +
1420 DAY_IN_YEAR(0, dt->value.date.mon, dt->value.date.year);
1421
1422 return ret;
1423}
1424
1425/**
1426 * TIME_TO_NUMBER:
1427 * @dt: an #xmlSchemaValPtr
1428 *
1429 * Calculates the number of seconds in the time portion of @dt.
1430 *
1431 * Returns seconds.
1432 */
1433#define TIME_TO_NUMBER(dt) \
1434 ((double)((dt->value.date.hour * SECS_PER_HOUR) + \
1435 (dt->value.date.min * SECS_PER_MIN)) + dt->value.date.sec)
1436
1437/**
1438 * xmlSchemaCompareDates:
1439 * @x: a first date/time value
1440 * @y: a second date/time value
1441 *
1442 * Compare 2 date/times
1443 *
1444 * Returns -1 if x < y, 0 if x == y, 1 if x > y, 2 if x <> y, and -2 in
1445 * case of error
1446 */
1447static int
1448xmlSchemaCompareDates (xmlSchemaValPtr x, xmlSchemaValPtr y)
1449{
1450 unsigned char xmask, ymask, xor_mask, and_mask;
1451 xmlSchemaValPtr p1, p2, q1, q2;
1452 long p1d, p2d, q1d, q2d;
1453
1454 if ((x == NULL) || (y == NULL))
1455 return -2;
1456
1457 if (x->value.date.tz_flag) {
1458
1459 if (!y->value.date.tz_flag) {
1460 p1 = xmlSchemaDateNormalize(x, 0);
1461 p1d = _xmlSchemaDateCastYMToDays(p1) + p1->value.date.day;
1462 /* normalize y + 14:00 */
1463 q1 = xmlSchemaDateNormalize(y, (14 * SECS_PER_HOUR));
1464
1465 q1d = _xmlSchemaDateCastYMToDays(q1) + q1->value.date.day;
Daniel Veillardfdc91562002-07-01 21:52:03 +00001466 if (p1d < q1d) {
1467 xmlSchemaFreeValue(p1);
1468 xmlSchemaFreeValue(q1);
Daniel Veillard5a872412002-05-22 06:40:27 +00001469 return -1;
Daniel Veillardfdc91562002-07-01 21:52:03 +00001470 } else if (p1d == q1d) {
Daniel Veillard5a872412002-05-22 06:40:27 +00001471 double sec;
1472
1473 sec = TIME_TO_NUMBER(p1) - TIME_TO_NUMBER(q1);
Daniel Veillardfdc91562002-07-01 21:52:03 +00001474 if (sec < 0.0) {
1475 xmlSchemaFreeValue(p1);
1476 xmlSchemaFreeValue(q1);
Daniel Veillard5a872412002-05-22 06:40:27 +00001477 return -1;
Daniel Veillardfdc91562002-07-01 21:52:03 +00001478 } else {
Daniel Veillard5a872412002-05-22 06:40:27 +00001479 /* normalize y - 14:00 */
1480 q2 = xmlSchemaDateNormalize(y, -(14 * SECS_PER_HOUR));
1481 q2d = _xmlSchemaDateCastYMToDays(q2) + q2->value.date.day;
Daniel Veillardfdc91562002-07-01 21:52:03 +00001482 xmlSchemaFreeValue(p1);
1483 xmlSchemaFreeValue(q1);
1484 xmlSchemaFreeValue(q2);
Daniel Veillard5a872412002-05-22 06:40:27 +00001485 if (p1d > q2d)
1486 return 1;
1487 else if (p1d == q2d) {
1488 sec = TIME_TO_NUMBER(p1) - TIME_TO_NUMBER(q2);
1489 if (sec > 0.0)
1490 return 1;
1491 else
1492 return 2; /* indeterminate */
1493 }
1494 }
Daniel Veillardfdc91562002-07-01 21:52:03 +00001495 } else {
1496 xmlSchemaFreeValue(p1);
1497 xmlSchemaFreeValue(q1);
1498 }
Daniel Veillard5a872412002-05-22 06:40:27 +00001499 }
1500 } else if (y->value.date.tz_flag) {
1501 q1 = xmlSchemaDateNormalize(y, 0);
1502 q1d = _xmlSchemaDateCastYMToDays(q1) + q1->value.date.day;
1503
1504 /* normalize x - 14:00 */
1505 p1 = xmlSchemaDateNormalize(x, -(14 * SECS_PER_HOUR));
1506 p1d = _xmlSchemaDateCastYMToDays(p1) + p1->value.date.day;
1507
Daniel Veillardfdc91562002-07-01 21:52:03 +00001508 if (p1d < q1d) {
1509 xmlSchemaFreeValue(p1);
1510 xmlSchemaFreeValue(q1);
Daniel Veillard5a872412002-05-22 06:40:27 +00001511 return -1;
Daniel Veillardfdc91562002-07-01 21:52:03 +00001512 } else if (p1d == q1d) {
Daniel Veillard5a872412002-05-22 06:40:27 +00001513 double sec;
1514
1515 sec = TIME_TO_NUMBER(p1) - TIME_TO_NUMBER(q1);
Daniel Veillardfdc91562002-07-01 21:52:03 +00001516 if (sec < 0.0) {
1517 xmlSchemaFreeValue(p1);
1518 xmlSchemaFreeValue(q1);
Daniel Veillard5a872412002-05-22 06:40:27 +00001519 return -1;
Daniel Veillardfdc91562002-07-01 21:52:03 +00001520 } else {
Daniel Veillard5a872412002-05-22 06:40:27 +00001521 /* normalize x + 14:00 */
1522 p2 = xmlSchemaDateNormalize(x, (14 * SECS_PER_HOUR));
1523 p2d = _xmlSchemaDateCastYMToDays(p2) + p2->value.date.day;
1524
Daniel Veillardfdc91562002-07-01 21:52:03 +00001525 xmlSchemaFreeValue(p1);
1526 xmlSchemaFreeValue(q1);
1527 xmlSchemaFreeValue(p2);
Daniel Veillard5a872412002-05-22 06:40:27 +00001528 if (p2d > q1d)
1529 return 1;
1530 else if (p2d == q1d) {
1531 sec = TIME_TO_NUMBER(p2) - TIME_TO_NUMBER(q1);
1532 if (sec > 0.0)
1533 return 1;
1534 else
1535 return 2; /* indeterminate */
1536 }
1537 }
Daniel Veillardfdc91562002-07-01 21:52:03 +00001538 } else {
1539 xmlSchemaFreeValue(p1);
1540 xmlSchemaFreeValue(q1);
Daniel Veillard5a872412002-05-22 06:40:27 +00001541 }
1542 }
1543
1544 /*
1545 * if the same type then calculate the difference
1546 */
1547 if (x->type == y->type) {
1548 q1 = xmlSchemaDateNormalize(y, 0);
1549 q1d = _xmlSchemaDateCastYMToDays(q1) + q1->value.date.day;
1550
1551 p1 = xmlSchemaDateNormalize(x, 0);
1552 p1d = _xmlSchemaDateCastYMToDays(p1) + p1->value.date.day;
1553
Daniel Veillardfdc91562002-07-01 21:52:03 +00001554 if (p1d < q1d) {
1555 xmlSchemaFreeValue(p1);
1556 xmlSchemaFreeValue(q1);
Daniel Veillard5a872412002-05-22 06:40:27 +00001557 return -1;
Daniel Veillardfdc91562002-07-01 21:52:03 +00001558 } else if (p1d > q1d) {
1559 xmlSchemaFreeValue(p1);
1560 xmlSchemaFreeValue(q1);
Daniel Veillard5a872412002-05-22 06:40:27 +00001561 return 1;
Daniel Veillardfdc91562002-07-01 21:52:03 +00001562 } else {
Daniel Veillard5a872412002-05-22 06:40:27 +00001563 double sec;
1564
1565 sec = TIME_TO_NUMBER(p1) - TIME_TO_NUMBER(q1);
Daniel Veillardfdc91562002-07-01 21:52:03 +00001566 xmlSchemaFreeValue(p1);
1567 xmlSchemaFreeValue(q1);
Daniel Veillard5a872412002-05-22 06:40:27 +00001568 if (sec < 0.0)
1569 return -1;
1570 else if (sec > 0.0)
1571 return 1;
1572
1573 }
1574 return 0;
1575 }
1576
1577 switch (x->type) {
1578 case XML_SCHEMAS_DATETIME:
1579 xmask = 0xf;
1580 break;
1581 case XML_SCHEMAS_DATE:
1582 xmask = 0x7;
1583 break;
1584 case XML_SCHEMAS_GYEAR:
1585 xmask = 0x1;
1586 break;
1587 case XML_SCHEMAS_GMONTH:
1588 xmask = 0x2;
1589 break;
1590 case XML_SCHEMAS_GDAY:
1591 xmask = 0x3;
1592 break;
1593 case XML_SCHEMAS_GYEARMONTH:
1594 xmask = 0x3;
1595 break;
1596 case XML_SCHEMAS_GMONTHDAY:
1597 xmask = 0x6;
1598 break;
1599 case XML_SCHEMAS_TIME:
1600 xmask = 0x8;
1601 break;
1602 default:
1603 xmask = 0;
1604 break;
1605 }
1606
1607 switch (y->type) {
1608 case XML_SCHEMAS_DATETIME:
1609 ymask = 0xf;
1610 break;
1611 case XML_SCHEMAS_DATE:
1612 ymask = 0x7;
1613 break;
1614 case XML_SCHEMAS_GYEAR:
1615 ymask = 0x1;
1616 break;
1617 case XML_SCHEMAS_GMONTH:
1618 ymask = 0x2;
1619 break;
1620 case XML_SCHEMAS_GDAY:
1621 ymask = 0x3;
1622 break;
1623 case XML_SCHEMAS_GYEARMONTH:
1624 ymask = 0x3;
1625 break;
1626 case XML_SCHEMAS_GMONTHDAY:
1627 ymask = 0x6;
1628 break;
1629 case XML_SCHEMAS_TIME:
1630 ymask = 0x8;
1631 break;
1632 default:
1633 ymask = 0;
1634 break;
1635 }
1636
1637 xor_mask = xmask ^ ymask; /* mark type differences */
1638 and_mask = xmask & ymask; /* mark field specification */
1639
1640 /* year */
1641 if (xor_mask & 1)
1642 return 2; /* indeterminate */
1643 else if (and_mask & 1) {
1644 if (x->value.date.year < y->value.date.year)
1645 return -1;
1646 else if (x->value.date.year > y->value.date.year)
1647 return 1;
1648 }
1649
1650 /* month */
1651 if (xor_mask & 2)
1652 return 2; /* indeterminate */
1653 else if (and_mask & 2) {
1654 if (x->value.date.mon < y->value.date.mon)
1655 return -1;
1656 else if (x->value.date.mon > y->value.date.mon)
1657 return 1;
1658 }
1659
1660 /* day */
1661 if (xor_mask & 4)
1662 return 2; /* indeterminate */
1663 else if (and_mask & 4) {
1664 if (x->value.date.day < y->value.date.day)
1665 return -1;
1666 else if (x->value.date.day > y->value.date.day)
1667 return 1;
1668 }
1669
1670 /* time */
1671 if (xor_mask & 8)
1672 return 2; /* indeterminate */
1673 else if (and_mask & 8) {
1674 if (x->value.date.hour < y->value.date.hour)
1675 return -1;
1676 else if (x->value.date.hour > y->value.date.hour)
1677 return 1;
1678 else if (x->value.date.min < y->value.date.min)
1679 return -1;
1680 else if (x->value.date.min > y->value.date.min)
1681 return 1;
1682 else if (x->value.date.sec < y->value.date.sec)
1683 return -1;
1684 else if (x->value.date.sec > y->value.date.sec)
1685 return 1;
1686 }
1687
Daniel Veillard070803b2002-05-03 07:29:38 +00001688 return 0;
1689}
1690
1691/**
Daniel Veillard4255d502002-04-16 15:50:10 +00001692 * xmlSchemaCompareValues:
1693 * @x: a first value
1694 * @y: a second value
1695 *
1696 * Compare 2 values
1697 *
Daniel Veillard5a872412002-05-22 06:40:27 +00001698 * Returns -1 if x < y, 0 if x == y, 1 if x > y, 2 if x <> y, and -2 in
1699 * case of error
Daniel Veillard4255d502002-04-16 15:50:10 +00001700 */
Daniel Veillarde19fc232002-04-22 16:01:24 +00001701static int
Daniel Veillard4255d502002-04-16 15:50:10 +00001702xmlSchemaCompareValues(xmlSchemaValPtr x, xmlSchemaValPtr y) {
1703 if ((x == NULL) || (y == NULL))
1704 return(-2);
1705
1706 switch (x->type) {
1707 case XML_SCHEMAS_STRING:
1708 TODO
1709 case XML_SCHEMAS_DECIMAL:
1710 if (y->type == XML_SCHEMAS_DECIMAL)
1711 return(xmlSchemaCompareDecimals(x, y));
Daniel Veillard5a872412002-05-22 06:40:27 +00001712 return(-2);
Daniel Veillard070803b2002-05-03 07:29:38 +00001713 case XML_SCHEMAS_DURATION:
1714 if (y->type == XML_SCHEMAS_DURATION)
1715 return(xmlSchemaCompareDurations(x, y));
Daniel Veillard5a872412002-05-22 06:40:27 +00001716 return(-2);
1717 case XML_SCHEMAS_TIME:
1718 case XML_SCHEMAS_GDAY:
1719 case XML_SCHEMAS_GMONTH:
1720 case XML_SCHEMAS_GMONTHDAY:
1721 case XML_SCHEMAS_GYEAR:
1722 case XML_SCHEMAS_GYEARMONTH:
1723 case XML_SCHEMAS_DATE:
1724 case XML_SCHEMAS_DATETIME:
1725 if ((y->type == XML_SCHEMAS_DATETIME) ||
1726 (y->type == XML_SCHEMAS_TIME) ||
1727 (y->type == XML_SCHEMAS_GDAY) ||
1728 (y->type == XML_SCHEMAS_GMONTH) ||
1729 (y->type == XML_SCHEMAS_GMONTHDAY) ||
1730 (y->type == XML_SCHEMAS_GYEAR) ||
1731 (y->type == XML_SCHEMAS_DATE) ||
1732 (y->type == XML_SCHEMAS_GYEARMONTH))
1733 return (xmlSchemaCompareDates(x, y));
1734
1735 return (-2);
Daniel Veillard4255d502002-04-16 15:50:10 +00001736 default:
1737 TODO
1738 }
Daniel Veillard5a872412002-05-22 06:40:27 +00001739 return -2;
Daniel Veillard4255d502002-04-16 15:50:10 +00001740}
1741
1742/**
1743 * xmlSchemaValidateFacet:
1744 * @type: the type declaration
1745 * @facet: the facet to check
1746 * @value: the lexical repr of the value to validate
1747 * @val: the precomputed value
1748 *
1749 * Check a value against a facet condition
1750 *
1751 * Returns 0 if the element is schemas valid, a positive error code
1752 * number otherwise and -1 in case of internal or API error.
1753 */
1754int
1755xmlSchemaValidateFacet(xmlSchemaTypePtr base, xmlSchemaFacetPtr facet,
1756 const xmlChar *value, xmlSchemaValPtr val)
1757{
1758 int ret;
1759
1760 switch (facet->type) {
1761 case XML_SCHEMA_FACET_PATTERN:
1762 ret = xmlRegexpExec(facet->regexp, value);
1763 if (ret == 1)
1764 return(0);
1765 if (ret == 0) {
1766 TODO /* error code */
1767 return(1);
1768 }
1769 return(ret);
1770 case XML_SCHEMA_FACET_MAXEXCLUSIVE:
1771 ret = xmlSchemaCompareValues(val, facet->val);
1772 if (ret == -2) {
1773 TODO /* error code */
1774 return(-1);
1775 }
1776 if (ret == -1)
1777 return(0);
Daniel Veillard5a872412002-05-22 06:40:27 +00001778 /* error code */
Daniel Veillard4255d502002-04-16 15:50:10 +00001779 return(1);
Daniel Veillard070803b2002-05-03 07:29:38 +00001780 case XML_SCHEMA_FACET_MAXINCLUSIVE:
1781 ret = xmlSchemaCompareValues(val, facet->val);
1782 if (ret == -2) {
1783 TODO /* error code */
1784 return(-1);
1785 }
1786 if ((ret == -1) || (ret == 0))
1787 return(0);
Daniel Veillard5a872412002-05-22 06:40:27 +00001788 /* error code */
Daniel Veillard070803b2002-05-03 07:29:38 +00001789 return(1);
1790 case XML_SCHEMA_FACET_MINEXCLUSIVE:
1791 ret = xmlSchemaCompareValues(val, facet->val);
1792 if (ret == -2) {
1793 TODO /* error code */
1794 return(-1);
1795 }
1796 if (ret == 1)
1797 return(0);
Daniel Veillard5a872412002-05-22 06:40:27 +00001798 /* error code */
Daniel Veillard070803b2002-05-03 07:29:38 +00001799 return(1);
1800 case XML_SCHEMA_FACET_MININCLUSIVE:
1801 ret = xmlSchemaCompareValues(val, facet->val);
1802 if (ret == -2) {
1803 TODO /* error code */
1804 return(-1);
1805 }
1806 if ((ret == 1) || (ret == 0))
1807 return(0);
Daniel Veillard5a872412002-05-22 06:40:27 +00001808 /* error code */
Daniel Veillard070803b2002-05-03 07:29:38 +00001809 return(1);
Daniel Veillard8651f532002-04-17 09:06:27 +00001810 case XML_SCHEMA_FACET_WHITESPACE:
1811 TODO /* whitespaces */
1812 return(0);
Daniel Veillarde19fc232002-04-22 16:01:24 +00001813 case XML_SCHEMA_FACET_MAXLENGTH:
1814 if ((facet->val != NULL) &&
1815 (facet->val->type == XML_SCHEMAS_DECIMAL) &&
1816 (facet->val->value.decimal.frac == 0)) {
1817 int len;
1818
1819 if (facet->val->value.decimal.sign == 1)
1820 return(1);
1821 len = xmlUTF8Strlen(value);
1822 if (len > facet->val->value.decimal.base)
1823 return(1);
1824 return(0);
1825 }
1826 TODO /* error code */
1827 return(1);
Daniel Veillard88c58912002-04-23 07:12:20 +00001828 case XML_SCHEMA_FACET_ENUMERATION:
1829 if ((facet->value != NULL) &&
1830 (xmlStrEqual(facet->value, value)))
1831 return(0);
1832 return(1);
Daniel Veillard4255d502002-04-16 15:50:10 +00001833 default:
1834 TODO
1835 }
1836 return(0);
1837}
1838
1839#endif /* LIBXML_SCHEMAS_ENABLED */