blob: 976b89f74c6332c30915d148fbbbbf7aedc606df [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 Veillard84d70a42002-09-16 10:51:38 +000054 XML_SCHEMAS_FLOAT,
55 XML_SCHEMAS_DOUBLE,
Daniel Veillard4255d502002-04-16 15:50:10 +000056 XML_SCHEMAS_,
57 XML_SCHEMAS_XXX
58} xmlSchemaValType;
59
60unsigned long powten[10] = {
61 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000L,
62 100000000L, 1000000000L
63};
64
Daniel Veillard070803b2002-05-03 07:29:38 +000065/* Date value */
66typedef struct _xmlSchemaValDate xmlSchemaValDate;
67typedef xmlSchemaValDate *xmlSchemaValDatePtr;
68struct _xmlSchemaValDate {
69 long year;
70 unsigned int mon :4; /* 1 <= mon <= 12 */
71 unsigned int day :5; /* 1 <= day <= 31 */
72 unsigned int hour :5; /* 0 <= hour <= 23 */
73 unsigned int min :6; /* 0 <= min <= 59 */
74 double sec;
75 int tz_flag :1; /* is tzo explicitely set? */
76 int tzo :11; /* -1440 <= tzo <= 1440 */
77};
78
79/* Duration value */
80typedef struct _xmlSchemaValDuration xmlSchemaValDuration;
81typedef xmlSchemaValDuration *xmlSchemaValDurationPtr;
82struct _xmlSchemaValDuration {
83 long mon; /* mon stores years also */
84 long day;
85 double sec; /* sec stores min and hour also */
86};
87
Daniel Veillard4255d502002-04-16 15:50:10 +000088typedef struct _xmlSchemaValDecimal xmlSchemaValDecimal;
89typedef xmlSchemaValDecimal *xmlSchemaValDecimalPtr;
90struct _xmlSchemaValDecimal {
91 /* would use long long but not portable */
92 unsigned long base;
93 unsigned int extra;
Daniel Veillard5a872412002-05-22 06:40:27 +000094 unsigned int sign:1;
Daniel Veillard4255d502002-04-16 15:50:10 +000095 int frac:7;
96 int total:8;
97};
98
99struct _xmlSchemaVal {
100 xmlSchemaValType type;
101 union {
Daniel Veillard5a872412002-05-22 06:40:27 +0000102 xmlSchemaValDecimal decimal;
Daniel Veillard070803b2002-05-03 07:29:38 +0000103 xmlSchemaValDate date;
104 xmlSchemaValDuration dur;
Daniel Veillard84d70a42002-09-16 10:51:38 +0000105 float f;
106 double d;
Daniel Veillard4255d502002-04-16 15:50:10 +0000107 } value;
108};
109
110static int xmlSchemaTypesInitialized = 0;
111static xmlHashTablePtr xmlSchemaTypesBank = NULL;
112
113static xmlSchemaTypePtr xmlSchemaTypeStringDef = NULL;
114static xmlSchemaTypePtr xmlSchemaTypeAnyTypeDef = NULL;
115static xmlSchemaTypePtr xmlSchemaTypeAnySimpleTypeDef = NULL;
116static xmlSchemaTypePtr xmlSchemaTypeDecimalDef = NULL;
Daniel Veillard070803b2002-05-03 07:29:38 +0000117static xmlSchemaTypePtr xmlSchemaTypeDatetimeDef = NULL;
Daniel Veillard4255d502002-04-16 15:50:10 +0000118static xmlSchemaTypePtr xmlSchemaTypeDateDef = NULL;
Daniel Veillard070803b2002-05-03 07:29:38 +0000119static xmlSchemaTypePtr xmlSchemaTypeTimeDef = NULL;
120static xmlSchemaTypePtr xmlSchemaTypeGYearDef = NULL;
121static xmlSchemaTypePtr xmlSchemaTypeGYearMonthDef = NULL;
122static xmlSchemaTypePtr xmlSchemaTypeGDayDef = NULL;
123static xmlSchemaTypePtr xmlSchemaTypeGMonthDayDef = NULL;
124static xmlSchemaTypePtr xmlSchemaTypeGMonthDef = NULL;
125static xmlSchemaTypePtr xmlSchemaTypeDurationDef = NULL;
Daniel Veillard4255d502002-04-16 15:50:10 +0000126static xmlSchemaTypePtr xmlSchemaTypePositiveIntegerDef = NULL;
127static xmlSchemaTypePtr xmlSchemaTypeNonNegativeIntegerDef = NULL;
128static xmlSchemaTypePtr xmlSchemaTypeNmtoken = NULL;
Daniel Veillard84d70a42002-09-16 10:51:38 +0000129static xmlSchemaTypePtr xmlSchemaTypeFloatDef = NULL;
130static xmlSchemaTypePtr xmlSchemaTypeDoubleDef = NULL;
Daniel Veillard4255d502002-04-16 15:50:10 +0000131
132/*
133 * xmlSchemaInitBasicType:
134 * @name: the type name
135 *
136 * Initialize one default type
137 */
138static xmlSchemaTypePtr
139xmlSchemaInitBasicType(const char *name) {
140 xmlSchemaTypePtr ret;
141
142 ret = (xmlSchemaTypePtr) xmlMalloc(sizeof(xmlSchemaType));
143 if (ret == NULL) {
144 xmlGenericError(xmlGenericErrorContext,
145 "Could not initilize type %s: out of memory\n", name);
146 return(NULL);
147 }
148 memset(ret, 0, sizeof(xmlSchemaType));
149 ret->name = xmlStrdup((const xmlChar *)name);
150 ret->type = XML_SCHEMA_TYPE_BASIC;
151 ret->contentType = XML_SCHEMA_CONTENT_BASIC;
152 xmlHashAddEntry2(xmlSchemaTypesBank, ret->name,
153 XML_SCHEMAS_NAMESPACE_NAME, ret);
154 return(ret);
155}
156
157/*
158 * xmlSchemaInitTypes:
159 *
160 * Initialize the default XML Schemas type library
161 */
162void
163xmlSchemaInitTypes(void) {
164 if (xmlSchemaTypesInitialized != 0)
165 return;
166 xmlSchemaTypesBank = xmlHashCreate(40);
167
168 xmlSchemaTypeStringDef = xmlSchemaInitBasicType("string");
169 xmlSchemaTypeAnyTypeDef = xmlSchemaInitBasicType("anyType");
170 xmlSchemaTypeAnySimpleTypeDef = xmlSchemaInitBasicType("anySimpleType");
171 xmlSchemaTypeDecimalDef = xmlSchemaInitBasicType("decimal");
172 xmlSchemaTypeDateDef = xmlSchemaInitBasicType("date");
Daniel Veillard070803b2002-05-03 07:29:38 +0000173 xmlSchemaTypeDatetimeDef = xmlSchemaInitBasicType("dateTime");
174 xmlSchemaTypeTimeDef = xmlSchemaInitBasicType("time");
175 xmlSchemaTypeGYearDef = xmlSchemaInitBasicType("gYear");
176 xmlSchemaTypeGYearMonthDef = xmlSchemaInitBasicType("gYearMonth");
177 xmlSchemaTypeGMonthDef = xmlSchemaInitBasicType("gMonth");
178 xmlSchemaTypeGMonthDayDef = xmlSchemaInitBasicType("gMonthDay");
179 xmlSchemaTypeGDayDef = xmlSchemaInitBasicType("gDay");
180 xmlSchemaTypeDurationDef = xmlSchemaInitBasicType("duration");
Daniel Veillard4255d502002-04-16 15:50:10 +0000181 xmlSchemaTypePositiveIntegerDef = xmlSchemaInitBasicType("positiveInteger");
182 xmlSchemaTypeNonNegativeIntegerDef =
183 xmlSchemaInitBasicType("nonNegativeInteger");
184 xmlSchemaTypeNmtoken = xmlSchemaInitBasicType("NMTOKEN");
Daniel Veillard84d70a42002-09-16 10:51:38 +0000185 xmlSchemaTypeFloatDef = xmlSchemaInitBasicType("float");
186 xmlSchemaTypeDoubleDef = xmlSchemaInitBasicType("double");
Daniel Veillard4255d502002-04-16 15:50:10 +0000187
188 xmlSchemaTypesInitialized = 1;
189}
190
191/**
192 * xmlSchemaCleanupTypes:
193 *
194 * Cleanup the default XML Schemas type library
195 */
196void
197xmlSchemaCleanupTypes(void) {
198 if (xmlSchemaTypesInitialized == 0)
199 return;
200 xmlHashFree(xmlSchemaTypesBank, (xmlHashDeallocator) xmlSchemaFreeType);
201 xmlSchemaTypesInitialized = 0;
202}
203
204/**
205 * xmlSchemaNewValue:
206 * @type: the value type
207 *
208 * Allocate a new simple type value
209 *
210 * Returns a pointer to the new value or NULL in case of error
211 */
212static xmlSchemaValPtr
213xmlSchemaNewValue(xmlSchemaValType type) {
214 xmlSchemaValPtr value;
215
216 value = (xmlSchemaValPtr) xmlMalloc(sizeof(xmlSchemaVal));
217 if (value == NULL) {
218 return(NULL);
219 }
220 memset(value, 0, sizeof(xmlSchemaVal));
221 value->type = type;
222 return(value);
223}
224
225/**
226 * xmlSchemaFreeValue:
227 * @value: the value to free
228 *
229 * Cleanup the default XML Schemas type library
230 */
231void
232xmlSchemaFreeValue(xmlSchemaValPtr value) {
233 if (value == NULL)
234 return;
235 xmlFree(value);
236}
237
238/**
239 * xmlSchemaGetPredefinedType:
240 * @name: the type name
241 * @ns: the URI of the namespace usually "http://www.w3.org/2001/XMLSchema"
242 *
243 * Lookup a type in the default XML Schemas type library
244 *
245 * Returns the type if found, NULL otherwise
246 */
247xmlSchemaTypePtr
248xmlSchemaGetPredefinedType(const xmlChar *name, const xmlChar *ns) {
249 if (xmlSchemaTypesInitialized == 0)
250 xmlSchemaInitTypes();
251 if (name == NULL)
252 return(NULL);
253 return((xmlSchemaTypePtr) xmlHashLookup2(xmlSchemaTypesBank, name, ns));
254}
Daniel Veillard070803b2002-05-03 07:29:38 +0000255
256/****************************************************************
257 * *
258 * Convenience macros and functions *
259 * *
260 ****************************************************************/
261
262#define IS_TZO_CHAR(c) \
263 ((c == 0) || (c == 'Z') || (c == '+') || (c == '-'))
264
265#define VALID_YEAR(yr) (yr != 0)
266#define VALID_MONTH(mon) ((mon >= 1) && (mon <= 12))
267/* VALID_DAY should only be used when month is unknown */
268#define VALID_DAY(day) ((day >= 1) && (day <= 31))
269#define VALID_HOUR(hr) ((hr >= 0) && (hr <= 23))
270#define VALID_MIN(min) ((min >= 0) && (min <= 59))
271#define VALID_SEC(sec) ((sec >= 0) && (sec < 60))
272#define VALID_TZO(tzo) ((tzo > -1440) && (tzo < 1440))
273#define IS_LEAP(y) \
274 (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
275
276static const long daysInMonth[12] =
277 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
278static const long daysInMonthLeap[12] =
279 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
280
Daniel Veillard5a872412002-05-22 06:40:27 +0000281#define MAX_DAYINMONTH(yr,mon) \
282 (IS_LEAP(yr) ? daysInMonthLeap[mon - 1] : daysInMonth[mon - 1])
283
Daniel Veillard070803b2002-05-03 07:29:38 +0000284#define VALID_MDAY(dt) \
285 (IS_LEAP(dt->year) ? \
286 (dt->day <= daysInMonthLeap[dt->mon - 1]) : \
287 (dt->day <= daysInMonth[dt->mon - 1]))
288
289#define VALID_DATE(dt) \
290 (VALID_YEAR(dt->year) && VALID_MONTH(dt->mon) && VALID_MDAY(dt))
291
292#define VALID_TIME(dt) \
293 (VALID_HOUR(dt->hour) && VALID_MIN(dt->min) && \
294 VALID_SEC(dt->sec) && VALID_TZO(dt->tzo))
295
296#define VALID_DATETIME(dt) \
297 (VALID_DATE(dt) && VALID_TIME(dt))
298
299#define SECS_PER_MIN (60)
300#define SECS_PER_HOUR (60 * SECS_PER_MIN)
301#define SECS_PER_DAY (24 * SECS_PER_HOUR)
302
Daniel Veillard5a872412002-05-22 06:40:27 +0000303static const long dayInYearByMonth[12] =
304 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
305static const long dayInLeapYearByMonth[12] =
306 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 };
307
308#define DAY_IN_YEAR(day, month, year) \
309 ((IS_LEAP(year) ? \
310 dayInLeapYearByMonth[month - 1] : \
311 dayInYearByMonth[month - 1]) + day)
312
313#ifdef DEBUG
314#define DEBUG_DATE(dt) \
315 xmlGenericError(xmlGenericErrorContext, \
316 "type=%o %04ld-%02u-%02uT%02u:%02u:%03f", \
317 dt->type,dt->value.date.year,dt->value.date.mon, \
318 dt->value.date.day,dt->value.date.hour,dt->value.date.min, \
319 dt->value.date.sec); \
320 if (dt->value.date.tz_flag) \
321 if (dt->value.date.tzo != 0) \
322 xmlGenericError(xmlGenericErrorContext, \
323 "%+05d\n",dt->value.date.tzo); \
324 else \
325 xmlGenericError(xmlGenericErrorContext, "Z\n"); \
326 else \
327 xmlGenericError(xmlGenericErrorContext,"\n")
328#else
329#define DEBUG_DATE(dt)
330#endif
331
Daniel Veillard070803b2002-05-03 07:29:38 +0000332/**
333 * _xmlSchemaParseGYear:
334 * @dt: pointer to a date structure
335 * @str: pointer to the string to analyze
336 *
337 * Parses a xs:gYear without time zone and fills in the appropriate
338 * field of the @dt structure. @str is updated to point just after the
339 * xs:gYear. It is supposed that @dt->year is big enough to contain
340 * the year.
341 *
342 * Returns 0 or the error code
343 */
344static int
345_xmlSchemaParseGYear (xmlSchemaValDatePtr dt, const xmlChar **str) {
346 const xmlChar *cur = *str, *firstChar;
347 int isneg = 0, digcnt = 0;
348
349 if (((*cur < '0') || (*cur > '9')) &&
350 (*cur != '-') && (*cur != '+'))
351 return -1;
352
353 if (*cur == '-') {
354 isneg = 1;
355 cur++;
356 }
357
358 firstChar = cur;
359
360 while ((*cur >= '0') && (*cur <= '9')) {
361 dt->year = dt->year * 10 + (*cur - '0');
362 cur++;
363 digcnt++;
364 }
365
366 /* year must be at least 4 digits (CCYY); over 4
367 * digits cannot have a leading zero. */
368 if ((digcnt < 4) || ((digcnt > 4) && (*firstChar == '0')))
369 return 1;
370
371 if (isneg)
372 dt->year = - dt->year;
373
374 if (!VALID_YEAR(dt->year))
375 return 2;
376
377 *str = cur;
378 return 0;
379}
380
381/**
382 * PARSE_2_DIGITS:
383 * @num: the integer to fill in
384 * @cur: an #xmlChar *
385 * @invalid: an integer
386 *
387 * Parses a 2-digits integer and updates @num with the value. @cur is
388 * updated to point just after the integer.
389 * In case of error, @invalid is set to %TRUE, values of @num and
390 * @cur are undefined.
391 */
392#define PARSE_2_DIGITS(num, cur, invalid) \
393 if ((cur[0] < '0') || (cur[0] > '9') || \
394 (cur[1] < '0') || (cur[1] > '9')) \
395 invalid = 1; \
396 else \
397 num = (cur[0] - '0') * 10 + (cur[1] - '0'); \
398 cur += 2;
399
400/**
401 * PARSE_FLOAT:
402 * @num: the double to fill in
403 * @cur: an #xmlChar *
404 * @invalid: an integer
405 *
406 * Parses a float and updates @num with the value. @cur is
407 * updated to point just after the float. The float must have a
408 * 2-digits integer part and may or may not have a decimal part.
409 * In case of error, @invalid is set to %TRUE, values of @num and
410 * @cur are undefined.
411 */
412#define PARSE_FLOAT(num, cur, invalid) \
413 PARSE_2_DIGITS(num, cur, invalid); \
414 if (!invalid && (*cur == '.')) { \
415 double mult = 1; \
416 cur++; \
417 if ((*cur < '0') || (*cur > '9')) \
418 invalid = 1; \
419 while ((*cur >= '0') && (*cur <= '9')) { \
420 mult /= 10; \
421 num += (*cur - '0') * mult; \
422 cur++; \
423 } \
424 }
425
426/**
427 * _xmlSchemaParseGMonth:
428 * @dt: pointer to a date structure
429 * @str: pointer to the string to analyze
430 *
431 * Parses a xs:gMonth without time zone and fills in the appropriate
432 * field of the @dt structure. @str is updated to point just after the
433 * xs:gMonth.
434 *
435 * Returns 0 or the error code
436 */
437static int
438_xmlSchemaParseGMonth (xmlSchemaValDatePtr dt, const xmlChar **str) {
439 const xmlChar *cur = *str;
440 int ret = 0;
441
442 PARSE_2_DIGITS(dt->mon, cur, ret);
443 if (ret != 0)
444 return ret;
445
446 if (!VALID_MONTH(dt->mon))
447 return 2;
448
449 *str = cur;
450 return 0;
451}
452
453/**
454 * _xmlSchemaParseGDay:
455 * @dt: pointer to a date structure
456 * @str: pointer to the string to analyze
457 *
458 * Parses a xs:gDay without time zone and fills in the appropriate
459 * field of the @dt structure. @str is updated to point just after the
460 * xs:gDay.
461 *
462 * Returns 0 or the error code
463 */
464static int
465_xmlSchemaParseGDay (xmlSchemaValDatePtr dt, const xmlChar **str) {
466 const xmlChar *cur = *str;
467 int ret = 0;
468
469 PARSE_2_DIGITS(dt->day, cur, ret);
470 if (ret != 0)
471 return ret;
472
473 if (!VALID_DAY(dt->day))
474 return 2;
475
476 *str = cur;
477 return 0;
478}
479
480/**
481 * _xmlSchemaParseTime:
482 * @dt: pointer to a date structure
483 * @str: pointer to the string to analyze
484 *
485 * Parses a xs:time without time zone and fills in the appropriate
486 * fields of the @dt structure. @str is updated to point just after the
487 * xs:time.
488 * In case of error, values of @dt fields are undefined.
489 *
490 * Returns 0 or the error code
491 */
492static int
493_xmlSchemaParseTime (xmlSchemaValDatePtr dt, const xmlChar **str) {
494 const xmlChar *cur = *str;
495 unsigned int hour = 0; /* use temp var in case str is not xs:time */
496 int ret = 0;
497
498 PARSE_2_DIGITS(hour, cur, ret);
499 if (ret != 0)
500 return ret;
501
502 if (*cur != ':')
503 return 1;
504 cur++;
505
506 /* the ':' insures this string is xs:time */
507 dt->hour = hour;
508
509 PARSE_2_DIGITS(dt->min, cur, ret);
510 if (ret != 0)
511 return ret;
512
513 if (*cur != ':')
514 return 1;
515 cur++;
516
517 PARSE_FLOAT(dt->sec, cur, ret);
518 if (ret != 0)
519 return ret;
520
521 if (!VALID_TIME(dt))
522 return 2;
523
524 *str = cur;
525 return 0;
526}
527
528/**
529 * _xmlSchemaParseTimeZone:
530 * @dt: pointer to a date structure
531 * @str: pointer to the string to analyze
532 *
533 * Parses a time zone without time zone and fills in the appropriate
534 * field of the @dt structure. @str is updated to point just after the
535 * time zone.
536 *
537 * Returns 0 or the error code
538 */
539static int
540_xmlSchemaParseTimeZone (xmlSchemaValDatePtr dt, const xmlChar **str) {
541 const xmlChar *cur = *str;
542 int ret = 0;
543
544 if (str == NULL)
545 return -1;
546
547 switch (*cur) {
548 case 0:
549 dt->tz_flag = 0;
550 dt->tzo = 0;
551 break;
552
553 case 'Z':
554 dt->tz_flag = 1;
555 dt->tzo = 0;
556 cur++;
557 break;
558
559 case '+':
560 case '-': {
561 int isneg = 0, tmp = 0;
562 isneg = (*cur == '-');
563
564 cur++;
565
566 PARSE_2_DIGITS(tmp, cur, ret);
567 if (ret != 0)
568 return ret;
569 if (!VALID_HOUR(tmp))
570 return 2;
571
572 if (*cur != ':')
573 return 1;
574 cur++;
575
576 dt->tzo = tmp * 60;
577
578 PARSE_2_DIGITS(tmp, cur, ret);
579 if (ret != 0)
580 return ret;
581 if (!VALID_MIN(tmp))
582 return 2;
583
584 dt->tzo += tmp;
585 if (isneg)
586 dt->tzo = - dt->tzo;
587
588 if (!VALID_TZO(dt->tzo))
589 return 2;
590
Daniel Veillard5a872412002-05-22 06:40:27 +0000591 dt->tz_flag = 1;
Daniel Veillard070803b2002-05-03 07:29:38 +0000592 break;
593 }
594 default:
595 return 1;
596 }
597
598 *str = cur;
599 return 0;
600}
601
602/****************************************************************
603 * *
604 * XML Schema Dates/Times Datatypes Handling *
605 * *
606 ****************************************************************/
607
608/**
609 * PARSE_DIGITS:
610 * @num: the integer to fill in
611 * @cur: an #xmlChar *
612 * @num_type: an integer flag
613 *
614 * Parses a digits integer and updates @num with the value. @cur is
615 * updated to point just after the integer.
616 * In case of error, @num_type is set to -1, values of @num and
617 * @cur are undefined.
618 */
619#define PARSE_DIGITS(num, cur, num_type) \
620 if ((*cur < '0') || (*cur > '9')) \
621 num_type = -1; \
622 else \
623 while ((*cur >= '0') && (*cur <= '9')) { \
624 num = num * 10 + (*cur - '0'); \
625 cur++; \
626 }
627
628/**
629 * PARSE_NUM:
630 * @num: the double to fill in
631 * @cur: an #xmlChar *
632 * @num_type: an integer flag
633 *
634 * Parses a float or integer and updates @num with the value. @cur is
635 * updated to point just after the number. If the number is a float,
636 * then it must have an integer part and a decimal part; @num_type will
637 * be set to 1. If there is no decimal part, @num_type is set to zero.
638 * In case of error, @num_type is set to -1, values of @num and
639 * @cur are undefined.
640 */
641#define PARSE_NUM(num, cur, num_type) \
642 num = 0; \
643 PARSE_DIGITS(num, cur, num_type); \
644 if (!num_type && (*cur == '.')) { \
645 double mult = 1; \
646 cur++; \
647 if ((*cur < '0') || (*cur > '9')) \
648 num_type = -1; \
649 else \
650 num_type = 1; \
651 while ((*cur >= '0') && (*cur <= '9')) { \
652 mult /= 10; \
653 num += (*cur - '0') * mult; \
654 cur++; \
655 } \
656 }
657
658/**
Daniel Veillard5a872412002-05-22 06:40:27 +0000659 * xmlSchemaValidateDates:
Daniel Veillard070803b2002-05-03 07:29:38 +0000660 * @type: the predefined type
661 * @dateTime: string to analyze
662 * @val: the return computed value
663 *
664 * Check that @dateTime conforms to the lexical space of one of the date types.
665 * if true a value is computed and returned in @val.
666 *
667 * Returns 0 if this validates, a positive error code number otherwise
668 * and -1 in case of internal or API error.
669 */
670static int
Daniel Veillarddda8f1b2002-09-26 09:47:36 +0000671xmlSchemaValidateDates (xmlSchemaTypePtr type ATTRIBUTE_UNUSED,
Daniel Veillard118aed72002-09-24 14:13:13 +0000672 const xmlChar *dateTime, xmlSchemaValPtr *val) {
Daniel Veillard070803b2002-05-03 07:29:38 +0000673 xmlSchemaValPtr dt;
674 int ret;
675 const xmlChar *cur = dateTime;
676
677#define RETURN_TYPE_IF_VALID(t) \
678 if (IS_TZO_CHAR(*cur)) { \
679 ret = _xmlSchemaParseTimeZone(&(dt->value.date), &cur); \
680 if (ret == 0) { \
681 if (*cur != 0) \
682 goto error; \
683 dt->type = t; \
684 if (val != NULL) \
685 *val = dt; \
686 return 0; \
687 } \
688 }
689
690 if (dateTime == NULL)
691 return -1;
692
693 if ((*cur != '-') && (*cur < '0') && (*cur > '9'))
694 return 1;
695
696 dt = xmlSchemaNewValue(XML_SCHEMAS_UNKNOWN);
697 if (dt == NULL)
698 return -1;
699
700 if ((cur[0] == '-') && (cur[1] == '-')) {
701 /*
702 * It's an incomplete date (xs:gMonthDay, xs:gMonth or
703 * xs:gDay)
704 */
705 cur += 2;
706
707 /* is it an xs:gDay? */
708 if (*cur == '-') {
709 ++cur;
710 ret = _xmlSchemaParseGDay(&(dt->value.date), &cur);
711 if (ret != 0)
712 goto error;
713
714 RETURN_TYPE_IF_VALID(XML_SCHEMAS_GDAY);
715
716 goto error;
717 }
718
719 /*
720 * it should be an xs:gMonthDay or xs:gMonth
721 */
722 ret = _xmlSchemaParseGMonth(&(dt->value.date), &cur);
723 if (ret != 0)
724 goto error;
725
726 if (*cur != '-')
727 goto error;
728 cur++;
729
730 /* is it an xs:gMonth? */
731 if (*cur == '-') {
732 cur++;
733 RETURN_TYPE_IF_VALID(XML_SCHEMAS_GMONTH);
734 goto error;
735 }
736
737 /* it should be an xs:gMonthDay */
738 ret = _xmlSchemaParseGDay(&(dt->value.date), &cur);
739 if (ret != 0)
740 goto error;
741
742 RETURN_TYPE_IF_VALID(XML_SCHEMAS_GMONTHDAY);
743
744 goto error;
745 }
746
747 /*
748 * It's a right-truncated date or an xs:time.
749 * Try to parse an xs:time then fallback on right-truncated dates.
750 */
751 if ((*cur >= '0') && (*cur <= '9')) {
752 ret = _xmlSchemaParseTime(&(dt->value.date), &cur);
753 if (ret == 0) {
754 /* it's an xs:time */
755 RETURN_TYPE_IF_VALID(XML_SCHEMAS_TIME);
756 }
757 }
758
759 /* fallback on date parsing */
760 cur = dateTime;
761
762 ret = _xmlSchemaParseGYear(&(dt->value.date), &cur);
763 if (ret != 0)
764 goto error;
765
766 /* is it an xs:gYear? */
767 RETURN_TYPE_IF_VALID(XML_SCHEMAS_GYEAR);
768
769 if (*cur != '-')
770 goto error;
771 cur++;
772
773 ret = _xmlSchemaParseGMonth(&(dt->value.date), &cur);
774 if (ret != 0)
775 goto error;
776
777 /* is it an xs:gYearMonth? */
778 RETURN_TYPE_IF_VALID(XML_SCHEMAS_GYEARMONTH);
779
780 if (*cur != '-')
781 goto error;
782 cur++;
783
784 ret = _xmlSchemaParseGDay(&(dt->value.date), &cur);
785 if ((ret != 0) || !VALID_DATE((&(dt->value.date))))
786 goto error;
787
788 /* is it an xs:date? */
789 RETURN_TYPE_IF_VALID(XML_SCHEMAS_DATE);
790
791 if (*cur != 'T')
792 goto error;
793 cur++;
794
795 /* it should be an xs:dateTime */
796 ret = _xmlSchemaParseTime(&(dt->value.date), &cur);
797 if (ret != 0)
798 goto error;
799
800 ret = _xmlSchemaParseTimeZone(&(dt->value.date), &cur);
801 if ((ret != 0) || (*cur != 0) || !VALID_DATETIME((&(dt->value.date))))
802 goto error;
803
804 dt->type = XML_SCHEMAS_DATETIME;
805
806 if (val != NULL)
807 *val = dt;
808
809 return 0;
810
811error:
812 if (dt != NULL)
813 xmlSchemaFreeValue(dt);
814 return 1;
815}
816
817/**
Daniel Veillard5a872412002-05-22 06:40:27 +0000818 * xmlSchemaValidateDuration:
Daniel Veillard070803b2002-05-03 07:29:38 +0000819 * @type: the predefined type
820 * @duration: string to analyze
821 * @val: the return computed value
822 *
823 * Check that @duration conforms to the lexical space of the duration type.
824 * if true a value is computed and returned in @val.
825 *
826 * Returns 0 if this validates, a positive error code number otherwise
827 * and -1 in case of internal or API error.
828 */
829static int
Daniel Veillarddda8f1b2002-09-26 09:47:36 +0000830xmlSchemaValidateDuration (xmlSchemaTypePtr type ATTRIBUTE_UNUSED,
Daniel Veillard118aed72002-09-24 14:13:13 +0000831 const xmlChar *duration, xmlSchemaValPtr *val) {
Daniel Veillard070803b2002-05-03 07:29:38 +0000832 const xmlChar *cur = duration;
833 xmlSchemaValPtr dur;
834 int isneg = 0;
835 unsigned int seq = 0;
836
837 if (duration == NULL)
838 return -1;
839
840 if (*cur == '-') {
841 isneg = 1;
842 cur++;
843 }
844
845 /* duration must start with 'P' (after sign) */
846 if (*cur++ != 'P')
847 return 1;
848
849 dur = xmlSchemaNewValue(XML_SCHEMAS_DURATION);
850 if (dur == NULL)
851 return -1;
852
853 while (*cur != 0) {
854 double num;
855 int num_type = 0; /* -1 = invalid, 0 = int, 1 = floating */
856 const xmlChar desig[] = {'Y', 'M', 'D', 'H', 'M', 'S'};
857 const double multi[] = { 0.0, 0.0, 86400.0, 3600.0, 60.0, 1.0, 0.0};
858
859 /* input string should be empty or invalid date/time item */
860 if (seq >= sizeof(desig))
861 goto error;
862
863 /* T designator must be present for time items */
864 if (*cur == 'T') {
865 if (seq <= 3) {
866 seq = 3;
867 cur++;
868 } else
869 return 1;
870 } else if (seq == 3)
871 goto error;
872
873 /* parse the number portion of the item */
874 PARSE_NUM(num, cur, num_type);
875
876 if ((num_type == -1) || (*cur == 0))
877 goto error;
878
879 /* update duration based on item type */
880 while (seq < sizeof(desig)) {
881 if (*cur == desig[seq]) {
882
883 /* verify numeric type; only seconds can be float */
884 if ((num_type != 0) && (seq < (sizeof(desig)-1)))
885 goto error;
886
887 switch (seq) {
888 case 0:
889 dur->value.dur.mon = (long)num * 12;
890 break;
891 case 1:
892 dur->value.dur.mon += (long)num;
893 break;
894 default:
895 /* convert to seconds using multiplier */
896 dur->value.dur.sec += num * multi[seq];
897 seq++;
898 break;
899 }
900
901 break; /* exit loop */
902 }
903 /* no date designators found? */
904 if (++seq == 3)
905 goto error;
906 }
907 cur++;
908 }
909
910 if (isneg) {
911 dur->value.dur.mon = -dur->value.dur.mon;
912 dur->value.dur.day = -dur->value.dur.day;
913 dur->value.dur.sec = -dur->value.dur.sec;
914 }
915
916 if (val != NULL)
917 *val = dur;
918
919 return 0;
920
921error:
922 if (dur != NULL)
923 xmlSchemaFreeValue(dur);
924 return 1;
925}
926
Daniel Veillard4255d502002-04-16 15:50:10 +0000927/**
928 * xmlSchemaValidatePredefinedType:
929 * @type: the predefined type
930 * @value: the value to check
931 * @val: the return computed value
932 *
933 * Check that a value conforms to the lexical space of the predefined type.
934 * if true a value is computed and returned in @val.
935 *
936 * Returns 0 if this validates, a positive error code number otherwise
937 * and -1 in case of internal or API error.
938 */
939int
940xmlSchemaValidatePredefinedType(xmlSchemaTypePtr type, const xmlChar *value,
941 xmlSchemaValPtr *val) {
942 xmlSchemaValPtr v;
943
944 if (xmlSchemaTypesInitialized == 0)
945 return(-1);
946 if (type == NULL)
947 return(-1);
Daniel Veillard5a872412002-05-22 06:40:27 +0000948
Daniel Veillard4255d502002-04-16 15:50:10 +0000949 if (val != NULL)
950 *val = NULL;
951 if (type == xmlSchemaTypeStringDef) {
952 return(0);
953 } else if (type == xmlSchemaTypeAnyTypeDef) {
954 return(0);
955 } else if (type == xmlSchemaTypeAnySimpleTypeDef) {
956 return(0);
957 } else if (type == xmlSchemaTypeNmtoken) {
958 if (xmlValidateNmtokenValue(value))
959 return(0);
960 return(1);
961 } else if (type == xmlSchemaTypeDecimalDef) {
962 const xmlChar *cur = value, *tmp;
Daniel Veillard5a872412002-05-22 06:40:27 +0000963 int frac = 0, len, neg = 0;
Daniel Veillard4255d502002-04-16 15:50:10 +0000964 unsigned long base = 0;
965 if (cur == NULL)
966 return(1);
967 if (*cur == '+')
968 cur++;
969 else if (*cur == '-') {
970 neg = 1;
971 cur++;
972 }
973 tmp = cur;
974 while ((*cur >= '0') && (*cur <= '9')) {
975 base = base * 10 + (*cur - '0');
976 cur++;
977 }
Daniel Veillard5a872412002-05-22 06:40:27 +0000978 len = cur - tmp;
Daniel Veillard4255d502002-04-16 15:50:10 +0000979 if (*cur == '.') {
980 cur++;
981 tmp = cur;
982 while ((*cur >= '0') && (*cur <= '9')) {
983 base = base * 10 + (*cur - '0');
984 cur++;
985 }
986 frac = cur - tmp;
987 }
988 if (*cur != 0)
989 return(1);
990 if (val != NULL) {
991 v = xmlSchemaNewValue(XML_SCHEMAS_DECIMAL);
992 if (v != NULL) {
993 v->value.decimal.base = base;
994 v->value.decimal.sign = neg;
995 v->value.decimal.frac = frac;
Daniel Veillard5a872412002-05-22 06:40:27 +0000996 v->value.decimal.total = frac + len;
Daniel Veillard4255d502002-04-16 15:50:10 +0000997 *val = v;
998 }
999 }
1000 return(0);
Daniel Veillard070803b2002-05-03 07:29:38 +00001001 } else if (type == xmlSchemaTypeDurationDef) {
Daniel Veillard5a872412002-05-22 06:40:27 +00001002 return xmlSchemaValidateDuration(type, value, val);
Daniel Veillard070803b2002-05-03 07:29:38 +00001003 } else if ((type == xmlSchemaTypeDatetimeDef) ||
1004 (type == xmlSchemaTypeTimeDef) ||
1005 (type == xmlSchemaTypeDateDef) ||
1006 (type == xmlSchemaTypeGYearDef) ||
1007 (type == xmlSchemaTypeGYearMonthDef) ||
1008 (type == xmlSchemaTypeGMonthDef) ||
1009 (type == xmlSchemaTypeGMonthDayDef) ||
1010 (type == xmlSchemaTypeGDayDef)) {
Daniel Veillard5a872412002-05-22 06:40:27 +00001011 return xmlSchemaValidateDates(type, value, val);
Daniel Veillard4255d502002-04-16 15:50:10 +00001012 } else if (type == xmlSchemaTypePositiveIntegerDef) {
1013 const xmlChar *cur = value;
1014 unsigned long base = 0;
1015 int total = 0;
1016 if (cur == NULL)
1017 return(1);
1018 if (*cur == '+')
1019 cur++;
1020 while ((*cur >= '0') && (*cur <= '9')) {
1021 base = base * 10 + (*cur - '0');
1022 total++;
1023 cur++;
1024 }
1025 if (*cur != 0)
1026 return(1);
1027 if (val != NULL) {
1028 v = xmlSchemaNewValue(XML_SCHEMAS_DECIMAL);
1029 if (v != NULL) {
1030 v->value.decimal.base = base;
1031 v->value.decimal.sign = 0;
1032 v->value.decimal.frac = 0;
1033 v->value.decimal.total = total;
1034 *val = v;
1035 }
1036 }
1037 return(0);
1038 } else if (type == xmlSchemaTypeNonNegativeIntegerDef) {
1039 const xmlChar *cur = value;
1040 unsigned long base = 0;
1041 int total = 0;
1042 int sign = 0;
1043 if (cur == NULL)
1044 return(1);
1045 if (*cur == '-') {
1046 sign = 1;
1047 cur++;
1048 } else if (*cur == '+')
1049 cur++;
1050 while ((*cur >= '0') && (*cur <= '9')) {
1051 base = base * 10 + (*cur - '0');
1052 total++;
1053 cur++;
1054 }
1055 if (*cur != 0)
1056 return(1);
1057 if ((sign == 1) && (base != 0))
1058 return(1);
1059 if (val != NULL) {
1060 v = xmlSchemaNewValue(XML_SCHEMAS_DECIMAL);
1061 if (v != NULL) {
1062 v->value.decimal.base = base;
1063 v->value.decimal.sign = 0;
1064 v->value.decimal.frac = 0;
1065 v->value.decimal.total = total;
1066 *val = v;
1067 }
1068 }
1069 return(0);
Daniel Veillard84d70a42002-09-16 10:51:38 +00001070 } else if (type == xmlSchemaTypeFloatDef) {
1071 const xmlChar *cur = value, *tmp;
1072 int frac = 0, len, neg = 0;
1073 unsigned long base = 0;
1074 if (cur == NULL)
1075 return(1);
1076 if (*cur == '+')
1077 cur++;
1078 else if (*cur == '-') {
1079 neg = 1;
1080 cur++;
1081 }
1082 tmp = cur;
1083 while ((*cur >= '0') && (*cur <= '9')) {
1084 base = base * 10 + (*cur - '0');
1085 cur++;
1086 }
1087 len = cur - tmp;
1088 if (*cur == '.') {
1089 cur++;
1090 tmp = cur;
1091 while ((*cur >= '0') && (*cur <= '9')) {
1092 base = base * 10 + (*cur - '0');
1093 cur++;
1094 }
1095 frac = cur - tmp;
1096 }
Daniel Veillardb5c05732002-09-20 13:36:25 +00001097 TODO
1098 return(0);
Daniel Veillard84d70a42002-09-16 10:51:38 +00001099 } else if (type == xmlSchemaTypeDoubleDef) {
Daniel Veillardb5c05732002-09-20 13:36:25 +00001100 TODO
1101 return(0);
Daniel Veillard4255d502002-04-16 15:50:10 +00001102 } else {
1103 TODO
1104 return(0);
1105 }
1106}
1107
1108/**
1109 * xmlSchemaCompareDecimals:
1110 * @x: a first decimal value
1111 * @y: a second decimal value
1112 *
1113 * Compare 2 decimals
1114 *
1115 * Returns -1 if x < y, 0 if x == y, 1 if x > y and -2 in case of error
1116 */
1117static int
1118xmlSchemaCompareDecimals(xmlSchemaValPtr x, xmlSchemaValPtr y)
1119{
1120 xmlSchemaValPtr swp;
1121 int order = 1;
1122 unsigned long tmp;
1123
1124 if ((x->value.decimal.sign) && (x->value.decimal.sign))
1125 order = -1;
1126 else if (x->value.decimal.sign)
1127 return (-1);
1128 else if (y->value.decimal.sign)
1129 return (1);
1130 if (x->value.decimal.frac == y->value.decimal.frac) {
1131 if (x->value.decimal.base < y->value.decimal.base)
1132 return (-1);
1133 return (x->value.decimal.base > y->value.decimal.base);
1134 }
1135 if (y->value.decimal.frac > x->value.decimal.frac) {
1136 swp = y;
1137 y = x;
1138 x = swp;
1139 order = -order;
1140 }
1141 tmp =
1142 x->value.decimal.base / powten[x->value.decimal.frac -
1143 y->value.decimal.frac];
1144 if (tmp > y->value.decimal.base)
1145 return (order);
1146 if (tmp < y->value.decimal.base)
1147 return (-order);
1148 tmp =
1149 y->value.decimal.base * powten[x->value.decimal.frac -
1150 y->value.decimal.frac];
1151 if (x->value.decimal.base < tmp)
1152 return (-order);
1153 if (x->value.decimal.base == tmp)
1154 return (0);
1155 return (order);
1156}
1157
1158/**
Daniel Veillard070803b2002-05-03 07:29:38 +00001159 * xmlSchemaCompareDurations:
1160 * @x: a first duration value
1161 * @y: a second duration value
1162 *
1163 * Compare 2 durations
1164 *
1165 * Returns -1 if x < y, 0 if x == y, 1 if x > y, 2 if x <> y, and -2 in
1166 * case of error
1167 */
1168static int
1169xmlSchemaCompareDurations(xmlSchemaValPtr x, xmlSchemaValPtr y)
1170{
1171 long carry, mon, day;
1172 double sec;
1173 long xmon, xday, myear, lyear, minday, maxday;
1174 static const long dayRange [2][12] = {
1175 { 0, 28, 59, 89, 120, 150, 181, 212, 242, 273, 303, 334, },
1176 { 0, 31, 62, 92, 123, 153, 184, 215, 245, 276, 306, 337} };
1177
1178 if ((x == NULL) || (y == NULL))
Daniel Veillard5a872412002-05-22 06:40:27 +00001179 return -2;
Daniel Veillard070803b2002-05-03 07:29:38 +00001180
1181 /* months */
1182 mon = x->value.dur.mon - y->value.dur.mon;
1183
1184 /* seconds */
1185 sec = x->value.dur.sec - y->value.dur.sec;
1186 carry = (long)sec / SECS_PER_DAY;
1187 sec -= (double)(carry * SECS_PER_DAY);
1188
1189 /* days */
1190 day = x->value.dur.day - y->value.dur.day + carry;
1191
1192 /* easy test */
1193 if (mon == 0) {
1194 if (day == 0)
1195 if (sec == 0.0)
1196 return 0;
1197 else if (sec < 0.0)
1198 return -1;
1199 else
1200 return 1;
1201 else if (day < 0)
1202 return -1;
1203 else
1204 return 1;
1205 }
1206
1207 if (mon > 0) {
1208 if ((day >= 0) && (sec >= 0.0))
1209 return 1;
1210 else {
1211 xmon = mon;
1212 xday = -day;
1213 }
1214 } else if ((day <= 0) && (sec <= 0.0)) {
1215 return -1;
1216 } else {
1217 xmon = -mon;
1218 xday = day;
1219 }
1220
1221 myear = xmon / 12;
1222 lyear = myear / 4;
1223 minday = (myear * 365) + (lyear != 0 ? lyear - 1 : 0);
1224 maxday = (myear * 365) + (lyear != 0 ? lyear + 1 : 0);
1225
1226 xmon = xmon % 12;
1227 minday += dayRange[0][xmon];
1228 maxday += dayRange[1][xmon];
1229
1230 if (maxday < xday)
1231 return 1;
1232 else if (minday > xday)
1233 return -1;
1234
1235 /* indeterminate */
Daniel Veillard5a872412002-05-22 06:40:27 +00001236 return 2;
1237}
1238
1239/*
1240 * macros for adding date/times and durations
1241 */
1242#define FQUOTIENT(a,b) (floor(((double)a/(double)b)))
1243#define MODULO(a,b) (a - FQUOTIENT(a,b) * b)
1244#define FQUOTIENT_RANGE(a,low,high) (FQUOTIENT((a-low),(high-low)))
1245#define MODULO_RANGE(a,low,high) ((MODULO((a-low),(high-low)))+low)
1246
1247/**
1248 * _xmlSchemaDateAdd:
1249 * @dt: an #xmlSchemaValPtr
1250 * @dur: an #xmlSchemaValPtr of type #XS_DURATION
1251 *
1252 * Compute a new date/time from @dt and @dur. This function assumes @dt
1253 * is either #XML_SCHEMAS_DATETIME, #XML_SCHEMAS_DATE, #XML_SCHEMAS_GYEARMONTH,
1254 * or #XML_SCHEMAS_GYEAR.
1255 *
1256 * Returns date/time pointer or NULL.
1257 */
1258static xmlSchemaValPtr
1259_xmlSchemaDateAdd (xmlSchemaValPtr dt, xmlSchemaValPtr dur)
1260{
1261 xmlSchemaValPtr ret;
1262 long carry, tempdays, temp;
1263 xmlSchemaValDatePtr r, d;
1264 xmlSchemaValDurationPtr u;
1265
1266 if ((dt == NULL) || (dur == NULL))
1267 return NULL;
1268
1269 ret = xmlSchemaNewValue(dt->type);
1270 if (ret == NULL)
1271 return NULL;
1272
1273 r = &(ret->value.date);
1274 d = &(dt->value.date);
1275 u = &(dur->value.dur);
1276
1277 /* normalization */
1278 if (d->mon == 0)
1279 d->mon = 1;
1280
1281 /* normalize for time zone offset */
1282 u->sec -= (d->tzo * 60);
1283 d->tzo = 0;
1284
1285 /* normalization */
1286 if (d->day == 0)
1287 d->day = 1;
1288
1289 /* month */
1290 carry = d->mon + u->mon;
1291 r->mon = MODULO_RANGE(carry, 1, 13);
1292 carry = FQUOTIENT_RANGE(carry, 1, 13);
1293
1294 /* year (may be modified later) */
1295 r->year = d->year + carry;
1296 if (r->year == 0) {
1297 if (d->year > 0)
1298 r->year--;
1299 else
1300 r->year++;
1301 }
1302
1303 /* time zone */
1304 r->tzo = d->tzo;
1305 r->tz_flag = d->tz_flag;
1306
1307 /* seconds */
1308 r->sec = d->sec + u->sec;
1309 carry = FQUOTIENT((long)r->sec, 60);
1310 if (r->sec != 0.0) {
1311 r->sec = MODULO(r->sec, 60.0);
1312 }
1313
1314 /* minute */
1315 carry += d->min;
1316 r->min = MODULO(carry, 60);
1317 carry = FQUOTIENT(carry, 60);
1318
1319 /* hours */
1320 carry += d->hour;
1321 r->hour = MODULO(carry, 24);
1322 carry = FQUOTIENT(carry, 24);
1323
1324 /*
1325 * days
1326 * Note we use tempdays because the temporary values may need more
1327 * than 5 bits
1328 */
1329 if ((VALID_YEAR(r->year)) && (VALID_MONTH(r->mon)) &&
1330 (d->day > MAX_DAYINMONTH(r->year, r->mon)))
1331 tempdays = MAX_DAYINMONTH(r->year, r->mon);
1332 else if (d->day < 1)
1333 tempdays = 1;
1334 else
1335 tempdays = d->day;
1336
1337 tempdays += u->day + carry;
1338
1339 while (1) {
1340 if (tempdays < 1) {
1341 long tmon = MODULO_RANGE(r->mon-1, 1, 13);
1342 long tyr = r->year + FQUOTIENT_RANGE(r->mon-1, 1, 13);
1343 if (tyr == 0)
1344 tyr--;
1345 tempdays += MAX_DAYINMONTH(tyr, tmon);
1346 carry = -1;
1347 } else if (tempdays > MAX_DAYINMONTH(r->year, r->mon)) {
1348 tempdays = tempdays - MAX_DAYINMONTH(r->year, r->mon);
1349 carry = 1;
1350 } else
1351 break;
1352
1353 temp = r->mon + carry;
1354 r->mon = MODULO_RANGE(temp, 1, 13);
1355 r->year = r->year + FQUOTIENT_RANGE(temp, 1, 13);
1356 if (r->year == 0) {
1357 if (temp < 1)
1358 r->year--;
1359 else
1360 r->year++;
1361 }
1362 }
1363
1364 r->day = tempdays;
1365
1366 /*
1367 * adjust the date/time type to the date values
1368 */
1369 if (ret->type != XML_SCHEMAS_DATETIME) {
1370 if ((r->hour) || (r->min) || (r->sec))
1371 ret->type = XML_SCHEMAS_DATETIME;
1372 else if (ret->type != XML_SCHEMAS_DATE) {
1373 if ((r->mon != 1) && (r->day != 1))
1374 ret->type = XML_SCHEMAS_DATE;
1375 else if ((ret->type != XML_SCHEMAS_GYEARMONTH) && (r->mon != 1))
1376 ret->type = XML_SCHEMAS_GYEARMONTH;
1377 }
1378 }
1379
1380 return ret;
1381}
1382
1383/**
1384 * xmlSchemaDupVal:
1385 * @v: value to duplicate
1386 *
1387 * returns a duplicated value.
1388 */
1389static xmlSchemaValPtr
1390xmlSchemaDupVal (xmlSchemaValPtr v)
1391{
1392 xmlSchemaValPtr ret = xmlSchemaNewValue(v->type);
1393 if (ret == NULL)
1394 return ret;
1395
1396 memcpy(ret, v, sizeof(xmlSchemaVal));
1397 return ret;
1398}
1399
1400/**
1401 * xmlSchemaDateNormalize:
1402 * @dt: an #xmlSchemaValPtr
1403 *
1404 * Normalize @dt to GMT time.
1405 *
1406 */
1407static xmlSchemaValPtr
1408xmlSchemaDateNormalize (xmlSchemaValPtr dt, double offset)
1409{
1410 xmlSchemaValPtr dur, ret;
1411
1412 if (dt == NULL)
1413 return NULL;
1414
1415 if (((dt->type != XML_SCHEMAS_TIME) &&
1416 (dt->type != XML_SCHEMAS_DATETIME)) || (dt->value.date.tzo == 0))
1417 return xmlSchemaDupVal(dt);
1418
1419 dur = xmlSchemaNewValue(XML_SCHEMAS_DURATION);
1420 if (dur == NULL)
1421 return NULL;
1422
1423 dur->value.date.sec -= offset;
1424
1425 ret = _xmlSchemaDateAdd(dt, dur);
1426 if (ret == NULL)
1427 return NULL;
1428
1429 xmlSchemaFreeValue(dur);
1430
1431 /* ret->value.date.tzo = 0; */
1432 return ret;
1433}
1434
1435/**
1436 * _xmlSchemaDateCastYMToDays:
1437 * @dt: an #xmlSchemaValPtr
1438 *
1439 * Convert mon and year of @dt to total number of days. Take the
1440 * number of years since (or before) 1 AD and add the number of leap
1441 * years. This is a function because negative
1442 * years must be handled a little differently and there is no zero year.
1443 *
1444 * Returns number of days.
1445 */
1446static long
1447_xmlSchemaDateCastYMToDays (const xmlSchemaValPtr dt)
1448{
1449 long ret;
1450
1451 if (dt->value.date.year < 0)
1452 ret = (dt->value.date.year * 365) +
1453 (((dt->value.date.year+1)/4)-((dt->value.date.year+1)/100)+
1454 ((dt->value.date.year+1)/400)) +
1455 DAY_IN_YEAR(0, dt->value.date.mon, dt->value.date.year);
1456 else
1457 ret = ((dt->value.date.year-1) * 365) +
1458 (((dt->value.date.year-1)/4)-((dt->value.date.year-1)/100)+
1459 ((dt->value.date.year-1)/400)) +
1460 DAY_IN_YEAR(0, dt->value.date.mon, dt->value.date.year);
1461
1462 return ret;
1463}
1464
1465/**
1466 * TIME_TO_NUMBER:
1467 * @dt: an #xmlSchemaValPtr
1468 *
1469 * Calculates the number of seconds in the time portion of @dt.
1470 *
1471 * Returns seconds.
1472 */
1473#define TIME_TO_NUMBER(dt) \
1474 ((double)((dt->value.date.hour * SECS_PER_HOUR) + \
1475 (dt->value.date.min * SECS_PER_MIN)) + dt->value.date.sec)
1476
1477/**
1478 * xmlSchemaCompareDates:
1479 * @x: a first date/time value
1480 * @y: a second date/time value
1481 *
1482 * Compare 2 date/times
1483 *
1484 * Returns -1 if x < y, 0 if x == y, 1 if x > y, 2 if x <> y, and -2 in
1485 * case of error
1486 */
1487static int
1488xmlSchemaCompareDates (xmlSchemaValPtr x, xmlSchemaValPtr y)
1489{
1490 unsigned char xmask, ymask, xor_mask, and_mask;
1491 xmlSchemaValPtr p1, p2, q1, q2;
1492 long p1d, p2d, q1d, q2d;
1493
1494 if ((x == NULL) || (y == NULL))
1495 return -2;
1496
1497 if (x->value.date.tz_flag) {
1498
1499 if (!y->value.date.tz_flag) {
1500 p1 = xmlSchemaDateNormalize(x, 0);
1501 p1d = _xmlSchemaDateCastYMToDays(p1) + p1->value.date.day;
1502 /* normalize y + 14:00 */
1503 q1 = xmlSchemaDateNormalize(y, (14 * SECS_PER_HOUR));
1504
1505 q1d = _xmlSchemaDateCastYMToDays(q1) + q1->value.date.day;
Daniel Veillardfdc91562002-07-01 21:52:03 +00001506 if (p1d < q1d) {
1507 xmlSchemaFreeValue(p1);
1508 xmlSchemaFreeValue(q1);
Daniel Veillard5a872412002-05-22 06:40:27 +00001509 return -1;
Daniel Veillardfdc91562002-07-01 21:52:03 +00001510 } else if (p1d == q1d) {
Daniel Veillard5a872412002-05-22 06:40:27 +00001511 double sec;
1512
1513 sec = TIME_TO_NUMBER(p1) - TIME_TO_NUMBER(q1);
Daniel Veillardfdc91562002-07-01 21:52:03 +00001514 if (sec < 0.0) {
1515 xmlSchemaFreeValue(p1);
1516 xmlSchemaFreeValue(q1);
Daniel Veillard5a872412002-05-22 06:40:27 +00001517 return -1;
Daniel Veillardfdc91562002-07-01 21:52:03 +00001518 } else {
Daniel Veillard5a872412002-05-22 06:40:27 +00001519 /* normalize y - 14:00 */
1520 q2 = xmlSchemaDateNormalize(y, -(14 * SECS_PER_HOUR));
1521 q2d = _xmlSchemaDateCastYMToDays(q2) + q2->value.date.day;
Daniel Veillardfdc91562002-07-01 21:52:03 +00001522 xmlSchemaFreeValue(p1);
1523 xmlSchemaFreeValue(q1);
1524 xmlSchemaFreeValue(q2);
Daniel Veillard5a872412002-05-22 06:40:27 +00001525 if (p1d > q2d)
1526 return 1;
1527 else if (p1d == q2d) {
1528 sec = TIME_TO_NUMBER(p1) - TIME_TO_NUMBER(q2);
1529 if (sec > 0.0)
1530 return 1;
1531 else
1532 return 2; /* indeterminate */
1533 }
1534 }
Daniel Veillardfdc91562002-07-01 21:52:03 +00001535 } else {
1536 xmlSchemaFreeValue(p1);
1537 xmlSchemaFreeValue(q1);
1538 }
Daniel Veillard5a872412002-05-22 06:40:27 +00001539 }
1540 } else if (y->value.date.tz_flag) {
1541 q1 = xmlSchemaDateNormalize(y, 0);
1542 q1d = _xmlSchemaDateCastYMToDays(q1) + q1->value.date.day;
1543
1544 /* normalize x - 14:00 */
1545 p1 = xmlSchemaDateNormalize(x, -(14 * SECS_PER_HOUR));
1546 p1d = _xmlSchemaDateCastYMToDays(p1) + p1->value.date.day;
1547
Daniel Veillardfdc91562002-07-01 21:52:03 +00001548 if (p1d < q1d) {
1549 xmlSchemaFreeValue(p1);
1550 xmlSchemaFreeValue(q1);
Daniel Veillard5a872412002-05-22 06:40:27 +00001551 return -1;
Daniel Veillardfdc91562002-07-01 21:52:03 +00001552 } else if (p1d == q1d) {
Daniel Veillard5a872412002-05-22 06:40:27 +00001553 double sec;
1554
1555 sec = TIME_TO_NUMBER(p1) - TIME_TO_NUMBER(q1);
Daniel Veillardfdc91562002-07-01 21:52:03 +00001556 if (sec < 0.0) {
1557 xmlSchemaFreeValue(p1);
1558 xmlSchemaFreeValue(q1);
Daniel Veillard5a872412002-05-22 06:40:27 +00001559 return -1;
Daniel Veillardfdc91562002-07-01 21:52:03 +00001560 } else {
Daniel Veillard5a872412002-05-22 06:40:27 +00001561 /* normalize x + 14:00 */
1562 p2 = xmlSchemaDateNormalize(x, (14 * SECS_PER_HOUR));
1563 p2d = _xmlSchemaDateCastYMToDays(p2) + p2->value.date.day;
1564
Daniel Veillardfdc91562002-07-01 21:52:03 +00001565 xmlSchemaFreeValue(p1);
1566 xmlSchemaFreeValue(q1);
1567 xmlSchemaFreeValue(p2);
Daniel Veillard5a872412002-05-22 06:40:27 +00001568 if (p2d > q1d)
1569 return 1;
1570 else if (p2d == q1d) {
1571 sec = TIME_TO_NUMBER(p2) - TIME_TO_NUMBER(q1);
1572 if (sec > 0.0)
1573 return 1;
1574 else
1575 return 2; /* indeterminate */
1576 }
1577 }
Daniel Veillardfdc91562002-07-01 21:52:03 +00001578 } else {
1579 xmlSchemaFreeValue(p1);
1580 xmlSchemaFreeValue(q1);
Daniel Veillard5a872412002-05-22 06:40:27 +00001581 }
1582 }
1583
1584 /*
1585 * if the same type then calculate the difference
1586 */
1587 if (x->type == y->type) {
1588 q1 = xmlSchemaDateNormalize(y, 0);
1589 q1d = _xmlSchemaDateCastYMToDays(q1) + q1->value.date.day;
1590
1591 p1 = xmlSchemaDateNormalize(x, 0);
1592 p1d = _xmlSchemaDateCastYMToDays(p1) + p1->value.date.day;
1593
Daniel Veillardfdc91562002-07-01 21:52:03 +00001594 if (p1d < q1d) {
1595 xmlSchemaFreeValue(p1);
1596 xmlSchemaFreeValue(q1);
Daniel Veillard5a872412002-05-22 06:40:27 +00001597 return -1;
Daniel Veillardfdc91562002-07-01 21:52:03 +00001598 } else if (p1d > q1d) {
1599 xmlSchemaFreeValue(p1);
1600 xmlSchemaFreeValue(q1);
Daniel Veillard5a872412002-05-22 06:40:27 +00001601 return 1;
Daniel Veillardfdc91562002-07-01 21:52:03 +00001602 } else {
Daniel Veillard5a872412002-05-22 06:40:27 +00001603 double sec;
1604
1605 sec = TIME_TO_NUMBER(p1) - TIME_TO_NUMBER(q1);
Daniel Veillardfdc91562002-07-01 21:52:03 +00001606 xmlSchemaFreeValue(p1);
1607 xmlSchemaFreeValue(q1);
Daniel Veillard5a872412002-05-22 06:40:27 +00001608 if (sec < 0.0)
1609 return -1;
1610 else if (sec > 0.0)
1611 return 1;
1612
1613 }
1614 return 0;
1615 }
1616
1617 switch (x->type) {
1618 case XML_SCHEMAS_DATETIME:
1619 xmask = 0xf;
1620 break;
1621 case XML_SCHEMAS_DATE:
1622 xmask = 0x7;
1623 break;
1624 case XML_SCHEMAS_GYEAR:
1625 xmask = 0x1;
1626 break;
1627 case XML_SCHEMAS_GMONTH:
1628 xmask = 0x2;
1629 break;
1630 case XML_SCHEMAS_GDAY:
1631 xmask = 0x3;
1632 break;
1633 case XML_SCHEMAS_GYEARMONTH:
1634 xmask = 0x3;
1635 break;
1636 case XML_SCHEMAS_GMONTHDAY:
1637 xmask = 0x6;
1638 break;
1639 case XML_SCHEMAS_TIME:
1640 xmask = 0x8;
1641 break;
1642 default:
1643 xmask = 0;
1644 break;
1645 }
1646
1647 switch (y->type) {
1648 case XML_SCHEMAS_DATETIME:
1649 ymask = 0xf;
1650 break;
1651 case XML_SCHEMAS_DATE:
1652 ymask = 0x7;
1653 break;
1654 case XML_SCHEMAS_GYEAR:
1655 ymask = 0x1;
1656 break;
1657 case XML_SCHEMAS_GMONTH:
1658 ymask = 0x2;
1659 break;
1660 case XML_SCHEMAS_GDAY:
1661 ymask = 0x3;
1662 break;
1663 case XML_SCHEMAS_GYEARMONTH:
1664 ymask = 0x3;
1665 break;
1666 case XML_SCHEMAS_GMONTHDAY:
1667 ymask = 0x6;
1668 break;
1669 case XML_SCHEMAS_TIME:
1670 ymask = 0x8;
1671 break;
1672 default:
1673 ymask = 0;
1674 break;
1675 }
1676
1677 xor_mask = xmask ^ ymask; /* mark type differences */
1678 and_mask = xmask & ymask; /* mark field specification */
1679
1680 /* year */
1681 if (xor_mask & 1)
1682 return 2; /* indeterminate */
1683 else if (and_mask & 1) {
1684 if (x->value.date.year < y->value.date.year)
1685 return -1;
1686 else if (x->value.date.year > y->value.date.year)
1687 return 1;
1688 }
1689
1690 /* month */
1691 if (xor_mask & 2)
1692 return 2; /* indeterminate */
1693 else if (and_mask & 2) {
1694 if (x->value.date.mon < y->value.date.mon)
1695 return -1;
1696 else if (x->value.date.mon > y->value.date.mon)
1697 return 1;
1698 }
1699
1700 /* day */
1701 if (xor_mask & 4)
1702 return 2; /* indeterminate */
1703 else if (and_mask & 4) {
1704 if (x->value.date.day < y->value.date.day)
1705 return -1;
1706 else if (x->value.date.day > y->value.date.day)
1707 return 1;
1708 }
1709
1710 /* time */
1711 if (xor_mask & 8)
1712 return 2; /* indeterminate */
1713 else if (and_mask & 8) {
1714 if (x->value.date.hour < y->value.date.hour)
1715 return -1;
1716 else if (x->value.date.hour > y->value.date.hour)
1717 return 1;
1718 else if (x->value.date.min < y->value.date.min)
1719 return -1;
1720 else if (x->value.date.min > y->value.date.min)
1721 return 1;
1722 else if (x->value.date.sec < y->value.date.sec)
1723 return -1;
1724 else if (x->value.date.sec > y->value.date.sec)
1725 return 1;
1726 }
1727
Daniel Veillard070803b2002-05-03 07:29:38 +00001728 return 0;
1729}
1730
1731/**
Daniel Veillard4255d502002-04-16 15:50:10 +00001732 * xmlSchemaCompareValues:
1733 * @x: a first value
1734 * @y: a second value
1735 *
1736 * Compare 2 values
1737 *
Daniel Veillard5a872412002-05-22 06:40:27 +00001738 * Returns -1 if x < y, 0 if x == y, 1 if x > y, 2 if x <> y, and -2 in
1739 * case of error
Daniel Veillard4255d502002-04-16 15:50:10 +00001740 */
Daniel Veillarde19fc232002-04-22 16:01:24 +00001741static int
Daniel Veillard4255d502002-04-16 15:50:10 +00001742xmlSchemaCompareValues(xmlSchemaValPtr x, xmlSchemaValPtr y) {
1743 if ((x == NULL) || (y == NULL))
1744 return(-2);
1745
1746 switch (x->type) {
1747 case XML_SCHEMAS_STRING:
1748 TODO
1749 case XML_SCHEMAS_DECIMAL:
1750 if (y->type == XML_SCHEMAS_DECIMAL)
1751 return(xmlSchemaCompareDecimals(x, y));
Daniel Veillard5a872412002-05-22 06:40:27 +00001752 return(-2);
Daniel Veillard070803b2002-05-03 07:29:38 +00001753 case XML_SCHEMAS_DURATION:
1754 if (y->type == XML_SCHEMAS_DURATION)
1755 return(xmlSchemaCompareDurations(x, y));
Daniel Veillard5a872412002-05-22 06:40:27 +00001756 return(-2);
1757 case XML_SCHEMAS_TIME:
1758 case XML_SCHEMAS_GDAY:
1759 case XML_SCHEMAS_GMONTH:
1760 case XML_SCHEMAS_GMONTHDAY:
1761 case XML_SCHEMAS_GYEAR:
1762 case XML_SCHEMAS_GYEARMONTH:
1763 case XML_SCHEMAS_DATE:
1764 case XML_SCHEMAS_DATETIME:
1765 if ((y->type == XML_SCHEMAS_DATETIME) ||
1766 (y->type == XML_SCHEMAS_TIME) ||
1767 (y->type == XML_SCHEMAS_GDAY) ||
1768 (y->type == XML_SCHEMAS_GMONTH) ||
1769 (y->type == XML_SCHEMAS_GMONTHDAY) ||
1770 (y->type == XML_SCHEMAS_GYEAR) ||
1771 (y->type == XML_SCHEMAS_DATE) ||
1772 (y->type == XML_SCHEMAS_GYEARMONTH))
1773 return (xmlSchemaCompareDates(x, y));
1774
1775 return (-2);
Daniel Veillard4255d502002-04-16 15:50:10 +00001776 default:
1777 TODO
1778 }
Daniel Veillard5a872412002-05-22 06:40:27 +00001779 return -2;
Daniel Veillard4255d502002-04-16 15:50:10 +00001780}
1781
1782/**
1783 * xmlSchemaValidateFacet:
1784 * @type: the type declaration
1785 * @facet: the facet to check
1786 * @value: the lexical repr of the value to validate
1787 * @val: the precomputed value
1788 *
1789 * Check a value against a facet condition
1790 *
1791 * Returns 0 if the element is schemas valid, a positive error code
1792 * number otherwise and -1 in case of internal or API error.
1793 */
1794int
Daniel Veillarddda8f1b2002-09-26 09:47:36 +00001795xmlSchemaValidateFacet(xmlSchemaTypePtr base ATTRIBUTE_UNUSED,
Daniel Veillard118aed72002-09-24 14:13:13 +00001796 xmlSchemaFacetPtr facet,
Daniel Veillard4255d502002-04-16 15:50:10 +00001797 const xmlChar *value, xmlSchemaValPtr val)
1798{
1799 int ret;
1800
1801 switch (facet->type) {
1802 case XML_SCHEMA_FACET_PATTERN:
1803 ret = xmlRegexpExec(facet->regexp, value);
1804 if (ret == 1)
1805 return(0);
1806 if (ret == 0) {
1807 TODO /* error code */
1808 return(1);
1809 }
1810 return(ret);
1811 case XML_SCHEMA_FACET_MAXEXCLUSIVE:
1812 ret = xmlSchemaCompareValues(val, facet->val);
1813 if (ret == -2) {
1814 TODO /* error code */
1815 return(-1);
1816 }
1817 if (ret == -1)
1818 return(0);
Daniel Veillard5a872412002-05-22 06:40:27 +00001819 /* error code */
Daniel Veillard4255d502002-04-16 15:50:10 +00001820 return(1);
Daniel Veillard070803b2002-05-03 07:29:38 +00001821 case XML_SCHEMA_FACET_MAXINCLUSIVE:
1822 ret = xmlSchemaCompareValues(val, facet->val);
1823 if (ret == -2) {
1824 TODO /* error code */
1825 return(-1);
1826 }
1827 if ((ret == -1) || (ret == 0))
1828 return(0);
Daniel Veillard5a872412002-05-22 06:40:27 +00001829 /* error code */
Daniel Veillard070803b2002-05-03 07:29:38 +00001830 return(1);
1831 case XML_SCHEMA_FACET_MINEXCLUSIVE:
1832 ret = xmlSchemaCompareValues(val, facet->val);
1833 if (ret == -2) {
1834 TODO /* error code */
1835 return(-1);
1836 }
1837 if (ret == 1)
1838 return(0);
Daniel Veillard5a872412002-05-22 06:40:27 +00001839 /* error code */
Daniel Veillard070803b2002-05-03 07:29:38 +00001840 return(1);
1841 case XML_SCHEMA_FACET_MININCLUSIVE:
1842 ret = xmlSchemaCompareValues(val, facet->val);
1843 if (ret == -2) {
1844 TODO /* error code */
1845 return(-1);
1846 }
1847 if ((ret == 1) || (ret == 0))
1848 return(0);
Daniel Veillard5a872412002-05-22 06:40:27 +00001849 /* error code */
Daniel Veillard070803b2002-05-03 07:29:38 +00001850 return(1);
Daniel Veillard8651f532002-04-17 09:06:27 +00001851 case XML_SCHEMA_FACET_WHITESPACE:
1852 TODO /* whitespaces */
1853 return(0);
Daniel Veillarde19fc232002-04-22 16:01:24 +00001854 case XML_SCHEMA_FACET_MAXLENGTH:
1855 if ((facet->val != NULL) &&
1856 (facet->val->type == XML_SCHEMAS_DECIMAL) &&
1857 (facet->val->value.decimal.frac == 0)) {
Daniel Veillard118aed72002-09-24 14:13:13 +00001858 unsigned int len;
Daniel Veillarde19fc232002-04-22 16:01:24 +00001859
1860 if (facet->val->value.decimal.sign == 1)
1861 return(1);
1862 len = xmlUTF8Strlen(value);
1863 if (len > facet->val->value.decimal.base)
1864 return(1);
1865 return(0);
1866 }
1867 TODO /* error code */
1868 return(1);
Daniel Veillard88c58912002-04-23 07:12:20 +00001869 case XML_SCHEMA_FACET_ENUMERATION:
1870 if ((facet->value != NULL) &&
1871 (xmlStrEqual(facet->value, value)))
1872 return(0);
1873 return(1);
Daniel Veillard4255d502002-04-16 15:50:10 +00001874 default:
1875 TODO
1876 }
1877 return(0);
1878}
1879
1880#endif /* LIBXML_SCHEMAS_ENABLED */