/*
 * schemastypes.c : implementation of the XML Schema Datatypes
 *             definition and validity checking
 *
 * See Copyright for the status of this software.
 *
 * Daniel Veillard <veillard@redhat.com>
 */

#define IN_LIBXML
#include "XmlEnglibxml.h"

//#ifdef LIBXML_SCHEMAS_ENABLED
#if defined(LIBXML_SCHEMAS_ENABLED) || defined(XMLENGINE_XMLSCHEMA_DATATYPES)

#include <string.h>
#include "Libxml2_xmlmemory.h"
#include "Libxml2_parser.h"
#include "Libxml2_parserInternals.h"
#include "Libxml2_hash.h"
#include "Libxml2_valid.h"
#include "Libxml2_xpath.h"
#include "Libxml2_uri.h"

#include "Libxml2_xmlschemas.h"
#include "Libxml2_schemasInternals.h"
#include "Libxml2_xmlschemasGlobals.h"
#include "Libxml2_globals.h"
#include "Libxml2_xmlschemastypes.h"


#ifdef HAVE_MATH_H
#include <math.h>
#endif

//#define DEBUG

#define _TODO_ \
    xmlGenericError(xmlGenericErrorContext,             \
        "Unimplemented block at %s:%d\n",               \
            __FILE__, __LINE__);

static const unsigned long powten[10] = {
    1, 10, 100, 1000, 10000, 100000, 1000000, 10000000L,
    100000000L, 1000000000L
};

// access to global data // Unfortunately, double dereference happens here everywhere
#define xmlSchemaTypesBank (xmlSchemaTypesGlobals->xmlSchemaTypesBank)

/*
* Basic types
*/
#define xmlSchemaTypeStringDef (xmlSchemaTypesGlobals->xmlSchemaTypeStringDef)
#define  xmlSchemaTypeAnyTypeDef (xmlSchemaTypesGlobals->xmlSchemaTypeAnyTypeDef)
#define  xmlSchemaTypeAnySimpleTypeDef (xmlSchemaTypesGlobals->xmlSchemaTypeAnySimpleTypeDef)
#define  xmlSchemaTypeDecimalDef (xmlSchemaTypesGlobals->xmlSchemaTypeDecimalDef)
#define  xmlSchemaTypeDatetimeDef (xmlSchemaTypesGlobals->xmlSchemaTypeDatetimeDef)
#define  xmlSchemaTypeDateDef (xmlSchemaTypesGlobals->xmlSchemaTypeDateDef)
#define  xmlSchemaTypeTimeDef (xmlSchemaTypesGlobals->xmlSchemaTypeTimeDef)
#define  xmlSchemaTypeGYearDef (xmlSchemaTypesGlobals->xmlSchemaTypeGYearDef)
#define  xmlSchemaTypeGYearMonthDef (xmlSchemaTypesGlobals->xmlSchemaTypeGYearMonthDef)
#define  xmlSchemaTypeGDayDef (xmlSchemaTypesGlobals->xmlSchemaTypeGDayDef)
#define  xmlSchemaTypeGMonthDayDef (xmlSchemaTypesGlobals->xmlSchemaTypeGMonthDayDef)
#define  xmlSchemaTypeGMonthDef (xmlSchemaTypesGlobals->xmlSchemaTypeGMonthDef)
#define  xmlSchemaTypeDurationDef (xmlSchemaTypesGlobals->xmlSchemaTypeDurationDef)
#define  xmlSchemaTypeFloatDef (xmlSchemaTypesGlobals->xmlSchemaTypeFloatDef)
#define  xmlSchemaTypeBooleanDef (xmlSchemaTypesGlobals->xmlSchemaTypeBooleanDef)
#define  xmlSchemaTypeDoubleDef (xmlSchemaTypesGlobals->xmlSchemaTypeDoubleDef)
#define  xmlSchemaTypeHexBinaryDef (xmlSchemaTypesGlobals->xmlSchemaTypeHexBinaryDef)
#define  xmlSchemaTypeBase64BinaryDef (xmlSchemaTypesGlobals->xmlSchemaTypeBase64BinaryDef)
#define  xmlSchemaTypeAnyURIDef (xmlSchemaTypesGlobals->xmlSchemaTypeAnyURIDef)

/*
* Derived types
*/
#define  xmlSchemaTypePositiveIntegerDef (xmlSchemaTypesGlobals->xmlSchemaTypePositiveIntegerDef)
#define  xmlSchemaTypeNonPositiveIntegerDef (xmlSchemaTypesGlobals->xmlSchemaTypeNonPositiveIntegerDef)
#define  xmlSchemaTypeNegativeIntegerDef (xmlSchemaTypesGlobals->xmlSchemaTypeNegativeIntegerDef)
#define  xmlSchemaTypeNonNegativeIntegerDef (xmlSchemaTypesGlobals->xmlSchemaTypeNonNegativeIntegerDef)
#define  xmlSchemaTypeIntegerDef (xmlSchemaTypesGlobals->xmlSchemaTypeIntegerDef)
#define  xmlSchemaTypeLongDef (xmlSchemaTypesGlobals->xmlSchemaTypeLongDef)
#define  xmlSchemaTypeIntDef (xmlSchemaTypesGlobals->xmlSchemaTypeIntDef)
#define  xmlSchemaTypeShortDef (xmlSchemaTypesGlobals->xmlSchemaTypeShortDef)
#define  xmlSchemaTypeByteDef (xmlSchemaTypesGlobals->xmlSchemaTypeByteDef)
#define  xmlSchemaTypeUnsignedLongDef (xmlSchemaTypesGlobals->xmlSchemaTypeUnsignedLongDef)
#define  xmlSchemaTypeUnsignedIntDef (xmlSchemaTypesGlobals->xmlSchemaTypeUnsignedIntDef)
#define  xmlSchemaTypeUnsignedShortDef (xmlSchemaTypesGlobals->xmlSchemaTypeUnsignedShortDef)
#define  xmlSchemaTypeUnsignedByteDef (xmlSchemaTypesGlobals->xmlSchemaTypeUnsignedByteDef)
#define  xmlSchemaTypeNormStringDef (xmlSchemaTypesGlobals->xmlSchemaTypeNormStringDef)
#define  xmlSchemaTypeTokenDef (xmlSchemaTypesGlobals->xmlSchemaTypeTokenDef)
#define  xmlSchemaTypeLanguageDef (xmlSchemaTypesGlobals->xmlSchemaTypeLanguageDef)
#define  xmlSchemaTypeNameDef (xmlSchemaTypesGlobals->xmlSchemaTypeNameDef)
#define  xmlSchemaTypeQNameDef (xmlSchemaTypesGlobals->xmlSchemaTypeQNameDef)
#define  xmlSchemaTypeNCNameDef (xmlSchemaTypesGlobals->xmlSchemaTypeNCNameDef)
#define  xmlSchemaTypeIdDef (xmlSchemaTypesGlobals->xmlSchemaTypeIdDef)
#define  xmlSchemaTypeIdrefDef (xmlSchemaTypesGlobals->xmlSchemaTypeIdrefDef)
#define  xmlSchemaTypeIdrefsDef (xmlSchemaTypesGlobals->xmlSchemaTypeIdrefsDef)
#define  xmlSchemaTypeEntityDef (xmlSchemaTypesGlobals->xmlSchemaTypeEntityDef)
#define  xmlSchemaTypeEntitiesDef (xmlSchemaTypesGlobals->xmlSchemaTypeEntitiesDef)
#define  xmlSchemaTypeNotationDef (xmlSchemaTypesGlobals->xmlSchemaTypeNotationDef)
#define  xmlSchemaTypeNmtokenDef (xmlSchemaTypesGlobals->xmlSchemaTypeNmtokenDef)
#define  xmlSchemaTypeNmtokensDef (xmlSchemaTypesGlobals->xmlSchemaTypeNmtokensDef)

/************************************************************************
 *                                                                      *
 *          Datatype error handlers                                     *
 *                                                                      *
 ************************************************************************/
/**
 * xmlSchemaTypeErrMemory:
 * @extra:  extra informations
 *
 * Handle an out of memory condition
 */
static void
xmlSchemaTypeErrMemory(xmlNodePtr node, const char *extra)
{
    __xmlSimpleError(XML_FROM_DATATYPE, XML_ERR_NO_MEMORY, node, NULL, extra);
}

/************************************************************************
 *                                                                      *
 *          Base types support                                          *
 *                                                                      *
 ************************************************************************/
/*
 * xmlSchemaInitBasicType:
 * @name:  the type name
 * @type:  the value type associated
 *
 * Initialize one default type
 */
static xmlSchemaTypePtr
xmlSchemaInitBasicType(const char *name, xmlSchemaValType type) {
    xmlSchemaTypePtr ret;

    ret = (xmlSchemaTypePtr) xmlMalloc(sizeof(xmlSchemaType));
    if (ret == NULL) {
        xmlSchemaTypeErrMemory(NULL, "could not initialize basic types");
        return(NULL);
    }
    memset(ret, 0, sizeof(xmlSchemaType));
    ret->name = (const xmlChar *)name;
    ret->type = XML_SCHEMA_TYPE_BASIC;
    ret->flags = type;
    ret->contentType = XML_SCHEMA_CONTENT_BASIC;
    xmlHashAddEntry2(
            xmlSchemaTypesBank,
            ret->name,
            XML_SCHEMAS_NAMESPACE_NAME,
            ret);
    return(ret);
}

/*
 * xmlSchemaInitTypes:
 *
 * Initialize the default XML Schemas type library
 */
void
xmlSchemaInitTypes(void)
{
    void* gStruct;

    if (xmlSchemaTypesInitialized != 0)
        return;
    gStruct = xmlMalloc(sizeof(xmlSchemaTypesGlobalData));
    memset(gStruct, 0, sizeof(xmlSchemaTypesGlobalData));
    xmlSchemaTypesGlobals = (xmlSchemaTypesGlobalDataPtr)gStruct;

    xmlSchemaTypesBank = xmlHashCreate(40);

    /*
     * primitive datatypes
     */
    xmlSchemaTypeStringDef
        = xmlSchemaInitBasicType("string", XML_SCHEMAS_STRING);
    xmlSchemaTypeAnyTypeDef
        = xmlSchemaInitBasicType("anyType", XML_SCHEMAS_UNKNOWN);
    xmlSchemaTypeAnySimpleTypeDef
        = xmlSchemaInitBasicType("anySimpleType", XML_SCHEMAS_UNKNOWN);
    xmlSchemaTypeDecimalDef
        = xmlSchemaInitBasicType("decimal", XML_SCHEMAS_DECIMAL);
    xmlSchemaTypeDateDef
        = xmlSchemaInitBasicType("date", XML_SCHEMAS_DATE);
    xmlSchemaTypeDatetimeDef
        = xmlSchemaInitBasicType("dateTime", XML_SCHEMAS_DATETIME);
    xmlSchemaTypeTimeDef
        = xmlSchemaInitBasicType("time", XML_SCHEMAS_TIME);
    xmlSchemaTypeGYearDef
        = xmlSchemaInitBasicType("gYear", XML_SCHEMAS_GYEAR);
    xmlSchemaTypeGYearMonthDef
        = xmlSchemaInitBasicType("gYearMonth", XML_SCHEMAS_GYEARMONTH);
    xmlSchemaTypeGMonthDef
        = xmlSchemaInitBasicType("gMonth", XML_SCHEMAS_GMONTH);
    xmlSchemaTypeGMonthDayDef
        = xmlSchemaInitBasicType("gMonthDay", XML_SCHEMAS_GMONTHDAY);
    xmlSchemaTypeGDayDef
        = xmlSchemaInitBasicType("gDay", XML_SCHEMAS_GDAY);
    xmlSchemaTypeDurationDef
        = xmlSchemaInitBasicType("duration", XML_SCHEMAS_DURATION);
    xmlSchemaTypeFloatDef
        = xmlSchemaInitBasicType("float", XML_SCHEMAS_FLOAT);
    xmlSchemaTypeDoubleDef
        = xmlSchemaInitBasicType("double", XML_SCHEMAS_DOUBLE);
    xmlSchemaTypeBooleanDef
        = xmlSchemaInitBasicType("boolean", XML_SCHEMAS_BOOLEAN);
    xmlSchemaTypeAnyURIDef
        = xmlSchemaInitBasicType("anyURI", XML_SCHEMAS_ANYURI);
    xmlSchemaTypeHexBinaryDef
        = xmlSchemaInitBasicType("hexBinary", XML_SCHEMAS_HEXBINARY);
    xmlSchemaTypeBase64BinaryDef
        = xmlSchemaInitBasicType("base64Binary", XML_SCHEMAS_BASE64BINARY);

    /*
     * derived datatypes
     */
    xmlSchemaTypeIntegerDef
        = xmlSchemaInitBasicType("integer", XML_SCHEMAS_INTEGER);
    xmlSchemaTypeNonPositiveIntegerDef
        = xmlSchemaInitBasicType("nonPositiveInteger", XML_SCHEMAS_NPINTEGER);
    xmlSchemaTypeNegativeIntegerDef
        = xmlSchemaInitBasicType("negativeInteger", XML_SCHEMAS_NINTEGER);
    xmlSchemaTypeLongDef
        = xmlSchemaInitBasicType("long", XML_SCHEMAS_LONG);
    xmlSchemaTypeIntDef
        = xmlSchemaInitBasicType("int", XML_SCHEMAS_INT);
    xmlSchemaTypeShortDef
        = xmlSchemaInitBasicType("short", XML_SCHEMAS_SHORT);
    xmlSchemaTypeByteDef
        = xmlSchemaInitBasicType("byte", XML_SCHEMAS_BYTE);
    xmlSchemaTypeNonNegativeIntegerDef
        = xmlSchemaInitBasicType("nonNegativeInteger", XML_SCHEMAS_NNINTEGER);
    xmlSchemaTypeUnsignedLongDef
        = xmlSchemaInitBasicType("unsignedLong", XML_SCHEMAS_ULONG);
    xmlSchemaTypeUnsignedIntDef
        = xmlSchemaInitBasicType("unsignedInt", XML_SCHEMAS_UINT);
    xmlSchemaTypeUnsignedShortDef
        = xmlSchemaInitBasicType("unsignedShort", XML_SCHEMAS_USHORT);
    xmlSchemaTypeUnsignedByteDef
        = xmlSchemaInitBasicType("unsignedByte", XML_SCHEMAS_UBYTE);
    xmlSchemaTypePositiveIntegerDef
        = xmlSchemaInitBasicType("positiveInteger", XML_SCHEMAS_PINTEGER);

    xmlSchemaTypeNormStringDef
        = xmlSchemaInitBasicType("normalizedString", XML_SCHEMAS_NORMSTRING);
    xmlSchemaTypeTokenDef
        = xmlSchemaInitBasicType("token", XML_SCHEMAS_TOKEN);
    xmlSchemaTypeLanguageDef
        = xmlSchemaInitBasicType("language", XML_SCHEMAS_LANGUAGE);
    xmlSchemaTypeIdDef
        = xmlSchemaInitBasicType("ID", XML_SCHEMAS_ID);
    xmlSchemaTypeIdrefDef
        = xmlSchemaInitBasicType("IDREF", XML_SCHEMAS_IDREF);
    xmlSchemaTypeIdrefsDef
        = xmlSchemaInitBasicType("IDREFS", XML_SCHEMAS_IDREFS);
    xmlSchemaTypeEntityDef
        = xmlSchemaInitBasicType("ENTITY", XML_SCHEMAS_ENTITY);
    xmlSchemaTypeEntitiesDef
        = xmlSchemaInitBasicType("ENTITIES", XML_SCHEMAS_ENTITIES);
    xmlSchemaTypeNotationDef
        = xmlSchemaInitBasicType("NOTATION", XML_SCHEMAS_NOTATION);
    xmlSchemaTypeNameDef
        = xmlSchemaInitBasicType("Name", XML_SCHEMAS_NAME);
    xmlSchemaTypeQNameDef
        = xmlSchemaInitBasicType("QName", XML_SCHEMAS_QNAME);
    xmlSchemaTypeNCNameDef
        = xmlSchemaInitBasicType("NCName", XML_SCHEMAS_NCNAME);
    xmlSchemaTypeNmtokenDef
        = xmlSchemaInitBasicType("NMTOKEN", XML_SCHEMAS_NMTOKEN);
    xmlSchemaTypeNmtokensDef
        = xmlSchemaInitBasicType("NMTOKENS", XML_SCHEMAS_NMTOKENS);

    // TODO: OOM: Check OOM flag
    xmlSchemaTypesInitialized = 1;
}

/**
 * xmlSchemaCleanupTypes:
 *
 * Cleanup the default XML Schemas type library
 */
void
xmlSchemaCleanupTypes(void) {
    if (xmlSchemaTypesInitialized == 0)
    return;
    xmlHashFree(xmlSchemaTypesBank, (xmlHashDeallocator) xmlSchemaFreeType);
    xmlSchemaTypesInitialized = 0;
    // Free memory for struct xmlSchemaTypesGlobalData referred to from xmlGlobalState
    xmlFree(xmlSchemaTypesGlobals);
}

/**
 * xmlSchemaNewValue:
 * @type:  the value type
 *
 * Allocate a new simple type value
 *
 * Returns a pointer to the new value or NULL in case of error
 *
 * OOM: returns NULL and sets OOM flag
 */
static xmlSchemaValPtr
xmlSchemaNewValue(xmlSchemaValType type) {
    xmlSchemaValPtr value;

    value = (xmlSchemaValPtr) xmlMalloc(sizeof(xmlSchemaVal));
    if (value == NULL) {
        // TODO: OOM: set OOM flag
        return(NULL);
    }
    memset(value, 0, sizeof(xmlSchemaVal));
    value->type = type;
    return(value);
}

/**
 * xmlSchemaFreeValue:
 * @value:  the value to free
 *
 * Cleanup the default XML Schemas type library
 *
 * OOM: never
 */
void
xmlSchemaFreeValue(xmlSchemaValPtr value) {
    if (value == NULL)
        return;
    switch (value->type) {
        case XML_SCHEMAS_STRING:
        case XML_SCHEMAS_NORMSTRING:
        case XML_SCHEMAS_TOKEN:
        case XML_SCHEMAS_LANGUAGE:
        case XML_SCHEMAS_NMTOKEN:
        case XML_SCHEMAS_NMTOKENS:
        case XML_SCHEMAS_NAME:
        case XML_SCHEMAS_NCNAME:
        case XML_SCHEMAS_ID:
        case XML_SCHEMAS_IDREF:
        case XML_SCHEMAS_IDREFS:
        case XML_SCHEMAS_ENTITY:
        case XML_SCHEMAS_ENTITIES:
        case XML_SCHEMAS_NOTATION:
        case XML_SCHEMAS_ANYURI:
            if (value->value.str != NULL)
                xmlFree(value->value.str);
            break;
        case XML_SCHEMAS_QNAME:
            if (value->value.qname.uri != NULL)
                xmlFree(value->value.qname.uri);
            if (value->value.qname.name != NULL)
                xmlFree(value->value.qname.name);
            break;
        case XML_SCHEMAS_HEXBINARY:
            if (value->value.hex.str != NULL)
                xmlFree(value->value.hex.str);
            break;
        case XML_SCHEMAS_BASE64BINARY:
            if (value->value.base64.str != NULL)
                xmlFree(value->value.base64.str);
            break;
        default:
            break;
    }
    xmlFree(value);
}

/**
 * xmlSchemaGetPredefinedType:
 * @name: the type name
 * @ns:  the URI of the namespace usually "http://www.w3.org/2001/XMLSchema"
 *
 * Lookup a type in the default XML Schemas type library
 *
 * Returns the type if found, NULL otherwise
 */
xmlSchemaTypePtr
xmlSchemaGetPredefinedType(const xmlChar *name, const xmlChar *ns) {
    if (xmlSchemaTypesInitialized == 0)
        xmlSchemaInitTypes();
    if (name == NULL)
        return(NULL);
    return((xmlSchemaTypePtr) xmlHashLookup2(xmlSchemaTypesBank, name, ns));
}

/****************************************************************
 *                                                              *
 *      Convenience macros and functions                        *
 *                                                              *
 ****************************************************************/

#define IS_TZO_CHAR(c)     ((c == 0) || (c == 'Z') || (c == '+') || (c == '-'))
#define VALID_YEAR(yr)     (yr != 0)
#define VALID_MONTH(mon)   ((mon >= 1) && (mon <= 12))
/* VALID_DAY should only be used when month is unknown */
#define VALID_DAY(day)     ((day >= 1) && (day <= 31))
#define VALID_HOUR(hr)     ((hr >= 0) && (hr <= 23))
#define VALID_MIN(min)     ((min >= 0) && (min <= 59))
#define VALID_SEC(sec)     ((sec >= 0) && (sec < 60))
#define VALID_TZO(tzo)     ((tzo > -1440) && (tzo < 1440))
#define IS_LEAP(y)         (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))

static const unsigned int daysInMonth[12] =
    { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static const unsigned int daysInMonthLeap[12] =
    { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

#define MAX_DAYINMONTH(yr,mon) \
       (IS_LEAP(yr) ? daysInMonthLeap[mon - 1] : daysInMonth[mon - 1])

#define VALID_MDAY(dt) \
    (IS_LEAP(dt->year) ?                                    \
        (dt->day <= daysInMonthLeap[dt->mon - 1]) :         \
        (dt->day <= daysInMonth[dt->mon - 1]))

#define VALID_DATE(dt) \
    (VALID_YEAR(dt->year) && VALID_MONTH(dt->mon) && VALID_MDAY(dt))

#define VALID_TIME(dt) \
    (VALID_HOUR(dt->hour) && VALID_MIN(dt->min) &&  \
     VALID_SEC(dt->sec) && VALID_TZO(dt->tzo))

#define VALID_DATETIME(dt) \
    (VALID_DATE(dt) && VALID_TIME(dt))

#define SECS_PER_MIN       (60)
#define SECS_PER_HOUR      (60 * SECS_PER_MIN)
#define SECS_PER_DAY       (24 * SECS_PER_HOUR)

static const long dayInYearByMonth[12] =
    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
static const long dayInLeapYearByMonth[12] =
    { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 };

#define DAY_IN_YEAR(day, month, year) \
        ((IS_LEAP(year) ?                               \
                dayInLeapYearByMonth[month - 1] :       \
                dayInYearByMonth[month - 1]) + day)

#ifdef DEBUG
#define DEBUG_DATE(dt)                                                  \
    xmlGenericError(xmlGenericErrorContext,                             \
        "type=%o %04ld-%02u-%02uT%02u:%02u:%03f",                       \
        dt->type,dt->value.date.year,dt->value.date.mon,                \
        dt->value.date.day,dt->value.date.hour,dt->value.date.min,      \
        dt->value.date.sec);                                            \
    if (dt->value.date.tz_flag)                                         \
        if (dt->value.date.tzo != 0)                                    \
            xmlGenericError(xmlGenericErrorContext,                     \
                "%+05d\n",dt->value.date.tzo);                          \
        else                                                            \
            xmlGenericError(xmlGenericErrorContext, "Z\n");             \
    else                                                                \
        xmlGenericError(xmlGenericErrorContext,"\n")
#else
#define DEBUG_DATE(dt)
#endif

/**
 * _xmlSchemaParseGYear:
 * @dt:  pointer to a date structure
 * @str: pointer to the string to analyze
 *
 * Parses a xs:gYear without time zone and fills in the appropriate
 * field of the @dt structure. @str is updated to point just after the
 * xs:gYear. It is supposed that @dt->year is big enough to contain
 * the year.
 *
 * Returns 0 or the error code
 *
 * OOM: never
 */
static int
_xmlSchemaParseGYear (xmlSchemaValDatePtr dt, const xmlChar **str) {
    const xmlChar *cur = *str, *firstChar;
    int isneg = 0, digcnt = 0;

    if (((*cur < '0') || (*cur > '9')) &&
        (*cur != '-') && (*cur != '+'))
    {
        return -1;
    }

    if (*cur == '-') {
        isneg = 1;
        cur++;
    }

    firstChar = cur;

    while ((*cur >= '0') && (*cur <= '9')) {
        dt->year = dt->year * 10 + (*cur - '0');
        cur++;
        digcnt++;
    }

    /* year must be at least 4 digits (CCYY); over 4
     * digits cannot have a leading zero. */
    if ((digcnt < 4) || ((digcnt > 4) && (*firstChar == '0')))
        return 1;

    if (isneg)
        dt->year = - dt->year;

    if (!VALID_YEAR(dt->year))
        return 2;

    *str = cur;
    return 0;
}

/**
 * PARSE_2_DIGITS:
 * @num:  the integer to fill in
 * @cur:  an #xmlChar *
 * @invalid: an integer
 *
 * Parses a 2-digits integer and updates @num with the value. @cur is
 * updated to point just after the integer.
 * In case of error, @invalid is set to %TRUE, values of @num and
 * @cur are undefined.
 */
#define PARSE_2_DIGITS(num, cur, invalid)           \
    if ((cur[0] < '0') || (cur[0] > '9') ||         \
        (cur[1] < '0') || (cur[1] > '9'))           \
    {                                               \
        invalid = 1;                                \
    }else{                                          \
        num = (cur[0] - '0') * 10 + (cur[1] - '0'); \
    }                                               \
    cur += 2;

/**
 * PARSE_FLOAT:
 * @num:  the double to fill in
 * @cur:  an #xmlChar *
 * @invalid: an integer
 *
 * Parses a float and updates @num with the value. @cur is
 * updated to point just after the float. The float must have a
 * 2-digits integer part and may or may not have a decimal part.
 * In case of error, @invalid is set to %TRUE, values of @num and
 * @cur are undefined.
 */
#define PARSE_FLOAT(num, cur, invalid)              \
    PARSE_2_DIGITS(num, cur, invalid);              \
    if (!invalid && (*cur == '.')) {                \
        double mult = 1;                            \
        cur++;                                      \
        if ((*cur < '0') || (*cur > '9'))           \
            invalid = 1;                            \
        while ((*cur >= '0') && (*cur <= '9')) {    \
            mult /= 10;                             \
            num += (*cur - '0') * mult;             \
            cur++;                                  \
        }                                           \
    }

/**
 * _xmlSchemaParseGMonth:
 * @dt:  pointer to a date structure
 * @str: pointer to the string to analyze
 *
 * Parses a xs:gMonth without time zone and fills in the appropriate
 * field of the @dt structure. @str is updated to point just after the
 * xs:gMonth.
 *
 * Returns 0 or the error code
 *
 * OOM: never
 */
static int
_xmlSchemaParseGMonth (xmlSchemaValDatePtr dt, const xmlChar **str)
{
    const xmlChar *cur = *str;
    int ret = 0;

    PARSE_2_DIGITS(dt->mon, cur, ret);
    if (ret != 0)
        return ret;

    if (!VALID_MONTH(dt->mon))
        return 2;

    *str = cur;
    return 0;
}

/**
 * _xmlSchemaParseGDay:
 * @dt:  pointer to a date structure
 * @str: pointer to the string to analyze
 *
 * Parses a xs:gDay without time zone and fills in the appropriate
 * field of the @dt structure. @str is updated to point just after the
 * xs:gDay.
 *
 * Returns 0 or the error code
 *
 * OOM: never
 */
static int
_xmlSchemaParseGDay (xmlSchemaValDatePtr dt, const xmlChar **str) {
    const xmlChar *cur = *str;
    int ret = 0;

    PARSE_2_DIGITS(dt->day, cur, ret);
    if (ret != 0)
        return ret;

    if (!VALID_DAY(dt->day))
        return 2;

    *str = cur;
    return 0;
}

/**
 * _xmlSchemaParseTime:
 * @dt:  pointer to a date structure
 * @str: pointer to the string to analyze
 *
 * Parses a xs:time without time zone and fills in the appropriate
 * fields of the @dt structure. @str is updated to point just after the
 * xs:time.
 * In case of error, values of @dt fields are undefined.
 *
 * Returns 0 or the error code
 *
 * OOM: never
 */
static int
_xmlSchemaParseTime (xmlSchemaValDatePtr dt, const xmlChar **str) {
    const xmlChar *cur = *str;
    unsigned int hour = 0; /* use temp var in case str is not xs:time */
    int ret = 0;

    PARSE_2_DIGITS(hour, cur, ret);
    if (ret != 0)
        return ret;

    if (*cur != ':')
        return 1;
    cur++;

    /* the ':' insures this string is xs:time */
    dt->hour = hour;

    PARSE_2_DIGITS(dt->min, cur, ret);
    if (ret != 0)
        return ret;

    if (*cur != ':')
        return 1;
    cur++;

    PARSE_FLOAT(dt->sec, cur, ret);
    if (ret != 0)
        return ret;

    if (!VALID_TIME(dt))
        return 2;

    *str = cur;
    return 0;
}

/**
 * _xmlSchemaParseTimeZone:
 * @dt:  pointer to a date structure
 * @str: pointer to the string to analyze
 *
 * Parses a time zone without time zone and fills in the appropriate
 * field of the @dt structure. @str is updated to point just after the
 * time zone.
 *
 * Returns 0 or the error code
 *
 * OOM: never
 */
static int
_xmlSchemaParseTimeZone (xmlSchemaValDatePtr dt, const xmlChar **str) {
    const xmlChar *cur = *str;
    int ret = 0;

    if (str == NULL)
        return -1;

    switch (*cur) {
    case 0:
        dt->tz_flag = 0;
        dt->tzo = 0;
        break;

    case 'Z':
        dt->tz_flag = 1;
        dt->tzo = 0;
        cur++;
        break;

    case '+':
    case '-': {
        int isneg = 0, tmp = 0;
        isneg = (*cur == '-');

        cur++;

        PARSE_2_DIGITS(tmp, cur, ret);
        if (ret != 0)
            return ret;
        if (!VALID_HOUR(tmp))
            return 2;

        if (*cur != ':')
            return 1;
        cur++;

        dt->tzo = tmp * 60;

        PARSE_2_DIGITS(tmp, cur, ret);
        if (ret != 0)
            return ret;
        if (!VALID_MIN(tmp))
            return 2;

        dt->tzo += tmp;
        if (isneg)
            dt->tzo = - dt->tzo;

        if (!VALID_TZO(dt->tzo))
            return 2;

        dt->tz_flag = 1;
        break;
      }
    default:
        return 1;
    }

    *str = cur;
    return 0;
}

/**
 * _xmlSchemaBase64Decode:
 * @ch: a character
 *
 * Converts a base64 encoded character to its base 64 value.
 *
 * Returns 0-63 (value), 64 (pad), or -1 (not recognized)
 *
 * OOM: never
 */
static int
_xmlSchemaBase64Decode (const xmlChar ch) {
    if (('A' <= ch) && (ch <= 'Z')) return ch - 'A';
    if (('a' <= ch) && (ch <= 'z')) return ch - 'a' + 26;
    if (('0' <= ch) && (ch <= '9')) return ch - '0' + 52;
    if ('+' == ch) return 62;
    if ('/' == ch) return 63;
    if ('=' == ch) return 64;
    return -1;
}

/****************************************************************
 *                                                              *
 *  XML Schema Dates/Times Datatypes Handling                   *
 *                                                              *
 ****************************************************************/

/**
 * PARSE_DIGITS:
 * @num:  the integer to fill in
 * @cur:  an #xmlChar *
 * @num_type: an integer flag
 *
 * Parses a digits integer and updates @num with the value. @cur is
 * updated to point just after the integer.
 * In case of error, @num_type is set to -1, values of @num and
 * @cur are undefined.
 */
#define PARSE_DIGITS(num, cur, num_type)                    \
    if ((*cur < '0') || (*cur > '9'))                       \
        num_type = -1;                                      \
    else                                                    \
        while ((*cur >= '0') && (*cur <= '9')) {            \
            num = num * 10 + (*cur - '0');                  \
            cur++;                                          \
        }

/**
 * PARSE_NUM:
 * @num:  the double to fill in
 * @cur:  an #xmlChar *
 * @num_type: an integer flag
 *
 * Parses a float or integer and updates @num with the value. @cur is
 * updated to point just after the number. If the number is a float,
 * then it must have an integer part and a decimal part; @num_type will
 * be set to 1. If there is no decimal part, @num_type is set to zero.
 * In case of error, @num_type is set to -1, values of @num and
 * @cur are undefined.
 */
#define PARSE_NUM(num, cur, num_type)               \
        num = 0;                                    \
        PARSE_DIGITS(num, cur, num_type);           \
        if (!num_type && (*cur == '.')) {           \
            double mult = 1;                        \
            cur++;                                  \
            if ((*cur < '0') || (*cur > '9'))       \
                num_type = -1;                      \
            else                                    \
                num_type = 1;                       \
            while ((*cur >= '0') && (*cur <= '9')){ \
                mult /= 10;                         \
                num += (*cur - '0') * mult;         \
                cur++;                              \
            }                                       \
        }

/**
 * xmlSchemaValidateDates:
 * @type: the expected type or XML_SCHEMAS_UNKNOWN
 * @dateTime:  string to analyze
 * @val:  the return computed value
 *
 * Check that @dateTime conforms to the lexical space of one of the date types.
 * if true a value is computed and returned in @val.
 *
 * Returns 0 if this validates, a positive error code number otherwise
 *         and -1 in case of internal or API error.
 *
 * OOM: OOM flag shold be checked (returns -1 too)
 */
static int
xmlSchemaValidateDates (xmlSchemaValType type,
                    const xmlChar *dateTime, xmlSchemaValPtr *val) {
    xmlSchemaValPtr dt;
    int ret;
    const xmlChar *cur = dateTime;

#define RETURN_TYPE_IF_VALID(t)                 \
    if (IS_TZO_CHAR(*cur)) {                    \
        ret = _xmlSchemaParseTimeZone(&(dt->value.date), &cur); \
        if (ret == 0) {                         \
            if (*cur != 0)                      \
                goto error;                     \
            dt->type = t;                       \
            goto done;                          \
        }                                       \
    }

    if (dateTime == NULL)
        return -1;

    if ((*cur != '-') && (*cur < '0') && (*cur > '9'))
        return 1;

    dt = xmlSchemaNewValue(XML_SCHEMAS_UNKNOWN);
    if (dt == NULL){
        // TODO: set OOM flag
        return -1;
    }

    if ((cur[0] == '-') && (cur[1] == '-')) {
    /*
     * It's an incomplete date (xs:gMonthDay, xs:gMonth or
     * xs:gDay)
     */
    cur += 2;

    /* is it an xs:gDay? */
    if (*cur == '-') {
        if (type == XML_SCHEMAS_GMONTH)
            goto error;
        ++cur;
        ret = _xmlSchemaParseGDay(&(dt->value.date), &cur);
        if (ret != 0)
            goto error;

        RETURN_TYPE_IF_VALID(XML_SCHEMAS_GDAY);

        goto error;
    }

    /*
     * it should be an xs:gMonthDay or xs:gMonth
     */
    ret = _xmlSchemaParseGMonth(&(dt->value.date), &cur);
    if (ret != 0)
        goto error;

    /*
     * a '-' char could indicate this type is xs:gMonthDay or
     * a negative time zone offset. Check for xs:gMonthDay first.
     * Also the first three char's of a negative tzo (-MM:SS) can
     * appear to be a valid day; so even if the day portion
     * of the xs:gMonthDay verifies, we must insure it was not
     * a tzo.
     */
     if (*cur == '-') {
        const xmlChar *rewnd = cur;
        cur++;

        ret = _xmlSchemaParseGDay(&(dt->value.date), &cur);
            if ((ret == 0) && ((*cur == 0) || (*cur != ':')))
            {
                /*
                 * we can use the VALID_MDAY macro to validate the month
                 * and day because the leap year test will flag year zero
                 * as a leap year (even though zero is an invalid year).
                 */
                if (VALID_MDAY((&(dt->value.date))))
                {
                    RETURN_TYPE_IF_VALID(XML_SCHEMAS_GMONTHDAY);

                    goto error;
                }
            }

            /*
             * not xs:gMonthDay so rewind and check if just xs:gMonth
             * with an optional time zone.
             */
            cur = rewnd;
        }

        RETURN_TYPE_IF_VALID(XML_SCHEMAS_GMONTH);

        goto error;
    }

    /*
     * It's a right-truncated date or an xs:time.
     * Try to parse an xs:time then fallback on right-truncated dates.
     */
    if ((*cur >= '0') && (*cur <= '9')) {
        ret = _xmlSchemaParseTime(&(dt->value.date), &cur);
        if (ret == 0) {
            /* it's an xs:time */
            RETURN_TYPE_IF_VALID(XML_SCHEMAS_TIME);
        }
    }

    /* fallback on date parsing */
    cur = dateTime;

    ret = _xmlSchemaParseGYear(&(dt->value.date), &cur);
    if (ret != 0)
        goto error;

    /* is it an xs:gYear? */
    RETURN_TYPE_IF_VALID(XML_SCHEMAS_GYEAR);

    if (*cur != '-')
        goto error;
    cur++;

    ret = _xmlSchemaParseGMonth(&(dt->value.date), &cur);
    if (ret != 0)
        goto error;

    /* is it an xs:gYearMonth? */
    RETURN_TYPE_IF_VALID(XML_SCHEMAS_GYEARMONTH);

    if (*cur != '-')
        goto error;
    cur++;

    ret = _xmlSchemaParseGDay(&(dt->value.date), &cur);
    if ((ret != 0) || !VALID_DATE((&(dt->value.date))))
    goto error;

    /* is it an xs:date? */
    RETURN_TYPE_IF_VALID(XML_SCHEMAS_DATE);

    if (*cur != 'T')
        goto error;
    cur++;

    /* it should be an xs:dateTime */
    ret = _xmlSchemaParseTime(&(dt->value.date), &cur);
    if (ret != 0)
        goto error;

    ret = _xmlSchemaParseTimeZone(&(dt->value.date), &cur);
    if ((ret != 0) || (*cur != 0) || !VALID_DATETIME((&(dt->value.date))))
        goto error;


    dt->type = XML_SCHEMAS_DATETIME;

done:
#if 1
    if ((type != XML_SCHEMAS_UNKNOWN) && (type != dt->type))
        goto error;
#else
    /*
     * insure the parsed type is equal to or less significant (right
     * truncated) than the desired type.
     */
    if ((type != XML_SCHEMAS_UNKNOWN) && (type != dt->type)) {

        /* time only matches time */
        if ((type == XML_SCHEMAS_TIME) && (dt->type == XML_SCHEMAS_TIME))
            goto error;

        if ((type == XML_SCHEMAS_DATETIME) &&
            ((dt->type != XML_SCHEMAS_DATE) ||
             (dt->type != XML_SCHEMAS_GYEARMONTH) ||
             (dt->type != XML_SCHEMAS_GYEAR)))
            goto error;

        if ((type == XML_SCHEMAS_DATE) &&
            ((dt->type != XML_SCHEMAS_GYEAR) ||
             (dt->type != XML_SCHEMAS_GYEARMONTH)))
            goto error;

        if ((type == XML_SCHEMAS_GYEARMONTH) && (dt->type != XML_SCHEMAS_GYEAR))
            goto error;

        if ((type == XML_SCHEMAS_GMONTHDAY) && (dt->type != XML_SCHEMAS_GMONTH))
            goto error;
    }
#endif

    if (val != NULL)
        *val = dt;
    else
        xmlSchemaFreeValue(dt);

    return 0;

error:
    if (dt != NULL)
        xmlSchemaFreeValue(dt);
    return 1;
}

/**
 * xmlSchemaValidateDuration:
 * @type: the predefined type
 * @duration:  string to analyze
 * @val:  the return computed value
 *
 * Check that @duration conforms to the lexical space of the duration type.
 * if true a value is computed and returned in @val.
 *
 * Returns 0 if this validates, a positive error code number otherwise
 *         and -1 in case of internal or API error.
 *
 * OOM: iif returns -1 and OOM flag is set
 */
static int
xmlSchemaValidateDuration (xmlSchemaTypePtr type ,
                       const xmlChar *duration, xmlSchemaValPtr *val)
{
    const xmlChar  *cur = duration;
    xmlSchemaValPtr dur;
    int isneg = 0;
    unsigned int seq = 0;
    double         num;
    int            num_type = 0;  /* -1 = invalid, 0 = int, 1 = floating */
    const xmlChar  desig[]  = {'Y', 'M', 'D', 'H', 'M', 'S'};
    const double   multi[]  = { 0.0, 0.0, 86400.0, 3600.0, 60.0, 1.0, 0.0};

    if (duration == NULL)
        return -1;

    if (*cur == '-') {
        isneg = 1;
        cur++;
    }

    /* duration must start with 'P' (after sign) */
    if (*cur++ != 'P')
        return 1;

    if (*cur == 0)
        return 1;

    dur = xmlSchemaNewValue(XML_SCHEMAS_DURATION);
    if (dur == NULL){
        // TODO: OOM: set OOM flag
        return -1;
    }

    while (*cur != 0) {

        /* input string should be empty or invalid date/time item */
        if (seq >= sizeof(desig))
            goto error;

        /* T designator must be present for time items */
        if (*cur == 'T') {
            if (seq <= 3) {
                seq = 3;
                cur++;
            }else{
                return 1;
            }
        } else if (seq == 3)
            goto error;

        /* parse the number portion of the item */
        PARSE_NUM(num, cur, num_type);

        if ((num_type == -1) || (*cur == 0))
            goto error;

        /* update duration based on item type */
        while (seq < sizeof(desig))
        {
            if (*cur == desig[seq])
            {
                /* verify numeric type; only seconds can be float */
                if ((num_type != 0) && (seq < (sizeof(desig)-1)))
                    goto error;

                switch (seq) {
                    case 0:
                        dur->value.dur.mon = (long)num * 12;
                        break;
                    case 1:
                        dur->value.dur.mon += (long)num;
                        break;
                    default:
                        /* convert to seconds using multiplier */
                        dur->value.dur.sec += num * multi[seq];
                        seq++;
                        break;
                }

                break;          /* exit loop */
            }
            /* no date designators found? */
            if (++seq == 3)
                goto error;
        }
        cur++;
    }

    if (isneg) {
        dur->value.dur.mon = -dur->value.dur.mon;
        dur->value.dur.day = -dur->value.dur.day;
        dur->value.dur.sec = -dur->value.dur.sec;
    }

    if (val != NULL)
        *val = dur;
    else
        xmlSchemaFreeValue(dur);

    return 0;

error:
    if (dur != NULL)
        xmlSchemaFreeValue(dur);
    return 1;
}

/**
 * xmlSchemaStrip:
 * @value: a value
 *
 * Removes the leading and ending spaces of a string
 *
 * Returns the new string or NULL if no change was required.
 *
 * OOM: iif returns NULL and OOM flag is set
 */
static xmlChar *
xmlSchemaStrip(const xmlChar *value) {
    const xmlChar *start = value, *end, *f;

    if (value == NULL) return(NULL);
    while ((*start != 0) && (IS_BLANK_CH(*start)))
    {
        start++;
    }
    end = start;

    while (*end != 0)
    {
        end++;
    }
    f = end;
    end--;

    while ((end > start) && (IS_BLANK_CH(*end)))
    {
        end--;
    }
    end++;

    if ((start == value) && (f == end))
        return(NULL);
    // Note: xmlStrndup may set OOM flag when NULL is returned
    return(xmlStrndup(start, end - start));
}

/**
 * xmlSchemaCollapseString:
 * @value: a value
 *
 * Removes and normalize white spaces in the string
 *
 * Returns the new string or NULL if no change was required.
 *
 * OOM: iif returns NULL and OOM flag is set
 */
static xmlChar *
xmlSchemaCollapseString(const xmlChar *value) {
    const xmlChar *start = value, *end, *f;
    xmlChar *g;
    int col = 0;

    if (value == NULL)
        return(NULL);

    while ((*start != 0) && (IS_BLANK_CH(*start)))
    {
        start++;
    }
    end = start;

    while (*end != 0) {
        if ((*end == ' ') && (IS_BLANK_CH(end[1]))){
            col = end - start;
            break;
        } else if ((*end == 0xa) || (*end == 0x9) || (*end == 0xd)) {
            col = end - start;
            break;
        }
        end++;
    }

    if (col == 0) {
        f = end;
        end--;
        while ((end > start) && (IS_BLANK_CH(*end)))
        {
            end--;
        }
        end++;
        if ((start == value) && (f == end))
        {
            return(NULL);
        }
        // xmlStrndup may set OOM flag
        return(xmlStrndup(start, end - start));
    }
    start = xmlStrdup(start);
    if (start == NULL)
        return(NULL);

    g = (xmlChar *) (start + col);
    end = g;
    while (*end != 0) {
        if (IS_BLANK_CH(*end))
        {
            end++;
            while (IS_BLANK_CH(*end))
                end++;
            if (*end != 0)
                *g++ = ' ';
        }
        else{
            *g++ = *end++;
        }
    }
    *g = 0;
    return((xmlChar *) start);
}

/**
 * xmlSchemaValAtomicListNode:
 * @type: the predefined atomic type for a token in the list
 * @value: the list value to check
 * @ret:  the return computed value
 * @node:  the node containing the value
 *
 * Check that a value conforms to the lexical space of the predefined
 * list type. if true a value is computed and returned in @ret.
 *
 * Returns the number of items if this validates, a negative error code
 *         number otherwise
 *
 * OOM: iif returns -1 and OOM flag is set
 */
static int
xmlSchemaValAtomicListNode(xmlSchemaTypePtr type, const xmlChar *value,
                       xmlSchemaValPtr *ret, xmlNodePtr node) {
    xmlChar *val, *cur, *endval;
    int nb_values = 0;
    int tmp = 0;

    if (value == NULL) {
        return(-1);
    }
    val = xmlStrdup(value);
    if (val == NULL) {
        return(-1);
    }
    cur = val;
    /*
     * Split the list
     */
    while (IS_BLANK_CH(*cur))
        *cur++ = 0;

    while (*cur != 0) {
        if (IS_BLANK_CH(*cur)) {
            *cur = 0;
            cur++;
            while (IS_BLANK_CH(*cur))
            {
                *cur++ = 0;
            }
        }else{
            nb_values++;
            cur++;
            while ((*cur != 0) && (!IS_BLANK_CH(*cur)))
            {
                cur++;
            }
        }
    }
    if (nb_values == 0) {
        if (ret != NULL) {
            // XMLENGINE: IMPLEMENTME: unimplemented block in xmlschematypes.c!
            _TODO_
        }
        xmlFree(val);
        return(nb_values);
    }
    endval = cur;
    cur = val;
    while ((*cur == 0) && (cur != endval))
    {
        cur++;
    }
    while (cur != endval)
    {
        tmp = xmlSchemaValPredefTypeNode(type, cur, NULL, node);
        if (tmp != 0)
            break;
        while (*cur != 0) cur++;
        while ((*cur == 0) && (cur != endval)) cur++;
    }
    xmlFree(val);
    if (ret != NULL) {
     _TODO_
    }
    if (tmp == 0)
    return(nb_values);
    return(-1);
}

/**
 * xmlSchemaParseUInt:
 * @str: pointer to the string R/W
 * @llo: pointer to the low result
 * @lmi: pointer to the mid result
 * @lhi: pointer to the high result
 *
 * Parse an unsigned long into 3 fields.
 *
 * Returns the number of chars parsed or -1 if overflow of the capacity
 */
static int
xmlSchemaParseUInt(const xmlChar **str, unsigned long *llo,
               unsigned long *lmi, unsigned long *lhi) {
    unsigned long lo = 0, mi = 0, hi = 0;
    const xmlChar *tmp, *cur = *str;
    int ret = 0, i = 0;

    while (*cur == '0') {
    ret++;
    cur++;
    }
    tmp = cur;
    while ((*tmp != 0) && (*tmp >= '0') && (*tmp <= '9')) {
    i++;tmp++;ret++;
    }
    if (i > 24) {
    *str = tmp;
    return(-1);
    }
    while (i > 16) {
    hi = hi * 10 + (*cur++ - '0');
    i--;
    }
    while (i > 8) {
    mi = mi * 10 + (*cur++ - '0');
    i--;
    }
    while (i > 0) {
    lo = lo * 10 + (*cur++ - '0');
    i--;
    }

    *str = cur;
    *llo = lo;
    *lmi = mi;
    *lhi = hi;
    return(ret);
}

/**
 * xmlSchemaValAtomicType:
 * @type: the predefined type
 * @value: the value to check
 * @val:  the return computed value
 * @node:  the node containing the value
 * flags:  flags to control the validation
 *
 * Check that a value conforms to the lexical space of the atomic type.
 * if true a value is computed and returned in @val.
 *
 * Returns 0 if this validates, a positive error code number otherwise
 *         and -1 in case of internal or API error.
 */
static int
xmlSchemaValAtomicType(xmlSchemaTypePtr type, const xmlChar * value,
                       xmlSchemaValPtr * val, xmlNodePtr node, int flags)
{
    xmlSchemaValPtr v;
    xmlChar *norm = NULL;
    int ret = 0;

    if (xmlSchemaTypesInitialized == 0)
        return (-1);
    if (type == NULL)
        return (-1);

    if (val != NULL)
        *val = NULL;
    if ((flags == 0) && (value != NULL)) {
        if ((type->flags != XML_SCHEMAS_STRING) &&
            (type->flags != XML_SCHEMAS_NORMSTRING)) {
            norm = xmlSchemaCollapseString(value);
            if (norm != NULL){
                // TODO: OOM: Check OOM flag
                value = norm;
            }
        }
    }

    switch (type->flags) {
        case XML_SCHEMAS_UNKNOWN:
            if (type == xmlSchemaTypeAnyTypeDef)
                goto return0;
            goto error;
        case XML_SCHEMAS_STRING:
            goto return0;
        case XML_SCHEMAS_NORMSTRING:{
                const xmlChar *cur = value;

                while (*cur != 0) {
                    if ((*cur == 0xd) || (*cur == 0xa) || (*cur == 0x9)) {
                        goto return1;
                    } else {
                        cur++;
                    }
                }
                if (val != NULL) {
                    v = xmlSchemaNewValue(XML_SCHEMAS_NORMSTRING);
                    if (v != NULL) {
                        v->value.str = xmlStrdup(value);
                        *val = v;
                    } else {
                        goto error;
                    }
                }
                goto return0;
            }
        case XML_SCHEMAS_DECIMAL:{
                const xmlChar *cur = value, *tmp;
                unsigned int frac = 0, len, neg = 0;
                unsigned long base = 0;

                if (cur == NULL)
                    goto return1;
                if (*cur == '+')
                    cur++;
                else if (*cur == '-') {
                    neg = 1;
                    cur++;
                }
                tmp = cur;
                while ((*cur >= '0') && (*cur <= '9')) {
                    base = base * 10 + (*cur - '0');
                    cur++;
                }
                len = cur - tmp;
                if (*cur == '.') {
                    cur++;
                    tmp = cur;
                    while ((*cur >= '0') && (*cur <= '9')) {
                        base = base * 10 + (*cur - '0');
                        cur++;
                    }
                    frac = cur - tmp;
                }
                if (*cur != 0)
                    goto return1;
                if (val != NULL) {
                    v = xmlSchemaNewValue(XML_SCHEMAS_DECIMAL);
                    if (v != NULL) {
                        v->value.decimal.lo = base;
                        v->value.decimal.sign = neg;
                        v->value.decimal.frac = frac;
                        v->value.decimal.total = frac + len;
                        *val = v;
                    }
                    else
                    {
                     // TODO: OOM happened!
                    }
                }
                goto return0;
            }
        case XML_SCHEMAS_TIME:
        case XML_SCHEMAS_GDAY:
        case XML_SCHEMAS_GMONTH:
        case XML_SCHEMAS_GMONTHDAY:
        case XML_SCHEMAS_GYEAR:
        case XML_SCHEMAS_GYEARMONTH:
        case XML_SCHEMAS_DATE:
        case XML_SCHEMAS_DATETIME:
            ret = xmlSchemaValidateDates((xmlSchemaValType)type->flags, value, val);
            // TODO: if ret == -1 and OOM flag is set, then OOM!
            break;
        case XML_SCHEMAS_DURATION:
            ret = xmlSchemaValidateDuration(type, value, val);
            // TODO: if ret == -1 and OOM flag is set, then OOM!
            break;
        case XML_SCHEMAS_FLOAT:
        case XML_SCHEMAS_DOUBLE:{
                const xmlChar *cur = value;
                int neg = 0;

                if (cur == NULL)
                    goto return1;
                if ((cur[0] == 'N') && (cur[1] == 'a') && (cur[2] == 'N')) {
                    cur += 3;
                    if (*cur != 0)
                        goto return1;
                    if (val != NULL) {
                        if (type == xmlSchemaTypeFloatDef) {
                            v = xmlSchemaNewValue(XML_SCHEMAS_FLOAT);
                            if (v != NULL) {
                                v->value.f = (float) xmlXPathNAN;
                            } else {
                                xmlSchemaFreeValue(v);
                                // TODO: OOM:
                                goto error;
                            }
                        } else {
                            v = xmlSchemaNewValue(XML_SCHEMAS_DOUBLE);
                            if (v != NULL) {
                                v->value.d = xmlXPathNAN;
                            } else {
                                xmlSchemaFreeValue(v);
                                // TODO: OOM:
                                goto error;
                            }
                        }
                        *val = v;
                    }
                    goto return0;
                }
                if (*cur == '-') {
                    neg = 1;
                    cur++;
                }
                if ((cur[0] == 'I') && (cur[1] == 'N') && (cur[2] == 'F')) {
                    cur += 3;
                    if (*cur != 0)
                        goto return1;
                    if (val != NULL) {
                        if (type == xmlSchemaTypeFloatDef) {
                            v = xmlSchemaNewValue(XML_SCHEMAS_FLOAT);
                            if (v != NULL) {
                                if (neg)
                                    v->value.f = (float) xmlXPathNINF;
                                else
                                    v->value.f = (float) xmlXPathPINF;
                            } else {
                                xmlSchemaFreeValue(v);
                                // TODO: OOM:
                                goto error;
                            }
                        } else {
                            v = xmlSchemaNewValue(XML_SCHEMAS_DOUBLE);
                            if (v != NULL) {
                                if (neg)
                                    v->value.d = xmlXPathNINF;
                                else
                                    v->value.d = xmlXPathPINF;
                            } else {
                                xmlSchemaFreeValue(v);
                                // TODO: OOM:
                                goto error;
                            }
                        }
                        *val = v;
                    }
                    goto return0;
                }
                if ((neg == 0) && (*cur == '+'))
                    cur++;
                if ((cur[0] == 0) || (cur[0] == '+') || (cur[0] == '-'))
                    goto return1;
                while ((*cur >= '0') && (*cur <= '9')) {
                    cur++;
                }
                if (*cur == '.') {
                    cur++;
                    while ((*cur >= '0') && (*cur <= '9'))
                        cur++;
                }
                if ((*cur == 'e') || (*cur == 'E')) {
                    cur++;
                    if ((*cur == '-') || (*cur == '+'))
                        cur++;
                    while ((*cur >= '0') && (*cur <= '9'))
                        cur++;
                }
                if (*cur != 0)
                    goto return1;
                if (val != NULL) {
                    if (type == xmlSchemaTypeFloatDef) {
                        v = xmlSchemaNewValue(XML_SCHEMAS_FLOAT);
                        if (v != NULL) {
                            if (sscanf((const char *) value, "%f",
                                 &(v->value.f)) == 1) {
                                *val = v;
                            } else {
                                xmlSchemaFreeValue(v);
                                goto return1;
                            }
                        } else {
                            goto error;
                        }
                    } else {
                        v = xmlSchemaNewValue(XML_SCHEMAS_DOUBLE);
                        if (v != NULL) {
                            if (sscanf((const char *) value, "%lf",
                                 &(v->value.d)) == 1) {
                                *val = v;
                            } else {
                                xmlSchemaFreeValue(v);
                                goto return1;
                            }
                        } else {
                            goto error;
                        }
                    }
                }
                goto return0;
            }
        case XML_SCHEMAS_BOOLEAN:{
                const xmlChar *cur = value;

                if ((cur[0] == '0') && (cur[1] == 0))
                    ret = 0;
                else if ((cur[0] == '1') && (cur[1] == 0))
                    ret = 1;
                else if ((cur[0] == 't') && (cur[1] == 'r')
                         && (cur[2] == 'u') && (cur[3] == 'e')
                         && (cur[4] == 0))
                    ret = 1;
                else if ((cur[0] == 'f') && (cur[1] == 'a')
                         && (cur[2] == 'l') && (cur[3] == 's')
                         && (cur[4] == 'e') && (cur[5] == 0))
                    ret = 0;
                else
                    goto return1;
                if (val != NULL) {
                    v = xmlSchemaNewValue(XML_SCHEMAS_BOOLEAN);
                    if (v != NULL) {
                        v->value.b = ret;
                        *val = v;
                    } else {
                        goto error;
                    }
                }
                goto return0;
            }
        case XML_SCHEMAS_TOKEN:{
                const xmlChar *cur = value;

                if (IS_BLANK_CH(*cur))
                    goto return1;

                while (*cur != 0) {
                    if ((*cur == 0xd) || (*cur == 0xa) || (*cur == 0x9)) {
                        goto return1;
                    } else if (*cur == ' ') {
                        cur++;
                        if (*cur == 0)
                            goto return1;
                        if (*cur == ' ')
                            goto return1;
                    } else {
                        cur++;
                    }
                }
                if (val != NULL) {
                    v = xmlSchemaNewValue(XML_SCHEMAS_TOKEN);
                    if (v != NULL) {
                        v->value.str = xmlStrdup(value);
                        *val = v;
                    } else {
                        goto error;
                    }
                }
                goto return0;
            }
        case XML_SCHEMAS_LANGUAGE:
            if (xmlCheckLanguageID(value) == 1) {
                if (val != NULL) {
                    v = xmlSchemaNewValue(XML_SCHEMAS_LANGUAGE);
                    if (v != NULL) {
                        v->value.str = xmlStrdup(value);
                        *val = v;
                    } else {
                        goto error;
                    }
                }
                goto return0;
            }
            goto return1;
        case XML_SCHEMAS_NMTOKEN:
            if (xmlValidateNMToken(value, 1) == 0) {
                if (val != NULL) {
                    v = xmlSchemaNewValue(XML_SCHEMAS_NMTOKEN);
                    if (v != NULL) {
                        v->value.str = xmlStrdup(value);
                        *val = v;
                    } else {
                        goto error;
                    }
                }
                goto return0;
            }
            goto return1;
        case XML_SCHEMAS_NMTOKENS:
            ret = xmlSchemaValAtomicListNode(xmlSchemaTypeNmtokenDef,
                                             value, val, node);
            if (ret > 0)
                ret = 0;
            else
                ret = 1;
            goto done;
        case XML_SCHEMAS_NAME:
            ret = xmlValidateName(value, 1);
            if ((ret == 0) && (val != NULL)) {
                _TODO_;
            }
            goto done;
        case XML_SCHEMAS_QNAME:{
                xmlChar *uri = NULL;
                xmlChar *local = NULL;

                ret = xmlValidateQName(value, 1);
                if ((ret == 0) && (node != NULL)) {
                    xmlChar *prefix;

                    local = xmlSplitQName2(value, &prefix);
                    if (prefix != NULL) {
                        xmlNsPtr ns;

                        ns = xmlSearchNs(node->doc, node, prefix);
                        if (ns == NULL)
                            ret = 1;
                        else if (val != NULL)
                            uri = xmlStrdup(ns->href);
                    }
                    if ((local != NULL) && ((val == NULL) || (ret != 0)))
                        xmlFree(local);
                    if (prefix != NULL)
                        xmlFree(prefix);
                }
                if ((ret == 0) && (val != NULL)) {
                    v = xmlSchemaNewValue(XML_SCHEMAS_QNAME);
                    if (v != NULL) {
                        if (local != NULL)
                            v->value.qname.name = local;
                        else
                            v->value.qname.name = xmlStrdup(value);
                        if (uri != NULL)
                            v->value.qname.uri = uri;

                        *val = v;
                    } else {
                        if (local != NULL)
                            xmlFree(local);
                        if (uri != NULL)
                            xmlFree(uri);
                        goto error;
                    }
                }
                goto done;
            }
        case XML_SCHEMAS_NCNAME:
            ret = xmlValidateNCName(value, 1);
            if ((ret == 0) && (val != NULL)) {
                v = xmlSchemaNewValue(XML_SCHEMAS_NCNAME);
                if (v != NULL) {
                    v->value.str = xmlStrdup(value);
                    *val = v;
                } else {
                    goto error;
                }
            }
            goto done;
        case XML_SCHEMAS_ID:
            ret = xmlValidateNCName(value, 1);
            if ((ret == 0) && (val != NULL)) {
                v = xmlSchemaNewValue(XML_SCHEMAS_ID);
                if (v != NULL) {
                    v->value.str = xmlStrdup(value);
                    *val = v;
                } else {
                    goto error;
                }
            }
            if ((ret == 0) && (node != NULL) &&
                (node->type == XML_ATTRIBUTE_NODE)) {
                xmlAttrPtr attr = (xmlAttrPtr) node;

                /*
                 * NOTE: the IDness might have already be declared in the DTD
                 */
                if (attr->atype != XML_ATTRIBUTE_ID) {
                    xmlIDPtr res;
                    xmlChar *strip;

                    strip = xmlSchemaStrip(value);
                    if (strip != NULL) {
                        res = xmlAddID(NULL, node->doc, strip, attr);
                        xmlFree(strip);
                    } else
                        res = xmlAddID(NULL, node->doc, value, attr);
                    if (res == NULL) {
                        ret = 2;
                    } else {
                        attr->atype = XML_ATTRIBUTE_ID;
                    }
                }
            }
            goto done;
        case XML_SCHEMAS_IDREF:
            ret = xmlValidateNCName(value, 1);
            if ((ret == 0) && (val != NULL)) {
                _TODO_;
            }
            if ((ret == 0) && (node != NULL) &&
                (node->type == XML_ATTRIBUTE_NODE)) {
                xmlAttrPtr attr = (xmlAttrPtr) node;
                xmlChar *strip;

                strip = xmlSchemaStrip(value);
                if (strip != NULL) {
                    xmlAddRef(NULL, node->doc, strip, attr);
                    xmlFree(strip);
                } else
                    xmlAddRef(NULL, node->doc, value, attr);
                attr->atype = XML_ATTRIBUTE_IDREF;
            }
            goto done;
        case XML_SCHEMAS_IDREFS:
            ret = xmlSchemaValAtomicListNode(xmlSchemaTypeIdrefDef,
                                             value, val, node);
            if (ret < 0)
                ret = 2;
            else
                ret = 0;
            if ((ret == 0) && (node != NULL) &&
                (node->type == XML_ATTRIBUTE_NODE)) {
                xmlAttrPtr attr = (xmlAttrPtr) node;

                attr->atype = XML_ATTRIBUTE_IDREFS;
            }
            goto done;
        case XML_SCHEMAS_ENTITY:{
                xmlChar *strip;

                ret = xmlValidateNCName(value, 1);
                if ((node == NULL) || (node->doc == NULL))
                    ret = 3;
                if (ret == 0) {
                    xmlEntityPtr ent;

                    strip = xmlSchemaStrip(value);
                    if (strip != NULL) {
                        ent = xmlGetDocEntity(node->doc, strip);
                        xmlFree(strip);
                    } else {
                        ent = xmlGetDocEntity(node->doc, value);
                    }
                    if ((ent == NULL) ||
                        (ent->etype !=
                         XML_EXTERNAL_GENERAL_UNPARSED_ENTITY))
                        ret = 4;
                }
                if ((ret == 0) && (val != NULL)) {
                    _TODO_;
                }
                if ((ret == 0) && (node != NULL) &&
                    (node->type == XML_ATTRIBUTE_NODE)) {
                    xmlAttrPtr attr = (xmlAttrPtr) node;

                    attr->atype = XML_ATTRIBUTE_ENTITY;
                }
                goto done;
            }
        case XML_SCHEMAS_ENTITIES:
            if ((node == NULL) || (node->doc == NULL))
                goto return3;
            ret = xmlSchemaValAtomicListNode(xmlSchemaTypeEntityDef,
                                             value, val, node);
            if (ret <= 0)
                ret = 1;
            else
                ret = 0;
            if ((ret == 0) && (node != NULL) &&
                (node->type == XML_ATTRIBUTE_NODE)) {
                xmlAttrPtr attr = (xmlAttrPtr) node;

                attr->atype = XML_ATTRIBUTE_ENTITIES;
            }
            goto done;
        case XML_SCHEMAS_NOTATION:{
                xmlChar *uri = NULL;
                xmlChar *local = NULL;

                ret = xmlValidateQName(value, 1);
                if ((ret == 0) && (node != NULL)) {
                    xmlChar *prefix;

                    local = xmlSplitQName2(value, &prefix);
                    if (prefix != NULL) {
                        xmlNsPtr ns;

                        ns = xmlSearchNs(node->doc, node, prefix);
                        if (ns == NULL)
                            ret = 1;
                        else if (val != NULL)
                            uri = xmlStrdup(ns->href);
                    }
                    if ((local != NULL) && ((val == NULL) || (ret != 0)))
                        xmlFree(local);
                    if (prefix != NULL)
                        xmlFree(prefix);
                }
                if ((node == NULL) || (node->doc == NULL))
                    ret = 3;
                if (ret == 0) {
                    ret = xmlValidateNotationUse(NULL, node->doc, value);
                    if (ret == 1)
                        ret = 0;
                    else
                        ret = 1;
                }
                if ((ret == 0) && (val != NULL)) {
                    v = xmlSchemaNewValue(XML_SCHEMAS_NOTATION);
                    if (v != NULL) {
                        if (local != NULL)
                            v->value.qname.name = local;
                        else
                            v->value.qname.name = xmlStrdup(value);
                        if (uri != NULL)
                            v->value.qname.uri = uri;

                        *val = v;
                    } else {
                        if (local != NULL)
                            xmlFree(local);
                        if (uri != NULL)
                            xmlFree(uri);
                        goto error;
                    }
                }
                goto done;
            }
        case XML_SCHEMAS_ANYURI:{
                if (*value != 0) {
                    xmlURIPtr uri = xmlParseURI((const char *) value);
                    if (uri == NULL)
                        goto return1;
                    xmlFreeURI(uri);
                }

                if (val != NULL) {
                    v = xmlSchemaNewValue(XML_SCHEMAS_ANYURI);
                    if (v == NULL)
                        goto error;
                    v->value.str = xmlStrdup(value);
                    *val = v;
                }
                goto return0;
            }
        case XML_SCHEMAS_HEXBINARY:{
                const xmlChar *cur = value;
                xmlChar *base;
                int total, i = 0;

                if (cur == NULL)
                    goto return1;

                while (((*cur >= '0') && (*cur <= '9')) ||
                       ((*cur >= 'A') && (*cur <= 'F')) ||
                       ((*cur >= 'a') && (*cur <= 'f'))) {
                    i++;
                    cur++;
                }

                if (*cur != 0)
                    goto return1;
                if ((i % 2) != 0)
                    goto return1;

                if (val != NULL) {

                    v = xmlSchemaNewValue(XML_SCHEMAS_HEXBINARY);
                    if (v == NULL)
                        goto error;

                    cur = xmlStrdup(value);
                    if (cur == NULL) {
                        xmlSchemaTypeErrMemory(node, "allocating hexbin data");
                        xmlFree(v);
                        goto return1;
                    }

                    total = i / 2;      /* number of octets */

                    base = (xmlChar *) cur;
                    while (i-- > 0) {
                        if (*base >= 'a')
                            *base = *base - ('a' - 'A');
                        base++;
                    }

                    v->value.hex.str = (xmlChar *) cur;
                    v->value.hex.total = total;
                    *val = v;
                }
                goto return0;
            }
        case XML_SCHEMAS_BASE64BINARY:{
                /* ISSUE:
                 *
                 * Ignore all stray characters? (yes, currently)
                 * Worry about long lines? (no, currently)
                 *
                 * rfc2045.txt:
                 *
                 * "The encoded output stream must be represented in lines of
                 * no more than 76 characters each.  All line breaks or other
                 * characters not found in Table 1 must be ignored by decoding
                 * software.  In base64 data, characters other than those in
                 * Table 1, line breaks, and other white space probably
                 * indicate a transmission error, about which a warning
                 * message or even a message rejection might be appropriate
                 * under some circumstances." */
                const xmlChar *cur = value;
                xmlChar *base;
                int total, i = 0, pad = 0;

                if (cur == NULL)
                    goto return1;

                for (; *cur; ++cur) {
                    int decc;

                    decc = _xmlSchemaBase64Decode(*cur);
                    if (decc < 0) ;
                    else if (decc < 64)
                        i++;
                    else
                        break;
                }
                for (; *cur; ++cur) {
                    int decc;

                    decc = _xmlSchemaBase64Decode(*cur);
                    if (decc < 0) ;
                    else if (decc < 64)
                        goto return1;
                    if (decc == 64)
                        pad++;
                }

                /* rfc2045.txt: "Special processing is performed if fewer than
                 * 24 bits are available at the end of the data being encoded.
                 * A full encoding quantum is always completed at the end of a
                 * body.  When fewer than 24 input bits are available in an
                 * input group, zero bits are added (on the right) to form an
                 * integral number of 6-bit groups.  Padding at the end of the
                 * data is performed using the "=" character.  Since all
                 * base64 input is an integral number of octets, only the
                 * following cases can arise: (1) the final quantum of
                 * encoding input is an integral multiple of 24 bits; here,
                 * the final unit of encoded output will be an integral
                 * multiple ofindent: Standard input:701: Warning:old style
                 * assignment ambiguity in "=*".  Assuming "= *" 4 characters
                 * with no "=" padding, (2) the final
                 * quantum of encoding input is exactly 8 bits; here, the
                 * final unit of encoded output will be two characters
                 * followed by two "=" padding characters, or (3) the final
                 * quantum of encoding input is exactly 16 bits; here, the
                 * final unit of encoded output will be three characters
                 * followed by one "=" padding character." */

                total = 3 * (i / 4);
                if (pad == 0) {
                    if (i % 4 != 0)
                        goto return1;
                } else if (pad == 1) {
                    int decc;

                    if (i % 4 != 3)
                        goto return1;
                    for (decc = _xmlSchemaBase64Decode(*cur);
                         (decc < 0) || (decc > 63);
                         decc = _xmlSchemaBase64Decode(*cur))
                        --cur;
                    /* 16bits in 24bits means 2 pad bits: nnnnnn nnmmmm mmmm00*/
                    /* 00111100 -> 0x3c */
                    if (decc & ~0x3c)
                        goto return1;
                    total += 2;
                } else if (pad == 2) {
                    int decc;

                    if (i % 4 != 2)
                        goto return1;
                    for (decc = _xmlSchemaBase64Decode(*cur);
                         (decc < 0) || (decc > 63);
                         decc = _xmlSchemaBase64Decode(*cur))
                        --cur;
                    /* 8bits in 12bits means 4 pad bits: nnnnnn nn0000 */
                    /* 00110000 -> 0x30 */
                    if (decc & ~0x30)
                        goto return1;
                    total += 1;
                } else
                    goto return1;

                if (val != NULL) {
                    v = xmlSchemaNewValue(XML_SCHEMAS_BASE64BINARY);
                    if (v == NULL)
                        goto error;
                    base =
                        (xmlChar *) xmlMallocAtomic((i + pad + 1) *
                                                    sizeof(xmlChar));
                    if (base == NULL) {
                xmlSchemaTypeErrMemory(node, "allocating base64 data");
                        xmlFree(v);
                        goto return1;
                    }
                    v->value.base64.str = base;
                    for (cur = value; *cur; ++cur)
                        if (_xmlSchemaBase64Decode(*cur) >= 0) {
                            *base = *cur;
                            ++base;
                        }
                    *base = 0;
                    v->value.base64.total = total;
                    *val = v;
                }
                goto return0;
            }
        case XML_SCHEMAS_INTEGER:
        case XML_SCHEMAS_PINTEGER:
        case XML_SCHEMAS_NPINTEGER:
        case XML_SCHEMAS_NINTEGER:
        case XML_SCHEMAS_NNINTEGER:{
                const xmlChar *cur = value;
                unsigned long lo, mi, hi;
                int sign = 0;

                if (cur == NULL)
                    goto return1;
                if (*cur == '-') {
                    sign = 1;
                    cur++;
                } else if (*cur == '+')
                    cur++;
                ret = xmlSchemaParseUInt(&cur, &lo, &mi, &hi);
                if (ret == 0)
                    goto return1;
                if (*cur != 0)
                    goto return1;
                if (type->flags == XML_SCHEMAS_NPINTEGER) {
                    if ((sign == 0) &&
                        ((hi != 0) || (mi != 0) || (lo != 0)))
                        goto return1;
                } else if (type->flags == XML_SCHEMAS_PINTEGER) {
                    if (sign == 1)
                        goto return1;
                    if ((hi == 0) && (mi == 0) && (lo == 0))
                        goto return1;
                } else if (type->flags == XML_SCHEMAS_NINTEGER) {
                    if (sign == 0)
                        goto return1;
                    if ((hi == 0) && (mi == 0) && (lo == 0))
                        goto return1;
                } else if (type->flags == XML_SCHEMAS_NNINTEGER) {
                    if ((sign == 1) &&
                        ((hi != 0) || (mi != 0) || (lo != 0)))
                        goto return1;
                }
                /*
                 * We can store a value only if no overflow occured
                 */
                if ((ret > 0) && (val != NULL)) {
                    v = xmlSchemaNewValue((xmlSchemaValType)type->flags);
                    if (v != NULL) {
                        v->value.decimal.lo = lo;
                        v->value.decimal.mi = lo;
                        v->value.decimal.hi = lo;
                        v->value.decimal.sign = sign;
                        v->value.decimal.frac = 0;
                        v->value.decimal.total = cur - value;
                        *val = v;
                    }
                }
                goto return0;
            }
        case XML_SCHEMAS_LONG:
        case XML_SCHEMAS_BYTE:
        case XML_SCHEMAS_SHORT:
        case XML_SCHEMAS_INT:{
                const xmlChar *cur = value;
                unsigned long lo, mi, hi;
                int total = 0;
                int sign = 0;

                if (cur == NULL)
                    goto return1;
                if (*cur == '-') {
                    sign = 1;
                    cur++;
                } else if (*cur == '+')
                    cur++;
                ret = xmlSchemaParseUInt(&cur, &lo, &mi, &hi);
                if (ret <= 0)
                    goto return1;
                if (*cur != 0)
                    goto return1;
                if (type->flags == XML_SCHEMAS_LONG) {
                    if (hi >= 922) {
                        if (hi > 922)
                            goto return1;
                        if (mi >= 33720368) {
                            if (mi > 33720368)
                                goto return1;
                            if ((sign == 0) && (lo > 54775807))
                                goto return1;
                            if ((sign == 1) && (lo > 54775808))
                                goto return1;
                        }
                    }
                } else if (type->flags == XML_SCHEMAS_INT) {
                    if (hi != 0)
                        goto return1;
                    if (mi >= 21) {
                        if (mi > 21)
                            goto return1;
                        if ((sign == 0) && (lo > 47483647))
                            goto return1;
                        if ((sign == 1) && (lo > 47483648))
                            goto return1;
                    }
                } else if (type->flags == XML_SCHEMAS_SHORT) {
                    if ((mi != 0) || (hi != 0))
                        goto return1;
                    if ((sign == 1) && (lo > 32768))
                        goto return1;
                    if ((sign == 0) && (lo > 32767))
                        goto return1;
                } else if (type->flags == XML_SCHEMAS_BYTE) {
                    if ((mi != 0) || (hi != 0))
                        goto return1;
                    if ((sign == 1) && (lo > 128))
                        goto return1;
                    if ((sign == 0) && (lo > 127))
                        goto return1;
                }
                if (val != NULL) {
                    v = xmlSchemaNewValue((xmlSchemaValType)type->flags);
                    if (v != NULL) {
                        v->value.decimal.lo = lo;
                        v->value.decimal.mi = lo;
                        v->value.decimal.hi = lo;
                        v->value.decimal.sign = sign;
                        v->value.decimal.frac = 0;
                        v->value.decimal.total = total;
                        *val = v;
                    }
                }
                goto return0;
            }
        case XML_SCHEMAS_UINT:
        case XML_SCHEMAS_ULONG:
        case XML_SCHEMAS_USHORT:
        case XML_SCHEMAS_UBYTE:{
                const xmlChar *cur = value;
                unsigned long lo, mi, hi;
                int total = 0;

                if (cur == NULL)
                    goto return1;
                ret = xmlSchemaParseUInt(&cur, &lo, &mi, &hi);
                if (ret <= 0)
                    goto return1;
                if (*cur != 0)
                    goto return1;
                if (type->flags == XML_SCHEMAS_ULONG) {
                    if (hi >= 1844) {
                        if (hi > 1844)
                            goto return1;
                        if (mi >= 67440737) {
                            if (mi > 67440737)
                                goto return1;
                            if (lo > 9551615)
                                goto return1;
                        }
                    }
                } else if (type->flags == XML_SCHEMAS_UINT) {
                    if (hi != 0)
                        goto return1;
                    if (mi >= 42) {
                        if (mi > 42)
                            goto return1;
                        if (lo > 94967295)
                            goto return1;
                    }
                } else if (type->flags == XML_SCHEMAS_USHORT) {
                    if ((mi != 0) || (hi != 0))
                        goto return1;
                    if (lo > 65535)
                        goto return1;
                } else if (type->flags == XML_SCHEMAS_UBYTE) {
                    if ((mi != 0) || (hi != 0))
                        goto return1;
                    if (lo > 255)
                        goto return1;
                }
                if (val != NULL) {
                    v = xmlSchemaNewValue((xmlSchemaValType)type->flags);
                    if (v != NULL) {
                        v->value.decimal.lo = lo;
                        v->value.decimal.mi = mi;
                        v->value.decimal.hi = hi;
                        v->value.decimal.sign = 0;
                        v->value.decimal.frac = 0;
                        v->value.decimal.total = total;
                        *val = v;
                    }
                }
                goto return0;
            }
    }

  done:
    if (norm != NULL)
        xmlFree(norm);
    return (ret);
  return3:
    if (norm != NULL)
        xmlFree(norm);
    return (3);
  return1:
    if (norm != NULL)
        xmlFree(norm);
    return (1);
  return0:
    if (norm != NULL)
        xmlFree(norm);
    return (0);
  error:
    if (norm != NULL)
        xmlFree(norm);
    return (-1);
}

/**
 * xmlSchemaValPredefTypeNode:
 * @type: the predefined type
 * @value: the value to check
 * @val:  the return computed value
 * @node:  the node containing the value
 *
 * Check that a value conforms to the lexical space of the predefined type.
 * if true a value is computed and returned in @val.
 *
 * Returns 0 if this validates, a positive error code number otherwise
 *         and -1 in case of internal or API error.
 */
int
xmlSchemaValPredefTypeNode(xmlSchemaTypePtr type, const xmlChar *value,
                       xmlSchemaValPtr *val, xmlNodePtr node) {
    return(xmlSchemaValAtomicType(type, value, val, node, 0));
}

/**
 * xmlSchemaValidatePredefinedType:
 * @type: the predefined type
 * @value: the value to check
 * @val:  the return computed value
 *
 * Check that a value conforms to the lexical space of the predefined type.
 * if true a value is computed and returned in @val.
 *
 * Returns 0 if this validates, a positive error code number otherwise
 *         and -1 in case of internal or API error.
 */
int
xmlSchemaValidatePredefinedType(xmlSchemaTypePtr type, const xmlChar *value,
                            xmlSchemaValPtr *val) {
    return(xmlSchemaValPredefTypeNode(type, value, val, NULL));
}

/**
 * xmlSchemaCompareDecimals:
 * @x:  a first decimal value
 * @y:  a second decimal value
 *
 * Compare 2 decimals
 *
 * Returns -1 if x < y, 0 if x == y, 1 if x > y and -2 in case of error
 *
 * OOM: never
 */
static int
xmlSchemaCompareDecimals(xmlSchemaValPtr x, xmlSchemaValPtr y)
{
    xmlSchemaValPtr swp;
    int order = 1, p;
    unsigned long tmp;

    if ((x->value.decimal.sign) &&
    ((x->value.decimal.lo != 0) ||
     (x->value.decimal.mi != 0) ||
     (x->value.decimal.hi != 0))) {
    if ((y->value.decimal.sign) &&
        ((y->value.decimal.lo != 0) ||
         (y->value.decimal.mi != 0) ||
         (y->value.decimal.hi != 0)))
        order = -1;
    else
        return (-1);
    } else if ((y->value.decimal.sign) &&
           ((y->value.decimal.lo != 0) ||
        (y->value.decimal.mi != 0) ||
        (y->value.decimal.hi != 0))) {
        return (1);
    }
    if (x->value.decimal.frac == y->value.decimal.frac) {
    if (x->value.decimal.hi < y->value.decimal.hi)
        return (-order);
    if (x->value.decimal.hi < y->value.decimal.hi)
        return (order);
    if (x->value.decimal.mi < y->value.decimal.mi)
        return (-order);
    if (x->value.decimal.mi < y->value.decimal.mi)
        return (order);
        if (x->value.decimal.lo < y->value.decimal.lo)
            return (-order);
        if (x->value.decimal.lo > y->value.decimal.lo)
        return(order);
    return(0);
    }
    if (y->value.decimal.frac > x->value.decimal.frac) {
        swp = y;
        y = x;
        x = swp;
        order = -order;
    }
    p = powten[x->value.decimal.frac - y->value.decimal.frac];
    tmp = x->value.decimal.lo / p;
    if (tmp > y->value.decimal.lo)
        return (order);
    if (tmp < y->value.decimal.lo)
        return (-order);
    tmp = y->value.decimal.lo * p;
    if (x->value.decimal.lo < tmp)
        return (-order);
    if (x->value.decimal.lo == tmp)
        return (0);
    return (order);
}

/**
 * xmlSchemaCompareDurations:
 * @x:  a first duration value
 * @y:  a second duration value
 *
 * Compare 2 durations
 *
 * Returns -1 if x < y, 0 if x == y, 1 if x > y, 2 if x <> y, and -2 in
 * case of error
 */
static int
xmlSchemaCompareDurations(xmlSchemaValPtr x, xmlSchemaValPtr y)
{
    long carry, mon, day;
    double sec;
    int invert = 1;
    long xmon, xday, myear, minday, maxday;
    static const long dayRange [2][12] = {
        { 0, 28, 59, 89, 120, 150, 181, 212, 242, 273, 303, 334, },
        { 0, 31, 62, 92, 123, 153, 184, 215, 245, 276, 306, 337} };

    if ((x == NULL) || (y == NULL))
        return -2;

    /* months */
    mon = x->value.dur.mon - y->value.dur.mon;

    /* seconds */
    sec = x->value.dur.sec - y->value.dur.sec;
    carry = (long)sec / SECS_PER_DAY;
    sec -= (double)(carry * SECS_PER_DAY);

    /* days */
    day = x->value.dur.day - y->value.dur.day + carry;

    /* easy test */
    if (mon == 0) {
        if (day == 0)
            if (sec == 0.0)
                return 0;
            else if (sec < 0.0)
                return -1;
            else
                return 1;
        else if (day < 0)
            return -1;
        else
            return 1;
    }

    if (mon > 0) {
        if ((day >= 0) && (sec >= 0.0))
            return 1;
        else {
            xmon = mon;
            xday = -day;
        }
    } else if ((day <= 0) && (sec <= 0.0)) {
        return -1;
    } else {
    invert = -1;
        xmon = -mon;
        xday = day;
    }

    myear = xmon / 12;
    if (myear == 0) {
    minday = 0;
    maxday = 0;
    } else {
    maxday = 366 * ((myear + 3) / 4) +
             365 * ((myear - 1) % 4);
    minday = maxday - 1;
    }

    xmon = xmon % 12;
    minday += dayRange[0][xmon];
    maxday += dayRange[1][xmon];

    if ((maxday == minday) && (maxday == xday))
    return(0); /* can this really happen ? */
    if (maxday < xday)
        return(-invert);
    if (minday > xday)
        return(invert);

    /* indeterminate */
    return 2;
}

/*
 * macros for adding date/times and durations
 */
#define FQUOTIENT(a,b)                  (floor(((double)a/(double)b)))
#define MODULO(a,b)                     (a - FQUOTIENT(a,b) * b)
#define FQUOTIENT_RANGE(a,low,high)     (FQUOTIENT((a-low),(high-low)))
#define MODULO_RANGE(a,low,high)        ((MODULO((a-low),(high-low)))+low)

/**
 * _xmlSchemaDateAdd:
 * @dt: an #xmlSchemaValPtr
 * @dur: an #xmlSchemaValPtr of type #XS_DURATION
 *
 * Compute a new date/time from @dt and @dur. This function assumes @dt
 * is either #XML_SCHEMAS_DATETIME, #XML_SCHEMAS_DATE, #XML_SCHEMAS_GYEARMONTH,
 * or #XML_SCHEMAS_GYEAR.
 *
 * Returns date/time pointer or NULL.
 *
 * OOM: returns NULL and sets OOM flag
 */
static xmlSchemaValPtr
_xmlSchemaDateAdd (xmlSchemaValPtr dt, xmlSchemaValPtr dur)
{
    xmlSchemaValPtr ret;
    long carry, tempdays, temp;
    xmlSchemaValDatePtr r, d;
    xmlSchemaValDurationPtr u;

    if ((dt == NULL) || (dur == NULL))
        return NULL;

    ret = xmlSchemaNewValue(dt->type); // may set OOM flag
    if (ret == NULL)
        return NULL;

    r = &(ret->value.date);
    d = &(dt->value.date);
    u = &(dur->value.dur);

    /* normalization */
    if (d->mon == 0)
        d->mon = 1;

    /* normalize for time zone offset */
    u->sec -= (d->tzo * 60);
    d->tzo = 0;

    /* normalization */
    if (d->day == 0)
        d->day = 1;

    /* month */
    carry  = d->mon + u->mon;
    r->mon = (unsigned int) MODULO_RANGE(carry, 1, 13);
    carry  = (long) FQUOTIENT_RANGE(carry, 1, 13);

    /* year (may be modified later) */
    r->year = d->year + carry;
    if (r->year == 0) {
        if (d->year > 0)
            r->year--;
        else
            r->year++;
    }

    /* time zone */
    r->tzo     = d->tzo;
    r->tz_flag = d->tz_flag;

    /* seconds */
    r->sec = d->sec + u->sec;
    carry  = (long) FQUOTIENT((long)r->sec, 60);
    if (r->sec != 0.0) {
        r->sec = MODULO(r->sec, 60.0);
    }

    /* minute */
    carry += d->min;
    r->min = (unsigned int) MODULO(carry, 60);
    carry  = (long) FQUOTIENT(carry, 60);

    /* hours */
    carry  += d->hour;
    r->hour = (unsigned int) MODULO(carry, 24);
    carry   = (long)FQUOTIENT(carry, 24);

    /*
     * days
     * Note we use tempdays because the temporary values may need more
     * than 5 bits
     */
    if ((VALID_YEAR(r->year)) && (VALID_MONTH(r->mon)) &&
                  (d->day > MAX_DAYINMONTH(r->year, r->mon)))
        tempdays = MAX_DAYINMONTH(r->year, r->mon);
    else if (d->day < 1)
        tempdays = 1;
    else
        tempdays = d->day;

    tempdays += u->day + carry;

    while (1) {
        if (tempdays < 1) {
            long tmon = (long) MODULO_RANGE(r->mon-1, 1, 13);
            long tyr  = r->year + (long)FQUOTIENT_RANGE(r->mon-1, 1, 13);
            if (tyr == 0)
                tyr--;
            tempdays += MAX_DAYINMONTH(tyr, tmon);
            carry = -1;
        } else if (tempdays > (long) MAX_DAYINMONTH(r->year, r->mon)) {
            tempdays = tempdays - MAX_DAYINMONTH(r->year, r->mon);
            carry = 1;
        } else
            break;

        temp = r->mon + carry;
        r->mon = (unsigned int) MODULO_RANGE(temp, 1, 13);
        r->year = r->year + (unsigned int) FQUOTIENT_RANGE(temp, 1, 13);
        if (r->year == 0) {
            if (temp < 1)
                r->year--;
            else
                r->year++;
    }
    }

    r->day = tempdays;

    /*
     * adjust the date/time type to the date values
     */
    if (ret->type != XML_SCHEMAS_DATETIME) {
        if ((r->hour) || (r->min) || (r->sec))
            ret->type = XML_SCHEMAS_DATETIME;
        else if (ret->type != XML_SCHEMAS_DATE) {
            if ((r->mon != 1) && (r->day != 1))
                ret->type = XML_SCHEMAS_DATE;
            else if ((ret->type != XML_SCHEMAS_GYEARMONTH) && (r->mon != 1))
                ret->type = XML_SCHEMAS_GYEARMONTH;
        }
    }

    return ret;
}

/**
 * xmlSchemaDupVal:
 * @v: value to duplicate
 *
 * returns a duplicated value.
 *
 * OOM: returns NULL and sets OOM flag
 */
static xmlSchemaValPtr
xmlSchemaDupVal (xmlSchemaValPtr v)
{
    xmlSchemaValPtr ret = xmlSchemaNewValue(v->type); // may set OOM flag
    if (ret)
        memcpy(ret, v, sizeof(xmlSchemaVal));
    return ret;
}

/**
 * xmlSchemaDateNormalize:
 * @dt: an #xmlSchemaValPtr
 *
 * Normalize @dt to GMT time.
 *
 * OOM: returns NULL and sets OOM flag
 */
static xmlSchemaValPtr
xmlSchemaDateNormalize (xmlSchemaValPtr dt, double offset)
{
    xmlSchemaValPtr dur, ret;

    if (dt == NULL)
        return NULL;

    if (((dt->type != XML_SCHEMAS_TIME) &&
         (dt->type != XML_SCHEMAS_DATETIME)) || (dt->value.date.tzo == 0))
        return xmlSchemaDupVal(dt); // may set OOM flag

    dur = xmlSchemaNewValue(XML_SCHEMAS_DURATION); // may set OOM flag
    if (dur == NULL)
        return NULL;

    dur->value.date.sec -= offset;

    ret = _xmlSchemaDateAdd(dt, dur);
    if (ret == NULL)
        return NULL; // OOM flag was set too

    xmlSchemaFreeValue(dur);

    /* ret->value.date.tzo = 0; */
    return ret;
}

/**
 * _xmlSchemaDateCastYMToDays:
 * @dt: an #xmlSchemaValPtr
 *
 * Convert mon and year of @dt to total number of days. Take the
 * number of years since (or before) 1 AD and add the number of leap
 * years. This is a function  because negative
 * years must be handled a little differently and there is no zero year.
 *
 * Returns number of days.
 *
 * OOM: never
 */
static long
_xmlSchemaDateCastYMToDays (const xmlSchemaValPtr dt)
{
    long ret;

    if (dt->value.date.year < 0)
        ret = (dt->value.date.year * 365) +
              (((dt->value.date.year+1)/4)-((dt->value.date.year+1)/100)+
               ((dt->value.date.year+1)/400)) +
              DAY_IN_YEAR(0, dt->value.date.mon, dt->value.date.year);
    else
        ret = ((dt->value.date.year-1) * 365) +
              (((dt->value.date.year-1)/4)-((dt->value.date.year-1)/100)+
               ((dt->value.date.year-1)/400)) +
              DAY_IN_YEAR(0, dt->value.date.mon, dt->value.date.year);

    return ret;
}

/**
 * TIME_TO_NUMBER:
 * @dt:  an #xmlSchemaValPtr
 *
 * Calculates the number of seconds in the time portion of @dt.
 *
 * Returns seconds.
 */
#define TIME_TO_NUMBER(dt)                              \
    ((double)((dt->value.date.hour * SECS_PER_HOUR) +   \
              (dt->value.date.min * SECS_PER_MIN) + \
              (dt->value.date.tzo * SECS_PER_MIN)) +    \
               dt->value.date.sec)

/**
 * xmlSchemaCompareDates:
 * @x:  a first date/time value
 * @y:  a second date/time value
 *
 * Compare 2 date/times
 *
 * Returns -1 if x < y, 0 if x == y, 1 if x > y, 2 if x <> y, and -2 in
 * case of error
 *
 * OOM: iif returns -2 AND OOM flag is set
 */
static int
xmlSchemaCompareDates (xmlSchemaValPtr x, xmlSchemaValPtr y)
{
    unsigned char xmask, ymask, xor_mask, and_mask;
    xmlSchemaValPtr p1, p2, q1, q2;
    long p1d, p2d, q1d, q2d;

    if ((x == NULL) || (y == NULL))
        return -2;

    if (x->value.date.tz_flag) {

        if (!y->value.date.tz_flag) {
            p1 = xmlSchemaDateNormalize(x, 0);
            // TODO: OOM: Check p1!=NULL and then OOM flag if p1==NULL
            //            Otherwise next line has NullPointer exception
            p1d = _xmlSchemaDateCastYMToDays(p1) + p1->value.date.day;
            /* normalize y + 14:00 */
            q1 = xmlSchemaDateNormalize(y, (14 * SECS_PER_HOUR));

            q1d = _xmlSchemaDateCastYMToDays(q1) + q1->value.date.day;
            if (p1d < q1d) {
                xmlSchemaFreeValue(p1);
                xmlSchemaFreeValue(q1);
                        return -1;
            } else if (p1d == q1d) {
                 double sec;

                 sec = TIME_TO_NUMBER(p1) - TIME_TO_NUMBER(q1);
                 if (sec < 0.0) {
                    xmlSchemaFreeValue(p1);
                    xmlSchemaFreeValue(q1);
                    return -1;
                 } else {
                    int ret = 0;
                            /* normalize y - 14:00 */
                            q2 = xmlSchemaDateNormalize(y, -(14 * SECS_PER_HOUR));
                            q2d = _xmlSchemaDateCastYMToDays(q2) + q2->value.date.day;
                            if (p1d > q2d)
                                ret = 1;
                            else if (p1d == q2d) {
                                sec = TIME_TO_NUMBER(p1) - TIME_TO_NUMBER(q2);
                                if (sec > 0.0)
                                    ret = 1;
                                else
                                    ret = 2; /* indeterminate */
                    }
            xmlSchemaFreeValue(p1);
            xmlSchemaFreeValue(q1);
            xmlSchemaFreeValue(q2);
            if (ret != 0)
                return(ret);
                }
            } else {
        xmlSchemaFreeValue(p1);
        xmlSchemaFreeValue(q1);
        }
        }
    } else if (y->value.date.tz_flag) {
        q1 = xmlSchemaDateNormalize(y, 0);
        q1d = _xmlSchemaDateCastYMToDays(q1) + q1->value.date.day;

        /* normalize x - 14:00 */
        p1 = xmlSchemaDateNormalize(x, -(14 * SECS_PER_HOUR));
        p1d = _xmlSchemaDateCastYMToDays(p1) + p1->value.date.day;

        if (p1d < q1d) {
        xmlSchemaFreeValue(p1);
        xmlSchemaFreeValue(q1);
            return -1;
    } else if (p1d == q1d) {
            double sec;

            sec = TIME_TO_NUMBER(p1) - TIME_TO_NUMBER(q1);
            if (sec < 0.0) {
        xmlSchemaFreeValue(p1);
        xmlSchemaFreeValue(q1);
                return -1;
        } else {
            int ret = 0;
                /* normalize x + 14:00 */
                p2 = xmlSchemaDateNormalize(x, (14 * SECS_PER_HOUR));
                p2d = _xmlSchemaDateCastYMToDays(p2) + p2->value.date.day;

                if (p2d > q1d) {
                    ret = 1;
        } else if (p2d == q1d) {
                    sec = TIME_TO_NUMBER(p2) - TIME_TO_NUMBER(q1);
                    if (sec > 0.0)
                        ret = 1;
                    else
                        ret = 2; /* indeterminate */
                }
        xmlSchemaFreeValue(p1);
        xmlSchemaFreeValue(q1);
        xmlSchemaFreeValue(p2);
        if (ret != 0)
            return(ret);
            }
    } else {
        xmlSchemaFreeValue(p1);
        xmlSchemaFreeValue(q1);
        }
    }

    /*
     * if the same type then calculate the difference
     */
    if (x->type == y->type) {
        int ret = 0;
        q1 = xmlSchemaDateNormalize(y, 0);
        q1d = _xmlSchemaDateCastYMToDays(q1) + q1->value.date.day;

        p1 = xmlSchemaDateNormalize(x, 0);
        p1d = _xmlSchemaDateCastYMToDays(p1) + p1->value.date.day;

        if (p1d < q1d) {
            ret = -1;
    } else if (p1d > q1d) {
            ret = 1;
    } else {
            double sec;

            sec = TIME_TO_NUMBER(p1) - TIME_TO_NUMBER(q1);
            if (sec < 0.0)
                ret = -1;
            else if (sec > 0.0)
                ret = 1;

        }
    xmlSchemaFreeValue(p1);
    xmlSchemaFreeValue(q1);
        return(ret);
    }

    switch (x->type) {
        case XML_SCHEMAS_DATETIME:
            xmask = 0xf;
            break;
        case XML_SCHEMAS_DATE:
            xmask = 0x7;
            break;
        case XML_SCHEMAS_GYEAR:
            xmask = 0x1;
            break;
        case XML_SCHEMAS_GMONTH:
            xmask = 0x2;
            break;
        case XML_SCHEMAS_GDAY:
            xmask = 0x3;
            break;
        case XML_SCHEMAS_GYEARMONTH:
            xmask = 0x3;
            break;
        case XML_SCHEMAS_GMONTHDAY:
            xmask = 0x6;
            break;
        case XML_SCHEMAS_TIME:
            xmask = 0x8;
            break;
        default:
            xmask = 0;
            break;
    }

    switch (y->type) {
        case XML_SCHEMAS_DATETIME:
            ymask = 0xf;
            break;
        case XML_SCHEMAS_DATE:
            ymask = 0x7;
            break;
        case XML_SCHEMAS_GYEAR:
            ymask = 0x1;
            break;
        case XML_SCHEMAS_GMONTH:
            ymask = 0x2;
            break;
        case XML_SCHEMAS_GDAY:
            ymask = 0x3;
            break;
        case XML_SCHEMAS_GYEARMONTH:
            ymask = 0x3;
            break;
        case XML_SCHEMAS_GMONTHDAY:
            ymask = 0x6;
            break;
        case XML_SCHEMAS_TIME:
            ymask = 0x8;
            break;
        default:
            ymask = 0;
            break;
    }

    xor_mask = xmask ^ ymask;           /* mark type differences */
    and_mask = xmask & ymask;           /* mark field specification */

    /* year */
    if (xor_mask & 1)
        return 2; /* indeterminate */
    else if (and_mask & 1) {
        if (x->value.date.year < y->value.date.year)
            return -1;
        else if (x->value.date.year > y->value.date.year)
            return 1;
    }

    /* month */
    if (xor_mask & 2)
        return 2; /* indeterminate */
    else if (and_mask & 2) {
        if (x->value.date.mon < y->value.date.mon)
            return -1;
        else if (x->value.date.mon > y->value.date.mon)
            return 1;
    }

    /* day */
    if (xor_mask & 4)
        return 2; /* indeterminate */
    else if (and_mask & 4) {
        if (x->value.date.day < y->value.date.day)
            return -1;
        else if (x->value.date.day > y->value.date.day)
            return 1;
    }

    /* time */
    if (xor_mask & 8)
        return 2; /* indeterminate */
    else if (and_mask & 8) {
        if (x->value.date.hour < y->value.date.hour)
            return -1;
        else if (x->value.date.hour > y->value.date.hour)
            return 1;
        else if (x->value.date.min < y->value.date.min)
            return -1;
        else if (x->value.date.min > y->value.date.min)
            return 1;
        else if (x->value.date.sec < y->value.date.sec)
            return -1;
        else if (x->value.date.sec > y->value.date.sec)
            return 1;
    }

    return 0;
}

/**
 * xmlSchemaCompareNormStrings:
 * @x:  a first string value
 * @y:  a second string value
 *
 * Compare 2 string for their normalized values.
 *
 * Returns -1 if x < y, 0 if x == y, 1 if x > y, and -2 in
 * case of error
 *
 * OOM: never
 */
static int
xmlSchemaCompareNormStrings(xmlSchemaValPtr x, xmlSchemaValPtr y) {
    const xmlChar *utf1;
    const xmlChar *utf2;
    int tmp;

    if ((x == NULL) || (y == NULL))
        return(-2);
    utf1 = x->value.str;
    utf2 = y->value.str;

    while (IS_BLANK_CH(*utf1)) utf1++;
    while (IS_BLANK_CH(*utf2)) utf2++;
    while ((*utf1 != 0) && (*utf2 != 0)) {
    if (IS_BLANK_CH(*utf1)) {
        if (!IS_BLANK_CH(*utf2)) {
        tmp = *utf1 - *utf2;
        return(tmp);
        }
        while (IS_BLANK_CH(*utf1)) utf1++;
        while (IS_BLANK_CH(*utf2)) utf2++;
    } else {
        tmp = *utf1++ - *utf2++;
        if (tmp < 0)
        return(-1);
        if (tmp > 0)
        return(1);
    }
    }
    if (*utf1 != 0) {
    while (IS_BLANK_CH(*utf1)) utf1++;
    if (*utf1 != 0)
        return(1);
    }
    if (*utf2 != 0) {
    while (IS_BLANK_CH(*utf2)) utf2++;
    if (*utf2 != 0)
        return(-1);
    }
    return(0);
}

// XMLENGIINE: Added to replace unnesessary function calls
//             Later it will use internal "Number API" that hides TRIO part
#define xmlXPathIsNaN(val) trio_isnan(val)

/**
 * xmlSchemaCompareFloats:
 * @x:  a first float or double value
 * @y:  a second float or double value
 *
 * Compare 2 values
 *
 * Returns -1 if x < y, 0 if x == y, 1 if x > y, 2 if x <> y, and -2 in
 * case of error
 *
 * OOM: never
 */
static int
xmlSchemaCompareFloats(xmlSchemaValPtr x, xmlSchemaValPtr y) {
    double d1, d2;

    if ((x == NULL) || (y == NULL))
        return(-2);

    /*
     * Cast everything to doubles.
     */
    if (x->type == XML_SCHEMAS_DOUBLE)
        d1 = x->value.d;
    else if (x->type == XML_SCHEMAS_FLOAT)
        d1 = x->value.f;
    else
        return(-2);

    if (y->type == XML_SCHEMAS_DOUBLE)
        d2 = y->value.d;
    else if (y->type == XML_SCHEMAS_FLOAT)
        d2 = y->value.f;
    else
        return(-2);

    /*
     * Check for special cases.
     */
     // XMLENGINE: xmlXPathIsNaN is defined locally via trio_nan.h calls
    if (xmlXPathIsNaN(d1)) {
        if (xmlXPathIsNaN(d2))
            return(0);
        return(1);
    }
    if (xmlXPathIsNaN(d2))
        return(-1);
    if (d1 == xmlXPathPINF) {
        if (d2 == xmlXPathPINF)
            return(0);
        return(1);
    }
    if (d2 == xmlXPathPINF)
        return(-1);
    if (d1 == xmlXPathNINF) {
        if (d2 == xmlXPathNINF)
            return(0);
        return(-1);
    }
    if (d2 == xmlXPathNINF)
        return(1);

    /*
     * basic tests, the last one we should have equality, but
     * portability is more important than speed and handling
     * NaN or Inf in a portable way is always a challenge, so ...
     */
    if (d1 < d2)
        return(-1);
    if (d1 > d2)
        return(1);
    if (d1 == d2)
        return(0);
    return(2);
}

/**
 * xmlSchemaCompareValues:
 * @x:  a first value
 * @y:  a second value
 *
 * Compare 2 values
 *
 * Returns -1 if x < y, 0 if x == y, 1 if x > y, 2 if x <> y, and -2 in
 * case of error
 */
int
xmlSchemaCompareValues(xmlSchemaValPtr x, xmlSchemaValPtr y) {
    if ((x == NULL) || (y == NULL))
        return(-2);
    // TODO: OPTIMIZE: prefetch x's and y's  ->type values
    switch (x->type) {
        case XML_SCHEMAS_UNKNOWN:
            return(-2);
        case XML_SCHEMAS_INTEGER:
        case XML_SCHEMAS_NPINTEGER:
        case XML_SCHEMAS_NINTEGER:
        case XML_SCHEMAS_NNINTEGER:
        case XML_SCHEMAS_PINTEGER:
        case XML_SCHEMAS_INT:
        case XML_SCHEMAS_UINT:
        case XML_SCHEMAS_LONG:
        case XML_SCHEMAS_ULONG:
        case XML_SCHEMAS_SHORT:
        case XML_SCHEMAS_USHORT:
        case XML_SCHEMAS_BYTE:
        case XML_SCHEMAS_UBYTE:
        case XML_SCHEMAS_DECIMAL:
            if (y->type == x->type)
                return(xmlSchemaCompareDecimals(x, y));
            if ((y->type == XML_SCHEMAS_DECIMAL) ||
                (y->type == XML_SCHEMAS_INTEGER) ||
                (y->type == XML_SCHEMAS_NPINTEGER) ||
                (y->type == XML_SCHEMAS_NINTEGER) ||
                (y->type == XML_SCHEMAS_NNINTEGER) ||
                (y->type == XML_SCHEMAS_PINTEGER) ||
                (y->type == XML_SCHEMAS_INT) ||
                (y->type == XML_SCHEMAS_UINT) ||
                (y->type == XML_SCHEMAS_LONG) ||
                (y->type == XML_SCHEMAS_ULONG) ||
                (y->type == XML_SCHEMAS_SHORT) ||
                (y->type == XML_SCHEMAS_USHORT) ||
                (y->type == XML_SCHEMAS_BYTE) ||
                (y->type == XML_SCHEMAS_UBYTE))
            {
                return(xmlSchemaCompareDecimals(x, y));
            }
            return(-2);
        case XML_SCHEMAS_DURATION:
            if (y->type == XML_SCHEMAS_DURATION)
                    return(xmlSchemaCompareDurations(x, y));
            return(-2);
        case XML_SCHEMAS_TIME:
        case XML_SCHEMAS_GDAY:
        case XML_SCHEMAS_GMONTH:
        case XML_SCHEMAS_GMONTHDAY:
        case XML_SCHEMAS_GYEAR:
        case XML_SCHEMAS_GYEARMONTH:
        case XML_SCHEMAS_DATE:
        case XML_SCHEMAS_DATETIME:
            if ((y->type == XML_SCHEMAS_DATETIME)  ||
                (y->type == XML_SCHEMAS_TIME)      ||
                (y->type == XML_SCHEMAS_GDAY)      ||
                (y->type == XML_SCHEMAS_GMONTH)    ||
                (y->type == XML_SCHEMAS_GMONTHDAY) ||
                (y->type == XML_SCHEMAS_GYEAR)     ||
                (y->type == XML_SCHEMAS_DATE)      ||
                (y->type == XML_SCHEMAS_GYEARMONTH))
            {
                return (xmlSchemaCompareDates(x, y)); // This may end up with OOM
            }
            return (-2);
        case XML_SCHEMAS_NORMSTRING:
        case XML_SCHEMAS_TOKEN:
        case XML_SCHEMAS_LANGUAGE:
        case XML_SCHEMAS_NMTOKEN:
        case XML_SCHEMAS_NAME:
        case XML_SCHEMAS_NCNAME:
        case XML_SCHEMAS_ID:
        case XML_SCHEMAS_IDREF:
        case XML_SCHEMAS_ENTITY:
        case XML_SCHEMAS_NOTATION:
        case XML_SCHEMAS_ANYURI:
            if ((y->type == XML_SCHEMAS_NORMSTRING) ||
                (y->type == XML_SCHEMAS_TOKEN) ||
                (y->type == XML_SCHEMAS_LANGUAGE) ||
                (y->type == XML_SCHEMAS_NMTOKEN) ||
                (y->type == XML_SCHEMAS_NAME) ||
                (y->type == XML_SCHEMAS_QNAME) ||
                (y->type == XML_SCHEMAS_NCNAME) ||
                (y->type == XML_SCHEMAS_ID) ||
                (y->type == XML_SCHEMAS_IDREF) ||
                (y->type == XML_SCHEMAS_ENTITY) ||
                (y->type == XML_SCHEMAS_NOTATION) ||
                (y->type == XML_SCHEMAS_ANYURI))
             {
                return (xmlSchemaCompareNormStrings(x, y));
             }
            return (-2);
        case XML_SCHEMAS_QNAME:
            if (y->type == XML_SCHEMAS_QNAME) {
                if ((xmlStrEqual(x->value.qname.name, y->value.qname.name)) &&
                    (xmlStrEqual(x->value.qname.uri, y->value.qname.uri)))
                {
                    return(0);
                }
                return(2);
            }
            return (-2);
        case XML_SCHEMAS_FLOAT:
        case XML_SCHEMAS_DOUBLE:
            if ((y->type == XML_SCHEMAS_FLOAT) ||
                (y->type == XML_SCHEMAS_DOUBLE))
            {
                return (xmlSchemaCompareFloats(x, y));
            }
            return (-2);
        case XML_SCHEMAS_BOOLEAN:
            if (y->type == XML_SCHEMAS_BOOLEAN) {
                if (x->value.b == y->value.b)
                    return(0);
                if (x->value.b == 0)
                    return(-1);
                return(1);
            }
            return (-2);
        case XML_SCHEMAS_HEXBINARY:
            if (y->type == XML_SCHEMAS_HEXBINARY)
            {
                if (x->value.hex.total == y->value.hex.total)
                {
                    int ret = xmlStrcmp(x->value.hex.str, y->value.hex.str);
                    if (ret > 0)
                        return(1);
                    else if (ret == 0)
                        return(0);
                }
                else
                {
                    if (x->value.hex.total > y->value.hex.total)
                        return(1);
                }
                return(-1);
            }
            return (-2);
        case XML_SCHEMAS_BASE64BINARY:
            if (y->type == XML_SCHEMAS_BASE64BINARY)
            {
                if (x->value.base64.total == y->value.base64.total)
                {
                    int ret = xmlStrcmp(x->value.base64.str, y->value.base64.str);
                    if (ret > 0)
                        return(1);
                    else if (ret == 0)
                        return(0);
                }
                else if (x->value.base64.total > y->value.base64.total)
                    return(1);
                else
                    return(-1);
            }
            return (-2);
        case XML_SCHEMAS_STRING:
        case XML_SCHEMAS_IDREFS:
        case XML_SCHEMAS_ENTITIES:
        case XML_SCHEMAS_NMTOKENS:
            _TODO_
        break;
    }
    return -2;
}

/**
 * xmlSchemaNormLen:
 * @value:  a string
 *
 * Computes the UTF8 length of the normalized value of the string
 *
 * Returns the length or -1 in case of error.
 */
static int
xmlSchemaNormLen(const xmlChar *value) {
    const xmlChar *utf;
    int ret = 0;

    if (value == NULL)
    return(-1);
    utf = value;
    while (IS_BLANK_CH(*utf)) utf++;
    while (*utf != 0) {
    if (utf[0] & 0x80) {
        if ((utf[1] & 0xc0) != 0x80)
        return(-1);
        if ((utf[0] & 0xe0) == 0xe0) {
        if ((utf[2] & 0xc0) != 0x80)
            return(-1);
        if ((utf[0] & 0xf0) == 0xf0) {
            if ((utf[0] & 0xf8) != 0xf0 || (utf[3] & 0xc0) != 0x80)
            return(-1);
            utf += 4;
        } else {
            utf += 3;
        }
        } else {
        utf += 2;
        }
    } else if (IS_BLANK_CH(*utf)) {
        while (IS_BLANK_CH(*utf)) utf++;
        if (*utf == 0)
        break;
    } else {
        utf++;
    }
    ret++;
    }
    return(ret);
}

/**
 * xmlSchemaValidateFacet:
 * @base:  the base type
 * @facet:  the facet to check
 * @value:  the lexical repr of the value to validate
 * @val:  the precomputed value
 *
 * Check a value against a facet condition
 *
 * Returns 0 if the element is schemas valid, a positive error code
 *     number otherwise and -1 in case of internal or API error.
 */
int
xmlSchemaValidateFacet(xmlSchemaTypePtr base ATTRIBUTE_UNUSED,
                   xmlSchemaFacetPtr facet,
                   const xmlChar *value, xmlSchemaValPtr val)
{
    int ret;

    switch (facet->type) {
    case XML_SCHEMA_FACET_PATTERN:
#ifdef LIBXML_REGEXP_ENABLED
        ret = xmlRegexpExec(facet->regexp, value);
        if (ret == 1)
        return(0);
        if (ret == 0) {
        /* TODO error code */
        return(1);
        }
        return(ret);
#else
        return -1; // Regexp is not compiled in
#endif
    case XML_SCHEMA_FACET_MAXEXCLUSIVE:
        ret = xmlSchemaCompareValues(val, facet->val);
        if (ret == -2) {
        /* TODO error code */
        return(-1);
        }
        if (ret == -1)
        return(0);
        /* error code */
        return(1);
    case XML_SCHEMA_FACET_MAXINCLUSIVE:
        ret = xmlSchemaCompareValues(val, facet->val);
        if (ret == -2) {
        /* TODO error code */
        return(-1);
        }
        if ((ret == -1) || (ret == 0))
        return(0);
        /* error code */
        return(1);
    case XML_SCHEMA_FACET_MINEXCLUSIVE:
        ret = xmlSchemaCompareValues(val, facet->val);
        if (ret == -2) {
        /* TODO error code */
        return(-1);
        }
        if (ret == 1)
        return(0);
        /* error code */
        return(1);
    case XML_SCHEMA_FACET_MININCLUSIVE:
        ret = xmlSchemaCompareValues(val, facet->val);
        if (ret == -2) {
        /* TODO error code */
        return(-1);
        }
        if ((ret == 1) || (ret == 0))
        return(0);
        /* error code */
        return(1);
    case XML_SCHEMA_FACET_WHITESPACE:
        /* TODO whitespaces */
        return(0);
    case  XML_SCHEMA_FACET_ENUMERATION:
        if ((facet->value != NULL) &&
        (xmlStrEqual(facet->value, value)))
        return(0);
        return(1);
    case XML_SCHEMA_FACET_LENGTH:
    case XML_SCHEMA_FACET_MAXLENGTH:
    case XML_SCHEMA_FACET_MINLENGTH: {
        unsigned int len = 0;

        if ((facet->val == NULL) ||
        ((facet->val->type != XML_SCHEMAS_DECIMAL) &&
         (facet->val->type != XML_SCHEMAS_NNINTEGER)) ||
        (facet->val->value.decimal.frac != 0)) {
        return(-1);
        }
        if ((val != NULL) && (val->type == XML_SCHEMAS_HEXBINARY))
        len = val->value.hex.total;
        else if ((val != NULL) && (val->type == XML_SCHEMAS_BASE64BINARY))
        len = val->value.base64.total;
        else {
            switch (base->flags) {
                case XML_SCHEMAS_IDREF:
            case XML_SCHEMAS_NORMSTRING:
            case XML_SCHEMAS_TOKEN:
            case XML_SCHEMAS_LANGUAGE:
            case XML_SCHEMAS_NMTOKEN:
            case XML_SCHEMAS_NAME:
            case XML_SCHEMAS_NCNAME:
            case XML_SCHEMAS_ID:
                len = xmlSchemaNormLen(value);
                break;
            case XML_SCHEMAS_STRING:
                if (value != NULL)
                len = xmlUTF8Strlen(value);
                break;
            default:
                _TODO_
            }
        }
        if (facet->type == XML_SCHEMA_FACET_LENGTH) {
        if (len != facet->val->value.decimal.lo)
            return(1);
        } else if (facet->type == XML_SCHEMA_FACET_MINLENGTH) {
        if (len < facet->val->value.decimal.lo)
            return(1);
        } else {
        if (len > facet->val->value.decimal.lo)
            return(1);
        }
        break;
    }
    case XML_SCHEMA_FACET_TOTALDIGITS:
    case XML_SCHEMA_FACET_FRACTIONDIGITS:

        if ((facet->val == NULL) ||
        ((facet->val->type != XML_SCHEMAS_DECIMAL) &&
         (facet->val->type != XML_SCHEMAS_NNINTEGER)) ||
        (facet->val->value.decimal.frac != 0)) {
        return(-1);
        }
        if ((val == NULL) ||
        ((val->type != XML_SCHEMAS_DECIMAL) &&
         (val->type != XML_SCHEMAS_INTEGER) &&
         (val->type != XML_SCHEMAS_NPINTEGER) &&
         (val->type != XML_SCHEMAS_NINTEGER) &&
         (val->type != XML_SCHEMAS_NNINTEGER) &&
         (val->type != XML_SCHEMAS_PINTEGER) &&
         (val->type != XML_SCHEMAS_INT) &&
         (val->type != XML_SCHEMAS_UINT) &&
         (val->type != XML_SCHEMAS_LONG) &&
         (val->type != XML_SCHEMAS_ULONG) &&
         (val->type != XML_SCHEMAS_SHORT) &&
         (val->type != XML_SCHEMAS_USHORT) &&
         (val->type != XML_SCHEMAS_BYTE) &&
         (val->type != XML_SCHEMAS_UBYTE))) {
        return(-1);
        }
        if (facet->type == XML_SCHEMA_FACET_TOTALDIGITS) {
            if (val->value.decimal.total > facet->val->value.decimal.lo)
                return(1);

        } else if (facet->type == XML_SCHEMA_FACET_FRACTIONDIGITS) {
            if (val->value.decimal.frac > facet->val->value.decimal.lo)
            return(1);
        }
        break;
    default:
        _TODO_
    }
    return(0);

}

#endif /* defined(LIBXML_SCHEMAS_ENABLED) || defined(XMLENGINE_XMLSCHEMA_DATATYPES)  */
