diff --git a/Demos/ColladaDemo/Jamfile b/Demos/ColladaDemo/Jamfile index 972b07425..b8d1c3cf2 100644 --- a/Demos/ColladaDemo/Jamfile +++ b/Demos/ColladaDemo/Jamfile @@ -1,4 +1,4 @@ SubDir TOP Demos ColladaDemo ; -BulletDemo ColladaDemo : [ Wildcard *.h *.cpp ] ; +ExtraDemo ColladaDemo : [ Wildcard *.h *.cpp ] ; MsvcIncDirs ColladaDemo : "../../Extras/FCollada" ; diff --git a/Demos/Jamfile b/Demos/Jamfile index 0d1544d90..44292f977 100644 --- a/Demos/Jamfile +++ b/Demos/Jamfile @@ -24,7 +24,7 @@ if $(GLUT.AVAILABLE) = "yes" rule ExtraDemo { Application $(<) : $(>) : noinstall console nomanifest ; - LinkWith $(<) : collada-dom libxml convexdecomposition bullet bulletopenglsupport bulletphysicsinterfacecommon bulletccdphysics bulletmath ; + LinkWith $(<) : collada-dom libxml convexdecomposition bullet bulletopenglsupport bulletphysicsinterfacecommon bulletccdphysics bulletmath ; CFlags $(<) : [ FIncludes $(TOP)/Demos/OpenGL ] [ FIncludes $(TOP)/Extras/PhysicsInterface/CcdPhysics ] diff --git a/Extras/LibXML/config.h b/Extras/LibXML/config.h index 6fff57adf..2febfcde3 100644 --- a/Extras/LibXML/config.h +++ b/Extras/LibXML/config.h @@ -1,113 +1,296 @@ -#ifndef __LIBXML_WIN32_CONFIG__ -#define __LIBXML_WIN32_CONFIG__ +/* config.h. Generated by configure. */ +/* config.h.in. Generated from configure.in by autoheader. */ +#define PACKAGE "libxml2" +#define VERSION "2.6.26" +#define HAVE_LIBZ +/* #undef HAVE_LIBM */ +#define HAVE_ISINF +#define HAVE_ISNAN +/* #undef HAVE_LIBHISTORY */ +/* #undef HAVE_LIBREADLINE */ +/* #define HAVE_LIBPTHREAD */ +/* #define HAVE_PTHREAD_H */ -#define HAVE_CTYPE_H -#define HAVE_STDARG_H -#define HAVE_MALLOC_H -#define HAVE_ERRNO_H +/* Define if IPV6 support is there */ +#define SUPPORT_IP6 -#ifdef _WIN32_WCE -#undef HAVE_ERRNO_H -#include -#include "wincecompat.h" -#elif defined(WIN32) -#define HAVE_SYS_STAT_H -#define HAVE__STAT -#define HAVE_STAT -#define HAVE_STDLIB_H -#define HAVE_TIME_H -#define HAVE_FCNTL_H -#include -#include -#endif +/* Define if getaddrinfo is there */ +#define HAVE_GETADDRINFO -#include +/* Define to 1 if you have the header file. */ +#define HAVE_ANSIDECL_H 1 -#ifdef NEED_SOCKETS -#include -#endif +/* Define to 1 if you have the header file. */ +#define HAVE_ARPA_INET_H 1 -#define HAVE_ISINF -#define HAVE_ISNAN -#include -#ifdef WIN32 -#if defined(_MSC_VER) || defined(__BORLANDC__) -/* MS C-runtime has functions which can be used in order to determine if - a given floating-point variable contains NaN, (+-)INF. These are - preferred, because floating-point technology is considered propriatary - by MS and we can assume that their functions know more about their - oddities than we do. */ -#include -/* Bjorn Reese figured a quite nice construct for isinf() using the _fpclass - function. */ -#ifndef isinf -#define isinf(d) ((_fpclass(d) == _FPCLASS_PINF) ? 1 \ - : ((_fpclass(d) == _FPCLASS_NINF) ? -1 : 0)) -#endif -/* _isnan(x) returns nonzero if (x == NaN) and zero otherwise. */ -#ifndef isnan -#define isnan(d) (_isnan(d)) -#endif -#else /* _MSC_VER */ -#ifndef isinf -static int isinf (double d) { - int expon = 0; - double val = frexp (d, &expon); - if (expon == 1025) { - if (val == 0.5) { - return 1; - } else if (val == -0.5) { - return -1; - } else { - return 0; - } - } else { - return 0; - } -} -#endif -#ifndef isnan -static int isnan (double d) { - int expon = 0; - double val = frexp (d, &expon); - if (expon == 1025) { - if (val == 0.5) { - return 0; - } else if (val == -0.5) { - return 0; - } else { - return 1; - } - } else { - return 0; - } -} -#endif -#endif /* _MSC_VER */ -#endif // WIN32 +/* Define to 1 if you have the header file. */ +#define HAVE_ARPA_NAMESER_H 1 -#if defined(_MSC_VER) || defined(__MINGW32__) -#define mkdir(p,m) _mkdir(p) -#define snprintf _snprintf -#define vsnprintf(b,c,f,a) _vsnprintf(b,c,f,a) -#endif +/* Whether struct sockaddr::__ss_family exists */ +/* #undef HAVE_BROKEN_SS_FAMILY */ -/* Threading API to use should be specified here for compatibility reasons. - This is however best specified on the compiler's command-line. */ -#if defined(LIBXML_THREAD_ENABLED) -#if !defined(HAVE_PTHREAD_H) && !defined(HAVE_WIN32_THREADS) -#define HAVE_WIN32_THREADS -#endif -#endif +/* Define to 1 if you have the `class' function. */ +/* #undef HAVE_CLASS */ -/* Some third-party libraries far from our control assume the following - is defined, which it is not if we don't include windows.h. */ -#if !defined(FALSE) -#define FALSE 0 -#endif -#if !defined(TRUE) -#define TRUE (!(FALSE)) -#endif +/* Define to 1 if you have the header file. */ +#define HAVE_CTYPE_H 1 -#endif /* __LIBXML_WIN32_CONFIG__ */ +/* Define to 1 if you have the header file. */ +#define HAVE_DIRENT_H 1 +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Have dlopen based dso */ +#define HAVE_DLOPEN + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DL_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `finite' function. */ +#define HAVE_FINITE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FLOAT_H 1 + +/* Define to 1 if you have the `fpclass' function. */ +/* #undef HAVE_FPCLASS */ + +/* Define to 1 if you have the `fprintf' function. */ +#define HAVE_FPRINTF 1 + +/* Define to 1 if you have the `fp_class' function. */ +/* #undef HAVE_FP_CLASS */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_FP_CLASS_H */ + +/* Define to 1 if you have the `ftime' function. */ +#define HAVE_FTIME 1 + +/* Define if getaddrinfo is there */ +#define HAVE_GETADDRINFO + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_IEEEFP_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define if isinf is there */ +#define HAVE_ISINF + +/* Define if isnan is there */ +#define HAVE_ISNAN + +/* Define to 1 if you have the `isnand' function. */ +/* #undef HAVE_ISNAND */ + +/* Define if history library is there (-lhistory) */ +/* #undef HAVE_LIBHISTORY */ + +/* Define if pthread library is there (-lpthread) */ +/* #define HAVE_LIBPTHREAD */ + +/* Define if readline library is there (-lreadline) */ +/* #undef HAVE_LIBREADLINE */ + +/* Have compression library */ +/* #define HAVE_LIBZ */ + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the `localtime' function. */ +#define HAVE_LOCALTIME 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MALLOC_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MATH_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NAN_H */ + +/* Define to 1 if you have the header file, and it defines `DIR'. */ +/* #undef HAVE_NDIR_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_NETDB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the `printf' function. */ +#define HAVE_PRINTF 1 + +/* Define if is there */ +/* #define HAVE_PTHREAD_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_RESOLV_H 1 + +/* Have shl_load based dso */ +/* #undef HAVE_SHLLOAD */ + +/* Define to 1 if you have the `signal' function. */ +#define HAVE_SIGNAL 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SIGNAL_H 1 + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Define to 1 if you have the `sprintf' function. */ +#define HAVE_SPRINTF 1 + +/* Define to 1 if you have the `sscanf' function. */ +#define HAVE_SSCANF 1 + +/* Define to 1 if you have the `stat' function. */ +#define HAVE_STAT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDARG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the `strftime' function. */ +#define HAVE_STRFTIME 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strndup' function. */ +#define HAVE_STRNDUP 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_DIR_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_MMAN_H 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIMEB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Whether va_copy() is available */ +#define HAVE_VA_COPY 1 + +/* Define to 1 if you have the `vfprintf' function. */ +#define HAVE_VFPRINTF 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* Define to 1 if you have the `vsprintf' function. */ +#define HAVE_VSPRINTF 1 + +/* Define to 1 if you have the header file. */ +/* #define HAVE_ZLIB_H 1 */ + +/* Define to 1 if you have the `_stat' function. */ +/* #undef HAVE__STAT */ + +/* Whether __va_copy() is available */ +/* #undef HAVE___VA_COPY */ + +/* Name of package */ +#define PACKAGE "libxml2" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define to 1 if the C compiler supports function prototypes. */ +#define PROTOTYPES 1 + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Support for IPv6 */ +#define SUPPORT_IP6 + +/* Version number of package */ +#define VERSION "2.6.26" + +/* Determine what socket length (socklen_t) data type is */ +#define XML_SOCKLEN_T socklen_t + +/* Using the Win32 Socket implementation */ +/* #undef _WINSOCKAPI_ */ + +/* Define like PROTOTYPES; this can be used by system headers. */ +#define __PROTOTYPES 1 + +/* Win32 Std C name mangling work-around */ +/* #undef snprintf */ + +/* ss_family is not defined here, use __ss_family instead */ +/* #undef ss_family */ + +/* Win32 Std C name mangling work-around */ +/* #undef vsnprintf */ diff --git a/Extras/LibXML/config2.h b/Extras/LibXML/config2.h new file mode 100644 index 000000000..2febfcde3 --- /dev/null +++ b/Extras/LibXML/config2.h @@ -0,0 +1,296 @@ +/* config.h. Generated by configure. */ +/* config.h.in. Generated from configure.in by autoheader. */ +#define PACKAGE "libxml2" +#define VERSION "2.6.26" +#define HAVE_LIBZ +/* #undef HAVE_LIBM */ +#define HAVE_ISINF +#define HAVE_ISNAN +/* #undef HAVE_LIBHISTORY */ +/* #undef HAVE_LIBREADLINE */ +/* #define HAVE_LIBPTHREAD */ +/* #define HAVE_PTHREAD_H */ + +/* Define if IPV6 support is there */ +#define SUPPORT_IP6 + +/* Define if getaddrinfo is there */ +#define HAVE_GETADDRINFO + +/* Define to 1 if you have the header file. */ +#define HAVE_ANSIDECL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ARPA_INET_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ARPA_NAMESER_H 1 + +/* Whether struct sockaddr::__ss_family exists */ +/* #undef HAVE_BROKEN_SS_FAMILY */ + +/* Define to 1 if you have the `class' function. */ +/* #undef HAVE_CLASS */ + +/* Define to 1 if you have the header file. */ +#define HAVE_CTYPE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DIRENT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Have dlopen based dso */ +#define HAVE_DLOPEN + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DL_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `finite' function. */ +#define HAVE_FINITE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FLOAT_H 1 + +/* Define to 1 if you have the `fpclass' function. */ +/* #undef HAVE_FPCLASS */ + +/* Define to 1 if you have the `fprintf' function. */ +#define HAVE_FPRINTF 1 + +/* Define to 1 if you have the `fp_class' function. */ +/* #undef HAVE_FP_CLASS */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_FP_CLASS_H */ + +/* Define to 1 if you have the `ftime' function. */ +#define HAVE_FTIME 1 + +/* Define if getaddrinfo is there */ +#define HAVE_GETADDRINFO + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_IEEEFP_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define if isinf is there */ +#define HAVE_ISINF + +/* Define if isnan is there */ +#define HAVE_ISNAN + +/* Define to 1 if you have the `isnand' function. */ +/* #undef HAVE_ISNAND */ + +/* Define if history library is there (-lhistory) */ +/* #undef HAVE_LIBHISTORY */ + +/* Define if pthread library is there (-lpthread) */ +/* #define HAVE_LIBPTHREAD */ + +/* Define if readline library is there (-lreadline) */ +/* #undef HAVE_LIBREADLINE */ + +/* Have compression library */ +/* #define HAVE_LIBZ */ + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the `localtime' function. */ +#define HAVE_LOCALTIME 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MALLOC_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MATH_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NAN_H */ + +/* Define to 1 if you have the header file, and it defines `DIR'. */ +/* #undef HAVE_NDIR_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_NETDB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the `printf' function. */ +#define HAVE_PRINTF 1 + +/* Define if is there */ +/* #define HAVE_PTHREAD_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_RESOLV_H 1 + +/* Have shl_load based dso */ +/* #undef HAVE_SHLLOAD */ + +/* Define to 1 if you have the `signal' function. */ +#define HAVE_SIGNAL 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SIGNAL_H 1 + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Define to 1 if you have the `sprintf' function. */ +#define HAVE_SPRINTF 1 + +/* Define to 1 if you have the `sscanf' function. */ +#define HAVE_SSCANF 1 + +/* Define to 1 if you have the `stat' function. */ +#define HAVE_STAT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDARG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the `strftime' function. */ +#define HAVE_STRFTIME 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strndup' function. */ +#define HAVE_STRNDUP 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_DIR_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_MMAN_H 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIMEB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Whether va_copy() is available */ +#define HAVE_VA_COPY 1 + +/* Define to 1 if you have the `vfprintf' function. */ +#define HAVE_VFPRINTF 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* Define to 1 if you have the `vsprintf' function. */ +#define HAVE_VSPRINTF 1 + +/* Define to 1 if you have the header file. */ +/* #define HAVE_ZLIB_H 1 */ + +/* Define to 1 if you have the `_stat' function. */ +/* #undef HAVE__STAT */ + +/* Whether __va_copy() is available */ +/* #undef HAVE___VA_COPY */ + +/* Name of package */ +#define PACKAGE "libxml2" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define to 1 if the C compiler supports function prototypes. */ +#define PROTOTYPES 1 + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Support for IPv6 */ +#define SUPPORT_IP6 + +/* Version number of package */ +#define VERSION "2.6.26" + +/* Determine what socket length (socklen_t) data type is */ +#define XML_SOCKLEN_T socklen_t + +/* Using the Win32 Socket implementation */ +/* #undef _WINSOCKAPI_ */ + +/* Define like PROTOTYPES; this can be used by system headers. */ +#define __PROTOTYPES 1 + +/* Win32 Std C name mangling work-around */ +/* #undef snprintf */ + +/* ss_family is not defined here, use __ss_family instead */ +/* #undef ss_family */ + +/* Win32 Std C name mangling work-around */ +/* #undef vsnprintf */ diff --git a/Extras/LibXML/configwin32.h b/Extras/LibXML/configwin32.h new file mode 100644 index 000000000..6fff57adf --- /dev/null +++ b/Extras/LibXML/configwin32.h @@ -0,0 +1,113 @@ +#ifndef __LIBXML_WIN32_CONFIG__ +#define __LIBXML_WIN32_CONFIG__ + +#define HAVE_CTYPE_H +#define HAVE_STDARG_H +#define HAVE_MALLOC_H +#define HAVE_ERRNO_H + +#ifdef _WIN32_WCE +#undef HAVE_ERRNO_H +#include +#include "wincecompat.h" +#elif defined(WIN32) +#define HAVE_SYS_STAT_H +#define HAVE__STAT +#define HAVE_STAT +#define HAVE_STDLIB_H +#define HAVE_TIME_H +#define HAVE_FCNTL_H +#include +#include +#endif + +#include + +#ifdef NEED_SOCKETS +#include +#endif + +#define HAVE_ISINF +#define HAVE_ISNAN +#include +#ifdef WIN32 +#if defined(_MSC_VER) || defined(__BORLANDC__) +/* MS C-runtime has functions which can be used in order to determine if + a given floating-point variable contains NaN, (+-)INF. These are + preferred, because floating-point technology is considered propriatary + by MS and we can assume that their functions know more about their + oddities than we do. */ +#include +/* Bjorn Reese figured a quite nice construct for isinf() using the _fpclass + function. */ +#ifndef isinf +#define isinf(d) ((_fpclass(d) == _FPCLASS_PINF) ? 1 \ + : ((_fpclass(d) == _FPCLASS_NINF) ? -1 : 0)) +#endif +/* _isnan(x) returns nonzero if (x == NaN) and zero otherwise. */ +#ifndef isnan +#define isnan(d) (_isnan(d)) +#endif +#else /* _MSC_VER */ +#ifndef isinf +static int isinf (double d) { + int expon = 0; + double val = frexp (d, &expon); + if (expon == 1025) { + if (val == 0.5) { + return 1; + } else if (val == -0.5) { + return -1; + } else { + return 0; + } + } else { + return 0; + } +} +#endif +#ifndef isnan +static int isnan (double d) { + int expon = 0; + double val = frexp (d, &expon); + if (expon == 1025) { + if (val == 0.5) { + return 0; + } else if (val == -0.5) { + return 0; + } else { + return 1; + } + } else { + return 0; + } +} +#endif +#endif /* _MSC_VER */ +#endif // WIN32 + +#if defined(_MSC_VER) || defined(__MINGW32__) +#define mkdir(p,m) _mkdir(p) +#define snprintf _snprintf +#define vsnprintf(b,c,f,a) _vsnprintf(b,c,f,a) +#endif + +/* Threading API to use should be specified here for compatibility reasons. + This is however best specified on the compiler's command-line. */ +#if defined(LIBXML_THREAD_ENABLED) +#if !defined(HAVE_PTHREAD_H) && !defined(HAVE_WIN32_THREADS) +#define HAVE_WIN32_THREADS +#endif +#endif + +/* Some third-party libraries far from our control assume the following + is defined, which it is not if we don't include windows.h. */ +#if !defined(FALSE) +#define FALSE 0 +#endif +#if !defined(TRUE) +#define TRUE (!(FALSE)) +#endif + +#endif /* __LIBXML_WIN32_CONFIG__ */ + diff --git a/Extras/LibXML/include/libxml/Makefile b/Extras/LibXML/include/libxml/Makefile new file mode 100644 index 000000000..a154f7fdd --- /dev/null +++ b/Extras/LibXML/include/libxml/Makefile @@ -0,0 +1,559 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# include/libxml/Makefile. Generated from Makefile.in by configure. + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + + +srcdir = . +top_srcdir = ../.. + +pkgdatadir = $(datadir)/libxml2 +pkglibdir = $(libdir)/libxml2 +pkgincludedir = $(includedir)/libxml2 +top_builddir = ../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = /usr/bin/install -c +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = i686-redhat-linux-gnu +host_triplet = i686-redhat-linux-gnu +subdir = include/libxml +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(srcdir)/xmlversion.h.in $(xmlinc_HEADERS) +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = xmlversion.h +SOURCES = +DIST_SOURCES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; +am__installdirs = "$(DESTDIR)$(xmlincdir)" +xmlincHEADERS_INSTALL = $(INSTALL_HEADER) +HEADERS = $(xmlinc_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = ${SHELL} /home/erwin/develop/libxml2-2.6.26/missing --run aclocal-1.9 +AMDEP_FALSE = # +AMDEP_TRUE = +AMTAR = ${SHELL} /home/erwin/develop/libxml2-2.6.26/missing --run tar +AR = ar +AS = as +AUTOCONF = ${SHELL} /home/erwin/develop/libxml2-2.6.26/missing --run autoconf +AUTOHEADER = ${SHELL} /home/erwin/develop/libxml2-2.6.26/missing --run autoheader +AUTOMAKE = ${SHELL} /home/erwin/develop/libxml2-2.6.26/missing --run automake-1.9 +AWK = gawk +BASE_THREAD_LIBS = -lpthread +C14N_OBJ = c14n.c +CATALOG_OBJ = catalog.o +CC = gcc +CCDEPMODE = depmode=gcc3 +CFLAGS = -g -O2 -pedantic -W -Wformat -Wunused -Wimplicit -Wreturn-type -Wswitch -Wcomment -Wtrigraphs -Wformat -Wchar-subscripts -Wuninitialized -Wparentheses -Wshadow -Wpointer-arith -Wcast-align -Wwrite-strings -Waggregate-return -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Winline -Wredundant-decls +CPP = gcc -E +CPPFLAGS = +CXX = g++ +CXXCPP = g++ -E +CXXDEPMODE = depmode=gcc3 +CXXFLAGS = -g -O2 +CYGPATH_W = echo +CYGWIN_EXTRA_LDFLAGS = +CYGWIN_EXTRA_PYTHON_LIBADD = +DEBUG_OBJ = debugXML.o +DEFS = -DHAVE_CONFIG_H +DEPDIR = .deps +DLLTOOL = dlltool +DOCB_OBJ = DOCBparser.o +ECHO = echo +ECHO_C = +ECHO_N = -n +ECHO_T = +EGREP = grep -E +EXEEXT = +F77 = f95 +FFLAGS = -g -O2 +FTP_OBJ = nanoftp.o +HAVE_ISINF = +HAVE_ISNAN = +HTML_DIR = $(datadir)/doc/$(PACKAGE)-$(VERSION)/html +HTML_OBJ = HTMLparser.o HTMLtree.o +HTTP_OBJ = nanohttp.o +ICONV_LIBS = +INSTALL_DATA = ${INSTALL} -m 644 +INSTALL_PROGRAM = ${INSTALL} +INSTALL_SCRIPT = ${INSTALL} +INSTALL_STRIP_PROGRAM = ${SHELL} $(install_sh) -c -s +LDFLAGS = +LIBOBJS = +LIBS = +LIBTOOL = $(SHELL) $(top_builddir)/libtool +LIBXML_MAJOR_VERSION = 2 +LIBXML_MICRO_VERSION = 26 +LIBXML_MINOR_VERSION = 6 +LIBXML_VERSION = 2.6.26 +LIBXML_VERSION_EXTRA = +LIBXML_VERSION_INFO = 8:26:6 +LIBXML_VERSION_NUMBER = 20626 +LN_S = ln -s +LTLIBOBJS = +MAKEINFO = ${SHELL} /home/erwin/develop/libxml2-2.6.26/missing --run makeinfo +MODULE_EXTENSION = .so +MODULE_PLATFORM_LIBS = -ldl +MV = /bin/mv +M_LIBS = -lm +OBJDUMP = objdump +OBJEXT = o +PACKAGE = libxml2 +PACKAGE_BUGREPORT = +PACKAGE_NAME = +PACKAGE_STRING = +PACKAGE_TARNAME = +PACKAGE_VERSION = +PATH_SEPARATOR = : +PERL = /usr/bin/perl +PYTHON = /usr/bin/python +PYTHON_INCLUDES = /usr/include/python2.4 +PYTHON_SITE_PACKAGES = /usr/lib/python2.4/site-packages +PYTHON_SUBDIR = python +PYTHON_TESTS = RelaxNGPythonTests SchemasPythonTests +PYTHON_VERSION = 2.4 +RANLIB = ranlib +RDL_LIBS = +READER_TEST = Readertests +RELDATE = Sun Jun 18 2006 +RM = /bin/rm +SET_MAKE = +SHELL = /bin/sh +STATIC_BINARIES = +STRIP = strip +TAR = /bin/tar +TEST_C14N = C14Ntests +TEST_CATALOG = Catatests +TEST_DEBUG = Scripttests +TEST_HTML = HTMLtests +TEST_MODULES = ModuleTests +TEST_PATTERN = Patterntests +TEST_PHTML = HTMLPushtests +TEST_PUSH = XMLPushtests +TEST_REGEXPS = Regexptests Automatatests +TEST_SAX = SAXtests +TEST_SCHEMAS = Schemastests Relaxtests +TEST_SCHEMATRON = Schematrontests +TEST_THREADS = Threadtests +TEST_VALID = Validtests +TEST_VTIME = VTimingtests +TEST_XINCLUDE = XIncludetests +TEST_XPATH = XPathtests +TEST_XPTR = XPtrtests +THREADS_W32 = +THREAD_CFLAGS = -D_REENTRANT +THREAD_LIBS = +U = +VERSION = 2.6.26 +WIN32_EXTRA_LDFLAGS = +WIN32_EXTRA_LIBADD = +WITH_C14N = 1 +WITH_CATALOG = 1 +WITH_DEBUG = 1 +WITH_DOCB = 1 +WITH_FTP = 1 +WITH_HTML = 1 +WITH_HTTP = 1 +WITH_ICONV = 1 +WITH_ISO8859X = 1 +WITH_LEGACY = 1 +WITH_MEM_DEBUG = 0 +WITH_MODULES = 1 +WITH_OUTPUT = 1 +WITH_PATTERN = 1 +WITH_PUSH = 1 +WITH_PYTHON_FALSE = # +WITH_PYTHON_TRUE = +WITH_READER = 1 +WITH_REGEXPS = 1 +WITH_RUN_DEBUG = 0 +WITH_SAX1 = 1 +WITH_SCHEMAS = 1 +WITH_SCHEMATRON = 1 +WITH_THREADS = 1 +WITH_TREE = 1 +WITH_TRIO = 0 +WITH_TRIO_SOURCES_FALSE = +WITH_TRIO_SOURCES_TRUE = # +WITH_VALID = 1 +WITH_WRITER = 1 +WITH_XINCLUDE = 1 +WITH_XPATH = 1 +WITH_XPTR = 1 +XINCLUDE_OBJ = xinclude.o +XMLLINT = /usr/bin/xmllint +XML_CFLAGS = +XML_INCLUDEDIR = -I${includedir}/libxml2 +XML_LIBDIR = -L${libdir} +XML_LIBS = -lxml2 -lz -lm +XML_LIBTOOLLIBS = libxml2.la +XPATH_OBJ = xpath.o +XPTR_OBJ = xpointer.o +XSLTPROC = /usr/bin/xsltproc +Z_CFLAGS = +Z_LIBS = -lz +ac_ct_AR = ar +ac_ct_AS = +ac_ct_CC = gcc +ac_ct_CXX = g++ +ac_ct_DLLTOOL = +ac_ct_F77 = f95 +ac_ct_OBJDUMP = +ac_ct_RANLIB = ranlib +ac_ct_STRIP = strip +am__fastdepCC_FALSE = # +am__fastdepCC_TRUE = +am__fastdepCXX_FALSE = # +am__fastdepCXX_TRUE = +am__include = include +am__leading_dot = . +am__quote = +am__tar = ${AMTAR} chof - "$$tardir" +am__untar = ${AMTAR} xf - +bindir = ${exec_prefix}/bin +build = i686-redhat-linux-gnu +build_alias = +build_cpu = i686 +build_os = linux-gnu +build_vendor = redhat +datadir = ${prefix}/share +exec_prefix = ${prefix} +host = i686-redhat-linux-gnu +host_alias = +host_cpu = i686 +host_os = linux-gnu +host_vendor = redhat +includedir = ${prefix}/include +infodir = ${prefix}/info +install_sh = /home/erwin/develop/libxml2-2.6.26/install-sh +libdir = ${exec_prefix}/lib +libexecdir = ${exec_prefix}/libexec +localstatedir = ${prefix}/var +mandir = ${prefix}/man +mkdir_p = mkdir -p -- +oldincludedir = /usr/include +prefix = /usr/local +program_transform_name = s,x,x, +pythondir = $(libdir)/python$(PYTHON_VERSION)/site-packages +sbindir = ${exec_prefix}/sbin +sharedstatedir = ${prefix}/com +sysconfdir = ${prefix}/etc +target_alias = +xmlincdir = $(includedir)/libxml2/libxml +xmlinc_HEADERS = \ + SAX.h \ + entities.h \ + encoding.h \ + parser.h \ + parserInternals.h \ + xmlerror.h \ + HTMLparser.h \ + HTMLtree.h \ + debugXML.h \ + tree.h \ + list.h \ + hash.h \ + xpath.h \ + xpathInternals.h \ + xpointer.h \ + xinclude.h \ + xmlIO.h \ + xmlmemory.h \ + nanohttp.h \ + nanoftp.h \ + uri.h \ + valid.h \ + xlink.h \ + xmlversion.h \ + DOCBparser.h \ + catalog.h \ + threads.h \ + globals.h \ + c14n.h \ + xmlautomata.h \ + xmlregexp.h \ + xmlmodule.h \ + xmlschemas.h \ + schemasInternals.h \ + xmlschemastypes.h \ + xmlstring.h \ + xmlunicode.h \ + xmlreader.h \ + relaxng.h \ + dict.h \ + SAX2.h \ + xmlexports.h \ + xmlwriter.h \ + chvalid.h \ + pattern.h \ + xmlsave.h \ + schematron.h + +EXTRA_DIST = xmlversion.h.in +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu include/libxml/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu include/libxml/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +xmlversion.h: $(top_builddir)/config.status $(srcdir)/xmlversion.h.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: +install-xmlincHEADERS: $(xmlinc_HEADERS) + @$(NORMAL_INSTALL) + test -z "$(xmlincdir)" || $(mkdir_p) "$(DESTDIR)$(xmlincdir)" + @list='$(xmlinc_HEADERS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f=$(am__strip_dir) \ + echo " $(xmlincHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(xmlincdir)/$$f'"; \ + $(xmlincHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(xmlincdir)/$$f"; \ + done + +uninstall-xmlincHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(xmlinc_HEADERS)'; for p in $$list; do \ + f=$(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(xmlincdir)/$$f'"; \ + rm -f "$(DESTDIR)$(xmlincdir)/$$f"; \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(xmlincdir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-libtool \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-xmlincHEADERS + +install-exec-am: + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-exec-hook + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-info-am uninstall-xmlincHEADERS + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool ctags distclean distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-exec-hook \ + install-info install-info-am install-man install-strip \ + install-xmlincHEADERS installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-info-am \ + uninstall-xmlincHEADERS + + +install-exec-hook: + $(mkinstalldirs) $(DESTDIR)$(xmlincdir) +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/Extras/LibXML/include/libxml/Makefile.am b/Extras/LibXML/include/libxml/Makefile.am new file mode 100644 index 000000000..bf03a3b22 --- /dev/null +++ b/Extras/LibXML/include/libxml/Makefile.am @@ -0,0 +1,57 @@ +## Process this file with automake to produce Makefile.in + +xmlincdir = $(includedir)/libxml2/libxml + +xmlinc_HEADERS = \ + SAX.h \ + entities.h \ + encoding.h \ + parser.h \ + parserInternals.h \ + xmlerror.h \ + HTMLparser.h \ + HTMLtree.h \ + debugXML.h \ + tree.h \ + list.h \ + hash.h \ + xpath.h \ + xpathInternals.h \ + xpointer.h \ + xinclude.h \ + xmlIO.h \ + xmlmemory.h \ + nanohttp.h \ + nanoftp.h \ + uri.h \ + valid.h \ + xlink.h \ + xmlversion.h \ + DOCBparser.h \ + catalog.h \ + threads.h \ + globals.h \ + c14n.h \ + xmlautomata.h \ + xmlregexp.h \ + xmlmodule.h \ + xmlschemas.h \ + schemasInternals.h \ + xmlschemastypes.h \ + xmlstring.h \ + xmlunicode.h \ + xmlreader.h \ + relaxng.h \ + dict.h \ + SAX2.h \ + xmlexports.h \ + xmlwriter.h \ + chvalid.h \ + pattern.h \ + xmlsave.h \ + schematron.h + +install-exec-hook: + $(mkinstalldirs) $(DESTDIR)$(xmlincdir) + +EXTRA_DIST = xmlversion.h.in diff --git a/Extras/LibXML/include/libxml/Makefile.in b/Extras/LibXML/include/libxml/Makefile.in new file mode 100644 index 000000000..f273b8254 --- /dev/null +++ b/Extras/LibXML/include/libxml/Makefile.in @@ -0,0 +1,559 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = include/libxml +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(srcdir)/xmlversion.h.in $(xmlinc_HEADERS) +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = xmlversion.h +SOURCES = +DIST_SOURCES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; +am__installdirs = "$(DESTDIR)$(xmlincdir)" +xmlincHEADERS_INSTALL = $(INSTALL_HEADER) +HEADERS = $(xmlinc_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BASE_THREAD_LIBS = @BASE_THREAD_LIBS@ +C14N_OBJ = @C14N_OBJ@ +CATALOG_OBJ = @CATALOG_OBJ@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYGWIN_EXTRA_LDFLAGS = @CYGWIN_EXTRA_LDFLAGS@ +CYGWIN_EXTRA_PYTHON_LIBADD = @CYGWIN_EXTRA_PYTHON_LIBADD@ +DEBUG_OBJ = @DEBUG_OBJ@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOCB_OBJ = @DOCB_OBJ@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +FTP_OBJ = @FTP_OBJ@ +HAVE_ISINF = @HAVE_ISINF@ +HAVE_ISNAN = @HAVE_ISNAN@ +HTML_DIR = @HTML_DIR@ +HTML_OBJ = @HTML_OBJ@ +HTTP_OBJ = @HTTP_OBJ@ +ICONV_LIBS = @ICONV_LIBS@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBXML_MAJOR_VERSION = @LIBXML_MAJOR_VERSION@ +LIBXML_MICRO_VERSION = @LIBXML_MICRO_VERSION@ +LIBXML_MINOR_VERSION = @LIBXML_MINOR_VERSION@ +LIBXML_VERSION = @LIBXML_VERSION@ +LIBXML_VERSION_EXTRA = @LIBXML_VERSION_EXTRA@ +LIBXML_VERSION_INFO = @LIBXML_VERSION_INFO@ +LIBXML_VERSION_NUMBER = @LIBXML_VERSION_NUMBER@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MODULE_EXTENSION = @MODULE_EXTENSION@ +MODULE_PLATFORM_LIBS = @MODULE_PLATFORM_LIBS@ +MV = @MV@ +M_LIBS = @M_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PYTHON = @PYTHON@ +PYTHON_INCLUDES = @PYTHON_INCLUDES@ +PYTHON_SITE_PACKAGES = @PYTHON_SITE_PACKAGES@ +PYTHON_SUBDIR = @PYTHON_SUBDIR@ +PYTHON_TESTS = @PYTHON_TESTS@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +RDL_LIBS = @RDL_LIBS@ +READER_TEST = @READER_TEST@ +RELDATE = @RELDATE@ +RM = @RM@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STATIC_BINARIES = @STATIC_BINARIES@ +STRIP = @STRIP@ +TAR = @TAR@ +TEST_C14N = @TEST_C14N@ +TEST_CATALOG = @TEST_CATALOG@ +TEST_DEBUG = @TEST_DEBUG@ +TEST_HTML = @TEST_HTML@ +TEST_MODULES = @TEST_MODULES@ +TEST_PATTERN = @TEST_PATTERN@ +TEST_PHTML = @TEST_PHTML@ +TEST_PUSH = @TEST_PUSH@ +TEST_REGEXPS = @TEST_REGEXPS@ +TEST_SAX = @TEST_SAX@ +TEST_SCHEMAS = @TEST_SCHEMAS@ +TEST_SCHEMATRON = @TEST_SCHEMATRON@ +TEST_THREADS = @TEST_THREADS@ +TEST_VALID = @TEST_VALID@ +TEST_VTIME = @TEST_VTIME@ +TEST_XINCLUDE = @TEST_XINCLUDE@ +TEST_XPATH = @TEST_XPATH@ +TEST_XPTR = @TEST_XPTR@ +THREADS_W32 = @THREADS_W32@ +THREAD_CFLAGS = @THREAD_CFLAGS@ +THREAD_LIBS = @THREAD_LIBS@ +U = @U@ +VERSION = @VERSION@ +WIN32_EXTRA_LDFLAGS = @WIN32_EXTRA_LDFLAGS@ +WIN32_EXTRA_LIBADD = @WIN32_EXTRA_LIBADD@ +WITH_C14N = @WITH_C14N@ +WITH_CATALOG = @WITH_CATALOG@ +WITH_DEBUG = @WITH_DEBUG@ +WITH_DOCB = @WITH_DOCB@ +WITH_FTP = @WITH_FTP@ +WITH_HTML = @WITH_HTML@ +WITH_HTTP = @WITH_HTTP@ +WITH_ICONV = @WITH_ICONV@ +WITH_ISO8859X = @WITH_ISO8859X@ +WITH_LEGACY = @WITH_LEGACY@ +WITH_MEM_DEBUG = @WITH_MEM_DEBUG@ +WITH_MODULES = @WITH_MODULES@ +WITH_OUTPUT = @WITH_OUTPUT@ +WITH_PATTERN = @WITH_PATTERN@ +WITH_PUSH = @WITH_PUSH@ +WITH_PYTHON_FALSE = @WITH_PYTHON_FALSE@ +WITH_PYTHON_TRUE = @WITH_PYTHON_TRUE@ +WITH_READER = @WITH_READER@ +WITH_REGEXPS = @WITH_REGEXPS@ +WITH_RUN_DEBUG = @WITH_RUN_DEBUG@ +WITH_SAX1 = @WITH_SAX1@ +WITH_SCHEMAS = @WITH_SCHEMAS@ +WITH_SCHEMATRON = @WITH_SCHEMATRON@ +WITH_THREADS = @WITH_THREADS@ +WITH_TREE = @WITH_TREE@ +WITH_TRIO = @WITH_TRIO@ +WITH_TRIO_SOURCES_FALSE = @WITH_TRIO_SOURCES_FALSE@ +WITH_TRIO_SOURCES_TRUE = @WITH_TRIO_SOURCES_TRUE@ +WITH_VALID = @WITH_VALID@ +WITH_WRITER = @WITH_WRITER@ +WITH_XINCLUDE = @WITH_XINCLUDE@ +WITH_XPATH = @WITH_XPATH@ +WITH_XPTR = @WITH_XPTR@ +XINCLUDE_OBJ = @XINCLUDE_OBJ@ +XMLLINT = @XMLLINT@ +XML_CFLAGS = @XML_CFLAGS@ +XML_INCLUDEDIR = @XML_INCLUDEDIR@ +XML_LIBDIR = @XML_LIBDIR@ +XML_LIBS = @XML_LIBS@ +XML_LIBTOOLLIBS = @XML_LIBTOOLLIBS@ +XPATH_OBJ = @XPATH_OBJ@ +XPTR_OBJ = @XPTR_OBJ@ +XSLTPROC = @XSLTPROC@ +Z_CFLAGS = @Z_CFLAGS@ +Z_LIBS = @Z_LIBS@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_AS = @ac_ct_AS@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DLLTOOL = @ac_ct_DLLTOOL@ +ac_ct_F77 = @ac_ct_F77@ +ac_ct_OBJDUMP = @ac_ct_OBJDUMP@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +xmlincdir = $(includedir)/libxml2/libxml +xmlinc_HEADERS = \ + SAX.h \ + entities.h \ + encoding.h \ + parser.h \ + parserInternals.h \ + xmlerror.h \ + HTMLparser.h \ + HTMLtree.h \ + debugXML.h \ + tree.h \ + list.h \ + hash.h \ + xpath.h \ + xpathInternals.h \ + xpointer.h \ + xinclude.h \ + xmlIO.h \ + xmlmemory.h \ + nanohttp.h \ + nanoftp.h \ + uri.h \ + valid.h \ + xlink.h \ + xmlversion.h \ + DOCBparser.h \ + catalog.h \ + threads.h \ + globals.h \ + c14n.h \ + xmlautomata.h \ + xmlregexp.h \ + xmlmodule.h \ + xmlschemas.h \ + schemasInternals.h \ + xmlschemastypes.h \ + xmlstring.h \ + xmlunicode.h \ + xmlreader.h \ + relaxng.h \ + dict.h \ + SAX2.h \ + xmlexports.h \ + xmlwriter.h \ + chvalid.h \ + pattern.h \ + xmlsave.h \ + schematron.h + +EXTRA_DIST = xmlversion.h.in +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu include/libxml/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu include/libxml/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +xmlversion.h: $(top_builddir)/config.status $(srcdir)/xmlversion.h.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: +install-xmlincHEADERS: $(xmlinc_HEADERS) + @$(NORMAL_INSTALL) + test -z "$(xmlincdir)" || $(mkdir_p) "$(DESTDIR)$(xmlincdir)" + @list='$(xmlinc_HEADERS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f=$(am__strip_dir) \ + echo " $(xmlincHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(xmlincdir)/$$f'"; \ + $(xmlincHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(xmlincdir)/$$f"; \ + done + +uninstall-xmlincHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(xmlinc_HEADERS)'; for p in $$list; do \ + f=$(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(xmlincdir)/$$f'"; \ + rm -f "$(DESTDIR)$(xmlincdir)/$$f"; \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(xmlincdir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-libtool \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-xmlincHEADERS + +install-exec-am: + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-exec-hook + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-info-am uninstall-xmlincHEADERS + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool ctags distclean distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-exec-hook \ + install-info install-info-am install-man install-strip \ + install-xmlincHEADERS installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-info-am \ + uninstall-xmlincHEADERS + + +install-exec-hook: + $(mkinstalldirs) $(DESTDIR)$(xmlincdir) +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/Extras/LibXML/include/libxml/schematron.h b/Extras/LibXML/include/libxml/schematron.h new file mode 100644 index 000000000..6eaa699a8 --- /dev/null +++ b/Extras/LibXML/include/libxml/schematron.h @@ -0,0 +1,124 @@ +/* + * Summary: XML Schemastron implementation + * Description: interface to the XML Schematron validity checking. + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + + +#ifndef __XML_SCHEMATRON_H__ +#define __XML_SCHEMATRON_H__ + +#include + +#ifdef LIBXML_SCHEMATRON_ENABLED + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + XML_SCHEMATRON_OUT_QUIET = 1 << 0, /* quiet no report */ + XML_SCHEMATRON_OUT_TEXT = 1 << 1, /* build a textual report */ + XML_SCHEMATRON_OUT_XML = 1 << 2, /* output SVRL */ + XML_SCHEMATRON_OUT_FILE = 1 << 8, /* output to a file descriptor */ + XML_SCHEMATRON_OUT_BUFFER = 1 << 9, /* output to a buffer */ + XML_SCHEMATRON_OUT_IO = 1 << 10 /* output to I/O mechanism */ +} xmlSchematronValidOptions; + +/** + * The schemas related types are kept internal + */ +typedef struct _xmlSchematron xmlSchematron; +typedef xmlSchematron *xmlSchematronPtr; + +/** + * A schemas validation context + */ +typedef void (*xmlSchematronValidityErrorFunc) (void *ctx, const char *msg, ...); +typedef void (*xmlSchematronValidityWarningFunc) (void *ctx, const char *msg, ...); + +typedef struct _xmlSchematronParserCtxt xmlSchematronParserCtxt; +typedef xmlSchematronParserCtxt *xmlSchematronParserCtxtPtr; + +typedef struct _xmlSchematronValidCtxt xmlSchematronValidCtxt; +typedef xmlSchematronValidCtxt *xmlSchematronValidCtxtPtr; + +/* + * Interfaces for parsing. + */ +XMLPUBFUN xmlSchematronParserCtxtPtr XMLCALL + xmlSchematronNewParserCtxt (const char *URL); +XMLPUBFUN xmlSchematronParserCtxtPtr XMLCALL + xmlSchematronNewMemParserCtxt(const char *buffer, + int size); +XMLPUBFUN xmlSchematronParserCtxtPtr XMLCALL + xmlSchematronNewDocParserCtxt(xmlDocPtr doc); +XMLPUBFUN void XMLCALL + xmlSchematronFreeParserCtxt (xmlSchematronParserCtxtPtr ctxt); +/***** +XMLPUBFUN void XMLCALL + xmlSchematronSetParserErrors(xmlSchematronParserCtxtPtr ctxt, + xmlSchematronValidityErrorFunc err, + xmlSchematronValidityWarningFunc warn, + void *ctx); +XMLPUBFUN int XMLCALL + xmlSchematronGetParserErrors(xmlSchematronParserCtxtPtr ctxt, + xmlSchematronValidityErrorFunc * err, + xmlSchematronValidityWarningFunc * warn, + void **ctx); +XMLPUBFUN int XMLCALL + xmlSchematronIsValid (xmlSchematronValidCtxtPtr ctxt); + *****/ +XMLPUBFUN xmlSchematronPtr XMLCALL + xmlSchematronParse (xmlSchematronParserCtxtPtr ctxt); +XMLPUBFUN void XMLCALL + xmlSchematronFree (xmlSchematronPtr schema); +/* + * Interfaces for validating + */ +/****** +XMLPUBFUN void XMLCALL + xmlSchematronSetValidStructuredErrors( + xmlSchematronValidCtxtPtr ctxt, + xmlStructuredErrorFunc serror, + void *ctx); +XMLPUBFUN void XMLCALL + xmlSchematronSetValidErrors (xmlSchematronValidCtxtPtr ctxt, + xmlSchematronValidityErrorFunc err, + xmlSchematronValidityWarningFunc warn, + void *ctx); +XMLPUBFUN int XMLCALL + xmlSchematronGetValidErrors (xmlSchematronValidCtxtPtr ctxt, + xmlSchematronValidityErrorFunc *err, + xmlSchematronValidityWarningFunc *warn, + void **ctx); +XMLPUBFUN int XMLCALL + xmlSchematronSetValidOptions(xmlSchematronValidCtxtPtr ctxt, + int options); +XMLPUBFUN int XMLCALL + xmlSchematronValidCtxtGetOptions(xmlSchematronValidCtxtPtr ctxt); +XMLPUBFUN int XMLCALL + xmlSchematronValidateOneElement (xmlSchematronValidCtxtPtr ctxt, + xmlNodePtr elem); + *******/ + +XMLPUBFUN xmlSchematronValidCtxtPtr XMLCALL + xmlSchematronNewValidCtxt (xmlSchematronPtr schema, + int options); +XMLPUBFUN void XMLCALL + xmlSchematronFreeValidCtxt (xmlSchematronValidCtxtPtr ctxt); +XMLPUBFUN int XMLCALL + xmlSchematronValidateDoc (xmlSchematronValidCtxtPtr ctxt, + xmlDocPtr instance); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBXML_SCHEMATRON_ENABLED */ +#endif /* __XML_SCHEMATRON_H__ */ diff --git a/Extras/LibXML/include/libxml/xmlversion.h b/Extras/LibXML/include/libxml/xmlversion.h index cc75dc027..416950812 100644 --- a/Extras/LibXML/include/libxml/xmlversion.h +++ b/Extras/LibXML/include/libxml/xmlversion.h @@ -50,7 +50,7 @@ XMLPUBFUN void XMLCALL xmlCheckVersion(int version); * * extra version information, used to show a CVS compilation */ -#define LIBXML_VERSION_EXTRA "-CVS2798" +#define LIBXML_VERSION_EXTRA "" /** * LIBXML_TEST_VERSION: @@ -290,7 +290,7 @@ XMLPUBFUN void XMLCALL xmlCheckVersion(int version); * * Whether the memory debugging is configured in */ -#if 1 +#if 0 #define DEBUG_MEMORY_LOCATION #endif @@ -299,7 +299,7 @@ XMLPUBFUN void XMLCALL xmlCheckVersion(int version); * * Whether the runtime debugging is configured in */ -#if 1 +#if 0 #define LIBXML_DEBUG_RUNTIME #endif diff --git a/Extras/LibXML/include/libxml/xmlversion.h.in b/Extras/LibXML/include/libxml/xmlversion.h.in new file mode 100644 index 000000000..cf547dd92 --- /dev/null +++ b/Extras/LibXML/include/libxml/xmlversion.h.in @@ -0,0 +1,396 @@ +/* + * Summary: compile-time version informations + * Description: compile-time version informations for the XML library + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XML_VERSION_H__ +#define __XML_VERSION_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * use those to be sure nothing nasty will happen if + * your library and includes mismatch + */ +#ifndef LIBXML2_COMPILING_MSCCDEF +XMLPUBFUN void XMLCALL xmlCheckVersion(int version); +#endif /* LIBXML2_COMPILING_MSCCDEF */ + +/** + * LIBXML_DOTTED_VERSION: + * + * the version string like "1.2.3" + */ +#define LIBXML_DOTTED_VERSION "@VERSION@" + +/** + * LIBXML_VERSION: + * + * the version number: 1.2.3 value is 1002003 + */ +#define LIBXML_VERSION @LIBXML_VERSION_NUMBER@ + +/** + * LIBXML_VERSION_STRING: + * + * the version number string, 1.2.3 value is "1002003" + */ +#define LIBXML_VERSION_STRING "@LIBXML_VERSION_NUMBER@" + +/** + * LIBXML_VERSION_EXTRA: + * + * extra version information, used to show a CVS compilation + */ +#define LIBXML_VERSION_EXTRA "@LIBXML_VERSION_EXTRA@" + +/** + * LIBXML_TEST_VERSION: + * + * Macro to check that the libxml version in use is compatible with + * the version the software has been compiled against + */ +#define LIBXML_TEST_VERSION xmlCheckVersion(@LIBXML_VERSION_NUMBER@); + +#ifndef VMS +#if @WITH_TRIO@ +/** + * WITH_TRIO: + * + * defined if the trio support need to be configured in + */ +#define WITH_TRIO +#else +/** + * WITHOUT_TRIO: + * + * defined if the trio support should not be configured in + */ +#define WITHOUT_TRIO +#endif +#else /* VMS */ +/** + * WITH_TRIO: + * + * defined if the trio support need to be configured in + */ +#define WITH_TRIO 1 +#endif /* VMS */ + +/** + * LIBXML_THREAD_ENABLED: + * + * Whether the thread support is configured in + */ +#if @WITH_THREADS@ +#if defined(_REENTRANT) || defined(__MT__) || (_POSIX_C_SOURCE - 0 >= 199506L) +#define LIBXML_THREAD_ENABLED +#endif +#endif + +/** + * LIBXML_TREE_ENABLED: + * + * Whether the DOM like tree manipulation API support is configured in + */ +#if @WITH_TREE@ +#define LIBXML_TREE_ENABLED +#endif + +/** + * LIBXML_OUTPUT_ENABLED: + * + * Whether the serialization/saving support is configured in + */ +#if @WITH_OUTPUT@ +#define LIBXML_OUTPUT_ENABLED +#endif + +/** + * LIBXML_PUSH_ENABLED: + * + * Whether the push parsing interfaces are configured in + */ +#if @WITH_PUSH@ +#define LIBXML_PUSH_ENABLED +#endif + +/** + * LIBXML_READER_ENABLED: + * + * Whether the xmlReader parsing interface is configured in + */ +#if @WITH_READER@ +#define LIBXML_READER_ENABLED +#endif + +/** + * LIBXML_PATTERN_ENABLED: + * + * Whether the xmlPattern node selection interface is configured in + */ +#if @WITH_PATTERN@ +#define LIBXML_PATTERN_ENABLED +#endif + +/** + * LIBXML_WRITER_ENABLED: + * + * Whether the xmlWriter saving interface is configured in + */ +#if @WITH_WRITER@ +#define LIBXML_WRITER_ENABLED +#endif + +/** + * LIBXML_SAX1_ENABLED: + * + * Whether the older SAX1 interface is configured in + */ +#if @WITH_SAX1@ +#define LIBXML_SAX1_ENABLED +#endif + +/** + * LIBXML_FTP_ENABLED: + * + * Whether the FTP support is configured in + */ +#if @WITH_FTP@ +#define LIBXML_FTP_ENABLED +#endif + +/** + * LIBXML_HTTP_ENABLED: + * + * Whether the HTTP support is configured in + */ +#if @WITH_HTTP@ +#define LIBXML_HTTP_ENABLED +#endif + +/** + * LIBXML_VALID_ENABLED: + * + * Whether the DTD validation support is configured in + */ +#if @WITH_VALID@ +#define LIBXML_VALID_ENABLED +#endif + +/** + * LIBXML_HTML_ENABLED: + * + * Whether the HTML support is configured in + */ +#if @WITH_HTML@ +#define LIBXML_HTML_ENABLED +#endif + +/** + * LIBXML_LEGACY_ENABLED: + * + * Whether the deprecated APIs are compiled in for compatibility + */ +#if @WITH_LEGACY@ +#define LIBXML_LEGACY_ENABLED +#endif + +/** + * LIBXML_C14N_ENABLED: + * + * Whether the Canonicalization support is configured in + */ +#if @WITH_C14N@ +#define LIBXML_C14N_ENABLED +#endif + +/** + * LIBXML_CATALOG_ENABLED: + * + * Whether the Catalog support is configured in + */ +#if @WITH_CATALOG@ +#define LIBXML_CATALOG_ENABLED +#endif + +/** + * LIBXML_DOCB_ENABLED: + * + * Whether the SGML Docbook support is configured in + */ +#if @WITH_DOCB@ +#define LIBXML_DOCB_ENABLED +#endif + +/** + * LIBXML_XPATH_ENABLED: + * + * Whether XPath is configured in + */ +#if @WITH_XPATH@ +#define LIBXML_XPATH_ENABLED +#endif + +/** + * LIBXML_XPTR_ENABLED: + * + * Whether XPointer is configured in + */ +#if @WITH_XPTR@ +#define LIBXML_XPTR_ENABLED +#endif + +/** + * LIBXML_XINCLUDE_ENABLED: + * + * Whether XInclude is configured in + */ +#if @WITH_XINCLUDE@ +#define LIBXML_XINCLUDE_ENABLED +#endif + +/** + * LIBXML_ICONV_ENABLED: + * + * Whether iconv support is available + */ +#if @WITH_ICONV@ +#define LIBXML_ICONV_ENABLED +#endif + +/** + * LIBXML_ISO8859X_ENABLED: + * + * Whether ISO-8859-* support is made available in case iconv is not + */ +#if @WITH_ISO8859X@ +#define LIBXML_ISO8859X_ENABLED +#endif + +/** + * LIBXML_DEBUG_ENABLED: + * + * Whether Debugging module is configured in + */ +#if @WITH_DEBUG@ +#define LIBXML_DEBUG_ENABLED +#endif + +/** + * DEBUG_MEMORY_LOCATION: + * + * Whether the memory debugging is configured in + */ +#if @WITH_MEM_DEBUG@ +#define DEBUG_MEMORY_LOCATION +#endif + +/** + * LIBXML_DEBUG_RUNTIME: + * + * Whether the runtime debugging is configured in + */ +#if @WITH_RUN_DEBUG@ +#define LIBXML_DEBUG_RUNTIME +#endif + +/** + * LIBXML_UNICODE_ENABLED: + * + * Whether the Unicode related interfaces are compiled in + */ +#if @WITH_REGEXPS@ +#define LIBXML_UNICODE_ENABLED +#endif + +/** + * LIBXML_REGEXP_ENABLED: + * + * Whether the regular expressions interfaces are compiled in + */ +#if @WITH_REGEXPS@ +#define LIBXML_REGEXP_ENABLED +#endif + +/** + * LIBXML_AUTOMATA_ENABLED: + * + * Whether the automata interfaces are compiled in + */ +#if @WITH_REGEXPS@ +#define LIBXML_AUTOMATA_ENABLED +#endif + +/** + * LIBXML_EXPR_ENABLED: + * + * Whether the formal expressions interfaces are compiled in + */ +#if @WITH_SCHEMAS@ +#define LIBXML_EXPR_ENABLED +#endif + +/** + * LIBXML_SCHEMAS_ENABLED: + * + * Whether the Schemas validation interfaces are compiled in + */ +#if @WITH_SCHEMAS@ +#define LIBXML_SCHEMAS_ENABLED +#endif + +/** + * LIBXML_SCHEMATRON_ENABLED: + * + * Whether the Schematron validation interfaces are compiled in + */ +#if @WITH_SCHEMATRON@ +#define LIBXML_SCHEMATRON_ENABLED +#endif + +/** + * LIBXML_MODULES_ENABLED: + * + * Whether the module interfaces are compiled in + */ +#if @WITH_MODULES@ +#define LIBXML_MODULES_ENABLED +/** + * LIBXML_MODULE_EXTENSION: + * + * the string suffix used by dynamic modules (usually shared libraries) + */ +#define LIBXML_MODULE_EXTENSION "@MODULE_EXTENSION@" +#endif + +/** + * ATTRIBUTE_UNUSED: + * + * Macro used to signal to GCC unused function parameters + */ +#ifdef __GNUC__ +#ifdef HAVE_ANSIDECL_H +#include +#endif +#ifndef ATTRIBUTE_UNUSED +#define ATTRIBUTE_UNUSED __attribute__((unused)) +#endif +#else +#define ATTRIBUTE_UNUSED +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif + + diff --git a/Extras/LibXML/runsuite.c b/Extras/LibXML/runsuite.c new file mode 100644 index 000000000..744875c7b --- /dev/null +++ b/Extras/LibXML/runsuite.c @@ -0,0 +1,1184 @@ +/* + * runsuite.c: C program to run libxml2 againts published testsuites + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#ifdef HAVE_CONFIG_H +#include "libxml.h" +#else +#include +#endif + +#if !defined(_WIN32) || defined(__CYGWIN__) +#include +#endif +#include +#include +#include +#include + +#include +#include +#include +#include +#if defined(LIBXML_SCHEMAS_ENABLED) && defined(LIBXML_XPATH_ENABLED) +#include + +#include +#include + +#include +#include +#include + +#define LOGFILE "runsuite.log" +static FILE *logfile = NULL; +static int verbose = 0; + + + +#if defined(_WIN32) && !defined(__CYGWIN__) + +#define vsnprintf _vsnprintf + +#define snprintf _snprintf + +#endif + +/************************************************************************ + * * + * File name and path utilities * + * * + ************************************************************************/ + +static int checkTestFile(const char *filename) { + struct stat buf; + + if (stat(filename, &buf) == -1) + return(0); + +#if defined(_WIN32) && !defined(__CYGWIN__) + if (!(buf.st_mode & _S_IFREG)) + return(0); +#else + if (!S_ISREG(buf.st_mode)) + return(0); +#endif + + return(1); +} + +static xmlChar *composeDir(const xmlChar *dir, const xmlChar *path) { + char buf[500]; + + if (dir == NULL) return(xmlStrdup(path)); + if (path == NULL) return(NULL); + + snprintf(buf, 500, "%s/%s", (const char *) dir, (const char *) path); + return(xmlStrdup((const xmlChar *) buf)); +} + +/************************************************************************ + * * + * Libxml2 specific routines * + * * + ************************************************************************/ + +static int nb_tests = 0; +static int nb_errors = 0; +static int nb_internals = 0; +static int nb_schematas = 0; +static int nb_unimplemented = 0; +static int nb_leaks = 0; +static int extraMemoryFromResolver = 0; + +static int +fatalError(void) { + fprintf(stderr, "Exitting tests on fatal error\n"); + exit(1); +} + +/* + * that's needed to implement + */ +#define MAX_ENTITIES 20 +static char *testEntitiesName[MAX_ENTITIES]; +static char *testEntitiesValue[MAX_ENTITIES]; +static int nb_entities = 0; +static void resetEntities(void) { + int i; + + for (i = 0;i < nb_entities;i++) { + if (testEntitiesName[i] != NULL) + xmlFree(testEntitiesName[i]); + if (testEntitiesValue[i] != NULL) + xmlFree(testEntitiesValue[i]); + } + nb_entities = 0; +} +static int addEntity(char *name, char *content) { + if (nb_entities >= MAX_ENTITIES) { + fprintf(stderr, "Too many entities defined\n"); + return(-1); + } + testEntitiesName[nb_entities] = name; + testEntitiesValue[nb_entities] = content; + nb_entities++; + return(0); +} + +/* + * We need to trap calls to the resolver to not account memory for the catalog + * which is shared to the current running test. We also don't want to have + * network downloads modifying tests. + */ +static xmlParserInputPtr +testExternalEntityLoader(const char *URL, const char *ID, + xmlParserCtxtPtr ctxt) { + xmlParserInputPtr ret; + int i; + + for (i = 0;i < nb_entities;i++) { + if (!strcmp(testEntitiesName[i], URL)) { + ret = xmlNewStringInputStream(ctxt, + (const xmlChar *) testEntitiesValue[i]); + if (ret != NULL) { + ret->filename = (const char *) + xmlStrdup((xmlChar *)testEntitiesName[i]); + } + return(ret); + } + } + if (checkTestFile(URL)) { + ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt); + } else { + int memused = xmlMemUsed(); + ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt); + extraMemoryFromResolver += xmlMemUsed() - memused; + } +#if 0 + if (ret == NULL) { + fprintf(stderr, "Failed to find resource %s\n", URL); + } +#endif + + return(ret); +} + +/* + * Trapping the error messages at the generic level to grab the equivalent of + * stderr messages on CLI tools. + */ +static char testErrors[32769]; +static int testErrorsSize = 0; + +static void test_log(const char *msg, ...) { + va_list args; + if (logfile != NULL) { + fprintf(logfile, "\n------------\n"); + va_start(args, msg); + vfprintf(logfile, msg, args); + va_end(args); + fprintf(logfile, "%s", testErrors); + testErrorsSize = 0; testErrors[0] = 0; + } + if (verbose) { + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); + } +} + +static void +testErrorHandler(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) { + va_list args; + int res; + + if (testErrorsSize >= 32768) + return; + va_start(args, msg); + res = vsnprintf(&testErrors[testErrorsSize], + 32768 - testErrorsSize, + msg, args); + va_end(args); + if (testErrorsSize + res >= 32768) { + /* buffer is full */ + testErrorsSize = 32768; + testErrors[testErrorsSize] = 0; + } else { + testErrorsSize += res; + } + testErrors[testErrorsSize] = 0; +} + +static xmlXPathContextPtr ctxtXPath; + +static void +initializeLibxml2(void) { + xmlGetWarningsDefaultValue = 0; + xmlPedanticParserDefault(0); + + xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup); + xmlInitParser(); + xmlSetExternalEntityLoader(testExternalEntityLoader); + ctxtXPath = xmlXPathNewContext(NULL); + /* + * Deactivate the cache if created; otherwise we have to create/free it + * for every test, since it will confuse the memory leak detection. + * Note that normally this need not be done, since the cache is not + * created until set explicitely with xmlXPathContextSetCache(); + * but for test purposes it is sometimes usefull to activate the + * cache by default for the whole library. + */ + if (ctxtXPath->cache != NULL) + xmlXPathContextSetCache(ctxtXPath, 0, -1, 0); + /* used as default nanemspace in xstc tests */ + xmlXPathRegisterNs(ctxtXPath, BAD_CAST "ts", BAD_CAST "TestSuite"); + xmlXPathRegisterNs(ctxtXPath, BAD_CAST "xlink", + BAD_CAST "http://www.w3.org/1999/xlink"); + xmlSetGenericErrorFunc(NULL, testErrorHandler); +#ifdef LIBXML_SCHEMAS_ENABLED + xmlSchemaInitTypes(); + xmlRelaxNGInitTypes(); +#endif +} + +static xmlNodePtr +getNext(xmlNodePtr cur, const char *xpath) { + xmlNodePtr ret = NULL; + xmlXPathObjectPtr res; + xmlXPathCompExprPtr comp; + + if ((cur == NULL) || (cur->doc == NULL) || (xpath == NULL)) + return(NULL); + ctxtXPath->doc = cur->doc; + ctxtXPath->node = cur; + comp = xmlXPathCompile(BAD_CAST xpath); + if (comp == NULL) { + fprintf(stderr, "Failed to compile %s\n", xpath); + return(NULL); + } + res = xmlXPathCompiledEval(comp, ctxtXPath); + xmlXPathFreeCompExpr(comp); + if (res == NULL) + return(NULL); + if ((res->type == XPATH_NODESET) && + (res->nodesetval != NULL) && + (res->nodesetval->nodeNr > 0) && + (res->nodesetval->nodeTab != NULL)) + ret = res->nodesetval->nodeTab[0]; + xmlXPathFreeObject(res); + return(ret); +} + +static xmlChar * +getString(xmlNodePtr cur, const char *xpath) { + xmlChar *ret = NULL; + xmlXPathObjectPtr res; + xmlXPathCompExprPtr comp; + + if ((cur == NULL) || (cur->doc == NULL) || (xpath == NULL)) + return(NULL); + ctxtXPath->doc = cur->doc; + ctxtXPath->node = cur; + comp = xmlXPathCompile(BAD_CAST xpath); + if (comp == NULL) { + fprintf(stderr, "Failed to compile %s\n", xpath); + return(NULL); + } + res = xmlXPathCompiledEval(comp, ctxtXPath); + xmlXPathFreeCompExpr(comp); + if (res == NULL) + return(NULL); + if (res->type == XPATH_STRING) { + ret = res->stringval; + res->stringval = NULL; + } + xmlXPathFreeObject(res); + return(ret); +} + +/************************************************************************ + * * + * Test test/xsdtest/xsdtestsuite.xml * + * * + ************************************************************************/ + +static int +xsdIncorectTestCase(xmlNodePtr cur) { + xmlNodePtr test; + xmlBufferPtr buf; + xmlRelaxNGParserCtxtPtr pctxt; + xmlRelaxNGPtr rng = NULL; + int ret = 0, memt; + + cur = getNext(cur, "./incorrect[1]"); + if (cur == NULL) { + return(0); + } + + test = getNext(cur, "./*"); + if (test == NULL) { + test_log("Failed to find test in correct line %ld\n", + xmlGetLineNo(cur)); + return(1); + } + + memt = xmlMemUsed(); + extraMemoryFromResolver = 0; + /* + * dump the schemas to a buffer, then reparse it and compile the schemas + */ + buf = xmlBufferCreate(); + if (buf == NULL) { + fprintf(stderr, "out of memory !\n"); + fatalError(); + } + xmlNodeDump(buf, test->doc, test, 0, 0); + pctxt = xmlRelaxNGNewMemParserCtxt((const char *)buf->content, buf->use); + xmlRelaxNGSetParserErrors(pctxt, + (xmlRelaxNGValidityErrorFunc) testErrorHandler, + (xmlRelaxNGValidityWarningFunc) testErrorHandler, + pctxt); + rng = xmlRelaxNGParse(pctxt); + xmlRelaxNGFreeParserCtxt(pctxt); + if (rng != NULL) { + test_log("Failed to detect incorect RNG line %ld\n", + xmlGetLineNo(test)); + ret = 1; + goto done; + } + +done: + if (buf != NULL) + xmlBufferFree(buf); + if (rng != NULL) + xmlRelaxNGFree(rng); + xmlResetLastError(); + if ((memt != xmlMemUsed()) && (extraMemoryFromResolver == 0)) { + test_log("Validation of tests starting line %ld leaked %d\n", + xmlGetLineNo(cur), xmlMemUsed() - memt); + nb_leaks++; + } + return(ret); +} + +static void +installResources(xmlNodePtr tst, const xmlChar *base) { + xmlNodePtr test; + xmlBufferPtr buf; + xmlChar *name, *content, *res; + + buf = xmlBufferCreate(); + if (buf == NULL) { + fprintf(stderr, "out of memory !\n"); + fatalError(); + } + xmlNodeDump(buf, tst->doc, tst, 0, 0); + + while (tst != NULL) { + test = getNext(tst, "./*"); + if (test != NULL) { + xmlBufferEmpty(buf); + xmlNodeDump(buf, test->doc, test, 0, 0); + name = getString(tst, "string(@name)"); + content = xmlStrdup(buf->content); + if ((name != NULL) && (content != NULL)) { + res = composeDir(base, name); + xmlFree(name); + addEntity((char *) res, (char *) content); + } else { + if (name != NULL) xmlFree(name); + if (content != NULL) xmlFree(content); + } + } + tst = getNext(tst, "following-sibling::resource[1]"); + } + if (buf != NULL) + xmlBufferFree(buf); +} + +static void +installDirs(xmlNodePtr tst, const xmlChar *base) { + xmlNodePtr test; + xmlChar *name, *res; + + name = getString(tst, "string(@name)"); + if (name == NULL) + return; + res = composeDir(base, name); + xmlFree(name); + if (res == NULL) { + return; + } + /* Now process resources and subdir recursively */ + test = getNext(tst, "./resource[1]"); + if (test != NULL) { + installResources(test, res); + } + test = getNext(tst, "./dir[1]"); + while (test != NULL) { + installDirs(test, res); + test = getNext(test, "following-sibling::dir[1]"); + } + xmlFree(res); +} + +static int +xsdTestCase(xmlNodePtr tst) { + xmlNodePtr test, tmp, cur; + xmlBufferPtr buf; + xmlDocPtr doc = NULL; + xmlRelaxNGParserCtxtPtr pctxt; + xmlRelaxNGValidCtxtPtr ctxt; + xmlRelaxNGPtr rng = NULL; + int ret = 0, mem, memt; + xmlChar *dtd; + + resetEntities(); + testErrorsSize = 0; testErrors[0] = 0; + + tmp = getNext(tst, "./dir[1]"); + if (tmp != NULL) { + installDirs(tmp, NULL); + } + tmp = getNext(tst, "./resource[1]"); + if (tmp != NULL) { + installResources(tmp, NULL); + } + + cur = getNext(tst, "./correct[1]"); + if (cur == NULL) { + return(xsdIncorectTestCase(tst)); + } + + test = getNext(cur, "./*"); + if (test == NULL) { + fprintf(stderr, "Failed to find test in correct line %ld\n", + xmlGetLineNo(cur)); + return(1); + } + + memt = xmlMemUsed(); + extraMemoryFromResolver = 0; + /* + * dump the schemas to a buffer, then reparse it and compile the schemas + */ + buf = xmlBufferCreate(); + if (buf == NULL) { + fprintf(stderr, "out of memory !\n"); + fatalError(); + } + xmlNodeDump(buf, test->doc, test, 0, 0); + pctxt = xmlRelaxNGNewMemParserCtxt((const char *)buf->content, buf->use); + xmlRelaxNGSetParserErrors(pctxt, + (xmlRelaxNGValidityErrorFunc) testErrorHandler, + (xmlRelaxNGValidityWarningFunc) testErrorHandler, + pctxt); + rng = xmlRelaxNGParse(pctxt); + xmlRelaxNGFreeParserCtxt(pctxt); + if (extraMemoryFromResolver) + memt = 0; + + if (rng == NULL) { + test_log("Failed to parse RNGtest line %ld\n", + xmlGetLineNo(test)); + nb_errors++; + ret = 1; + goto done; + } + /* + * now scan all the siblings of correct to process the tests + */ + tmp = getNext(cur, "following-sibling::valid[1]"); + while (tmp != NULL) { + dtd = xmlGetProp(tmp, BAD_CAST "dtd"); + test = getNext(tmp, "./*"); + if (test == NULL) { + fprintf(stderr, "Failed to find test in line %ld\n", + xmlGetLineNo(tmp)); + + } else { + xmlBufferEmpty(buf); + if (dtd != NULL) + xmlBufferAdd(buf, dtd, -1); + xmlNodeDump(buf, test->doc, test, 0, 0); + + /* + * We are ready to run the test + */ + mem = xmlMemUsed(); + extraMemoryFromResolver = 0; + doc = xmlReadMemory((const char *)buf->content, buf->use, + "test", NULL, 0); + if (doc == NULL) { + test_log("Failed to parse valid instance line %ld\n", + xmlGetLineNo(tmp)); + nb_errors++; + } else { + nb_tests++; + ctxt = xmlRelaxNGNewValidCtxt(rng); + xmlRelaxNGSetValidErrors(ctxt, + (xmlRelaxNGValidityErrorFunc) testErrorHandler, + (xmlRelaxNGValidityWarningFunc) testErrorHandler, + ctxt); + ret = xmlRelaxNGValidateDoc(ctxt, doc); + xmlRelaxNGFreeValidCtxt(ctxt); + if (ret > 0) { + test_log("Failed to validate valid instance line %ld\n", + xmlGetLineNo(tmp)); + nb_errors++; + } else if (ret < 0) { + test_log("Internal error validating instance line %ld\n", + xmlGetLineNo(tmp)); + nb_errors++; + } + xmlFreeDoc(doc); + } + xmlResetLastError(); + if ((mem != xmlMemUsed()) && (extraMemoryFromResolver == 0)) { + test_log("Validation of instance line %ld leaked %d\n", + xmlGetLineNo(tmp), xmlMemUsed() - mem); + xmlMemoryDump(); + nb_leaks++; + } + } + if (dtd != NULL) + xmlFree(dtd); + tmp = getNext(tmp, "following-sibling::valid[1]"); + } + /* + * now scan all the siblings of correct to process the tests + */ + tmp = getNext(cur, "following-sibling::invalid[1]"); + while (tmp != NULL) { + test = getNext(tmp, "./*"); + if (test == NULL) { + fprintf(stderr, "Failed to find test in line %ld\n", + xmlGetLineNo(tmp)); + + } else { + xmlBufferEmpty(buf); + xmlNodeDump(buf, test->doc, test, 0, 0); + + /* + * We are ready to run the test + */ + mem = xmlMemUsed(); + extraMemoryFromResolver = 0; + doc = xmlReadMemory((const char *)buf->content, buf->use, + "test", NULL, 0); + if (doc == NULL) { + test_log("Failed to parse valid instance line %ld\n", + xmlGetLineNo(tmp)); + nb_errors++; + } else { + nb_tests++; + ctxt = xmlRelaxNGNewValidCtxt(rng); + xmlRelaxNGSetValidErrors(ctxt, + (xmlRelaxNGValidityErrorFunc) testErrorHandler, + (xmlRelaxNGValidityWarningFunc) testErrorHandler, + ctxt); + ret = xmlRelaxNGValidateDoc(ctxt, doc); + xmlRelaxNGFreeValidCtxt(ctxt); + if (ret == 0) { + test_log("Failed to detect invalid instance line %ld\n", + xmlGetLineNo(tmp)); + nb_errors++; + } else if (ret < 0) { + test_log("Internal error validating instance line %ld\n", + xmlGetLineNo(tmp)); + nb_errors++; + } + xmlFreeDoc(doc); + } + xmlResetLastError(); + if ((mem != xmlMemUsed()) && (extraMemoryFromResolver == 0)) { + test_log("Validation of instance line %ld leaked %d\n", + xmlGetLineNo(tmp), xmlMemUsed() - mem); + xmlMemoryDump(); + nb_leaks++; + } + } + tmp = getNext(tmp, "following-sibling::invalid[1]"); + } + +done: + if (buf != NULL) + xmlBufferFree(buf); + if (rng != NULL) + xmlRelaxNGFree(rng); + xmlResetLastError(); + if ((memt != xmlMemUsed()) && (memt != 0)) { + test_log("Validation of tests starting line %ld leaked %d\n", + xmlGetLineNo(cur), xmlMemUsed() - memt); + nb_leaks++; + } + return(ret); +} + +static int +xsdTestSuite(xmlNodePtr cur) { + if (verbose) { + xmlChar *doc = getString(cur, "string(documentation)"); + + if (doc != NULL) { + printf("Suite %s\n", doc); + xmlFree(doc); + } + } + cur = getNext(cur, "./testCase[1]"); + while (cur != NULL) { + xsdTestCase(cur); + cur = getNext(cur, "following-sibling::testCase[1]"); + } + + return(0); +} + +static int +xsdTest(void) { + xmlDocPtr doc; + xmlNodePtr cur; + const char *filename = "test/xsdtest/xsdtestsuite.xml"; + int ret = 0; + + doc = xmlReadFile(filename, NULL, XML_PARSE_NOENT); + if (doc == NULL) { + fprintf(stderr, "Failed to parse %s\n", filename); + return(-1); + } + printf("## XML Schemas datatypes test suite from James Clark\n"); + + cur = xmlDocGetRootElement(doc); + if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) { + fprintf(stderr, "Unexpected format %s\n", filename); + ret = -1; + goto done; + } + + cur = getNext(cur, "./testSuite[1]"); + if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) { + fprintf(stderr, "Unexpected format %s\n", filename); + ret = -1; + goto done; + } + while (cur != NULL) { + xsdTestSuite(cur); + cur = getNext(cur, "following-sibling::testSuite[1]"); + } + +done: + if (doc != NULL) + xmlFreeDoc(doc); + return(ret); +} + +static int +rngTestSuite(xmlNodePtr cur) { + if (verbose) { + xmlChar *doc = getString(cur, "string(documentation)"); + + if (doc != NULL) { + printf("Suite %s\n", doc); + xmlFree(doc); + } else { + doc = getString(cur, "string(section)"); + if (doc != NULL) { + printf("Section %s\n", doc); + xmlFree(doc); + } + } + } + cur = getNext(cur, "./testSuite[1]"); + while (cur != NULL) { + xsdTestSuite(cur); + cur = getNext(cur, "following-sibling::testSuite[1]"); + } + + return(0); +} + +static int +rngTest1(void) { + xmlDocPtr doc; + xmlNodePtr cur; + const char *filename = "test/relaxng/OASIS/spectest.xml"; + int ret = 0; + + doc = xmlReadFile(filename, NULL, XML_PARSE_NOENT); + if (doc == NULL) { + fprintf(stderr, "Failed to parse %s\n", filename); + return(-1); + } + printf("## Relax NG test suite from James Clark\n"); + + cur = xmlDocGetRootElement(doc); + if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) { + fprintf(stderr, "Unexpected format %s\n", filename); + ret = -1; + goto done; + } + + cur = getNext(cur, "./testSuite[1]"); + if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) { + fprintf(stderr, "Unexpected format %s\n", filename); + ret = -1; + goto done; + } + while (cur != NULL) { + rngTestSuite(cur); + cur = getNext(cur, "following-sibling::testSuite[1]"); + } + +done: + if (doc != NULL) + xmlFreeDoc(doc); + return(ret); +} + +static int +rngTest2(void) { + xmlDocPtr doc; + xmlNodePtr cur; + const char *filename = "test/relaxng/testsuite.xml"; + int ret = 0; + + doc = xmlReadFile(filename, NULL, XML_PARSE_NOENT); + if (doc == NULL) { + fprintf(stderr, "Failed to parse %s\n", filename); + return(-1); + } + printf("## Relax NG test suite for libxml2\n"); + + cur = xmlDocGetRootElement(doc); + if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) { + fprintf(stderr, "Unexpected format %s\n", filename); + ret = -1; + goto done; + } + + cur = getNext(cur, "./testSuite[1]"); + if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) { + fprintf(stderr, "Unexpected format %s\n", filename); + ret = -1; + goto done; + } + while (cur != NULL) { + xsdTestSuite(cur); + cur = getNext(cur, "following-sibling::testSuite[1]"); + } + +done: + if (doc != NULL) + xmlFreeDoc(doc); + return(ret); +} + +/************************************************************************ + * * + * Schemas test suites from W3C/NIST/MS/Sun * + * * + ************************************************************************/ + +static int +xstcTestInstance(xmlNodePtr cur, xmlSchemaPtr schemas, + const xmlChar *spath, const char *base) { + xmlChar *href = NULL; + xmlChar *path = NULL; + xmlChar *validity = NULL; + xmlSchemaValidCtxtPtr ctxt = NULL; + xmlDocPtr doc = NULL; + int ret = 0, mem; + + xmlResetLastError(); + testErrorsSize = 0; testErrors[0] = 0; + mem = xmlMemUsed(); + href = getString(cur, + "string(ts:instanceDocument/@xlink:href)"); + if ((href == NULL) || (href[0] == 0)) { + test_log("testGroup line %ld misses href for schemaDocument\n", + xmlGetLineNo(cur)); + ret = -1; + goto done; + } + path = xmlBuildURI(href, BAD_CAST base); + if (path == NULL) { + fprintf(stderr, + "Failed to build path to schemas testGroup line %ld : %s\n", + xmlGetLineNo(cur), href); + ret = -1; + goto done; + } + if (checkTestFile((const char *) path) <= 0) { + test_log("schemas for testGroup line %ld is missing: %s\n", + xmlGetLineNo(cur), path); + ret = -1; + goto done; + } + validity = getString(cur, + "string(ts:expected/@validity)"); + if (validity == NULL) { + fprintf(stderr, "instanceDocument line %ld misses expected validity\n", + xmlGetLineNo(cur)); + ret = -1; + goto done; + } + nb_tests++; + doc = xmlReadFile((const char *) path, NULL, XML_PARSE_NOENT); + if (doc == NULL) { + fprintf(stderr, "instance %s fails to parse\n", path); + ret = -1; + nb_errors++; + goto done; + } + + ctxt = xmlSchemaNewValidCtxt(schemas); + xmlSchemaSetValidErrors(ctxt, + (xmlSchemaValidityErrorFunc) testErrorHandler, + (xmlSchemaValidityWarningFunc) testErrorHandler, + ctxt); + ret = xmlSchemaValidateDoc(ctxt, doc); + + if (xmlStrEqual(validity, BAD_CAST "valid")) { + if (ret > 0) { + test_log("valid instance %s failed to validate against %s\n", + path, spath); + nb_errors++; + } else if (ret < 0) { + test_log("valid instance %s got internal error validating %s\n", + path, spath); + nb_internals++; + nb_errors++; + } + } else if (xmlStrEqual(validity, BAD_CAST "invalid")) { + if (ret == 0) { + test_log("Failed to detect invalid instance %s against %s\n", + path, spath); + nb_errors++; + } + } else { + test_log("instanceDocument line %ld has unexpected validity value%s\n", + xmlGetLineNo(cur), validity); + ret = -1; + goto done; + } + +done: + if (href != NULL) xmlFree(href); + if (path != NULL) xmlFree(path); + if (validity != NULL) xmlFree(validity); + if (ctxt != NULL) xmlSchemaFreeValidCtxt(ctxt); + if (doc != NULL) xmlFreeDoc(doc); + xmlResetLastError(); + if (mem != xmlMemUsed()) { + test_log("Validation of tests starting line %ld leaked %d\n", + xmlGetLineNo(cur), xmlMemUsed() - mem); + nb_leaks++; + } + return(ret); +} + +static int +xstcTestGroup(xmlNodePtr cur, const char *base) { + xmlChar *href = NULL; + xmlChar *path = NULL; + xmlChar *validity = NULL; + xmlSchemaPtr schemas = NULL; + xmlSchemaParserCtxtPtr ctxt; + xmlNodePtr instance; + int ret = 0, mem; + + xmlResetLastError(); + testErrorsSize = 0; testErrors[0] = 0; + mem = xmlMemUsed(); + href = getString(cur, + "string(ts:schemaTest/ts:schemaDocument/@xlink:href)"); + if ((href == NULL) || (href[0] == 0)) { + test_log("testGroup line %ld misses href for schemaDocument\n", + xmlGetLineNo(cur)); + ret = -1; + goto done; + } + path = xmlBuildURI(href, BAD_CAST base); + if (path == NULL) { + test_log("Failed to build path to schemas testGroup line %ld : %s\n", + xmlGetLineNo(cur), href); + ret = -1; + goto done; + } + if (checkTestFile((const char *) path) <= 0) { + test_log("schemas for testGroup line %ld is missing: %s\n", + xmlGetLineNo(cur), path); + ret = -1; + goto done; + } + validity = getString(cur, + "string(ts:schemaTest/ts:expected/@validity)"); + if (validity == NULL) { + test_log("testGroup line %ld misses expected validity\n", + xmlGetLineNo(cur)); + ret = -1; + goto done; + } + nb_tests++; + if (xmlStrEqual(validity, BAD_CAST "valid")) { + nb_schematas++; + ctxt = xmlSchemaNewParserCtxt((const char *) path); + xmlSchemaSetParserErrors(ctxt, + (xmlSchemaValidityErrorFunc) testErrorHandler, + (xmlSchemaValidityWarningFunc) testErrorHandler, + ctxt); + schemas = xmlSchemaParse(ctxt); + xmlSchemaFreeParserCtxt(ctxt); + if (schemas == NULL) { + test_log("valid schemas %s failed to parse\n", + path); + ret = 1; + nb_errors++; + } + if ((ret == 0) && (strstr(testErrors, "nimplemented") != NULL)) { + test_log("valid schemas %s hit an unimplemented block\n", + path); + ret = 1; + nb_unimplemented++; + nb_errors++; + } + instance = getNext(cur, "./ts:instanceTest[1]"); + while (instance != NULL) { + if (schemas != NULL) { + xstcTestInstance(instance, schemas, path, base); + } else { + /* + * We'll automatically mark the instances as failed + * if the schema was broken. + */ + nb_errors++; + } + instance = getNext(instance, + "following-sibling::ts:instanceTest[1]"); + } + } else if (xmlStrEqual(validity, BAD_CAST "invalid")) { + nb_schematas++; + ctxt = xmlSchemaNewParserCtxt((const char *) path); + xmlSchemaSetParserErrors(ctxt, + (xmlSchemaValidityErrorFunc) testErrorHandler, + (xmlSchemaValidityWarningFunc) testErrorHandler, + ctxt); + schemas = xmlSchemaParse(ctxt); + xmlSchemaFreeParserCtxt(ctxt); + if (schemas != NULL) { + test_log("Failed to detect error in schemas %s\n", + path); + nb_errors++; + ret = 1; + } + if ((ret == 0) && (strstr(testErrors, "nimplemented") != NULL)) { + nb_unimplemented++; + test_log("invalid schemas %s hit an unimplemented block\n", + path); + ret = 1; + nb_errors++; + } + } else { + test_log("testGroup line %ld misses unexpected validity value%s\n", + xmlGetLineNo(cur), validity); + ret = -1; + goto done; + } + +done: + if (href != NULL) xmlFree(href); + if (path != NULL) xmlFree(path); + if (validity != NULL) xmlFree(validity); + if (schemas != NULL) xmlSchemaFree(schemas); + xmlResetLastError(); + if ((mem != xmlMemUsed()) && (extraMemoryFromResolver == 0)) { + test_log("Processing test line %ld %s leaked %d\n", + xmlGetLineNo(cur), path, xmlMemUsed() - mem); + nb_leaks++; + } + return(ret); +} + +static int +xstcMetadata(const char *metadata, const char *base) { + xmlDocPtr doc; + xmlNodePtr cur; + xmlChar *contributor; + xmlChar *name; + int ret = 0; + + doc = xmlReadFile(metadata, NULL, XML_PARSE_NOENT); + if (doc == NULL) { + fprintf(stderr, "Failed to parse %s\n", metadata); + return(-1); + } + + cur = xmlDocGetRootElement(doc); + if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSet"))) { + fprintf(stderr, "Unexpected format %s\n", metadata); + return(-1); + } + contributor = xmlGetProp(cur, BAD_CAST "contributor"); + if (contributor == NULL) { + contributor = xmlStrdup(BAD_CAST "Unknown"); + } + name = xmlGetProp(cur, BAD_CAST "name"); + if (name == NULL) { + name = xmlStrdup(BAD_CAST "Unknown"); + } + printf("## %s test suite for Schemas version %s\n", contributor, name); + xmlFree(contributor); + xmlFree(name); + + cur = getNext(cur, "./ts:testGroup[1]"); + if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testGroup"))) { + fprintf(stderr, "Unexpected format %s\n", metadata); + ret = -1; + goto done; + } + while (cur != NULL) { + xstcTestGroup(cur, base); + cur = getNext(cur, "following-sibling::ts:testGroup[1]"); + } + +done: + xmlFreeDoc(doc); + return(ret); +} + +/************************************************************************ + * * + * The driver for the tests * + * * + ************************************************************************/ + +int +main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { + int ret = 0; + int old_errors, old_tests, old_leaks; + + logfile = fopen(LOGFILE, "w"); + if (logfile == NULL) { + fprintf(stderr, + "Could not open the log file, running in verbose mode\n"); + verbose = 1; + } + initializeLibxml2(); + + if ((argc >= 2) && (!strcmp(argv[1], "-v"))) + verbose = 1; + + + old_errors = nb_errors; + old_tests = nb_tests; + old_leaks = nb_leaks; + xsdTest(); + if ((nb_errors == old_errors) && (nb_leaks == old_leaks)) + printf("Ran %d tests, no errors\n", nb_tests - old_tests); + else + printf("Ran %d tests, %d errors, %d leaks\n", + nb_tests - old_tests, + nb_errors - old_errors, + nb_leaks - old_leaks); + old_errors = nb_errors; + old_tests = nb_tests; + old_leaks = nb_leaks; + rngTest1(); + if ((nb_errors == old_errors) && (nb_leaks == old_leaks)) + printf("Ran %d tests, no errors\n", nb_tests - old_tests); + else + printf("Ran %d tests, %d errors, %d leaks\n", + nb_tests - old_tests, + nb_errors - old_errors, + nb_leaks - old_leaks); + old_errors = nb_errors; + old_tests = nb_tests; + old_leaks = nb_leaks; + rngTest2(); + if ((nb_errors == old_errors) && (nb_leaks == old_leaks)) + printf("Ran %d tests, no errors\n", nb_tests - old_tests); + else + printf("Ran %d tests, %d errors, %d leaks\n", + nb_tests - old_tests, + nb_errors - old_errors, + nb_leaks - old_leaks); + old_errors = nb_errors; + old_tests = nb_tests; + old_leaks = nb_leaks; + nb_internals = 0; + nb_schematas = 0; + xstcMetadata("xstc/Tests/Metadata/NISTXMLSchemaDatatypes.testSet", + "xstc/Tests/Metadata/"); + if ((nb_errors == old_errors) && (nb_leaks == old_leaks)) + printf("Ran %d tests (%d schemata), no errors\n", + nb_tests - old_tests, nb_schematas); + else + printf("Ran %d tests (%d schemata), %d errors (%d internals), %d leaks\n", + nb_tests - old_tests, + nb_schematas, + nb_errors - old_errors, + nb_internals, + nb_leaks - old_leaks); + old_errors = nb_errors; + old_tests = nb_tests; + old_leaks = nb_leaks; + nb_internals = 0; + nb_schematas = 0; + xstcMetadata("xstc/Tests/Metadata/SunXMLSchema1-0-20020116.testSet", + "xstc/Tests/"); + if ((nb_errors == old_errors) && (nb_leaks == old_leaks)) + printf("Ran %d tests (%d schemata), no errors\n", + nb_tests - old_tests, nb_schematas); + else + printf("Ran %d tests (%d schemata), %d errors (%d internals), %d leaks\n", + nb_tests - old_tests, + nb_schematas, + nb_errors - old_errors, + nb_internals, + nb_leaks - old_leaks); + old_errors = nb_errors; + old_tests = nb_tests; + old_leaks = nb_leaks; + nb_internals = 0; + nb_schematas = 0; + xstcMetadata("xstc/Tests/Metadata/MSXMLSchema1-0-20020116.testSet", + "xstc/Tests/"); + if ((nb_errors == old_errors) && (nb_leaks == old_leaks)) + printf("Ran %d tests (%d schemata), no errors\n", + nb_tests - old_tests, nb_schematas); + else + printf("Ran %d tests (%d schemata), %d errors (%d internals), %d leaks\n", + nb_tests - old_tests, + nb_schematas, + nb_errors - old_errors, + nb_internals, + nb_leaks - old_leaks); + + if ((nb_errors == 0) && (nb_leaks == 0)) { + ret = 0; + printf("Total %d tests, no errors\n", + nb_tests); + } else { + ret = 1; + printf("Total %d tests, %d errors, %d leaks\n", + nb_tests, nb_errors, nb_leaks); + } + xmlXPathFreeContext(ctxtXPath); + xmlCleanupParser(); + xmlMemoryDump(); + + if (logfile != NULL) + fclose(logfile); + return(ret); +} +#else /* !SCHEMAS */ +int +main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { + fprintf(stderr, "runsuite requires support for schemas and xpath in libxml2\n"); +} +#endif diff --git a/Extras/LibXML/runtest.c b/Extras/LibXML/runtest.c new file mode 100644 index 000000000..cf0fb63a3 --- /dev/null +++ b/Extras/LibXML/runtest.c @@ -0,0 +1,4425 @@ +/* + * runtest.c: C program to run libxml2 regression tests without + * requiring make or Python, and reducing platform dependancies + * to a strict minimum. + * + * To compile on Unixes: + * cc -o runtest `xml2-config --cflags` runtest.c `xml2-config --libs` -lpthread + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#ifdef HAVE_CONFIG_H +#include "libxml.h" +#else +#include +#endif + +#if !defined(_WIN32) || defined(__CYGWIN__) +#include +#endif +#include +#include +#include +#include + +#include +#include +#include + +#ifdef LIBXML_OUTPUT_ENABLED +#ifdef LIBXML_READER_ENABLED +#include +#endif + +#ifdef LIBXML_XINCLUDE_ENABLED +#include +#endif + +#ifdef LIBXML_XPATH_ENABLED +#include +#include +#ifdef LIBXML_XPTR_ENABLED +#include +#endif +#endif + +#ifdef LIBXML_SCHEMAS_ENABLED +#include +#include +#include +#endif + +#ifdef LIBXML_PATTERN_ENABLED +#include +#endif + +#ifdef LIBXML_C14N_ENABLED +#include +#endif + +#ifdef LIBXML_HTML_ENABLED +#include +#include + +/* + * pseudo flag for the unification of HTML and XML tests + */ +#define XML_PARSE_HTML 1 << 24 +#endif + +#if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED) +#include +#include +#include +#include +#include +#endif + +/* + * O_BINARY is just for Windows compatibility - if it isn't defined + * on this system, avoid any compilation error + */ +#ifdef O_BINARY +#define RD_FLAGS O_RDONLY | O_BINARY +#else +#define RD_FLAGS O_RDONLY +#endif + +typedef int (*functest) (const char *filename, const char *result, + const char *error, int options); + +typedef struct testDesc testDesc; +typedef testDesc *testDescPtr; +struct testDesc { + const char *desc; /* descripton of the test */ + functest func; /* function implementing the test */ + const char *in; /* glob to path for input files */ + const char *out; /* output directory */ + const char *suffix;/* suffix for output files */ + const char *err; /* suffix for error output files */ + int options; /* parser options for the test */ +}; + +static int checkTestFile(const char *filename); + +#if defined(_WIN32) && !defined(__CYGWIN__) + +#include +#include + +typedef struct +{ + size_t gl_pathc; /* Count of paths matched so far */ + char **gl_pathv; /* List of matched pathnames. */ + size_t gl_offs; /* Slots to reserve in 'gl_pathv'. */ +} glob_t; + +#define GLOB_DOOFFS 0 +static int glob(const char *pattern, int flags, + int errfunc(const char *epath, int eerrno), + glob_t *pglob) { + glob_t *ret; + WIN32_FIND_DATA FindFileData; + HANDLE hFind; + unsigned int nb_paths = 0; + char directory[500]; + int len; + + if ((pattern == NULL) || (pglob == NULL)) return(-1); + + strncpy(directory, pattern, 499); + for (len = strlen(directory);len >= 0;len--) { + if (directory[len] == '/') { + len++; + directory[len] = 0; + break; + } + } + if (len <= 0) + len = 0; + + + ret = pglob; + memset(ret, 0, sizeof(glob_t)); + + hFind = FindFirstFileA(pattern, &FindFileData); + if (hFind == INVALID_HANDLE_VALUE) + return(0); + nb_paths = 20; + ret->gl_pathv = (char **) malloc(nb_paths * sizeof(char *)); + if (ret->gl_pathv == NULL) { + FindClose(hFind); + return(-1); + } + strncpy(directory + len, FindFileData.cFileName, 499 - len); + ret->gl_pathv[ret->gl_pathc] = strdup(directory); + if (ret->gl_pathv[ret->gl_pathc] == NULL) + goto done; + ret->gl_pathc++; + while(FindNextFileA(hFind, &FindFileData)) { + if (FindFileData.cFileName[0] == '.') + continue; + if (ret->gl_pathc + 2 > nb_paths) { + char **tmp = realloc(ret->gl_pathv, nb_paths * 2 * sizeof(char *)); + if (tmp == NULL) + break; + ret->gl_pathv = tmp; + nb_paths *= 2; + } + strncpy(directory + len, FindFileData.cFileName, 499 - len); + ret->gl_pathv[ret->gl_pathc] = strdup(directory); + if (ret->gl_pathv[ret->gl_pathc] == NULL) + break; + ret->gl_pathc++; + } + ret->gl_pathv[ret->gl_pathc] = NULL; + +done: + FindClose(hFind); + return(0); +} + + + +static void globfree(glob_t *pglob) { + unsigned int i; + if (pglob == NULL) + return; + + for (i = 0;i < pglob->gl_pathc;i++) { + if (pglob->gl_pathv[i] != NULL) + free(pglob->gl_pathv[i]); + } +} +#define vsnprintf _vsnprintf +#define snprintf _snprintf +#else +#include +#endif + +/************************************************************************ + * * + * Libxml2 specific routines * + * * + ************************************************************************/ + +static int nb_tests = 0; +static int nb_errors = 0; +static int nb_leaks = 0; +static int extraMemoryFromResolver = 0; + +static int +fatalError(void) { + fprintf(stderr, "Exitting tests on fatal error\n"); + exit(1); +} + +/* + * We need to trap calls to the resolver to not account memory for the catalog + * which is shared to the current running test. We also don't want to have + * network downloads modifying tests. + */ +static xmlParserInputPtr +testExternalEntityLoader(const char *URL, const char *ID, + xmlParserCtxtPtr ctxt) { + xmlParserInputPtr ret; + + if (checkTestFile(URL)) { + ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt); + } else { + int memused = xmlMemUsed(); + ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt); + extraMemoryFromResolver += xmlMemUsed() - memused; + } + + return(ret); +} + +/* + * Trapping the error messages at the generic level to grab the equivalent of + * stderr messages on CLI tools. + */ +static char testErrors[32769]; +static int testErrorsSize = 0; + +static void XMLCDECL +testErrorHandler(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) { + va_list args; + int res; + + if (testErrorsSize >= 32768) + return; + va_start(args, msg); + res = vsnprintf(&testErrors[testErrorsSize], + 32768 - testErrorsSize, + msg, args); + va_end(args); + if (testErrorsSize + res >= 32768) { + /* buffer is full */ + testErrorsSize = 32768; + testErrors[testErrorsSize] = 0; + } else { + testErrorsSize += res; + } + testErrors[testErrorsSize] = 0; +} + +static void XMLCDECL +channel(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) { + va_list args; + int res; + + if (testErrorsSize >= 32768) + return; + va_start(args, msg); + res = vsnprintf(&testErrors[testErrorsSize], + 32768 - testErrorsSize, + msg, args); + va_end(args); + if (testErrorsSize + res >= 32768) { + /* buffer is full */ + testErrorsSize = 32768; + testErrors[testErrorsSize] = 0; + } else { + testErrorsSize += res; + } + testErrors[testErrorsSize] = 0; +} + +/** + * xmlParserPrintFileContext: + * @input: an xmlParserInputPtr input + * + * Displays current context within the input content for error tracking + */ + +static void +xmlParserPrintFileContextInternal(xmlParserInputPtr input , + xmlGenericErrorFunc chanl, void *data ) { + const xmlChar *cur, *base; + unsigned int n, col; /* GCC warns if signed, because compared with sizeof() */ + xmlChar content[81]; /* space for 80 chars + line terminator */ + xmlChar *ctnt; + + if (input == NULL) return; + cur = input->cur; + base = input->base; + /* skip backwards over any end-of-lines */ + while ((cur > base) && ((*(cur) == '\n') || (*(cur) == '\r'))) { + cur--; + } + n = 0; + /* search backwards for beginning-of-line (to max buff size) */ + while ((n++ < (sizeof(content)-1)) && (cur > base) && + (*(cur) != '\n') && (*(cur) != '\r')) + cur--; + if ((*(cur) == '\n') || (*(cur) == '\r')) cur++; + /* calculate the error position in terms of the current position */ + col = input->cur - cur; + /* search forward for end-of-line (to max buff size) */ + n = 0; + ctnt = content; + /* copy selected text to our buffer */ + while ((*cur != 0) && (*(cur) != '\n') && + (*(cur) != '\r') && (n < sizeof(content)-1)) { + *ctnt++ = *cur++; + n++; + } + *ctnt = 0; + /* print out the selected text */ + chanl(data ,"%s\n", content); + /* create blank line with problem pointer */ + n = 0; + ctnt = content; + /* (leave buffer space for pointer + line terminator) */ + while ((nfile; + line = err->line; + code = err->code; + domain = err->domain; + level = err->level; + node = err->node; + if ((domain == XML_FROM_PARSER) || (domain == XML_FROM_HTML) || + (domain == XML_FROM_DTD) || (domain == XML_FROM_NAMESPACE) || + (domain == XML_FROM_IO) || (domain == XML_FROM_VALID)) { + ctxt = err->ctxt; + } + str = err->message; + + if (code == XML_ERR_OK) + return; + + if ((node != NULL) && (node->type == XML_ELEMENT_NODE)) + name = node->name; + + /* + * Maintain the compatibility with the legacy error handling + */ + if (ctxt != NULL) { + input = ctxt->input; + if ((input != NULL) && (input->filename == NULL) && + (ctxt->inputNr > 1)) { + cur = input; + input = ctxt->inputTab[ctxt->inputNr - 2]; + } + if (input != NULL) { + if (input->filename) + channel(data, "%s:%d: ", input->filename, input->line); + else if ((line != 0) && (domain == XML_FROM_PARSER)) + channel(data, "Entity: line %d: ", input->line); + } + } else { + if (file != NULL) + channel(data, "%s:%d: ", file, line); + else if ((line != 0) && (domain == XML_FROM_PARSER)) + channel(data, "Entity: line %d: ", line); + } + if (name != NULL) { + channel(data, "element %s: ", name); + } + if (code == XML_ERR_OK) + return; + switch (domain) { + case XML_FROM_PARSER: + channel(data, "parser "); + break; + case XML_FROM_NAMESPACE: + channel(data, "namespace "); + break; + case XML_FROM_DTD: + case XML_FROM_VALID: + channel(data, "validity "); + break; + case XML_FROM_HTML: + channel(data, "HTML parser "); + break; + case XML_FROM_MEMORY: + channel(data, "memory "); + break; + case XML_FROM_OUTPUT: + channel(data, "output "); + break; + case XML_FROM_IO: + channel(data, "I/O "); + break; + case XML_FROM_XINCLUDE: + channel(data, "XInclude "); + break; + case XML_FROM_XPATH: + channel(data, "XPath "); + break; + case XML_FROM_XPOINTER: + channel(data, "parser "); + break; + case XML_FROM_REGEXP: + channel(data, "regexp "); + break; + case XML_FROM_MODULE: + channel(data, "module "); + break; + case XML_FROM_SCHEMASV: + channel(data, "Schemas validity "); + break; + case XML_FROM_SCHEMASP: + channel(data, "Schemas parser "); + break; + case XML_FROM_RELAXNGP: + channel(data, "Relax-NG parser "); + break; + case XML_FROM_RELAXNGV: + channel(data, "Relax-NG validity "); + break; + case XML_FROM_CATALOG: + channel(data, "Catalog "); + break; + case XML_FROM_C14N: + channel(data, "C14N "); + break; + case XML_FROM_XSLT: + channel(data, "XSLT "); + break; + default: + break; + } + if (code == XML_ERR_OK) + return; + switch (level) { + case XML_ERR_NONE: + channel(data, ": "); + break; + case XML_ERR_WARNING: + channel(data, "warning : "); + break; + case XML_ERR_ERROR: + channel(data, "error : "); + break; + case XML_ERR_FATAL: + channel(data, "error : "); + break; + } + if (code == XML_ERR_OK) + return; + if (str != NULL) { + int len; + len = xmlStrlen((const xmlChar *)str); + if ((len > 0) && (str[len - 1] != '\n')) + channel(data, "%s\n", str); + else + channel(data, "%s", str); + } else { + channel(data, "%s\n", "out of memory error"); + } + if (code == XML_ERR_OK) + return; + + if (ctxt != NULL) { + xmlParserPrintFileContextInternal(input, channel, data); + if (cur != NULL) { + if (cur->filename) + channel(data, "%s:%d: \n", cur->filename, cur->line); + else if ((line != 0) && (domain == XML_FROM_PARSER)) + channel(data, "Entity: line %d: \n", cur->line); + xmlParserPrintFileContextInternal(cur, channel, data); + } + } + if ((domain == XML_FROM_XPATH) && (err->str1 != NULL) && + (err->int1 < 100) && + (err->int1 < xmlStrlen((const xmlChar *)err->str1))) { + xmlChar buf[150]; + int i; + + channel(data, "%s\n", err->str1); + for (i=0;i < err->int1;i++) + buf[i] = ' '; + buf[i++] = '^'; + buf[i] = 0; + channel(data, "%s\n", buf); + } +} + +static void +initializeLibxml2(void) { + xmlGetWarningsDefaultValue = 0; + xmlPedanticParserDefault(0); + + xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup); + xmlInitParser(); + xmlSetExternalEntityLoader(testExternalEntityLoader); + xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler); +#ifdef LIBXML_SCHEMAS_ENABLED + xmlSchemaInitTypes(); + xmlRelaxNGInitTypes(); +#endif +} + + +/************************************************************************ + * * + * File name and path utilities * + * * + ************************************************************************/ + +static const char *baseFilename(const char *filename) { + const char *cur; + if (filename == NULL) + return(NULL); + cur = &filename[strlen(filename)]; + while ((cur > filename) && (*cur != '/')) + cur--; + if (*cur == '/') + return(cur + 1); + return(cur); +} + +static char *resultFilename(const char *filename, const char *out, + const char *suffix) { + const char *base; + char res[500]; + +/************* + if ((filename[0] == 't') && (filename[1] == 'e') && + (filename[2] == 's') && (filename[3] == 't') && + (filename[4] == '/')) + filename = &filename[5]; + *************/ + + base = baseFilename(filename); + if (suffix == NULL) + suffix = ".tmp"; + if (out == NULL) + out = ""; + snprintf(res, 499, "%s%s%s", out, base, suffix); + res[499] = 0; + return(strdup(res)); +} + +static int checkTestFile(const char *filename) { + struct stat buf; + + if (stat(filename, &buf) == -1) + return(0); + +#if defined(_WIN32) && !defined(__CYGWIN__) + if (!(buf.st_mode & _S_IFREG)) + return(0); +#else + if (!S_ISREG(buf.st_mode)) + return(0); +#endif + + return(1); +} + +static int compareFiles(const char *r1, const char *r2) { + int res1, res2; + int fd1, fd2; + char bytes1[4096]; + char bytes2[4096]; + + fd1 = open(r1, RD_FLAGS); + if (fd1 < 0) + return(-1); + fd2 = open(r2, RD_FLAGS); + if (fd2 < 0) { + close(fd1); + return(-1); + } + while (1) { + res1 = read(fd1, bytes1, 4096); + res2 = read(fd2, bytes2, 4096); + if ((res1 != res2) || (res1 < 0)) { + close(fd1); + close(fd2); + return(1); + } + if (res1 == 0) + break; + if (memcmp(bytes1, bytes2, res1) != 0) { + close(fd1); + close(fd2); + return(1); + } + } + close(fd1); + close(fd2); + return(0); +} + +static int compareFileMem(const char *filename, const char *mem, int size) { + int res; + int fd; + char bytes[4096]; + int idx = 0; + struct stat info; + + if (stat(filename, &info) < 0) + return(-1); + if (info.st_size != size) + return(-1); + fd = open(filename, RD_FLAGS); + if (fd < 0) + return(-1); + while (idx < size) { + res = read(fd, bytes, 4096); + if (res <= 0) + break; + if (res + idx > size) + break; + if (memcmp(bytes, &mem[idx], res) != 0) { + int ix; + for (ix=0; ix 0) { + siz += res; + } + close(fd); +#if !defined(_WIN32) + if (siz != info.st_size) { + free(base); + return(-1); + } +#endif + base[siz] = 0; + *mem = base; + *size = siz; + return(0); +} + +static int unloadMem(const char *mem) { + free((char *)mem); + return(0); +} + +/************************************************************************ + * * + * Tests implementations * + * * + ************************************************************************/ + +/************************************************************************ + * * + * Parse to SAX based tests * + * * + ************************************************************************/ + +static FILE *SAXdebug = NULL; + +/* + * empty SAX block + */ +static xmlSAXHandler emptySAXHandlerStruct = { + NULL, /* internalSubset */ + NULL, /* isStandalone */ + NULL, /* hasInternalSubset */ + NULL, /* hasExternalSubset */ + NULL, /* resolveEntity */ + NULL, /* getEntity */ + NULL, /* entityDecl */ + NULL, /* notationDecl */ + NULL, /* attributeDecl */ + NULL, /* elementDecl */ + NULL, /* unparsedEntityDecl */ + NULL, /* setDocumentLocator */ + NULL, /* startDocument */ + NULL, /* endDocument */ + NULL, /* startElement */ + NULL, /* endElement */ + NULL, /* reference */ + NULL, /* characters */ + NULL, /* ignorableWhitespace */ + NULL, /* processingInstruction */ + NULL, /* comment */ + NULL, /* xmlParserWarning */ + NULL, /* xmlParserError */ + NULL, /* xmlParserError */ + NULL, /* getParameterEntity */ + NULL, /* cdataBlock; */ + NULL, /* externalSubset; */ + 1, + NULL, + NULL, /* startElementNs */ + NULL, /* endElementNs */ + NULL /* xmlStructuredErrorFunc */ +}; + +static xmlSAXHandlerPtr emptySAXHandler = &emptySAXHandlerStruct; +static int callbacks = 0; +static int quiet = 0; + +/** + * isStandaloneDebug: + * @ctxt: An XML parser context + * + * Is this document tagged standalone ? + * + * Returns 1 if true + */ +static int +isStandaloneDebug(void *ctx ATTRIBUTE_UNUSED) +{ + callbacks++; + if (quiet) + return(0); + fprintf(SAXdebug, "SAX.isStandalone()\n"); + return(0); +} + +/** + * hasInternalSubsetDebug: + * @ctxt: An XML parser context + * + * Does this document has an internal subset + * + * Returns 1 if true + */ +static int +hasInternalSubsetDebug(void *ctx ATTRIBUTE_UNUSED) +{ + callbacks++; + if (quiet) + return(0); + fprintf(SAXdebug, "SAX.hasInternalSubset()\n"); + return(0); +} + +/** + * hasExternalSubsetDebug: + * @ctxt: An XML parser context + * + * Does this document has an external subset + * + * Returns 1 if true + */ +static int +hasExternalSubsetDebug(void *ctx ATTRIBUTE_UNUSED) +{ + callbacks++; + if (quiet) + return(0); + fprintf(SAXdebug, "SAX.hasExternalSubset()\n"); + return(0); +} + +/** + * internalSubsetDebug: + * @ctxt: An XML parser context + * + * Does this document has an internal subset + */ +static void +internalSubsetDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, + const xmlChar *ExternalID, const xmlChar *SystemID) +{ + callbacks++; + if (quiet) + return; + fprintf(SAXdebug, "SAX.internalSubset(%s,", name); + if (ExternalID == NULL) + fprintf(SAXdebug, " ,"); + else + fprintf(SAXdebug, " %s,", ExternalID); + if (SystemID == NULL) + fprintf(SAXdebug, " )\n"); + else + fprintf(SAXdebug, " %s)\n", SystemID); +} + +/** + * externalSubsetDebug: + * @ctxt: An XML parser context + * + * Does this document has an external subset + */ +static void +externalSubsetDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, + const xmlChar *ExternalID, const xmlChar *SystemID) +{ + callbacks++; + if (quiet) + return; + fprintf(SAXdebug, "SAX.externalSubset(%s,", name); + if (ExternalID == NULL) + fprintf(SAXdebug, " ,"); + else + fprintf(SAXdebug, " %s,", ExternalID); + if (SystemID == NULL) + fprintf(SAXdebug, " )\n"); + else + fprintf(SAXdebug, " %s)\n", SystemID); +} + +/** + * resolveEntityDebug: + * @ctxt: An XML parser context + * @publicId: The public ID of the entity + * @systemId: The system ID of the entity + * + * Special entity resolver, better left to the parser, it has + * more context than the application layer. + * The default behaviour is to NOT resolve the entities, in that case + * the ENTITY_REF nodes are built in the structure (and the parameter + * values). + * + * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour. + */ +static xmlParserInputPtr +resolveEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *publicId, const xmlChar *systemId) +{ + callbacks++; + if (quiet) + return(NULL); + /* xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; */ + + + fprintf(SAXdebug, "SAX.resolveEntity("); + if (publicId != NULL) + fprintf(SAXdebug, "%s", (char *)publicId); + else + fprintf(SAXdebug, " "); + if (systemId != NULL) + fprintf(SAXdebug, ", %s)\n", (char *)systemId); + else + fprintf(SAXdebug, ", )\n"); +/********* + if (systemId != NULL) { + return(xmlNewInputFromFile(ctxt, (char *) systemId)); + } + *********/ + return(NULL); +} + +/** + * getEntityDebug: + * @ctxt: An XML parser context + * @name: The entity name + * + * Get an entity by name + * + * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour. + */ +static xmlEntityPtr +getEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name) +{ + callbacks++; + if (quiet) + return(NULL); + fprintf(SAXdebug, "SAX.getEntity(%s)\n", name); + return(NULL); +} + +/** + * getParameterEntityDebug: + * @ctxt: An XML parser context + * @name: The entity name + * + * Get a parameter entity by name + * + * Returns the xmlParserInputPtr + */ +static xmlEntityPtr +getParameterEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name) +{ + callbacks++; + if (quiet) + return(NULL); + fprintf(SAXdebug, "SAX.getParameterEntity(%s)\n", name); + return(NULL); +} + + +/** + * entityDeclDebug: + * @ctxt: An XML parser context + * @name: the entity name + * @type: the entity type + * @publicId: The public ID of the entity + * @systemId: The system ID of the entity + * @content: the entity value (without processing). + * + * An entity definition has been parsed + */ +static void +entityDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, int type, + const xmlChar *publicId, const xmlChar *systemId, xmlChar *content) +{ +const xmlChar *nullstr = BAD_CAST "(null)"; + /* not all libraries handle printing null pointers nicely */ + if (publicId == NULL) + publicId = nullstr; + if (systemId == NULL) + systemId = nullstr; + if (content == NULL) + content = (xmlChar *)nullstr; + callbacks++; + if (quiet) + return; + fprintf(SAXdebug, "SAX.entityDecl(%s, %d, %s, %s, %s)\n", + name, type, publicId, systemId, content); +} + +/** + * attributeDeclDebug: + * @ctxt: An XML parser context + * @name: the attribute name + * @type: the attribute type + * + * An attribute definition has been parsed + */ +static void +attributeDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar * elem, + const xmlChar * name, int type, int def, + const xmlChar * defaultValue, xmlEnumerationPtr tree) +{ + callbacks++; + if (quiet) + return; + if (defaultValue == NULL) + fprintf(SAXdebug, "SAX.attributeDecl(%s, %s, %d, %d, NULL, ...)\n", + elem, name, type, def); + else + fprintf(SAXdebug, "SAX.attributeDecl(%s, %s, %d, %d, %s, ...)\n", + elem, name, type, def, defaultValue); + xmlFreeEnumeration(tree); +} + +/** + * elementDeclDebug: + * @ctxt: An XML parser context + * @name: the element name + * @type: the element type + * @content: the element value (without processing). + * + * An element definition has been parsed + */ +static void +elementDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, int type, + xmlElementContentPtr content ATTRIBUTE_UNUSED) +{ + callbacks++; + if (quiet) + return; + fprintf(SAXdebug, "SAX.elementDecl(%s, %d, ...)\n", + name, type); +} + +/** + * notationDeclDebug: + * @ctxt: An XML parser context + * @name: The name of the notation + * @publicId: The public ID of the entity + * @systemId: The system ID of the entity + * + * What to do when a notation declaration has been parsed. + */ +static void +notationDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, + const xmlChar *publicId, const xmlChar *systemId) +{ + callbacks++; + if (quiet) + return; + fprintf(SAXdebug, "SAX.notationDecl(%s, %s, %s)\n", + (char *) name, (char *) publicId, (char *) systemId); +} + +/** + * unparsedEntityDeclDebug: + * @ctxt: An XML parser context + * @name: The name of the entity + * @publicId: The public ID of the entity + * @systemId: The system ID of the entity + * @notationName: the name of the notation + * + * What to do when an unparsed entity declaration is parsed + */ +static void +unparsedEntityDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, + const xmlChar *publicId, const xmlChar *systemId, + const xmlChar *notationName) +{ +const xmlChar *nullstr = BAD_CAST "(null)"; + + if (publicId == NULL) + publicId = nullstr; + if (systemId == NULL) + systemId = nullstr; + if (notationName == NULL) + notationName = nullstr; + callbacks++; + if (quiet) + return; + fprintf(SAXdebug, "SAX.unparsedEntityDecl(%s, %s, %s, %s)\n", + (char *) name, (char *) publicId, (char *) systemId, + (char *) notationName); +} + +/** + * setDocumentLocatorDebug: + * @ctxt: An XML parser context + * @loc: A SAX Locator + * + * Receive the document locator at startup, actually xmlDefaultSAXLocator + * Everything is available on the context, so this is useless in our case. + */ +static void +setDocumentLocatorDebug(void *ctx ATTRIBUTE_UNUSED, xmlSAXLocatorPtr loc ATTRIBUTE_UNUSED) +{ + callbacks++; + if (quiet) + return; + fprintf(SAXdebug, "SAX.setDocumentLocator()\n"); +} + +/** + * startDocumentDebug: + * @ctxt: An XML parser context + * + * called when the document start being processed. + */ +static void +startDocumentDebug(void *ctx ATTRIBUTE_UNUSED) +{ + callbacks++; + if (quiet) + return; + fprintf(SAXdebug, "SAX.startDocument()\n"); +} + +/** + * endDocumentDebug: + * @ctxt: An XML parser context + * + * called when the document end has been detected. + */ +static void +endDocumentDebug(void *ctx ATTRIBUTE_UNUSED) +{ + callbacks++; + if (quiet) + return; + fprintf(SAXdebug, "SAX.endDocument()\n"); +} + +/** + * startElementDebug: + * @ctxt: An XML parser context + * @name: The element name + * + * called when an opening tag has been processed. + */ +static void +startElementDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, const xmlChar **atts) +{ + int i; + + callbacks++; + if (quiet) + return; + fprintf(SAXdebug, "SAX.startElement(%s", (char *) name); + if (atts != NULL) { + for (i = 0;(atts[i] != NULL);i++) { + fprintf(SAXdebug, ", %s='", atts[i++]); + if (atts[i] != NULL) + fprintf(SAXdebug, "%s'", atts[i]); + } + } + fprintf(SAXdebug, ")\n"); +} + +/** + * endElementDebug: + * @ctxt: An XML parser context + * @name: The element name + * + * called when the end of an element has been detected. + */ +static void +endElementDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name) +{ + callbacks++; + if (quiet) + return; + fprintf(SAXdebug, "SAX.endElement(%s)\n", (char *) name); +} + +/** + * charactersDebug: + * @ctxt: An XML parser context + * @ch: a xmlChar string + * @len: the number of xmlChar + * + * receiving some chars from the parser. + * Question: how much at a time ??? + */ +static void +charactersDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len) +{ + char output[40]; + int i; + + callbacks++; + if (quiet) + return; + for (i = 0;(i= size) { +#ifdef LIBXML_HTML_ENABLED + if (options & XML_PARSE_HTML) + htmlParseChunk(ctxt, base + cur, size - cur, 1); + else +#endif + xmlParseChunk(ctxt, base + cur, size - cur, 1); + break; + } else { +#ifdef LIBXML_HTML_ENABLED + if (options & XML_PARSE_HTML) + htmlParseChunk(ctxt, base + cur, 1024, 0); + else +#endif + xmlParseChunk(ctxt, base + cur, 1024, 0); + cur += 1024; + } + } + doc = ctxt->myDoc; +#ifdef LIBXML_HTML_ENABLED + if (options & XML_PARSE_HTML) + res = 1; + else +#endif + res = ctxt->wellFormed; + xmlFreeParserCtxt(ctxt); + free((char *)base); + if (!res) { + xmlFreeDoc(doc); + fprintf(stderr, "Failed to parse %s\n", filename); + return(-1); + } +#ifdef LIBXML_HTML_ENABLED + if (options & XML_PARSE_HTML) + htmlDocDumpMemory(doc, (xmlChar **) &base, &size); + else +#endif + xmlDocDumpMemory(doc, (xmlChar **) &base, &size); + xmlFreeDoc(doc); + res = compareFileMem(result, base, size); + if ((base == NULL) || (res != 0)) { + if (base != NULL) + xmlFree((char *)base); + fprintf(stderr, "Result for %s failed\n", filename); + return(-1); + } + xmlFree((char *)base); + if (err != NULL) { + res = compareFileMem(err, testErrors, testErrorsSize); + if (res != 0) { + fprintf(stderr, "Error for %s failed\n", filename); + return(-1); + } + } + return(0); +} +#endif + +/** + * memParseTest: + * @filename: the file to parse + * @result: the file with expected result + * @err: the file with error messages: unused + * + * Parse a file using the old xmlReadMemory API, then serialize back + * reparse the result and serialize again, then check for deviation + * in serialization. + * + * Returns 0 in case of success, an error code otherwise + */ +static int +memParseTest(const char *filename, const char *result, + const char *err ATTRIBUTE_UNUSED, + int options ATTRIBUTE_UNUSED) { + xmlDocPtr doc; + const char *base; + int size, res; + + nb_tests++; + /* + * load and parse the memory + */ + if (loadMem(filename, &base, &size) != 0) { + fprintf(stderr, "Failed to load %s\n", filename); + return(-1); + } + + doc = xmlReadMemory(base, size, filename, NULL, 0); + unloadMem(base); + if (doc == NULL) { + return(1); + } + xmlDocDumpMemory(doc, (xmlChar **) &base, &size); + xmlFreeDoc(doc); + res = compareFileMem(result, base, size); + if ((base == NULL) || (res != 0)) { + if (base != NULL) + xmlFree((char *)base); + fprintf(stderr, "Result for %s failed\n", filename); + return(-1); + } + xmlFree((char *)base); + return(0); +} + +/** + * noentParseTest: + * @filename: the file to parse + * @result: the file with expected result + * @err: the file with error messages: unused + * + * Parse a file with entity resolution, then serialize back + * reparse the result and serialize again, then check for deviation + * in serialization. + * + * Returns 0 in case of success, an error code otherwise + */ +static int +noentParseTest(const char *filename, const char *result, + const char *err ATTRIBUTE_UNUSED, + int options) { + xmlDocPtr doc; + char *temp; + int res = 0; + + nb_tests++; + /* + * base of the test, parse with the old API + */ + doc = xmlReadFile(filename, NULL, options); + if (doc == NULL) + return(1); + temp = resultFilename(filename, "", ".res"); + if (temp == NULL) { + fprintf(stderr, "Out of memory\n"); + fatalError(); + } + xmlSaveFile(temp, doc); + if (compareFiles(temp, result)) { + res = 1; + } + xmlFreeDoc(doc); + + /* + * Parse the saved result to make sure the round trip is okay + */ + doc = xmlReadFile(filename, NULL, options); + if (doc == NULL) + return(1); + xmlSaveFile(temp, doc); + if (compareFiles(temp, result)) { + res = 1; + } + xmlFreeDoc(doc); + + unlink(temp); + free(temp); + return(res); +} + +/** + * errParseTest: + * @filename: the file to parse + * @result: the file with expected result + * @err: the file with error messages + * + * Parse a file using the xmlReadFile API and check for errors. + * + * Returns 0 in case of success, an error code otherwise + */ +static int +errParseTest(const char *filename, const char *result, const char *err, + int options) { + xmlDocPtr doc; + const char *base = NULL; + int size, res = 0; + + nb_tests++; +#ifdef LIBXML_HTML_ENABLED + if (options & XML_PARSE_HTML) { + doc = htmlReadFile(filename, NULL, options); + } else +#endif +#ifdef LIBXML_XINCLUDE_ENABLED + if (options & XML_PARSE_XINCLUDE) { + doc = xmlReadFile(filename, NULL, options); + xmlXIncludeProcessFlags(doc, options); + } else +#endif + { + xmlGetWarningsDefaultValue = 1; + doc = xmlReadFile(filename, NULL, options); + } + xmlGetWarningsDefaultValue = 0; + if (result) { + if (doc == NULL) { + base = ""; + size = 0; + } else { +#ifdef LIBXML_HTML_ENABLED + if (options & XML_PARSE_HTML) { + htmlDocDumpMemory(doc, (xmlChar **) &base, &size); + } else +#endif + xmlDocDumpMemory(doc, (xmlChar **) &base, &size); + } + res = compareFileMem(result, base, size); + } + if (doc != NULL) { + if (base != NULL) + xmlFree((char *)base); + xmlFreeDoc(doc); + } + if (res != 0) { + fprintf(stderr, "Result for %s failed\n", filename); + return(-1); + } + if (err != NULL) { + res = compareFileMem(err, testErrors, testErrorsSize); + if (res != 0) { + fprintf(stderr, "Error for %s failed\n", filename); + return(-1); + } + } else if (options & XML_PARSE_DTDVALID) { + if (testErrorsSize != 0) + fprintf(stderr, "Validation for %s failed\n", filename); + } + + return(0); +} + +#ifdef LIBXML_READER_ENABLED +/************************************************************************ + * * + * Reader based tests * + * * + ************************************************************************/ + +static void processNode(FILE *out, xmlTextReaderPtr reader) { + const xmlChar *name, *value; + int type, empty; + + type = xmlTextReaderNodeType(reader); + empty = xmlTextReaderIsEmptyElement(reader); + + name = xmlTextReaderConstName(reader); + if (name == NULL) + name = BAD_CAST "--"; + + value = xmlTextReaderConstValue(reader); + + + fprintf(out, "%d %d %s %d %d", + xmlTextReaderDepth(reader), + type, + name, + empty, + xmlTextReaderHasValue(reader)); + if (value == NULL) + fprintf(out, "\n"); + else { + fprintf(out, " %s\n", value); + } +} +static int +streamProcessTest(const char *filename, const char *result, const char *err, + xmlTextReaderPtr reader, const char *rng) { + int ret; + char *temp = NULL; + FILE *t = NULL; + + if (reader == NULL) + return(-1); + + nb_tests++; + if (result != NULL) { + temp = resultFilename(filename, "", ".res"); + if (temp == NULL) { + fprintf(stderr, "Out of memory\n"); + fatalError(); + } + t = fopen(temp, "wb"); + if (t == NULL) { + fprintf(stderr, "Can't open temp file %s\n", temp); + free(temp); + return(-1); + } + } +#ifdef LIBXML_SCHEMAS_ENABLED + if (rng != NULL) { + ret = xmlTextReaderRelaxNGValidate(reader, rng); + if (ret < 0) { + testErrorHandler(NULL, "Relax-NG schema %s failed to compile\n", + rng); + fclose(t); + unlink(temp); + free(temp); + return(0); + } + } +#endif + xmlGetWarningsDefaultValue = 1; + ret = xmlTextReaderRead(reader); + while (ret == 1) { + if ((t != NULL) && (rng == NULL)) + processNode(t, reader); + ret = xmlTextReaderRead(reader); + } + if (ret != 0) { + testErrorHandler(NULL, "%s : failed to parse\n", filename); + } + if (rng != NULL) { + if (xmlTextReaderIsValid(reader) != 1) { + testErrorHandler(NULL, "%s fails to validate\n", filename); + } else { + testErrorHandler(NULL, "%s validates\n", filename); + } + } + xmlGetWarningsDefaultValue = 0; + if (t != NULL) { + fclose(t); + ret = compareFiles(temp, result); + unlink(temp); + free(temp); + if (ret) { + fprintf(stderr, "Result for %s failed\n", filename); + return(-1); + } + } + if (err != NULL) { + ret = compareFileMem(err, testErrors, testErrorsSize); + if (ret != 0) { + fprintf(stderr, "Error for %s failed\n", filename); + printf("%s", testErrors); + return(-1); + } + } + + return(0); +} + +/** + * streamParseTest: + * @filename: the file to parse + * @result: the file with expected result + * @err: the file with error messages + * + * Parse a file using the reader API and check for errors. + * + * Returns 0 in case of success, an error code otherwise + */ +static int +streamParseTest(const char *filename, const char *result, const char *err, + int options) { + xmlTextReaderPtr reader; + int ret; + + reader = xmlReaderForFile(filename, NULL, options); + ret = streamProcessTest(filename, result, err, reader, NULL); + xmlFreeTextReader(reader); + return(ret); +} + +/** + * walkerParseTest: + * @filename: the file to parse + * @result: the file with expected result + * @err: the file with error messages + * + * Parse a file using the walker, i.e. a reader built from a atree. + * + * Returns 0 in case of success, an error code otherwise + */ +static int +walkerParseTest(const char *filename, const char *result, const char *err, + int options) { + xmlDocPtr doc; + xmlTextReaderPtr reader; + int ret; + + doc = xmlReadFile(filename, NULL, options); + if (doc == NULL) { + fprintf(stderr, "Failed to parse %s\n", filename); + return(-1); + } + reader = xmlReaderWalker(doc); + ret = streamProcessTest(filename, result, err, reader, NULL); + xmlFreeTextReader(reader); + xmlFreeDoc(doc); + return(ret); +} + +/** + * streamMemParseTest: + * @filename: the file to parse + * @result: the file with expected result + * @err: the file with error messages + * + * Parse a file using the reader API from memory and check for errors. + * + * Returns 0 in case of success, an error code otherwise + */ +static int +streamMemParseTest(const char *filename, const char *result, const char *err, + int options) { + xmlTextReaderPtr reader; + int ret; + const char *base; + int size; + + /* + * load and parse the memory + */ + if (loadMem(filename, &base, &size) != 0) { + fprintf(stderr, "Failed to load %s\n", filename); + return(-1); + } + reader = xmlReaderForMemory(base, size, filename, NULL, options); + ret = streamProcessTest(filename, result, err, reader, NULL); + free((char *)base); + xmlFreeTextReader(reader); + return(ret); +} +#endif + +#ifdef LIBXML_XPATH_ENABLED +#ifdef LIBXML_DEBUG_ENABLED +/************************************************************************ + * * + * XPath and XPointer based tests * + * * + ************************************************************************/ + +static FILE *xpathOutput; +static xmlDocPtr xpathDocument; + +static void +testXPath(const char *str, int xptr, int expr) { + xmlXPathObjectPtr res; + xmlXPathContextPtr ctxt; + + nb_tests++; +#if defined(LIBXML_XPTR_ENABLED) + if (xptr) { + ctxt = xmlXPtrNewContext(xpathDocument, NULL, NULL); + res = xmlXPtrEval(BAD_CAST str, ctxt); + } else { +#endif + ctxt = xmlXPathNewContext(xpathDocument); + ctxt->node = xmlDocGetRootElement(xpathDocument); + if (expr) + res = xmlXPathEvalExpression(BAD_CAST str, ctxt); + else { + /* res = xmlXPathEval(BAD_CAST str, ctxt); */ + xmlXPathCompExprPtr comp; + + comp = xmlXPathCompile(BAD_CAST str); + if (comp != NULL) { + res = xmlXPathCompiledEval(comp, ctxt); + xmlXPathFreeCompExpr(comp); + } else + res = NULL; + } +#if defined(LIBXML_XPTR_ENABLED) + } +#endif + xmlXPathDebugDumpObject(xpathOutput, res, 0); + xmlXPathFreeObject(res); + xmlXPathFreeContext(ctxt); +} + +/** + * xpathExprTest: + * @filename: the file to parse + * @result: the file with expected result + * @err: the file with error messages + * + * Parse a file containing XPath standalone expressions and evaluate them + * + * Returns 0 in case of success, an error code otherwise + */ +static int +xpathCommonTest(const char *filename, const char *result, + int xptr, int expr) { + FILE *input; + char expression[5000]; + int len, ret = 0; + char *temp; + + temp = resultFilename(filename, "", ".res"); + if (temp == NULL) { + fprintf(stderr, "Out of memory\n"); + fatalError(); + } + xpathOutput = fopen(temp, "wb"); + if (xpathOutput == NULL) { + fprintf(stderr, "failed to open output file %s\n", temp); + free(temp); + return(-1); + } + + input = fopen(filename, "rb"); + if (input == NULL) { + xmlGenericError(xmlGenericErrorContext, + "Cannot open %s for reading\n", filename); + free(temp); + return(-1); + } + while (fgets(expression, 4500, input) != NULL) { + len = strlen(expression); + len--; + while ((len >= 0) && + ((expression[len] == '\n') || (expression[len] == '\t') || + (expression[len] == '\r') || (expression[len] == ' '))) len--; + expression[len + 1] = 0; + if (len >= 0) { + fprintf(xpathOutput, + "\n========================\nExpression: %s\n", + expression) ; + testXPath(expression, xptr, expr); + } + } + + fclose(input); + fclose(xpathOutput); + if (result != NULL) { + ret = compareFiles(temp, result); + if (ret) { + fprintf(stderr, "Result for %s failed\n", filename); + } + } + + unlink(temp); + free(temp); + return(ret); +} + +/** + * xpathExprTest: + * @filename: the file to parse + * @result: the file with expected result + * @err: the file with error messages + * + * Parse a file containing XPath standalone expressions and evaluate them + * + * Returns 0 in case of success, an error code otherwise + */ +static int +xpathExprTest(const char *filename, const char *result, + const char *err ATTRIBUTE_UNUSED, + int options ATTRIBUTE_UNUSED) { + return(xpathCommonTest(filename, result, 0, 1)); +} + +/** + * xpathDocTest: + * @filename: the file to parse + * @result: the file with expected result + * @err: the file with error messages + * + * Parse a file containing XPath expressions and evaluate them against + * a set of corresponding documents. + * + * Returns 0 in case of success, an error code otherwise + */ +static int +xpathDocTest(const char *filename, + const char *resul ATTRIBUTE_UNUSED, + const char *err ATTRIBUTE_UNUSED, + int options) { + + char pattern[500]; + char result[500]; + glob_t globbuf; + size_t i; + int ret = 0, res; + + xpathDocument = xmlReadFile(filename, NULL, + options | XML_PARSE_DTDATTR | XML_PARSE_NOENT); + if (xpathDocument == NULL) { + fprintf(stderr, "Failed to load %s\n", filename); + return(-1); + } + + snprintf(pattern, 499, "./test/XPath/tests/%s*", baseFilename(filename)); + pattern[499] = 0; + globbuf.gl_offs = 0; + glob(pattern, GLOB_DOOFFS, NULL, &globbuf); + for (i = 0;i < globbuf.gl_pathc;i++) { + snprintf(result, 499, "result/XPath/tests/%s", + baseFilename(globbuf.gl_pathv[i])); + res = xpathCommonTest(globbuf.gl_pathv[i], &result[0], 0, 0); + if (res != 0) + ret = res; + } + globfree(&globbuf); + + xmlFreeDoc(xpathDocument); + return(ret); +} + +#ifdef LIBXML_XPTR_ENABLED +/** + * xptrDocTest: + * @filename: the file to parse + * @result: the file with expected result + * @err: the file with error messages + * + * Parse a file containing XPath expressions and evaluate them against + * a set of corresponding documents. + * + * Returns 0 in case of success, an error code otherwise + */ +static int +xptrDocTest(const char *filename, + const char *resul ATTRIBUTE_UNUSED, + const char *err ATTRIBUTE_UNUSED, + int options) { + + char pattern[500]; + char result[500]; + glob_t globbuf; + size_t i; + int ret = 0, res; + + xpathDocument = xmlReadFile(filename, NULL, + options | XML_PARSE_DTDATTR | XML_PARSE_NOENT); + if (xpathDocument == NULL) { + fprintf(stderr, "Failed to load %s\n", filename); + return(-1); + } + + snprintf(pattern, 499, "./test/XPath/xptr/%s*", baseFilename(filename)); + pattern[499] = 0; + globbuf.gl_offs = 0; + glob(pattern, GLOB_DOOFFS, NULL, &globbuf); + for (i = 0;i < globbuf.gl_pathc;i++) { + snprintf(result, 499, "result/XPath/xptr/%s", + baseFilename(globbuf.gl_pathv[i])); + res = xpathCommonTest(globbuf.gl_pathv[i], &result[0], 1, 0); + if (res != 0) + ret = res; + } + globfree(&globbuf); + + xmlFreeDoc(xpathDocument); + return(ret); +} +#endif /* LIBXML_XPTR_ENABLED */ + +/** + * xmlidDocTest: + * @filename: the file to parse + * @result: the file with expected result + * @err: the file with error messages + * + * Parse a file containing xml:id and check for errors and verify + * that XPath queries will work on them as expected. + * + * Returns 0 in case of success, an error code otherwise + */ +static int +xmlidDocTest(const char *filename, + const char *result, + const char *err, + int options) { + + int res = 0; + int ret = 0; + char *temp; + + xpathDocument = xmlReadFile(filename, NULL, + options | XML_PARSE_DTDATTR | XML_PARSE_NOENT); + if (xpathDocument == NULL) { + fprintf(stderr, "Failed to load %s\n", filename); + return(-1); + } + + temp = resultFilename(filename, "", ".res"); + if (temp == NULL) { + fprintf(stderr, "Out of memory\n"); + fatalError(); + } + xpathOutput = fopen(temp, "wb"); + if (xpathOutput == NULL) { + fprintf(stderr, "failed to open output file %s\n", temp); + xmlFreeDoc(xpathDocument); + free(temp); + return(-1); + } + + testXPath("id('bar')", 0, 0); + + fclose(xpathOutput); + if (result != NULL) { + ret = compareFiles(temp, result); + if (ret) { + fprintf(stderr, "Result for %s failed\n", filename); + res = 1; + } + } + + unlink(temp); + free(temp); + xmlFreeDoc(xpathDocument); + + if (err != NULL) { + ret = compareFileMem(err, testErrors, testErrorsSize); + if (ret != 0) { + fprintf(stderr, "Error for %s failed\n", filename); + res = 1; + } + } + return(res); +} + +#endif /* LIBXML_DEBUG_ENABLED */ +#endif /* XPATH */ +/************************************************************************ + * * + * URI based tests * + * * + ************************************************************************/ + +static void +handleURI(const char *str, const char *base, FILE *o) { + int ret; + xmlURIPtr uri; + xmlChar *res = NULL; + + uri = xmlCreateURI(); + + if (base == NULL) { + ret = xmlParseURIReference(uri, str); + if (ret != 0) + fprintf(o, "%s : error %d\n", str, ret); + else { + xmlNormalizeURIPath(uri->path); + xmlPrintURI(o, uri); + fprintf(o, "\n"); + } + } else { + res = xmlBuildURI((xmlChar *)str, (xmlChar *) base); + if (res != NULL) { + fprintf(o, "%s\n", (char *) res); + } + else + fprintf(o, "::ERROR::\n"); + } + if (res != NULL) + xmlFree(res); + xmlFreeURI(uri); +} + +/** + * uriCommonTest: + * @filename: the file to parse + * @result: the file with expected result + * @err: the file with error messages + * + * Parse a file containing URI and check for errors + * + * Returns 0 in case of success, an error code otherwise + */ +static int +uriCommonTest(const char *filename, + const char *result, + const char *err, + const char *base) { + char *temp; + FILE *o, *f; + char str[1024]; + int res = 0, i, ret; + + temp = resultFilename(filename, "", ".res"); + if (temp == NULL) { + fprintf(stderr, "Out of memory\n"); + fatalError(); + } + o = fopen(temp, "wb"); + if (o == NULL) { + fprintf(stderr, "failed to open output file %s\n", temp); + free(temp); + return(-1); + } + f = fopen(filename, "rb"); + if (f == NULL) { + fprintf(stderr, "failed to open input file %s\n", filename); + fclose(o); + unlink(temp); + free(temp); + return(-1); + } + + while (1) { + /* + * read one line in string buffer. + */ + if (fgets (&str[0], sizeof (str) - 1, f) == NULL) + break; + + /* + * remove the ending spaces + */ + i = strlen(str); + while ((i > 0) && + ((str[i - 1] == '\n') || (str[i - 1] == '\r') || + (str[i - 1] == ' ') || (str[i - 1] == '\t'))) { + i--; + str[i] = 0; + } + nb_tests++; + handleURI(str, base, o); + } + + fclose(f); + fclose(o); + + if (result != NULL) { + ret = compareFiles(temp, result); + if (ret) { + fprintf(stderr, "Result for %s failed\n", filename); + res = 1; + } + } + if (err != NULL) { + ret = compareFileMem(err, testErrors, testErrorsSize); + if (ret != 0) { + fprintf(stderr, "Error for %s failed\n", filename); + res = 1; + } + } + + unlink(temp); + free(temp); + return(res); +} + +/** + * uriParseTest: + * @filename: the file to parse + * @result: the file with expected result + * @err: the file with error messages + * + * Parse a file containing URI and check for errors + * + * Returns 0 in case of success, an error code otherwise + */ +static int +uriParseTest(const char *filename, + const char *result, + const char *err, + int options ATTRIBUTE_UNUSED) { + return(uriCommonTest(filename, result, err, NULL)); +} + +/** + * uriBaseTest: + * @filename: the file to parse + * @result: the file with expected result + * @err: the file with error messages + * + * Parse a file containing URI, compose them against a fixed base and + * check for errors + * + * Returns 0 in case of success, an error code otherwise + */ +static int +uriBaseTest(const char *filename, + const char *result, + const char *err, + int options ATTRIBUTE_UNUSED) { + return(uriCommonTest(filename, result, err, + "http://foo.com/path/to/index.html?orig#help")); +} + +static int urip_success = 1; +static int urip_current = 0; +static const char *urip_testURLs[] = { + "urip://example.com/a b.html", + "urip://example.com/a%20b.html", + "file:///path/to/a b.html", + "file:///path/to/a%20b.html", + "/path/to/a b.html", + "/path/to/a%20b.html", + "urip://example.com/résumé.html", + "urip://example.com/test?a=1&b=2%263&c=4#foo", + NULL +}; +static const char *urip_rcvsURLs[] = { + /* it is an URI the strings must be escaped */ + "urip://example.com/a%20b.html", + /* check that % escaping is not broken */ + "urip://example.com/a%20b.html", + /* it's an URI path the strings must be escaped */ + "file:///path/to/a%20b.html", + /* check that % escaping is not broken */ + "file:///path/to/a%20b.html", + /* this is not an URI, this is a path, so this should not be escaped */ + "/path/to/a b.html", + /* check that paths with % are not broken */ + "/path/to/a%20b.html", + /* out of context the encoding can't be guessed byte by byte conversion */ + "urip://example.com/r%E9sum%E9.html", + /* verify we don't destroy URIs especially the query part */ + "urip://example.com/test?a=1&b=2%263&c=4#foo", + NULL +}; +static const char *urip_res = ""; +static const char *urip_cur = NULL; +static int urip_rlen; + +/** + * uripMatch: + * @URI: an URI to test + * + * Check for an urip: query + * + * Returns 1 if yes and 0 if another Input module should be used + */ +static int +uripMatch(const char * URI) { + if ((URI == NULL) || (!strcmp(URI, "file:///etc/xml/catalog"))) + return(0); + /* Verify we received the escaped URL */ + if (strcmp(urip_rcvsURLs[urip_current], URI)) + urip_success = 0; + return(1); +} + +/** + * uripOpen: + * @URI: an URI to test + * + * Return a pointer to the urip: query handler, in this example simply + * the urip_current pointer... + * + * Returns an Input context or NULL in case or error + */ +static void * +uripOpen(const char * URI) { + if ((URI == NULL) || (!strcmp(URI, "file:///etc/xml/catalog"))) + return(NULL); + /* Verify we received the escaped URL */ + if (strcmp(urip_rcvsURLs[urip_current], URI)) + urip_success = 0; + urip_cur = urip_res; + urip_rlen = strlen(urip_res); + return((void *) urip_cur); +} + +/** + * uripClose: + * @context: the read context + * + * Close the urip: query handler + * + * Returns 0 or -1 in case of error + */ +static int +uripClose(void * context) { + if (context == NULL) return(-1); + urip_cur = NULL; + urip_rlen = 0; + return(0); +} + +/** + * uripRead: + * @context: the read context + * @buffer: where to store data + * @len: number of bytes to read + * + * Implement an urip: query read. + * + * Returns the number of bytes read or -1 in case of error + */ +static int +uripRead(void * context, char * buffer, int len) { + const char *ptr = (const char *) context; + + if ((context == NULL) || (buffer == NULL) || (len < 0)) + return(-1); + + if (len > urip_rlen) len = urip_rlen; + memcpy(buffer, ptr, len); + urip_rlen -= len; + return(len); +} + +static int +urip_checkURL(const char *URL) { + xmlDocPtr doc; + + doc = xmlReadFile(URL, NULL, 0); + if (doc == NULL) + return(-1); + xmlFreeDoc(doc); + return(1); +} + +/** + * uriPathTest: + * @filename: ignored + * @result: ignored + * @err: ignored + * + * Run a set of tests to check how Path and URI are handled before + * being passed to the I/O layer + * + * Returns 0 in case of success, an error code otherwise + */ +static int +uriPathTest(const char *filename ATTRIBUTE_UNUSED, + const char *result ATTRIBUTE_UNUSED, + const char *err ATTRIBUTE_UNUSED, + int options ATTRIBUTE_UNUSED) { + int parsed; + int failures = 0; + + /* + * register the new I/O handlers + */ + if (xmlRegisterInputCallbacks(uripMatch, uripOpen, uripRead, uripClose) < 0) + { + fprintf(stderr, "failed to register HTTP handler\n"); + return(-1); + } + + for (urip_current = 0;urip_testURLs[urip_current] != NULL;urip_current++) { + urip_success = 1; + parsed = urip_checkURL(urip_testURLs[urip_current]); + if (urip_success != 1) { + fprintf(stderr, "failed the URL passing test for %s", + urip_testURLs[urip_current]); + failures++; + } else if (parsed != 1) { + fprintf(stderr, "failed the parsing test for %s", + urip_testURLs[urip_current]); + failures++; + } + nb_tests++; + } + + xmlPopInputCallbacks(); + return(failures); +} + +#ifdef LIBXML_SCHEMAS_ENABLED +/************************************************************************ + * * + * Schemas tests * + * * + ************************************************************************/ +static int +schemasOneTest(const char *sch, + const char *filename, + const char *result, + const char *err, + int options, + xmlSchemaPtr schemas) { + xmlDocPtr doc; + xmlSchemaValidCtxtPtr ctxt; + int ret = 0; + char *temp; + FILE *schemasOutput; + + doc = xmlReadFile(filename, NULL, options); + if (doc == NULL) { + fprintf(stderr, "failed to parse instance %s for %s\n", filename, sch); + return(-1); + } + + temp = resultFilename(result, "", ".res"); + if (temp == NULL) { + fprintf(stderr, "Out of memory\n"); + fatalError(); + } + schemasOutput = fopen(temp, "wb"); + if (schemasOutput == NULL) { + fprintf(stderr, "failed to open output file %s\n", temp); + xmlFreeDoc(doc); + free(temp); + return(-1); + } + + ctxt = xmlSchemaNewValidCtxt(schemas); + xmlSchemaSetValidErrors(ctxt, + (xmlSchemaValidityErrorFunc) testErrorHandler, + (xmlSchemaValidityWarningFunc) testErrorHandler, + ctxt); + ret = xmlSchemaValidateDoc(ctxt, doc); + if (ret == 0) { + fprintf(schemasOutput, "%s validates\n", filename); + } else if (ret > 0) { + fprintf(schemasOutput, "%s fails to validate\n", filename); + } else { + fprintf(schemasOutput, "%s validation generated an internal error\n", + filename); + } + fclose(schemasOutput); + if (result) { + if (compareFiles(temp, result)) { + fprintf(stderr, "Result for %s on %s failed\n", filename, sch); + ret = 1; + } + } + unlink(temp); + free(temp); + + if ((ret != 0) && (err != NULL)) { + if (compareFileMem(err, testErrors, testErrorsSize)) { + fprintf(stderr, "Error for %s on %s failed\n", filename, sch); + ret = 1; + } else { + ret = 0; + } + } + + xmlSchemaFreeValidCtxt(ctxt); + xmlFreeDoc(doc); + return(ret); +} +/** + * schemasTest: + * @filename: the schemas file + * @result: the file with expected result + * @err: the file with error messages + * + * Parse a file containing URI, compose them against a fixed base and + * check for errors + * + * Returns 0 in case of success, an error code otherwise + */ +static int +schemasTest(const char *filename, + const char *resul ATTRIBUTE_UNUSED, + const char *errr ATTRIBUTE_UNUSED, + int options) { + const char *base = baseFilename(filename); + const char *base2; + const char *instance; + xmlSchemaParserCtxtPtr ctxt; + xmlSchemaPtr schemas; + int res = 0, len, ret; + char pattern[500]; + char prefix[500]; + char result[500]; + char err[500]; + glob_t globbuf; + size_t i; + char count = 0; + + /* first compile the schemas if possible */ + ctxt = xmlSchemaNewParserCtxt(filename); + xmlSchemaSetParserErrors(ctxt, + (xmlSchemaValidityErrorFunc) testErrorHandler, + (xmlSchemaValidityWarningFunc) testErrorHandler, + ctxt); + schemas = xmlSchemaParse(ctxt); + xmlSchemaFreeParserCtxt(ctxt); + + /* + * most of the mess is about the output filenames generated by the Makefile + */ + len = strlen(base); + if ((len > 499) || (len < 5)) { + xmlSchemaFree(schemas); + return(-1); + } + len -= 4; /* remove trailing .xsd */ + if (base[len - 2] == '_') { + len -= 2; /* remove subtest number */ + } + if (base[len - 2] == '_') { + len -= 2; /* remove subtest number */ + } + memcpy(prefix, base, len); + prefix[len] = 0; + + snprintf(pattern, 499, "./test/schemas/%s_?.xml", prefix); + pattern[499] = 0; + + if (base[len] == '_') { + len += 2; + memcpy(prefix, base, len); + prefix[len] = 0; + } + + globbuf.gl_offs = 0; + glob(pattern, GLOB_DOOFFS, NULL, &globbuf); + for (i = 0;i < globbuf.gl_pathc;i++) { + testErrorsSize = 0; + testErrors[0] = 0; + instance = globbuf.gl_pathv[i]; + base2 = baseFilename(instance); + len = strlen(base2); + if ((len > 6) && (base2[len - 6] == '_')) { + count = base2[len - 5]; + snprintf(result, 499, "result/schemas/%s_%c", + prefix, count); + result[499] = 0; + snprintf(err, 499, "result/schemas/%s_%c.err", + prefix, count); + err[499] = 0; + } else { + fprintf(stderr, "don't know how to process %s\n", instance); + continue; + } + if (schemas == NULL) { + } else { + nb_tests++; + ret = schemasOneTest(filename, instance, result, err, + options, schemas); + if (ret != 0) + res = ret; + } + } + globfree(&globbuf); + xmlSchemaFree(schemas); + + return(res); +} + +/************************************************************************ + * * + * Schemas tests * + * * + ************************************************************************/ +static int +rngOneTest(const char *sch, + const char *filename, + const char *result, + const char *err, + int options, + xmlRelaxNGPtr schemas) { + xmlDocPtr doc; + xmlRelaxNGValidCtxtPtr ctxt; + int ret = 0; + char *temp; + FILE *schemasOutput; + + doc = xmlReadFile(filename, NULL, options); + if (doc == NULL) { + fprintf(stderr, "failed to parse instance %s for %s\n", filename, sch); + return(-1); + } + + temp = resultFilename(result, "", ".res"); + if (temp == NULL) { + fprintf(stderr, "Out of memory\n"); + fatalError(); + } + schemasOutput = fopen(temp, "wb"); + if (schemasOutput == NULL) { + fprintf(stderr, "failed to open output file %s\n", temp); + xmlFreeDoc(doc); + free(temp); + return(-1); + } + + ctxt = xmlRelaxNGNewValidCtxt(schemas); + xmlRelaxNGSetValidErrors(ctxt, + (xmlRelaxNGValidityErrorFunc) testErrorHandler, + (xmlRelaxNGValidityWarningFunc) testErrorHandler, + ctxt); + ret = xmlRelaxNGValidateDoc(ctxt, doc); + if (ret == 0) { + testErrorHandler(NULL, "%s validates\n", filename); + } else if (ret > 0) { + testErrorHandler(NULL, "%s fails to validate\n", filename); + } else { + testErrorHandler(NULL, "%s validation generated an internal error\n", + filename); + } + fclose(schemasOutput); + if (result) { + if (compareFiles(temp, result)) { + fprintf(stderr, "Result for %s on %s failed\n", filename, sch); + ret = 1; + } + } + unlink(temp); + free(temp); + + if (err != NULL) { + if (compareFileMem(err, testErrors, testErrorsSize)) { + fprintf(stderr, "Error for %s on %s failed\n", filename, sch); + ret = 1; + printf("%s", testErrors); + } + } + + + xmlRelaxNGFreeValidCtxt(ctxt); + xmlFreeDoc(doc); + return(ret); +} +/** + * rngTest: + * @filename: the schemas file + * @result: the file with expected result + * @err: the file with error messages + * + * Parse an RNG schemas and then apply it to the related .xml + * + * Returns 0 in case of success, an error code otherwise + */ +static int +rngTest(const char *filename, + const char *resul ATTRIBUTE_UNUSED, + const char *errr ATTRIBUTE_UNUSED, + int options) { + const char *base = baseFilename(filename); + const char *base2; + const char *instance; + xmlRelaxNGParserCtxtPtr ctxt; + xmlRelaxNGPtr schemas; + int res = 0, len, ret; + char pattern[500]; + char prefix[500]; + char result[500]; + char err[500]; + glob_t globbuf; + size_t i; + char count = 0; + + /* first compile the schemas if possible */ + ctxt = xmlRelaxNGNewParserCtxt(filename); + xmlRelaxNGSetParserErrors(ctxt, + (xmlRelaxNGValidityErrorFunc) testErrorHandler, + (xmlRelaxNGValidityWarningFunc) testErrorHandler, + ctxt); + schemas = xmlRelaxNGParse(ctxt); + xmlRelaxNGFreeParserCtxt(ctxt); + + /* + * most of the mess is about the output filenames generated by the Makefile + */ + len = strlen(base); + if ((len > 499) || (len < 5)) { + xmlRelaxNGFree(schemas); + return(-1); + } + len -= 4; /* remove trailing .rng */ + memcpy(prefix, base, len); + prefix[len] = 0; + + snprintf(pattern, 499, "./test/relaxng/%s_?.xml", prefix); + pattern[499] = 0; + + globbuf.gl_offs = 0; + glob(pattern, GLOB_DOOFFS, NULL, &globbuf); + for (i = 0;i < globbuf.gl_pathc;i++) { + testErrorsSize = 0; + testErrors[0] = 0; + instance = globbuf.gl_pathv[i]; + base2 = baseFilename(instance); + len = strlen(base2); + if ((len > 6) && (base2[len - 6] == '_')) { + count = base2[len - 5]; + snprintf(result, 499, "result/relaxng/%s_%c", + prefix, count); + result[499] = 0; + snprintf(err, 499, "result/relaxng/%s_%c.err", + prefix, count); + err[499] = 0; + } else { + fprintf(stderr, "don't know how to process %s\n", instance); + continue; + } + if (schemas == NULL) { + } else { + nb_tests++; + ret = rngOneTest(filename, instance, result, err, + options, schemas); + if (res != 0) + ret = res; + } + } + globfree(&globbuf); + xmlRelaxNGFree(schemas); + + return(res); +} + +#ifdef LIBXML_READER_ENABLED +/** + * rngStreamTest: + * @filename: the schemas file + * @result: the file with expected result + * @err: the file with error messages + * + * Parse a set of files with streaming, applying an RNG schemas + * + * Returns 0 in case of success, an error code otherwise + */ +static int +rngStreamTest(const char *filename, + const char *resul ATTRIBUTE_UNUSED, + const char *errr ATTRIBUTE_UNUSED, + int options) { + const char *base = baseFilename(filename); + const char *base2; + const char *instance; + int res = 0, len, ret; + char pattern[500]; + char prefix[500]; + char result[500]; + char err[500]; + glob_t globbuf; + size_t i; + char count = 0; + xmlTextReaderPtr reader; + int disable_err = 0; + + /* + * most of the mess is about the output filenames generated by the Makefile + */ + len = strlen(base); + if ((len > 499) || (len < 5)) { + fprintf(stderr, "len(base) == %d !\n", len); + return(-1); + } + len -= 4; /* remove trailing .rng */ + memcpy(prefix, base, len); + prefix[len] = 0; + + /* + * strictly unifying the error messages is nearly impossible this + * hack is also done in the Makefile + */ + if ((!strcmp(prefix, "tutor10_1")) || (!strcmp(prefix, "tutor10_2")) || + (!strcmp(prefix, "tutor3_2")) || (!strcmp(prefix, "307377"))) + disable_err = 1; + + snprintf(pattern, 499, "./test/relaxng/%s_?.xml", prefix); + pattern[499] = 0; + + globbuf.gl_offs = 0; + glob(pattern, GLOB_DOOFFS, NULL, &globbuf); + for (i = 0;i < globbuf.gl_pathc;i++) { + testErrorsSize = 0; + testErrors[0] = 0; + instance = globbuf.gl_pathv[i]; + base2 = baseFilename(instance); + len = strlen(base2); + if ((len > 6) && (base2[len - 6] == '_')) { + count = base2[len - 5]; + snprintf(result, 499, "result/relaxng/%s_%c", + prefix, count); + result[499] = 0; + snprintf(err, 499, "result/relaxng/%s_%c.err", + prefix, count); + err[499] = 0; + } else { + fprintf(stderr, "don't know how to process %s\n", instance); + continue; + } + reader = xmlReaderForFile(instance, NULL, options); + if (reader == NULL) { + fprintf(stderr, "Failed to build reder for %s\n", instance); + } + if (disable_err == 1) + ret = streamProcessTest(instance, result, NULL, reader, filename); + else + ret = streamProcessTest(instance, result, err, reader, filename); + xmlFreeTextReader(reader); + if (ret != 0) { + fprintf(stderr, "instance %s failed\n", instance); + res = ret; + } + } + globfree(&globbuf); + + return(res); +} +#endif /* READER */ + +#endif + +#ifdef LIBXML_PATTERN_ENABLED +#ifdef LIBXML_READER_ENABLED +/************************************************************************ + * * + * Patterns tests * + * * + ************************************************************************/ +static void patternNode(FILE *out, xmlTextReaderPtr reader, + const char *pattern, xmlPatternPtr patternc, + xmlStreamCtxtPtr patstream) { + xmlChar *path = NULL; + int match = -1; + int type, empty; + + type = xmlTextReaderNodeType(reader); + empty = xmlTextReaderIsEmptyElement(reader); + + if (type == XML_READER_TYPE_ELEMENT) { + /* do the check only on element start */ + match = xmlPatternMatch(patternc, xmlTextReaderCurrentNode(reader)); + + if (match) { + path = xmlGetNodePath(xmlTextReaderCurrentNode(reader)); + fprintf(out, "Node %s matches pattern %s\n", path, pattern); + } + } + if (patstream != NULL) { + int ret; + + if (type == XML_READER_TYPE_ELEMENT) { + ret = xmlStreamPush(patstream, + xmlTextReaderConstLocalName(reader), + xmlTextReaderConstNamespaceUri(reader)); + if (ret < 0) { + fprintf(out, "xmlStreamPush() failure\n"); + xmlFreeStreamCtxt(patstream); + patstream = NULL; + } else if (ret != match) { + if (path == NULL) { + path = xmlGetNodePath( + xmlTextReaderCurrentNode(reader)); + } + fprintf(out, + "xmlPatternMatch and xmlStreamPush disagree\n"); + fprintf(out, + " pattern %s node %s\n", + pattern, path); + } + + + } + if ((type == XML_READER_TYPE_END_ELEMENT) || + ((type == XML_READER_TYPE_ELEMENT) && (empty))) { + ret = xmlStreamPop(patstream); + if (ret < 0) { + fprintf(out, "xmlStreamPop() failure\n"); + xmlFreeStreamCtxt(patstream); + patstream = NULL; + } + } + } + if (path != NULL) + xmlFree(path); +} + +/** + * patternTest: + * @filename: the schemas file + * @result: the file with expected result + * @err: the file with error messages + * + * Parse a set of files with streaming, applying an RNG schemas + * + * Returns 0 in case of success, an error code otherwise + */ +static int +patternTest(const char *filename, + const char *resul ATTRIBUTE_UNUSED, + const char *err ATTRIBUTE_UNUSED, + int options) { + xmlPatternPtr patternc = NULL; + xmlStreamCtxtPtr patstream = NULL; + FILE *o, *f; + char str[1024]; + char xml[500]; + char result[500]; + int len, i; + int ret = 0, res; + char *temp; + xmlTextReaderPtr reader; + xmlDocPtr doc; + + len = strlen(filename); + len -= 4; + memcpy(xml, filename, len); + xml[len] = 0; + snprintf(result, 499, "result/pattern/%s", baseFilename(xml)); + result[499] = 0; + memcpy(xml + len, ".xml", 5); + + if (!checkTestFile(xml)) { + fprintf(stderr, "Missing xml file %s\n", xml); + return(-1); + } + if (!checkTestFile(result)) { + fprintf(stderr, "Missing result file %s\n", result); + return(-1); + } + f = fopen(filename, "rb"); + if (f == NULL) { + fprintf(stderr, "Failed to open %s\n", filename); + return(-1); + } + temp = resultFilename(filename, "", ".res"); + if (temp == NULL) { + fprintf(stderr, "Out of memory\n"); + fatalError(); + } + o = fopen(temp, "wb"); + if (o == NULL) { + fprintf(stderr, "failed to open output file %s\n", temp); + fclose(f); + free(temp); + return(-1); + } + while (1) { + /* + * read one line in string buffer. + */ + if (fgets (&str[0], sizeof (str) - 1, f) == NULL) + break; + + /* + * remove the ending spaces + */ + i = strlen(str); + while ((i > 0) && + ((str[i - 1] == '\n') || (str[i - 1] == '\r') || + (str[i - 1] == ' ') || (str[i - 1] == '\t'))) { + i--; + str[i] = 0; + } + doc = xmlReadFile(xml, NULL, options); + if (doc == NULL) { + fprintf(stderr, "Failed to parse %s\n", xml); + ret = 1; + } else { + xmlNodePtr root; + const xmlChar *namespaces[22]; + int j; + xmlNsPtr ns; + + root = xmlDocGetRootElement(doc); + for (ns = root->nsDef, j = 0;ns != NULL && j < 20;ns=ns->next) { + namespaces[j++] = ns->href; + namespaces[j++] = ns->prefix; + } + namespaces[j++] = NULL; + namespaces[j++] = NULL; + + patternc = xmlPatterncompile((const xmlChar *) str, doc->dict, + 0, &namespaces[0]); + if (patternc == NULL) { + testErrorHandler(NULL, + "Pattern %s failed to compile\n", str); + xmlFreeDoc(doc); + ret = 1; + continue; + } + patstream = xmlPatternGetStreamCtxt(patternc); + if (patstream != NULL) { + ret = xmlStreamPush(patstream, NULL, NULL); + if (ret < 0) { + fprintf(stderr, "xmlStreamPush() failure\n"); + xmlFreeStreamCtxt(patstream); + patstream = NULL; + } + } + nb_tests++; + + reader = xmlReaderWalker(doc); + res = xmlTextReaderRead(reader); + while (res == 1) { + patternNode(o, reader, str, patternc, patstream); + res = xmlTextReaderRead(reader); + } + if (res != 0) { + fprintf(o, "%s : failed to parse\n", filename); + } + xmlFreeTextReader(reader); + xmlFreeDoc(doc); + xmlFreeStreamCtxt(patstream); + patstream = NULL; + xmlFreePattern(patternc); + + } + } + + fclose(f); + fclose(o); + + ret = compareFiles(temp, result); + if (ret) { + fprintf(stderr, "Result for %s failed\n", filename); + ret = 1; + } + unlink(temp); + free(temp); + return(ret); +} +#endif /* READER */ +#endif /* PATTERN */ +#ifdef LIBXML_C14N_ENABLED +/************************************************************************ + * * + * Canonicalization tests * + * * + ************************************************************************/ +static xmlXPathObjectPtr +load_xpath_expr (xmlDocPtr parent_doc, const char* filename) { + xmlXPathObjectPtr xpath; + xmlDocPtr doc; + xmlChar *expr; + xmlXPathContextPtr ctx; + xmlNodePtr node; + xmlNsPtr ns; + + /* + * load XPath expr as a file + */ + xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS; + xmlSubstituteEntitiesDefault(1); + + doc = xmlReadFile(filename, NULL, XML_PARSE_DTDATTR | XML_PARSE_NOENT); + if (doc == NULL) { + fprintf(stderr, "Error: unable to parse file \"%s\"\n", filename); + return(NULL); + } + + /* + * Check the document is of the right kind + */ + if(xmlDocGetRootElement(doc) == NULL) { + fprintf(stderr,"Error: empty document for file \"%s\"\n", filename); + xmlFreeDoc(doc); + return(NULL); + } + + node = doc->children; + while(node != NULL && !xmlStrEqual(node->name, (const xmlChar *)"XPath")) { + node = node->next; + } + + if(node == NULL) { + fprintf(stderr,"Error: XPath element expected in the file \"%s\"\n", filename); + xmlFreeDoc(doc); + return(NULL); + } + + expr = xmlNodeGetContent(node); + if(expr == NULL) { + fprintf(stderr,"Error: XPath content element is NULL \"%s\"\n", filename); + xmlFreeDoc(doc); + return(NULL); + } + + ctx = xmlXPathNewContext(parent_doc); + if(ctx == NULL) { + fprintf(stderr,"Error: unable to create new context\n"); + xmlFree(expr); + xmlFreeDoc(doc); + return(NULL); + } + + /* + * Register namespaces + */ + ns = node->nsDef; + while(ns != NULL) { + if(xmlXPathRegisterNs(ctx, ns->prefix, ns->href) != 0) { + fprintf(stderr,"Error: unable to register NS with prefix=\"%s\" and href=\"%s\"\n", ns->prefix, ns->href); + xmlFree(expr); + xmlXPathFreeContext(ctx); + xmlFreeDoc(doc); + return(NULL); + } + ns = ns->next; + } + + /* + * Evaluate xpath + */ + xpath = xmlXPathEvalExpression(expr, ctx); + if(xpath == NULL) { + fprintf(stderr,"Error: unable to evaluate xpath expression\n"); + xmlFree(expr); + xmlXPathFreeContext(ctx); + xmlFreeDoc(doc); + return(NULL); + } + + /* print_xpath_nodes(xpath->nodesetval); */ + + xmlFree(expr); + xmlXPathFreeContext(ctx); + xmlFreeDoc(doc); + return(xpath); +} + +/* + * Macro used to grow the current buffer. + */ +#define xxx_growBufferReentrant() { \ + buffer_size *= 2; \ + buffer = (xmlChar **) \ + xmlRealloc(buffer, buffer_size * sizeof(xmlChar*)); \ + if (buffer == NULL) { \ + perror("realloc failed"); \ + return(NULL); \ + } \ +} + +static xmlChar ** +parse_list(xmlChar *str) { + xmlChar **buffer; + xmlChar **out = NULL; + int buffer_size = 0; + int len; + + if(str == NULL) { + return(NULL); + } + + len = xmlStrlen(str); + if((str[0] == '\'') && (str[len - 1] == '\'')) { + str[len - 1] = '\0'; + str++; + len -= 2; + } + /* + * allocate an translation buffer. + */ + buffer_size = 1000; + buffer = (xmlChar **) xmlMalloc(buffer_size * sizeof(xmlChar*)); + if (buffer == NULL) { + perror("malloc failed"); + return(NULL); + } + out = buffer; + + while(*str != '\0') { + if (out - buffer > buffer_size - 10) { + int indx = out - buffer; + + xxx_growBufferReentrant(); + out = &buffer[indx]; + } + (*out++) = str; + while(*str != ',' && *str != '\0') ++str; + if(*str == ',') *(str++) = '\0'; + } + (*out) = NULL; + return buffer; +} + +static int +c14nRunTest(const char* xml_filename, int with_comments, int exclusive, + const char* xpath_filename, const char *ns_filename, + const char* result_file) { + xmlDocPtr doc; + xmlXPathObjectPtr xpath = NULL; + xmlChar *result = NULL; + int ret; + xmlChar **inclusive_namespaces = NULL; + const char *nslist = NULL; + int nssize; + + + /* + * build an XML tree from a the file; we need to add default + * attributes and resolve all character and entities references + */ + xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS; + xmlSubstituteEntitiesDefault(1); + + doc = xmlReadFile(xml_filename, NULL, XML_PARSE_DTDATTR | XML_PARSE_NOENT); + if (doc == NULL) { + fprintf(stderr, "Error: unable to parse file \"%s\"\n", xml_filename); + return(-1); + } + + /* + * Check the document is of the right kind + */ + if(xmlDocGetRootElement(doc) == NULL) { + fprintf(stderr,"Error: empty document for file \"%s\"\n", xml_filename); + xmlFreeDoc(doc); + return(-1); + } + + /* + * load xpath file if specified + */ + if(xpath_filename) { + xpath = load_xpath_expr(doc, xpath_filename); + if(xpath == NULL) { + fprintf(stderr,"Error: unable to evaluate xpath expression\n"); + xmlFreeDoc(doc); + return(-1); + } + } + + if (ns_filename != NULL) { + if (loadMem(ns_filename, &nslist, &nssize)) { + fprintf(stderr,"Error: unable to evaluate xpath expression\n"); + if(xpath != NULL) xmlXPathFreeObject(xpath); + xmlFreeDoc(doc); + return(-1); + } + inclusive_namespaces = parse_list((xmlChar *) nslist); + } + + /* + * Canonical form + */ + /* fprintf(stderr,"File \"%s\" loaded: start canonization\n", xml_filename); */ + ret = xmlC14NDocDumpMemory(doc, + (xpath) ? xpath->nodesetval : NULL, + exclusive, inclusive_namespaces, + with_comments, &result); + if (ret >= 0) { + if(result != NULL) { + if (compareFileMem(result_file, (const char *) result, ret)) { + fprintf(stderr, "Result mismatch for %s\n", xml_filename); + ret = -1; + } + } + } else { + fprintf(stderr,"Error: failed to canonicalize XML file \"%s\" (ret=%d)\n", xml_filename, ret); + ret = -1; + } + + /* + * Cleanup + */ + if (result != NULL) xmlFree(result); + if(xpath != NULL) xmlXPathFreeObject(xpath); + if (inclusive_namespaces != NULL) xmlFree(inclusive_namespaces); + if (nslist != NULL) free((char *) nslist); + xmlFreeDoc(doc); + + return(ret); +} + +static int +c14nCommonTest(const char *filename, int with_comments, int exclusive, + const char *subdir) { + char buf[500]; + char prefix[500]; + const char *base; + int len; + char *result = NULL; + char *xpath = NULL; + char *ns = NULL; + int ret = 0; + + base = baseFilename(filename); + len = strlen(base); + len -= 4; + memcpy(prefix, base, len); + prefix[len] = 0; + + snprintf(buf, 499, "result/c14n/%s/%s", subdir,prefix); + if (!checkTestFile(buf)) { + fprintf(stderr, "Missing result file %s", buf); + return(-1); + } + result = strdup(buf); + snprintf(buf, 499, "test/c14n/%s/%s.xpath", subdir,prefix); + if (checkTestFile(buf)) { + xpath = strdup(buf); + } + snprintf(buf, 499, "test/c14n/%s/%s.ns", subdir,prefix); + if (checkTestFile(buf)) { + ns = strdup(buf); + } + + nb_tests++; + if (c14nRunTest(filename, with_comments, exclusive, + xpath, ns, result) < 0) + ret = 1; + + if (result != NULL) free(result); + if (xpath != NULL) free(xpath); + if (ns != NULL) free(ns); + return(ret); +} + +static int +c14nWithCommentTest(const char *filename, + const char *resul ATTRIBUTE_UNUSED, + const char *err ATTRIBUTE_UNUSED, + int options ATTRIBUTE_UNUSED) { + return(c14nCommonTest(filename, 1, 0, "with-comments")); +} +static int +c14nWithoutCommentTest(const char *filename, + const char *resul ATTRIBUTE_UNUSED, + const char *err ATTRIBUTE_UNUSED, + int options ATTRIBUTE_UNUSED) { + return(c14nCommonTest(filename, 0, 0, "without-comments")); +} +static int +c14nExcWithoutCommentTest(const char *filename, + const char *resul ATTRIBUTE_UNUSED, + const char *err ATTRIBUTE_UNUSED, + int options ATTRIBUTE_UNUSED) { + return(c14nCommonTest(filename, 0, 1, "exc-without-comments")); +} +#endif +#if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED) && defined (LIBXML_SAX1_ENABLED) +/************************************************************************ + * * + * Catalog and threads test * + * * + ************************************************************************/ + +/* + * mostly a cut and paste from testThreads.c + */ +#define MAX_ARGC 20 + +static const char *catalog = "test/threads/complex.xml"; +static const char *testfiles[] = { + "test/threads/abc.xml", + "test/threads/acb.xml", + "test/threads/bac.xml", + "test/threads/bca.xml", + "test/threads/cab.xml", + "test/threads/cba.xml", + "test/threads/invalid.xml", +}; + +static const char *Okay = "OK"; +static const char *Failed = "Failed"; + +#ifndef xmlDoValidityCheckingDefaultValue +#error xmlDoValidityCheckingDefaultValue is not a macro +#endif +#ifndef xmlGenericErrorContext +#error xmlGenericErrorContext is not a macro +#endif + +static void * +thread_specific_data(void *private_data) +{ + xmlDocPtr myDoc; + const char *filename = (const char *) private_data; + int okay = 1; + + if (!strcmp(filename, "test/threads/invalid.xml")) { + xmlDoValidityCheckingDefaultValue = 0; + xmlGenericErrorContext = stdout; + } else { + xmlDoValidityCheckingDefaultValue = 1; + xmlGenericErrorContext = stderr; + } + myDoc = xmlParseFile(filename); + if (myDoc) { + xmlFreeDoc(myDoc); + } else { + printf("parse failed\n"); + okay = 0; + } + if (!strcmp(filename, "test/threads/invalid.xml")) { + if (xmlDoValidityCheckingDefaultValue != 0) { + printf("ValidityCheckingDefaultValue override failed\n"); + okay = 0; + } + if (xmlGenericErrorContext != stdout) { + printf("xmlGenericErrorContext override failed\n"); + okay = 0; + } + } else { + if (xmlDoValidityCheckingDefaultValue != 1) { + printf("ValidityCheckingDefaultValue override failed\n"); + okay = 0; + } + if (xmlGenericErrorContext != stderr) { + printf("xmlGenericErrorContext override failed\n"); + okay = 0; + } + } + if (okay == 0) + return ((void *) Failed); + return ((void *) Okay); +} + +#if defined(linux) || defined(solaris) + +#include + +static pthread_t tid[MAX_ARGC]; + +static int +testThread(void) +{ + unsigned int i, repeat; + unsigned int num_threads = sizeof(testfiles) / sizeof(testfiles[0]); + void *results[MAX_ARGC]; + int ret; + int res = 0; + + xmlInitParser(); + + for (repeat = 0; repeat < 500; repeat++) { + xmlLoadCatalog(catalog); + nb_tests++; + + for (i = 0; i < num_threads; i++) { + results[i] = NULL; + tid[i] = (pthread_t) - 1; + } + + for (i = 0; i < num_threads; i++) { + ret = pthread_create(&tid[i], 0, thread_specific_data, + (void *) testfiles[i]); + if (ret != 0) { + fprintf(stderr, "pthread_create failed\n"); + return (1); + } + } + for (i = 0; i < num_threads; i++) { + ret = pthread_join(tid[i], &results[i]); + if (ret != 0) { + fprintf(stderr, "pthread_join failed\n"); + return (1); + } + } + + xmlCatalogCleanup(); + for (i = 0; i < num_threads; i++) + if (results[i] != (void *) Okay) { + fprintf(stderr, "Thread %d handling %s failed\n", + i, testfiles[i]); + res = 1; + } + } + return (res); +} + +#elif defined WIN32 +#include +#include + +#define TEST_REPEAT_COUNT 500 + +static HANDLE tid[MAX_ARGC]; + +static DWORD WINAPI +win32_thread_specific_data(void *private_data) +{ + return((DWORD) thread_specific_data(private_data)); +} + +static int +testThread(void) +{ + unsigned int i, repeat; + unsigned int num_threads = sizeof(testfiles) / sizeof(testfiles[0]); + DWORD results[MAX_ARGC]; + BOOL ret; + int res = 0; + + xmlInitParser(); + for (repeat = 0; repeat < TEST_REPEAT_COUNT; repeat++) { + xmlLoadCatalog(catalog); + nb_tests++; + + for (i = 0; i < num_threads; i++) { + results[i] = 0; + tid[i] = (HANDLE) - 1; + } + + for (i = 0; i < num_threads; i++) { + DWORD useless; + + tid[i] = CreateThread(NULL, 0, + win32_thread_specific_data, + (void *) testfiles[i], 0, + &useless); + if (tid[i] == NULL) { + fprintf(stderr, "CreateThread failed\n"); + return(1); + } + } + + if (WaitForMultipleObjects(num_threads, tid, TRUE, INFINITE) == + WAIT_FAILED) { + fprintf(stderr, "WaitForMultipleObjects failed\n"); + return(1); + } + + for (i = 0; i < num_threads; i++) { + ret = GetExitCodeThread(tid[i], &results[i]); + if (ret == 0) { + fprintf(stderr, "GetExitCodeThread failed\n"); + return(1); + } + CloseHandle(tid[i]); + } + + xmlCatalogCleanup(); + for (i = 0; i < num_threads; i++) { + if (results[i] != (DWORD) Okay) { + fprintf(stderr, "Thread %d handling %s failed\n", + i, testfiles[i]); + res = 1; + } + } + } + + return (res); +} + +#elif defined __BEOS__ +#include + +static thread_id tid[MAX_ARGC]; + +static int +testThread(void) +{ + unsigned int i, repeat; + unsigned int num_threads = sizeof(testfiles) / sizeof(testfiles[0]); + void *results[MAX_ARGC]; + status_t ret; + int res = 0; + + xmlInitParser(); + for (repeat = 0; repeat < 500; repeat++) { + xmlLoadCatalog(catalog); + for (i = 0; i < num_threads; i++) { + results[i] = NULL; + tid[i] = (thread_id) - 1; + } + for (i = 0; i < num_threads; i++) { + tid[i] = + spawn_thread(thread_specific_data, "xmlTestThread", + B_NORMAL_PRIORITY, (void *) testfiles[i]); + if (tid[i] < B_OK) { + fprintf(stderr, "beos_thread_create failed\n"); + return (1); + } + printf("beos_thread_create %d -> %d\n", i, tid[i]); + } + for (i = 0; i < num_threads; i++) { + ret = wait_for_thread(tid[i], &results[i]); + printf("beos_thread_wait %d -> %d\n", i, ret); + if (ret != B_OK) { + fprintf(stderr, "beos_thread_wait failed\n"); + return (1); + } + } + + xmlCatalogCleanup(); + ret = B_OK; + for (i = 0; i < num_threads; i++) + if (results[i] != (void *) Okay) { + printf("Thread %d handling %s failed\n", i, testfiles[i]); + ret = B_ERROR; + } + } + if (ret != B_OK) + return(1); + return (0); +} +#else +static int +testThread(void) +{ + fprintf(stderr, + "Specific platform thread support not detected\n"); + return (-1); +} +#endif +static int +threadsTest(const char *filename ATTRIBUTE_UNUSED, + const char *resul ATTRIBUTE_UNUSED, + const char *err ATTRIBUTE_UNUSED, + int options ATTRIBUTE_UNUSED) { + return(testThread()); +} +#endif +/************************************************************************ + * * + * Tests Descriptions * + * * + ************************************************************************/ + +static +testDesc testDescriptions[] = { + { "XML regression tests" , + oldParseTest, "./test/*", "result/", "", NULL, + 0 }, + { "XML regression tests on memory" , + memParseTest, "./test/*", "result/", "", NULL, + 0 }, + { "XML entity subst regression tests" , + noentParseTest, "./test/*", "result/noent/", "", NULL, + XML_PARSE_NOENT }, + { "XML Namespaces regression tests", + errParseTest, "./test/namespaces/*", "result/namespaces/", "", ".err", + 0 }, + { "Error cases regression tests", + errParseTest, "./test/errors/*.xml", "result/errors/", "", ".err", + 0 }, +#ifdef LIBXML_READER_ENABLED + { "Error cases stream regression tests", + streamParseTest, "./test/errors/*.xml", "result/errors/", NULL, ".str", + 0 }, + { "Reader regression tests", + streamParseTest, "./test/*", "result/", ".rdr", NULL, + 0 }, + { "Reader entities substitution regression tests", + streamParseTest, "./test/*", "result/", ".rde", NULL, + XML_PARSE_NOENT }, + { "Reader on memory regression tests", + streamMemParseTest, "./test/*", "result/", ".rdr", NULL, + 0 }, + { "Walker regression tests", + walkerParseTest, "./test/*", "result/", ".rdr", NULL, + 0 }, +#endif +#ifdef LIBXML_SAX1_ENABLED + { "SAX1 callbacks regression tests" , + saxParseTest, "./test/*", "result/", ".sax", NULL, + XML_PARSE_SAX1 }, + { "SAX2 callbacks regression tests" , + saxParseTest, "./test/*", "result/", ".sax2", NULL, + 0 }, +#endif +#ifdef LIBXML_PUSH_ENABLED + { "XML push regression tests" , + pushParseTest, "./test/*", "result/", "", NULL, + 0 }, +#endif +#ifdef LIBXML_HTML_ENABLED + { "HTML regression tests" , + errParseTest, "./test/HTML/*", "result/HTML/", "", ".err", + XML_PARSE_HTML }, +#ifdef LIBXML_PUSH_ENABLED + { "Push HTML regression tests" , + pushParseTest, "./test/HTML/*", "result/HTML/", "", ".err", + XML_PARSE_HTML }, +#endif +#ifdef LIBXML_SAX1_ENABLED + { "HTML SAX regression tests" , + saxParseTest, "./test/HTML/*", "result/HTML/", ".sax", NULL, + XML_PARSE_HTML }, +#endif +#endif +#ifdef LIBXML_VALID_ENABLED + { "Valid documents regression tests" , + errParseTest, "./test/VCM/*", NULL, NULL, NULL, + XML_PARSE_DTDVALID }, + { "Validity checking regression tests" , + errParseTest, "./test/VC/*", "result/VC/", NULL, "", + XML_PARSE_DTDVALID }, + { "General documents valid regression tests" , + errParseTest, "./test/valid/*", "result/valid/", "", ".err", + XML_PARSE_DTDVALID }, +#endif +#ifdef LIBXML_XINCLUDE_ENABLED + { "XInclude regression tests" , + errParseTest, "./test/XInclude/docs/*", "result/XInclude/", "", NULL, + /* Ignore errors at this point ".err", */ + XML_PARSE_XINCLUDE }, + { "XInclude xmlReader regression tests", + streamParseTest, "./test/XInclude/docs/*", "result/XInclude/", ".rdr", + /* Ignore errors at this point ".err", */ + NULL, XML_PARSE_XINCLUDE }, + { "XInclude regression tests stripping include nodes" , + errParseTest, "./test/XInclude/docs/*", "result/XInclude/", "", NULL, + /* Ignore errors at this point ".err", */ + XML_PARSE_XINCLUDE | XML_PARSE_NOXINCNODE }, + { "XInclude xmlReader regression tests stripping include nodes", + streamParseTest, "./test/XInclude/docs/*", "result/XInclude/", ".rdr", + /* Ignore errors at this point ".err", */ + NULL, XML_PARSE_XINCLUDE | XML_PARSE_NOXINCNODE }, +#endif +#ifdef LIBXML_XPATH_ENABLED +#ifdef LIBXML_DEBUG_ENABLED + { "XPath expressions regression tests" , + xpathExprTest, "./test/XPath/expr/*", "result/XPath/expr/", "", NULL, + 0 }, + { "XPath document queries regression tests" , + xpathDocTest, "./test/XPath/docs/*", NULL, NULL, NULL, + 0 }, +#ifdef LIBXML_XPTR_ENABLED + { "XPointer document queries regression tests" , + xptrDocTest, "./test/XPath/docs/*", NULL, NULL, NULL, + 0 }, +#endif + { "xml:id regression tests" , + xmlidDocTest, "./test/xmlid/*", "result/xmlid/", "", ".err", + 0 }, +#endif +#endif + { "URI parsing tests" , + uriParseTest, "./test/URI/*.uri", "result/URI/", "", NULL, + 0 }, + { "URI base composition tests" , + uriBaseTest, "./test/URI/*.data", "result/URI/", "", NULL, + 0 }, + { "Path URI conversion tests" , + uriPathTest, NULL, NULL, NULL, NULL, + 0 }, +#ifdef LIBXML_SCHEMAS_ENABLED + { "Schemas regression tests" , + schemasTest, "./test/schemas/*_*.xsd", NULL, NULL, NULL, + 0 }, + { "Relax-NG regression tests" , + rngTest, "./test/relaxng/*.rng", NULL, NULL, NULL, + XML_PARSE_DTDATTR | XML_PARSE_NOENT }, +#ifdef LIBXML_READER_ENABLED + { "Relax-NG streaming regression tests" , + rngStreamTest, "./test/relaxng/*.rng", NULL, NULL, NULL, + XML_PARSE_DTDATTR | XML_PARSE_NOENT }, +#endif +#endif +#ifdef LIBXML_PATTERN_ENABLED +#ifdef LIBXML_READER_ENABLED + { "Pattern regression tests" , + patternTest, "./test/pattern/*.pat", "result/pattern/", NULL, NULL, + 0 }, +#endif +#endif +#ifdef LIBXML_C14N_ENABLED + { "C14N with comments regression tests" , + c14nWithCommentTest, "./test/c14n/with-comments/*.xml", NULL, NULL, NULL, + 0 }, + { "C14N without comments regression tests" , + c14nWithoutCommentTest, "./test/c14n/without-comments/*.xml", NULL, NULL, NULL, + 0 }, + { "C14N exclusive without comments regression tests" , + c14nExcWithoutCommentTest, "./test/c14n/exc-without-comments/*.xml", NULL, NULL, NULL, + 0 }, +#endif +#if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED) && defined(LIBXML_SAX1_ENABLED) + { "Catalog and Threads regression tests" , + threadsTest, NULL, NULL, NULL, NULL, + 0 }, +#endif + {NULL, NULL, NULL, NULL, NULL, NULL, 0} +}; + +/************************************************************************ + * * + * The main code driving the tests * + * * + ************************************************************************/ + +static int +launchTests(testDescPtr tst) { + int res = 0, err = 0; + size_t i; + char *result; + char *error; + int mem; + + if (tst == NULL) return(-1); + if (tst->in != NULL) { + glob_t globbuf; + + globbuf.gl_offs = 0; + glob(tst->in, GLOB_DOOFFS, NULL, &globbuf); + for (i = 0;i < globbuf.gl_pathc;i++) { + if (!checkTestFile(globbuf.gl_pathv[i])) + continue; + if (tst->suffix != NULL) { + result = resultFilename(globbuf.gl_pathv[i], tst->out, + tst->suffix); + if (result == NULL) { + fprintf(stderr, "Out of memory !\n"); + fatalError(); + } + } else { + result = NULL; + } + if (tst->err != NULL) { + error = resultFilename(globbuf.gl_pathv[i], tst->out, + tst->err); + if (error == NULL) { + fprintf(stderr, "Out of memory !\n"); + fatalError(); + } + } else { + error = NULL; + } + if ((result) &&(!checkTestFile(result))) { + fprintf(stderr, "Missing result file %s\n", result); + } else if ((error) &&(!checkTestFile(error))) { + fprintf(stderr, "Missing error file %s\n", error); + } else { + mem = xmlMemUsed(); + extraMemoryFromResolver = 0; + testErrorsSize = 0; + testErrors[0] = 0; + res = tst->func(globbuf.gl_pathv[i], result, error, + tst->options | XML_PARSE_COMPACT); + xmlResetLastError(); + if (res != 0) { + fprintf(stderr, "File %s generated an error\n", + globbuf.gl_pathv[i]); + nb_errors++; + err++; + } + else if (xmlMemUsed() != mem) { + if ((xmlMemUsed() != mem) && + (extraMemoryFromResolver == 0)) { + fprintf(stderr, "File %s leaked %d bytes\n", + globbuf.gl_pathv[i], xmlMemUsed() - mem); + nb_leaks++; + err++; + } + } + testErrorsSize = 0; + } + if (result) + free(result); + if (error) + free(error); + } + globfree(&globbuf); + } else { + testErrorsSize = 0; + testErrors[0] = 0; + extraMemoryFromResolver = 0; + res = tst->func(NULL, NULL, NULL, tst->options); + if (res != 0) { + nb_errors++; + err++; + } + } + return(err); +} + +static int verbose = 0; +static int tests_quiet = 0; + +static int +runtest(int i) { + int ret = 0, res; + int old_errors, old_tests, old_leaks; + + old_errors = nb_errors; + old_tests = nb_tests; + old_leaks = nb_leaks; + if ((tests_quiet == 0) && (testDescriptions[i].desc != NULL)) + printf("## %s\n", testDescriptions[i].desc); + res = launchTests(&testDescriptions[i]); + if (res != 0) + ret++; + if (verbose) { + if ((nb_errors == old_errors) && (nb_leaks == old_leaks)) + printf("Ran %d tests, no errors\n", nb_tests - old_tests); + else + printf("Ran %d tests, %d errors, %d leaks\n", + nb_tests - old_tests, + nb_errors - old_errors, + nb_leaks - old_leaks); + } + return(ret); +} + +int +main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { + int i, a, ret = 0; + int subset = 0; + + initializeLibxml2(); + + + for (a = 1; a < argc;a++) { + if (!strcmp(argv[a], "-v")) + verbose = 1; + else if (!strcmp(argv[a], "-quiet")) + tests_quiet = 1; + else { + for (i = 0; testDescriptions[i].func != NULL; i++) { + if (strstr(testDescriptions[i].desc, argv[a])) { + ret += runtest(i); + subset++; + } + } + } + } + if (subset == 0) { + for (i = 0; testDescriptions[i].func != NULL; i++) { + ret += runtest(i); + } + } + if ((nb_errors == 0) && (nb_leaks == 0)) { + ret = 0; + printf("Total %d tests, no errors\n", + nb_tests); + } else { + ret = 1; + printf("Total %d tests, %d errors, %d leaks\n", + nb_tests, nb_errors, nb_leaks); + } + xmlCleanupParser(); + xmlMemoryDump(); + + return(ret); +} + +#else /* ! LIBXML_OUTPUT_ENABLED */ +int +main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { + fprintf(stderr, "runtest requires output to be enabled in libxml2\n"); + return(1); +} +#endif diff --git a/Extras/LibXML/schematron.c b/Extras/LibXML/schematron.c new file mode 100644 index 000000000..0a90ff801 --- /dev/null +++ b/Extras/LibXML/schematron.c @@ -0,0 +1,1730 @@ +/* + * schematron.c : implementation of the Schematron schema validity checking + * + * See Copyright for the status of this software. + * + * Daniel Veillard + */ + +/* + * TODO: + * + double check the semantic, especially + * - multiple rules applying in a single pattern/node + * - the semantic of libxml2 patterns vs. XSLT production referenced + * by the spec. + * + export of results in SVRL + * + full parsing and coverage of the spec, conformance of the input to the + * spec + * + divergences between the draft and the ISO proposed standard :-( + * + hook and test include + * + try and compare with the XSLT version + */ + +#define IN_LIBXML +#include "libxml.h" + +#ifdef LIBXML_SCHEMATRON_ENABLED + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SCHEMATRON_PARSE_OPTIONS XML_PARSE_NOENT + +#define SCT_OLD_NS BAD_CAST "http://www.ascc.net/xml/schematron" + +#define XML_SCHEMATRON_NS BAD_CAST "http://purl.oclc.org/dsdl/schematron" + + +static const xmlChar *xmlSchematronNs = XML_SCHEMATRON_NS; +static const xmlChar *xmlOldSchematronNs = SCT_OLD_NS; + +#define IS_SCHEMATRON(node, elem) \ + ((node != NULL) && (node->type == XML_ELEMENT_NODE ) && \ + (node->ns != NULL) && \ + (xmlStrEqual(node->name, (const xmlChar *) elem)) && \ + ((xmlStrEqual(node->ns->href, xmlSchematronNs)) || \ + (xmlStrEqual(node->ns->href, xmlOldSchematronNs)))) + +#define NEXT_SCHEMATRON(node) \ + while (node != NULL) { \ + if ((node->type == XML_ELEMENT_NODE ) && (node->ns != NULL) && \ + ((xmlStrEqual(node->ns->href, xmlSchematronNs)) || \ + (xmlStrEqual(node->ns->href, xmlOldSchematronNs)))) \ + break; \ + node = node->next; \ + } + +/** + * TODO: + * + * macro to flag unimplemented blocks + */ +#define TODO \ + xmlGenericError(xmlGenericErrorContext, \ + "Unimplemented block at %s:%d\n", \ + __FILE__, __LINE__); + +typedef enum { + XML_SCHEMATRON_ASSERT=1, + XML_SCHEMATRON_REPORT=2 +} xmlSchematronTestType; + +/** + * _xmlSchematronTest: + * + * A Schematrons test, either an assert or a report + */ +typedef struct _xmlSchematronTest xmlSchematronTest; +typedef xmlSchematronTest *xmlSchematronTestPtr; +struct _xmlSchematronTest { + xmlSchematronTestPtr next; /* the next test in the list */ + xmlSchematronTestType type; /* the test type */ + xmlNodePtr node; /* the node in the tree */ + xmlChar *test; /* the expression to test */ + xmlXPathCompExprPtr comp; /* the compiled expression */ + xmlChar *report; /* the message to report */ +}; + +/** + * _xmlSchematronRule: + * + * A Schematrons rule + */ +typedef struct _xmlSchematronRule xmlSchematronRule; +typedef xmlSchematronRule *xmlSchematronRulePtr; +struct _xmlSchematronRule { + xmlSchematronRulePtr next; /* the next rule in the list */ + xmlSchematronRulePtr patnext;/* the next rule in the pattern list */ + xmlNodePtr node; /* the node in the tree */ + xmlChar *context; /* the context evaluation rule */ + xmlSchematronTestPtr tests; /* the list of tests */ + xmlPatternPtr pattern; /* the compiled pattern associated */ + xmlChar *report; /* the message to report */ +}; + +/** + * _xmlSchematronPattern: + * + * A Schematrons pattern + */ +typedef struct _xmlSchematronPattern xmlSchematronPattern; +typedef xmlSchematronPattern *xmlSchematronPatternPtr; +struct _xmlSchematronPattern { + xmlSchematronPatternPtr next;/* the next pattern in the list */ + xmlSchematronRulePtr rules; /* the list of rules */ + xmlChar *name; /* the name of the pattern */ +}; + +/** + * _xmlSchematron: + * + * A Schematrons definition + */ +struct _xmlSchematron { + const xmlChar *name; /* schema name */ + int preserve; /* was the document passed by the user */ + xmlDocPtr doc; /* pointer to the parsed document */ + int flags; /* specific to this schematron */ + + void *_private; /* unused by the library */ + xmlDictPtr dict; /* the dictionnary used internally */ + + const xmlChar *title; /* the title if any */ + + int nbNs; /* the number of namespaces */ + + int nbPattern; /* the number of patterns */ + xmlSchematronPatternPtr patterns;/* the patterns found */ + xmlSchematronRulePtr rules; /* the rules gathered */ + int nbNamespaces; /* number of namespaces in the array */ + int maxNamespaces; /* size of the array */ + const xmlChar **namespaces; /* the array of namespaces */ +}; + +/** + * xmlSchematronValidCtxt: + * + * A Schematrons validation context + */ +struct _xmlSchematronValidCtxt { + int type; + int flags; /* an or of xmlSchematronValidOptions */ + + xmlDictPtr dict; + int nberrors; + int err; + + xmlSchematronPtr schema; + xmlXPathContextPtr xctxt; + + FILE *outputFile; /* if using XML_SCHEMATRON_OUT_FILE */ + xmlBufferPtr outputBuffer; /* if using XML_SCHEMATRON_OUT_BUFFER */ + xmlOutputWriteCallback iowrite; /* if using XML_SCHEMATRON_OUT_IO */ + xmlOutputCloseCallback ioclose; + void *ioctx; +}; + +struct _xmlSchematronParserCtxt { + int type; + const xmlChar *URL; + xmlDocPtr doc; + int preserve; /* Whether the doc should be freed */ + const char *buffer; + int size; + + xmlDictPtr dict; /* dictionnary for interned string names */ + + int nberrors; + int err; + xmlXPathContextPtr xctxt; /* the XPath context used for compilation */ + xmlSchematronPtr schema; + + int nbNamespaces; /* number of namespaces in the array */ + int maxNamespaces; /* size of the array */ + const xmlChar **namespaces; /* the array of namespaces */ + + int nbIncludes; /* number of includes in the array */ + int maxIncludes; /* size of the array */ + xmlNodePtr *includes; /* the array of includes */ + + /* error rreporting data */ + void *userData; /* user specific data block */ + xmlSchematronValidityErrorFunc error;/* the callback in case of errors */ + xmlSchematronValidityWarningFunc warning;/* callback in case of warning */ + xmlStructuredErrorFunc serror; /* the structured function */ + +}; + +#define XML_STRON_CTXT_PARSER 1 +#define XML_STRON_CTXT_VALIDATOR 2 + +/************************************************************************ + * * + * Error reporting * + * * + ************************************************************************/ + +/** + * xmlSchematronPErrMemory: + * @node: a context node + * @extra: extra informations + * + * Handle an out of memory condition + */ +static void +xmlSchematronPErrMemory(xmlSchematronParserCtxtPtr ctxt, + const char *extra, xmlNodePtr node) +{ + if (ctxt != NULL) + ctxt->nberrors++; + __xmlSimpleError(XML_FROM_SCHEMASP, XML_ERR_NO_MEMORY, node, NULL, + extra); +} + +/** + * xmlSchematronPErr: + * @ctxt: the parsing context + * @node: the context node + * @error: the error code + * @msg: the error message + * @str1: extra data + * @str2: extra data + * + * Handle a parser error + */ +static void +xmlSchematronPErr(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr node, int error, + const char *msg, const xmlChar * str1, const xmlChar * str2) +{ + xmlGenericErrorFunc channel = NULL; + xmlStructuredErrorFunc schannel = NULL; + void *data = NULL; + + if (ctxt != NULL) { + ctxt->nberrors++; + channel = ctxt->error; + data = ctxt->userData; + schannel = ctxt->serror; + } + __xmlRaiseError(schannel, channel, data, ctxt, node, XML_FROM_SCHEMASP, + error, XML_ERR_ERROR, NULL, 0, + (const char *) str1, (const char *) str2, NULL, 0, 0, + msg, str1, str2); +} + +/** + * xmlSchematronVTypeErrMemory: + * @node: a context node + * @extra: extra informations + * + * Handle an out of memory condition + */ +static void +xmlSchematronVErrMemory(xmlSchematronValidCtxtPtr ctxt, + const char *extra, xmlNodePtr node) +{ + if (ctxt != NULL) { + ctxt->nberrors++; + ctxt->err = XML_SCHEMAV_INTERNAL; + } + __xmlSimpleError(XML_FROM_SCHEMASV, XML_ERR_NO_MEMORY, node, NULL, + extra); +} + +/************************************************************************ + * * + * Parsing and compilation of the Schematrontrons * + * * + ************************************************************************/ + +/** + * xmlSchematronAddTest: + * @ctxt: the schema parsing context + * @type: the type of test + * @rule: the parent rule + * @node: the node hosting the test + * @test: the associated test + * @report: the associated report string + * + * Add a test to a schematron + * + * Returns the new pointer or NULL in case of error + */ +static xmlSchematronTestPtr +xmlSchematronAddTest(xmlSchematronParserCtxtPtr ctxt, + xmlSchematronTestType type, + xmlSchematronRulePtr rule, + xmlNodePtr node, xmlChar *test, xmlChar *report) +{ + xmlSchematronTestPtr ret; + xmlXPathCompExprPtr comp; + + if ((ctxt == NULL) || (rule == NULL) || (node == NULL) || + (test == NULL)) + return(NULL); + + /* + * try first to compile the test expression + */ + comp = xmlXPathCtxtCompile(ctxt->xctxt, test); + if (comp == NULL) { + xmlSchematronPErr(ctxt, node, + XML_SCHEMAP_NOROOT, + "Failed to compile test expression %s", + test, NULL); + return(NULL); + } + + ret = (xmlSchematronTestPtr) xmlMalloc(sizeof(xmlSchematronTest)); + if (ret == NULL) { + xmlSchematronPErrMemory(ctxt, "allocating schema test", node); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchematronTest)); + ret->type = type; + ret->node = node; + ret->test = test; + ret->comp = comp; + ret->report = report; + ret->next = NULL; + if (rule->tests == NULL) { + rule->tests = ret; + } else { + xmlSchematronTestPtr prev = rule->tests; + + while (prev->next != NULL) + prev = prev->next; + prev->next = ret; + } + return (ret); +} + +/** + * xmlSchematronFreeTests: + * @tests: a list of tests + * + * Free a list of tests. + */ +static void +xmlSchematronFreeTests(xmlSchematronTestPtr tests) { + xmlSchematronTestPtr next; + + while (tests != NULL) { + next = tests->next; + if (tests->test != NULL) + xmlFree(tests->test); + if (tests->comp != NULL) + xmlXPathFreeCompExpr(tests->comp); + if (tests->report != NULL) + xmlFree(tests->report); + xmlFree(tests); + tests = next; + } +} + +/** + * xmlSchematronAddRule: + * @ctxt: the schema parsing context + * @schema: a schema structure + * @node: the node hosting the rule + * @context: the associated context string + * @report: the associated report string + * + * Add a rule to a schematron + * + * Returns the new pointer or NULL in case of error + */ +static xmlSchematronRulePtr +xmlSchematronAddRule(xmlSchematronParserCtxtPtr ctxt, xmlSchematronPtr schema, + xmlSchematronPatternPtr pat, xmlNodePtr node, + xmlChar *context, xmlChar *report) +{ + xmlSchematronRulePtr ret; + xmlPatternPtr pattern; + + if ((ctxt == NULL) || (schema == NULL) || (node == NULL) || + (context == NULL)) + return(NULL); + + /* + * Try first to compile the pattern + */ + pattern = xmlPatterncompile(context, ctxt->dict, XML_PATTERN_XPATH, + ctxt->namespaces); + if (pattern == NULL) { + xmlSchematronPErr(ctxt, node, + XML_SCHEMAP_NOROOT, + "Failed to compile context expression %s", + context, NULL); + } + + ret = (xmlSchematronRulePtr) xmlMalloc(sizeof(xmlSchematronRule)); + if (ret == NULL) { + xmlSchematronPErrMemory(ctxt, "allocating schema rule", node); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchematronRule)); + ret->node = node; + ret->context = context; + ret->pattern = pattern; + ret->report = report; + ret->next = NULL; + if (schema->rules == NULL) { + schema->rules = ret; + } else { + xmlSchematronRulePtr prev = schema->rules; + + while (prev->next != NULL) + prev = prev->next; + prev->next = ret; + } + ret->patnext = NULL; + if (pat->rules == NULL) { + pat->rules = ret; + } else { + xmlSchematronRulePtr prev = pat->rules; + + while (prev->patnext != NULL) + prev = prev->patnext; + prev->patnext = ret; + } + return (ret); +} + +/** + * xmlSchematronFreeRules: + * @rules: a list of rules + * + * Free a list of rules. + */ +static void +xmlSchematronFreeRules(xmlSchematronRulePtr rules) { + xmlSchematronRulePtr next; + + while (rules != NULL) { + next = rules->next; + if (rules->tests) + xmlSchematronFreeTests(rules->tests); + if (rules->context != NULL) + xmlFree(rules->context); + if (rules->pattern) + xmlFreePattern(rules->pattern); + if (rules->report != NULL) + xmlFree(rules->report); + xmlFree(rules); + rules = next; + } +} + +/** + * xmlSchematronAddPattern: + * @ctxt: the schema parsing context + * @schema: a schema structure + * @node: the node hosting the pattern + * @id: the id or name of the pattern + * + * Add a pattern to a schematron + * + * Returns the new pointer or NULL in case of error + */ +static xmlSchematronPatternPtr +xmlSchematronAddPattern(xmlSchematronParserCtxtPtr ctxt, + xmlSchematronPtr schema, xmlNodePtr node, xmlChar *name) +{ + xmlSchematronPatternPtr ret; + + if ((ctxt == NULL) || (schema == NULL) || (node == NULL) || (name == NULL)) + return(NULL); + + ret = (xmlSchematronPatternPtr) xmlMalloc(sizeof(xmlSchematronPattern)); + if (ret == NULL) { + xmlSchematronPErrMemory(ctxt, "allocating schema pattern", node); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchematronPattern)); + ret->name = name; + ret->next = NULL; + if (schema->patterns == NULL) { + schema->patterns = ret; + } else { + xmlSchematronPatternPtr prev = schema->patterns; + + while (prev->next != NULL) + prev = prev->next; + prev->next = ret; + } + return (ret); +} + +/** + * xmlSchematronFreePatterns: + * @patterns: a list of patterns + * + * Free a list of patterns. + */ +static void +xmlSchematronFreePatterns(xmlSchematronPatternPtr patterns) { + xmlSchematronPatternPtr next; + + while (patterns != NULL) { + next = patterns->next; + if (patterns->name != NULL) + xmlFree(patterns->name); + xmlFree(patterns); + patterns = next; + } +} + +/** + * xmlSchematronNewSchematron: + * @ctxt: a schema validation context + * + * Allocate a new Schematron structure. + * + * Returns the newly allocated structure or NULL in case or error + */ +static xmlSchematronPtr +xmlSchematronNewSchematron(xmlSchematronParserCtxtPtr ctxt) +{ + xmlSchematronPtr ret; + + ret = (xmlSchematronPtr) xmlMalloc(sizeof(xmlSchematron)); + if (ret == NULL) { + xmlSchematronPErrMemory(ctxt, "allocating schema", NULL); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchematron)); + ret->dict = ctxt->dict; + xmlDictReference(ret->dict); + + return (ret); +} + +/** + * xmlSchematronFree: + * @schema: a schema structure + * + * Deallocate a Schematron structure. + */ +void +xmlSchematronFree(xmlSchematronPtr schema) +{ + if (schema == NULL) + return; + + if ((schema->doc != NULL) && (!(schema->preserve))) + xmlFreeDoc(schema->doc); + + if (schema->namespaces != NULL) + xmlFree((char **) schema->namespaces); + + xmlSchematronFreeRules(schema->rules); + xmlSchematronFreePatterns(schema->patterns); + xmlDictFree(schema->dict); + xmlFree(schema); +} + +/** + * xmlSchematronNewParserCtxt: + * @URL: the location of the schema + * + * Create an XML Schematrons parse context for that file/resource expected + * to contain an XML Schematrons file. + * + * Returns the parser context or NULL in case of error + */ +xmlSchematronParserCtxtPtr +xmlSchematronNewParserCtxt(const char *URL) +{ + xmlSchematronParserCtxtPtr ret; + + if (URL == NULL) + return (NULL); + + ret = + (xmlSchematronParserCtxtPtr) + xmlMalloc(sizeof(xmlSchematronParserCtxt)); + if (ret == NULL) { + xmlSchematronPErrMemory(NULL, "allocating schema parser context", + NULL); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchematronParserCtxt)); + ret->type = XML_STRON_CTXT_PARSER; + ret->dict = xmlDictCreate(); + ret->URL = xmlDictLookup(ret->dict, (const xmlChar *) URL, -1); + ret->includes = NULL; + ret->xctxt = xmlXPathNewContext(NULL); + if (ret->xctxt == NULL) { + xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context", + NULL); + xmlSchematronFreeParserCtxt(ret); + return (NULL); + } + ret->xctxt->flags = XML_XPATH_CHECKNS; + return (ret); +} + +/** + * xmlSchematronNewMemParserCtxt: + * @buffer: a pointer to a char array containing the schemas + * @size: the size of the array + * + * Create an XML Schematrons parse context for that memory buffer expected + * to contain an XML Schematrons file. + * + * Returns the parser context or NULL in case of error + */ +xmlSchematronParserCtxtPtr +xmlSchematronNewMemParserCtxt(const char *buffer, int size) +{ + xmlSchematronParserCtxtPtr ret; + + if ((buffer == NULL) || (size <= 0)) + return (NULL); + + ret = + (xmlSchematronParserCtxtPtr) + xmlMalloc(sizeof(xmlSchematronParserCtxt)); + if (ret == NULL) { + xmlSchematronPErrMemory(NULL, "allocating schema parser context", + NULL); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchematronParserCtxt)); + ret->buffer = buffer; + ret->size = size; + ret->dict = xmlDictCreate(); + ret->xctxt = xmlXPathNewContext(NULL); + if (ret->xctxt == NULL) { + xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context", + NULL); + xmlSchematronFreeParserCtxt(ret); + return (NULL); + } + return (ret); +} + +/** + * xmlSchematronNewDocParserCtxt: + * @doc: a preparsed document tree + * + * Create an XML Schematrons parse context for that document. + * NB. The document may be modified during the parsing process. + * + * Returns the parser context or NULL in case of error + */ +xmlSchematronParserCtxtPtr +xmlSchematronNewDocParserCtxt(xmlDocPtr doc) +{ + xmlSchematronParserCtxtPtr ret; + + if (doc == NULL) + return (NULL); + + ret = + (xmlSchematronParserCtxtPtr) + xmlMalloc(sizeof(xmlSchematronParserCtxt)); + if (ret == NULL) { + xmlSchematronPErrMemory(NULL, "allocating schema parser context", + NULL); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchematronParserCtxt)); + ret->doc = doc; + ret->dict = xmlDictCreate(); + /* The application has responsibility for the document */ + ret->preserve = 1; + ret->xctxt = xmlXPathNewContext(doc); + if (ret->xctxt == NULL) { + xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context", + NULL); + xmlSchematronFreeParserCtxt(ret); + return (NULL); + } + + return (ret); +} + +/** + * xmlSchematronFreeParserCtxt: + * @ctxt: the schema parser context + * + * Free the resources associated to the schema parser context + */ +void +xmlSchematronFreeParserCtxt(xmlSchematronParserCtxtPtr ctxt) +{ + if (ctxt == NULL) + return; + if (ctxt->doc != NULL && !ctxt->preserve) + xmlFreeDoc(ctxt->doc); + if (ctxt->xctxt != NULL) { + xmlXPathFreeContext(ctxt->xctxt); + } + if (ctxt->namespaces != NULL) + xmlFree((char **) ctxt->namespaces); + xmlDictFree(ctxt->dict); + xmlFree(ctxt); +} + +/** + * xmlSchematronPushInclude: + * @ctxt: the schema parser context + * @doc: the included document + * @cur: the current include node + * + * Add an included document + */ +static void +xmlSchematronPushInclude(xmlSchematronParserCtxtPtr ctxt, + xmlDocPtr doc, xmlNodePtr cur) +{ + if (ctxt->includes == NULL) { + ctxt->maxIncludes = 10; + ctxt->includes = (xmlNodePtr *) + xmlMalloc(ctxt->maxIncludes * 2 * sizeof(xmlNodePtr)); + if (ctxt->includes == NULL) { + xmlSchematronPErrMemory(NULL, "allocating parser includes", + NULL); + return; + } + ctxt->nbIncludes = 0; + } else if (ctxt->nbIncludes + 2 >= ctxt->maxIncludes) { + xmlNodePtr *tmp; + + tmp = (xmlNodePtr *) + xmlRealloc(ctxt->includes, ctxt->maxIncludes * 4 * + sizeof(xmlNodePtr)); + if (tmp == NULL) { + xmlSchematronPErrMemory(NULL, "allocating parser includes", + NULL); + return; + } + ctxt->includes = tmp; + ctxt->maxIncludes *= 2; + } + ctxt->includes[2 * ctxt->nbIncludes] = cur; + ctxt->includes[2 * ctxt->nbIncludes + 1] = (xmlNodePtr) doc; + ctxt->nbIncludes++; +} + +/** + * xmlSchematronPopInclude: + * @ctxt: the schema parser context + * + * Pop an include level. The included document is being freed + * + * Returns the node immediately following the include or NULL if the + * include list was empty. + */ +static xmlNodePtr +xmlSchematronPopInclude(xmlSchematronParserCtxtPtr ctxt) +{ + xmlDocPtr doc; + xmlNodePtr ret; + + if (ctxt->nbIncludes <= 0) + return(NULL); + ctxt->nbIncludes--; + doc = (xmlDocPtr) ctxt->includes[2 * ctxt->nbIncludes + 1]; + ret = ctxt->includes[2 * ctxt->nbIncludes]; + xmlFreeDoc(doc); + if (ret != NULL) + ret = ret->next; + if (ret == NULL) + return(xmlSchematronPopInclude(ctxt)); + return(ret); +} + +/** + * xmlSchematronAddNamespace: + * @ctxt: the schema parser context + * @prefix: the namespace prefix + * @ns: the namespace name + * + * Add a namespace definition in the context + */ +static void +xmlSchematronAddNamespace(xmlSchematronParserCtxtPtr ctxt, + const xmlChar *prefix, const xmlChar *ns) +{ + if (ctxt->namespaces == NULL) { + ctxt->maxNamespaces = 10; + ctxt->namespaces = (const xmlChar **) + xmlMalloc(ctxt->maxNamespaces * 2 * sizeof(const xmlChar *)); + if (ctxt->namespaces == NULL) { + xmlSchematronPErrMemory(NULL, "allocating parser namespaces", + NULL); + return; + } + ctxt->nbNamespaces = 0; + } else if (ctxt->nbNamespaces + 2 >= ctxt->maxNamespaces) { + const xmlChar **tmp; + + tmp = (const xmlChar **) + xmlRealloc((xmlChar **) ctxt->namespaces, ctxt->maxNamespaces * 4 * + sizeof(const xmlChar *)); + if (tmp == NULL) { + xmlSchematronPErrMemory(NULL, "allocating parser namespaces", + NULL); + return; + } + ctxt->namespaces = tmp; + ctxt->maxNamespaces *= 2; + } + ctxt->namespaces[2 * ctxt->nbNamespaces] = + xmlDictLookup(ctxt->dict, ns, -1); + ctxt->namespaces[2 * ctxt->nbNamespaces + 1] = + xmlDictLookup(ctxt->dict, prefix, -1); + ctxt->nbNamespaces++; + ctxt->namespaces[2 * ctxt->nbNamespaces] = NULL; + ctxt->namespaces[2 * ctxt->nbNamespaces + 1] = NULL; + +} + +/** + * xmlSchematronParseRule: + * @ctxt: a schema validation context + * @rule: the rule node + * + * parse a rule element + */ +static void +xmlSchematronParseRule(xmlSchematronParserCtxtPtr ctxt, + xmlSchematronPatternPtr pattern, + xmlNodePtr rule) +{ + xmlNodePtr cur; + int nbChecks = 0; + xmlChar *test; + xmlChar *context; + xmlChar *report; + xmlSchematronRulePtr ruleptr; + xmlSchematronTestPtr testptr; + + if ((ctxt == NULL) || (rule == NULL)) return; + + context = xmlGetNoNsProp(rule, BAD_CAST "context"); + if (context == NULL) { + xmlSchematronPErr(ctxt, rule, + XML_SCHEMAP_NOROOT, + "rule has no context attribute", + NULL, NULL); + return; + } else if (context[0] == 0) { + xmlSchematronPErr(ctxt, rule, + XML_SCHEMAP_NOROOT, + "rule has an empty context attribute", + NULL, NULL); + xmlFree(context); + return; + } else { + ruleptr = xmlSchematronAddRule(ctxt, ctxt->schema, pattern, + rule, context, NULL); + if (ruleptr == NULL) { + xmlFree(context); + return; + } + } + + cur = rule->children; + NEXT_SCHEMATRON(cur); + while (cur != NULL) { + if (IS_SCHEMATRON(cur, "assert")) { + nbChecks++; + test = xmlGetNoNsProp(cur, BAD_CAST "test"); + if (test == NULL) { + xmlSchematronPErr(ctxt, cur, + XML_SCHEMAP_NOROOT, + "assert has no test attribute", + NULL, NULL); + } else if (test[0] == 0) { + xmlSchematronPErr(ctxt, cur, + XML_SCHEMAP_NOROOT, + "assert has an empty test attribute", + NULL, NULL); + xmlFree(test); + } else { + /* TODO will need dynamic processing instead */ + report = xmlNodeGetContent(cur); + + testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_ASSERT, + ruleptr, cur, test, report); + if (testptr == NULL) + xmlFree(test); + } + } else if (IS_SCHEMATRON(cur, "report")) { + nbChecks++; + test = xmlGetNoNsProp(cur, BAD_CAST "test"); + if (test == NULL) { + xmlSchematronPErr(ctxt, cur, + XML_SCHEMAP_NOROOT, + "assert has no test attribute", + NULL, NULL); + } else if (test[0] == 0) { + xmlSchematronPErr(ctxt, cur, + XML_SCHEMAP_NOROOT, + "assert has an empty test attribute", + NULL, NULL); + xmlFree(test); + } else { + /* TODO will need dynamic processing instead */ + report = xmlNodeGetContent(cur); + + testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_REPORT, + ruleptr, cur, test, report); + if (testptr == NULL) + xmlFree(test); + } + } else { + xmlSchematronPErr(ctxt, cur, + XML_SCHEMAP_NOROOT, + "Expecting an assert or a report element instead of %s", + cur->name, NULL); + } + cur = cur->next; + NEXT_SCHEMATRON(cur); + } + if (nbChecks == 0) { + xmlSchematronPErr(ctxt, rule, + XML_SCHEMAP_NOROOT, + "rule has no assert nor report element", NULL, NULL); + } +} + +/** + * xmlSchematronParsePattern: + * @ctxt: a schema validation context + * @pat: the pattern node + * + * parse a pattern element + */ +static void +xmlSchematronParsePattern(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr pat) +{ + xmlNodePtr cur; + xmlSchematronPatternPtr pattern; + int nbRules = 0; + xmlChar *id; + + if ((ctxt == NULL) || (pat == NULL)) return; + + id = xmlGetNoNsProp(pat, BAD_CAST "id"); + if (id == NULL) { + id = xmlGetNoNsProp(pat, BAD_CAST "name"); + } + pattern = xmlSchematronAddPattern(ctxt, ctxt->schema, pat, id); + if (pattern == NULL) { + if (id != NULL) + xmlFree(id); + return; + } + cur = pat->children; + NEXT_SCHEMATRON(cur); + while (cur != NULL) { + if (IS_SCHEMATRON(cur, "rule")) { + xmlSchematronParseRule(ctxt, pattern, cur); + nbRules++; + } else { + xmlSchematronPErr(ctxt, cur, + XML_SCHEMAP_NOROOT, + "Expecting a rule element instead of %s", cur->name, NULL); + } + cur = cur->next; + NEXT_SCHEMATRON(cur); + } + if (nbRules == 0) { + xmlSchematronPErr(ctxt, pat, + XML_SCHEMAP_NOROOT, + "Pattern has no rule element", NULL, NULL); + } +} + +/** + * xmlSchematronLoadInclude: + * @ctxt: a schema validation context + * @cur: the include element + * + * Load the include document, Push the current pointer + * + * Returns the updated node pointer + */ +static xmlNodePtr +xmlSchematronLoadInclude(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr cur) +{ + xmlNodePtr ret = NULL; + xmlDocPtr doc = NULL; + xmlChar *href = NULL; + xmlChar *base = NULL; + xmlChar *URI = NULL; + + if ((ctxt == NULL) || (cur == NULL)) + return(NULL); + + href = xmlGetNoNsProp(cur, BAD_CAST "href"); + if (href == NULL) { + xmlSchematronPErr(ctxt, cur, + XML_SCHEMAP_NOROOT, + "Include has no href attribute", NULL, NULL); + return(cur->next); + } + + /* do the URI base composition, load and find the root */ + base = xmlNodeGetBase(cur->doc, cur); + URI = xmlBuildURI(href, base); + doc = xmlReadFile((const char *) URI, NULL, SCHEMATRON_PARSE_OPTIONS); + if (doc == NULL) { + xmlSchematronPErr(ctxt, cur, + XML_SCHEMAP_FAILED_LOAD, + "could not load include '%s'.\n", + URI, NULL); + goto done; + } + ret = xmlDocGetRootElement(doc); + if (ret == NULL) { + xmlSchematronPErr(ctxt, cur, + XML_SCHEMAP_FAILED_LOAD, + "could not find root from include '%s'.\n", + URI, NULL); + goto done; + } + + /* Success, push the include for rollback on exit */ + xmlSchematronPushInclude(ctxt, doc, cur); + +done: + if (ret == NULL) { + if (doc != NULL) + xmlFreeDoc(doc); + } + xmlFree(href); + if (base != NULL) + xmlFree(base); + if (URI != NULL) + xmlFree(URI); + return(ret); +} + +/** + * xmlSchematronParse: + * @ctxt: a schema validation context + * + * parse a schema definition resource and build an internal + * XML Shema struture which can be used to validate instances. + * + * Returns the internal XML Schematron structure built from the resource or + * NULL in case of error + */ +xmlSchematronPtr +xmlSchematronParse(xmlSchematronParserCtxtPtr ctxt) +{ + xmlSchematronPtr ret = NULL; + xmlDocPtr doc; + xmlNodePtr root, cur; + int preserve = 0; + + if (ctxt == NULL) + return (NULL); + + ctxt->nberrors = 0; + + /* + * First step is to parse the input document into an DOM/Infoset + */ + if (ctxt->URL != NULL) { + doc = xmlReadFile((const char *) ctxt->URL, NULL, + SCHEMATRON_PARSE_OPTIONS); + if (doc == NULL) { + xmlSchematronPErr(ctxt, NULL, + XML_SCHEMAP_FAILED_LOAD, + "xmlSchematronParse: could not load '%s'.\n", + ctxt->URL, NULL); + return (NULL); + } + ctxt->preserve = 0; + } else if (ctxt->buffer != NULL) { + doc = xmlReadMemory(ctxt->buffer, ctxt->size, NULL, NULL, + SCHEMATRON_PARSE_OPTIONS); + if (doc == NULL) { + xmlSchematronPErr(ctxt, NULL, + XML_SCHEMAP_FAILED_PARSE, + "xmlSchematronParse: could not parse.\n", + NULL, NULL); + return (NULL); + } + doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer"); + ctxt->URL = xmlDictLookup(ctxt->dict, BAD_CAST "in_memory_buffer", -1); + ctxt->preserve = 0; + } else if (ctxt->doc != NULL) { + doc = ctxt->doc; + preserve = 1; + ctxt->preserve = 1; + } else { + xmlSchematronPErr(ctxt, NULL, + XML_SCHEMAP_NOTHING_TO_PARSE, + "xmlSchematronParse: could not parse.\n", + NULL, NULL); + return (NULL); + } + + /* + * Then extract the root and Schematron parse it + */ + root = xmlDocGetRootElement(doc); + if (root == NULL) { + xmlSchematronPErr(ctxt, (xmlNodePtr) doc, + XML_SCHEMAP_NOROOT, + "The schema has no document element.\n", NULL, NULL); + if (!preserve) { + xmlFreeDoc(doc); + } + return (NULL); + } + + if (!IS_SCHEMATRON(root, "schema")) { + xmlSchematronPErr(ctxt, root, + XML_SCHEMAP_NOROOT, + "The XML document '%s' is not a XML schematron document", + ctxt->URL, NULL); + goto exit; + } + ret = xmlSchematronNewSchematron(ctxt); + if (ret == NULL) + goto exit; + ctxt->schema = ret; + + /* + * scan the schema elements + */ + cur = root->children; + NEXT_SCHEMATRON(cur); + if (IS_SCHEMATRON(cur, "title")) { + xmlChar *title = xmlNodeGetContent(cur); + if (title != NULL) { + ret->title = xmlDictLookup(ret->dict, title, -1); + xmlFree(title); + } + cur = cur->next; + NEXT_SCHEMATRON(cur); + } + while (IS_SCHEMATRON(cur, "ns")) { + xmlChar *prefix = xmlGetNoNsProp(cur, BAD_CAST "prefix"); + xmlChar *uri = xmlGetNoNsProp(cur, BAD_CAST "uri"); + if ((uri == NULL) || (uri[0] == 0)) { + xmlSchematronPErr(ctxt, cur, + XML_SCHEMAP_NOROOT, + "ns element has no uri", NULL, NULL); + } + if ((prefix == NULL) || (prefix[0] == 0)) { + xmlSchematronPErr(ctxt, cur, + XML_SCHEMAP_NOROOT, + "ns element has no prefix", NULL, NULL); + } + if ((prefix) && (uri)) { + xmlXPathRegisterNs(ctxt->xctxt, prefix, uri); + xmlSchematronAddNamespace(ctxt, prefix, uri); + ret->nbNs++; + } + if (uri) + xmlFree(uri); + if (prefix) + xmlFree(prefix); + cur = cur->next; + NEXT_SCHEMATRON(cur); + } + while (cur != NULL) { + if (IS_SCHEMATRON(cur, "pattern")) { + xmlSchematronParsePattern(ctxt, cur); + ret->nbPattern++; + } else { + xmlSchematronPErr(ctxt, cur, + XML_SCHEMAP_NOROOT, + "Expecting a pattern element instead of %s", cur->name, NULL); + } + cur = cur->next; + NEXT_SCHEMATRON(cur); + } + if (ret->nbPattern == 0) { + xmlSchematronPErr(ctxt, root, + XML_SCHEMAP_NOROOT, + "The schematron document '%s' has no pattern", + ctxt->URL, NULL); + goto exit; + } + /* the original document must be kept for reporting */ + ret->doc = doc; + preserve = 1; + +exit: + if (!preserve) { + xmlFreeDoc(doc); + } + if (ret != NULL) { + if (ctxt->nberrors != 0) { + xmlSchematronFree(ret); + ret = NULL; + } else { + ret->namespaces = ctxt->namespaces; + ret->nbNamespaces = ctxt->nbNamespaces; + ctxt->namespaces = NULL; + } + } + return (ret); +} + +/************************************************************************ + * * + * Schematrontron Reports handler * + * * + ************************************************************************/ + +static xmlNodePtr +xmlSchematronGetNode(xmlSchematronValidCtxtPtr ctxt, + xmlNodePtr cur, const xmlChar *xpath) { + xmlNodePtr node = NULL; + xmlXPathObjectPtr ret; + + if ((ctxt == NULL) || (cur == NULL) || (xpath == NULL)) + return(NULL); + + ctxt->xctxt->doc = cur->doc; + ctxt->xctxt->node = cur; + ret = xmlXPathEval(xpath, ctxt->xctxt); + if (ret == NULL) + return(NULL); + + if ((ret->type == XPATH_NODESET) && + (ret->nodesetval != NULL) && (ret->nodesetval->nodeNr > 0)) + node = ret->nodesetval->nodeTab[0]; + + xmlXPathFreeObject(ret); + return(node); +} + +/** + * xmlSchematronReportOutput: + * @ctxt: the validation context + * @cur: the current node tested + * @msg: the message output + * + * Output part of the report to whatever channel the user selected + */ +static void +xmlSchematronReportOutput(xmlSchematronValidCtxtPtr ctxt ATTRIBUTE_UNUSED, + xmlNodePtr cur ATTRIBUTE_UNUSED, + const char *msg) { + /* TODO */ + fprintf(stderr, "%s", msg); +} + +/** + * xmlSchematronFormatReport: + * @ctxt: the validation context + * @test: the test node + * @cur: the current node tested + * + * Build the string being reported to the user. + * + * Returns a report string or NULL in case of error. The string needs + * to be deallocated by teh caller + */ +static xmlChar * +xmlSchematronFormatReport(xmlSchematronValidCtxtPtr ctxt, + xmlNodePtr test, xmlNodePtr cur) { + xmlChar *ret = NULL; + xmlNodePtr child, node; + + if ((test == NULL) || (cur == NULL)) + return(ret); + + child = test->children; + while (child != NULL) { + if ((child->type == XML_TEXT_NODE) || + (child->type == XML_CDATA_SECTION_NODE)) + ret = xmlStrcat(ret, child->content); + else if (IS_SCHEMATRON(child, "name")) { + xmlChar *path; + + path = xmlGetNoNsProp(child, BAD_CAST "path"); + + node = cur; + if (path != NULL) { + node = xmlSchematronGetNode(ctxt, cur, path); + if (node == NULL) + node = cur; + xmlFree(path); + } + + if ((node->ns == NULL) || (node->ns->prefix == NULL)) + ret = xmlStrcat(ret, node->name); + else { + ret = xmlStrcat(ret, node->ns->prefix); + ret = xmlStrcat(ret, BAD_CAST ":"); + ret = xmlStrcat(ret, node->name); + } + } else { + child = child->next; + continue; + } + + /* + * remove superfluous \n + */ + if (ret != NULL) { + int len = xmlStrlen(ret); + xmlChar c; + + if (len > 0) { + c = ret[len - 1]; + if ((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t')) { + while ((c == ' ') || (c == '\n') || + (c == '\r') || (c == '\t')) { + len--; + if (len == 0) + break; + c = ret[len - 1]; + } + ret[len] = ' '; + ret[len + 1] = 0; + } + } + } + + child = child->next; + } + return(ret); +} + +/** + * xmlSchematronReportSuccess: + * @ctxt: the validation context + * @test: the compiled test + * @cur: the current node tested + * @success: boolean value for the result + * + * called from the validation engine when an assert or report test have + * been done. + */ +static void +xmlSchematronReportSuccess(xmlSchematronValidCtxtPtr ctxt, + xmlSchematronTestPtr test, xmlNodePtr cur, int success) { + if ((ctxt == NULL) || (cur == NULL) || (test == NULL)) + return; + /* if quiet and not SVRL report only failures */ + if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) && + ((ctxt->flags & XML_SCHEMATRON_OUT_XML) == 0) && + (test->type == XML_SCHEMATRON_REPORT)) + return; + if (ctxt->flags & XML_SCHEMATRON_OUT_XML) { + TODO + } else { + xmlChar *path; + char msg[1000]; + long line; + const xmlChar *report = NULL; + + if (((test->type == XML_SCHEMATRON_REPORT) & (!success)) || + ((test->type == XML_SCHEMATRON_ASSERT) & (success))) + return; + line = xmlGetLineNo(cur); + path = xmlGetNodePath(cur); + if (path == NULL) + path = (xmlChar *) cur->name; +#if 0 + if ((test->report != NULL) && (test->report[0] != 0)) + report = test->report; +#endif + if (test->node != NULL) + report = xmlSchematronFormatReport(ctxt, test->node, cur); + if (report == NULL) { + if (test->type == XML_SCHEMATRON_ASSERT) { + snprintf(msg, 999, "%s line %ld: node failed assert\n", + (const char *) path, line); + } else { + snprintf(msg, 999, "%s line %ld: node failed report\n", + (const char *) path, line); + } + } else { + snprintf(msg, 999, "%s line %ld: %s\n", (const char *) path, + line, (const char *) report); + xmlFree((char *) report); + } + xmlSchematronReportOutput(ctxt, cur, &msg[0]); + if ((path != NULL) && (path != (xmlChar *) cur->name)) + xmlFree(path); + } +} + +/** + * xmlSchematronReportPattern: + * @ctxt: the validation context + * @pattern: the current pattern + * + * called from the validation engine when starting to check a pattern + */ +static void +xmlSchematronReportPattern(xmlSchematronValidCtxtPtr ctxt, + xmlSchematronPatternPtr pattern) { + if ((ctxt == NULL) || (pattern == NULL)) + return; + if (ctxt->flags & XML_SCHEMATRON_OUT_QUIET) + return; + if (ctxt->flags & XML_SCHEMATRON_OUT_XML) { + TODO + } else { + char msg[1000]; + + if (pattern->name == NULL) + return; + snprintf(msg, 999, "Pattern: %s\n", (const char *) pattern->name); + xmlSchematronReportOutput(ctxt, NULL, &msg[0]); + } +} + + +/************************************************************************ + * * + * Validation against a Schematrontron * + * * + ************************************************************************/ + +/** + * xmlSchematronNewValidCtxt: + * @schema: a precompiled XML Schematrons + * @options: a set of xmlSchematronValidOptions + * + * Create an XML Schematrons validation context based on the given schema. + * + * Returns the validation context or NULL in case of error + */ +xmlSchematronValidCtxtPtr +xmlSchematronNewValidCtxt(xmlSchematronPtr schema, int options) +{ + int i; + xmlSchematronValidCtxtPtr ret; + + ret = (xmlSchematronValidCtxtPtr) xmlMalloc(sizeof(xmlSchematronValidCtxt)); + if (ret == NULL) { + xmlSchematronVErrMemory(NULL, "allocating validation context", + NULL); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchematronValidCtxt)); + ret->type = XML_STRON_CTXT_VALIDATOR; + ret->schema = schema; + ret->xctxt = xmlXPathNewContext(NULL); + ret->flags = options; + if (ret->xctxt == NULL) { + xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context", + NULL); + xmlSchematronFreeValidCtxt(ret); + return (NULL); + } + for (i = 0;i < schema->nbNamespaces;i++) { + if ((schema->namespaces[2 * i] == NULL) || + (schema->namespaces[2 * i + 1] == NULL)) + break; + xmlXPathRegisterNs(ret->xctxt, schema->namespaces[2 * i + 1], + schema->namespaces[2 * i]); + } + return (ret); +} + +/** + * xmlSchematronFreeValidCtxt: + * @ctxt: the schema validation context + * + * Free the resources associated to the schema validation context + */ +void +xmlSchematronFreeValidCtxt(xmlSchematronValidCtxtPtr ctxt) +{ + if (ctxt == NULL) + return; + if (ctxt->xctxt != NULL) + xmlXPathFreeContext(ctxt->xctxt); + if (ctxt->dict != NULL) + xmlDictFree(ctxt->dict); + xmlFree(ctxt); +} + +static xmlNodePtr +xmlSchematronNextNode(xmlNodePtr cur) { + if (cur->children != NULL) { + /* + * Do not descend on entities declarations + */ + if (cur->children->type != XML_ENTITY_DECL) { + cur = cur->children; + /* + * Skip DTDs + */ + if (cur->type != XML_DTD_NODE) + return(cur); + } + } + + while (cur->next != NULL) { + cur = cur->next; + if ((cur->type != XML_ENTITY_DECL) && + (cur->type != XML_DTD_NODE)) + return(cur); + } + + do { + cur = cur->parent; + if (cur == NULL) break; + if (cur->type == XML_DOCUMENT_NODE) return(NULL); + if (cur->next != NULL) { + cur = cur->next; + return(cur); + } + } while (cur != NULL); + return(cur); +} + +/** + * xmlSchematronRunTest: + * @ctxt: the schema validation context + * @test: the current test + * @instance: the document instace tree + * @cur: the current node in the instance + * + * Validate a rule against a tree instance at a given position + * + * Returns 1 in case of success, 0 if error and -1 in case of internal error + */ +static int +xmlSchematronRunTest(xmlSchematronValidCtxtPtr ctxt, + xmlSchematronTestPtr test, xmlDocPtr instance, xmlNodePtr cur) +{ + xmlXPathObjectPtr ret; + int failed; + + failed = 0; + ctxt->xctxt->doc = instance; + ctxt->xctxt->node = cur; + ret = xmlXPathCompiledEval(test->comp, ctxt->xctxt); + if (ret == NULL) { + failed = 1; + } else { + switch (ret->type) { + case XPATH_XSLT_TREE: + case XPATH_NODESET: + if ((ret->nodesetval == NULL) || + (ret->nodesetval->nodeNr == 0)) + failed = 1; + break; + case XPATH_BOOLEAN: + failed = !ret->boolval; + break; + case XPATH_NUMBER: + if ((xmlXPathIsNaN(ret->floatval)) || + (ret->floatval == 0.0)) + failed = 1; + break; + case XPATH_STRING: + if ((ret->stringval == NULL) || + (ret->stringval[0] == 0)) + failed = 1; + break; + case XPATH_UNDEFINED: + case XPATH_POINT: + case XPATH_RANGE: + case XPATH_LOCATIONSET: + case XPATH_USERS: + failed = 1; + break; + } + xmlXPathFreeObject(ret); + } + if ((failed) && (test->type == XML_SCHEMATRON_ASSERT)) + ctxt->nberrors++; + else if ((!failed) && (test->type == XML_SCHEMATRON_REPORT)) + ctxt->nberrors++; + + xmlSchematronReportSuccess(ctxt, test, cur, !failed); + + return(!failed); +} + +/** + * xmlSchematronValidateDoc: + * @ctxt: the schema validation context + * @instance: the document instace tree + * + * Validate a tree instance against the schematron + * + * Returns 0 in case of success, -1 in case of internal error + * and an error count otherwise. + */ +int +xmlSchematronValidateDoc(xmlSchematronValidCtxtPtr ctxt, xmlDocPtr instance) +{ + xmlNodePtr cur, root; + xmlSchematronPatternPtr pattern; + xmlSchematronRulePtr rule; + xmlSchematronTestPtr test; + + if ((ctxt == NULL) || (ctxt->schema == NULL) || + (ctxt->schema->rules == NULL) || (instance == NULL)) + return(-1); + ctxt->nberrors = 0; + root = xmlDocGetRootElement(instance); + if (root == NULL) { + TODO + ctxt->nberrors++; + return(1); + } + if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) || + (ctxt->flags == 0)) { + /* + * we are just trying to assert the validity of the document, + * speed primes over the output, run in a single pass + */ + cur = root; + while (cur != NULL) { + rule = ctxt->schema->rules; + while (rule != NULL) { + if (xmlPatternMatch(rule->pattern, cur) == 1) { + test = rule->tests; + while (test != NULL) { + xmlSchematronRunTest(ctxt, test, instance, cur); + test = test->next; + } + } + rule = rule->next; + } + + cur = xmlSchematronNextNode(cur); + } + } else { + /* + * Process all contexts one at a time + */ + pattern = ctxt->schema->patterns; + + while (pattern != NULL) { + xmlSchematronReportPattern(ctxt, pattern); + + /* + * TODO convert the pattern rule to a direct XPath and + * compute directly instead of using the pattern matching + * over the full document... + * Check the exact semantic + */ + cur = root; + while (cur != NULL) { + rule = pattern->rules; + while (rule != NULL) { + if (xmlPatternMatch(rule->pattern, cur) == 1) { + test = rule->tests; + while (test != NULL) { + xmlSchematronRunTest(ctxt, test, instance, cur); + test = test->next; + } + } + rule = rule->patnext; + } + + cur = xmlSchematronNextNode(cur); + } + pattern = pattern->next; + } + } + return(ctxt->nberrors); +} + +#ifdef STANDALONE +int +main(void) +{ + int ret; + xmlDocPtr instance; + xmlSchematronParserCtxtPtr pctxt; + xmlSchematronValidCtxtPtr vctxt; + xmlSchematronPtr schema = NULL; + + pctxt = xmlSchematronNewParserCtxt("tst.sct"); + if (pctxt == NULL) { + fprintf(stderr, "failed to build schematron parser\n"); + } else { + schema = xmlSchematronParse(pctxt); + if (schema == NULL) { + fprintf(stderr, "failed to compile schematron\n"); + } + xmlSchematronFreeParserCtxt(pctxt); + } + instance = xmlReadFile("tst.sct", NULL, + XML_PARSE_NOENT | XML_PARSE_NOCDATA); + if (instance == NULL) { + fprintf(stderr, "failed to parse instance\n"); + } + if ((schema != NULL) && (instance != NULL)) { + vctxt = xmlSchematronNewValidCtxt(schema); + if (vctxt == NULL) { + fprintf(stderr, "failed to build schematron validator\n"); + } else { + ret = xmlSchematronValidateDoc(vctxt, instance); + xmlSchematronFreeValidCtxt(vctxt); + } + } + xmlSchematronFree(schema); + xmlFreeDoc(instance); + + xmlCleanupParser(); + xmlMemoryDump(); + + return (0); +} +#endif +#define bottom_schematron +#include "elfgcchack.h" +#endif /* LIBXML_SCHEMATRON_ENABLED */ diff --git a/Extras/LibXML/trio.c b/Extras/LibXML/trio.c new file mode 100644 index 000000000..2dd8478f1 --- /dev/null +++ b/Extras/LibXML/trio.c @@ -0,0 +1,6855 @@ +/************************************************************************* + * + * $Id: trio.c,v 1.11 2003/04/03 15:28:27 veillard Exp $ + * + * Copyright (C) 1998 Bjorn Reese and Daniel Stenberg. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. + * + ************************************************************************* + * + * A note to trio contributors: + * + * Avoid heap allocation at all costs to ensure that the trio functions + * are async-safe. The exceptions are the printf/fprintf functions, which + * uses fputc, and the asprintf functions and the modifier, which + * by design are required to allocate form the heap. + * + ************************************************************************/ + +/* + * TODO: + * - Scan is probably too permissive about its modifiers. + * - C escapes in %#[] ? + * - Multibyte characters (done for format parsing, except scan groups) + * - Complex numbers? (C99 _Complex) + * - Boolean values? (C99 _Bool) + * - C99 NaN(n-char-sequence) missing. The n-char-sequence can be used + * to print the mantissa, e.g. NaN(0xc000000000000000) + * - Should we support the GNU %a alloc modifier? GNU has an ugly hack + * for %a, because C99 used %a for other purposes. If specified as + * %as or %a[ it is interpreted as the alloc modifier, otherwise as + * the C99 hex-float. This means that you cannot scan %as as a hex-float + * immediately followed by an 's'. + * - Scanning of collating symbols. + */ + +/************************************************************************* + * Trio include files + */ +#include "triodef.h" +#include "trio.h" +#include "triop.h" +#include "trionan.h" +#if !defined(TRIO_MINIMAL) +# include "triostr.h" +#endif + +/************************************************************************** + * + * Definitions + * + *************************************************************************/ + +#include +#include +#include + +#if defined(__STDC_ISO_10646__) || defined(MB_LEN_MAX) || defined(USE_MULTIBYTE) || TRIO_WIDECHAR +# define TRIO_COMPILER_SUPPORTS_MULTIBYTE +# if !defined(MB_LEN_MAX) +# define MB_LEN_MAX 6 +# endif +#endif + +#if (defined(TRIO_COMPILER_MSVC) && (_MSC_VER >= 1100)) || defined(TRIO_COMPILER_BCB) +# define TRIO_COMPILER_SUPPORTS_MSVC_INT +#endif + +/************************************************************************* + * Generic definitions + */ + +#if !(defined(DEBUG) || defined(NDEBUG)) +# define NDEBUG +#endif + +#include +#include +#if !defined(TRIO_COMPILER_SUPPORTS_C99) +# define isblank(x) (((x)==32) || ((x)==9)) +#endif +#if defined(TRIO_COMPILER_ANCIENT) +# include +#else +# include +#endif +#include +#include + +#ifndef NULL +# define NULL 0 +#endif +#define NIL ((char)0) +#ifndef FALSE +# define FALSE (1 == 0) +# define TRUE (! FALSE) +#endif +#define BOOLEAN_T int + +/* mincore() can be used for debugging purposes */ +#define VALID(x) (NULL != (x)) + +#if TRIO_ERRORS + /* + * Encode the error code and the position. This is decoded + * with TRIO_ERROR_CODE and TRIO_ERROR_POSITION. + */ +# define TRIO_ERROR_RETURN(x,y) (- ((x) + ((y) << 8))) +#else +# define TRIO_ERROR_RETURN(x,y) (-1) +#endif + +typedef unsigned long trio_flags_t; + + +/************************************************************************* + * Platform specific definitions + */ +#if defined(TRIO_PLATFORM_UNIX) +# include +# include +# include +# define USE_LOCALE +#endif /* TRIO_PLATFORM_UNIX */ +#if defined(TRIO_PLATFORM_VMS) +# include +#endif +#if defined(TRIO_PLATFORM_WIN32) +# include +# define read _read +# define write _write +#endif /* TRIO_PLATFORM_WIN32 */ + +#if TRIO_WIDECHAR +# if defined(TRIO_COMPILER_SUPPORTS_ISO94) +# include +# include +typedef wchar_t trio_wchar_t; +typedef wint_t trio_wint_t; +# else +typedef char trio_wchar_t; +typedef int trio_wint_t; +# define WCONST(x) L ## x +# define WEOF EOF +# define iswalnum(x) isalnum(x) +# define iswalpha(x) isalpha(x) +# define iswblank(x) isblank(x) +# define iswcntrl(x) iscntrl(x) +# define iswdigit(x) isdigit(x) +# define iswgraph(x) isgraph(x) +# define iswlower(x) islower(x) +# define iswprint(x) isprint(x) +# define iswpunct(x) ispunct(x) +# define iswspace(x) isspace(x) +# define iswupper(x) isupper(x) +# define iswxdigit(x) isxdigit(x) +# endif +#endif + + +/************************************************************************* + * Compiler dependent definitions + */ + +/* Support for long long */ +#ifndef __cplusplus +# if !defined(USE_LONGLONG) +# if defined(TRIO_COMPILER_GCC) && !defined(__STRICT_ANSI__) +# define USE_LONGLONG +# elif defined(TRIO_COMPILER_SUNPRO) +# define USE_LONGLONG +# elif defined(_LONG_LONG) || defined(_LONGLONG) +# define USE_LONGLONG +# endif +# endif +#endif + +/* The extra long numbers */ +#if defined(USE_LONGLONG) +typedef signed long long int trio_longlong_t; +typedef unsigned long long int trio_ulonglong_t; +#elif defined(TRIO_COMPILER_SUPPORTS_MSVC_INT) +typedef signed __int64 trio_longlong_t; +typedef unsigned __int64 trio_ulonglong_t; +#else +typedef TRIO_SIGNED long int trio_longlong_t; +typedef unsigned long int trio_ulonglong_t; +#endif + +/* Maximal and fixed integer types */ +#if defined(TRIO_COMPILER_SUPPORTS_C99) +# include +typedef intmax_t trio_intmax_t; +typedef uintmax_t trio_uintmax_t; +typedef int8_t trio_int8_t; +typedef int16_t trio_int16_t; +typedef int32_t trio_int32_t; +typedef int64_t trio_int64_t; +#elif defined(TRIO_COMPILER_SUPPORTS_UNIX98) +# include +typedef intmax_t trio_intmax_t; +typedef uintmax_t trio_uintmax_t; +typedef int8_t trio_int8_t; +typedef int16_t trio_int16_t; +typedef int32_t trio_int32_t; +typedef int64_t trio_int64_t; +#elif defined(TRIO_COMPILER_SUPPORTS_MSVC_INT) +typedef trio_longlong_t trio_intmax_t; +typedef trio_ulonglong_t trio_uintmax_t; +typedef __int8 trio_int8_t; +typedef __int16 trio_int16_t; +typedef __int32 trio_int32_t; +typedef __int64 trio_int64_t; +#else +typedef trio_longlong_t trio_intmax_t; +typedef trio_ulonglong_t trio_uintmax_t; +# if defined(TRIO_INT8_T) +typedef TRIO_INT8_T trio_int8_t; +# else +typedef TRIO_SIGNED char trio_int8_t; +# endif +# if defined(TRIO_INT16_T) +typedef TRIO_INT16_T trio_int16_t; +# else +typedef TRIO_SIGNED short trio_int16_t; +# endif +# if defined(TRIO_INT32_T) +typedef TRIO_INT32_T trio_int32_t; +# else +typedef TRIO_SIGNED int trio_int32_t; +# endif +# if defined(TRIO_INT64_T) +typedef TRIO_INT64_T trio_int64_t; +# else +typedef trio_longlong_t trio_int64_t; +# endif +#endif + +#if !(defined(TRIO_COMPILER_SUPPORTS_C99) \ + || defined(TRIO_COMPILER_SUPPORTS_UNIX01)) +# define floorl(x) floor((double)(x)) +# define fmodl(x,y) fmod((double)(x),(double)(y)) +# define powl(x,y) pow((double)(x),(double)(y)) +#endif + +#define TRIO_FABS(x) (((x) < 0.0) ? -(x) : (x)) + +/************************************************************************* + * Internal Definitions + */ + +#ifndef DECIMAL_DIG +# define DECIMAL_DIG DBL_DIG +#endif + +/* Long double sizes */ +#ifdef LDBL_DIG +# define MAX_MANTISSA_DIGITS LDBL_DIG +# define MAX_EXPONENT_DIGITS 4 +# define MAX_DOUBLE_DIGITS LDBL_MAX_10_EXP +#else +# define MAX_MANTISSA_DIGITS DECIMAL_DIG +# define MAX_EXPONENT_DIGITS 3 +# define MAX_DOUBLE_DIGITS DBL_MAX_10_EXP +#endif + +#if defined(TRIO_COMPILER_ANCIENT) || !defined(LDBL_DIG) +# undef LDBL_DIG +# undef LDBL_MANT_DIG +# undef LDBL_EPSILON +# define LDBL_DIG DBL_DIG +# define LDBL_MANT_DIG DBL_MANT_DIG +# define LDBL_EPSILON DBL_EPSILON +#endif + +/* The maximal number of digits is for base 2 */ +#define MAX_CHARS_IN(x) (sizeof(x) * CHAR_BIT) +/* The width of a pointer. The number of bits in a hex digit is 4 */ +#define POINTER_WIDTH ((sizeof("0x") - 1) + sizeof(trio_pointer_t) * CHAR_BIT / 4) + +/* Infinite and Not-A-Number for floating-point */ +#define INFINITE_LOWER "inf" +#define INFINITE_UPPER "INF" +#define LONG_INFINITE_LOWER "infinite" +#define LONG_INFINITE_UPPER "INFINITE" +#define NAN_LOWER "nan" +#define NAN_UPPER "NAN" + +/* Various constants */ +enum { + TYPE_PRINT = 1, + TYPE_SCAN = 2, + + /* Flags. FLAGS_LAST must be less than ULONG_MAX */ + FLAGS_NEW = 0, + FLAGS_STICKY = 1, + FLAGS_SPACE = 2 * FLAGS_STICKY, + FLAGS_SHOWSIGN = 2 * FLAGS_SPACE, + FLAGS_LEFTADJUST = 2 * FLAGS_SHOWSIGN, + FLAGS_ALTERNATIVE = 2 * FLAGS_LEFTADJUST, + FLAGS_SHORT = 2 * FLAGS_ALTERNATIVE, + FLAGS_SHORTSHORT = 2 * FLAGS_SHORT, + FLAGS_LONG = 2 * FLAGS_SHORTSHORT, + FLAGS_QUAD = 2 * FLAGS_LONG, + FLAGS_LONGDOUBLE = 2 * FLAGS_QUAD, + FLAGS_SIZE_T = 2 * FLAGS_LONGDOUBLE, + FLAGS_PTRDIFF_T = 2 * FLAGS_SIZE_T, + FLAGS_INTMAX_T = 2 * FLAGS_PTRDIFF_T, + FLAGS_NILPADDING = 2 * FLAGS_INTMAX_T, + FLAGS_UNSIGNED = 2 * FLAGS_NILPADDING, + FLAGS_UPPER = 2 * FLAGS_UNSIGNED, + FLAGS_WIDTH = 2 * FLAGS_UPPER, + FLAGS_WIDTH_PARAMETER = 2 * FLAGS_WIDTH, + FLAGS_PRECISION = 2 * FLAGS_WIDTH_PARAMETER, + FLAGS_PRECISION_PARAMETER = 2 * FLAGS_PRECISION, + FLAGS_BASE = 2 * FLAGS_PRECISION_PARAMETER, + FLAGS_BASE_PARAMETER = 2 * FLAGS_BASE, + FLAGS_FLOAT_E = 2 * FLAGS_BASE_PARAMETER, + FLAGS_FLOAT_G = 2 * FLAGS_FLOAT_E, + FLAGS_QUOTE = 2 * FLAGS_FLOAT_G, + FLAGS_WIDECHAR = 2 * FLAGS_QUOTE, + FLAGS_ALLOC = 2 * FLAGS_WIDECHAR, + FLAGS_IGNORE = 2 * FLAGS_ALLOC, + FLAGS_IGNORE_PARAMETER = 2 * FLAGS_IGNORE, + FLAGS_VARSIZE_PARAMETER = 2 * FLAGS_IGNORE_PARAMETER, + FLAGS_FIXED_SIZE = 2 * FLAGS_VARSIZE_PARAMETER, + FLAGS_LAST = FLAGS_FIXED_SIZE, + /* Reused flags */ + FLAGS_EXCLUDE = FLAGS_SHORT, + FLAGS_USER_DEFINED = FLAGS_IGNORE, + FLAGS_ROUNDING = FLAGS_INTMAX_T, + /* Compounded flags */ + FLAGS_ALL_VARSIZES = FLAGS_LONG | FLAGS_QUAD | FLAGS_INTMAX_T | FLAGS_PTRDIFF_T | FLAGS_SIZE_T, + FLAGS_ALL_SIZES = FLAGS_ALL_VARSIZES | FLAGS_SHORTSHORT | FLAGS_SHORT, + + NO_POSITION = -1, + NO_WIDTH = 0, + NO_PRECISION = -1, + NO_SIZE = -1, + + /* Do not change these */ + NO_BASE = -1, + MIN_BASE = 2, + MAX_BASE = 36, + BASE_BINARY = 2, + BASE_OCTAL = 8, + BASE_DECIMAL = 10, + BASE_HEX = 16, + + /* Maximal number of allowed parameters */ + MAX_PARAMETERS = 64, + /* Maximal number of characters in class */ + MAX_CHARACTER_CLASS = UCHAR_MAX + 1, + + /* Maximal string lengths for user-defined specifiers */ + MAX_USER_NAME = 64, + MAX_USER_DATA = 256, + + /* Maximal length of locale separator strings */ + MAX_LOCALE_SEPARATOR_LENGTH = MB_LEN_MAX, + /* Maximal number of integers in grouping */ + MAX_LOCALE_GROUPS = 64, + + /* Initial size of asprintf buffer */ + DYNAMIC_START_SIZE = 32 +}; + +#define NO_GROUPING ((int)CHAR_MAX) + +/* Fundamental formatting parameter types */ +#define FORMAT_UNKNOWN 0 +#define FORMAT_INT 1 +#define FORMAT_DOUBLE 2 +#define FORMAT_CHAR 3 +#define FORMAT_STRING 4 +#define FORMAT_POINTER 5 +#define FORMAT_COUNT 6 +#define FORMAT_PARAMETER 7 +#define FORMAT_GROUP 8 +#if TRIO_GNU +# define FORMAT_ERRNO 9 +#endif +#if TRIO_EXTENSION +# define FORMAT_USER_DEFINED 10 +#endif + +/* Character constants */ +#define CHAR_IDENTIFIER '%' +#define CHAR_BACKSLASH '\\' +#define CHAR_QUOTE '\"' +#define CHAR_ADJUST ' ' + +/* Character class expressions */ +#define CLASS_ALNUM "[:alnum:]" +#define CLASS_ALPHA "[:alpha:]" +#define CLASS_BLANK "[:blank:]" +#define CLASS_CNTRL "[:cntrl:]" +#define CLASS_DIGIT "[:digit:]" +#define CLASS_GRAPH "[:graph:]" +#define CLASS_LOWER "[:lower:]" +#define CLASS_PRINT "[:print:]" +#define CLASS_PUNCT "[:punct:]" +#define CLASS_SPACE "[:space:]" +#define CLASS_UPPER "[:upper:]" +#define CLASS_XDIGIT "[:xdigit:]" + +/* + * SPECIFIERS: + * + * + * a Hex-float + * A Hex-float + * c Character + * C Widechar character (wint_t) + * d Decimal + * e Float + * E Float + * F Float + * F Float + * g Float + * G Float + * i Integer + * m Error message + * n Count + * o Octal + * p Pointer + * s String + * S Widechar string (wchar_t *) + * u Unsigned + * x Hex + * X Hex + * [] Group + * <> User-defined + * + * Reserved: + * + * D Binary Coded Decimal %D(length,precision) (OS/390) + */ +#define SPECIFIER_CHAR 'c' +#define SPECIFIER_STRING 's' +#define SPECIFIER_DECIMAL 'd' +#define SPECIFIER_INTEGER 'i' +#define SPECIFIER_UNSIGNED 'u' +#define SPECIFIER_OCTAL 'o' +#define SPECIFIER_HEX 'x' +#define SPECIFIER_HEX_UPPER 'X' +#define SPECIFIER_FLOAT_E 'e' +#define SPECIFIER_FLOAT_E_UPPER 'E' +#define SPECIFIER_FLOAT_F 'f' +#define SPECIFIER_FLOAT_F_UPPER 'F' +#define SPECIFIER_FLOAT_G 'g' +#define SPECIFIER_FLOAT_G_UPPER 'G' +#define SPECIFIER_POINTER 'p' +#define SPECIFIER_GROUP '[' +#define SPECIFIER_UNGROUP ']' +#define SPECIFIER_COUNT 'n' +#if TRIO_UNIX98 +# define SPECIFIER_CHAR_UPPER 'C' +# define SPECIFIER_STRING_UPPER 'S' +#endif +#if TRIO_C99 +# define SPECIFIER_HEXFLOAT 'a' +# define SPECIFIER_HEXFLOAT_UPPER 'A' +#endif +#if TRIO_GNU +# define SPECIFIER_ERRNO 'm' +#endif +#if TRIO_EXTENSION +# define SPECIFIER_BINARY 'b' +# define SPECIFIER_BINARY_UPPER 'B' +# define SPECIFIER_USER_DEFINED_BEGIN '<' +# define SPECIFIER_USER_DEFINED_END '>' +# define SPECIFIER_USER_DEFINED_SEPARATOR ':' +#endif + +/* + * QUALIFIERS: + * + * + * Numbers = d,i,o,u,x,X + * Float = a,A,e,E,f,F,g,G + * String = s + * Char = c + * + * + * 9$ Position + * Use the 9th parameter. 9 can be any number between 1 and + * the maximal argument + * + * 9 Width + * Set width to 9. 9 can be any number, but must not be postfixed + * by '$' + * + * h Short + * Numbers: + * (unsigned) short int + * + * hh Short short + * Numbers: + * (unsigned) char + * + * l Long + * Numbers: + * (unsigned) long int + * String: + * as the S specifier + * Char: + * as the C specifier + * + * ll Long Long + * Numbers: + * (unsigned) long long int + * + * L Long Double + * Float + * long double + * + * # Alternative + * Float: + * Decimal-point is always present + * String: + * non-printable characters are handled as \number + * + * Spacing + * + * + Sign + * + * - Alignment + * + * . Precision + * + * * Parameter + * print: use parameter + * scan: no parameter (ignore) + * + * q Quad + * + * Z size_t + * + * w Widechar + * + * ' Thousands/quote + * Numbers: + * Integer part grouped in thousands + * Binary numbers: + * Number grouped in nibbles (4 bits) + * String: + * Quoted string + * + * j intmax_t + * t prtdiff_t + * z size_t + * + * ! Sticky + * @ Parameter (for both print and scan) + * + * I n-bit Integer + * Numbers: + * The following options exists + * I8 = 8-bit integer + * I16 = 16-bit integer + * I32 = 32-bit integer + * I64 = 64-bit integer + */ +#define QUALIFIER_POSITION '$' +#define QUALIFIER_SHORT 'h' +#define QUALIFIER_LONG 'l' +#define QUALIFIER_LONG_UPPER 'L' +#define QUALIFIER_ALTERNATIVE '#' +#define QUALIFIER_SPACE ' ' +#define QUALIFIER_PLUS '+' +#define QUALIFIER_MINUS '-' +#define QUALIFIER_DOT '.' +#define QUALIFIER_STAR '*' +#define QUALIFIER_CIRCUMFLEX '^' /* For scanlists */ +#if TRIO_C99 +# define QUALIFIER_SIZE_T 'z' +# define QUALIFIER_PTRDIFF_T 't' +# define QUALIFIER_INTMAX_T 'j' +#endif +#if TRIO_BSD || TRIO_GNU +# define QUALIFIER_QUAD 'q' +#endif +#if TRIO_GNU +# define QUALIFIER_SIZE_T_UPPER 'Z' +#endif +#if TRIO_MISC +# define QUALIFIER_WIDECHAR 'w' +#endif +#if TRIO_MICROSOFT +# define QUALIFIER_FIXED_SIZE 'I' +#endif +#if TRIO_EXTENSION +# define QUALIFIER_QUOTE '\'' +# define QUALIFIER_STICKY '!' +# define QUALIFIER_VARSIZE '&' /* This should remain undocumented */ +# define QUALIFIER_PARAM '@' /* Experimental */ +# define QUALIFIER_COLON ':' /* For scanlists */ +# define QUALIFIER_EQUAL '=' /* For scanlists */ +# define QUALIFIER_ROUNDING_UPPER 'R' +#endif + + +/************************************************************************* + * + * Internal Structures + * + *************************************************************************/ + +/* Parameters */ +typedef struct { + /* An indication of which entry in the data union is used */ + int type; + /* The flags */ + trio_flags_t flags; + /* The width qualifier */ + int width; + /* The precision qualifier */ + int precision; + /* The base qualifier */ + int base; + /* The size for the variable size qualifier */ + int varsize; + /* The marker of the end of the specifier */ + int indexAfterSpecifier; + /* The data from the argument list */ + union { + char *string; +#if TRIO_WIDECHAR + trio_wchar_t *wstring; +#endif + trio_pointer_t pointer; + union { + trio_intmax_t as_signed; + trio_uintmax_t as_unsigned; + } number; + double doubleNumber; + double *doublePointer; + trio_long_double_t longdoubleNumber; + trio_long_double_t *longdoublePointer; + int errorNumber; + } data; + /* For the user-defined specifier */ + char user_name[MAX_USER_NAME]; + char user_data[MAX_USER_DATA]; +} trio_parameter_t; + +/* Container for customized functions */ +typedef struct { + union { + trio_outstream_t out; + trio_instream_t in; + } stream; + trio_pointer_t closure; +} trio_custom_t; + +/* General trio "class" */ +typedef struct _trio_class_t { + /* + * The function to write characters to a stream. + */ + void (*OutStream) TRIO_PROTO((struct _trio_class_t *, int)); + /* + * The function to read characters from a stream. + */ + void (*InStream) TRIO_PROTO((struct _trio_class_t *, int *)); + /* + * The current location in the stream. + */ + trio_pointer_t location; + /* + * The character currently being processed. + */ + int current; + /* + * The number of characters that would have been written/read + * if there had been sufficient space. + */ + int processed; + /* + * The number of characters that are actually written/read. + * Processed and committed will only differ for the *nprintf + * and *nscanf functions. + */ + int committed; + /* + * The upper limit of characters that may be written/read. + */ + int max; + /* + * The last output error that was detected. + */ + int error; +} trio_class_t; + +/* References (for user-defined callbacks) */ +typedef struct _trio_reference_t { + trio_class_t *data; + trio_parameter_t *parameter; +} trio_reference_t; + +/* Registered entries (for user-defined callbacks) */ +typedef struct _trio_userdef_t { + struct _trio_userdef_t *next; + trio_callback_t callback; + char *name; +} trio_userdef_t; + +/************************************************************************* + * + * Internal Variables + * + *************************************************************************/ + +static TRIO_CONST char rcsid[] = "@(#)$Id: trio.c,v 1.11 2003/04/03 15:28:27 veillard Exp $"; + +/* + * Need this to workaround a parser bug in HP C/iX compiler that fails + * to resolves macro definitions that includes type 'long double', + * e.g: va_arg(arg_ptr, long double) + */ +#if defined(TRIO_PLATFORM_MPEIX) +static TRIO_CONST trio_long_double_t ___dummy_long_double = 0; +#endif + +static TRIO_CONST char internalNullString[] = "(nil)"; + +#if defined(USE_LOCALE) +static struct lconv *internalLocaleValues = NULL; +#endif + +/* + * UNIX98 says "in a locale where the radix character is not defined, + * the radix character defaults to a period (.)" + */ +static int internalDecimalPointLength = 1; +static int internalThousandSeparatorLength = 1; +static char internalDecimalPoint = '.'; +static char internalDecimalPointString[MAX_LOCALE_SEPARATOR_LENGTH + 1] = "."; +static char internalThousandSeparator[MAX_LOCALE_SEPARATOR_LENGTH + 1] = ","; +static char internalGrouping[MAX_LOCALE_GROUPS] = { (char)NO_GROUPING }; + +static TRIO_CONST char internalDigitsLower[] = "0123456789abcdefghijklmnopqrstuvwxyz"; +static TRIO_CONST char internalDigitsUpper[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +static BOOLEAN_T internalDigitsUnconverted = TRUE; +static int internalDigitArray[128]; +#if TRIO_EXTENSION +static BOOLEAN_T internalCollationUnconverted = TRUE; +static char internalCollationArray[MAX_CHARACTER_CLASS][MAX_CHARACTER_CLASS]; +#endif + +#if TRIO_EXTENSION +static TRIO_VOLATILE trio_callback_t internalEnterCriticalRegion = NULL; +static TRIO_VOLATILE trio_callback_t internalLeaveCriticalRegion = NULL; +static trio_userdef_t *internalUserDef = NULL; +#endif + + +/************************************************************************* + * + * Internal Functions + * + ************************************************************************/ + +#if defined(TRIO_MINIMAL) +# define TRIO_STRING_PUBLIC static +# include "triostr.c" +#endif /* defined(TRIO_MINIMAL) */ + +/************************************************************************* + * TrioIsQualifier + * + * Description: + * Remember to add all new qualifiers to this function. + * QUALIFIER_POSITION must not be added. + */ +TRIO_PRIVATE BOOLEAN_T +TrioIsQualifier +TRIO_ARGS1((character), + TRIO_CONST char character) +{ + /* QUALIFIER_POSITION is not included */ + switch (character) + { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case QUALIFIER_PLUS: + case QUALIFIER_MINUS: + case QUALIFIER_SPACE: + case QUALIFIER_DOT: + case QUALIFIER_STAR: + case QUALIFIER_ALTERNATIVE: + case QUALIFIER_SHORT: + case QUALIFIER_LONG: + case QUALIFIER_LONG_UPPER: + case QUALIFIER_CIRCUMFLEX: +#if defined(QUALIFIER_SIZE_T) + case QUALIFIER_SIZE_T: +#endif +#if defined(QUALIFIER_PTRDIFF_T) + case QUALIFIER_PTRDIFF_T: +#endif +#if defined(QUALIFIER_INTMAX_T) + case QUALIFIER_INTMAX_T: +#endif +#if defined(QUALIFIER_QUAD) + case QUALIFIER_QUAD: +#endif +#if defined(QUALIFIER_SIZE_T_UPPER) + case QUALIFIER_SIZE_T_UPPER: +#endif +#if defined(QUALIFIER_WIDECHAR) + case QUALIFIER_WIDECHAR: +#endif +#if defined(QUALIFIER_QUOTE) + case QUALIFIER_QUOTE: +#endif +#if defined(QUALIFIER_STICKY) + case QUALIFIER_STICKY: +#endif +#if defined(QUALIFIER_VARSIZE) + case QUALIFIER_VARSIZE: +#endif +#if defined(QUALIFIER_PARAM) + case QUALIFIER_PARAM: +#endif +#if defined(QUALIFIER_FIXED_SIZE) + case QUALIFIER_FIXED_SIZE: +#endif +#if defined(QUALIFIER_ROUNDING_UPPER) + case QUALIFIER_ROUNDING_UPPER: +#endif + return TRUE; + default: + return FALSE; + } +} + +/************************************************************************* + * TrioSetLocale + */ +#if defined(USE_LOCALE) +TRIO_PRIVATE void +TrioSetLocale(TRIO_NOARGS) +{ + internalLocaleValues = (struct lconv *)localeconv(); + if (internalLocaleValues) + { + if ((internalLocaleValues->decimal_point) && + (internalLocaleValues->decimal_point[0] != NIL)) + { + internalDecimalPointLength = trio_length(internalLocaleValues->decimal_point); + if (internalDecimalPointLength == 1) + { + internalDecimalPoint = internalLocaleValues->decimal_point[0]; + } + else + { + internalDecimalPoint = NIL; + trio_copy_max(internalDecimalPointString, + sizeof(internalDecimalPointString), + internalLocaleValues->decimal_point); + } + } + if ((internalLocaleValues->thousands_sep) && + (internalLocaleValues->thousands_sep[0] != NIL)) + { + trio_copy_max(internalThousandSeparator, + sizeof(internalThousandSeparator), + internalLocaleValues->thousands_sep); + internalThousandSeparatorLength = trio_length(internalThousandSeparator); + } + if ((internalLocaleValues->grouping) && + (internalLocaleValues->grouping[0] != NIL)) + { + trio_copy_max(internalGrouping, + sizeof(internalGrouping), + internalLocaleValues->grouping); + } + } +} +#endif /* defined(USE_LOCALE) */ + +TRIO_PRIVATE int +TrioCalcThousandSeparatorLength +TRIO_ARGS1((digits), + int digits) +{ +#if TRIO_EXTENSION + int count = 0; + int step = NO_GROUPING; + char *groupingPointer = internalGrouping; + + while (digits > 0) + { + if (*groupingPointer == CHAR_MAX) + { + /* Disable grouping */ + break; /* while */ + } + else if (*groupingPointer == 0) + { + /* Repeat last group */ + if (step == NO_GROUPING) + { + /* Error in locale */ + break; /* while */ + } + } + else + { + step = *groupingPointer++; + } + if (digits > step) + count += internalThousandSeparatorLength; + digits -= step; + } + return count; +#else + return 0; +#endif +} + +TRIO_PRIVATE BOOLEAN_T +TrioFollowedBySeparator +TRIO_ARGS1((position), + int position) +{ +#if TRIO_EXTENSION + int step = 0; + char *groupingPointer = internalGrouping; + + position--; + if (position == 0) + return FALSE; + while (position > 0) + { + if (*groupingPointer == CHAR_MAX) + { + /* Disable grouping */ + break; /* while */ + } + else if (*groupingPointer != 0) + { + step = *groupingPointer++; + } + if (step == 0) + break; + position -= step; + } + return (position == 0); +#else + return FALSE; +#endif +} + +/************************************************************************* + * TrioGetPosition + * + * Get the %n$ position. + */ +TRIO_PRIVATE int +TrioGetPosition +TRIO_ARGS2((format, indexPointer), + TRIO_CONST char *format, + int *indexPointer) +{ +#if TRIO_UNIX98 + char *tmpformat; + int number = 0; + int index = *indexPointer; + + number = (int)trio_to_long(&format[index], &tmpformat, BASE_DECIMAL); + index = (int)(tmpformat - format); + if ((number != 0) && (QUALIFIER_POSITION == format[index++])) + { + *indexPointer = index; + /* + * number is decreased by 1, because n$ starts from 1, whereas + * the array it is indexing starts from 0. + */ + return number - 1; + } +#endif + return NO_POSITION; +} + +#if TRIO_EXTENSION +/************************************************************************* + * TrioFindNamespace + * + * Find registered user-defined specifier. + * The prev argument is used for optimization only. + */ +TRIO_PRIVATE trio_userdef_t * +TrioFindNamespace +TRIO_ARGS2((name, prev), + TRIO_CONST char *name, + trio_userdef_t **prev) +{ + trio_userdef_t *def; + + if (internalEnterCriticalRegion) + (void)internalEnterCriticalRegion(NULL); + + for (def = internalUserDef; def; def = def->next) + { + /* Case-sensitive string comparison */ + if (trio_equal_case(def->name, name)) + break; + + if (prev) + *prev = def; + } + + if (internalLeaveCriticalRegion) + (void)internalLeaveCriticalRegion(NULL); + + return def; +} +#endif + +/************************************************************************* + * TrioPower + * + * Description: + * Calculate pow(base, exponent), where number and exponent are integers. + */ +TRIO_PRIVATE trio_long_double_t +TrioPower +TRIO_ARGS2((number, exponent), + int number, + int exponent) +{ + trio_long_double_t result; + + if (number == 10) + { + switch (exponent) + { + /* Speed up calculation of common cases */ + case 0: + result = (trio_long_double_t)number * TRIO_SUFFIX_LONG(1E-1); + break; + case 1: + result = (trio_long_double_t)number * TRIO_SUFFIX_LONG(1E+0); + break; + case 2: + result = (trio_long_double_t)number * TRIO_SUFFIX_LONG(1E+1); + break; + case 3: + result = (trio_long_double_t)number * TRIO_SUFFIX_LONG(1E+2); + break; + case 4: + result = (trio_long_double_t)number * TRIO_SUFFIX_LONG(1E+3); + break; + case 5: + result = (trio_long_double_t)number * TRIO_SUFFIX_LONG(1E+4); + break; + case 6: + result = (trio_long_double_t)number * TRIO_SUFFIX_LONG(1E+5); + break; + case 7: + result = (trio_long_double_t)number * TRIO_SUFFIX_LONG(1E+6); + break; + case 8: + result = (trio_long_double_t)number * TRIO_SUFFIX_LONG(1E+7); + break; + case 9: + result = (trio_long_double_t)number * TRIO_SUFFIX_LONG(1E+8); + break; + default: + result = powl((trio_long_double_t)number, + (trio_long_double_t)exponent); + break; + } + } + else + { + return powl((trio_long_double_t)number, (trio_long_double_t)exponent); + } + return result; +} + +/************************************************************************* + * TrioLogarithm + */ +TRIO_PRIVATE double +TrioLogarithm +TRIO_ARGS2((number, base), + double number, + int base) +{ + double result; + + if (number <= 0.0) + { + /* xlC crashes on log(0) */ + result = (number == 0.0) ? trio_ninf() : trio_nan(); + } + else + { + if (base == 10) + { + result = log10(number); + } + else + { + result = log10(number) / log10((double)base); + } + } + return result; +} + +/************************************************************************* + * TrioLogarithmBase + */ +TRIO_PRIVATE double +TrioLogarithmBase +TRIO_ARGS1((base), + int base) +{ + switch (base) + { + case BASE_BINARY : return 1.0; + case BASE_OCTAL : return 3.0; + case BASE_DECIMAL: return 3.321928094887362345; + case BASE_HEX : return 4.0; + default : return TrioLogarithm((double)base, 2); + } +} + +/************************************************************************* + * TrioParse + * + * Description: + * Parse the format string + */ +TRIO_PRIVATE int +TrioParse +TRIO_ARGS5((type, format, parameters, arglist, argarray), + int type, + TRIO_CONST char *format, + trio_parameter_t *parameters, + va_list *arglist, + trio_pointer_t *argarray) +{ + /* Count the number of times a parameter is referenced */ + unsigned short usedEntries[MAX_PARAMETERS]; + /* Parameter counters */ + int parameterPosition; + int currentParam; + int maxParam = -1; + /* Utility variables */ + trio_flags_t flags; + int width; + int precision; + int varsize; + int base; + int index; /* Index into formatting string */ + int dots; /* Count number of dots in modifier part */ + BOOLEAN_T positional; /* Does the specifier have a positional? */ + BOOLEAN_T gotSticky = FALSE; /* Are there any sticky modifiers at all? */ + /* + * indices specifies the order in which the parameters must be + * read from the va_args (this is necessary to handle positionals) + */ + int indices[MAX_PARAMETERS]; + int pos = 0; + /* Various variables */ + char ch; +#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE) + int charlen; +#endif + int save_errno; + int i = -1; + int num; + char *tmpformat; + + /* One and only one of arglist and argarray must be used */ + assert((arglist != NULL) ^ (argarray != NULL)); + + /* + * The 'parameters' array is not initialized, but we need to + * know which entries we have used. + */ + memset(usedEntries, 0, sizeof(usedEntries)); + + save_errno = errno; + index = 0; + parameterPosition = 0; +#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE) + (void)mblen(NULL, 0); +#endif + + while (format[index]) + { +#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE) + if (! isascii(format[index])) + { + /* + * Multibyte characters cannot be legal specifiers or + * modifiers, so we skip over them. + */ + charlen = mblen(&format[index], MB_LEN_MAX); + index += (charlen > 0) ? charlen : 1; + continue; /* while */ + } +#endif /* TRIO_COMPILER_SUPPORTS_MULTIBYTE */ + if (CHAR_IDENTIFIER == format[index++]) + { + if (CHAR_IDENTIFIER == format[index]) + { + index++; + continue; /* while */ + } + + flags = FLAGS_NEW; + dots = 0; + currentParam = TrioGetPosition(format, &index); + positional = (NO_POSITION != currentParam); + if (!positional) + { + /* We have no positional, get the next counter */ + currentParam = parameterPosition; + } + if(currentParam >= MAX_PARAMETERS) + { + /* Bail out completely to make the error more obvious */ + return TRIO_ERROR_RETURN(TRIO_ETOOMANY, index); + } + + if (currentParam > maxParam) + maxParam = currentParam; + + /* Default values */ + width = NO_WIDTH; + precision = NO_PRECISION; + base = NO_BASE; + varsize = NO_SIZE; + + while (TrioIsQualifier(format[index])) + { + ch = format[index++]; + + switch (ch) + { + case QUALIFIER_SPACE: + flags |= FLAGS_SPACE; + break; + + case QUALIFIER_PLUS: + flags |= FLAGS_SHOWSIGN; + break; + + case QUALIFIER_MINUS: + flags |= FLAGS_LEFTADJUST; + flags &= ~FLAGS_NILPADDING; + break; + + case QUALIFIER_ALTERNATIVE: + flags |= FLAGS_ALTERNATIVE; + break; + + case QUALIFIER_DOT: + if (dots == 0) /* Precision */ + { + dots++; + + /* Skip if no precision */ + if (QUALIFIER_DOT == format[index]) + break; + + /* After the first dot we have the precision */ + flags |= FLAGS_PRECISION; + if ((QUALIFIER_STAR == format[index]) +#if defined(QUALIFIER_PARAM) + || (QUALIFIER_PARAM == format[index]) +#endif + ) + { + index++; + flags |= FLAGS_PRECISION_PARAMETER; + + precision = TrioGetPosition(format, &index); + if (precision == NO_POSITION) + { + parameterPosition++; + if (positional) + precision = parameterPosition; + else + { + precision = currentParam; + currentParam = precision + 1; + } + } + else + { + if (! positional) + currentParam = precision + 1; + if (width > maxParam) + maxParam = precision; + } + if (currentParam > maxParam) + maxParam = currentParam; + } + else + { + precision = trio_to_long(&format[index], + &tmpformat, + BASE_DECIMAL); + index = (int)(tmpformat - format); + } + } + else if (dots == 1) /* Base */ + { + dots++; + + /* After the second dot we have the base */ + flags |= FLAGS_BASE; + if ((QUALIFIER_STAR == format[index]) +#if defined(QUALIFIER_PARAM) + || (QUALIFIER_PARAM == format[index]) +#endif + ) + { + index++; + flags |= FLAGS_BASE_PARAMETER; + base = TrioGetPosition(format, &index); + if (base == NO_POSITION) + { + parameterPosition++; + if (positional) + base = parameterPosition; + else + { + base = currentParam; + currentParam = base + 1; + } + } + else + { + if (! positional) + currentParam = base + 1; + if (base > maxParam) + maxParam = base; + } + if (currentParam > maxParam) + maxParam = currentParam; + } + else + { + base = trio_to_long(&format[index], + &tmpformat, + BASE_DECIMAL); + if (base > MAX_BASE) + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + index = (int)(tmpformat - format); + } + } + else + { + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + } + break; /* QUALIFIER_DOT */ + +#if defined(QUALIFIER_PARAM) + case QUALIFIER_PARAM: + type = TYPE_PRINT; + /* FALLTHROUGH */ +#endif + case QUALIFIER_STAR: + /* This has different meanings for print and scan */ + if (TYPE_PRINT == type) + { + /* Read with from parameter */ + flags |= (FLAGS_WIDTH | FLAGS_WIDTH_PARAMETER); + width = TrioGetPosition(format, &index); + if (width == NO_POSITION) + { + parameterPosition++; + if (positional) + width = parameterPosition; + else + { + width = currentParam; + currentParam = width + 1; + } + } + else + { + if (! positional) + currentParam = width + 1; + if (width > maxParam) + maxParam = width; + } + if (currentParam > maxParam) + maxParam = currentParam; + } + else + { + /* Scan, but do not store result */ + flags |= FLAGS_IGNORE; + } + + break; /* QUALIFIER_STAR */ + + case '0': + if (! (flags & FLAGS_LEFTADJUST)) + flags |= FLAGS_NILPADDING; + /* FALLTHROUGH */ + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + flags |= FLAGS_WIDTH; + /* &format[index - 1] is used to "rewind" the read + * character from format + */ + width = trio_to_long(&format[index - 1], + &tmpformat, + BASE_DECIMAL); + index = (int)(tmpformat - format); + break; + + case QUALIFIER_SHORT: + if (flags & FLAGS_SHORTSHORT) + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + else if (flags & FLAGS_SHORT) + flags |= FLAGS_SHORTSHORT; + else + flags |= FLAGS_SHORT; + break; + + case QUALIFIER_LONG: + if (flags & FLAGS_QUAD) + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + else if (flags & FLAGS_LONG) + flags |= FLAGS_QUAD; + else + flags |= FLAGS_LONG; + break; + + case QUALIFIER_LONG_UPPER: + flags |= FLAGS_LONGDOUBLE; + break; + +#if defined(QUALIFIER_SIZE_T) + case QUALIFIER_SIZE_T: + flags |= FLAGS_SIZE_T; + /* Modify flags for later truncation of number */ + if (sizeof(size_t) == sizeof(trio_ulonglong_t)) + flags |= FLAGS_QUAD; + else if (sizeof(size_t) == sizeof(long)) + flags |= FLAGS_LONG; + break; +#endif + +#if defined(QUALIFIER_PTRDIFF_T) + case QUALIFIER_PTRDIFF_T: + flags |= FLAGS_PTRDIFF_T; + if (sizeof(ptrdiff_t) == sizeof(trio_ulonglong_t)) + flags |= FLAGS_QUAD; + else if (sizeof(ptrdiff_t) == sizeof(long)) + flags |= FLAGS_LONG; + break; +#endif + +#if defined(QUALIFIER_INTMAX_T) + case QUALIFIER_INTMAX_T: + flags |= FLAGS_INTMAX_T; + if (sizeof(trio_intmax_t) == sizeof(trio_ulonglong_t)) + flags |= FLAGS_QUAD; + else if (sizeof(trio_intmax_t) == sizeof(long)) + flags |= FLAGS_LONG; + break; +#endif + +#if defined(QUALIFIER_QUAD) + case QUALIFIER_QUAD: + flags |= FLAGS_QUAD; + break; +#endif + +#if defined(QUALIFIER_FIXED_SIZE) + case QUALIFIER_FIXED_SIZE: + if (flags & FLAGS_FIXED_SIZE) + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + + if (flags & (FLAGS_ALL_SIZES | FLAGS_LONGDOUBLE | + FLAGS_WIDECHAR | FLAGS_VARSIZE_PARAMETER)) + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + + if ((format[index] == '6') && + (format[index + 1] == '4')) + { + varsize = sizeof(trio_int64_t); + index += 2; + } + else if ((format[index] == '3') && + (format[index + 1] == '2')) + { + varsize = sizeof(trio_int32_t); + index += 2; + } + else if ((format[index] == '1') && + (format[index + 1] == '6')) + { + varsize = sizeof(trio_int16_t); + index += 2; + } + else if (format[index] == '8') + { + varsize = sizeof(trio_int8_t); + index++; + } + else + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + + flags |= FLAGS_FIXED_SIZE; + break; +#endif + +#if defined(QUALIFIER_WIDECHAR) + case QUALIFIER_WIDECHAR: + flags |= FLAGS_WIDECHAR; + break; +#endif + +#if defined(QUALIFIER_SIZE_T_UPPER) + case QUALIFIER_SIZE_T_UPPER: + break; +#endif + +#if defined(QUALIFIER_QUOTE) + case QUALIFIER_QUOTE: + flags |= FLAGS_QUOTE; + break; +#endif + +#if defined(QUALIFIER_STICKY) + case QUALIFIER_STICKY: + flags |= FLAGS_STICKY; + gotSticky = TRUE; + break; +#endif + +#if defined(QUALIFIER_VARSIZE) + case QUALIFIER_VARSIZE: + flags |= FLAGS_VARSIZE_PARAMETER; + parameterPosition++; + if (positional) + varsize = parameterPosition; + else + { + varsize = currentParam; + currentParam = varsize + 1; + } + if (currentParam > maxParam) + maxParam = currentParam; + break; +#endif + +#if defined(QUALIFIER_ROUNDING_UPPER) + case QUALIFIER_ROUNDING_UPPER: + flags |= FLAGS_ROUNDING; + break; +#endif + + default: + /* Bail out completely to make the error more obvious */ + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + } + } /* while qualifier */ + + /* + * Parameters only need the type and value. The value is + * read later. + */ + if (flags & FLAGS_WIDTH_PARAMETER) + { + usedEntries[width] += 1; + parameters[pos].type = FORMAT_PARAMETER; + parameters[pos].flags = 0; + indices[width] = pos; + width = pos++; + } + if (flags & FLAGS_PRECISION_PARAMETER) + { + usedEntries[precision] += 1; + parameters[pos].type = FORMAT_PARAMETER; + parameters[pos].flags = 0; + indices[precision] = pos; + precision = pos++; + } + if (flags & FLAGS_BASE_PARAMETER) + { + usedEntries[base] += 1; + parameters[pos].type = FORMAT_PARAMETER; + parameters[pos].flags = 0; + indices[base] = pos; + base = pos++; + } + if (flags & FLAGS_VARSIZE_PARAMETER) + { + usedEntries[varsize] += 1; + parameters[pos].type = FORMAT_PARAMETER; + parameters[pos].flags = 0; + indices[varsize] = pos; + varsize = pos++; + } + + indices[currentParam] = pos; + + switch (format[index++]) + { +#if defined(SPECIFIER_CHAR_UPPER) + case SPECIFIER_CHAR_UPPER: + flags |= FLAGS_WIDECHAR; + /* FALLTHROUGH */ +#endif + case SPECIFIER_CHAR: + if (flags & FLAGS_LONG) + flags |= FLAGS_WIDECHAR; + else if (flags & FLAGS_SHORT) + flags &= ~FLAGS_WIDECHAR; + parameters[pos].type = FORMAT_CHAR; + break; + +#if defined(SPECIFIER_STRING_UPPER) + case SPECIFIER_STRING_UPPER: + flags |= FLAGS_WIDECHAR; + /* FALLTHROUGH */ +#endif + case SPECIFIER_STRING: + if (flags & FLAGS_LONG) + flags |= FLAGS_WIDECHAR; + else if (flags & FLAGS_SHORT) + flags &= ~FLAGS_WIDECHAR; + parameters[pos].type = FORMAT_STRING; + break; + + case SPECIFIER_GROUP: + if (TYPE_SCAN == type) + { + int depth = 1; + parameters[pos].type = FORMAT_GROUP; + if (format[index] == QUALIFIER_CIRCUMFLEX) + index++; + if (format[index] == SPECIFIER_UNGROUP) + index++; + if (format[index] == QUALIFIER_MINUS) + index++; + /* Skip nested brackets */ + while (format[index] != NIL) + { + if (format[index] == SPECIFIER_GROUP) + { + depth++; + } + else if (format[index] == SPECIFIER_UNGROUP) + { + if (--depth <= 0) + { + index++; + break; + } + } + index++; + } + } + break; + + case SPECIFIER_INTEGER: + parameters[pos].type = FORMAT_INT; + break; + + case SPECIFIER_UNSIGNED: + flags |= FLAGS_UNSIGNED; + parameters[pos].type = FORMAT_INT; + break; + + case SPECIFIER_DECIMAL: + /* Disable base modifier */ + flags &= ~FLAGS_BASE_PARAMETER; + base = BASE_DECIMAL; + parameters[pos].type = FORMAT_INT; + break; + + case SPECIFIER_OCTAL: + flags |= FLAGS_UNSIGNED; + flags &= ~FLAGS_BASE_PARAMETER; + base = BASE_OCTAL; + parameters[pos].type = FORMAT_INT; + break; + +#if defined(SPECIFIER_BINARY) + case SPECIFIER_BINARY_UPPER: + flags |= FLAGS_UPPER; + /* FALLTHROUGH */ + case SPECIFIER_BINARY: + flags |= FLAGS_NILPADDING; + flags &= ~FLAGS_BASE_PARAMETER; + base = BASE_BINARY; + parameters[pos].type = FORMAT_INT; + break; +#endif + + case SPECIFIER_HEX_UPPER: + flags |= FLAGS_UPPER; + /* FALLTHROUGH */ + case SPECIFIER_HEX: + flags |= FLAGS_UNSIGNED; + flags &= ~FLAGS_BASE_PARAMETER; + base = BASE_HEX; + parameters[pos].type = FORMAT_INT; + break; + + case SPECIFIER_FLOAT_E_UPPER: + flags |= FLAGS_UPPER; + /* FALLTHROUGH */ + case SPECIFIER_FLOAT_E: + flags |= FLAGS_FLOAT_E; + parameters[pos].type = FORMAT_DOUBLE; + break; + + case SPECIFIER_FLOAT_G_UPPER: + flags |= FLAGS_UPPER; + /* FALLTHROUGH */ + case SPECIFIER_FLOAT_G: + flags |= FLAGS_FLOAT_G; + parameters[pos].type = FORMAT_DOUBLE; + break; + + case SPECIFIER_FLOAT_F_UPPER: + flags |= FLAGS_UPPER; + /* FALLTHROUGH */ + case SPECIFIER_FLOAT_F: + parameters[pos].type = FORMAT_DOUBLE; + break; + + case SPECIFIER_POINTER: + if (sizeof(trio_pointer_t) == sizeof(trio_ulonglong_t)) + flags |= FLAGS_QUAD; + else if (sizeof(trio_pointer_t) == sizeof(long)) + flags |= FLAGS_LONG; + parameters[pos].type = FORMAT_POINTER; + break; + + case SPECIFIER_COUNT: + parameters[pos].type = FORMAT_COUNT; + break; + +#if defined(SPECIFIER_HEXFLOAT) +# if defined(SPECIFIER_HEXFLOAT_UPPER) + case SPECIFIER_HEXFLOAT_UPPER: + flags |= FLAGS_UPPER; + /* FALLTHROUGH */ +# endif + case SPECIFIER_HEXFLOAT: + base = BASE_HEX; + parameters[pos].type = FORMAT_DOUBLE; + break; +#endif + +#if defined(FORMAT_ERRNO) + case SPECIFIER_ERRNO: + parameters[pos].type = FORMAT_ERRNO; + break; +#endif + +#if defined(SPECIFIER_USER_DEFINED_BEGIN) + case SPECIFIER_USER_DEFINED_BEGIN: + { + unsigned int max; + int without_namespace = TRUE; + + parameters[pos].type = FORMAT_USER_DEFINED; + parameters[pos].user_name[0] = NIL; + tmpformat = (char *)&format[index]; + + while ((ch = format[index])) + { + index++; + if (ch == SPECIFIER_USER_DEFINED_END) + { + if (without_namespace) + { + /* We must get the handle first */ + parameters[pos].type = FORMAT_PARAMETER; + parameters[pos].indexAfterSpecifier = index; + parameters[pos].flags = FLAGS_USER_DEFINED; + /* Adjust parameters for insertion of new one */ + pos++; + usedEntries[currentParam] += 1; + parameters[pos].type = FORMAT_USER_DEFINED; + currentParam++; + indices[currentParam] = pos; + if (currentParam > maxParam) + maxParam = currentParam; + } + /* Copy the user data */ + max = (unsigned int)(&format[index] - tmpformat); + if (max > MAX_USER_DATA) + max = MAX_USER_DATA; + trio_copy_max(parameters[pos].user_data, + max, + tmpformat); + break; /* while */ + } + if (ch == SPECIFIER_USER_DEFINED_SEPARATOR) + { + without_namespace = FALSE; + /* Copy the namespace for later looking-up */ + max = (int)(&format[index] - tmpformat); + if (max > MAX_USER_NAME) + max = MAX_USER_NAME; + trio_copy_max(parameters[pos].user_name, + max, + tmpformat); + tmpformat = (char *)&format[index]; + } + } + if (ch != SPECIFIER_USER_DEFINED_END) + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + } + break; +#endif /* defined(SPECIFIER_USER_DEFINED_BEGIN) */ + + default: + /* Bail out completely to make the error more obvious */ + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + } + + /* Count the number of times this entry has been used */ + usedEntries[currentParam] += 1; + + /* Find last sticky parameters */ + if (gotSticky && !(flags & FLAGS_STICKY)) + { + for (i = pos - 1; i >= 0; i--) + { + if (parameters[i].type == FORMAT_PARAMETER) + continue; + if ((parameters[i].flags & FLAGS_STICKY) && + (parameters[i].type == parameters[pos].type)) + { + /* Do not overwrite current qualifiers */ + flags |= (parameters[i].flags & (unsigned long)~FLAGS_STICKY); + if (width == NO_WIDTH) + width = parameters[i].width; + if (precision == NO_PRECISION) + precision = parameters[i].precision; + if (base == NO_BASE) + base = parameters[i].base; + break; + } + } + } + + parameters[pos].indexAfterSpecifier = index; + parameters[pos].flags = flags; + parameters[pos].width = width; + parameters[pos].precision = precision; + parameters[pos].base = (base == NO_BASE) ? BASE_DECIMAL : base; + parameters[pos].varsize = varsize; + pos++; + + if (! positional) + parameterPosition++; + + } /* if identifier */ + + } /* while format characters left */ + + for (num = 0; num <= maxParam; num++) + { + if (usedEntries[num] != 1) + { + if (usedEntries[num] == 0) /* gap detected */ + return TRIO_ERROR_RETURN(TRIO_EGAP, num); + else /* double references detected */ + return TRIO_ERROR_RETURN(TRIO_EDBLREF, num); + } + + i = indices[num]; + + /* + * FORMAT_PARAMETERS are only present if they must be read, + * so it makes no sense to check the ignore flag (besides, + * the flags variable is not set for that particular type) + */ + if ((parameters[i].type != FORMAT_PARAMETER) && + (parameters[i].flags & FLAGS_IGNORE)) + continue; /* for all arguments */ + + /* + * The stack arguments are read according to ANSI C89 + * default argument promotions: + * + * char = int + * short = int + * unsigned char = unsigned int + * unsigned short = unsigned int + * float = double + * + * In addition to the ANSI C89 these types are read (the + * default argument promotions of C99 has not been + * considered yet) + * + * long long + * long double + * size_t + * ptrdiff_t + * intmax_t + */ + switch (parameters[i].type) + { + case FORMAT_GROUP: + case FORMAT_STRING: +#if TRIO_WIDECHAR + if (flags & FLAGS_WIDECHAR) + { + parameters[i].data.wstring = (argarray == NULL) + ? va_arg(*arglist, trio_wchar_t *) + : (trio_wchar_t *)(argarray[num]); + } + else +#endif + { + parameters[i].data.string = (argarray == NULL) + ? va_arg(*arglist, char *) + : (char *)(argarray[num]); + } + break; + +#if defined(FORMAT_USER_DEFINED) + case FORMAT_USER_DEFINED: +#endif + case FORMAT_POINTER: + case FORMAT_COUNT: + case FORMAT_UNKNOWN: + parameters[i].data.pointer = (argarray == NULL) + ? va_arg(*arglist, trio_pointer_t ) + : argarray[num]; + break; + + case FORMAT_CHAR: + case FORMAT_INT: + if (TYPE_SCAN == type) + { + if (argarray == NULL) + parameters[i].data.pointer = + (trio_pointer_t)va_arg(*arglist, trio_pointer_t); + else + { + if (parameters[i].type == FORMAT_CHAR) + parameters[i].data.pointer = + (trio_pointer_t)((char *)argarray[num]); + else if (parameters[i].flags & FLAGS_SHORT) + parameters[i].data.pointer = + (trio_pointer_t)((short *)argarray[num]); + else + parameters[i].data.pointer = + (trio_pointer_t)((int *)argarray[num]); + } + } + else + { +#if defined(QUALIFIER_VARSIZE) || defined(QUALIFIER_FIXED_SIZE) + if (parameters[i].flags + & (FLAGS_VARSIZE_PARAMETER | FLAGS_FIXED_SIZE)) + { + if (parameters[i].flags & FLAGS_VARSIZE_PARAMETER) + { + /* + * Variable sizes are mapped onto the fixed sizes, in + * accordance with integer promotion. + * + * Please note that this may not be portable, as we + * only guess the size, not the layout of the numbers. + * For example, if int is little-endian, and long is + * big-endian, then this will fail. + */ + varsize = (int)parameters[parameters[i].varsize].data.number.as_unsigned; + } + else + { + /* Used for the I modifiers */ + varsize = parameters[i].varsize; + } + parameters[i].flags &= ~FLAGS_ALL_VARSIZES; + + if (varsize <= (int)sizeof(int)) + ; + else if (varsize <= (int)sizeof(long)) + parameters[i].flags |= FLAGS_LONG; +#if defined(QUALIFIER_INTMAX_T) + else if (varsize <= (int)sizeof(trio_longlong_t)) + parameters[i].flags |= FLAGS_QUAD; + else + parameters[i].flags |= FLAGS_INTMAX_T; +#else + else + parameters[i].flags |= FLAGS_QUAD; +#endif + } +#endif /* defined(QUALIFIER_VARSIZE) */ +#if defined(QUALIFIER_SIZE_T) || defined(QUALIFIER_SIZE_T_UPPER) + if (parameters[i].flags & FLAGS_SIZE_T) + parameters[i].data.number.as_unsigned = (argarray == NULL) + ? (trio_uintmax_t)va_arg(*arglist, size_t) + : (trio_uintmax_t)(*((size_t *)argarray[num])); + else +#endif +#if defined(QUALIFIER_PTRDIFF_T) + if (parameters[i].flags & FLAGS_PTRDIFF_T) + parameters[i].data.number.as_unsigned = (argarray == NULL) + ? (trio_uintmax_t)va_arg(*arglist, ptrdiff_t) + : (trio_uintmax_t)(*((ptrdiff_t *)argarray[num])); + else +#endif +#if defined(QUALIFIER_INTMAX_T) + if (parameters[i].flags & FLAGS_INTMAX_T) + parameters[i].data.number.as_unsigned = (argarray == NULL) + ? (trio_uintmax_t)va_arg(*arglist, trio_intmax_t) + : (trio_uintmax_t)(*((trio_intmax_t *)argarray[num])); + else +#endif + if (parameters[i].flags & FLAGS_QUAD) + parameters[i].data.number.as_unsigned = (argarray == NULL) + ? (trio_uintmax_t)va_arg(*arglist, trio_ulonglong_t) + : (trio_uintmax_t)(*((trio_ulonglong_t *)argarray[num])); + else if (parameters[i].flags & FLAGS_LONG) + parameters[i].data.number.as_unsigned = (argarray == NULL) + ? (trio_uintmax_t)va_arg(*arglist, long) + : (trio_uintmax_t)(*((long *)argarray[num])); + else + { + if (argarray == NULL) + parameters[i].data.number.as_unsigned = (trio_uintmax_t)va_arg(*arglist, int); + else + { + if (parameters[i].type == FORMAT_CHAR) + parameters[i].data.number.as_unsigned = (trio_uintmax_t)(*((char *)argarray[num])); + else if (parameters[i].flags & FLAGS_SHORT) + parameters[i].data.number.as_unsigned = (trio_uintmax_t)(*((short *)argarray[num])); + else + parameters[i].data.number.as_unsigned = (trio_uintmax_t)(*((int *)argarray[num])); + } + } + } + break; + + case FORMAT_PARAMETER: + /* + * The parameter for the user-defined specifier is a pointer, + * whereas the rest (width, precision, base) uses an integer. + */ + if (parameters[i].flags & FLAGS_USER_DEFINED) + parameters[i].data.pointer = (argarray == NULL) + ? va_arg(*arglist, trio_pointer_t ) + : argarray[num]; + else + parameters[i].data.number.as_unsigned = (argarray == NULL) + ? (trio_uintmax_t)va_arg(*arglist, int) + : (trio_uintmax_t)(*((int *)argarray[num])); + break; + + case FORMAT_DOUBLE: + if (TYPE_SCAN == type) + { + if (parameters[i].flags & FLAGS_LONGDOUBLE) + parameters[i].data.longdoublePointer = (argarray == NULL) + ? va_arg(*arglist, trio_long_double_t *) + : (trio_long_double_t *)argarray[num]; + else + { + if (parameters[i].flags & FLAGS_LONG) + parameters[i].data.doublePointer = (argarray == NULL) + ? va_arg(*arglist, double *) + : (double *)argarray[num]; + else + parameters[i].data.doublePointer = (argarray == NULL) + ? (double *)va_arg(*arglist, float *) + : (double *)((float *)argarray[num]); + } + } + else + { + if (parameters[i].flags & FLAGS_LONGDOUBLE) + parameters[i].data.longdoubleNumber = (argarray == NULL) + ? va_arg(*arglist, trio_long_double_t) + : (trio_long_double_t)(*((trio_long_double_t *)argarray[num])); + else + { + if (argarray == NULL) + parameters[i].data.longdoubleNumber = + (trio_long_double_t)va_arg(*arglist, double); + else + { + if (parameters[i].flags & FLAGS_SHORT) + parameters[i].data.longdoubleNumber = + (trio_long_double_t)(*((float *)argarray[num])); + else + parameters[i].data.longdoubleNumber = + (trio_long_double_t)(*((double *)argarray[num])); + } + } + } + break; + +#if defined(FORMAT_ERRNO) + case FORMAT_ERRNO: + parameters[i].data.errorNumber = save_errno; + break; +#endif + + default: + break; + } + } /* for all specifiers */ + return num; +} + + +/************************************************************************* + * + * FORMATTING + * + ************************************************************************/ + + +/************************************************************************* + * TrioWriteNumber + * + * Description: + * Output a number. + * The complexity of this function is a result of the complexity + * of the dependencies of the flags. + */ +TRIO_PRIVATE void +TrioWriteNumber +TRIO_ARGS6((self, number, flags, width, precision, base), + trio_class_t *self, + trio_uintmax_t number, + trio_flags_t flags, + int width, + int precision, + int base) +{ + BOOLEAN_T isNegative; + BOOLEAN_T isNumberZero; + BOOLEAN_T isPrecisionZero; + BOOLEAN_T ignoreNumber; + char buffer[MAX_CHARS_IN(trio_uintmax_t) * (1 + MAX_LOCALE_SEPARATOR_LENGTH) + 1]; + char *bufferend; + char *pointer; + TRIO_CONST char *digits; + int i; + int length; + char *p; + int count; + + assert(VALID(self)); + assert(VALID(self->OutStream)); + assert(((base >= MIN_BASE) && (base <= MAX_BASE)) || (base == NO_BASE)); + + digits = (flags & FLAGS_UPPER) ? internalDigitsUpper : internalDigitsLower; + if (base == NO_BASE) + base = BASE_DECIMAL; + + isNumberZero = (number == 0); + isPrecisionZero = (precision == 0); + ignoreNumber = (isNumberZero + && isPrecisionZero + && !((flags & FLAGS_ALTERNATIVE) && (base == BASE_OCTAL))); + + if (flags & FLAGS_UNSIGNED) + { + isNegative = FALSE; + flags &= ~FLAGS_SHOWSIGN; + } + else + { + isNegative = ((trio_intmax_t)number < 0); + if (isNegative) + number = -((trio_intmax_t)number); + } + + if (flags & FLAGS_QUAD) + number &= (trio_ulonglong_t)-1; + else if (flags & FLAGS_LONG) + number &= (unsigned long)-1; + else + number &= (unsigned int)-1; + + /* Build number */ + pointer = bufferend = &buffer[sizeof(buffer) - 1]; + *pointer-- = NIL; + for (i = 1; i < (int)sizeof(buffer); i++) + { + *pointer-- = digits[number % base]; + number /= base; + if (number == 0) + break; + + if ((flags & FLAGS_QUOTE) && TrioFollowedBySeparator(i + 1)) + { + /* + * We are building the number from the least significant + * to the most significant digit, so we have to copy the + * thousand separator backwards + */ + length = internalThousandSeparatorLength; + if (((int)(pointer - buffer) - length) > 0) + { + p = &internalThousandSeparator[length - 1]; + while (length-- > 0) + *pointer-- = *p--; + } + } + } + + if (! ignoreNumber) + { + /* Adjust width */ + width -= (bufferend - pointer) - 1; + } + + /* Adjust precision */ + if (NO_PRECISION != precision) + { + precision -= (bufferend - pointer) - 1; + if (precision < 0) + precision = 0; + flags |= FLAGS_NILPADDING; + } + + /* Calculate padding */ + count = (! ((flags & FLAGS_LEFTADJUST) || (precision == NO_PRECISION))) + ? precision + : 0; + + /* Adjust width further */ + if (isNegative || (flags & FLAGS_SHOWSIGN) || (flags & FLAGS_SPACE)) + width--; + if ((flags & FLAGS_ALTERNATIVE) && !isNumberZero) + { + switch (base) + { + case BASE_BINARY: + case BASE_HEX: + width -= 2; + break; + case BASE_OCTAL: + if (!(flags & FLAGS_NILPADDING) || (count == 0)) + width--; + break; + default: + break; + } + } + + /* Output prefixes spaces if needed */ + if (! ((flags & FLAGS_LEFTADJUST) || + ((flags & FLAGS_NILPADDING) && (precision == NO_PRECISION)))) + { + while (width-- > count) + self->OutStream(self, CHAR_ADJUST); + } + + /* width has been adjusted for signs and alternatives */ + if (isNegative) + self->OutStream(self, '-'); + else if (flags & FLAGS_SHOWSIGN) + self->OutStream(self, '+'); + else if (flags & FLAGS_SPACE) + self->OutStream(self, ' '); + + /* Prefix is not written when the value is zero */ + if ((flags & FLAGS_ALTERNATIVE) && !isNumberZero) + { + switch (base) + { + case BASE_BINARY: + self->OutStream(self, '0'); + self->OutStream(self, (flags & FLAGS_UPPER) ? 'B' : 'b'); + break; + + case BASE_OCTAL: + if (!(flags & FLAGS_NILPADDING) || (count == 0)) + self->OutStream(self, '0'); + break; + + case BASE_HEX: + self->OutStream(self, '0'); + self->OutStream(self, (flags & FLAGS_UPPER) ? 'X' : 'x'); + break; + + default: + break; + } /* switch base */ + } + + /* Output prefixed zero padding if needed */ + if (flags & FLAGS_NILPADDING) + { + if (precision == NO_PRECISION) + precision = width; + while (precision-- > 0) + { + self->OutStream(self, '0'); + width--; + } + } + + if (! ignoreNumber) + { + /* Output the number itself */ + while (*(++pointer)) + { + self->OutStream(self, *pointer); + } + } + + /* Output trailing spaces if needed */ + if (flags & FLAGS_LEFTADJUST) + { + while (width-- > 0) + self->OutStream(self, CHAR_ADJUST); + } +} + +/************************************************************************* + * TrioWriteStringCharacter + * + * Description: + * Output a single character of a string + */ +TRIO_PRIVATE void +TrioWriteStringCharacter +TRIO_ARGS3((self, ch, flags), + trio_class_t *self, + int ch, + trio_flags_t flags) +{ + if (flags & FLAGS_ALTERNATIVE) + { + if (! isprint(ch)) + { + /* + * Non-printable characters are converted to C escapes or + * \number, if no C escape exists. + */ + self->OutStream(self, CHAR_BACKSLASH); + switch (ch) + { + case '\007': self->OutStream(self, 'a'); break; + case '\b': self->OutStream(self, 'b'); break; + case '\f': self->OutStream(self, 'f'); break; + case '\n': self->OutStream(self, 'n'); break; + case '\r': self->OutStream(self, 'r'); break; + case '\t': self->OutStream(self, 't'); break; + case '\v': self->OutStream(self, 'v'); break; + case '\\': self->OutStream(self, '\\'); break; + default: + self->OutStream(self, 'x'); + TrioWriteNumber(self, (trio_uintmax_t)ch, + FLAGS_UNSIGNED | FLAGS_NILPADDING, + 2, 2, BASE_HEX); + break; + } + } + else if (ch == CHAR_BACKSLASH) + { + self->OutStream(self, CHAR_BACKSLASH); + self->OutStream(self, CHAR_BACKSLASH); + } + else + { + self->OutStream(self, ch); + } + } + else + { + self->OutStream(self, ch); + } +} + +/************************************************************************* + * TrioWriteString + * + * Description: + * Output a string + */ +TRIO_PRIVATE void +TrioWriteString +TRIO_ARGS5((self, string, flags, width, precision), + trio_class_t *self, + TRIO_CONST char *string, + trio_flags_t flags, + int width, + int precision) +{ + int length; + int ch; + + assert(VALID(self)); + assert(VALID(self->OutStream)); + + if (string == NULL) + { + string = internalNullString; + length = sizeof(internalNullString) - 1; + /* Disable quoting for the null pointer */ + flags &= (~FLAGS_QUOTE); + width = 0; + } + else + { + length = trio_length(string); + } + if ((NO_PRECISION != precision) && + (precision < length)) + { + length = precision; + } + width -= length; + + if (flags & FLAGS_QUOTE) + self->OutStream(self, CHAR_QUOTE); + + if (! (flags & FLAGS_LEFTADJUST)) + { + while (width-- > 0) + self->OutStream(self, CHAR_ADJUST); + } + + while (length-- > 0) + { + /* The ctype parameters must be an unsigned char (or EOF) */ + ch = (int)((unsigned char)(*string++)); + TrioWriteStringCharacter(self, ch, flags); + } + + if (flags & FLAGS_LEFTADJUST) + { + while (width-- > 0) + self->OutStream(self, CHAR_ADJUST); + } + if (flags & FLAGS_QUOTE) + self->OutStream(self, CHAR_QUOTE); +} + +/************************************************************************* + * TrioWriteWideStringCharacter + * + * Description: + * Output a wide string as a multi-byte sequence + */ +#if TRIO_WIDECHAR +TRIO_PRIVATE int +TrioWriteWideStringCharacter +TRIO_ARGS4((self, wch, flags, width), + trio_class_t *self, + trio_wchar_t wch, + trio_flags_t flags, + int width) +{ + int size; + int i; + int ch; + char *string; + char buffer[MB_LEN_MAX + 1]; + + if (width == NO_WIDTH) + width = sizeof(buffer); + + size = wctomb(buffer, wch); + if ((size <= 0) || (size > width) || (buffer[0] == NIL)) + return 0; + + string = buffer; + i = size; + while ((width >= i) && (width-- > 0) && (i-- > 0)) + { + /* The ctype parameters must be an unsigned char (or EOF) */ + ch = (int)((unsigned char)(*string++)); + TrioWriteStringCharacter(self, ch, flags); + } + return size; +} +#endif /* TRIO_WIDECHAR */ + +/************************************************************************* + * TrioWriteWideString + * + * Description: + * Output a wide character string as a multi-byte string + */ +#if TRIO_WIDECHAR +TRIO_PRIVATE void +TrioWriteWideString +TRIO_ARGS5((self, wstring, flags, width, precision), + trio_class_t *self, + TRIO_CONST trio_wchar_t *wstring, + trio_flags_t flags, + int width, + int precision) +{ + int length; + int size; + + assert(VALID(self)); + assert(VALID(self->OutStream)); + +#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE) + (void)mblen(NULL, 0); +#endif + + if (wstring == NULL) + { + TrioWriteString(self, NULL, flags, width, precision); + return; + } + + if (NO_PRECISION == precision) + { + length = INT_MAX; + } + else + { + length = precision; + width -= length; + } + + if (flags & FLAGS_QUOTE) + self->OutStream(self, CHAR_QUOTE); + + if (! (flags & FLAGS_LEFTADJUST)) + { + while (width-- > 0) + self->OutStream(self, CHAR_ADJUST); + } + + while (length > 0) + { + size = TrioWriteWideStringCharacter(self, *wstring++, flags, length); + if (size == 0) + break; /* while */ + length -= size; + } + + if (flags & FLAGS_LEFTADJUST) + { + while (width-- > 0) + self->OutStream(self, CHAR_ADJUST); + } + if (flags & FLAGS_QUOTE) + self->OutStream(self, CHAR_QUOTE); +} +#endif /* TRIO_WIDECHAR */ + +/************************************************************************* + * TrioWriteDouble + * + * http://wwwold.dkuug.dk/JTC1/SC22/WG14/www/docs/dr_211.htm + * + * "5.2.4.2.2 paragraph #4 + * + * The accuracy [...] is implementation defined, as is the accuracy + * of the conversion between floating-point internal representations + * and string representations performed by the libray routine in + * " + */ +/* FIXME: handle all instances of constant long-double number (L) + * and *l() math functions. + */ +TRIO_PRIVATE void +TrioWriteDouble +TRIO_ARGS6((self, number, flags, width, precision, base), + trio_class_t *self, + trio_long_double_t number, + trio_flags_t flags, + int width, + int precision, + int base) +{ + trio_long_double_t integerNumber; + trio_long_double_t fractionNumber; + trio_long_double_t workNumber; + int integerDigits; + int fractionDigits; + int exponentDigits; + int baseDigits; + int integerThreshold; + int fractionThreshold; + int expectedWidth; + int exponent = 0; + unsigned int uExponent = 0; + int exponentBase; + trio_long_double_t dblBase; + trio_long_double_t dblIntegerBase; + trio_long_double_t dblFractionBase; + trio_long_double_t integerAdjust; + trio_long_double_t fractionAdjust; + BOOLEAN_T isNegative; + BOOLEAN_T isExponentNegative = FALSE; + BOOLEAN_T requireTwoDigitExponent; + BOOLEAN_T isHex; + TRIO_CONST char *digits; + char *groupingPointer; + int i; + int index; + BOOLEAN_T hasOnlyZeroes; + int zeroes = 0; + register int trailingZeroes; + BOOLEAN_T keepTrailingZeroes; + BOOLEAN_T keepDecimalPoint; + trio_long_double_t epsilon; + + assert(VALID(self)); + assert(VALID(self->OutStream)); + assert(((base >= MIN_BASE) && (base <= MAX_BASE)) || (base == NO_BASE)); + + /* Determine sign and look for special quantities */ + switch (trio_fpclassify_and_signbit(number, &isNegative)) + { + case TRIO_FP_NAN: + TrioWriteString(self, + (flags & FLAGS_UPPER) + ? NAN_UPPER + : NAN_LOWER, + flags, width, precision); + return; + + case TRIO_FP_INFINITE: + if (isNegative) + { + /* Negative infinity */ + TrioWriteString(self, + (flags & FLAGS_UPPER) + ? "-" INFINITE_UPPER + : "-" INFINITE_LOWER, + flags, width, precision); + return; + } + else + { + /* Positive infinity */ + TrioWriteString(self, + (flags & FLAGS_UPPER) + ? INFINITE_UPPER + : INFINITE_LOWER, + flags, width, precision); + return; + } + + default: + /* Finitude */ + break; + } + + /* Normal numbers */ + if (flags & FLAGS_LONGDOUBLE) + { + baseDigits = (base == 10) + ? LDBL_DIG + : (int)floor(LDBL_MANT_DIG / TrioLogarithmBase(base)); + epsilon = LDBL_EPSILON; + } + else if (flags & FLAGS_SHORT) + { + baseDigits = (base == BASE_DECIMAL) + ? FLT_DIG + : (int)floor(FLT_MANT_DIG / TrioLogarithmBase(base)); + epsilon = FLT_EPSILON; + } + else + { + baseDigits = (base == BASE_DECIMAL) + ? DBL_DIG + : (int)floor(DBL_MANT_DIG / TrioLogarithmBase(base)); + epsilon = DBL_EPSILON; + } + + digits = (flags & FLAGS_UPPER) ? internalDigitsUpper : internalDigitsLower; + isHex = (base == BASE_HEX); + if (base == NO_BASE) + base = BASE_DECIMAL; + dblBase = (trio_long_double_t)base; + keepTrailingZeroes = !( (flags & FLAGS_ROUNDING) || + ( (flags & FLAGS_FLOAT_G) && + !(flags & FLAGS_ALTERNATIVE) ) ); + + if (flags & FLAGS_ROUNDING) + precision = baseDigits; + + if (precision == NO_PRECISION) + { + if (isHex) + { + keepTrailingZeroes = FALSE; + precision = FLT_MANT_DIG; + } + else + { + precision = FLT_DIG; + } + } + + if (isNegative) + number = -number; + + if (isHex) + flags |= FLAGS_FLOAT_E; + + if (flags & FLAGS_FLOAT_G) + { + if (precision == 0) + precision = 1; + + if ((number < 1.0E-4) || (number > powl(base, + (trio_long_double_t)precision))) + { + /* Use scientific notation */ + flags |= FLAGS_FLOAT_E; + } + else if (number < 1.0) + { + /* + * Use normal notation. If the integer part of the number is + * zero, then adjust the precision to include leading fractional + * zeros. + */ + workNumber = TrioLogarithm(number, base); + workNumber = TRIO_FABS(workNumber); + if (workNumber - floorl(workNumber) < 0.001) + workNumber--; + zeroes = (int)floorl(workNumber); + } + } + + if (flags & FLAGS_FLOAT_E) + { + /* Scale the number */ + workNumber = TrioLogarithm(number, base); + if (trio_isinf(workNumber) == -1) + { + exponent = 0; + /* Undo setting */ + if (flags & FLAGS_FLOAT_G) + flags &= ~FLAGS_FLOAT_E; + } + else + { + exponent = (int)floorl(workNumber); + number /= powl(dblBase, (trio_long_double_t)exponent); + isExponentNegative = (exponent < 0); + uExponent = (isExponentNegative) ? -exponent : exponent; + if (isHex) + uExponent *= 4; /* log16(2) */ + /* No thousand separators */ + flags &= ~FLAGS_QUOTE; + } + } + + integerNumber = floorl(number); + fractionNumber = number - integerNumber; + + /* + * Truncated number. + * + * Precision is number of significant digits for FLOAT_G + * and number of fractional digits for others. + */ + integerDigits = (integerNumber > epsilon) + ? 1 + (int)TrioLogarithm(integerNumber, base) + : 1; + fractionDigits = ((flags & FLAGS_FLOAT_G) && (zeroes == 0)) + ? precision - integerDigits + : zeroes + precision; + + dblFractionBase = TrioPower(base, fractionDigits); + + workNumber = number + 0.5 / dblFractionBase; + if (floorl(number) != floorl(workNumber)) + { + if (flags & FLAGS_FLOAT_E) + { + /* Adjust if number was rounded up one digit (ie. 0.99 to 1.00) */ + exponent++; + isExponentNegative = (exponent < 0); + uExponent = (isExponentNegative) ? -exponent : exponent; + if (isHex) + uExponent *= 4; /* log16(2) */ + workNumber = (number + 0.5 / dblFractionBase) / dblBase; + integerNumber = floorl(workNumber); + fractionNumber = workNumber - integerNumber; + } + else + { + /* Adjust if number was rounded up one digit (ie. 99 to 100) */ + integerNumber = floorl(number + 0.5); + fractionNumber = 0.0; + integerDigits = (integerNumber > epsilon) + ? 1 + (int)TrioLogarithm(integerNumber, base) + : 1; + } + } + + /* Estimate accuracy */ + integerAdjust = fractionAdjust = 0.5; + if (flags & FLAGS_ROUNDING) + { + if (integerDigits > baseDigits) + { + integerThreshold = baseDigits; + fractionDigits = 0; + dblFractionBase = 1.0; + fractionThreshold = 0; + precision = 0; /* Disable decimal-point */ + integerAdjust = TrioPower(base, integerDigits - integerThreshold - 1); + fractionAdjust = 0.0; + } + else + { + integerThreshold = integerDigits; + fractionThreshold = fractionDigits - integerThreshold; + fractionAdjust = 1.0; + } + } + else + { + integerThreshold = INT_MAX; + fractionThreshold = INT_MAX; + } + + /* + * Calculate expected width. + * sign + integer part + thousands separators + decimal point + * + fraction + exponent + */ + fractionAdjust /= dblFractionBase; + hasOnlyZeroes = (floorl((fractionNumber + fractionAdjust) * dblFractionBase) < epsilon); + keepDecimalPoint = ( (flags & FLAGS_ALTERNATIVE) || + !((precision == 0) || + (!keepTrailingZeroes && hasOnlyZeroes)) ); + if (flags & FLAGS_FLOAT_E) + { + exponentDigits = (uExponent == 0) + ? 1 + : (int)ceil(TrioLogarithm((double)(uExponent + 1), + (isHex) ? 10.0 : base)); + } + else + exponentDigits = 0; + requireTwoDigitExponent = ((base == BASE_DECIMAL) && (exponentDigits == 1)); + + expectedWidth = integerDigits + fractionDigits + + (keepDecimalPoint + ? internalDecimalPointLength + : 0) + + ((flags & FLAGS_QUOTE) + ? TrioCalcThousandSeparatorLength(integerDigits) + : 0); + if (isNegative || (flags & FLAGS_SHOWSIGN) || (flags & FLAGS_SPACE)) + expectedWidth += sizeof("-") - 1; + if (exponentDigits > 0) + expectedWidth += exponentDigits + + ((requireTwoDigitExponent ? sizeof("E+0") : sizeof("E+")) - 1); + if (isHex) + expectedWidth += sizeof("0X") - 1; + + /* Output prefixing */ + if (flags & FLAGS_NILPADDING) + { + /* Leading zeros must be after sign */ + if (isNegative) + self->OutStream(self, '-'); + else if (flags & FLAGS_SHOWSIGN) + self->OutStream(self, '+'); + else if (flags & FLAGS_SPACE) + self->OutStream(self, ' '); + if (isHex) + { + self->OutStream(self, '0'); + self->OutStream(self, (flags & FLAGS_UPPER) ? 'X' : 'x'); + } + if (!(flags & FLAGS_LEFTADJUST)) + { + for (i = expectedWidth; i < width; i++) + { + self->OutStream(self, '0'); + } + } + } + else + { + /* Leading spaces must be before sign */ + if (!(flags & FLAGS_LEFTADJUST)) + { + for (i = expectedWidth; i < width; i++) + { + self->OutStream(self, CHAR_ADJUST); + } + } + if (isNegative) + self->OutStream(self, '-'); + else if (flags & FLAGS_SHOWSIGN) + self->OutStream(self, '+'); + else if (flags & FLAGS_SPACE) + self->OutStream(self, ' '); + if (isHex) + { + self->OutStream(self, '0'); + self->OutStream(self, (flags & FLAGS_UPPER) ? 'X' : 'x'); + } + } + + /* Output the integer part and thousand separators */ + dblIntegerBase = 1.0 / TrioPower(base, integerDigits - 1); + for (i = 0; i < integerDigits; i++) + { + workNumber = floorl(((integerNumber + integerAdjust) * dblIntegerBase)); + if (i > integerThreshold) + { + /* Beyond accuracy */ + self->OutStream(self, digits[0]); + } + else + { + self->OutStream(self, digits[(int)fmodl(workNumber, dblBase)]); + } + dblIntegerBase *= dblBase; + + if (((flags & (FLAGS_FLOAT_E | FLAGS_QUOTE)) == FLAGS_QUOTE) + && TrioFollowedBySeparator(integerDigits - i)) + { + for (groupingPointer = internalThousandSeparator; + *groupingPointer != NIL; + groupingPointer++) + { + self->OutStream(self, *groupingPointer); + } + } + } + + /* Insert decimal point and build the fraction part */ + trailingZeroes = 0; + + if (keepDecimalPoint) + { + if (internalDecimalPoint) + { + self->OutStream(self, internalDecimalPoint); + } + else + { + for (i = 0; i < internalDecimalPointLength; i++) + { + self->OutStream(self, internalDecimalPointString[i]); + } + } + } + + for (i = 0; i < fractionDigits; i++) + { + if ((integerDigits > integerThreshold) || (i > fractionThreshold)) + { + /* Beyond accuracy */ + trailingZeroes++; + } + else + { + fractionNumber *= dblBase; + fractionAdjust *= dblBase; + workNumber = floorl(fractionNumber + fractionAdjust); + fractionNumber -= workNumber; + index = (int)fmodl(workNumber, dblBase); + if (index == 0) + { + trailingZeroes++; + } + else + { + while (trailingZeroes > 0) + { + /* Not trailing zeroes after all */ + self->OutStream(self, digits[0]); + trailingZeroes--; + } + self->OutStream(self, digits[index]); + } + } + } + + if (keepTrailingZeroes) + { + while (trailingZeroes > 0) + { + self->OutStream(self, digits[0]); + trailingZeroes--; + } + } + + /* Output exponent */ + if (exponentDigits > 0) + { + self->OutStream(self, + isHex + ? ((flags & FLAGS_UPPER) ? 'P' : 'p') + : ((flags & FLAGS_UPPER) ? 'E' : 'e')); + self->OutStream(self, (isExponentNegative) ? '-' : '+'); + + /* The exponent must contain at least two digits */ + if (requireTwoDigitExponent) + self->OutStream(self, '0'); + + if (isHex) + base = 10.0; + exponentBase = (int)TrioPower(base, exponentDigits - 1); + for (i = 0; i < exponentDigits; i++) + { + self->OutStream(self, digits[(uExponent / exponentBase) % base]); + exponentBase /= base; + } + } + /* Output trailing spaces */ + if (flags & FLAGS_LEFTADJUST) + { + for (i = expectedWidth; i < width; i++) + { + self->OutStream(self, CHAR_ADJUST); + } + } +} + +/************************************************************************* + * TrioFormatProcess + * + * Description: + * This is the main engine for formatting output + */ +TRIO_PRIVATE int +TrioFormatProcess +TRIO_ARGS3((data, format, parameters), + trio_class_t *data, + TRIO_CONST char *format, + trio_parameter_t *parameters) +{ +#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE) + int charlen; +#endif + int i; + TRIO_CONST char *string; + trio_pointer_t pointer; + trio_flags_t flags; + int width; + int precision; + int base; + int index; + + index = 0; + i = 0; +#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE) + (void)mblen(NULL, 0); +#endif + + while (format[index]) + { +#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE) + if (! isascii(format[index])) + { + charlen = mblen(&format[index], MB_LEN_MAX); + /* + * Only valid multibyte characters are handled here. Invalid + * multibyte characters (charlen == -1) are handled as normal + * characters. + */ + if (charlen != -1) + { + while (charlen-- > 0) + { + data->OutStream(data, format[index++]); + } + continue; /* while characters left in formatting string */ + } + } +#endif /* TRIO_COMPILER_SUPPORTS_MULTIBYTE */ + if (CHAR_IDENTIFIER == format[index]) + { + if (CHAR_IDENTIFIER == format[index + 1]) + { + data->OutStream(data, CHAR_IDENTIFIER); + index += 2; + } + else + { + /* Skip the parameter entries */ + while (parameters[i].type == FORMAT_PARAMETER) + i++; + + flags = parameters[i].flags; + + /* Find width */ + width = parameters[i].width; + if (flags & FLAGS_WIDTH_PARAMETER) + { + /* Get width from parameter list */ + width = (int)parameters[width].data.number.as_signed; + if (width < 0) + { + /* + * A negative width is the same as the - flag and + * a positive width. + */ + flags |= FLAGS_LEFTADJUST; + flags &= ~FLAGS_NILPADDING; + width = -width; + } + } + + /* Find precision */ + if (flags & FLAGS_PRECISION) + { + precision = parameters[i].precision; + if (flags & FLAGS_PRECISION_PARAMETER) + { + /* Get precision from parameter list */ + precision = (int)parameters[precision].data.number.as_signed; + if (precision < 0) + { + /* + * A negative precision is the same as no + * precision + */ + precision = NO_PRECISION; + } + } + } + else + { + precision = NO_PRECISION; + } + + /* Find base */ + base = parameters[i].base; + if (flags & FLAGS_BASE_PARAMETER) + { + /* Get base from parameter list */ + base = (int)parameters[base].data.number.as_signed; + } + + switch (parameters[i].type) + { + case FORMAT_CHAR: + if (flags & FLAGS_QUOTE) + data->OutStream(data, CHAR_QUOTE); + if (! (flags & FLAGS_LEFTADJUST)) + { + while (--width > 0) + data->OutStream(data, CHAR_ADJUST); + } +#if TRIO_WIDECHAR + if (flags & FLAGS_WIDECHAR) + { + TrioWriteWideStringCharacter(data, + (trio_wchar_t)parameters[i].data.number.as_signed, + flags, + NO_WIDTH); + } + else +#endif + { + TrioWriteStringCharacter(data, + (int)parameters[i].data.number.as_signed, + flags); + } + + if (flags & FLAGS_LEFTADJUST) + { + while(--width > 0) + data->OutStream(data, CHAR_ADJUST); + } + if (flags & FLAGS_QUOTE) + data->OutStream(data, CHAR_QUOTE); + + break; /* FORMAT_CHAR */ + + case FORMAT_INT: + TrioWriteNumber(data, + parameters[i].data.number.as_unsigned, + flags, + width, + precision, + base); + + break; /* FORMAT_INT */ + + case FORMAT_DOUBLE: + TrioWriteDouble(data, + parameters[i].data.longdoubleNumber, + flags, + width, + precision, + base); + break; /* FORMAT_DOUBLE */ + + case FORMAT_STRING: +#if TRIO_WIDECHAR + if (flags & FLAGS_WIDECHAR) + { + TrioWriteWideString(data, + parameters[i].data.wstring, + flags, + width, + precision); + } + else +#endif + { + TrioWriteString(data, + parameters[i].data.string, + flags, + width, + precision); + } + break; /* FORMAT_STRING */ + + case FORMAT_POINTER: + { + trio_reference_t reference; + + reference.data = data; + reference.parameter = ¶meters[i]; + trio_print_pointer(&reference, parameters[i].data.pointer); + } + break; /* FORMAT_POINTER */ + + case FORMAT_COUNT: + pointer = parameters[i].data.pointer; + if (NULL != pointer) + { + /* + * C99 paragraph 7.19.6.1.8 says "the number of + * characters written to the output stream so far by + * this call", which is data->committed + */ +#if defined(QUALIFIER_SIZE_T) || defined(QUALIFIER_SIZE_T_UPPER) + if (flags & FLAGS_SIZE_T) + *(size_t *)pointer = (size_t)data->committed; + else +#endif +#if defined(QUALIFIER_PTRDIFF_T) + if (flags & FLAGS_PTRDIFF_T) + *(ptrdiff_t *)pointer = (ptrdiff_t)data->committed; + else +#endif +#if defined(QUALIFIER_INTMAX_T) + if (flags & FLAGS_INTMAX_T) + *(trio_intmax_t *)pointer = (trio_intmax_t)data->committed; + else +#endif + if (flags & FLAGS_QUAD) + { + *(trio_ulonglong_t *)pointer = (trio_ulonglong_t)data->committed; + } + else if (flags & FLAGS_LONG) + { + *(long int *)pointer = (long int)data->committed; + } + else if (flags & FLAGS_SHORT) + { + *(short int *)pointer = (short int)data->committed; + } + else + { + *(int *)pointer = (int)data->committed; + } + } + break; /* FORMAT_COUNT */ + + case FORMAT_PARAMETER: + break; /* FORMAT_PARAMETER */ + +#if defined(FORMAT_ERRNO) + case FORMAT_ERRNO: + string = trio_error(parameters[i].data.errorNumber); + if (string) + { + TrioWriteString(data, + string, + flags, + width, + precision); + } + else + { + data->OutStream(data, '#'); + TrioWriteNumber(data, + (trio_uintmax_t)parameters[i].data.errorNumber, + flags, + width, + precision, + BASE_DECIMAL); + } + break; /* FORMAT_ERRNO */ +#endif /* defined(FORMAT_ERRNO) */ + +#if defined(FORMAT_USER_DEFINED) + case FORMAT_USER_DEFINED: + { + trio_reference_t reference; + trio_userdef_t *def = NULL; + + if (parameters[i].user_name[0] == NIL) + { + /* Use handle */ + if ((i > 0) || + (parameters[i - 1].type == FORMAT_PARAMETER)) + def = (trio_userdef_t *)parameters[i - 1].data.pointer; + } + else + { + /* Look up namespace */ + def = TrioFindNamespace(parameters[i].user_name, NULL); + } + if (def) { + reference.data = data; + reference.parameter = ¶meters[i]; + def->callback(&reference); + } + } + break; +#endif /* defined(FORMAT_USER_DEFINED) */ + + default: + break; + } /* switch parameter type */ + + /* Prepare for next */ + index = parameters[i].indexAfterSpecifier; + i++; + } + } + else /* not identifier */ + { + data->OutStream(data, format[index++]); + } + } + return data->processed; +} + +/************************************************************************* + * TrioFormatRef + */ +TRIO_PRIVATE int +TrioFormatRef +TRIO_ARGS4((reference, format, arglist, argarray), + trio_reference_t *reference, + TRIO_CONST char *format, + va_list *arglist, + trio_pointer_t *argarray) +{ + int status; + trio_parameter_t parameters[MAX_PARAMETERS]; + + status = TrioParse(TYPE_PRINT, format, parameters, arglist, argarray); + if (status < 0) + return status; + + status = TrioFormatProcess(reference->data, format, parameters); + if (reference->data->error != 0) + { + status = reference->data->error; + } + return status; +} + +/************************************************************************* + * TrioFormat + */ +TRIO_PRIVATE int +TrioFormat +TRIO_ARGS6((destination, destinationSize, OutStream, format, arglist, argarray), + trio_pointer_t destination, + size_t destinationSize, + void (*OutStream) TRIO_PROTO((trio_class_t *, int)), + TRIO_CONST char *format, + va_list *arglist, + trio_pointer_t *argarray) +{ + int status; + trio_class_t data; + trio_parameter_t parameters[MAX_PARAMETERS]; + + assert(VALID(OutStream)); + assert(VALID(format)); + + memset(&data, 0, sizeof(data)); + data.OutStream = OutStream; + data.location = destination; + data.max = destinationSize; + data.error = 0; + +#if defined(USE_LOCALE) + if (NULL == internalLocaleValues) + { + TrioSetLocale(); + } +#endif + + status = TrioParse(TYPE_PRINT, format, parameters, arglist, argarray); + if (status < 0) + return status; + + status = TrioFormatProcess(&data, format, parameters); + if (data.error != 0) + { + status = data.error; + } + return status; +} + +/************************************************************************* + * TrioOutStreamFile + */ +TRIO_PRIVATE void +TrioOutStreamFile +TRIO_ARGS2((self, output), + trio_class_t *self, + int output) +{ + FILE *file; + + assert(VALID(self)); + assert(VALID(self->location)); + + file = (FILE *)self->location; + self->processed++; + if (fputc(output, file) == EOF) + { + self->error = TRIO_ERROR_RETURN(TRIO_EOF, 0); + } + else + { + self->committed++; + } +} + +/************************************************************************* + * TrioOutStreamFileDescriptor + */ +TRIO_PRIVATE void +TrioOutStreamFileDescriptor +TRIO_ARGS2((self, output), + trio_class_t *self, + int output) +{ + int fd; + char ch; + + assert(VALID(self)); + + fd = *((int *)self->location); + ch = (char)output; + self->processed++; + if (write(fd, &ch, sizeof(char)) == -1) + { + self->error = TRIO_ERROR_RETURN(TRIO_ERRNO, 0); + } + else + { + self->committed++; + } +} + +/************************************************************************* + * TrioOutStreamCustom + */ +TRIO_PRIVATE void +TrioOutStreamCustom +TRIO_ARGS2((self, output), + trio_class_t *self, + int output) +{ + int status; + trio_custom_t *data; + + assert(VALID(self)); + assert(VALID(self->location)); + + data = (trio_custom_t *)self->location; + if (data->stream.out) + { + status = (data->stream.out)(data->closure, output); + if (status >= 0) + { + self->committed++; + } + else + { + if (self->error == 0) + { + self->error = TRIO_ERROR_RETURN(TRIO_ECUSTOM, -status); + } + } + } + self->processed++; +} + +/************************************************************************* + * TrioOutStreamString + */ +TRIO_PRIVATE void +TrioOutStreamString +TRIO_ARGS2((self, output), + trio_class_t *self, + int output) +{ + char **buffer; + + assert(VALID(self)); + assert(VALID(self->location)); + + buffer = (char **)self->location; + **buffer = (char)output; + (*buffer)++; + self->processed++; + self->committed++; +} + +/************************************************************************* + * TrioOutStreamStringMax + */ +TRIO_PRIVATE void +TrioOutStreamStringMax +TRIO_ARGS2((self, output), + trio_class_t *self, + int output) +{ + char **buffer; + + assert(VALID(self)); + assert(VALID(self->location)); + + buffer = (char **)self->location; + + if (self->processed < self->max) + { + **buffer = (char)output; + (*buffer)++; + self->committed++; + } + self->processed++; +} + +/************************************************************************* + * TrioOutStreamStringDynamic + */ +TRIO_PRIVATE void +TrioOutStreamStringDynamic +TRIO_ARGS2((self, output), + trio_class_t *self, + int output) +{ + assert(VALID(self)); + assert(VALID(self->location)); + + if (self->error == 0) + { + trio_xstring_append_char((trio_string_t *)self->location, + (char)output); + self->committed++; + } + /* The processed variable must always be increased */ + self->processed++; +} + +/************************************************************************* + * + * Formatted printing functions + * + ************************************************************************/ + +#if defined(TRIO_DOCUMENTATION) +# include "doc/doc_printf.h" +#endif +/** @addtogroup Printf + @{ +*/ + +/************************************************************************* + * printf + */ + +/** + Print to standard output stream. + + @param format Formatting string. + @param ... Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_printf +TRIO_VARGS2((format, va_alist), + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + + assert(VALID(format)); + + TRIO_VA_START(args, format); + status = TrioFormat(stdout, 0, TrioOutStreamFile, format, &args, NULL); + TRIO_VA_END(args); + return status; +} + +/** + Print to standard output stream. + + @param format Formatting string. + @param args Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_vprintf +TRIO_ARGS2((format, args), + TRIO_CONST char *format, + va_list args) +{ + assert(VALID(format)); + + return TrioFormat(stdout, 0, TrioOutStreamFile, format, &args, NULL); +} + +/** + Print to standard output stream. + + @param format Formatting string. + @param args Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_printfv +TRIO_ARGS2((format, args), + TRIO_CONST char *format, + trio_pointer_t * args) +{ + assert(VALID(format)); + + return TrioFormat(stdout, 0, TrioOutStreamFile, format, NULL, args); +} + +/************************************************************************* + * fprintf + */ + +/** + Print to file. + + @param file File pointer. + @param format Formatting string. + @param ... Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_fprintf +TRIO_VARGS3((file, format, va_alist), + FILE *file, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + + assert(VALID(file)); + assert(VALID(format)); + + TRIO_VA_START(args, format); + status = TrioFormat(file, 0, TrioOutStreamFile, format, &args, NULL); + TRIO_VA_END(args); + return status; +} + +/** + Print to file. + + @param file File pointer. + @param format Formatting string. + @param args Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_vfprintf +TRIO_ARGS3((file, format, args), + FILE *file, + TRIO_CONST char *format, + va_list args) +{ + assert(VALID(file)); + assert(VALID(format)); + + return TrioFormat(file, 0, TrioOutStreamFile, format, &args, NULL); +} + +/** + Print to file. + + @param file File pointer. + @param format Formatting string. + @param args Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_fprintfv +TRIO_ARGS3((file, format, args), + FILE *file, + TRIO_CONST char *format, + trio_pointer_t * args) +{ + assert(VALID(file)); + assert(VALID(format)); + + return TrioFormat(file, 0, TrioOutStreamFile, format, NULL, args); +} + +/************************************************************************* + * dprintf + */ + +/** + Print to file descriptor. + + @param fd File descriptor. + @param format Formatting string. + @param ... Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_dprintf +TRIO_VARGS3((fd, format, va_alist), + int fd, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + + assert(VALID(format)); + + TRIO_VA_START(args, format); + status = TrioFormat(&fd, 0, TrioOutStreamFileDescriptor, format, &args, NULL); + TRIO_VA_END(args); + return status; +} + +/** + Print to file descriptor. + + @param fd File descriptor. + @param format Formatting string. + @param args Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_vdprintf +TRIO_ARGS3((fd, format, args), + int fd, + TRIO_CONST char *format, + va_list args) +{ + assert(VALID(format)); + + return TrioFormat(&fd, 0, TrioOutStreamFileDescriptor, format, &args, NULL); +} + +/** + Print to file descriptor. + + @param fd File descriptor. + @param format Formatting string. + @param args Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_dprintfv +TRIO_ARGS3((fd, format, args), + int fd, + TRIO_CONST char *format, + trio_pointer_t *args) +{ + assert(VALID(format)); + + return TrioFormat(&fd, 0, TrioOutStreamFileDescriptor, format, NULL, args); +} + +/************************************************************************* + * cprintf + */ +TRIO_PUBLIC int +trio_cprintf +TRIO_VARGS4((stream, closure, format, va_alist), + trio_outstream_t stream, + trio_pointer_t closure, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + trio_custom_t data; + + assert(VALID(stream)); + assert(VALID(format)); + + TRIO_VA_START(args, format); + data.stream.out = stream; + data.closure = closure; + status = TrioFormat(&data, 0, TrioOutStreamCustom, format, &args, NULL); + TRIO_VA_END(args); + return status; +} + +TRIO_PUBLIC int +trio_vcprintf +TRIO_ARGS4((stream, closure, format, args), + trio_outstream_t stream, + trio_pointer_t closure, + TRIO_CONST char *format, + va_list args) +{ + trio_custom_t data; + + assert(VALID(stream)); + assert(VALID(format)); + + data.stream.out = stream; + data.closure = closure; + return TrioFormat(&data, 0, TrioOutStreamCustom, format, &args, NULL); +} + +TRIO_PUBLIC int +trio_cprintfv +TRIO_ARGS4((stream, closure, format, args), + trio_outstream_t stream, + trio_pointer_t closure, + TRIO_CONST char *format, + void **args) +{ + trio_custom_t data; + + assert(VALID(stream)); + assert(VALID(format)); + + data.stream.out = stream; + data.closure = closure; + return TrioFormat(&data, 0, TrioOutStreamCustom, format, NULL, args); +} + +/************************************************************************* + * sprintf + */ + +/** + Print to string. + + @param buffer Output string. + @param format Formatting string. + @param ... Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_sprintf +TRIO_VARGS3((buffer, format, va_alist), + char *buffer, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + + assert(VALID(buffer)); + assert(VALID(format)); + + TRIO_VA_START(args, format); + status = TrioFormat(&buffer, 0, TrioOutStreamString, format, &args, NULL); + *buffer = NIL; /* Terminate with NIL character */ + TRIO_VA_END(args); + return status; +} + +/** + Print to string. + + @param buffer Output string. + @param format Formatting string. + @param args Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_vsprintf +TRIO_ARGS3((buffer, format, args), + char *buffer, + TRIO_CONST char *format, + va_list args) +{ + int status; + + assert(VALID(buffer)); + assert(VALID(format)); + + status = TrioFormat(&buffer, 0, TrioOutStreamString, format, &args, NULL); + *buffer = NIL; + return status; +} + +/** + Print to string. + + @param buffer Output string. + @param format Formatting string. + @param args Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_sprintfv +TRIO_ARGS3((buffer, format, args), + char *buffer, + TRIO_CONST char *format, + trio_pointer_t *args) +{ + int status; + + assert(VALID(buffer)); + assert(VALID(format)); + + status = TrioFormat(&buffer, 0, TrioOutStreamString, format, NULL, args); + *buffer = NIL; + return status; +} + +/************************************************************************* + * snprintf + */ + +/** + Print at most @p max characters to string. + + @param buffer Output string. + @param max Maximum number of characters to print. + @param format Formatting string. + @param ... Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_snprintf +TRIO_VARGS4((buffer, max, format, va_alist), + char *buffer, + size_t max, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + + assert(VALID(buffer)); + assert(VALID(format)); + + TRIO_VA_START(args, format); + status = TrioFormat(&buffer, max > 0 ? max - 1 : 0, + TrioOutStreamStringMax, format, &args, NULL); + if (max > 0) + *buffer = NIL; + TRIO_VA_END(args); + return status; +} + +/** + Print at most @p max characters to string. + + @param buffer Output string. + @param max Maximum number of characters to print. + @param format Formatting string. + @param args Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_vsnprintf +TRIO_ARGS4((buffer, max, format, args), + char *buffer, + size_t max, + TRIO_CONST char *format, + va_list args) +{ + int status; + + assert(VALID(buffer)); + assert(VALID(format)); + + status = TrioFormat(&buffer, max > 0 ? max - 1 : 0, + TrioOutStreamStringMax, format, &args, NULL); + if (max > 0) + *buffer = NIL; + return status; +} + +/** + Print at most @p max characters to string. + + @param buffer Output string. + @param max Maximum number of characters to print. + @param format Formatting string. + @param args Arguments. + @return Number of printed characters. + */ +TRIO_PUBLIC int +trio_snprintfv +TRIO_ARGS4((buffer, max, format, args), + char *buffer, + size_t max, + TRIO_CONST char *format, + trio_pointer_t *args) +{ + int status; + + assert(VALID(buffer)); + assert(VALID(format)); + + status = TrioFormat(&buffer, max > 0 ? max - 1 : 0, + TrioOutStreamStringMax, format, NULL, args); + if (max > 0) + *buffer = NIL; + return status; +} + +/************************************************************************* + * snprintfcat + * Appends the new string to the buffer string overwriting the '\0' + * character at the end of buffer. + */ +TRIO_PUBLIC int +trio_snprintfcat +TRIO_VARGS4((buffer, max, format, va_alist), + char *buffer, + size_t max, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + size_t buf_len; + + TRIO_VA_START(args, format); + + assert(VALID(buffer)); + assert(VALID(format)); + + buf_len = trio_length(buffer); + buffer = &buffer[buf_len]; + + status = TrioFormat(&buffer, max - 1 - buf_len, + TrioOutStreamStringMax, format, &args, NULL); + TRIO_VA_END(args); + *buffer = NIL; + return status; +} + +TRIO_PUBLIC int +trio_vsnprintfcat +TRIO_ARGS4((buffer, max, format, args), + char *buffer, + size_t max, + TRIO_CONST char *format, + va_list args) +{ + int status; + size_t buf_len; + + assert(VALID(buffer)); + assert(VALID(format)); + + buf_len = trio_length(buffer); + buffer = &buffer[buf_len]; + status = TrioFormat(&buffer, max - 1 - buf_len, + TrioOutStreamStringMax, format, &args, NULL); + *buffer = NIL; + return status; +} + +/************************************************************************* + * trio_aprintf + */ + +/* Deprecated */ +TRIO_PUBLIC char * +trio_aprintf +TRIO_VARGS2((format, va_alist), + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + va_list args; + trio_string_t *info; + char *result = NULL; + + assert(VALID(format)); + + info = trio_xstring_duplicate(""); + if (info) + { + TRIO_VA_START(args, format); + (void)TrioFormat(info, 0, TrioOutStreamStringDynamic, + format, &args, NULL); + TRIO_VA_END(args); + + trio_string_terminate(info); + result = trio_string_extract(info); + trio_string_destroy(info); + } + return result; +} + +/* Deprecated */ +TRIO_PUBLIC char * +trio_vaprintf +TRIO_ARGS2((format, args), + TRIO_CONST char *format, + va_list args) +{ + trio_string_t *info; + char *result = NULL; + + assert(VALID(format)); + + info = trio_xstring_duplicate(""); + if (info) + { + (void)TrioFormat(info, 0, TrioOutStreamStringDynamic, + format, &args, NULL); + trio_string_terminate(info); + result = trio_string_extract(info); + trio_string_destroy(info); + } + return result; +} + +TRIO_PUBLIC int +trio_asprintf +TRIO_VARGS3((result, format, va_alist), + char **result, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + va_list args; + int status; + trio_string_t *info; + + assert(VALID(format)); + + *result = NULL; + + info = trio_xstring_duplicate(""); + if (info == NULL) + { + status = TRIO_ERROR_RETURN(TRIO_ENOMEM, 0); + } + else + { + TRIO_VA_START(args, format); + status = TrioFormat(info, 0, TrioOutStreamStringDynamic, + format, &args, NULL); + TRIO_VA_END(args); + if (status >= 0) + { + trio_string_terminate(info); + *result = trio_string_extract(info); + } + trio_string_destroy(info); + } + return status; +} + +TRIO_PUBLIC int +trio_vasprintf +TRIO_ARGS3((result, format, args), + char **result, + TRIO_CONST char *format, + va_list args) +{ + int status; + trio_string_t *info; + + assert(VALID(format)); + + *result = NULL; + + info = trio_xstring_duplicate(""); + if (info == NULL) + { + status = TRIO_ERROR_RETURN(TRIO_ENOMEM, 0); + } + else + { + status = TrioFormat(info, 0, TrioOutStreamStringDynamic, + format, &args, NULL); + if (status >= 0) + { + trio_string_terminate(info); + *result = trio_string_extract(info); + } + trio_string_destroy(info); + } + return status; +} + +/** @} End of Printf documentation module */ + +/************************************************************************* + * + * CALLBACK + * + ************************************************************************/ + +#if defined(TRIO_DOCUMENTATION) +# include "doc/doc_register.h" +#endif +/** + @addtogroup UserDefined + @{ +*/ + +#if TRIO_EXTENSION + +/************************************************************************* + * trio_register + */ + +/** + Register new user-defined specifier. + + @param callback + @param name + @return Handle. + */ +TRIO_PUBLIC trio_pointer_t +trio_register +TRIO_ARGS2((callback, name), + trio_callback_t callback, + TRIO_CONST char *name) +{ + trio_userdef_t *def; + trio_userdef_t *prev = NULL; + + if (callback == NULL) + return NULL; + + if (name) + { + /* Handle built-in namespaces */ + if (name[0] == ':') + { + if (trio_equal(name, ":enter")) + { + internalEnterCriticalRegion = callback; + } + else if (trio_equal(name, ":leave")) + { + internalLeaveCriticalRegion = callback; + } + return NULL; + } + + /* Bail out if namespace is too long */ + if (trio_length(name) >= MAX_USER_NAME) + return NULL; + + /* Bail out if namespace already is registered */ + def = TrioFindNamespace(name, &prev); + if (def) + return NULL; + } + + def = (trio_userdef_t *)TRIO_MALLOC(sizeof(trio_userdef_t)); + if (def) + { + if (internalEnterCriticalRegion) + (void)internalEnterCriticalRegion(NULL); + + if (name) + { + /* Link into internal list */ + if (prev == NULL) + internalUserDef = def; + else + prev->next = def; + } + /* Initialize */ + def->callback = callback; + def->name = (name == NULL) + ? NULL + : trio_duplicate(name); + def->next = NULL; + + if (internalLeaveCriticalRegion) + (void)internalLeaveCriticalRegion(NULL); + } + return (trio_pointer_t)def; +} + +/** + Unregister an existing user-defined specifier. + + @param handle + */ +void +trio_unregister +TRIO_ARGS1((handle), + trio_pointer_t handle) +{ + trio_userdef_t *self = (trio_userdef_t *)handle; + trio_userdef_t *def; + trio_userdef_t *prev = NULL; + + assert(VALID(self)); + + if (self->name) + { + def = TrioFindNamespace(self->name, &prev); + if (def) + { + if (internalEnterCriticalRegion) + (void)internalEnterCriticalRegion(NULL); + + if (prev == NULL) + internalUserDef = NULL; + else + prev->next = def->next; + + if (internalLeaveCriticalRegion) + (void)internalLeaveCriticalRegion(NULL); + } + trio_destroy(self->name); + } + TRIO_FREE(self); +} + +/************************************************************************* + * trio_get_format [public] + */ +TRIO_CONST char * +trio_get_format +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ +#if defined(FORMAT_USER_DEFINED) + assert(((trio_reference_t *)ref)->parameter->type == FORMAT_USER_DEFINED); +#endif + + return (((trio_reference_t *)ref)->parameter->user_data); +} + +/************************************************************************* + * trio_get_argument [public] + */ +trio_pointer_t +trio_get_argument +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ +#if defined(FORMAT_USER_DEFINED) + assert(((trio_reference_t *)ref)->parameter->type == FORMAT_USER_DEFINED); +#endif + + return ((trio_reference_t *)ref)->parameter->data.pointer; +} + +/************************************************************************* + * trio_get_width / trio_set_width [public] + */ +int +trio_get_width +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return ((trio_reference_t *)ref)->parameter->width; +} + +void +trio_set_width +TRIO_ARGS2((ref, width), + trio_pointer_t ref, + int width) +{ + ((trio_reference_t *)ref)->parameter->width = width; +} + +/************************************************************************* + * trio_get_precision / trio_set_precision [public] + */ +int +trio_get_precision +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->precision); +} + +void +trio_set_precision +TRIO_ARGS2((ref, precision), + trio_pointer_t ref, + int precision) +{ + ((trio_reference_t *)ref)->parameter->precision = precision; +} + +/************************************************************************* + * trio_get_base / trio_set_base [public] + */ +int +trio_get_base +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->base); +} + +void +trio_set_base +TRIO_ARGS2((ref, base), + trio_pointer_t ref, + int base) +{ + ((trio_reference_t *)ref)->parameter->base = base; +} + +/************************************************************************* + * trio_get_long / trio_set_long [public] + */ +int +trio_get_long +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_LONG) + ? TRUE + : FALSE; +} + +void +trio_set_long +TRIO_ARGS2((ref, is_long), + trio_pointer_t ref, + int is_long) +{ + if (is_long) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_LONG; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_LONG; +} + +/************************************************************************* + * trio_get_longlong / trio_set_longlong [public] + */ +int +trio_get_longlong +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_QUAD) + ? TRUE + : FALSE; +} + +void +trio_set_longlong +TRIO_ARGS2((ref, is_longlong), + trio_pointer_t ref, + int is_longlong) +{ + if (is_longlong) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_QUAD; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_QUAD; +} + +/************************************************************************* + * trio_get_longdouble / trio_set_longdouble [public] + */ +int +trio_get_longdouble +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_LONGDOUBLE) + ? TRUE + : FALSE; +} + +void +trio_set_longdouble +TRIO_ARGS2((ref, is_longdouble), + trio_pointer_t ref, + int is_longdouble) +{ + if (is_longdouble) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_LONGDOUBLE; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_LONGDOUBLE; +} + +/************************************************************************* + * trio_get_short / trio_set_short [public] + */ +int +trio_get_short +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_SHORT) + ? TRUE + : FALSE; +} + +void +trio_set_short +TRIO_ARGS2((ref, is_short), + trio_pointer_t ref, + int is_short) +{ + if (is_short) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_SHORT; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_SHORT; +} + +/************************************************************************* + * trio_get_shortshort / trio_set_shortshort [public] + */ +int +trio_get_shortshort +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_SHORTSHORT) + ? TRUE + : FALSE; +} + +void +trio_set_shortshort +TRIO_ARGS2((ref, is_shortshort), + trio_pointer_t ref, + int is_shortshort) +{ + if (is_shortshort) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_SHORTSHORT; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_SHORTSHORT; +} + +/************************************************************************* + * trio_get_alternative / trio_set_alternative [public] + */ +int +trio_get_alternative +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_ALTERNATIVE) + ? TRUE + : FALSE; +} + +void +trio_set_alternative +TRIO_ARGS2((ref, is_alternative), + trio_pointer_t ref, + int is_alternative) +{ + if (is_alternative) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_ALTERNATIVE; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_ALTERNATIVE; +} + +/************************************************************************* + * trio_get_alignment / trio_set_alignment [public] + */ +int +trio_get_alignment +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_LEFTADJUST) + ? TRUE + : FALSE; +} + +void +trio_set_alignment +TRIO_ARGS2((ref, is_leftaligned), + trio_pointer_t ref, + int is_leftaligned) +{ + if (is_leftaligned) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_LEFTADJUST; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_LEFTADJUST; +} + +/************************************************************************* + * trio_get_spacing /trio_set_spacing [public] + */ +int +trio_get_spacing +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_SPACE) + ? TRUE + : FALSE; +} + +void +trio_set_spacing +TRIO_ARGS2((ref, is_space), + trio_pointer_t ref, + int is_space) +{ + if (is_space) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_SPACE; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_SPACE; +} + +/************************************************************************* + * trio_get_sign / trio_set_sign [public] + */ +int +trio_get_sign +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_SHOWSIGN) + ? TRUE + : FALSE; +} + +void +trio_set_sign +TRIO_ARGS2((ref, is_sign), + trio_pointer_t ref, + int is_sign) +{ + if (is_sign) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_SHOWSIGN; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_SHOWSIGN; +} + +/************************************************************************* + * trio_get_padding / trio_set_padding [public] + */ +int +trio_get_padding +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_NILPADDING) + ? TRUE + : FALSE; +} + +void +trio_set_padding +TRIO_ARGS2((ref, is_padding), + trio_pointer_t ref, + int is_padding) +{ + if (is_padding) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_NILPADDING; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_NILPADDING; +} + +/************************************************************************* + * trio_get_quote / trio_set_quote [public] + */ +int +trio_get_quote +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_QUOTE) + ? TRUE + : FALSE; +} + +void +trio_set_quote +TRIO_ARGS2((ref, is_quote), + trio_pointer_t ref, + int is_quote) +{ + if (is_quote) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_QUOTE; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_QUOTE; +} + +/************************************************************************* + * trio_get_upper / trio_set_upper [public] + */ +int +trio_get_upper +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_UPPER) + ? TRUE + : FALSE; +} + +void +trio_set_upper +TRIO_ARGS2((ref, is_upper), + trio_pointer_t ref, + int is_upper) +{ + if (is_upper) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_UPPER; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_UPPER; +} + +/************************************************************************* + * trio_get_largest / trio_set_largest [public] + */ +#if TRIO_C99 +int +trio_get_largest +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_INTMAX_T) + ? TRUE + : FALSE; +} + +void +trio_set_largest +TRIO_ARGS2((ref, is_largest), + trio_pointer_t ref, + int is_largest) +{ + if (is_largest) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_INTMAX_T; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_INTMAX_T; +} +#endif + +/************************************************************************* + * trio_get_ptrdiff / trio_set_ptrdiff [public] + */ +int +trio_get_ptrdiff +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_PTRDIFF_T) + ? TRUE + : FALSE; +} + +void +trio_set_ptrdiff +TRIO_ARGS2((ref, is_ptrdiff), + trio_pointer_t ref, + int is_ptrdiff) +{ + if (is_ptrdiff) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_PTRDIFF_T; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_PTRDIFF_T; +} + +/************************************************************************* + * trio_get_size / trio_set_size [public] + */ +#if TRIO_C99 +int +trio_get_size +TRIO_ARGS1((ref), + trio_pointer_t ref) +{ + return (((trio_reference_t *)ref)->parameter->flags & FLAGS_SIZE_T) + ? TRUE + : FALSE; +} + +void +trio_set_size +TRIO_ARGS2((ref, is_size), + trio_pointer_t ref, + int is_size) +{ + if (is_size) + ((trio_reference_t *)ref)->parameter->flags |= FLAGS_SIZE_T; + else + ((trio_reference_t *)ref)->parameter->flags &= ~FLAGS_SIZE_T; +} +#endif + +/************************************************************************* + * trio_print_int [public] + */ +void +trio_print_int +TRIO_ARGS2((ref, number), + trio_pointer_t ref, + int number) +{ + trio_reference_t *self = (trio_reference_t *)ref; + + TrioWriteNumber(self->data, + (trio_uintmax_t)number, + self->parameter->flags, + self->parameter->width, + self->parameter->precision, + self->parameter->base); +} + +/************************************************************************* + * trio_print_uint [public] + */ +void +trio_print_uint +TRIO_ARGS2((ref, number), + trio_pointer_t ref, + unsigned int number) +{ + trio_reference_t *self = (trio_reference_t *)ref; + + TrioWriteNumber(self->data, + (trio_uintmax_t)number, + self->parameter->flags | FLAGS_UNSIGNED, + self->parameter->width, + self->parameter->precision, + self->parameter->base); +} + +/************************************************************************* + * trio_print_double [public] + */ +void +trio_print_double +TRIO_ARGS2((ref, number), + trio_pointer_t ref, + double number) +{ + trio_reference_t *self = (trio_reference_t *)ref; + + TrioWriteDouble(self->data, + number, + self->parameter->flags, + self->parameter->width, + self->parameter->precision, + self->parameter->base); +} + +/************************************************************************* + * trio_print_string [public] + */ +void +trio_print_string +TRIO_ARGS2((ref, string), + trio_pointer_t ref, + char *string) +{ + trio_reference_t *self = (trio_reference_t *)ref; + + TrioWriteString(self->data, + string, + self->parameter->flags, + self->parameter->width, + self->parameter->precision); +} + +/************************************************************************* + * trio_print_ref [public] + */ +int +trio_print_ref +TRIO_VARGS3((ref, format, va_alist), + trio_pointer_t ref, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list arglist; + + assert(VALID(format)); + + TRIO_VA_START(arglist, format); + status = TrioFormatRef((trio_reference_t *)ref, format, &arglist, NULL); + TRIO_VA_END(arglist); + return status; +} + +/************************************************************************* + * trio_vprint_ref [public] + */ +int +trio_vprint_ref +TRIO_ARGS3((ref, format, arglist), + trio_pointer_t ref, + TRIO_CONST char *format, + va_list arglist) +{ + assert(VALID(format)); + + return TrioFormatRef((trio_reference_t *)ref, format, &arglist, NULL); +} + +/************************************************************************* + * trio_printv_ref [public] + */ +int +trio_printv_ref +TRIO_ARGS3((ref, format, argarray), + trio_pointer_t ref, + TRIO_CONST char *format, + trio_pointer_t *argarray) +{ + assert(VALID(format)); + + return TrioFormatRef((trio_reference_t *)ref, format, NULL, argarray); +} + +#endif /* TRIO_EXTENSION */ + +/************************************************************************* + * trio_print_pointer [public] + */ +void +trio_print_pointer +TRIO_ARGS2((ref, pointer), + trio_pointer_t ref, + trio_pointer_t pointer) +{ + trio_reference_t *self = (trio_reference_t *)ref; + trio_flags_t flags; + trio_uintmax_t number; + + if (NULL == pointer) + { + TRIO_CONST char *string = internalNullString; + while (*string) + self->data->OutStream(self->data, *string++); + } + else + { + /* + * The subtraction of the null pointer is a workaround + * to avoid a compiler warning. The performance overhead + * is negligible (and likely to be removed by an + * optimizing compiler). The (char *) casting is done + * to please ANSI C++. + */ + number = (trio_uintmax_t)((char *)pointer - (char *)0); + /* Shrink to size of pointer */ + number &= (trio_uintmax_t)-1; + flags = self->parameter->flags; + flags |= (FLAGS_UNSIGNED | FLAGS_ALTERNATIVE | + FLAGS_NILPADDING); + TrioWriteNumber(self->data, + number, + flags, + POINTER_WIDTH, + NO_PRECISION, + BASE_HEX); + } +} + +/** @} End of UserDefined documentation module */ + +/************************************************************************* + * + * LOCALES + * + ************************************************************************/ + +/************************************************************************* + * trio_locale_set_decimal_point + * + * Decimal point can only be one character. The input argument is a + * string to enable multibyte characters. At most MB_LEN_MAX characters + * will be used. + */ +TRIO_PUBLIC void +trio_locale_set_decimal_point +TRIO_ARGS1((decimalPoint), + char *decimalPoint) +{ +#if defined(USE_LOCALE) + if (NULL == internalLocaleValues) + { + TrioSetLocale(); + } +#endif + internalDecimalPointLength = trio_length(decimalPoint); + if (internalDecimalPointLength == 1) + { + internalDecimalPoint = *decimalPoint; + } + else + { + internalDecimalPoint = NIL; + trio_copy_max(internalDecimalPointString, + sizeof(internalDecimalPointString), + decimalPoint); + } +} + +/************************************************************************* + * trio_locale_set_thousand_separator + * + * See trio_locale_set_decimal_point + */ +TRIO_PUBLIC void +trio_locale_set_thousand_separator +TRIO_ARGS1((thousandSeparator), + char *thousandSeparator) +{ +#if defined(USE_LOCALE) + if (NULL == internalLocaleValues) + { + TrioSetLocale(); + } +#endif + trio_copy_max(internalThousandSeparator, + sizeof(internalThousandSeparator), + thousandSeparator); + internalThousandSeparatorLength = trio_length(internalThousandSeparator); +} + +/************************************************************************* + * trio_locale_set_grouping + * + * Array of bytes. Reversed order. + * + * CHAR_MAX : No further grouping + * 0 : Repeat last group for the remaining digits (not necessary + * as C strings are zero-terminated) + * n : Set current group to n + * + * Same order as the grouping attribute in LC_NUMERIC. + */ +TRIO_PUBLIC void +trio_locale_set_grouping +TRIO_ARGS1((grouping), + char *grouping) +{ +#if defined(USE_LOCALE) + if (NULL == internalLocaleValues) + { + TrioSetLocale(); + } +#endif + trio_copy_max(internalGrouping, + sizeof(internalGrouping), + grouping); +} + + +/************************************************************************* + * + * SCANNING + * + ************************************************************************/ + +/************************************************************************* + * TrioSkipWhitespaces + */ +TRIO_PRIVATE int +TrioSkipWhitespaces +TRIO_ARGS1((self), + trio_class_t *self) +{ + int ch; + + ch = self->current; + while (isspace(ch)) + { + self->InStream(self, &ch); + } + return ch; +} + +/************************************************************************* + * TrioGetCollation + */ +#if TRIO_EXTENSION +TRIO_PRIVATE void +TrioGetCollation(TRIO_NOARGS) +{ + int i; + int j; + int k; + char first[2]; + char second[2]; + + /* This is computationally expensive */ + first[1] = NIL; + second[1] = NIL; + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + { + k = 0; + first[0] = (char)i; + for (j = 0; j < MAX_CHARACTER_CLASS; j++) + { + second[0] = (char)j; + if (trio_equal_locale(first, second)) + internalCollationArray[i][k++] = (char)j; + } + internalCollationArray[i][k] = NIL; + } +} +#endif + +/************************************************************************* + * TrioGetCharacterClass + * + * FIXME: + * multibyte + */ +TRIO_PRIVATE int +TrioGetCharacterClass +TRIO_ARGS4((format, indexPointer, flagsPointer, characterclass), + TRIO_CONST char *format, + int *indexPointer, + trio_flags_t *flagsPointer, + int *characterclass) +{ + int index = *indexPointer; + int i; + char ch; + char range_begin; + char range_end; + + *flagsPointer &= ~FLAGS_EXCLUDE; + + if (format[index] == QUALIFIER_CIRCUMFLEX) + { + *flagsPointer |= FLAGS_EXCLUDE; + index++; + } + /* + * If the ungroup character is at the beginning of the scanlist, + * it will be part of the class, and a second ungroup character + * must follow to end the group. + */ + if (format[index] == SPECIFIER_UNGROUP) + { + characterclass[(int)SPECIFIER_UNGROUP]++; + index++; + } + /* + * Minus is used to specify ranges. To include minus in the class, + * it must be at the beginning of the list + */ + if (format[index] == QUALIFIER_MINUS) + { + characterclass[(int)QUALIFIER_MINUS]++; + index++; + } + /* Collect characters */ + for (ch = format[index]; + (ch != SPECIFIER_UNGROUP) && (ch != NIL); + ch = format[++index]) + { + switch (ch) + { + case QUALIFIER_MINUS: /* Scanlist ranges */ + + /* + * Both C99 and UNIX98 describes ranges as implementation- + * defined. + * + * We support the following behaviour (although this may + * change as we become wiser) + * - only increasing ranges, ie. [a-b] but not [b-a] + * - transitive ranges, ie. [a-b-c] == [a-c] + * - trailing minus, ie. [a-] is interpreted as an 'a' + * and a '-' + * - duplicates (although we can easily convert these + * into errors) + */ + range_begin = format[index - 1]; + range_end = format[++index]; + if (range_end == SPECIFIER_UNGROUP) + { + /* Trailing minus is included */ + characterclass[(int)ch]++; + ch = range_end; + break; /* for */ + } + if (range_end == NIL) + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + if (range_begin > range_end) + return TRIO_ERROR_RETURN(TRIO_ERANGE, index); + + for (i = (int)range_begin; i <= (int)range_end; i++) + characterclass[i]++; + + ch = range_end; + break; + +#if TRIO_EXTENSION + + case SPECIFIER_GROUP: + + switch (format[index + 1]) + { + case QUALIFIER_DOT: /* Collating symbol */ + /* + * FIXME: This will be easier to implement when multibyte + * characters have been implemented. Until now, we ignore + * this feature. + */ + for (i = index + 2; ; i++) + { + if (format[i] == NIL) + /* Error in syntax */ + return -1; + else if (format[i] == QUALIFIER_DOT) + break; /* for */ + } + if (format[++i] != SPECIFIER_UNGROUP) + return -1; + + index = i; + break; + + case QUALIFIER_EQUAL: /* Equivalence class expressions */ + { + unsigned int j; + unsigned int k; + + if (internalCollationUnconverted) + { + /* Lazy evaluation of collation array */ + TrioGetCollation(); + internalCollationUnconverted = FALSE; + } + for (i = index + 2; ; i++) + { + if (format[i] == NIL) + /* Error in syntax */ + return -1; + else if (format[i] == QUALIFIER_EQUAL) + break; /* for */ + else + { + /* Mark any equivalent character */ + k = (unsigned int)format[i]; + for (j = 0; internalCollationArray[k][j] != NIL; j++) + characterclass[(int)internalCollationArray[k][j]]++; + } + } + if (format[++i] != SPECIFIER_UNGROUP) + return -1; + + index = i; + } + break; + + case QUALIFIER_COLON: /* Character class expressions */ + + if (trio_equal_max(CLASS_ALNUM, sizeof(CLASS_ALNUM) - 1, + &format[index])) + { + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + if (isalnum(i)) + characterclass[i]++; + index += sizeof(CLASS_ALNUM) - 1; + } + else if (trio_equal_max(CLASS_ALPHA, sizeof(CLASS_ALPHA) - 1, + &format[index])) + { + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + if (isalpha(i)) + characterclass[i]++; + index += sizeof(CLASS_ALPHA) - 1; + } + else if (trio_equal_max(CLASS_CNTRL, sizeof(CLASS_CNTRL) - 1, + &format[index])) + { + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + if (iscntrl(i)) + characterclass[i]++; + index += sizeof(CLASS_CNTRL) - 1; + } + else if (trio_equal_max(CLASS_DIGIT, sizeof(CLASS_DIGIT) - 1, + &format[index])) + { + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + if (isdigit(i)) + characterclass[i]++; + index += sizeof(CLASS_DIGIT) - 1; + } + else if (trio_equal_max(CLASS_GRAPH, sizeof(CLASS_GRAPH) - 1, + &format[index])) + { + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + if (isgraph(i)) + characterclass[i]++; + index += sizeof(CLASS_GRAPH) - 1; + } + else if (trio_equal_max(CLASS_LOWER, sizeof(CLASS_LOWER) - 1, + &format[index])) + { + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + if (islower(i)) + characterclass[i]++; + index += sizeof(CLASS_LOWER) - 1; + } + else if (trio_equal_max(CLASS_PRINT, sizeof(CLASS_PRINT) - 1, + &format[index])) + { + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + if (isprint(i)) + characterclass[i]++; + index += sizeof(CLASS_PRINT) - 1; + } + else if (trio_equal_max(CLASS_PUNCT, sizeof(CLASS_PUNCT) - 1, + &format[index])) + { + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + if (ispunct(i)) + characterclass[i]++; + index += sizeof(CLASS_PUNCT) - 1; + } + else if (trio_equal_max(CLASS_SPACE, sizeof(CLASS_SPACE) - 1, + &format[index])) + { + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + if (isspace(i)) + characterclass[i]++; + index += sizeof(CLASS_SPACE) - 1; + } + else if (trio_equal_max(CLASS_UPPER, sizeof(CLASS_UPPER) - 1, + &format[index])) + { + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + if (isupper(i)) + characterclass[i]++; + index += sizeof(CLASS_UPPER) - 1; + } + else if (trio_equal_max(CLASS_XDIGIT, sizeof(CLASS_XDIGIT) - 1, + &format[index])) + { + for (i = 0; i < MAX_CHARACTER_CLASS; i++) + if (isxdigit(i)) + characterclass[i]++; + index += sizeof(CLASS_XDIGIT) - 1; + } + else + { + characterclass[(int)ch]++; + } + break; + + default: + characterclass[(int)ch]++; + break; + } + break; + +#endif /* TRIO_EXTENSION */ + + default: + characterclass[(int)ch]++; + break; + } + } + return 0; +} + +/************************************************************************* + * TrioReadNumber + * + * We implement our own number conversion in preference of strtol and + * strtoul, because we must handle 'long long' and thousand separators. + */ +TRIO_PRIVATE BOOLEAN_T +TrioReadNumber +TRIO_ARGS5((self, target, flags, width, base), + trio_class_t *self, + trio_uintmax_t *target, + trio_flags_t flags, + int width, + int base) +{ + trio_uintmax_t number = 0; + int digit; + int count; + BOOLEAN_T isNegative = FALSE; + BOOLEAN_T gotNumber = FALSE; + int j; + + assert(VALID(self)); + assert(VALID(self->InStream)); + assert((base >= MIN_BASE && base <= MAX_BASE) || (base == NO_BASE)); + + if (internalDigitsUnconverted) + { + /* Lazy evaluation of digits array */ + memset(internalDigitArray, -1, sizeof(internalDigitArray)); + for (j = 0; j < (int)sizeof(internalDigitsLower) - 1; j++) + { + internalDigitArray[(int)internalDigitsLower[j]] = j; + internalDigitArray[(int)internalDigitsUpper[j]] = j; + } + internalDigitsUnconverted = FALSE; + } + + TrioSkipWhitespaces(self); + + if (!(flags & FLAGS_UNSIGNED)) + { + /* Leading sign */ + if (self->current == '+') + { + self->InStream(self, NULL); + } + else if (self->current == '-') + { + self->InStream(self, NULL); + isNegative = TRUE; + } + } + + count = self->processed; + + if (flags & FLAGS_ALTERNATIVE) + { + switch (base) + { + case NO_BASE: + case BASE_OCTAL: + case BASE_HEX: + case BASE_BINARY: + if (self->current == '0') + { + self->InStream(self, NULL); + if (self->current) + { + if ((base == BASE_HEX) && + (trio_to_upper(self->current) == 'X')) + { + self->InStream(self, NULL); + } + else if ((base == BASE_BINARY) && + (trio_to_upper(self->current) == 'B')) + { + self->InStream(self, NULL); + } + } + } + else + return FALSE; + break; + default: + break; + } + } + + while (((width == NO_WIDTH) || (self->processed - count < width)) && + (! ((self->current == EOF) || isspace(self->current)))) + { + if (isascii(self->current)) + { + digit = internalDigitArray[self->current]; + /* Abort if digit is not allowed in the specified base */ + if ((digit == -1) || (digit >= base)) + break; + } + else if (flags & FLAGS_QUOTE) + { + /* Compare with thousands separator */ + for (j = 0; internalThousandSeparator[j] && self->current; j++) + { + if (internalThousandSeparator[j] != self->current) + break; + + self->InStream(self, NULL); + } + if (internalThousandSeparator[j]) + break; /* Mismatch */ + else + continue; /* Match */ + } + else + break; + + number *= base; + number += digit; + gotNumber = TRUE; /* we need at least one digit */ + + self->InStream(self, NULL); + } + + /* Was anything read at all? */ + if (!gotNumber) + return FALSE; + + if (target) + *target = (isNegative) ? -((trio_intmax_t)number) : number; + return TRUE; +} + +/************************************************************************* + * TrioReadChar + */ +TRIO_PRIVATE int +TrioReadChar +TRIO_ARGS4((self, target, flags, width), + trio_class_t *self, + char *target, + trio_flags_t flags, + int width) +{ + int i; + char ch; + trio_uintmax_t number; + + assert(VALID(self)); + assert(VALID(self->InStream)); + + for (i = 0; + (self->current != EOF) && (i < width); + i++) + { + ch = (char)self->current; + self->InStream(self, NULL); + if ((flags & FLAGS_ALTERNATIVE) && (ch == CHAR_BACKSLASH)) + { + switch (self->current) + { + case '\\': ch = '\\'; break; + case 'a': ch = '\007'; break; + case 'b': ch = '\b'; break; + case 'f': ch = '\f'; break; + case 'n': ch = '\n'; break; + case 'r': ch = '\r'; break; + case 't': ch = '\t'; break; + case 'v': ch = '\v'; break; + default: + if (isdigit(self->current)) + { + /* Read octal number */ + if (!TrioReadNumber(self, &number, 0, 3, BASE_OCTAL)) + return 0; + ch = (char)number; + } + else if (trio_to_upper(self->current) == 'X') + { + /* Read hexadecimal number */ + self->InStream(self, NULL); + if (!TrioReadNumber(self, &number, 0, 2, BASE_HEX)) + return 0; + ch = (char)number; + } + else + { + ch = (char)self->current; + } + break; + } + } + + if (target) + target[i] = ch; + } + return i + 1; +} + +/************************************************************************* + * TrioReadString + */ +TRIO_PRIVATE BOOLEAN_T +TrioReadString +TRIO_ARGS4((self, target, flags, width), + trio_class_t *self, + char *target, + trio_flags_t flags, + int width) +{ + int i; + + assert(VALID(self)); + assert(VALID(self->InStream)); + + TrioSkipWhitespaces(self); + + /* + * Continue until end of string is reached, a whitespace is encountered, + * or width is exceeded + */ + for (i = 0; + ((width == NO_WIDTH) || (i < width)) && + (! ((self->current == EOF) || isspace(self->current))); + i++) + { + if (TrioReadChar(self, (target ? &target[i] : 0), flags, 1) == 0) + break; /* for */ + } + if (target) + target[i] = NIL; + return TRUE; +} + +/************************************************************************* + * TrioReadWideChar + */ +#if TRIO_WIDECHAR +TRIO_PRIVATE int +TrioReadWideChar +TRIO_ARGS4((self, target, flags, width), + trio_class_t *self, + trio_wchar_t *target, + trio_flags_t flags, + int width) +{ + int i; + int j; + int size; + int amount = 0; + trio_wchar_t wch; + char buffer[MB_LEN_MAX + 1]; + + assert(VALID(self)); + assert(VALID(self->InStream)); + + for (i = 0; + (self->current != EOF) && (i < width); + i++) + { + if (isascii(self->current)) + { + if (TrioReadChar(self, buffer, flags, 1) == 0) + return 0; + buffer[1] = NIL; + } + else + { + /* + * Collect a multibyte character, by enlarging buffer until + * it contains a fully legal multibyte character, or the + * buffer is full. + */ + j = 0; + do + { + buffer[j++] = (char)self->current; + buffer[j] = NIL; + self->InStream(self, NULL); + } + while ((j < (int)sizeof(buffer)) && (mblen(buffer, (size_t)j) != j)); + } + if (target) + { + size = mbtowc(&wch, buffer, sizeof(buffer)); + if (size > 0) + target[i] = wch; + } + amount += size; + self->InStream(self, NULL); + } + return amount; +} +#endif /* TRIO_WIDECHAR */ + +/************************************************************************* + * TrioReadWideString + */ +#if TRIO_WIDECHAR +TRIO_PRIVATE BOOLEAN_T +TrioReadWideString +TRIO_ARGS4((self, target, flags, width), + trio_class_t *self, + trio_wchar_t *target, + trio_flags_t flags, + int width) +{ + int i; + int size; + + assert(VALID(self)); + assert(VALID(self->InStream)); + + TrioSkipWhitespaces(self); + +#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE) + (void)mblen(NULL, 0); +#endif + + /* + * Continue until end of string is reached, a whitespace is encountered, + * or width is exceeded + */ + for (i = 0; + ((width == NO_WIDTH) || (i < width)) && + (! ((self->current == EOF) || isspace(self->current))); + ) + { + size = TrioReadWideChar(self, &target[i], flags, 1); + if (size == 0) + break; /* for */ + + i += size; + } + if (target) + target[i] = WCONST('\0'); + return TRUE; +} +#endif /* TRIO_WIDECHAR */ + +/************************************************************************* + * TrioReadGroup + * + * FIXME: characterclass does not work with multibyte characters + */ +TRIO_PRIVATE BOOLEAN_T +TrioReadGroup +TRIO_ARGS5((self, target, characterclass, flags, width), + trio_class_t *self, + char *target, + int *characterclass, + trio_flags_t flags, + int width) +{ + int ch; + int i; + + assert(VALID(self)); + assert(VALID(self->InStream)); + + ch = self->current; + for (i = 0; + ((width == NO_WIDTH) || (i < width)) && + (! ((ch == EOF) || + (((flags & FLAGS_EXCLUDE) != 0) ^ (characterclass[ch] == 0)))); + i++) + { + if (target) + target[i] = (char)ch; + self->InStream(self, &ch); + } + + if (target) + target[i] = NIL; + return TRUE; +} + +/************************************************************************* + * TrioReadDouble + * + * FIXME: + * add long double + * handle base + */ +TRIO_PRIVATE BOOLEAN_T +TrioReadDouble +TRIO_ARGS4((self, target, flags, width), + trio_class_t *self, + trio_pointer_t target, + trio_flags_t flags, + int width) +{ + int ch; + char doubleString[512]; + int index = 0; + int start; + int j; + BOOLEAN_T isHex = FALSE; + + doubleString[0] = 0; + + if ((width == NO_WIDTH) || (width > (int)sizeof(doubleString) - 1)) + width = sizeof(doubleString) - 1; + + TrioSkipWhitespaces(self); + + /* + * Read entire double number from stream. trio_to_double requires + * a string as input, but InStream can be anything, so we have to + * collect all characters. + */ + ch = self->current; + if ((ch == '+') || (ch == '-')) + { + doubleString[index++] = (char)ch; + self->InStream(self, &ch); + width--; + } + + start = index; + switch (ch) + { + case 'n': + case 'N': + /* Not-a-number */ + if (index != 0) + break; + /* FALLTHROUGH */ + case 'i': + case 'I': + /* Infinity */ + while (isalpha(ch) && (index - start < width)) + { + doubleString[index++] = (char)ch; + self->InStream(self, &ch); + } + doubleString[index] = NIL; + + /* Case insensitive string comparison */ + if (trio_equal(&doubleString[start], INFINITE_UPPER) || + trio_equal(&doubleString[start], LONG_INFINITE_UPPER)) + { + if (flags & FLAGS_LONGDOUBLE) + { + if ((start == 1) && (doubleString[0] == '-')) + { + *((trio_long_double_t *)target) = trio_ninf(); + } + else + { + *((trio_long_double_t *)target) = trio_pinf(); + } + } + else + { + if ((start == 1) && (doubleString[0] == '-')) + { + *((double *)target) = trio_ninf(); + } + else + { + *((double *)target) = trio_pinf(); + } + } + return TRUE; + } + if (trio_equal(doubleString, NAN_UPPER)) + { + /* NaN must not have a preceeding + nor - */ + if (flags & FLAGS_LONGDOUBLE) + { + *((trio_long_double_t *)target) = trio_nan(); + } + else + { + *((double *)target) = trio_nan(); + } + return TRUE; + } + return FALSE; + + case '0': + doubleString[index++] = (char)ch; + self->InStream(self, &ch); + if (trio_to_upper(ch) == 'X') + { + isHex = TRUE; + doubleString[index++] = (char)ch; + self->InStream(self, &ch); + } + break; + + default: + break; + } + + while ((ch != EOF) && (index - start < width)) + { + /* Integer part */ + if (isHex ? isxdigit(ch) : isdigit(ch)) + { + doubleString[index++] = (char)ch; + self->InStream(self, &ch); + } + else if (flags & FLAGS_QUOTE) + { + /* Compare with thousands separator */ + for (j = 0; internalThousandSeparator[j] && self->current; j++) + { + if (internalThousandSeparator[j] != self->current) + break; + + self->InStream(self, &ch); + } + if (internalThousandSeparator[j]) + break; /* Mismatch */ + else + continue; /* Match */ + } + else + break; /* while */ + } + if (ch == '.') + { + /* Decimal part */ + doubleString[index++] = (char)ch; + self->InStream(self, &ch); + while ((isHex ? isxdigit(ch) : isdigit(ch)) && + (index - start < width)) + { + doubleString[index++] = (char)ch; + self->InStream(self, &ch); + } + if (isHex ? (trio_to_upper(ch) == 'P') : (trio_to_upper(ch) == 'E')) + { + /* Exponent */ + doubleString[index++] = (char)ch; + self->InStream(self, &ch); + if ((ch == '+') || (ch == '-')) + { + doubleString[index++] = (char)ch; + self->InStream(self, &ch); + } + while (isdigit(ch) && (index - start < width)) + { + doubleString[index++] = (char)ch; + self->InStream(self, &ch); + } + } + } + + if ((index == start) || (*doubleString == NIL)) + return FALSE; + + doubleString[index] = 0; + + if (flags & FLAGS_LONGDOUBLE) + { + *((trio_long_double_t *)target) = trio_to_long_double(doubleString, NULL); + } + else + { + *((double *)target) = trio_to_double(doubleString, NULL); + } + return TRUE; +} + +/************************************************************************* + * TrioReadPointer + */ +TRIO_PRIVATE BOOLEAN_T +TrioReadPointer +TRIO_ARGS3((self, target, flags), + trio_class_t *self, + trio_pointer_t *target, + trio_flags_t flags) +{ + trio_uintmax_t number; + char buffer[sizeof(internalNullString)]; + + flags |= (FLAGS_UNSIGNED | FLAGS_ALTERNATIVE | FLAGS_NILPADDING); + + if (TrioReadNumber(self, + &number, + flags, + POINTER_WIDTH, + BASE_HEX)) + { + /* + * The strange assignment of number is a workaround for a compiler + * warning + */ + if (target) + *target = (char *)0 + number; + return TRUE; + } + else if (TrioReadString(self, + (flags & FLAGS_IGNORE) + ? NULL + : buffer, + 0, + sizeof(internalNullString) - 1)) + { + if (trio_equal_case(buffer, internalNullString)) + { + if (target) + *target = NULL; + return TRUE; + } + } + return FALSE; +} + +/************************************************************************* + * TrioScanProcess + */ +TRIO_PRIVATE int +TrioScanProcess +TRIO_ARGS3((data, format, parameters), + trio_class_t *data, + TRIO_CONST char *format, + trio_parameter_t *parameters) +{ +#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE) + int charlen; + int cnt; +#endif + int assignment; + int ch; + int index; /* Index of format string */ + int i; /* Index of current parameter */ + trio_flags_t flags; + int width; + int base; + trio_pointer_t pointer; + + assignment = 0; + i = 0; + index = 0; + data->InStream(data, &ch); + +#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE) + (void)mblen(NULL, 0); +#endif + + while (format[index]) + { +#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE) + if (! isascii(format[index])) + { + charlen = mblen(&format[index], MB_LEN_MAX); + if (charlen != -1) + { + /* Compare multibyte characters in format string */ + for (cnt = 0; cnt < charlen - 1; cnt++) + { + if (ch != format[index + cnt]) + { + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + } + data->InStream(data, &ch); + } + continue; /* while characters left in formatting string */ + } + } +#endif /* TRIO_COMPILER_SUPPORTS_MULTIBYTE */ + + if ((EOF == ch) && (parameters[i].type != FORMAT_COUNT)) + { + return (assignment > 0) ? assignment : EOF; + } + + if (CHAR_IDENTIFIER == format[index]) + { + if (CHAR_IDENTIFIER == format[index + 1]) + { + /* Two % in format matches one % in input stream */ + if (CHAR_IDENTIFIER == ch) + { + data->InStream(data, &ch); + index += 2; + continue; /* while format chars left */ + } + else + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + } + + /* Skip the parameter entries */ + while (parameters[i].type == FORMAT_PARAMETER) + i++; + + flags = parameters[i].flags; + /* Find width */ + width = parameters[i].width; + if (flags & FLAGS_WIDTH_PARAMETER) + { + /* Get width from parameter list */ + width = (int)parameters[width].data.number.as_signed; + } + /* Find base */ + base = parameters[i].base; + if (flags & FLAGS_BASE_PARAMETER) + { + /* Get base from parameter list */ + base = (int)parameters[base].data.number.as_signed; + } + + switch (parameters[i].type) + { + case FORMAT_INT: + { + trio_uintmax_t number; + + if (0 == base) + base = BASE_DECIMAL; + + if (!TrioReadNumber(data, + &number, + flags, + width, + base)) + return assignment; + + if (!(flags & FLAGS_IGNORE)) + { + assignment++; + + pointer = parameters[i].data.pointer; +#if defined(QUALIFIER_SIZE_T) || defined(QUALIFIER_SIZE_T_UPPER) + if (flags & FLAGS_SIZE_T) + *(size_t *)pointer = (size_t)number; + else +#endif +#if defined(QUALIFIER_PTRDIFF_T) + if (flags & FLAGS_PTRDIFF_T) + *(ptrdiff_t *)pointer = (ptrdiff_t)number; + else +#endif +#if defined(QUALIFIER_INTMAX_T) + if (flags & FLAGS_INTMAX_T) + *(trio_intmax_t *)pointer = (trio_intmax_t)number; + else +#endif + if (flags & FLAGS_QUAD) + *(trio_ulonglong_t *)pointer = (trio_ulonglong_t)number; + else if (flags & FLAGS_LONG) + *(long int *)pointer = (long int)number; + else if (flags & FLAGS_SHORT) + *(short int *)pointer = (short int)number; + else + *(int *)pointer = (int)number; + } + } + break; /* FORMAT_INT */ + + case FORMAT_STRING: +#if TRIO_WIDECHAR + if (flags & FLAGS_WIDECHAR) + { + if (!TrioReadWideString(data, + (flags & FLAGS_IGNORE) + ? NULL + : parameters[i].data.wstring, + flags, + width)) + return assignment; + } + else +#endif + { + if (!TrioReadString(data, + (flags & FLAGS_IGNORE) + ? NULL + : parameters[i].data.string, + flags, + width)) + return assignment; + } + if (!(flags & FLAGS_IGNORE)) + assignment++; + break; /* FORMAT_STRING */ + + case FORMAT_DOUBLE: + { + trio_pointer_t pointer; + + if (flags & FLAGS_IGNORE) + { + pointer = NULL; + } + else + { + pointer = (flags & FLAGS_LONGDOUBLE) + ? (trio_pointer_t)parameters[i].data.longdoublePointer + : (trio_pointer_t)parameters[i].data.doublePointer; + } + if (!TrioReadDouble(data, pointer, flags, width)) + { + return assignment; + } + if (!(flags & FLAGS_IGNORE)) + { + assignment++; + } + break; /* FORMAT_DOUBLE */ + } + case FORMAT_GROUP: + { + int characterclass[MAX_CHARACTER_CLASS + 1]; + int rc; + + /* Skip over modifiers */ + while (format[index] != SPECIFIER_GROUP) + { + index++; + } + /* Skip over group specifier */ + index++; + + memset(characterclass, 0, sizeof(characterclass)); + rc = TrioGetCharacterClass(format, + &index, + &flags, + characterclass); + if (rc < 0) + return rc; + + if (!TrioReadGroup(data, + (flags & FLAGS_IGNORE) + ? NULL + : parameters[i].data.string, + characterclass, + flags, + parameters[i].width)) + return assignment; + if (!(flags & FLAGS_IGNORE)) + assignment++; + } + break; /* FORMAT_GROUP */ + + case FORMAT_COUNT: + pointer = parameters[i].data.pointer; + if (NULL != pointer) + { + int count = data->committed; + if (ch != EOF) + count--; /* a character is read, but is not consumed yet */ +#if defined(QUALIFIER_SIZE_T) || defined(QUALIFIER_SIZE_T_UPPER) + if (flags & FLAGS_SIZE_T) + *(size_t *)pointer = (size_t)count; + else +#endif +#if defined(QUALIFIER_PTRDIFF_T) + if (flags & FLAGS_PTRDIFF_T) + *(ptrdiff_t *)pointer = (ptrdiff_t)count; + else +#endif +#if defined(QUALIFIER_INTMAX_T) + if (flags & FLAGS_INTMAX_T) + *(trio_intmax_t *)pointer = (trio_intmax_t)count; + else +#endif + if (flags & FLAGS_QUAD) + { + *(trio_ulonglong_t *)pointer = (trio_ulonglong_t)count; + } + else if (flags & FLAGS_LONG) + { + *(long int *)pointer = (long int)count; + } + else if (flags & FLAGS_SHORT) + { + *(short int *)pointer = (short int)count; + } + else + { + *(int *)pointer = (int)count; + } + } + break; /* FORMAT_COUNT */ + + case FORMAT_CHAR: +#if TRIO_WIDECHAR + if (flags & FLAGS_WIDECHAR) + { + if (TrioReadWideChar(data, + (flags & FLAGS_IGNORE) + ? NULL + : parameters[i].data.wstring, + flags, + (width == NO_WIDTH) ? 1 : width) == 0) + return assignment; + } + else +#endif + { + if (TrioReadChar(data, + (flags & FLAGS_IGNORE) + ? NULL + : parameters[i].data.string, + flags, + (width == NO_WIDTH) ? 1 : width) == 0) + return assignment; + } + if (!(flags & FLAGS_IGNORE)) + assignment++; + break; /* FORMAT_CHAR */ + + case FORMAT_POINTER: + if (!TrioReadPointer(data, + (flags & FLAGS_IGNORE) + ? NULL + : (trio_pointer_t *)parameters[i].data.pointer, + flags)) + return assignment; + if (!(flags & FLAGS_IGNORE)) + assignment++; + break; /* FORMAT_POINTER */ + + case FORMAT_PARAMETER: + break; /* FORMAT_PARAMETER */ + + default: + return TRIO_ERROR_RETURN(TRIO_EINVAL, index); + } + ch = data->current; + index = parameters[i].indexAfterSpecifier; + i++; + } + else /* Not an % identifier */ + { + if (isspace((int)format[index])) + { + /* Whitespaces may match any amount of whitespaces */ + ch = TrioSkipWhitespaces(data); + } + else if (ch == format[index]) + { + data->InStream(data, &ch); + } + else + return assignment; + + index++; + } + } + return assignment; +} + +/************************************************************************* + * TrioScan + */ +TRIO_PRIVATE int +TrioScan +TRIO_ARGS6((source, sourceSize, InStream, format, arglist, argarray), + trio_pointer_t source, + size_t sourceSize, + void (*InStream) TRIO_PROTO((trio_class_t *, int *)), + TRIO_CONST char *format, + va_list *arglist, + trio_pointer_t *argarray) +{ + int status; + trio_parameter_t parameters[MAX_PARAMETERS]; + trio_class_t data; + + assert(VALID(InStream)); + assert(VALID(format)); + + memset(&data, 0, sizeof(data)); + data.InStream = InStream; + data.location = (trio_pointer_t)source; + data.max = sourceSize; + data.error = 0; + +#if defined(USE_LOCALE) + if (NULL == internalLocaleValues) + { + TrioSetLocale(); + } +#endif + + status = TrioParse(TYPE_SCAN, format, parameters, arglist, argarray); + if (status < 0) + return status; + + status = TrioScanProcess(&data, format, parameters); + if (data.error != 0) + { + status = data.error; + } + return status; +} + +/************************************************************************* + * TrioInStreamFile + */ +TRIO_PRIVATE void +TrioInStreamFile +TRIO_ARGS2((self, intPointer), + trio_class_t *self, + int *intPointer) +{ + FILE *file = (FILE *)self->location; + + assert(VALID(self)); + assert(VALID(file)); + + self->current = fgetc(file); + if (self->current == EOF) + { + self->error = (ferror(file)) + ? TRIO_ERROR_RETURN(TRIO_ERRNO, 0) + : TRIO_ERROR_RETURN(TRIO_EOF, 0); + } + else + { + self->processed++; + self->committed++; + } + + if (VALID(intPointer)) + { + *intPointer = self->current; + } +} + +/************************************************************************* + * TrioInStreamFileDescriptor + */ +TRIO_PRIVATE void +TrioInStreamFileDescriptor +TRIO_ARGS2((self, intPointer), + trio_class_t *self, + int *intPointer) +{ + int fd = *((int *)self->location); + int size; + unsigned char input; + + assert(VALID(self)); + + size = read(fd, &input, sizeof(char)); + if (size == -1) + { + self->error = TRIO_ERROR_RETURN(TRIO_ERRNO, 0); + self->current = EOF; + } + else + { + self->current = (size == 0) ? EOF : input; + } + if (self->current != EOF) + { + self->committed++; + self->processed++; + } + + if (VALID(intPointer)) + { + *intPointer = self->current; + } +} + +/************************************************************************* + * TrioInStreamCustom + */ +TRIO_PRIVATE void +TrioInStreamCustom +TRIO_ARGS2((self, intPointer), + trio_class_t *self, + int *intPointer) +{ + trio_custom_t *data; + + assert(VALID(self)); + assert(VALID(self->location)); + + data = (trio_custom_t *)self->location; + + self->current = (data->stream.in == NULL) + ? NIL + : (data->stream.in)(data->closure); + + if (self->current == NIL) + { + self->current = EOF; + } + else + { + self->processed++; + self->committed++; + } + + if (VALID(intPointer)) + { + *intPointer = self->current; + } +} + +/************************************************************************* + * TrioInStreamString + */ +TRIO_PRIVATE void +TrioInStreamString +TRIO_ARGS2((self, intPointer), + trio_class_t *self, + int *intPointer) +{ + unsigned char **buffer; + + assert(VALID(self)); + assert(VALID(self->location)); + + buffer = (unsigned char **)self->location; + self->current = (*buffer)[0]; + if (self->current == NIL) + { + self->current = EOF; + } + else + { + (*buffer)++; + self->processed++; + self->committed++; + } + + if (VALID(intPointer)) + { + *intPointer = self->current; + } +} + +/************************************************************************* + * + * Formatted scanning functions + * + ************************************************************************/ + +#if defined(TRIO_DOCUMENTATION) +# include "doc/doc_scanf.h" +#endif +/** @addtogroup Scanf + @{ +*/ + +/************************************************************************* + * scanf + */ + +/** + Scan characters from standard input stream. + + @param format Formatting string. + @param ... Arguments. + @return Number of scanned characters. + */ +TRIO_PUBLIC int +trio_scanf +TRIO_VARGS2((format, va_alist), + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + + assert(VALID(format)); + + TRIO_VA_START(args, format); + status = TrioScan((trio_pointer_t)stdin, 0, + TrioInStreamFile, + format, &args, NULL); + TRIO_VA_END(args); + return status; +} + +TRIO_PUBLIC int +trio_vscanf +TRIO_ARGS2((format, args), + TRIO_CONST char *format, + va_list args) +{ + assert(VALID(format)); + + return TrioScan((trio_pointer_t)stdin, 0, + TrioInStreamFile, + format, &args, NULL); +} + +TRIO_PUBLIC int +trio_scanfv +TRIO_ARGS2((format, args), + TRIO_CONST char *format, + trio_pointer_t *args) +{ + assert(VALID(format)); + + return TrioScan((trio_pointer_t)stdin, 0, + TrioInStreamFile, + format, NULL, args); +} + +/************************************************************************* + * fscanf + */ +TRIO_PUBLIC int +trio_fscanf +TRIO_VARGS3((file, format, va_alist), + FILE *file, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + + assert(VALID(file)); + assert(VALID(format)); + + TRIO_VA_START(args, format); + status = TrioScan((trio_pointer_t)file, 0, + TrioInStreamFile, + format, &args, NULL); + TRIO_VA_END(args); + return status; +} + +TRIO_PUBLIC int +trio_vfscanf +TRIO_ARGS3((file, format, args), + FILE *file, + TRIO_CONST char *format, + va_list args) +{ + assert(VALID(file)); + assert(VALID(format)); + + return TrioScan((trio_pointer_t)file, 0, + TrioInStreamFile, + format, &args, NULL); +} + +TRIO_PUBLIC int +trio_fscanfv +TRIO_ARGS3((file, format, args), + FILE *file, + TRIO_CONST char *format, + trio_pointer_t *args) +{ + assert(VALID(file)); + assert(VALID(format)); + + return TrioScan((trio_pointer_t)file, 0, + TrioInStreamFile, + format, NULL, args); +} + +/************************************************************************* + * dscanf + */ +TRIO_PUBLIC int +trio_dscanf +TRIO_VARGS3((fd, format, va_alist), + int fd, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + + assert(VALID(format)); + + TRIO_VA_START(args, format); + status = TrioScan((trio_pointer_t)&fd, 0, + TrioInStreamFileDescriptor, + format, &args, NULL); + TRIO_VA_END(args); + return status; +} + +TRIO_PUBLIC int +trio_vdscanf +TRIO_ARGS3((fd, format, args), + int fd, + TRIO_CONST char *format, + va_list args) +{ + assert(VALID(format)); + + return TrioScan((trio_pointer_t)&fd, 0, + TrioInStreamFileDescriptor, + format, &args, NULL); +} + +TRIO_PUBLIC int +trio_dscanfv +TRIO_ARGS3((fd, format, args), + int fd, + TRIO_CONST char *format, + trio_pointer_t *args) +{ + assert(VALID(format)); + + return TrioScan((trio_pointer_t)&fd, 0, + TrioInStreamFileDescriptor, + format, NULL, args); +} + +/************************************************************************* + * cscanf + */ +TRIO_PUBLIC int +trio_cscanf +TRIO_VARGS4((stream, closure, format, va_alist), + trio_instream_t stream, + trio_pointer_t closure, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + trio_custom_t data; + + assert(VALID(stream)); + assert(VALID(format)); + + TRIO_VA_START(args, format); + data.stream.in = stream; + data.closure = closure; + status = TrioScan(&data, 0, TrioInStreamCustom, format, &args, NULL); + TRIO_VA_END(args); + return status; +} + +TRIO_PUBLIC int +trio_vcscanf +TRIO_ARGS4((stream, closure, format, args), + trio_instream_t stream, + trio_pointer_t closure, + TRIO_CONST char *format, + va_list args) +{ + trio_custom_t data; + + assert(VALID(stream)); + assert(VALID(format)); + + data.stream.in = stream; + data.closure = closure; + return TrioScan(&data, 0, TrioInStreamCustom, format, &args, NULL); +} + +TRIO_PUBLIC int +trio_cscanfv +TRIO_ARGS4((stream, closure, format, args), + trio_instream_t stream, + trio_pointer_t closure, + TRIO_CONST char *format, + trio_pointer_t *args) +{ + trio_custom_t data; + + assert(VALID(stream)); + assert(VALID(format)); + + data.stream.in = stream; + data.closure = closure; + return TrioScan(&data, 0, TrioInStreamCustom, format, NULL, args); +} + +/************************************************************************* + * sscanf + */ +TRIO_PUBLIC int +trio_sscanf +TRIO_VARGS3((buffer, format, va_alist), + TRIO_CONST char *buffer, + TRIO_CONST char *format, + TRIO_VA_DECL) +{ + int status; + va_list args; + + assert(VALID(buffer)); + assert(VALID(format)); + + TRIO_VA_START(args, format); + status = TrioScan((trio_pointer_t)&buffer, 0, + TrioInStreamString, + format, &args, NULL); + TRIO_VA_END(args); + return status; +} + +TRIO_PUBLIC int +trio_vsscanf +TRIO_ARGS3((buffer, format, args), + TRIO_CONST char *buffer, + TRIO_CONST char *format, + va_list args) +{ + assert(VALID(buffer)); + assert(VALID(format)); + + return TrioScan((trio_pointer_t)&buffer, 0, + TrioInStreamString, + format, &args, NULL); +} + +TRIO_PUBLIC int +trio_sscanfv +TRIO_ARGS3((buffer, format, args), + TRIO_CONST char *buffer, + TRIO_CONST char *format, + trio_pointer_t *args) +{ + assert(VALID(buffer)); + assert(VALID(format)); + + return TrioScan((trio_pointer_t)&buffer, 0, + TrioInStreamString, + format, NULL, args); +} + +/** @} End of Scanf documentation module */ + +/************************************************************************* + * trio_strerror + */ +TRIO_PUBLIC TRIO_CONST char * +trio_strerror +TRIO_ARGS1((errorcode), + int errorcode) +{ + /* Textual versions of the error codes */ + switch (TRIO_ERROR_CODE(errorcode)) + { + case TRIO_EOF: + return "End of file"; + case TRIO_EINVAL: + return "Invalid argument"; + case TRIO_ETOOMANY: + return "Too many arguments"; + case TRIO_EDBLREF: + return "Double reference"; + case TRIO_EGAP: + return "Reference gap"; + case TRIO_ENOMEM: + return "Out of memory"; + case TRIO_ERANGE: + return "Invalid range"; + case TRIO_ECUSTOM: + return "Custom error"; + default: + return "Unknown"; + } +} diff --git a/Extras/LibXML/trio.h b/Extras/LibXML/trio.h new file mode 100644 index 000000000..6a3c21a65 --- /dev/null +++ b/Extras/LibXML/trio.h @@ -0,0 +1,216 @@ +/************************************************************************* + * + * $Id: trio.h,v 1.6 2003/04/03 15:28:27 veillard Exp $ + * + * Copyright (C) 1998 Bjorn Reese and Daniel Stenberg. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. + * + ************************************************************************* + * + * http://ctrio.sourceforge.net/ + * + ************************************************************************/ + +#ifndef TRIO_TRIO_H +#define TRIO_TRIO_H + +#if !defined(WITHOUT_TRIO) + +/* + * Use autoconf defines if present. Packages using trio must define + * HAVE_CONFIG_H as a compiler option themselves. + */ +#if defined(HAVE_CONFIG_H) +# include +#endif + +#include "triodef.h" + +#include +#include +#if defined(TRIO_COMPILER_ANCIENT) +# include +#else +# include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Error codes. + * + * Remember to add a textual description to trio_strerror. + */ +enum { + TRIO_EOF = 1, + TRIO_EINVAL = 2, + TRIO_ETOOMANY = 3, + TRIO_EDBLREF = 4, + TRIO_EGAP = 5, + TRIO_ENOMEM = 6, + TRIO_ERANGE = 7, + TRIO_ERRNO = 8, + TRIO_ECUSTOM = 9 +}; + +/* Error macros */ +#define TRIO_ERROR_CODE(x) ((-(x)) & 0x00FF) +#define TRIO_ERROR_POSITION(x) ((-(x)) >> 8) +#define TRIO_ERROR_NAME(x) trio_strerror(x) + +typedef int (*trio_outstream_t) TRIO_PROTO((trio_pointer_t, int)); +typedef int (*trio_instream_t) TRIO_PROTO((trio_pointer_t)); + +TRIO_CONST char *trio_strerror TRIO_PROTO((int)); + +/************************************************************************* + * Print Functions + */ + +int trio_printf TRIO_PROTO((TRIO_CONST char *format, ...)); +int trio_vprintf TRIO_PROTO((TRIO_CONST char *format, va_list args)); +int trio_printfv TRIO_PROTO((TRIO_CONST char *format, void **args)); + +int trio_fprintf TRIO_PROTO((FILE *file, TRIO_CONST char *format, ...)); +int trio_vfprintf TRIO_PROTO((FILE *file, TRIO_CONST char *format, va_list args)); +int trio_fprintfv TRIO_PROTO((FILE *file, TRIO_CONST char *format, void **args)); + +int trio_dprintf TRIO_PROTO((int fd, TRIO_CONST char *format, ...)); +int trio_vdprintf TRIO_PROTO((int fd, TRIO_CONST char *format, va_list args)); +int trio_dprintfv TRIO_PROTO((int fd, TRIO_CONST char *format, void **args)); + +int trio_cprintf TRIO_PROTO((trio_outstream_t stream, trio_pointer_t closure, + TRIO_CONST char *format, ...)); +int trio_vcprintf TRIO_PROTO((trio_outstream_t stream, trio_pointer_t closure, + TRIO_CONST char *format, va_list args)); +int trio_cprintfv TRIO_PROTO((trio_outstream_t stream, trio_pointer_t closure, + TRIO_CONST char *format, void **args)); + +int trio_sprintf TRIO_PROTO((char *buffer, TRIO_CONST char *format, ...)); +int trio_vsprintf TRIO_PROTO((char *buffer, TRIO_CONST char *format, va_list args)); +int trio_sprintfv TRIO_PROTO((char *buffer, TRIO_CONST char *format, void **args)); + +int trio_snprintf TRIO_PROTO((char *buffer, size_t max, TRIO_CONST char *format, ...)); +int trio_vsnprintf TRIO_PROTO((char *buffer, size_t bufferSize, TRIO_CONST char *format, + va_list args)); +int trio_snprintfv TRIO_PROTO((char *buffer, size_t bufferSize, TRIO_CONST char *format, + void **args)); + +int trio_snprintfcat TRIO_PROTO((char *buffer, size_t max, TRIO_CONST char *format, ...)); +int trio_vsnprintfcat TRIO_PROTO((char *buffer, size_t bufferSize, TRIO_CONST char *format, + va_list args)); + +char *trio_aprintf TRIO_PROTO((TRIO_CONST char *format, ...)); +char *trio_vaprintf TRIO_PROTO((TRIO_CONST char *format, va_list args)); + +int trio_asprintf TRIO_PROTO((char **ret, TRIO_CONST char *format, ...)); +int trio_vasprintf TRIO_PROTO((char **ret, TRIO_CONST char *format, va_list args)); + +/************************************************************************* + * Scan Functions + */ +int trio_scanf TRIO_PROTO((TRIO_CONST char *format, ...)); +int trio_vscanf TRIO_PROTO((TRIO_CONST char *format, va_list args)); +int trio_scanfv TRIO_PROTO((TRIO_CONST char *format, void **args)); + +int trio_fscanf TRIO_PROTO((FILE *file, TRIO_CONST char *format, ...)); +int trio_vfscanf TRIO_PROTO((FILE *file, TRIO_CONST char *format, va_list args)); +int trio_fscanfv TRIO_PROTO((FILE *file, TRIO_CONST char *format, void **args)); + +int trio_dscanf TRIO_PROTO((int fd, TRIO_CONST char *format, ...)); +int trio_vdscanf TRIO_PROTO((int fd, TRIO_CONST char *format, va_list args)); +int trio_dscanfv TRIO_PROTO((int fd, TRIO_CONST char *format, void **args)); + +int trio_cscanf TRIO_PROTO((trio_instream_t stream, trio_pointer_t closure, + TRIO_CONST char *format, ...)); +int trio_vcscanf TRIO_PROTO((trio_instream_t stream, trio_pointer_t closure, + TRIO_CONST char *format, va_list args)); +int trio_cscanfv TRIO_PROTO((trio_instream_t stream, trio_pointer_t closure, + TRIO_CONST char *format, void **args)); + +int trio_sscanf TRIO_PROTO((TRIO_CONST char *buffer, TRIO_CONST char *format, ...)); +int trio_vsscanf TRIO_PROTO((TRIO_CONST char *buffer, TRIO_CONST char *format, va_list args)); +int trio_sscanfv TRIO_PROTO((TRIO_CONST char *buffer, TRIO_CONST char *format, void **args)); + +/************************************************************************* + * Locale Functions + */ +void trio_locale_set_decimal_point TRIO_PROTO((char *decimalPoint)); +void trio_locale_set_thousand_separator TRIO_PROTO((char *thousandSeparator)); +void trio_locale_set_grouping TRIO_PROTO((char *grouping)); + +/************************************************************************* + * Renaming + */ +#ifdef TRIO_REPLACE_STDIO +/* Replace the functions */ +#ifndef HAVE_PRINTF +# define printf trio_printf +#endif +#ifndef HAVE_VPRINTF +# define vprintf trio_vprintf +#endif +#ifndef HAVE_FPRINTF +# define fprintf trio_fprintf +#endif +#ifndef HAVE_VFPRINTF +# define vfprintf trio_vfprintf +#endif +#ifndef HAVE_SPRINTF +# define sprintf trio_sprintf +#endif +#ifndef HAVE_VSPRINTF +# define vsprintf trio_vsprintf +#endif +#ifndef HAVE_SNPRINTF +# define snprintf trio_snprintf +#endif +#ifndef HAVE_VSNPRINTF +# define vsnprintf trio_vsnprintf +#endif +#ifndef HAVE_SCANF +# define scanf trio_scanf +#endif +#ifndef HAVE_VSCANF +# define vscanf trio_vscanf +#endif +#ifndef HAVE_FSCANF +# define fscanf trio_fscanf +#endif +#ifndef HAVE_VFSCANF +# define vfscanf trio_vfscanf +#endif +#ifndef HAVE_SSCANF +# define sscanf trio_sscanf +#endif +#ifndef HAVE_VSSCANF +# define vsscanf trio_vsscanf +#endif +/* These aren't stdio functions, but we make them look similar */ +#define dprintf trio_dprintf +#define vdprintf trio_vdprintf +#define aprintf trio_aprintf +#define vaprintf trio_vaprintf +#define asprintf trio_asprintf +#define vasprintf trio_vasprintf +#define dscanf trio_dscanf +#define vdscanf trio_vdscanf +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* WITHOUT_TRIO */ + +#endif /* TRIO_TRIO_H */ diff --git a/Extras/LibXML/triodef.h b/Extras/LibXML/triodef.h new file mode 100644 index 000000000..6c4e151aa --- /dev/null +++ b/Extras/LibXML/triodef.h @@ -0,0 +1,222 @@ +/************************************************************************* + * + * $Id: triodef.h,v 1.8 2006/05/31 13:35:28 veillard Exp $ + * + * Copyright (C) 2001 Bjorn Reese + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. + * + ************************************************************************/ + +#ifndef TRIO_TRIODEF_H +#define TRIO_TRIODEF_H + +/************************************************************************* + * Platform and compiler support detection + */ +#if defined(__GNUC__) +# define TRIO_COMPILER_GCC +#elif defined(__SUNPRO_C) +# define TRIO_COMPILER_SUNPRO +#elif defined(__SUNPRO_CC) +# define TRIO_COMPILER_SUNPRO +# define __SUNPRO_C __SUNPRO_CC +#elif defined(__xlC__) || defined(__IBMC__) || defined(__IBMCPP__) +# define TRIO_COMPILER_XLC +#elif defined(_AIX) && !defined(__GNUC__) +# define TRIO_COMPILER_XLC /* Workaround for old xlc */ +#elif defined(__DECC) || defined(__DECCXX) +# define TRIO_COMPILER_DECC +#elif defined(__osf__) && defined(__LANGUAGE_C__) +# define TRIO_COMPILER_DECC /* Workaround for old DEC C compilers */ +#elif defined(_MSC_VER) +# define TRIO_COMPILER_MSVC +#elif defined(__BORLANDC__) +# define TRIO_COMPILER_BCB +#endif + +#if defined(VMS) || defined(__VMS) +/* + * VMS is placed first to avoid identifying the platform as Unix + * based on the DECC compiler later on. + */ +# define TRIO_PLATFORM_VMS +#elif defined(unix) || defined(__unix) || defined(__unix__) +# define TRIO_PLATFORM_UNIX +#elif defined(TRIO_COMPILER_XLC) || defined(_AIX) +# define TRIO_PLATFORM_UNIX +#elif defined(TRIO_COMPILER_DECC) || defined(__osf___) +# define TRIO_PLATFORM_UNIX +#elif defined(__NetBSD__) +# define TRIO_PLATFORM_UNIX +#elif defined(__Lynx__) +# define TRIO_PLATFORM_UNIX +#elif defined(__QNX__) +# define TRIO_PLATFORM_UNIX +# define TRIO_PLATFORM_QNX +#elif defined(__CYGWIN__) +# define TRIO_PLATFORM_UNIX +#elif defined(AMIGA) && defined(TRIO_COMPILER_GCC) +# define TRIO_PLATFORM_UNIX +#elif defined(TRIO_COMPILER_MSVC) || defined(WIN32) || defined(_WIN32) +# define TRIO_PLATFORM_WIN32 +#elif defined(mpeix) || defined(__mpexl) +# define TRIO_PLATFORM_MPEIX +#endif + +#if defined(_AIX) +# define TRIO_PLATFORM_AIX +#elif defined(__hpux) +# define TRIO_PLATFORM_HPUX +#elif defined(sun) || defined(__sun__) +# if defined(__SVR4) || defined(__svr4__) +# define TRIO_PLATFORM_SOLARIS +# else +# define TRIO_PLATFORM_SUNOS +# endif +#endif + +#if defined(__STDC__) || defined(TRIO_COMPILER_MSVC) || defined(TRIO_COMPILER_BCB) +# define TRIO_COMPILER_SUPPORTS_C89 +# if defined(__STDC_VERSION__) +# define TRIO_COMPILER_SUPPORTS_C90 +# if (__STDC_VERSION__ >= 199409L) +# define TRIO_COMPILER_SUPPORTS_C94 +# endif +# if (__STDC_VERSION__ >= 199901L) +# define TRIO_COMPILER_SUPPORTS_C99 +# endif +# elif defined(TRIO_COMPILER_SUNPRO) +# if (__SUNPRO_C >= 0x420) +# define TRIO_COMPILER_SUPPORTS_C94 +# endif +# endif +#endif + +#if defined(_XOPEN_SOURCE) +# if defined(_XOPEN_SOURCE_EXTENDED) +# define TRIO_COMPILER_SUPPORTS_UNIX95 +# endif +# if (_XOPEN_VERSION >= 500) +# define TRIO_COMPILER_SUPPORTS_UNIX98 +# endif +# if (_XOPEN_VERSION >= 600) +# define TRIO_COMPILER_SUPPORTS_UNIX01 +# endif +#endif + +/************************************************************************* + * Generic defines + */ + +#if !defined(TRIO_PUBLIC) +# define TRIO_PUBLIC +#endif +#if !defined(TRIO_PRIVATE) +# define TRIO_PRIVATE static +#endif + +#if !(defined(TRIO_COMPILER_SUPPORTS_C89) || defined(__cplusplus)) +# define TRIO_COMPILER_ANCIENT +#endif + +#if defined(TRIO_COMPILER_ANCIENT) +# define TRIO_CONST +# define TRIO_VOLATILE +# define TRIO_SIGNED +typedef double trio_long_double_t; +typedef char * trio_pointer_t; +# define TRIO_SUFFIX_LONG(x) x +# define TRIO_PROTO(x) () +# define TRIO_NOARGS +# define TRIO_ARGS1(list,a1) list a1; +# define TRIO_ARGS2(list,a1,a2) list a1; a2; +# define TRIO_ARGS3(list,a1,a2,a3) list a1; a2; a3; +# define TRIO_ARGS4(list,a1,a2,a3,a4) list a1; a2; a3; a4; +# define TRIO_ARGS5(list,a1,a2,a3,a4,a5) list a1; a2; a3; a4; a5; +# define TRIO_ARGS6(list,a1,a2,a3,a4,a5,a6) list a1; a2; a3; a4; a5; a6; +# define TRIO_VARGS2(list,a1,a2) list a1; a2 +# define TRIO_VARGS3(list,a1,a2,a3) list a1; a2; a3 +# define TRIO_VARGS4(list,a1,a2,a3,a4) list a1; a2; a3; a4 +# define TRIO_VARGS5(list,a1,a2,a3,a4,a5) list a1; a2; a3; a4; a5 +# define TRIO_VA_DECL va_dcl +# define TRIO_VA_START(x,y) va_start(x) +# define TRIO_VA_END(x) va_end(x) +#else /* ANSI C */ +# define TRIO_CONST const +# define TRIO_VOLATILE volatile +# define TRIO_SIGNED signed +typedef long double trio_long_double_t; +typedef void * trio_pointer_t; +# define TRIO_SUFFIX_LONG(x) x ## L +# define TRIO_PROTO(x) x +# define TRIO_NOARGS void +# define TRIO_ARGS1(list,a1) (a1) +# define TRIO_ARGS2(list,a1,a2) (a1,a2) +# define TRIO_ARGS3(list,a1,a2,a3) (a1,a2,a3) +# define TRIO_ARGS4(list,a1,a2,a3,a4) (a1,a2,a3,a4) +# define TRIO_ARGS5(list,a1,a2,a3,a4,a5) (a1,a2,a3,a4,a5) +# define TRIO_ARGS6(list,a1,a2,a3,a4,a5,a6) (a1,a2,a3,a4,a5,a6) +# define TRIO_VARGS2 TRIO_ARGS2 +# define TRIO_VARGS3 TRIO_ARGS3 +# define TRIO_VARGS4 TRIO_ARGS4 +# define TRIO_VARGS5 TRIO_ARGS5 +# define TRIO_VA_DECL ... +# define TRIO_VA_START(x,y) va_start(x,y) +# define TRIO_VA_END(x) va_end(x) +#endif + +#if defined(TRIO_COMPILER_SUPPORTS_C99) || defined(__cplusplus) +# define TRIO_INLINE inline +#elif defined(TRIO_COMPILER_GCC) +# define TRIO_INLINE __inline__ +#elif defined(TRIO_COMPILER_MSVC) +# define TRIO_INLINE _inline +#elif defined(TRIO_COMPILER_BCB) +# define TRIO_INLINE __inline +#else +# define TRIO_INLINE +#endif + +/************************************************************************* + * Workarounds + */ + +#if defined(TRIO_PLATFORM_VMS) +/* + * Computations done with constants at compile time can trigger these + * even when compiling with IEEE enabled. + */ +# pragma message disable (UNDERFLOW, FLOATOVERFL) + +# if (__CRTL_VER < 80000000) +/* + * Although the compiler supports C99 language constructs, the C + * run-time library does not contain all C99 functions. + * + * This was the case for 70300022. Update the 80000000 value when + * it has been accurately determined what version of the library + * supports C99. + */ +# if defined(TRIO_COMPILER_SUPPORTS_C99) +# undef TRIO_COMPILER_SUPPORTS_C99 +# endif +# endif +#endif + +/* + * Not all preprocessors supports the LL token. + */ +#if defined(TRIO_COMPILER_BCB) +#else +# define TRIO_COMPILER_SUPPORTS_LL +#endif + +#endif /* TRIO_TRIODEF_H */ diff --git a/Extras/LibXML/trionan.c b/Extras/LibXML/trionan.c new file mode 100644 index 000000000..5248eaf64 --- /dev/null +++ b/Extras/LibXML/trionan.c @@ -0,0 +1,910 @@ +/************************************************************************* + * + * $Id: trionan.c,v 1.14 2003/10/15 08:17:58 veillard Exp $ + * + * Copyright (C) 2001 Bjorn Reese + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. + * + ************************************************************************ + * + * Functions to handle special quantities in floating-point numbers + * (that is, NaNs and infinity). They provide the capability to detect + * and fabricate special quantities. + * + * Although written to be as portable as possible, it can never be + * guaranteed to work on all platforms, as not all hardware supports + * special quantities. + * + * The approach used here (approximately) is to: + * + * 1. Use C99 functionality when available. + * 2. Use IEEE 754 bit-patterns if possible. + * 3. Use platform-specific techniques. + * + ************************************************************************/ + +/* + * TODO: + * o Put all the magic into trio_fpclassify_and_signbit(), and use this from + * trio_isnan() etc. + */ + +/************************************************************************* + * Include files + */ +#include "triodef.h" +#include "trionan.h" + +#include +#include +#include +#include +#if defined(TRIO_PLATFORM_UNIX) +# include +#endif +#if defined(TRIO_COMPILER_DECC) +# if defined(__linux__) +# include +# else +# include +# endif +#endif +#include + +#if defined(TRIO_DOCUMENTATION) +# include "doc/doc_nan.h" +#endif +/** @addtogroup SpecialQuantities + @{ +*/ + +/************************************************************************* + * Definitions + */ + +#define TRIO_TRUE (1 == 1) +#define TRIO_FALSE (0 == 1) + +/* + * We must enable IEEE floating-point on Alpha + */ +#if defined(__alpha) && !defined(_IEEE_FP) +# if defined(TRIO_COMPILER_DECC) +# if defined(TRIO_PLATFORM_VMS) +# error "Must be compiled with option /IEEE_MODE=UNDERFLOW_TO_ZERO/FLOAT=IEEE" +# else +# if !defined(_CFE) +# error "Must be compiled with option -ieee" +# endif +# endif +# elif defined(TRIO_COMPILER_GCC) && (defined(__osf__) || defined(__linux__)) +# error "Must be compiled with option -mieee" +# endif +#endif /* __alpha && ! _IEEE_FP */ + +/* + * In ANSI/IEEE 754-1985 64-bits double format numbers have the + * following properties (amoungst others) + * + * o FLT_RADIX == 2: binary encoding + * o DBL_MAX_EXP == 1024: 11 bits exponent, where one bit is used + * to indicate special numbers (e.g. NaN and Infinity), so the + * maximum exponent is 10 bits wide (2^10 == 1024). + * o DBL_MANT_DIG == 53: The mantissa is 52 bits wide, but because + * numbers are normalized the initial binary 1 is represented + * implicitly (the so-called "hidden bit"), which leaves us with + * the ability to represent 53 bits wide mantissa. + */ +#if (FLT_RADIX == 2) && (DBL_MAX_EXP == 1024) && (DBL_MANT_DIG == 53) +# define USE_IEEE_754 +#endif + + +/************************************************************************* + * Constants + */ + +static TRIO_CONST char rcsid[] = "@(#)$Id: trionan.c,v 1.14 2003/10/15 08:17:58 veillard Exp $"; + +#if defined(USE_IEEE_754) + +/* + * Endian-agnostic indexing macro. + * + * The value of internalEndianMagic, when converted into a 64-bit + * integer, becomes 0x0706050403020100 (we could have used a 64-bit + * integer value instead of a double, but not all platforms supports + * that type). The value is automatically encoded with the correct + * endianess by the compiler, which means that we can support any + * kind of endianess. The individual bytes are then used as an index + * for the IEEE 754 bit-patterns and masks. + */ +#define TRIO_DOUBLE_INDEX(x) (((unsigned char *)&internalEndianMagic)[7-(x)]) + +static TRIO_CONST double internalEndianMagic = 7.949928895127363e-275; + +/* Mask for the exponent */ +static TRIO_CONST unsigned char ieee_754_exponent_mask[] = { + 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* Mask for the mantissa */ +static TRIO_CONST unsigned char ieee_754_mantissa_mask[] = { + 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/* Mask for the sign bit */ +static TRIO_CONST unsigned char ieee_754_sign_mask[] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* Bit-pattern for negative zero */ +static TRIO_CONST unsigned char ieee_754_negzero_array[] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* Bit-pattern for infinity */ +static TRIO_CONST unsigned char ieee_754_infinity_array[] = { + 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* Bit-pattern for quiet NaN */ +static TRIO_CONST unsigned char ieee_754_qnan_array[] = { + 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + + +/************************************************************************* + * Functions + */ + +/* + * trio_make_double + */ +TRIO_PRIVATE double +trio_make_double +TRIO_ARGS1((values), + TRIO_CONST unsigned char *values) +{ + TRIO_VOLATILE double result; + int i; + + for (i = 0; i < (int)sizeof(double); i++) { + ((TRIO_VOLATILE unsigned char *)&result)[TRIO_DOUBLE_INDEX(i)] = values[i]; + } + return result; +} + +/* + * trio_is_special_quantity + */ +TRIO_PRIVATE int +trio_is_special_quantity +TRIO_ARGS2((number, has_mantissa), + double number, + int *has_mantissa) +{ + unsigned int i; + unsigned char current; + int is_special_quantity = TRIO_TRUE; + + *has_mantissa = 0; + + for (i = 0; i < (unsigned int)sizeof(double); i++) { + current = ((unsigned char *)&number)[TRIO_DOUBLE_INDEX(i)]; + is_special_quantity + &= ((current & ieee_754_exponent_mask[i]) == ieee_754_exponent_mask[i]); + *has_mantissa |= (current & ieee_754_mantissa_mask[i]); + } + return is_special_quantity; +} + +/* + * trio_is_negative + */ +TRIO_PRIVATE int +trio_is_negative +TRIO_ARGS1((number), + double number) +{ + unsigned int i; + int is_negative = TRIO_FALSE; + + for (i = 0; i < (unsigned int)sizeof(double); i++) { + is_negative |= (((unsigned char *)&number)[TRIO_DOUBLE_INDEX(i)] + & ieee_754_sign_mask[i]); + } + return is_negative; +} + +#endif /* USE_IEEE_754 */ + + +/** + Generate negative zero. + + @return Floating-point representation of negative zero. +*/ +TRIO_PUBLIC double +trio_nzero(TRIO_NOARGS) +{ +#if defined(USE_IEEE_754) + return trio_make_double(ieee_754_negzero_array); +#else + TRIO_VOLATILE double zero = 0.0; + + return -zero; +#endif +} + +/** + Generate positive infinity. + + @return Floating-point representation of positive infinity. +*/ +TRIO_PUBLIC double +trio_pinf(TRIO_NOARGS) +{ + /* Cache the result */ + static double result = 0.0; + + if (result == 0.0) { + +#if defined(INFINITY) && defined(__STDC_IEC_559__) + result = (double)INFINITY; + +#elif defined(USE_IEEE_754) + result = trio_make_double(ieee_754_infinity_array); + +#else + /* + * If HUGE_VAL is different from DBL_MAX, then HUGE_VAL is used + * as infinity. Otherwise we have to resort to an overflow + * operation to generate infinity. + */ +# if defined(TRIO_PLATFORM_UNIX) + void (*signal_handler)(int) = signal(SIGFPE, SIG_IGN); +# endif + + result = HUGE_VAL; + if (HUGE_VAL == DBL_MAX) { + /* Force overflow */ + result += HUGE_VAL; + } + +# if defined(TRIO_PLATFORM_UNIX) + signal(SIGFPE, signal_handler); +# endif + +#endif + } + return result; +} + +/** + Generate negative infinity. + + @return Floating-point value of negative infinity. +*/ +TRIO_PUBLIC double +trio_ninf(TRIO_NOARGS) +{ + static double result = 0.0; + + if (result == 0.0) { + /* + * Negative infinity is calculated by negating positive infinity, + * which can be done because it is legal to do calculations on + * infinity (for example, 1 / infinity == 0). + */ + result = -trio_pinf(); + } + return result; +} + +/** + Generate NaN. + + @return Floating-point representation of NaN. +*/ +TRIO_PUBLIC double +trio_nan(TRIO_NOARGS) +{ + /* Cache the result */ + static double result = 0.0; + + if (result == 0.0) { + +#if defined(TRIO_COMPILER_SUPPORTS_C99) + result = nan(""); + +#elif defined(NAN) && defined(__STDC_IEC_559__) + result = (double)NAN; + +#elif defined(USE_IEEE_754) + result = trio_make_double(ieee_754_qnan_array); + +#else + /* + * There are several ways to generate NaN. The one used here is + * to divide infinity by infinity. I would have preferred to add + * negative infinity to positive infinity, but that yields wrong + * result (infinity) on FreeBSD. + * + * This may fail if the hardware does not support NaN, or if + * the Invalid Operation floating-point exception is unmasked. + */ +# if defined(TRIO_PLATFORM_UNIX) + void (*signal_handler)(int) = signal(SIGFPE, SIG_IGN); +# endif + + result = trio_pinf() / trio_pinf(); + +# if defined(TRIO_PLATFORM_UNIX) + signal(SIGFPE, signal_handler); +# endif + +#endif + } + return result; +} + +/** + Check for NaN. + + @param number An arbitrary floating-point number. + @return Boolean value indicating whether or not the number is a NaN. +*/ +TRIO_PUBLIC int +trio_isnan +TRIO_ARGS1((number), + double number) +{ +#if (defined(TRIO_COMPILER_SUPPORTS_C99) && defined(isnan)) \ + || defined(TRIO_COMPILER_SUPPORTS_UNIX95) + /* + * C99 defines isnan() as a macro. UNIX95 defines isnan() as a + * function. This function was already present in XPG4, but this + * is a bit tricky to detect with compiler defines, so we choose + * the conservative approach and only use it for UNIX95. + */ + return isnan(number); + +#elif defined(TRIO_COMPILER_MSVC) || defined(TRIO_COMPILER_BCB) + /* + * Microsoft Visual C++ and Borland C++ Builder have an _isnan() + * function. + */ + return _isnan(number) ? TRIO_TRUE : TRIO_FALSE; + +#elif defined(USE_IEEE_754) + /* + * Examine IEEE 754 bit-pattern. A NaN must have a special exponent + * pattern, and a non-empty mantissa. + */ + int has_mantissa; + int is_special_quantity; + + is_special_quantity = trio_is_special_quantity(number, &has_mantissa); + + return (is_special_quantity && has_mantissa); + +#else + /* + * Fallback solution + */ + int status; + double integral, fraction; + +# if defined(TRIO_PLATFORM_UNIX) + void (*signal_handler)(int) = signal(SIGFPE, SIG_IGN); +# endif + + status = (/* + * NaN is the only number which does not compare to itself + */ + ((TRIO_VOLATILE double)number != (TRIO_VOLATILE double)number) || + /* + * Fallback solution if NaN compares to NaN + */ + ((number != 0.0) && + (fraction = modf(number, &integral), + integral == fraction))); + +# if defined(TRIO_PLATFORM_UNIX) + signal(SIGFPE, signal_handler); +# endif + + return status; + +#endif +} + +/** + Check for infinity. + + @param number An arbitrary floating-point number. + @return 1 if positive infinity, -1 if negative infinity, 0 otherwise. +*/ +TRIO_PUBLIC int +trio_isinf +TRIO_ARGS1((number), + double number) +{ +#if defined(TRIO_COMPILER_DECC) && !defined(__linux__) + /* + * DECC has an isinf() macro, but it works differently than that + * of C99, so we use the fp_class() function instead. + */ + return ((fp_class(number) == FP_POS_INF) + ? 1 + : ((fp_class(number) == FP_NEG_INF) ? -1 : 0)); + +#elif defined(isinf) + /* + * C99 defines isinf() as a macro. + */ + return isinf(number) + ? ((number > 0.0) ? 1 : -1) + : 0; + +#elif defined(TRIO_COMPILER_MSVC) || defined(TRIO_COMPILER_BCB) + /* + * Microsoft Visual C++ and Borland C++ Builder have an _fpclass() + * function that can be used to detect infinity. + */ + return ((_fpclass(number) == _FPCLASS_PINF) + ? 1 + : ((_fpclass(number) == _FPCLASS_NINF) ? -1 : 0)); + +#elif defined(USE_IEEE_754) + /* + * Examine IEEE 754 bit-pattern. Infinity must have a special exponent + * pattern, and an empty mantissa. + */ + int has_mantissa; + int is_special_quantity; + + is_special_quantity = trio_is_special_quantity(number, &has_mantissa); + + return (is_special_quantity && !has_mantissa) + ? ((number < 0.0) ? -1 : 1) + : 0; + +#else + /* + * Fallback solution. + */ + int status; + +# if defined(TRIO_PLATFORM_UNIX) + void (*signal_handler)(int) = signal(SIGFPE, SIG_IGN); +# endif + + double infinity = trio_pinf(); + + status = ((number == infinity) + ? 1 + : ((number == -infinity) ? -1 : 0)); + +# if defined(TRIO_PLATFORM_UNIX) + signal(SIGFPE, signal_handler); +# endif + + return status; + +#endif +} + +#if 0 + /* Temporary fix - this routine is not used anywhere */ +/** + Check for finity. + + @param number An arbitrary floating-point number. + @return Boolean value indicating whether or not the number is a finite. +*/ +TRIO_PUBLIC int +trio_isfinite +TRIO_ARGS1((number), + double number) +{ +#if defined(TRIO_COMPILER_SUPPORTS_C99) && defined(isfinite) + /* + * C99 defines isfinite() as a macro. + */ + return isfinite(number); + +#elif defined(TRIO_COMPILER_MSVC) || defined(TRIO_COMPILER_BCB) + /* + * Microsoft Visual C++ and Borland C++ Builder use _finite(). + */ + return _finite(number); + +#elif defined(USE_IEEE_754) + /* + * Examine IEEE 754 bit-pattern. For finity we do not care about the + * mantissa. + */ + int dummy; + + return (! trio_is_special_quantity(number, &dummy)); + +#else + /* + * Fallback solution. + */ + return ((trio_isinf(number) == 0) && (trio_isnan(number) == 0)); + +#endif +} + +#endif + +/* + * The sign of NaN is always false + */ +TRIO_PUBLIC int +trio_fpclassify_and_signbit +TRIO_ARGS2((number, is_negative), + double number, + int *is_negative) +{ +#if defined(fpclassify) && defined(signbit) + /* + * C99 defines fpclassify() and signbit() as a macros + */ + *is_negative = signbit(number); + switch (fpclassify(number)) { + case FP_NAN: + return TRIO_FP_NAN; + case FP_INFINITE: + return TRIO_FP_INFINITE; + case FP_SUBNORMAL: + return TRIO_FP_SUBNORMAL; + case FP_ZERO: + return TRIO_FP_ZERO; + default: + return TRIO_FP_NORMAL; + } + +#else +# if defined(TRIO_COMPILER_DECC) + /* + * DECC has an fp_class() function. + */ +# define TRIO_FPCLASSIFY(n) fp_class(n) +# define TRIO_QUIET_NAN FP_QNAN +# define TRIO_SIGNALLING_NAN FP_SNAN +# define TRIO_POSITIVE_INFINITY FP_POS_INF +# define TRIO_NEGATIVE_INFINITY FP_NEG_INF +# define TRIO_POSITIVE_SUBNORMAL FP_POS_DENORM +# define TRIO_NEGATIVE_SUBNORMAL FP_NEG_DENORM +# define TRIO_POSITIVE_ZERO FP_POS_ZERO +# define TRIO_NEGATIVE_ZERO FP_NEG_ZERO +# define TRIO_POSITIVE_NORMAL FP_POS_NORM +# define TRIO_NEGATIVE_NORMAL FP_NEG_NORM + +# elif defined(TRIO_COMPILER_MSVC) || defined(TRIO_COMPILER_BCB) + /* + * Microsoft Visual C++ and Borland C++ Builder have an _fpclass() + * function. + */ +# define TRIO_FPCLASSIFY(n) _fpclass(n) +# define TRIO_QUIET_NAN _FPCLASS_QNAN +# define TRIO_SIGNALLING_NAN _FPCLASS_SNAN +# define TRIO_POSITIVE_INFINITY _FPCLASS_PINF +# define TRIO_NEGATIVE_INFINITY _FPCLASS_NINF +# define TRIO_POSITIVE_SUBNORMAL _FPCLASS_PD +# define TRIO_NEGATIVE_SUBNORMAL _FPCLASS_ND +# define TRIO_POSITIVE_ZERO _FPCLASS_PZ +# define TRIO_NEGATIVE_ZERO _FPCLASS_NZ +# define TRIO_POSITIVE_NORMAL _FPCLASS_PN +# define TRIO_NEGATIVE_NORMAL _FPCLASS_NN + +# elif defined(FP_PLUS_NORM) + /* + * HP-UX 9.x and 10.x have an fpclassify() function, that is different + * from the C99 fpclassify() macro supported on HP-UX 11.x. + * + * AIX has class() for C, and _class() for C++, which returns the + * same values as the HP-UX fpclassify() function. + */ +# if defined(TRIO_PLATFORM_AIX) +# if defined(__cplusplus) +# define TRIO_FPCLASSIFY(n) _class(n) +# else +# define TRIO_FPCLASSIFY(n) class(n) +# endif +# else +# define TRIO_FPCLASSIFY(n) fpclassify(n) +# endif +# define TRIO_QUIET_NAN FP_QNAN +# define TRIO_SIGNALLING_NAN FP_SNAN +# define TRIO_POSITIVE_INFINITY FP_PLUS_INF +# define TRIO_NEGATIVE_INFINITY FP_MINUS_INF +# define TRIO_POSITIVE_SUBNORMAL FP_PLUS_DENORM +# define TRIO_NEGATIVE_SUBNORMAL FP_MINUS_DENORM +# define TRIO_POSITIVE_ZERO FP_PLUS_ZERO +# define TRIO_NEGATIVE_ZERO FP_MINUS_ZERO +# define TRIO_POSITIVE_NORMAL FP_PLUS_NORM +# define TRIO_NEGATIVE_NORMAL FP_MINUS_NORM +# endif + +# if defined(TRIO_FPCLASSIFY) + switch (TRIO_FPCLASSIFY(number)) { + case TRIO_QUIET_NAN: + case TRIO_SIGNALLING_NAN: + *is_negative = TRIO_FALSE; /* NaN has no sign */ + return TRIO_FP_NAN; + case TRIO_POSITIVE_INFINITY: + *is_negative = TRIO_FALSE; + return TRIO_FP_INFINITE; + case TRIO_NEGATIVE_INFINITY: + *is_negative = TRIO_TRUE; + return TRIO_FP_INFINITE; + case TRIO_POSITIVE_SUBNORMAL: + *is_negative = TRIO_FALSE; + return TRIO_FP_SUBNORMAL; + case TRIO_NEGATIVE_SUBNORMAL: + *is_negative = TRIO_TRUE; + return TRIO_FP_SUBNORMAL; + case TRIO_POSITIVE_ZERO: + *is_negative = TRIO_FALSE; + return TRIO_FP_ZERO; + case TRIO_NEGATIVE_ZERO: + *is_negative = TRIO_TRUE; + return TRIO_FP_ZERO; + case TRIO_POSITIVE_NORMAL: + *is_negative = TRIO_FALSE; + return TRIO_FP_NORMAL; + case TRIO_NEGATIVE_NORMAL: + *is_negative = TRIO_TRUE; + return TRIO_FP_NORMAL; + default: + /* Just in case... */ + *is_negative = (number < 0.0); + return TRIO_FP_NORMAL; + } + +# else + /* + * Fallback solution. + */ + int rc; + + if (number == 0.0) { + /* + * In IEEE 754 the sign of zero is ignored in comparisons, so we + * have to handle this as a special case by examining the sign bit + * directly. + */ +# if defined(USE_IEEE_754) + *is_negative = trio_is_negative(number); +# else + *is_negative = TRIO_FALSE; /* FIXME */ +# endif + return TRIO_FP_ZERO; + } + if (trio_isnan(number)) { + *is_negative = TRIO_FALSE; + return TRIO_FP_NAN; + } + if ((rc = trio_isinf(number))) { + *is_negative = (rc == -1); + return TRIO_FP_INFINITE; + } + if ((number > 0.0) && (number < DBL_MIN)) { + *is_negative = TRIO_FALSE; + return TRIO_FP_SUBNORMAL; + } + if ((number < 0.0) && (number > -DBL_MIN)) { + *is_negative = TRIO_TRUE; + return TRIO_FP_SUBNORMAL; + } + *is_negative = (number < 0.0); + return TRIO_FP_NORMAL; + +# endif +#endif +} + +/** + Examine the sign of a number. + + @param number An arbitrary floating-point number. + @return Boolean value indicating whether or not the number has the + sign bit set (i.e. is negative). +*/ +TRIO_PUBLIC int +trio_signbit +TRIO_ARGS1((number), + double number) +{ + int is_negative; + + (void)trio_fpclassify_and_signbit(number, &is_negative); + return is_negative; +} + +#if 0 + /* Temporary fix - this routine is not used in libxml */ +/** + Examine the class of a number. + + @param number An arbitrary floating-point number. + @return Enumerable value indicating the class of @p number +*/ +TRIO_PUBLIC int +trio_fpclassify +TRIO_ARGS1((number), + double number) +{ + int dummy; + + return trio_fpclassify_and_signbit(number, &dummy); +} + +#endif + +/** @} SpecialQuantities */ + +/************************************************************************* + * For test purposes. + * + * Add the following compiler option to include this test code. + * + * Unix : -DSTANDALONE + * VMS : /DEFINE=(STANDALONE) + */ +#if defined(STANDALONE) +# include + +static TRIO_CONST char * +getClassification +TRIO_ARGS1((type), + int type) +{ + switch (type) { + case TRIO_FP_INFINITE: + return "FP_INFINITE"; + case TRIO_FP_NAN: + return "FP_NAN"; + case TRIO_FP_NORMAL: + return "FP_NORMAL"; + case TRIO_FP_SUBNORMAL: + return "FP_SUBNORMAL"; + case TRIO_FP_ZERO: + return "FP_ZERO"; + default: + return "FP_UNKNOWN"; + } +} + +static void +print_class +TRIO_ARGS2((prefix, number), + TRIO_CONST char *prefix, + double number) +{ + printf("%-6s: %s %-15s %g\n", + prefix, + trio_signbit(number) ? "-" : "+", + getClassification(TRIO_FPCLASSIFY(number)), + number); +} + +int main(TRIO_NOARGS) +{ + double my_nan; + double my_pinf; + double my_ninf; +# if defined(TRIO_PLATFORM_UNIX) + void (*signal_handler) TRIO_PROTO((int)); +# endif + + my_nan = trio_nan(); + my_pinf = trio_pinf(); + my_ninf = trio_ninf(); + + print_class("Nan", my_nan); + print_class("PInf", my_pinf); + print_class("NInf", my_ninf); + print_class("PZero", 0.0); + print_class("NZero", -0.0); + print_class("PNorm", 1.0); + print_class("NNorm", -1.0); + print_class("PSub", 1.01e-307 - 1.00e-307); + print_class("NSub", 1.00e-307 - 1.01e-307); + + printf("NaN : %4g 0x%02x%02x%02x%02x%02x%02x%02x%02x (%2d, %2d)\n", + my_nan, + ((unsigned char *)&my_nan)[0], + ((unsigned char *)&my_nan)[1], + ((unsigned char *)&my_nan)[2], + ((unsigned char *)&my_nan)[3], + ((unsigned char *)&my_nan)[4], + ((unsigned char *)&my_nan)[5], + ((unsigned char *)&my_nan)[6], + ((unsigned char *)&my_nan)[7], + trio_isnan(my_nan), trio_isinf(my_nan)); + printf("PInf: %4g 0x%02x%02x%02x%02x%02x%02x%02x%02x (%2d, %2d)\n", + my_pinf, + ((unsigned char *)&my_pinf)[0], + ((unsigned char *)&my_pinf)[1], + ((unsigned char *)&my_pinf)[2], + ((unsigned char *)&my_pinf)[3], + ((unsigned char *)&my_pinf)[4], + ((unsigned char *)&my_pinf)[5], + ((unsigned char *)&my_pinf)[6], + ((unsigned char *)&my_pinf)[7], + trio_isnan(my_pinf), trio_isinf(my_pinf)); + printf("NInf: %4g 0x%02x%02x%02x%02x%02x%02x%02x%02x (%2d, %2d)\n", + my_ninf, + ((unsigned char *)&my_ninf)[0], + ((unsigned char *)&my_ninf)[1], + ((unsigned char *)&my_ninf)[2], + ((unsigned char *)&my_ninf)[3], + ((unsigned char *)&my_ninf)[4], + ((unsigned char *)&my_ninf)[5], + ((unsigned char *)&my_ninf)[6], + ((unsigned char *)&my_ninf)[7], + trio_isnan(my_ninf), trio_isinf(my_ninf)); + +# if defined(TRIO_PLATFORM_UNIX) + signal_handler = signal(SIGFPE, SIG_IGN); +# endif + + my_pinf = DBL_MAX + DBL_MAX; + my_ninf = -my_pinf; + my_nan = my_pinf / my_pinf; + +# if defined(TRIO_PLATFORM_UNIX) + signal(SIGFPE, signal_handler); +# endif + + printf("NaN : %4g 0x%02x%02x%02x%02x%02x%02x%02x%02x (%2d, %2d)\n", + my_nan, + ((unsigned char *)&my_nan)[0], + ((unsigned char *)&my_nan)[1], + ((unsigned char *)&my_nan)[2], + ((unsigned char *)&my_nan)[3], + ((unsigned char *)&my_nan)[4], + ((unsigned char *)&my_nan)[5], + ((unsigned char *)&my_nan)[6], + ((unsigned char *)&my_nan)[7], + trio_isnan(my_nan), trio_isinf(my_nan)); + printf("PInf: %4g 0x%02x%02x%02x%02x%02x%02x%02x%02x (%2d, %2d)\n", + my_pinf, + ((unsigned char *)&my_pinf)[0], + ((unsigned char *)&my_pinf)[1], + ((unsigned char *)&my_pinf)[2], + ((unsigned char *)&my_pinf)[3], + ((unsigned char *)&my_pinf)[4], + ((unsigned char *)&my_pinf)[5], + ((unsigned char *)&my_pinf)[6], + ((unsigned char *)&my_pinf)[7], + trio_isnan(my_pinf), trio_isinf(my_pinf)); + printf("NInf: %4g 0x%02x%02x%02x%02x%02x%02x%02x%02x (%2d, %2d)\n", + my_ninf, + ((unsigned char *)&my_ninf)[0], + ((unsigned char *)&my_ninf)[1], + ((unsigned char *)&my_ninf)[2], + ((unsigned char *)&my_ninf)[3], + ((unsigned char *)&my_ninf)[4], + ((unsigned char *)&my_ninf)[5], + ((unsigned char *)&my_ninf)[6], + ((unsigned char *)&my_ninf)[7], + trio_isnan(my_ninf), trio_isinf(my_ninf)); + + return 0; +} +#endif diff --git a/Extras/LibXML/trionan.h b/Extras/LibXML/trionan.h new file mode 100644 index 000000000..4847a00a1 --- /dev/null +++ b/Extras/LibXML/trionan.h @@ -0,0 +1,84 @@ +/************************************************************************* + * + * $Id: trionan.h,v 1.6 2003/08/06 04:43:53 wbrack Exp $ + * + * Copyright (C) 2001 Bjorn Reese + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. + * + ************************************************************************/ + +#ifndef TRIO_NAN_H +#define TRIO_NAN_H + +#include "triodef.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + TRIO_FP_INFINITE, + TRIO_FP_NAN, + TRIO_FP_NORMAL, + TRIO_FP_SUBNORMAL, + TRIO_FP_ZERO +}; + +/* + * Return NaN (Not-a-Number). + */ +TRIO_PUBLIC double trio_nan TRIO_PROTO((void)); + +/* + * Return positive infinity. + */ +TRIO_PUBLIC double trio_pinf TRIO_PROTO((void)); + +/* + * Return negative infinity. + */ +TRIO_PUBLIC double trio_ninf TRIO_PROTO((void)); + +/* + * Return negative zero. + */ +TRIO_PUBLIC double trio_nzero TRIO_PROTO((TRIO_NOARGS)); + +/* + * If number is a NaN return non-zero, otherwise return zero. + */ +TRIO_PUBLIC int trio_isnan TRIO_PROTO((double number)); + +/* + * If number is positive infinity return 1, if number is negative + * infinity return -1, otherwise return 0. + */ +TRIO_PUBLIC int trio_isinf TRIO_PROTO((double number)); + +/* + * If number is finite return non-zero, otherwise return zero. + */ +#if 0 + /* Temporary fix - these 2 routines not used in libxml */ +TRIO_PUBLIC int trio_isfinite TRIO_PROTO((double number)); + +TRIO_PUBLIC int trio_fpclassify TRIO_PROTO((double number)); +#endif + +TRIO_PUBLIC int trio_signbit TRIO_PROTO((double number)); + +TRIO_PUBLIC int trio_fpclassify_and_signbit TRIO_PROTO((double number, int *is_negative)); + +#ifdef __cplusplus +} +#endif + +#endif /* TRIO_NAN_H */ diff --git a/Extras/LibXML/triop.h b/Extras/LibXML/triop.h new file mode 100644 index 000000000..e984c2839 --- /dev/null +++ b/Extras/LibXML/triop.h @@ -0,0 +1,150 @@ +/************************************************************************* + * + * $Id: triop.h,v 1.5 2003/04/03 15:28:28 veillard Exp $ + * + * Copyright (C) 2000 Bjorn Reese and Daniel Stenberg. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. + * + ************************************************************************ + * + * Private functions, types, etc. used for callback functions. + * + * The ref pointer is an opaque type and should remain as such. + * Private data must only be accessible through the getter and + * setter functions. + * + ************************************************************************/ + +#ifndef TRIO_TRIOP_H +#define TRIO_TRIOP_H + +#include "triodef.h" + +#include +#if defined(TRIO_COMPILER_ANCIENT) +# include +#else +# include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef TRIO_C99 +# define TRIO_C99 1 +#endif +#ifndef TRIO_BSD +# define TRIO_BSD 1 +#endif +#ifndef TRIO_GNU +# define TRIO_GNU 1 +#endif +#ifndef TRIO_MISC +# define TRIO_MISC 1 +#endif +#ifndef TRIO_UNIX98 +# define TRIO_UNIX98 1 +#endif +#ifndef TRIO_MICROSOFT +# define TRIO_MICROSOFT 1 +#endif +#ifndef TRIO_EXTENSION +# define TRIO_EXTENSION 1 +#endif +#ifndef TRIO_WIDECHAR /* Does not work yet. Do not enable */ +# define TRIO_WIDECHAR 0 +#endif +#ifndef TRIO_ERRORS +# define TRIO_ERRORS 1 +#endif + +#ifndef TRIO_MALLOC +# define TRIO_MALLOC(n) malloc(n) +#endif +#ifndef TRIO_REALLOC +# define TRIO_REALLOC(x,n) realloc((x),(n)) +#endif +#ifndef TRIO_FREE +# define TRIO_FREE(x) free(x) +#endif + + +/************************************************************************* + * User-defined specifiers + */ + +typedef int (*trio_callback_t) TRIO_PROTO((trio_pointer_t)); + +trio_pointer_t trio_register TRIO_PROTO((trio_callback_t callback, const char *name)); +void trio_unregister TRIO_PROTO((trio_pointer_t handle)); + +TRIO_CONST char *trio_get_format TRIO_PROTO((trio_pointer_t ref)); +trio_pointer_t trio_get_argument TRIO_PROTO((trio_pointer_t ref)); + +/* Modifiers */ +int trio_get_width TRIO_PROTO((trio_pointer_t ref)); +void trio_set_width TRIO_PROTO((trio_pointer_t ref, int width)); +int trio_get_precision TRIO_PROTO((trio_pointer_t ref)); +void trio_set_precision TRIO_PROTO((trio_pointer_t ref, int precision)); +int trio_get_base TRIO_PROTO((trio_pointer_t ref)); +void trio_set_base TRIO_PROTO((trio_pointer_t ref, int base)); +int trio_get_padding TRIO_PROTO((trio_pointer_t ref)); +void trio_set_padding TRIO_PROTO((trio_pointer_t ref, int is_padding)); +int trio_get_short TRIO_PROTO((trio_pointer_t ref)); /* h */ +void trio_set_shortshort TRIO_PROTO((trio_pointer_t ref, int is_shortshort)); +int trio_get_shortshort TRIO_PROTO((trio_pointer_t ref)); /* hh */ +void trio_set_short TRIO_PROTO((trio_pointer_t ref, int is_short)); +int trio_get_long TRIO_PROTO((trio_pointer_t ref)); /* l */ +void trio_set_long TRIO_PROTO((trio_pointer_t ref, int is_long)); +int trio_get_longlong TRIO_PROTO((trio_pointer_t ref)); /* ll */ +void trio_set_longlong TRIO_PROTO((trio_pointer_t ref, int is_longlong)); +int trio_get_longdouble TRIO_PROTO((trio_pointer_t ref)); /* L */ +void trio_set_longdouble TRIO_PROTO((trio_pointer_t ref, int is_longdouble)); +int trio_get_alternative TRIO_PROTO((trio_pointer_t ref)); /* # */ +void trio_set_alternative TRIO_PROTO((trio_pointer_t ref, int is_alternative)); +int trio_get_alignment TRIO_PROTO((trio_pointer_t ref)); /* - */ +void trio_set_alignment TRIO_PROTO((trio_pointer_t ref, int is_leftaligned)); +int trio_get_spacing TRIO_PROTO((trio_pointer_t ref)); /* TRIO_PROTO((space) */ +void trio_set_spacing TRIO_PROTO((trio_pointer_t ref, int is_space)); +int trio_get_sign TRIO_PROTO((trio_pointer_t ref)); /* + */ +void trio_set_sign TRIO_PROTO((trio_pointer_t ref, int is_showsign)); +int trio_get_quote TRIO_PROTO((trio_pointer_t ref)); /* ' */ +void trio_set_quote TRIO_PROTO((trio_pointer_t ref, int is_quote)); +int trio_get_upper TRIO_PROTO((trio_pointer_t ref)); +void trio_set_upper TRIO_PROTO((trio_pointer_t ref, int is_upper)); +#if TRIO_C99 +int trio_get_largest TRIO_PROTO((trio_pointer_t ref)); /* j */ +void trio_set_largest TRIO_PROTO((trio_pointer_t ref, int is_largest)); +int trio_get_ptrdiff TRIO_PROTO((trio_pointer_t ref)); /* t */ +void trio_set_ptrdiff TRIO_PROTO((trio_pointer_t ref, int is_ptrdiff)); +int trio_get_size TRIO_PROTO((trio_pointer_t ref)); /* z / Z */ +void trio_set_size TRIO_PROTO((trio_pointer_t ref, int is_size)); +#endif + +/* Printing */ +int trio_print_ref TRIO_PROTO((trio_pointer_t ref, const char *format, ...)); +int trio_vprint_ref TRIO_PROTO((trio_pointer_t ref, const char *format, va_list args)); +int trio_printv_ref TRIO_PROTO((trio_pointer_t ref, const char *format, trio_pointer_t *args)); + +void trio_print_int TRIO_PROTO((trio_pointer_t ref, int number)); +void trio_print_uint TRIO_PROTO((trio_pointer_t ref, unsigned int number)); +/* void trio_print_long TRIO_PROTO((trio_pointer_t ref, long number)); */ +/* void trio_print_ulong TRIO_PROTO((trio_pointer_t ref, unsigned long number)); */ +void trio_print_double TRIO_PROTO((trio_pointer_t ref, double number)); +void trio_print_string TRIO_PROTO((trio_pointer_t ref, char *string)); +void trio_print_pointer TRIO_PROTO((trio_pointer_t ref, trio_pointer_t pointer)); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* TRIO_TRIOP_H */ diff --git a/Extras/LibXML/triostr.c b/Extras/LibXML/triostr.c new file mode 100644 index 000000000..0ecd6724e --- /dev/null +++ b/Extras/LibXML/triostr.c @@ -0,0 +1,2102 @@ +/************************************************************************* + * + * $Id: triostr.c,v 1.3 2003/04/03 15:28:28 veillard Exp $ + * + * Copyright (C) 2001 Bjorn Reese and Daniel Stenberg. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. + * + ************************************************************************/ + +/************************************************************************* + * Include files + */ + +#include +#include +#include +#include +#include +#include "triodef.h" +#include "triostr.h" + +/************************************************************************* + * Definitions + */ + +#if !defined(TRIO_STRING_PUBLIC) +# define TRIO_STRING_PUBLIC TRIO_PUBLIC +#endif +#if !defined(TRIO_STRING_PRIVATE) +# define TRIO_STRING_PRIVATE TRIO_PRIVATE +#endif + +#if !defined(NULL) +# define NULL 0 +#endif +#if !defined(NIL) +# define NIL ((char)0) +#endif +#if !defined(FALSE) +# define FALSE (1 == 0) +# define TRUE (! FALSE) +#endif +#if !defined(BOOLEAN_T) +# define BOOLEAN_T int +#endif + +#if defined(TRIO_COMPILER_SUPPORTS_C99) +# define USE_STRTOD +# define USE_STRTOF +#elif defined(TRIO_COMPILER_MSVC) +# define USE_STRTOD +#endif + +#if defined(TRIO_PLATFORM_UNIX) +# define USE_STRCASECMP +# define USE_STRNCASECMP +# if defined(TRIO_PLATFORM_SUNOS) +# define USE_SYS_ERRLIST +# else +# define USE_STRERROR +# endif +# if defined(TRIO_PLATFORM_QNX) +# define strcasecmp(x,y) stricmp(x,y) +# define strncasecmp(x,y,n) strnicmp(x,y,n) +# endif +#elif defined(TRIO_PLATFORM_WIN32) +# define USE_STRCASECMP +# define strcasecmp(x,y) strcmpi(x,y) +#endif + +#if !(defined(TRIO_PLATFORM_SUNOS)) +# define USE_TOLOWER +# define USE_TOUPPER +#endif + +/************************************************************************* + * Structures + */ + +struct _trio_string_t +{ + char *content; + size_t length; + size_t allocated; +}; + +/************************************************************************* + * Constants + */ + +#if !defined(TRIO_MINIMAL) +static TRIO_CONST char rcsid[] = "@(#)$Id: triostr.c,v 1.3 2003/04/03 15:28:28 veillard Exp $"; +#endif + +/************************************************************************* + * Static String Functions + */ + +#if defined(TRIO_DOCUMENTATION) +# include "doc/doc_static.h" +#endif +/** @addtogroup StaticStrings + @{ +*/ + +/** + Create new string. + + @param size Size of new string. + @return Pointer to string, or NULL if allocation failed. +*/ +TRIO_STRING_PUBLIC char * +trio_create +TRIO_ARGS1((size), + size_t size) +{ + return (char *)TRIO_MALLOC(size); +} + + +/** + Destroy string. + + @param string String to be freed. +*/ +TRIO_STRING_PUBLIC void +trio_destroy +TRIO_ARGS1((string), + char *string) +{ + if (string) + { + TRIO_FREE(string); + } +} + + +/** + Count the number of characters in a string. + + @param string String to measure. + @return Number of characters in @string. +*/ +TRIO_STRING_PUBLIC size_t +trio_length +TRIO_ARGS1((string), + TRIO_CONST char *string) +{ + return strlen(string); +} + + +#if !defined(TRIO_MINIMAL) +/** + Append @p source at the end of @p target. + + @param target Target string. + @param source Source string. + @return Boolean value indicating success or failure. + + @pre @p target must point to a memory chunk with sufficient room to + contain the @p target string and @p source string. + @pre No boundary checking is performed, so insufficient memory will + result in a buffer overrun. + @post @p target will be zero terminated. +*/ +TRIO_STRING_PUBLIC int +trio_append +TRIO_ARGS2((target, source), + char *target, + TRIO_CONST char *source) +{ + assert(target); + assert(source); + + return (strcat(target, source) != NULL); +} +#endif /* !defined(TRIO_MINIMAL) */ + +#if !defined(TRIO_MINIMAL) +/** + Append at most @p max characters from @p source to @p target. + + @param target Target string. + @param max Maximum number of characters to append. + @param source Source string. + @return Boolean value indicating success or failure. + + @pre @p target must point to a memory chuck with sufficient room to + contain the @p target string and the @p source string (at most @p max + characters). + @pre No boundary checking is performed, so insufficient memory will + result in a buffer overrun. + @post @p target will be zero terminated. +*/ +TRIO_STRING_PUBLIC int +trio_append_max +TRIO_ARGS3((target, max, source), + char *target, + size_t max, + TRIO_CONST char *source) +{ + size_t length; + + assert(target); + assert(source); + + length = trio_length(target); + + if (max > length) + { + strncat(target, source, max - length - 1); + } + return TRUE; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Determine if a string contains a substring. + + @param string String to be searched. + @param substring String to be found. + @return Boolean value indicating success or failure. +*/ +TRIO_STRING_PUBLIC int +trio_contains +TRIO_ARGS2((string, substring), + TRIO_CONST char *string, + TRIO_CONST char *substring) +{ + assert(string); + assert(substring); + + return (0 != strstr(string, substring)); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Copy @p source to @p target. + + @param target Target string. + @param source Source string. + @return Boolean value indicating success or failure. + + @pre @p target must point to a memory chunk with sufficient room to + contain the @p source string. + @pre No boundary checking is performed, so insufficient memory will + result in a buffer overrun. + @post @p target will be zero terminated. +*/ +TRIO_STRING_PUBLIC int +trio_copy +TRIO_ARGS2((target, source), + char *target, + TRIO_CONST char *source) +{ + assert(target); + assert(source); + + (void)strcpy(target, source); + return TRUE; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +/** + Copy at most @p max characters from @p source to @p target. + + @param target Target string. + @param max Maximum number of characters to append. + @param source Source string. + @return Boolean value indicating success or failure. + + @pre @p target must point to a memory chunk with sufficient room to + contain the @p source string (at most @p max characters). + @pre No boundary checking is performed, so insufficient memory will + result in a buffer overrun. + @post @p target will be zero terminated. +*/ +TRIO_STRING_PUBLIC int +trio_copy_max +TRIO_ARGS3((target, max, source), + char *target, + size_t max, + TRIO_CONST char *source) +{ + assert(target); + assert(source); + assert(max > 0); /* Includes != 0 */ + + (void)strncpy(target, source, max - 1); + target[max - 1] = (char)0; + return TRUE; +} + + +/* + * TrioDuplicateMax + */ +TRIO_STRING_PRIVATE char * +TrioDuplicateMax +TRIO_ARGS2((source, size), + TRIO_CONST char *source, + size_t size) +{ + char *target; + + assert(source); + + /* Make room for string plus a terminating zero */ + size++; + target = trio_create(size); + if (target) + { + trio_copy_max(target, size, source); + } + return target; +} + + +/** + Duplicate @p source. + + @param source Source string. + @return A copy of the @p source string. + + @post @p target will be zero terminated. +*/ +TRIO_STRING_PUBLIC char * +trio_duplicate +TRIO_ARGS1((source), + TRIO_CONST char *source) +{ + return TrioDuplicateMax(source, trio_length(source)); +} + + +#if !defined(TRIO_MINIMAL) +/** + Duplicate at most @p max characters of @p source. + + @param source Source string. + @param max Maximum number of characters to duplicate. + @return A copy of the @p source string. + + @post @p target will be zero terminated. +*/ +TRIO_STRING_PUBLIC char * +trio_duplicate_max TRIO_ARGS2((source, max), + TRIO_CONST char *source, + size_t max) +{ + size_t length; + + assert(source); + assert(max > 0); + + length = trio_length(source); + if (length > max) + { + length = max; + } + return TrioDuplicateMax(source, length); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +/** + Compare if two strings are equal. + + @param first First string. + @param second Second string. + @return Boolean indicating whether the two strings are equal or not. + + Case-insensitive comparison. +*/ +TRIO_STRING_PUBLIC int +trio_equal +TRIO_ARGS2((first, second), + TRIO_CONST char *first, + TRIO_CONST char *second) +{ + assert(first); + assert(second); + + if ((first != NULL) && (second != NULL)) + { +#if defined(USE_STRCASECMP) + return (0 == strcasecmp(first, second)); +#else + while ((*first != NIL) && (*second != NIL)) + { + if (trio_to_upper(*first) != trio_to_upper(*second)) + { + break; + } + first++; + second++; + } + return ((*first == NIL) && (*second == NIL)); +#endif + } + return FALSE; +} + + +/** + Compare if two strings are equal. + + @param first First string. + @param second Second string. + @return Boolean indicating whether the two strings are equal or not. + + Case-sensitive comparison. +*/ +TRIO_STRING_PUBLIC int +trio_equal_case +TRIO_ARGS2((first, second), + TRIO_CONST char *first, + TRIO_CONST char *second) +{ + assert(first); + assert(second); + + if ((first != NULL) && (second != NULL)) + { + return (0 == strcmp(first, second)); + } + return FALSE; +} + + +#if !defined(TRIO_MINIMAL) +/** + Compare if two strings up until the first @p max characters are equal. + + @param first First string. + @param max Maximum number of characters to compare. + @param second Second string. + @return Boolean indicating whether the two strings are equal or not. + + Case-sensitive comparison. +*/ +TRIO_STRING_PUBLIC int +trio_equal_case_max +TRIO_ARGS3((first, max, second), + TRIO_CONST char *first, + size_t max, + TRIO_CONST char *second) +{ + assert(first); + assert(second); + + if ((first != NULL) && (second != NULL)) + { + return (0 == strncmp(first, second, max)); + } + return FALSE; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +/** + Compare if two strings are equal. + + @param first First string. + @param second Second string. + @return Boolean indicating whether the two strings are equal or not. + + Collating characters are considered equal. +*/ +TRIO_STRING_PUBLIC int +trio_equal_locale +TRIO_ARGS2((first, second), + TRIO_CONST char *first, + TRIO_CONST char *second) +{ + assert(first); + assert(second); + +#if defined(LC_COLLATE) + return (strcoll(first, second) == 0); +#else + return trio_equal(first, second); +#endif +} + + +/** + Compare if two strings up until the first @p max characters are equal. + + @param first First string. + @param max Maximum number of characters to compare. + @param second Second string. + @return Boolean indicating whether the two strings are equal or not. + + Case-insensitive comparison. +*/ +TRIO_STRING_PUBLIC int +trio_equal_max +TRIO_ARGS3((first, max, second), + TRIO_CONST char *first, + size_t max, + TRIO_CONST char *second) +{ + assert(first); + assert(second); + + if ((first != NULL) && (second != NULL)) + { +#if defined(USE_STRNCASECMP) + return (0 == strncasecmp(first, second, max)); +#else + /* Not adequately tested yet */ + size_t cnt = 0; + while ((*first != NIL) && (*second != NIL) && (cnt <= max)) + { + if (trio_to_upper(*first) != trio_to_upper(*second)) + { + break; + } + first++; + second++; + cnt++; + } + return ((cnt == max) || ((*first == NIL) && (*second == NIL))); +#endif + } + return FALSE; +} + + +/** + Provide a textual description of an error code (errno). + + @param error_number Error number. + @return Textual description of @p error_number. +*/ +TRIO_STRING_PUBLIC TRIO_CONST char * +trio_error +TRIO_ARGS1((error_number), + int error_number) +{ +#if defined(USE_STRERROR) + + return strerror(error_number); + +#elif defined(USE_SYS_ERRLIST) + + extern char *sys_errlist[]; + extern int sys_nerr; + + return ((error_number < 0) || (error_number >= sys_nerr)) + ? "unknown" + : sys_errlist[error_number]; + +#else + + return "unknown"; + +#endif +} + + +#if !defined(TRIO_MINIMAL) +/** + Format the date/time according to @p format. + + @param target Target string. + @param max Maximum number of characters to format. + @param format Formatting string. + @param datetime Date/time structure. + @return Number of formatted characters. + + The formatting string accepts the same specifiers as the standard C + function strftime. +*/ +TRIO_STRING_PUBLIC size_t +trio_format_date_max +TRIO_ARGS4((target, max, format, datetime), + char *target, + size_t max, + TRIO_CONST char *format, + TRIO_CONST struct tm *datetime) +{ + assert(target); + assert(format); + assert(datetime); + assert(max > 0); + + return strftime(target, max, format, datetime); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Calculate a hash value for a string. + + @param string String to be calculated on. + @param type Hash function. + @return Calculated hash value. + + @p type can be one of the following + @li @c TRIO_HASH_PLAIN Plain hash function. +*/ +TRIO_STRING_PUBLIC unsigned long +trio_hash +TRIO_ARGS2((string, type), + TRIO_CONST char *string, + int type) +{ + unsigned long value = 0L; + char ch; + + assert(string); + + switch (type) + { + case TRIO_HASH_PLAIN: + while ( (ch = *string++) != NIL ) + { + value *= 31; + value += (unsigned long)ch; + } + break; + default: + assert(FALSE); + break; + } + return value; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Find first occurrence of a character in a string. + + @param string String to be searched. + @param character Character to be found. + @param A pointer to the found character, or NULL if character was not found. + */ +TRIO_STRING_PUBLIC char * +trio_index +TRIO_ARGS2((string, character), + TRIO_CONST char *string, + int character) +{ + assert(string); + + return strchr(string, character); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Find last occurrence of a character in a string. + + @param string String to be searched. + @param character Character to be found. + @param A pointer to the found character, or NULL if character was not found. + */ +TRIO_STRING_PUBLIC char * +trio_index_last +TRIO_ARGS2((string, character), + TRIO_CONST char *string, + int character) +{ + assert(string); + + return strchr(string, character); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Convert the alphabetic letters in the string to lower-case. + + @param target String to be converted. + @return Number of processed characters (converted or not). +*/ +TRIO_STRING_PUBLIC int +trio_lower +TRIO_ARGS1((target), + char *target) +{ + assert(target); + + return trio_span_function(target, target, trio_to_lower); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Compare two strings using wildcards. + + @param string String to be searched. + @param pattern Pattern, including wildcards, to search for. + @return Boolean value indicating success or failure. + + Case-insensitive comparison. + + The following wildcards can be used + @li @c * Match any number of characters. + @li @c ? Match a single character. +*/ +TRIO_STRING_PUBLIC int +trio_match +TRIO_ARGS2((string, pattern), + TRIO_CONST char *string, + TRIO_CONST char *pattern) +{ + assert(string); + assert(pattern); + + for (; ('*' != *pattern); ++pattern, ++string) + { + if (NIL == *string) + { + return (NIL == *pattern); + } + if ((trio_to_upper((int)*string) != trio_to_upper((int)*pattern)) + && ('?' != *pattern)) + { + return FALSE; + } + } + /* two-line patch to prevent *too* much recursiveness: */ + while ('*' == pattern[1]) + pattern++; + + do + { + if ( trio_match(string, &pattern[1]) ) + { + return TRUE; + } + } + while (*string++); + + return FALSE; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Compare two strings using wildcards. + + @param string String to be searched. + @param pattern Pattern, including wildcards, to search for. + @return Boolean value indicating success or failure. + + Case-sensitive comparison. + + The following wildcards can be used + @li @c * Match any number of characters. + @li @c ? Match a single character. +*/ +TRIO_STRING_PUBLIC int +trio_match_case +TRIO_ARGS2((string, pattern), + TRIO_CONST char *string, + TRIO_CONST char *pattern) +{ + assert(string); + assert(pattern); + + for (; ('*' != *pattern); ++pattern, ++string) + { + if (NIL == *string) + { + return (NIL == *pattern); + } + if ((*string != *pattern) + && ('?' != *pattern)) + { + return FALSE; + } + } + /* two-line patch to prevent *too* much recursiveness: */ + while ('*' == pattern[1]) + pattern++; + + do + { + if ( trio_match_case(string, &pattern[1]) ) + { + return TRUE; + } + } + while (*string++); + + return FALSE; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Execute a function on each character in string. + + @param target Target string. + @param source Source string. + @param Function Function to be executed. + @return Number of processed characters. +*/ +TRIO_STRING_PUBLIC size_t +trio_span_function +TRIO_ARGS3((target, source, Function), + char *target, + TRIO_CONST char *source, + int (*Function) TRIO_PROTO((int))) +{ + size_t count = 0; + + assert(target); + assert(source); + assert(Function); + + while (*source != NIL) + { + *target++ = Function(*source++); + count++; + } + return count; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Search for a substring in a string. + + @param string String to be searched. + @param substring String to be found. + @return Pointer to first occurrence of @p substring in @p string, or NULL + if no match was found. +*/ +TRIO_STRING_PUBLIC char * +trio_substring +TRIO_ARGS2((string, substring), + TRIO_CONST char *string, + TRIO_CONST char *substring) +{ + assert(string); + assert(substring); + + return strstr(string, substring); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Search for a substring in the first @p max characters of a string. + + @param string String to be searched. + @param max Maximum characters to be searched. + @param substring String to be found. + @return Pointer to first occurrence of @p substring in @p string, or NULL + if no match was found. +*/ +TRIO_STRING_PUBLIC char * +trio_substring_max +TRIO_ARGS3((string, max, substring), + TRIO_CONST char *string, + size_t max, + TRIO_CONST char *substring) +{ + size_t count; + size_t size; + char *result = NULL; + + assert(string); + assert(substring); + + size = trio_length(substring); + if (size <= max) + { + for (count = 0; count <= max - size; count++) + { + if (trio_equal_max(substring, size, &string[count])) + { + result = (char *)&string[count]; + break; + } + } + } + return result; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Tokenize string. + + @param string String to be tokenized. + @param tokens String containing list of delimiting characters. + @return Start of new token. + + @warning @p string will be destroyed. +*/ +TRIO_STRING_PUBLIC char * +trio_tokenize +TRIO_ARGS2((string, delimiters), + char *string, + TRIO_CONST char *delimiters) +{ + assert(delimiters); + + return strtok(string, delimiters); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +/** + Convert string to floating-point number. + + @param source String to be converted. + @param endp Pointer to end of the converted string. + @return A floating-point number. + + The following Extended Backus-Naur form is used + @verbatim + double ::= [ ] + ( | + | + ) + [ [ ] ] + number ::= 1*( ) + digit ::= ( '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ) + exponential ::= ( 'e' | 'E' ) + sign ::= ( '-' | '+' ) + decimal_point ::= '.' + @endverbatim +*/ +/* FIXME: Add EBNF for hex-floats */ +TRIO_STRING_PUBLIC trio_long_double_t +trio_to_long_double +TRIO_ARGS2((source, endp), + TRIO_CONST char *source, + char **endp) +{ +#if defined(USE_STRTOLD) + return strtold(source, endp); +#else + int isNegative = FALSE; + int isExponentNegative = FALSE; + trio_long_double_t integer = 0.0; + trio_long_double_t fraction = 0.0; + unsigned long exponent = 0; + trio_long_double_t base; + trio_long_double_t fracdiv = 1.0; + trio_long_double_t value = 0.0; + + /* First try hex-floats */ + if ((source[0] == '0') && ((source[1] == 'x') || (source[1] == 'X'))) + { + base = 16.0; + source += 2; + while (isxdigit((int)*source)) + { + integer *= base; + integer += (isdigit((int)*source) + ? (*source - '0') + : 10 + (trio_to_upper((int)*source) - 'A')); + source++; + } + if (*source == '.') + { + source++; + while (isxdigit((int)*source)) + { + fracdiv /= base; + fraction += fracdiv * (isdigit((int)*source) + ? (*source - '0') + : 10 + (trio_to_upper((int)*source) - 'A')); + source++; + } + if ((*source == 'p') || (*source == 'P')) + { + source++; + if ((*source == '+') || (*source == '-')) + { + isExponentNegative = (*source == '-'); + source++; + } + while (isdigit((int)*source)) + { + exponent *= 10; + exponent += (*source - '0'); + source++; + } + } + } + /* For later use with exponent */ + base = 2.0; + } + else /* Then try normal decimal floats */ + { + base = 10.0; + isNegative = (*source == '-'); + /* Skip sign */ + if ((*source == '+') || (*source == '-')) + source++; + + /* Integer part */ + while (isdigit((int)*source)) + { + integer *= base; + integer += (*source - '0'); + source++; + } + + if (*source == '.') + { + source++; /* skip decimal point */ + while (isdigit((int)*source)) + { + fracdiv /= base; + fraction += (*source - '0') * fracdiv; + source++; + } + } + if ((*source == 'e') + || (*source == 'E') +#if TRIO_MICROSOFT + || (*source == 'd') + || (*source == 'D') +#endif + ) + { + source++; /* Skip exponential indicator */ + isExponentNegative = (*source == '-'); + if ((*source == '+') || (*source == '-')) + source++; + while (isdigit((int)*source)) + { + exponent *= (int)base; + exponent += (*source - '0'); + source++; + } + } + } + + value = integer + fraction; + if (exponent != 0) + { + if (isExponentNegative) + value /= pow(base, (double)exponent); + else + value *= pow(base, (double)exponent); + } + if (isNegative) + value = -value; + + if (endp) + *endp = (char *)source; + return value; +#endif +} + + +/** + Convert string to floating-point number. + + @param source String to be converted. + @param endp Pointer to end of the converted string. + @return A floating-point number. + + See @ref trio_to_long_double. +*/ +TRIO_STRING_PUBLIC double +trio_to_double +TRIO_ARGS2((source, endp), + TRIO_CONST char *source, + char **endp) +{ +#if defined(USE_STRTOD) + return strtod(source, endp); +#else + return (double)trio_to_long_double(source, endp); +#endif +} + +#if !defined(TRIO_MINIMAL) +/** + Convert string to floating-point number. + + @param source String to be converted. + @param endp Pointer to end of the converted string. + @return A floating-point number. + + See @ref trio_to_long_double. +*/ +TRIO_STRING_PUBLIC float +trio_to_float +TRIO_ARGS2((source, endp), + TRIO_CONST char *source, + char **endp) +{ +#if defined(USE_STRTOF) + return strtof(source, endp); +#else + return (float)trio_to_long_double(source, endp); +#endif +} +#endif /* !defined(TRIO_MINIMAL) */ + + +/** + Convert string to signed integer. + + @param string String to be converted. + @param endp Pointer to end of converted string. + @param base Radix number of number. +*/ +TRIO_STRING_PUBLIC long +trio_to_long +TRIO_ARGS3((string, endp, base), + TRIO_CONST char *string, + char **endp, + int base) +{ + assert(string); + assert((base >= 2) && (base <= 36)); + + return strtol(string, endp, base); +} + + +#if !defined(TRIO_MINIMAL) +/** + Convert one alphabetic letter to lower-case. + + @param source The letter to be converted. + @return The converted letter. +*/ +TRIO_STRING_PUBLIC int +trio_to_lower +TRIO_ARGS1((source), + int source) +{ +#if defined(USE_TOLOWER) + + return tolower(source); + +#else + + /* Does not handle locales or non-contiguous alphabetic characters */ + return ((source >= (int)'A') && (source <= (int)'Z')) + ? source - 'A' + 'a' + : source; + +#endif +} +#endif /* !defined(TRIO_MINIMAL) */ + +#if !defined(TRIO_MINIMAL) +/** + Convert string to unsigned integer. + + @param string String to be converted. + @param endp Pointer to end of converted string. + @param base Radix number of number. +*/ +TRIO_STRING_PUBLIC unsigned long +trio_to_unsigned_long +TRIO_ARGS3((string, endp, base), + TRIO_CONST char *string, + char **endp, + int base) +{ + assert(string); + assert((base >= 2) && (base <= 36)); + + return strtoul(string, endp, base); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +/** + Convert one alphabetic letter to upper-case. + + @param source The letter to be converted. + @return The converted letter. +*/ +TRIO_STRING_PUBLIC int +trio_to_upper +TRIO_ARGS1((source), + int source) +{ +#if defined(USE_TOUPPER) + + return toupper(source); + +#else + + /* Does not handle locales or non-contiguous alphabetic characters */ + return ((source >= (int)'a') && (source <= (int)'z')) + ? source - 'a' + 'A' + : source; + +#endif +} + +#if !defined(TRIO_MINIMAL) +/** + Convert the alphabetic letters in the string to upper-case. + + @param target The string to be converted. + @return The number of processed characters (converted or not). +*/ +TRIO_STRING_PUBLIC int +trio_upper +TRIO_ARGS1((target), + char *target) +{ + assert(target); + + return trio_span_function(target, target, trio_to_upper); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +/** @} End of StaticStrings */ + + +/************************************************************************* + * Dynamic String Functions + */ + +#if defined(TRIO_DOCUMENTATION) +# include "doc/doc_dynamic.h" +#endif +/** @addtogroup DynamicStrings + @{ +*/ + +/* + * TrioStringAlloc + */ +TRIO_STRING_PRIVATE trio_string_t * +TrioStringAlloc(TRIO_NOARGS) +{ + trio_string_t *self; + + self = (trio_string_t *)TRIO_MALLOC(sizeof(trio_string_t)); + if (self) + { + self->content = NULL; + self->length = 0; + self->allocated = 0; + } + return self; +} + + +/* + * TrioStringGrow + * + * The size of the string will be increased by 'delta' characters. If + * 'delta' is zero, the size will be doubled. + */ +TRIO_STRING_PRIVATE BOOLEAN_T +TrioStringGrow +TRIO_ARGS2((self, delta), + trio_string_t *self, + size_t delta) +{ + BOOLEAN_T status = FALSE; + char *new_content; + size_t new_size; + + new_size = (delta == 0) + ? ( (self->allocated == 0) ? 1 : self->allocated * 2 ) + : self->allocated + delta; + + new_content = (char *)TRIO_REALLOC(self->content, new_size); + if (new_content) + { + self->content = new_content; + self->allocated = new_size; + status = TRUE; + } + return status; +} + + +#if !defined(TRIO_MINIMAL) +/* + * TrioStringGrowTo + * + * The size of the string will be increased to 'length' plus one characters. + * If 'length' is less than the original size, the original size will be + * used (that is, the size of the string is never decreased). + */ +TRIO_STRING_PRIVATE BOOLEAN_T +TrioStringGrowTo +TRIO_ARGS2((self, length), + trio_string_t *self, + size_t length) +{ + length++; /* Room for terminating zero */ + return (self->allocated < length) + ? TrioStringGrow(self, length - self->allocated) + : TRUE; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/** + Create a new dynamic string. + + @param initial_size Initial size of the buffer. + @return Newly allocated dynamic string, or NULL if memory allocation failed. +*/ +TRIO_STRING_PUBLIC trio_string_t * +trio_string_create +TRIO_ARGS1((initial_size), + int initial_size) +{ + trio_string_t *self; + + self = TrioStringAlloc(); + if (self) + { + if (TrioStringGrow(self, + (size_t)((initial_size > 0) ? initial_size : 1))) + { + self->content[0] = (char)0; + self->allocated = initial_size; + } + else + { + trio_string_destroy(self); + self = NULL; + } + } + return self; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +/** + Deallocate the dynamic string and its contents. + + @param self Dynamic string +*/ +TRIO_STRING_PUBLIC void +trio_string_destroy +TRIO_ARGS1((self), + trio_string_t *self) +{ + assert(self); + + if (self) + { + trio_destroy(self->content); + TRIO_FREE(self); + } +} + + +#if !defined(TRIO_MINIMAL) +/** + Get a pointer to the content. + + @param self Dynamic string. + @param offset Offset into content. + @return Pointer to the content. + + @p Offset can be zero, positive, or negative. If @p offset is zero, + then the start of the content will be returned. If @p offset is positive, + then a pointer to @p offset number of characters from the beginning of the + content is returned. If @p offset is negative, then a pointer to @p offset + number of characters from the ending of the string, starting at the + terminating zero, is returned. +*/ +TRIO_STRING_PUBLIC char * +trio_string_get +TRIO_ARGS2((self, offset), + trio_string_t *self, + int offset) +{ + char *result = NULL; + + assert(self); + + if (self->content != NULL) + { + if (self->length == 0) + { + (void)trio_string_length(self); + } + if (offset >= 0) + { + if (offset > (int)self->length) + { + offset = self->length; + } + } + else + { + offset += self->length + 1; + if (offset < 0) + { + offset = 0; + } + } + result = &(self->content[offset]); + } + return result; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +/** + Extract the content. + + @param self Dynamic String + @return Content of dynamic string. + + The content is removed from the dynamic string. This enables destruction + of the dynamic string without deallocation of the content. +*/ +TRIO_STRING_PUBLIC char * +trio_string_extract +TRIO_ARGS1((self), + trio_string_t *self) +{ + char *result; + + assert(self); + + result = self->content; + /* FIXME: Allocate new empty buffer? */ + self->content = NULL; + self->length = self->allocated = 0; + return result; +} + + +#if !defined(TRIO_MINIMAL) +/** + Set the content of the dynamic string. + + @param self Dynamic String + @param buffer The new content. + + Sets the content of the dynamic string to a copy @p buffer. + An existing content will be deallocated first, if necessary. + + @remark + This function will make a copy of @p buffer. + You are responsible for deallocating @p buffer yourself. +*/ +TRIO_STRING_PUBLIC void +trio_xstring_set +TRIO_ARGS2((self, buffer), + trio_string_t *self, + char *buffer) +{ + assert(self); + + trio_destroy(self->content); + self->content = trio_duplicate(buffer); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +/* + * trio_string_size + */ +TRIO_STRING_PUBLIC int +trio_string_size +TRIO_ARGS1((self), + trio_string_t *self) +{ + assert(self); + + return self->allocated; +} + + +/* + * trio_string_terminate + */ +TRIO_STRING_PUBLIC void +trio_string_terminate +TRIO_ARGS1((self), + trio_string_t *self) +{ + trio_xstring_append_char(self, 0); +} + + +#if !defined(TRIO_MINIMAL) +/** + Append the second string to the first. + + @param self Dynamic string to be modified. + @param other Dynamic string to copy from. + @return Boolean value indicating success or failure. +*/ +TRIO_STRING_PUBLIC int +trio_string_append +TRIO_ARGS2((self, other), + trio_string_t *self, + trio_string_t *other) +{ + size_t length; + + assert(self); + assert(other); + + length = self->length + other->length; + if (!TrioStringGrowTo(self, length)) + goto error; + trio_copy(&self->content[self->length], other->content); + self->length = length; + return TRUE; + + error: + return FALSE; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_xstring_append + */ +TRIO_STRING_PUBLIC int +trio_xstring_append +TRIO_ARGS2((self, other), + trio_string_t *self, + TRIO_CONST char *other) +{ + size_t length; + + assert(self); + assert(other); + + length = self->length + trio_length(other); + if (!TrioStringGrowTo(self, length)) + goto error; + trio_copy(&self->content[self->length], other); + self->length = length; + return TRUE; + + error: + return FALSE; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +/* + * trio_xstring_append_char + */ +TRIO_STRING_PUBLIC int +trio_xstring_append_char +TRIO_ARGS2((self, character), + trio_string_t *self, + char character) +{ + assert(self); + + if ((int)self->length >= trio_string_size(self)) + { + if (!TrioStringGrow(self, 0)) + goto error; + } + self->content[self->length] = character; + self->length++; + return TRUE; + + error: + return FALSE; +} + + +#if !defined(TRIO_MINIMAL) +/** + Search for the first occurrence of second parameter in the first. + + @param self Dynamic string to be modified. + @param other Dynamic string to copy from. + @return Boolean value indicating success or failure. +*/ +TRIO_STRING_PUBLIC int +trio_string_contains +TRIO_ARGS2((self, other), + trio_string_t *self, + trio_string_t *other) +{ + assert(self); + assert(other); + + return trio_contains(self->content, other->content); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_xstring_contains + */ +TRIO_STRING_PUBLIC int +trio_xstring_contains +TRIO_ARGS2((self, other), + trio_string_t *self, + TRIO_CONST char *other) +{ + assert(self); + assert(other); + + return trio_contains(self->content, other); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_copy + */ +TRIO_STRING_PUBLIC int +trio_string_copy +TRIO_ARGS2((self, other), + trio_string_t *self, + trio_string_t *other) +{ + assert(self); + assert(other); + + self->length = 0; + return trio_string_append(self, other); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_xstring_copy + */ +TRIO_STRING_PUBLIC int +trio_xstring_copy +TRIO_ARGS2((self, other), + trio_string_t *self, + TRIO_CONST char *other) +{ + assert(self); + assert(other); + + self->length = 0; + return trio_xstring_append(self, other); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_duplicate + */ +TRIO_STRING_PUBLIC trio_string_t * +trio_string_duplicate +TRIO_ARGS1((other), + trio_string_t *other) +{ + trio_string_t *self; + + assert(other); + + self = TrioStringAlloc(); + if (self) + { + self->content = TrioDuplicateMax(other->content, other->length); + if (self->content) + { + self->length = other->length; + self->allocated = self->length + 1; + } + else + { + self->length = self->allocated = 0; + } + } + return self; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +/* + * trio_xstring_duplicate + */ +TRIO_STRING_PUBLIC trio_string_t * +trio_xstring_duplicate +TRIO_ARGS1((other), + TRIO_CONST char *other) +{ + trio_string_t *self; + + assert(other); + + self = TrioStringAlloc(); + if (self) + { + self->content = TrioDuplicateMax(other, trio_length(other)); + if (self->content) + { + self->length = trio_length(self->content); + self->allocated = self->length + 1; + } + else + { + self->length = self->allocated = 0; + } + } + return self; +} + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_equal + */ +TRIO_STRING_PUBLIC int +trio_string_equal +TRIO_ARGS2((self, other), + trio_string_t *self, + trio_string_t *other) +{ + assert(self); + assert(other); + + return trio_equal(self->content, other->content); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_xstring_equal + */ +TRIO_STRING_PUBLIC int +trio_xstring_equal +TRIO_ARGS2((self, other), + trio_string_t *self, + TRIO_CONST char *other) +{ + assert(self); + assert(other); + + return trio_equal(self->content, other); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_equal_max + */ +TRIO_STRING_PUBLIC int +trio_string_equal_max +TRIO_ARGS3((self, max, other), + trio_string_t *self, + size_t max, + trio_string_t *other) +{ + assert(self); + assert(other); + + return trio_equal_max(self->content, max, other->content); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_xstring_equal_max + */ +TRIO_STRING_PUBLIC int +trio_xstring_equal_max +TRIO_ARGS3((self, max, other), + trio_string_t *self, + size_t max, + TRIO_CONST char *other) +{ + assert(self); + assert(other); + + return trio_equal_max(self->content, max, other); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_equal_case + */ +TRIO_STRING_PUBLIC int +trio_string_equal_case +TRIO_ARGS2((self, other), + trio_string_t *self, + trio_string_t *other) +{ + assert(self); + assert(other); + + return trio_equal_case(self->content, other->content); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_xstring_equal_case + */ +TRIO_STRING_PUBLIC int +trio_xstring_equal_case +TRIO_ARGS2((self, other), + trio_string_t *self, + TRIO_CONST char *other) +{ + assert(self); + assert(other); + + return trio_equal_case(self->content, other); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_equal_case_max + */ +TRIO_STRING_PUBLIC int +trio_string_equal_case_max +TRIO_ARGS3((self, max, other), + trio_string_t *self, + size_t max, + trio_string_t *other) +{ + assert(self); + assert(other); + + return trio_equal_case_max(self->content, max, other->content); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_xstring_equal_case_max + */ +TRIO_STRING_PUBLIC int +trio_xstring_equal_case_max +TRIO_ARGS3((self, max, other), + trio_string_t *self, + size_t max, + TRIO_CONST char *other) +{ + assert(self); + assert(other); + + return trio_equal_case_max(self->content, max, other); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_format_data_max + */ +TRIO_STRING_PUBLIC size_t +trio_string_format_date_max +TRIO_ARGS4((self, max, format, datetime), + trio_string_t *self, + size_t max, + TRIO_CONST char *format, + TRIO_CONST struct tm *datetime) +{ + assert(self); + + return trio_format_date_max(self->content, max, format, datetime); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_index + */ +TRIO_STRING_PUBLIC char * +trio_string_index +TRIO_ARGS2((self, character), + trio_string_t *self, + int character) +{ + assert(self); + + return trio_index(self->content, character); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_index_last + */ +TRIO_STRING_PUBLIC char * +trio_string_index_last +TRIO_ARGS2((self, character), + trio_string_t *self, + int character) +{ + assert(self); + + return trio_index_last(self->content, character); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_length + */ +TRIO_STRING_PUBLIC int +trio_string_length +TRIO_ARGS1((self), + trio_string_t *self) +{ + assert(self); + + if (self->length == 0) + { + self->length = trio_length(self->content); + } + return self->length; +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_lower + */ +TRIO_STRING_PUBLIC int +trio_string_lower +TRIO_ARGS1((self), + trio_string_t *self) +{ + assert(self); + + return trio_lower(self->content); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_match + */ +TRIO_STRING_PUBLIC int +trio_string_match +TRIO_ARGS2((self, other), + trio_string_t *self, + trio_string_t *other) +{ + assert(self); + assert(other); + + return trio_match(self->content, other->content); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_xstring_match + */ +TRIO_STRING_PUBLIC int +trio_xstring_match +TRIO_ARGS2((self, other), + trio_string_t *self, + TRIO_CONST char *other) +{ + assert(self); + assert(other); + + return trio_match(self->content, other); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_match_case + */ +TRIO_STRING_PUBLIC int +trio_string_match_case +TRIO_ARGS2((self, other), + trio_string_t *self, + trio_string_t *other) +{ + assert(self); + assert(other); + + return trio_match_case(self->content, other->content); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_xstring_match_case + */ +TRIO_STRING_PUBLIC int +trio_xstring_match_case +TRIO_ARGS2((self, other), + trio_string_t *self, + TRIO_CONST char *other) +{ + assert(self); + assert(other); + + return trio_match_case(self->content, other); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_substring + */ +TRIO_STRING_PUBLIC char * +trio_string_substring +TRIO_ARGS2((self, other), + trio_string_t *self, + trio_string_t *other) +{ + assert(self); + assert(other); + + return trio_substring(self->content, other->content); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_xstring_substring + */ +TRIO_STRING_PUBLIC char * +trio_xstring_substring +TRIO_ARGS2((self, other), + trio_string_t *self, + TRIO_CONST char *other) +{ + assert(self); + assert(other); + + return trio_substring(self->content, other); +} +#endif /* !defined(TRIO_MINIMAL) */ + + +#if !defined(TRIO_MINIMAL) +/* + * trio_string_upper + */ +TRIO_STRING_PUBLIC int +trio_string_upper +TRIO_ARGS1((self), + trio_string_t *self) +{ + assert(self); + + return trio_upper(self->content); +} +#endif /* !defined(TRIO_MINIMAL) */ + +/** @} End of DynamicStrings */ diff --git a/Extras/LibXML/triostr.h b/Extras/LibXML/triostr.h new file mode 100644 index 000000000..d0e3836fb --- /dev/null +++ b/Extras/LibXML/triostr.h @@ -0,0 +1,140 @@ +/************************************************************************* + * + * $Id: triostr.h,v 1.3 2003/04/03 15:28:28 veillard Exp $ + * + * Copyright (C) 2001 Bjorn Reese and Daniel Stenberg. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. + * + ************************************************************************/ + +#ifndef TRIO_TRIOSTR_H +#define TRIO_TRIOSTR_H + +#include +#include +#include +#include +#include "triodef.h" +#include "triop.h" + +enum { + TRIO_HASH_NONE = 0, + TRIO_HASH_PLAIN, + TRIO_HASH_TWOSIGNED +}; + +#if !defined(TRIO_STRING_PUBLIC) +# if !defined(TRIO_PUBLIC) +# define TRIO_PUBLIC +# endif +# define TRIO_STRING_PUBLIC TRIO_PUBLIC +#endif + +/************************************************************************* + * String functions + */ + +TRIO_STRING_PUBLIC int trio_copy_max TRIO_PROTO((char *target, size_t max, const char *source)); +TRIO_STRING_PUBLIC char *trio_create TRIO_PROTO((size_t size)); +TRIO_STRING_PUBLIC void trio_destroy TRIO_PROTO((char *string)); +TRIO_STRING_PUBLIC char *trio_duplicate TRIO_PROTO((const char *source)); +TRIO_STRING_PUBLIC int trio_equal TRIO_PROTO((const char *first, const char *second)); +TRIO_STRING_PUBLIC int trio_equal_case TRIO_PROTO((const char *first, const char *second)); +TRIO_STRING_PUBLIC int trio_equal_locale TRIO_PROTO((const char *first, const char *second)); +TRIO_STRING_PUBLIC int trio_equal_max TRIO_PROTO((const char *first, size_t max, const char *second)); +TRIO_STRING_PUBLIC TRIO_CONST char *trio_error TRIO_PROTO((int)); +TRIO_STRING_PUBLIC size_t trio_length TRIO_PROTO((const char *string)); +TRIO_STRING_PUBLIC double trio_to_double TRIO_PROTO((const char *source, char **endp)); +TRIO_STRING_PUBLIC long trio_to_long TRIO_PROTO((const char *source, char **endp, int base)); +TRIO_STRING_PUBLIC trio_long_double_t trio_to_long_double TRIO_PROTO((const char *source, char **endp)); +TRIO_STRING_PUBLIC int trio_to_upper TRIO_PROTO((int source)); + +#if !defined(TRIO_MINIMAL) + +TRIO_STRING_PUBLIC int trio_append TRIO_PROTO((char *target, const char *source)); +TRIO_STRING_PUBLIC int trio_append_max TRIO_PROTO((char *target, size_t max, const char *source)); +TRIO_STRING_PUBLIC int trio_contains TRIO_PROTO((const char *string, const char *substring)); +TRIO_STRING_PUBLIC int trio_copy TRIO_PROTO((char *target, const char *source)); +TRIO_STRING_PUBLIC char *trio_duplicate_max TRIO_PROTO((const char *source, size_t max)); +TRIO_STRING_PUBLIC int trio_equal_case_max TRIO_PROTO((const char *first, size_t max, const char *second)); +TRIO_STRING_PUBLIC size_t trio_format_date_max TRIO_PROTO((char *target, size_t max, const char *format, const struct tm *datetime)); +TRIO_STRING_PUBLIC unsigned long trio_hash TRIO_PROTO((const char *string, int type)); +TRIO_STRING_PUBLIC char *trio_index TRIO_PROTO((const char *string, int character)); +TRIO_STRING_PUBLIC char *trio_index_last TRIO_PROTO((const char *string, int character)); +TRIO_STRING_PUBLIC int trio_lower TRIO_PROTO((char *target)); +TRIO_STRING_PUBLIC int trio_match TRIO_PROTO((const char *string, const char *pattern)); +TRIO_STRING_PUBLIC int trio_match_case TRIO_PROTO((const char *string, const char *pattern)); +TRIO_STRING_PUBLIC size_t trio_span_function TRIO_PROTO((char *target, const char *source, int (*Function) TRIO_PROTO((int)))); +TRIO_STRING_PUBLIC char *trio_substring TRIO_PROTO((const char *string, const char *substring)); +TRIO_STRING_PUBLIC char *trio_substring_max TRIO_PROTO((const char *string, size_t max, const char *substring)); +TRIO_STRING_PUBLIC float trio_to_float TRIO_PROTO((const char *source, char **endp)); +TRIO_STRING_PUBLIC int trio_to_lower TRIO_PROTO((int source)); +TRIO_STRING_PUBLIC unsigned long trio_to_unsigned_long TRIO_PROTO((const char *source, char **endp, int base)); +TRIO_STRING_PUBLIC char *trio_tokenize TRIO_PROTO((char *string, const char *delimiters)); +TRIO_STRING_PUBLIC int trio_upper TRIO_PROTO((char *target)); + +#endif /* !defined(TRIO_MINIMAL) */ + +/************************************************************************* + * Dynamic string functions + */ + +/* + * Opaque type for dynamic strings + */ + +typedef struct _trio_string_t trio_string_t; + +TRIO_STRING_PUBLIC void trio_string_destroy TRIO_PROTO((trio_string_t *self)); +TRIO_STRING_PUBLIC char *trio_string_extract TRIO_PROTO((trio_string_t *self)); +TRIO_STRING_PUBLIC int trio_string_size TRIO_PROTO((trio_string_t *self)); +TRIO_STRING_PUBLIC void trio_string_terminate TRIO_PROTO((trio_string_t *self)); +TRIO_STRING_PUBLIC int trio_xstring_append_char TRIO_PROTO((trio_string_t *self, char character)); +TRIO_STRING_PUBLIC trio_string_t *trio_xstring_duplicate TRIO_PROTO((const char *other)); + +#if !defined(TRIO_MINIMAL) + +TRIO_STRING_PUBLIC trio_string_t *trio_string_create TRIO_PROTO((int initial_size)); +TRIO_STRING_PUBLIC char *trio_string_get TRIO_PROTO((trio_string_t *self, int offset)); +TRIO_STRING_PUBLIC void trio_xstring_set TRIO_PROTO((trio_string_t *self, char *buffer)); + +TRIO_STRING_PUBLIC int trio_string_append TRIO_PROTO((trio_string_t *self, trio_string_t *other)); +TRIO_STRING_PUBLIC int trio_string_contains TRIO_PROTO((trio_string_t *self, trio_string_t *other)); +TRIO_STRING_PUBLIC int trio_string_copy TRIO_PROTO((trio_string_t *self, trio_string_t *other)); +TRIO_STRING_PUBLIC trio_string_t *trio_string_duplicate TRIO_PROTO((trio_string_t *other)); +TRIO_STRING_PUBLIC int trio_string_equal TRIO_PROTO((trio_string_t *self, trio_string_t *other)); +TRIO_STRING_PUBLIC int trio_string_equal_max TRIO_PROTO((trio_string_t *self, size_t max, trio_string_t *second)); +TRIO_STRING_PUBLIC int trio_string_equal_case TRIO_PROTO((trio_string_t *self, trio_string_t *other)); +TRIO_STRING_PUBLIC int trio_string_equal_case_max TRIO_PROTO((trio_string_t *self, size_t max, trio_string_t *other)); +TRIO_STRING_PUBLIC size_t trio_string_format_date_max TRIO_PROTO((trio_string_t *self, size_t max, const char *format, const struct tm *datetime)); +TRIO_STRING_PUBLIC char *trio_string_index TRIO_PROTO((trio_string_t *self, int character)); +TRIO_STRING_PUBLIC char *trio_string_index_last TRIO_PROTO((trio_string_t *self, int character)); +TRIO_STRING_PUBLIC int trio_string_length TRIO_PROTO((trio_string_t *self)); +TRIO_STRING_PUBLIC int trio_string_lower TRIO_PROTO((trio_string_t *self)); +TRIO_STRING_PUBLIC int trio_string_match TRIO_PROTO((trio_string_t *self, trio_string_t *other)); +TRIO_STRING_PUBLIC int trio_string_match_case TRIO_PROTO((trio_string_t *self, trio_string_t *other)); +TRIO_STRING_PUBLIC char *trio_string_substring TRIO_PROTO((trio_string_t *self, trio_string_t *other)); +TRIO_STRING_PUBLIC int trio_string_upper TRIO_PROTO((trio_string_t *self)); + +TRIO_STRING_PUBLIC int trio_xstring_append TRIO_PROTO((trio_string_t *self, const char *other)); +TRIO_STRING_PUBLIC int trio_xstring_contains TRIO_PROTO((trio_string_t *self, const char *other)); +TRIO_STRING_PUBLIC int trio_xstring_copy TRIO_PROTO((trio_string_t *self, const char *other)); +TRIO_STRING_PUBLIC int trio_xstring_equal TRIO_PROTO((trio_string_t *self, const char *other)); +TRIO_STRING_PUBLIC int trio_xstring_equal_max TRIO_PROTO((trio_string_t *self, size_t max, const char *other)); +TRIO_STRING_PUBLIC int trio_xstring_equal_case TRIO_PROTO((trio_string_t *self, const char *other)); +TRIO_STRING_PUBLIC int trio_xstring_equal_case_max TRIO_PROTO((trio_string_t *self, size_t max, const char *other)); +TRIO_STRING_PUBLIC int trio_xstring_match TRIO_PROTO((trio_string_t *self, const char *other)); +TRIO_STRING_PUBLIC int trio_xstring_match_case TRIO_PROTO((trio_string_t *self, const char *other)); +TRIO_STRING_PUBLIC char *trio_xstring_substring TRIO_PROTO((trio_string_t *self, const char *other)); + +#endif /* !defined(TRIO_MINIMAL) */ + +#endif /* TRIO_TRIOSTR_H */ diff --git a/Extras/LibXML/xmllint.c b/Extras/LibXML/xmllint.c new file mode 100644 index 000000000..79025f130 --- /dev/null +++ b/Extras/LibXML/xmllint.c @@ -0,0 +1,3490 @@ +/* + * xmllint.c : a small tester program for XML input. + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#include "libxml.h" + +#include +#include +#include + +#if defined (_WIN32) && !defined(__CYGWIN__) +#if defined (_MSC_VER) || defined(__BORLANDC__) +#include +#pragma comment(lib, "ws2_32.lib") +#define gettimeofday(p1,p2) +#endif /* _MSC_VER */ +#endif /* _WIN32 */ + +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_TIME_H +#include +#endif + +#ifdef __MINGW32__ +#define _WINSOCKAPI_ +#include +#include +#undef XML_SOCKLEN_T +#define XML_SOCKLEN_T unsigned int +#endif + +#ifdef HAVE_SYS_TIMEB_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_MMAN_H +#include +/* seems needed for Solaris */ +#ifndef MAP_FAILED +#define MAP_FAILED ((void *) -1) +#endif +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_LIBREADLINE +#include +#ifdef HAVE_LIBHISTORY +#include +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef LIBXML_XINCLUDE_ENABLED +#include +#endif +#ifdef LIBXML_CATALOG_ENABLED +#include +#endif +#include +#include +#ifdef LIBXML_SCHEMATRON_ENABLED +#include +#endif +#ifdef LIBXML_SCHEMAS_ENABLED +#include +#include +#endif +#ifdef LIBXML_PATTERN_ENABLED +#include +#endif +#ifdef LIBXML_C14N_ENABLED +#include +#endif + +#ifndef XML_XML_DEFAULT_CATALOG +#define XML_XML_DEFAULT_CATALOG "file:///etc/xml/catalog" +#endif + +typedef enum { + XMLLINT_RETURN_OK = 0, /* No error */ + XMLLINT_ERR_UNCLASS, /* Unclassified */ + XMLLINT_ERR_DTD, /* Error in DTD */ + XMLLINT_ERR_VALID, /* Validation error */ + XMLLINT_ERR_RDFILE, /* CtxtReadFile error */ + XMLLINT_ERR_SCHEMACOMP, /* Schema compilation */ + XMLLINT_ERR_OUT, /* Error writing output */ + XMLLINT_ERR_SCHEMAPAT, /* Error in schema pattern */ + XMLLINT_ERR_RDREGIS, /* Error in Reader registration */ + XMLLINT_ERR_MEM /* Out of memory error */ +} xmllintReturnCode; +#ifdef LIBXML_DEBUG_ENABLED +static int shell = 0; +static int debugent = 0; +#endif +static int debug = 0; +static int maxmem = 0; +#ifdef LIBXML_TREE_ENABLED +static int copy = 0; +#endif /* LIBXML_TREE_ENABLED */ +static int recovery = 0; +static int noent = 0; +static int noblanks = 0; +static int noout = 0; +static int nowrap = 0; +#ifdef LIBXML_OUTPUT_ENABLED +static int format = 0; +static const char *output = NULL; +static int compress = 0; +#endif /* LIBXML_OUTPUT_ENABLED */ +#ifdef LIBXML_VALID_ENABLED +static int valid = 0; +static int postvalid = 0; +static char * dtdvalid = NULL; +static char * dtdvalidfpi = NULL; +#endif +#ifdef LIBXML_SCHEMAS_ENABLED +static char * relaxng = NULL; +static xmlRelaxNGPtr relaxngschemas = NULL; +static char * schema = NULL; +static xmlSchemaPtr wxschemas = NULL; +#endif +#ifdef LIBXML_SCHEMATRON_ENABLED +static char * schematron = NULL; +static xmlSchematronPtr wxschematron = NULL; +#endif +static int repeat = 0; +static int insert = 0; +#if defined(LIBXML_HTML_ENABLED) || defined(LIBXML_VALID_ENABLED) +static int html = 0; +static int xmlout = 0; +#endif +static int htmlout = 0; +#ifdef LIBXML_PUSH_ENABLED +static int push = 0; +#endif /* LIBXML_PUSH_ENABLED */ +#ifdef HAVE_SYS_MMAN_H +static int memory = 0; +#endif +static int testIO = 0; +static char *encoding = NULL; +#ifdef LIBXML_XINCLUDE_ENABLED +static int xinclude = 0; +#endif +static int dtdattrs = 0; +static int loaddtd = 0; +static xmllintReturnCode progresult = XMLLINT_RETURN_OK; +static int timing = 0; +static int generate = 0; +static int dropdtd = 0; +#ifdef LIBXML_CATALOG_ENABLED +static int catalogs = 0; +static int nocatalogs = 0; +#endif +#ifdef LIBXML_C14N_ENABLED +static int canonical = 0; +static int exc_canonical = 0; +#endif +#ifdef LIBXML_READER_ENABLED +static int stream = 0; +static int walker = 0; +#endif /* LIBXML_READER_ENABLED */ +static int chkregister = 0; +static int nbregister = 0; +#ifdef LIBXML_SAX1_ENABLED +static int sax1 = 0; +#endif /* LIBXML_SAX1_ENABLED */ +#ifdef LIBXML_PATTERN_ENABLED +static const char *pattern = NULL; +static xmlPatternPtr patternc = NULL; +static xmlStreamCtxtPtr patstream = NULL; +#endif +static int options = XML_PARSE_COMPACT; +static int sax = 0; + +/************************************************************************ + * * + * Entity loading control and customization. * + * * + ************************************************************************/ +#define MAX_PATHS 64 +static xmlChar *paths[MAX_PATHS + 1]; +static int nbpaths = 0; +static int load_trace = 0; + +static +void parsePath(const xmlChar *path) { + const xmlChar *cur; + + if (path == NULL) + return; + while (*path != 0) { + if (nbpaths >= MAX_PATHS) { + fprintf(stderr, "MAX_PATHS reached: too many paths\n"); + return; + } + cur = path; + while ((*cur == ' ') || (*cur == ':')) + cur++; + path = cur; + while ((*cur != 0) && (*cur != ' ') && (*cur != ':')) + cur++; + if (cur != path) { + paths[nbpaths] = xmlStrndup(path, cur - path); + if (paths[nbpaths] != NULL) + nbpaths++; + path = cur; + } + } +} + +static xmlExternalEntityLoader defaultEntityLoader = NULL; + +static xmlParserInputPtr +xmllintExternalEntityLoader(const char *URL, const char *ID, + xmlParserCtxtPtr ctxt) { + xmlParserInputPtr ret; + warningSAXFunc warning = NULL; + errorSAXFunc err = NULL; + + int i; + const char *lastsegment = URL; + const char *iter = URL; + + if (nbpaths > 0) { + while (*iter != 0) { + if (*iter == '/') + lastsegment = iter + 1; + iter++; + } + } + + if ((ctxt != NULL) && (ctxt->sax != NULL)) { + warning = ctxt->sax->warning; + err = ctxt->sax->error; + ctxt->sax->warning = NULL; + ctxt->sax->error = NULL; + } + + if (defaultEntityLoader != NULL) { + ret = defaultEntityLoader(URL, ID, ctxt); + if (ret != NULL) { + if (warning != NULL) + ctxt->sax->warning = warning; + if (err != NULL) + ctxt->sax->error = err; + if (load_trace) { + fprintf \ + (stderr, + "Loaded URL=\"%s\" ID=\"%s\"\n", + URL ? URL : "(null)", + ID ? ID : "(null)"); + } + return(ret); + } + } + for (i = 0;i < nbpaths;i++) { + xmlChar *newURL; + + newURL = xmlStrdup((const xmlChar *) paths[i]); + newURL = xmlStrcat(newURL, (const xmlChar *) "/"); + newURL = xmlStrcat(newURL, (const xmlChar *) lastsegment); + if (newURL != NULL) { + ret = defaultEntityLoader((const char *)newURL, ID, ctxt); + if (ret != NULL) { + if (warning != NULL) + ctxt->sax->warning = warning; + if (err != NULL) + ctxt->sax->error = err; + if (load_trace) { + fprintf \ + (stderr, + "Loaded URL=\"%s\" ID=\"%s\"\n", + newURL, + ID ? ID : "(null)"); + } + xmlFree(newURL); + return(ret); + } + xmlFree(newURL); + } + } + if (err != NULL) + ctxt->sax->error = err; + if (warning != NULL) { + ctxt->sax->warning = warning; + if (URL != NULL) + warning(ctxt, "failed to load external entity \"%s\"\n", URL); + else if (ID != NULL) + warning(ctxt, "failed to load external entity \"%s\"\n", ID); + } + return(NULL); +} +/************************************************************************ + * * + * Memory allocation consumption debugging * + * * + ************************************************************************/ + +static void +OOM(void) +{ + fprintf(stderr, "Ran out of memory needs > %d bytes\n", maxmem); + progresult = XMLLINT_ERR_MEM; +} + +static void +myFreeFunc(void *mem) +{ + xmlMemFree(mem); +} +static void * +myMallocFunc(size_t size) +{ + void *ret; + + ret = xmlMemMalloc(size); + if (ret != NULL) { + if (xmlMemUsed() > maxmem) { + OOM(); + xmlMemFree(ret); + return (NULL); + } + } + return (ret); +} +static void * +myReallocFunc(void *mem, size_t size) +{ + void *ret; + + ret = xmlMemRealloc(mem, size); + if (ret != NULL) { + if (xmlMemUsed() > maxmem) { + OOM(); + xmlMemFree(ret); + return (NULL); + } + } + return (ret); +} +static char * +myStrdupFunc(const char *str) +{ + char *ret; + + ret = xmlMemoryStrdup(str); + if (ret != NULL) { + if (xmlMemUsed() > maxmem) { + OOM(); + xmlFree(ret); + return (NULL); + } + } + return (ret); +} +/************************************************************************ + * * + * Internal timing routines to remove the necessity to have * + * unix-specific function calls. * + * * + ************************************************************************/ + +#ifndef HAVE_GETTIMEOFDAY +#ifdef HAVE_SYS_TIMEB_H +#ifdef HAVE_SYS_TIME_H +#ifdef HAVE_FTIME + +static int +my_gettimeofday(struct timeval *tvp, void *tzp) +{ + struct timeb timebuffer; + + ftime(&timebuffer); + if (tvp) { + tvp->tv_sec = timebuffer.time; + tvp->tv_usec = timebuffer.millitm * 1000L; + } + return (0); +} +#define HAVE_GETTIMEOFDAY 1 +#define gettimeofday my_gettimeofday + +#endif /* HAVE_FTIME */ +#endif /* HAVE_SYS_TIME_H */ +#endif /* HAVE_SYS_TIMEB_H */ +#endif /* !HAVE_GETTIMEOFDAY */ + +#if defined(HAVE_GETTIMEOFDAY) +static struct timeval begin, end; + +/* + * startTimer: call where you want to start timing + */ +static void +startTimer(void) +{ + gettimeofday(&begin, NULL); +} + +/* + * endTimer: call where you want to stop timing and to print out a + * message about the timing performed; format is a printf + * type argument + */ +static void XMLCDECL +endTimer(const char *fmt, ...) +{ + long msec; + va_list ap; + + gettimeofday(&end, NULL); + msec = end.tv_sec - begin.tv_sec; + msec *= 1000; + msec += (end.tv_usec - begin.tv_usec) / 1000; + +#ifndef HAVE_STDARG_H +#error "endTimer required stdarg functions" +#endif + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + fprintf(stderr, " took %ld ms\n", msec); +} +#elif defined(HAVE_TIME_H) +/* + * No gettimeofday function, so we have to make do with calling clock. + * This is obviously less accurate, but there's little we can do about + * that. + */ +#ifndef CLOCKS_PER_SEC +#define CLOCKS_PER_SEC 100 +#endif + +static clock_t begin, end; +static void +startTimer(void) +{ + begin = clock(); +} +static void XMLCDECL +endTimer(const char *fmt, ...) +{ + long msec; + va_list ap; + + end = clock(); + msec = ((end - begin) * 1000) / CLOCKS_PER_SEC; + +#ifndef HAVE_STDARG_H +#error "endTimer required stdarg functions" +#endif + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, " took %ld ms\n", msec); +} +#else + +/* + * We don't have a gettimeofday or time.h, so we just don't do timing + */ +static void +startTimer(void) +{ + /* + * Do nothing + */ +} +static void XMLCDECL +endTimer(char *format, ...) +{ + /* + * We cannot do anything because we don't have a timing function + */ +#ifdef HAVE_STDARG_H + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + fprintf(stderr, " was not timed\n", msec); +#else + /* We don't have gettimeofday, time or stdarg.h, what crazy world is + * this ?! + */ +#endif +} +#endif +/************************************************************************ + * * + * HTML ouput * + * * + ************************************************************************/ +static char buffer[50000]; + +static void +xmlHTMLEncodeSend(void) { + char *result; + + result = (char *) xmlEncodeEntitiesReentrant(NULL, BAD_CAST buffer); + if (result) { + xmlGenericError(xmlGenericErrorContext, "%s", result); + xmlFree(result); + } + buffer[0] = 0; +} + +/** + * xmlHTMLPrintFileInfo: + * @input: an xmlParserInputPtr input + * + * Displays the associated file and line informations for the current input + */ + +static void +xmlHTMLPrintFileInfo(xmlParserInputPtr input) { + int len; + xmlGenericError(xmlGenericErrorContext, "

"); + + len = strlen(buffer); + if (input != NULL) { + if (input->filename) { + snprintf(&buffer[len], sizeof(buffer) - len, "%s:%d: ", input->filename, + input->line); + } else { + snprintf(&buffer[len], sizeof(buffer) - len, "Entity: line %d: ", input->line); + } + } + xmlHTMLEncodeSend(); +} + +/** + * xmlHTMLPrintFileContext: + * @input: an xmlParserInputPtr input + * + * Displays current context within the input content for error tracking + */ + +static void +xmlHTMLPrintFileContext(xmlParserInputPtr input) { + const xmlChar *cur, *base; + int len; + int n; + + if (input == NULL) return; + xmlGenericError(xmlGenericErrorContext, "

\n");
+    cur = input->cur;
+    base = input->base;
+    while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) {
+	cur--;
+    }
+    n = 0;
+    while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r'))
+        cur--;
+    if ((*cur == '\n') || (*cur == '\r')) cur++;
+    base = cur;
+    n = 0;
+    while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79)) {
+	len = strlen(buffer);
+        snprintf(&buffer[len], sizeof(buffer) - len, "%c", 
+		    (unsigned char) *cur++);
+	n++;
+    }
+    len = strlen(buffer);
+    snprintf(&buffer[len], sizeof(buffer) - len, "\n");
+    cur = input->cur;
+    while ((*cur == '\n') || (*cur == '\r'))
+	cur--;
+    n = 0;
+    while ((cur != base) && (n++ < 80)) {
+	len = strlen(buffer);
+        snprintf(&buffer[len], sizeof(buffer) - len, " ");
+        base++;
+    }
+    len = strlen(buffer);
+    snprintf(&buffer[len], sizeof(buffer) - len, "^\n");
+    xmlHTMLEncodeSend();
+    xmlGenericError(xmlGenericErrorContext, "
"); +} + +/** + * xmlHTMLError: + * @ctx: an XML parser context + * @msg: the message to display/transmit + * @...: extra parameters for the message display + * + * Display and format an error messages, gives file, line, position and + * extra parameters. + */ +static void XMLCDECL +xmlHTMLError(void *ctx, const char *msg, ...) +{ + xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; + xmlParserInputPtr input; + va_list args; + int len; + + buffer[0] = 0; + input = ctxt->input; + if ((input != NULL) && (input->filename == NULL) && (ctxt->inputNr > 1)) { + input = ctxt->inputTab[ctxt->inputNr - 2]; + } + + xmlHTMLPrintFileInfo(input); + + xmlGenericError(xmlGenericErrorContext, "error: "); + va_start(args, msg); + len = strlen(buffer); + vsnprintf(&buffer[len], sizeof(buffer) - len, msg, args); + va_end(args); + xmlHTMLEncodeSend(); + xmlGenericError(xmlGenericErrorContext, "

\n"); + + xmlHTMLPrintFileContext(input); + xmlHTMLEncodeSend(); +} + +/** + * xmlHTMLWarning: + * @ctx: an XML parser context + * @msg: the message to display/transmit + * @...: extra parameters for the message display + * + * Display and format a warning messages, gives file, line, position and + * extra parameters. + */ +static void XMLCDECL +xmlHTMLWarning(void *ctx, const char *msg, ...) +{ + xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; + xmlParserInputPtr input; + va_list args; + int len; + + buffer[0] = 0; + input = ctxt->input; + if ((input != NULL) && (input->filename == NULL) && (ctxt->inputNr > 1)) { + input = ctxt->inputTab[ctxt->inputNr - 2]; + } + + + xmlHTMLPrintFileInfo(input); + + xmlGenericError(xmlGenericErrorContext, "warning: "); + va_start(args, msg); + len = strlen(buffer); + vsnprintf(&buffer[len], sizeof(buffer) - len, msg, args); + va_end(args); + xmlHTMLEncodeSend(); + xmlGenericError(xmlGenericErrorContext, "

\n"); + + xmlHTMLPrintFileContext(input); + xmlHTMLEncodeSend(); +} + +/** + * xmlHTMLValidityError: + * @ctx: an XML parser context + * @msg: the message to display/transmit + * @...: extra parameters for the message display + * + * Display and format an validity error messages, gives file, + * line, position and extra parameters. + */ +static void XMLCDECL +xmlHTMLValidityError(void *ctx, const char *msg, ...) +{ + xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; + xmlParserInputPtr input; + va_list args; + int len; + + buffer[0] = 0; + input = ctxt->input; + if ((input->filename == NULL) && (ctxt->inputNr > 1)) + input = ctxt->inputTab[ctxt->inputNr - 2]; + + xmlHTMLPrintFileInfo(input); + + xmlGenericError(xmlGenericErrorContext, "validity error: "); + len = strlen(buffer); + va_start(args, msg); + vsnprintf(&buffer[len], sizeof(buffer) - len, msg, args); + va_end(args); + xmlHTMLEncodeSend(); + xmlGenericError(xmlGenericErrorContext, "

\n"); + + xmlHTMLPrintFileContext(input); + xmlHTMLEncodeSend(); + progresult = XMLLINT_ERR_VALID; +} + +/** + * xmlHTMLValidityWarning: + * @ctx: an XML parser context + * @msg: the message to display/transmit + * @...: extra parameters for the message display + * + * Display and format a validity warning messages, gives file, line, + * position and extra parameters. + */ +static void XMLCDECL +xmlHTMLValidityWarning(void *ctx, const char *msg, ...) +{ + xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; + xmlParserInputPtr input; + va_list args; + int len; + + buffer[0] = 0; + input = ctxt->input; + if ((input->filename == NULL) && (ctxt->inputNr > 1)) + input = ctxt->inputTab[ctxt->inputNr - 2]; + + xmlHTMLPrintFileInfo(input); + + xmlGenericError(xmlGenericErrorContext, "validity warning: "); + va_start(args, msg); + len = strlen(buffer); + vsnprintf(&buffer[len], sizeof(buffer) - len, msg, args); + va_end(args); + xmlHTMLEncodeSend(); + xmlGenericError(xmlGenericErrorContext, "

\n"); + + xmlHTMLPrintFileContext(input); + xmlHTMLEncodeSend(); +} + +/************************************************************************ + * * + * Shell Interface * + * * + ************************************************************************/ +#ifdef LIBXML_DEBUG_ENABLED +#ifdef LIBXML_XPATH_ENABLED +/** + * xmlShellReadline: + * @prompt: the prompt value + * + * Read a string + * + * Returns a pointer to it or NULL on EOF the caller is expected to + * free the returned string. + */ +static char * +xmlShellReadline(char *prompt) { +#ifdef HAVE_LIBREADLINE + char *line_read; + + /* Get a line from the user. */ + line_read = readline (prompt); + + /* If the line has any text in it, save it on the history. */ + if (line_read && *line_read) + add_history (line_read); + + return (line_read); +#else + char line_read[501]; + char *ret; + int len; + + if (prompt != NULL) + fprintf(stdout, "%s", prompt); + if (!fgets(line_read, 500, stdin)) + return(NULL); + line_read[500] = 0; + len = strlen(line_read); + ret = (char *) malloc(len + 1); + if (ret != NULL) { + memcpy (ret, line_read, len + 1); + } + return(ret); +#endif +} +#endif /* LIBXML_XPATH_ENABLED */ +#endif /* LIBXML_DEBUG_ENABLED */ + +/************************************************************************ + * * + * I/O Interfaces * + * * + ************************************************************************/ + +static int myRead(FILE *f, char * buf, int len) { + return(fread(buf, 1, len, f)); +} +static void myClose(FILE *f) { + if (f != stdin) { + fclose(f); + } +} + +/************************************************************************ + * * + * SAX based tests * + * * + ************************************************************************/ + +/* + * empty SAX block + */ +static xmlSAXHandler emptySAXHandlerStruct = { + NULL, /* internalSubset */ + NULL, /* isStandalone */ + NULL, /* hasInternalSubset */ + NULL, /* hasExternalSubset */ + NULL, /* resolveEntity */ + NULL, /* getEntity */ + NULL, /* entityDecl */ + NULL, /* notationDecl */ + NULL, /* attributeDecl */ + NULL, /* elementDecl */ + NULL, /* unparsedEntityDecl */ + NULL, /* setDocumentLocator */ + NULL, /* startDocument */ + NULL, /* endDocument */ + NULL, /* startElement */ + NULL, /* endElement */ + NULL, /* reference */ + NULL, /* characters */ + NULL, /* ignorableWhitespace */ + NULL, /* processingInstruction */ + NULL, /* comment */ + NULL, /* xmlParserWarning */ + NULL, /* xmlParserError */ + NULL, /* xmlParserError */ + NULL, /* getParameterEntity */ + NULL, /* cdataBlock; */ + NULL, /* externalSubset; */ + XML_SAX2_MAGIC, + NULL, + NULL, /* startElementNs */ + NULL, /* endElementNs */ + NULL /* xmlStructuredErrorFunc */ +}; + +static xmlSAXHandlerPtr emptySAXHandler = &emptySAXHandlerStruct; +extern xmlSAXHandlerPtr debugSAXHandler; +static int callbacks; + +/** + * isStandaloneDebug: + * @ctxt: An XML parser context + * + * Is this document tagged standalone ? + * + * Returns 1 if true + */ +static int +isStandaloneDebug(void *ctx ATTRIBUTE_UNUSED) +{ + callbacks++; + if (noout) + return(0); + fprintf(stdout, "SAX.isStandalone()\n"); + return(0); +} + +/** + * hasInternalSubsetDebug: + * @ctxt: An XML parser context + * + * Does this document has an internal subset + * + * Returns 1 if true + */ +static int +hasInternalSubsetDebug(void *ctx ATTRIBUTE_UNUSED) +{ + callbacks++; + if (noout) + return(0); + fprintf(stdout, "SAX.hasInternalSubset()\n"); + return(0); +} + +/** + * hasExternalSubsetDebug: + * @ctxt: An XML parser context + * + * Does this document has an external subset + * + * Returns 1 if true + */ +static int +hasExternalSubsetDebug(void *ctx ATTRIBUTE_UNUSED) +{ + callbacks++; + if (noout) + return(0); + fprintf(stdout, "SAX.hasExternalSubset()\n"); + return(0); +} + +/** + * internalSubsetDebug: + * @ctxt: An XML parser context + * + * Does this document has an internal subset + */ +static void +internalSubsetDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, + const xmlChar *ExternalID, const xmlChar *SystemID) +{ + callbacks++; + if (noout) + return; + fprintf(stdout, "SAX.internalSubset(%s,", name); + if (ExternalID == NULL) + fprintf(stdout, " ,"); + else + fprintf(stdout, " %s,", ExternalID); + if (SystemID == NULL) + fprintf(stdout, " )\n"); + else + fprintf(stdout, " %s)\n", SystemID); +} + +/** + * externalSubsetDebug: + * @ctxt: An XML parser context + * + * Does this document has an external subset + */ +static void +externalSubsetDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, + const xmlChar *ExternalID, const xmlChar *SystemID) +{ + callbacks++; + if (noout) + return; + fprintf(stdout, "SAX.externalSubset(%s,", name); + if (ExternalID == NULL) + fprintf(stdout, " ,"); + else + fprintf(stdout, " %s,", ExternalID); + if (SystemID == NULL) + fprintf(stdout, " )\n"); + else + fprintf(stdout, " %s)\n", SystemID); +} + +/** + * resolveEntityDebug: + * @ctxt: An XML parser context + * @publicId: The public ID of the entity + * @systemId: The system ID of the entity + * + * Special entity resolver, better left to the parser, it has + * more context than the application layer. + * The default behaviour is to NOT resolve the entities, in that case + * the ENTITY_REF nodes are built in the structure (and the parameter + * values). + * + * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour. + */ +static xmlParserInputPtr +resolveEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *publicId, const xmlChar *systemId) +{ + callbacks++; + if (noout) + return(NULL); + /* xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; */ + + + fprintf(stdout, "SAX.resolveEntity("); + if (publicId != NULL) + fprintf(stdout, "%s", (char *)publicId); + else + fprintf(stdout, " "); + if (systemId != NULL) + fprintf(stdout, ", %s)\n", (char *)systemId); + else + fprintf(stdout, ", )\n"); + return(NULL); +} + +/** + * getEntityDebug: + * @ctxt: An XML parser context + * @name: The entity name + * + * Get an entity by name + * + * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour. + */ +static xmlEntityPtr +getEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name) +{ + callbacks++; + if (noout) + return(NULL); + fprintf(stdout, "SAX.getEntity(%s)\n", name); + return(NULL); +} + +/** + * getParameterEntityDebug: + * @ctxt: An XML parser context + * @name: The entity name + * + * Get a parameter entity by name + * + * Returns the xmlParserInputPtr + */ +static xmlEntityPtr +getParameterEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name) +{ + callbacks++; + if (noout) + return(NULL); + fprintf(stdout, "SAX.getParameterEntity(%s)\n", name); + return(NULL); +} + + +/** + * entityDeclDebug: + * @ctxt: An XML parser context + * @name: the entity name + * @type: the entity type + * @publicId: The public ID of the entity + * @systemId: The system ID of the entity + * @content: the entity value (without processing). + * + * An entity definition has been parsed + */ +static void +entityDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, int type, + const xmlChar *publicId, const xmlChar *systemId, xmlChar *content) +{ +const xmlChar *nullstr = BAD_CAST "(null)"; + /* not all libraries handle printing null pointers nicely */ + if (publicId == NULL) + publicId = nullstr; + if (systemId == NULL) + systemId = nullstr; + if (content == NULL) + content = (xmlChar *)nullstr; + callbacks++; + if (noout) + return; + fprintf(stdout, "SAX.entityDecl(%s, %d, %s, %s, %s)\n", + name, type, publicId, systemId, content); +} + +/** + * attributeDeclDebug: + * @ctxt: An XML parser context + * @name: the attribute name + * @type: the attribute type + * + * An attribute definition has been parsed + */ +static void +attributeDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar * elem, + const xmlChar * name, int type, int def, + const xmlChar * defaultValue, xmlEnumerationPtr tree) +{ + callbacks++; + if (noout) + return; + if (defaultValue == NULL) + fprintf(stdout, "SAX.attributeDecl(%s, %s, %d, %d, NULL, ...)\n", + elem, name, type, def); + else + fprintf(stdout, "SAX.attributeDecl(%s, %s, %d, %d, %s, ...)\n", + elem, name, type, def, defaultValue); + xmlFreeEnumeration(tree); +} + +/** + * elementDeclDebug: + * @ctxt: An XML parser context + * @name: the element name + * @type: the element type + * @content: the element value (without processing). + * + * An element definition has been parsed + */ +static void +elementDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, int type, + xmlElementContentPtr content ATTRIBUTE_UNUSED) +{ + callbacks++; + if (noout) + return; + fprintf(stdout, "SAX.elementDecl(%s, %d, ...)\n", + name, type); +} + +/** + * notationDeclDebug: + * @ctxt: An XML parser context + * @name: The name of the notation + * @publicId: The public ID of the entity + * @systemId: The system ID of the entity + * + * What to do when a notation declaration has been parsed. + */ +static void +notationDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, + const xmlChar *publicId, const xmlChar *systemId) +{ + callbacks++; + if (noout) + return; + fprintf(stdout, "SAX.notationDecl(%s, %s, %s)\n", + (char *) name, (char *) publicId, (char *) systemId); +} + +/** + * unparsedEntityDeclDebug: + * @ctxt: An XML parser context + * @name: The name of the entity + * @publicId: The public ID of the entity + * @systemId: The system ID of the entity + * @notationName: the name of the notation + * + * What to do when an unparsed entity declaration is parsed + */ +static void +unparsedEntityDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, + const xmlChar *publicId, const xmlChar *systemId, + const xmlChar *notationName) +{ +const xmlChar *nullstr = BAD_CAST "(null)"; + + if (publicId == NULL) + publicId = nullstr; + if (systemId == NULL) + systemId = nullstr; + if (notationName == NULL) + notationName = nullstr; + callbacks++; + if (noout) + return; + fprintf(stdout, "SAX.unparsedEntityDecl(%s, %s, %s, %s)\n", + (char *) name, (char *) publicId, (char *) systemId, + (char *) notationName); +} + +/** + * setDocumentLocatorDebug: + * @ctxt: An XML parser context + * @loc: A SAX Locator + * + * Receive the document locator at startup, actually xmlDefaultSAXLocator + * Everything is available on the context, so this is useless in our case. + */ +static void +setDocumentLocatorDebug(void *ctx ATTRIBUTE_UNUSED, xmlSAXLocatorPtr loc ATTRIBUTE_UNUSED) +{ + callbacks++; + if (noout) + return; + fprintf(stdout, "SAX.setDocumentLocator()\n"); +} + +/** + * startDocumentDebug: + * @ctxt: An XML parser context + * + * called when the document start being processed. + */ +static void +startDocumentDebug(void *ctx ATTRIBUTE_UNUSED) +{ + callbacks++; + if (noout) + return; + fprintf(stdout, "SAX.startDocument()\n"); +} + +/** + * endDocumentDebug: + * @ctxt: An XML parser context + * + * called when the document end has been detected. + */ +static void +endDocumentDebug(void *ctx ATTRIBUTE_UNUSED) +{ + callbacks++; + if (noout) + return; + fprintf(stdout, "SAX.endDocument()\n"); +} + +/** + * startElementDebug: + * @ctxt: An XML parser context + * @name: The element name + * + * called when an opening tag has been processed. + */ +static void +startElementDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, const xmlChar **atts) +{ + int i; + + callbacks++; + if (noout) + return; + fprintf(stdout, "SAX.startElement(%s", (char *) name); + if (atts != NULL) { + for (i = 0;(atts[i] != NULL);i++) { + fprintf(stdout, ", %s='", atts[i++]); + if (atts[i] != NULL) + fprintf(stdout, "%s'", atts[i]); + } + } + fprintf(stdout, ")\n"); +} + +/** + * endElementDebug: + * @ctxt: An XML parser context + * @name: The element name + * + * called when the end of an element has been detected. + */ +static void +endElementDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name) +{ + callbacks++; + if (noout) + return; + fprintf(stdout, "SAX.endElement(%s)\n", (char *) name); +} + +/** + * charactersDebug: + * @ctxt: An XML parser context + * @ch: a xmlChar string + * @len: the number of xmlChar + * + * receiving some chars from the parser. + * Question: how much at a time ??? + */ +static void +charactersDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len) +{ + char out[40]; + int i; + + callbacks++; + if (noout) + return; + for (i = 0;(i 0) { + fprintf(stderr, "%s fails to validate\n", filename); + progresult = XMLLINT_ERR_VALID; + } else { + fprintf(stderr, "%s validation generated an internal error\n", + filename); + progresult = XMLLINT_ERR_VALID; + } + } + xmlSchemaFreeValidCtxt(vctxt); + } else +#endif + { + /* + * Create the parser context amd hook the input + */ + ctxt = xmlNewParserCtxt(); + if (ctxt == NULL) { + xmlFreeParserInputBuffer(buf); + goto error; + } + old_sax = ctxt->sax; + ctxt->sax = handler; + ctxt->userData = (void *) user_data; + inputStream = xmlNewIOInputStream(ctxt, buf, XML_CHAR_ENCODING_NONE); + if (inputStream == NULL) { + xmlFreeParserInputBuffer(buf); + goto error; + } + inputPush(ctxt, inputStream); + + /* do the parsing */ + xmlParseDocument(ctxt); + + if (ctxt->myDoc != NULL) { + fprintf(stderr, "SAX generated a doc !\n"); + xmlFreeDoc(ctxt->myDoc); + ctxt->myDoc = NULL; + } + } + +error: + if (ctxt != NULL) { + ctxt->sax = old_sax; + xmlFreeParserCtxt(ctxt); + } +} + +/************************************************************************ + * * + * Stream Test processing * + * * + ************************************************************************/ +#ifdef LIBXML_READER_ENABLED +static void processNode(xmlTextReaderPtr reader) { + const xmlChar *name, *value; + int type, empty; + + type = xmlTextReaderNodeType(reader); + empty = xmlTextReaderIsEmptyElement(reader); + + if (debug) { + name = xmlTextReaderConstName(reader); + if (name == NULL) + name = BAD_CAST "--"; + + value = xmlTextReaderConstValue(reader); + + + printf("%d %d %s %d %d", + xmlTextReaderDepth(reader), + type, + name, + empty, + xmlTextReaderHasValue(reader)); + if (value == NULL) + printf("\n"); + else { + printf(" %s\n", value); + } + } +#ifdef LIBXML_PATTERN_ENABLED + if (patternc) { + xmlChar *path = NULL; + int match = -1; + + if (type == XML_READER_TYPE_ELEMENT) { + /* do the check only on element start */ + match = xmlPatternMatch(patternc, xmlTextReaderCurrentNode(reader)); + + if (match) { + path = xmlGetNodePath(xmlTextReaderCurrentNode(reader)); + printf("Node %s matches pattern %s\n", path, pattern); + } + } + if (patstream != NULL) { + int ret; + + if (type == XML_READER_TYPE_ELEMENT) { + ret = xmlStreamPush(patstream, + xmlTextReaderConstLocalName(reader), + xmlTextReaderConstNamespaceUri(reader)); + if (ret < 0) { + fprintf(stderr, "xmlStreamPush() failure\n"); + xmlFreeStreamCtxt(patstream); + patstream = NULL; + } else if (ret != match) { + if (path == NULL) { + path = xmlGetNodePath( + xmlTextReaderCurrentNode(reader)); + } + fprintf(stderr, + "xmlPatternMatch and xmlStreamPush disagree\n"); + fprintf(stderr, + " pattern %s node %s\n", + pattern, path); + } + + + } + if ((type == XML_READER_TYPE_END_ELEMENT) || + ((type == XML_READER_TYPE_ELEMENT) && (empty))) { + ret = xmlStreamPop(patstream); + if (ret < 0) { + fprintf(stderr, "xmlStreamPop() failure\n"); + xmlFreeStreamCtxt(patstream); + patstream = NULL; + } + } + } + if (path != NULL) + xmlFree(path); + } +#endif +} + +static void streamFile(char *filename) { + xmlTextReaderPtr reader; + int ret; +#ifdef HAVE_SYS_MMAN_H + int fd = -1; + struct stat info; + const char *base = NULL; + xmlParserInputBufferPtr input = NULL; + + if (memory) { + if (stat(filename, &info) < 0) + return; + if ((fd = open(filename, O_RDONLY)) < 0) + return; + base = mmap(NULL, info.st_size, PROT_READ, MAP_SHARED, fd, 0) ; + if (base == (void *) MAP_FAILED) + return; + + reader = xmlReaderForMemory(base, info.st_size, filename, + NULL, options); + } else +#endif + reader = xmlReaderForFile(filename, NULL, options); +#ifdef LIBXML_PATTERN_ENABLED + if (pattern != NULL) { + patternc = xmlPatterncompile((const xmlChar *) pattern, NULL, 0, NULL); + if (patternc == NULL) { + xmlGenericError(xmlGenericErrorContext, + "Pattern %s failed to compile\n", pattern); + progresult = XMLLINT_ERR_SCHEMAPAT; + pattern = NULL; + } + } + if (patternc != NULL) { + patstream = xmlPatternGetStreamCtxt(patternc); + if (patstream != NULL) { + ret = xmlStreamPush(patstream, NULL, NULL); + if (ret < 0) { + fprintf(stderr, "xmlStreamPush() failure\n"); + xmlFreeStreamCtxt(patstream); + patstream = NULL; + } + } + } +#endif + + + if (reader != NULL) { +#ifdef LIBXML_VALID_ENABLED + if (valid) + xmlTextReaderSetParserProp(reader, XML_PARSER_VALIDATE, 1); + else +#endif /* LIBXML_VALID_ENABLED */ + xmlTextReaderSetParserProp(reader, XML_PARSER_LOADDTD, 1); +#ifdef LIBXML_SCHEMAS_ENABLED + if (relaxng != NULL) { + if ((timing) && (!repeat)) { + startTimer(); + } + ret = xmlTextReaderRelaxNGValidate(reader, relaxng); + if (ret < 0) { + xmlGenericError(xmlGenericErrorContext, + "Relax-NG schema %s failed to compile\n", relaxng); + progresult = XMLLINT_ERR_SCHEMACOMP; + relaxng = NULL; + } + if ((timing) && (!repeat)) { + endTimer("Compiling the schemas"); + } + } + if (schema != NULL) { + if ((timing) && (!repeat)) { + startTimer(); + } + ret = xmlTextReaderSchemaValidate(reader, schema); + if (ret < 0) { + xmlGenericError(xmlGenericErrorContext, + "XSD schema %s failed to compile\n", schema); + progresult = XMLLINT_ERR_SCHEMACOMP; + schema = NULL; + } + if ((timing) && (!repeat)) { + endTimer("Compiling the schemas"); + } + } +#endif + + /* + * Process all nodes in sequence + */ + if ((timing) && (!repeat)) { + startTimer(); + } + ret = xmlTextReaderRead(reader); + while (ret == 1) { + if ((debug) +#ifdef LIBXML_PATTERN_ENABLED + || (patternc) +#endif + ) + processNode(reader); + ret = xmlTextReaderRead(reader); + } + if ((timing) && (!repeat)) { +#ifdef LIBXML_SCHEMAS_ENABLED + if (relaxng != NULL) + endTimer("Parsing and validating"); + else +#endif +#ifdef LIBXML_VALID_ENABLED + if (valid) + endTimer("Parsing and validating"); + else +#endif + endTimer("Parsing"); + } + +#ifdef LIBXML_VALID_ENABLED + if (valid) { + if (xmlTextReaderIsValid(reader) != 1) { + xmlGenericError(xmlGenericErrorContext, + "Document %s does not validate\n", filename); + progresult = XMLLINT_ERR_VALID; + } + } +#endif /* LIBXML_VALID_ENABLED */ +#ifdef LIBXML_SCHEMAS_ENABLED + if ((relaxng != NULL) || (schema != NULL)) { + if (xmlTextReaderIsValid(reader) != 1) { + fprintf(stderr, "%s fails to validate\n", filename); + progresult = XMLLINT_ERR_VALID; + } else { + fprintf(stderr, "%s validates\n", filename); + } + } +#endif + /* + * Done, cleanup and status + */ + xmlFreeTextReader(reader); + if (ret != 0) { + fprintf(stderr, "%s : failed to parse\n", filename); + progresult = XMLLINT_ERR_UNCLASS; + } + } else { + fprintf(stderr, "Unable to open %s\n", filename); + progresult = XMLLINT_ERR_UNCLASS; + } +#ifdef LIBXML_PATTERN_ENABLED + if (patstream != NULL) { + xmlFreeStreamCtxt(patstream); + patstream = NULL; + } +#endif +#ifdef HAVE_SYS_MMAN_H + if (memory) { + xmlFreeParserInputBuffer(input); + munmap((char *) base, info.st_size); + close(fd); + } +#endif +} + +static void walkDoc(xmlDocPtr doc) { + xmlTextReaderPtr reader; + int ret; + +#ifdef LIBXML_PATTERN_ENABLED + xmlNodePtr root; + const xmlChar *namespaces[22]; + int i; + xmlNsPtr ns; + + root = xmlDocGetRootElement(doc); + for (ns = root->nsDef, i = 0;ns != NULL && i < 20;ns=ns->next) { + namespaces[i++] = ns->href; + namespaces[i++] = ns->prefix; + } + namespaces[i++] = NULL; + namespaces[i++] = NULL; + + if (pattern != NULL) { + patternc = xmlPatterncompile((const xmlChar *) pattern, doc->dict, + 0, &namespaces[0]); + if (patternc == NULL) { + xmlGenericError(xmlGenericErrorContext, + "Pattern %s failed to compile\n", pattern); + progresult = XMLLINT_ERR_SCHEMAPAT; + pattern = NULL; + } + } + if (patternc != NULL) { + patstream = xmlPatternGetStreamCtxt(patternc); + if (patstream != NULL) { + ret = xmlStreamPush(patstream, NULL, NULL); + if (ret < 0) { + fprintf(stderr, "xmlStreamPush() failure\n"); + xmlFreeStreamCtxt(patstream); + patstream = NULL; + } + } + } +#endif /* LIBXML_PATTERN_ENABLED */ + reader = xmlReaderWalker(doc); + if (reader != NULL) { + if ((timing) && (!repeat)) { + startTimer(); + } + ret = xmlTextReaderRead(reader); + while (ret == 1) { + if ((debug) +#ifdef LIBXML_PATTERN_ENABLED + || (patternc) +#endif + ) + processNode(reader); + ret = xmlTextReaderRead(reader); + } + if ((timing) && (!repeat)) { + endTimer("walking through the doc"); + } + xmlFreeTextReader(reader); + if (ret != 0) { + fprintf(stderr, "failed to walk through the doc\n"); + progresult = XMLLINT_ERR_UNCLASS; + } + } else { + fprintf(stderr, "Failed to crate a reader from the document\n"); + progresult = XMLLINT_ERR_UNCLASS; + } +#ifdef LIBXML_PATTERN_ENABLED + if (patstream != NULL) { + xmlFreeStreamCtxt(patstream); + patstream = NULL; + } +#endif +} +#endif /* LIBXML_READER_ENABLED */ + +/************************************************************************ + * * + * Tree Test processing * + * * + ************************************************************************/ +static void parseAndPrintFile(char *filename, xmlParserCtxtPtr rectxt) { + xmlDocPtr doc = NULL; +#ifdef LIBXML_TREE_ENABLED + xmlDocPtr tmp; +#endif /* LIBXML_TREE_ENABLED */ + + if ((timing) && (!repeat)) + startTimer(); + + +#ifdef LIBXML_TREE_ENABLED + if (filename == NULL) { + if (generate) { + xmlNodePtr n; + + doc = xmlNewDoc(BAD_CAST "1.0"); + n = xmlNewDocNode(doc, NULL, BAD_CAST "info", NULL); + xmlNodeSetContent(n, BAD_CAST "abc"); + xmlDocSetRootElement(doc, n); + } + } +#endif /* LIBXML_TREE_ENABLED */ +#ifdef LIBXML_HTML_ENABLED +#ifdef LIBXML_PUSH_ENABLED + else if ((html) && (push)) { + FILE *f; + +#if defined(_WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__) + f = fopen(filename, "rb"); +#else + f = fopen(filename, "r"); +#endif + if (f != NULL) { + int res, size = 3; + char chars[4096]; + htmlParserCtxtPtr ctxt; + + /* if (repeat) */ + size = 4096; + res = fread(chars, 1, 4, f); + if (res > 0) { + ctxt = htmlCreatePushParserCtxt(NULL, NULL, + chars, res, filename, XML_CHAR_ENCODING_NONE); + while ((res = fread(chars, 1, size, f)) > 0) { + htmlParseChunk(ctxt, chars, res, 0); + } + htmlParseChunk(ctxt, chars, 0, 1); + doc = ctxt->myDoc; + htmlFreeParserCtxt(ctxt); + } + fclose(f); + } + } +#endif /* LIBXML_PUSH_ENABLED */ + else if (html) { + doc = htmlReadFile(filename, NULL, options); + } +#endif /* LIBXML_HTML_ENABLED */ + else { +#ifdef LIBXML_PUSH_ENABLED + /* + * build an XML tree from a string; + */ + if (push) { + FILE *f; + + /* '-' Usually means stdin - */ + if ((filename[0] == '-') && (filename[1] == 0)) { + f = stdin; + } else { +#if defined(_WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__) + f = fopen(filename, "rb"); +#else + f = fopen(filename, "r"); +#endif + } + if (f != NULL) { + int ret; + int res, size = 1024; + char chars[1024]; + xmlParserCtxtPtr ctxt; + + /* if (repeat) size = 1024; */ + res = fread(chars, 1, 4, f); + if (res > 0) { + ctxt = xmlCreatePushParserCtxt(NULL, NULL, + chars, res, filename); + xmlCtxtUseOptions(ctxt, options); + while ((res = fread(chars, 1, size, f)) > 0) { + xmlParseChunk(ctxt, chars, res, 0); + } + xmlParseChunk(ctxt, chars, 0, 1); + doc = ctxt->myDoc; + ret = ctxt->wellFormed; + xmlFreeParserCtxt(ctxt); + if (!ret) { + xmlFreeDoc(doc); + doc = NULL; + } + } + } + } else +#endif /* LIBXML_PUSH_ENABLED */ + if (testIO) { + if ((filename[0] == '-') && (filename[1] == 0)) { + doc = xmlReadFd(0, NULL, NULL, options); + } else { + FILE *f; + +#if defined(_WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__) + f = fopen(filename, "rb"); +#else + f = fopen(filename, "r"); +#endif + if (f != NULL) { + if (rectxt == NULL) + doc = xmlReadIO((xmlInputReadCallback) myRead, + (xmlInputCloseCallback) myClose, f, + filename, NULL, options); + else + doc = xmlCtxtReadIO(rectxt, + (xmlInputReadCallback) myRead, + (xmlInputCloseCallback) myClose, f, + filename, NULL, options); + } else + doc = NULL; + } + } else if (htmlout) { + xmlParserCtxtPtr ctxt; + + if (rectxt == NULL) + ctxt = xmlNewParserCtxt(); + else + ctxt = rectxt; + if (ctxt == NULL) { + doc = NULL; + } else { + ctxt->sax->error = xmlHTMLError; + ctxt->sax->warning = xmlHTMLWarning; + ctxt->vctxt.error = xmlHTMLValidityError; + ctxt->vctxt.warning = xmlHTMLValidityWarning; + + doc = xmlCtxtReadFile(ctxt, filename, NULL, options); + + if (rectxt == NULL) + xmlFreeParserCtxt(ctxt); + } +#ifdef HAVE_SYS_MMAN_H + } else if (memory) { + int fd; + struct stat info; + const char *base; + if (stat(filename, &info) < 0) + return; + if ((fd = open(filename, O_RDONLY)) < 0) + return; + base = mmap(NULL, info.st_size, PROT_READ, MAP_SHARED, fd, 0) ; + if (base == (void *) MAP_FAILED) + return; + + if (rectxt == NULL) + doc = xmlReadMemory((char *) base, info.st_size, + filename, NULL, options); + else + doc = xmlCtxtReadMemory(rectxt, (char *) base, info.st_size, + filename, NULL, options); + + munmap((char *) base, info.st_size); +#endif +#ifdef LIBXML_VALID_ENABLED + } else if (valid) { + xmlParserCtxtPtr ctxt = NULL; + + if (rectxt == NULL) + ctxt = xmlNewParserCtxt(); + else + ctxt = rectxt; + if (ctxt == NULL) { + doc = NULL; + } else { + doc = xmlCtxtReadFile(ctxt, filename, NULL, options); + + if (ctxt->valid == 0) + progresult = XMLLINT_ERR_RDFILE; + if (rectxt == NULL) + xmlFreeParserCtxt(ctxt); + } +#endif /* LIBXML_VALID_ENABLED */ + } else { + if (rectxt != NULL) + doc = xmlCtxtReadFile(rectxt, filename, NULL, options); + else { +#ifdef LIBXML_SAX1_ENABLED + if (sax1) + doc = xmlParseFile(filename); + else +#endif /* LIBXML_SAX1_ENABLED */ + doc = xmlReadFile(filename, NULL, options); + } + } + } + + /* + * If we don't have a document we might as well give up. Do we + * want an error message here? */ + if (doc == NULL) { + progresult = XMLLINT_ERR_UNCLASS; + return; + } + + if ((timing) && (!repeat)) { + endTimer("Parsing"); + } + + /* + * Remove DOCTYPE nodes + */ + if (dropdtd) { + xmlDtdPtr dtd; + + dtd = xmlGetIntSubset(doc); + if (dtd != NULL) { + xmlUnlinkNode((xmlNodePtr)dtd); + xmlFreeDtd(dtd); + } + } + +#ifdef LIBXML_XINCLUDE_ENABLED + if (xinclude) { + if ((timing) && (!repeat)) { + startTimer(); + } + if (xmlXIncludeProcessFlags(doc, options) < 0) + progresult = XMLLINT_ERR_UNCLASS; + if ((timing) && (!repeat)) { + endTimer("Xinclude processing"); + } + } +#endif + +#ifdef LIBXML_DEBUG_ENABLED +#ifdef LIBXML_XPATH_ENABLED + /* + * shell interaction + */ + if (shell) + xmlShell(doc, filename, xmlShellReadline, stdout); +#endif +#endif + +#ifdef LIBXML_TREE_ENABLED + /* + * test intermediate copy if needed. + */ + if (copy) { + tmp = doc; + if (timing) { + startTimer(); + } + doc = xmlCopyDoc(doc, 1); + if (timing) { + endTimer("Copying"); + } + if (timing) { + startTimer(); + } + xmlFreeDoc(tmp); + if (timing) { + endTimer("Freeing original"); + } + } +#endif /* LIBXML_TREE_ENABLED */ + +#ifdef LIBXML_VALID_ENABLED + if ((insert) && (!html)) { + const xmlChar* list[256]; + int nb, i; + xmlNodePtr node; + + if (doc->children != NULL) { + node = doc->children; + while ((node != NULL) && (node->last == NULL)) node = node->next; + if (node != NULL) { + nb = xmlValidGetValidElements(node->last, NULL, list, 256); + if (nb < 0) { + fprintf(stderr, "could not get valid list of elements\n"); + } else if (nb == 0) { + fprintf(stderr, "No element can be inserted under root\n"); + } else { + fprintf(stderr, "%d element types can be inserted under root:\n", + nb); + for (i = 0;i < nb;i++) { + fprintf(stderr, "%s\n", (char *) list[i]); + } + } + } + } + }else +#endif /* LIBXML_VALID_ENABLED */ +#ifdef LIBXML_READER_ENABLED + if (walker) { + walkDoc(doc); + } +#endif /* LIBXML_READER_ENABLED */ +#ifdef LIBXML_OUTPUT_ENABLED + if (noout == 0) { + int ret; + + /* + * print it. + */ +#ifdef LIBXML_DEBUG_ENABLED + if (!debug) { +#endif + if ((timing) && (!repeat)) { + startTimer(); + } +#ifdef LIBXML_HTML_ENABLED + if ((html) && (!xmlout)) { + if (compress) { + htmlSaveFile(output ? output : "-", doc); + } + else if (encoding != NULL) { + if ( format ) { + htmlSaveFileFormat(output ? output : "-", doc, encoding, 1); + } + else { + htmlSaveFileFormat(output ? output : "-", doc, encoding, 0); + } + } + else if (format) { + htmlSaveFileFormat(output ? output : "-", doc, NULL, 1); + } + else { + FILE *out; + if (output == NULL) + out = stdout; + else { + out = fopen(output,"wb"); + } + if (out != NULL) { + if (htmlDocDump(out, doc) < 0) + progresult = XMLLINT_ERR_OUT; + + if (output != NULL) + fclose(out); + } else { + fprintf(stderr, "failed to open %s\n", output); + progresult = XMLLINT_ERR_OUT; + } + } + if ((timing) && (!repeat)) { + endTimer("Saving"); + } + } else +#endif +#ifdef LIBXML_C14N_ENABLED + if (canonical) { + xmlChar *result = NULL; + int size; + + size = xmlC14NDocDumpMemory(doc, NULL, 0, NULL, 1, &result); + if (size >= 0) { + write(1, result, size); + xmlFree(result); + } else { + fprintf(stderr, "Failed to canonicalize\n"); + progresult = XMLLINT_ERR_OUT; + } + } else + if (exc_canonical) { + xmlChar *result = NULL; + int size; + + size = xmlC14NDocDumpMemory(doc, NULL, 1, NULL, 1, &result); + if (size >= 0) { + write(1, result, size); + xmlFree(result); + } else { + fprintf(stderr, "Failed to canonicalize\n"); + progresult = XMLLINT_ERR_OUT; + } + } else +#endif +#ifdef HAVE_SYS_MMAN_H + if (memory) { + xmlChar *result; + int len; + + if (encoding != NULL) { + if ( format ) { + xmlDocDumpFormatMemoryEnc(doc, &result, &len, encoding, 1); + } else { + xmlDocDumpMemoryEnc(doc, &result, &len, encoding); + } + } else { + if (format) + xmlDocDumpFormatMemory(doc, &result, &len, 1); + else + xmlDocDumpMemory(doc, &result, &len); + } + if (result == NULL) { + fprintf(stderr, "Failed to save\n"); + progresult = XMLLINT_ERR_OUT; + } else { + write(1, result, len); + xmlFree(result); + } + } else +#endif /* HAVE_SYS_MMAN_H */ + if (compress) { + xmlSaveFile(output ? output : "-", doc); + } + else if (encoding != NULL) { + if ( format ) { + ret = xmlSaveFormatFileEnc(output ? output : "-", doc, + encoding, 1); + } + else { + ret = xmlSaveFileEnc(output ? output : "-", doc, encoding); + } + if (ret < 0) { + fprintf(stderr, "failed save to %s\n", + output ? output : "-"); + progresult = XMLLINT_ERR_OUT; + } + } + else if (format) { + ret = xmlSaveFormatFile(output ? output : "-", doc, 1); + if (ret < 0) { + fprintf(stderr, "failed save to %s\n", + output ? output : "-"); + progresult = XMLLINT_ERR_OUT; + } + } + else { + FILE *out; + if (output == NULL) + out = stdout; + else { + out = fopen(output,"wb"); + } + if (out != NULL) { + if (xmlDocDump(out, doc) < 0) + progresult = XMLLINT_ERR_OUT; + + if (output != NULL) + fclose(out); + } else { + fprintf(stderr, "failed to open %s\n", output); + progresult = XMLLINT_ERR_OUT; + } + } + if ((timing) && (!repeat)) { + endTimer("Saving"); + } +#ifdef LIBXML_DEBUG_ENABLED + } else { + FILE *out; + if (output == NULL) + out = stdout; + else { + out = fopen(output,"wb"); + } + if (out != NULL) { + xmlDebugDumpDocument(out, doc); + + if (output != NULL) + fclose(out); + } else { + fprintf(stderr, "failed to open %s\n", output); + progresult = XMLLINT_ERR_OUT; + } + } +#endif + } +#endif /* LIBXML_OUTPUT_ENABLED */ + +#ifdef LIBXML_VALID_ENABLED + /* + * A posteriori validation test + */ + if ((dtdvalid != NULL) || (dtdvalidfpi != NULL)) { + xmlDtdPtr dtd; + + if ((timing) && (!repeat)) { + startTimer(); + } + if (dtdvalid != NULL) + dtd = xmlParseDTD(NULL, (const xmlChar *)dtdvalid); + else + dtd = xmlParseDTD((const xmlChar *)dtdvalidfpi, NULL); + if ((timing) && (!repeat)) { + endTimer("Parsing DTD"); + } + if (dtd == NULL) { + if (dtdvalid != NULL) + xmlGenericError(xmlGenericErrorContext, + "Could not parse DTD %s\n", dtdvalid); + else + xmlGenericError(xmlGenericErrorContext, + "Could not parse DTD %s\n", dtdvalidfpi); + progresult = XMLLINT_ERR_DTD; + } else { + xmlValidCtxtPtr cvp; + + if ((cvp = xmlNewValidCtxt()) == NULL) { + xmlGenericError(xmlGenericErrorContext, + "Couldn't allocate validation context\n"); + exit(-1); + } + cvp->userData = (void *) stderr; + cvp->error = (xmlValidityErrorFunc) fprintf; + cvp->warning = (xmlValidityWarningFunc) fprintf; + + if ((timing) && (!repeat)) { + startTimer(); + } + if (!xmlValidateDtd(cvp, doc, dtd)) { + if (dtdvalid != NULL) + xmlGenericError(xmlGenericErrorContext, + "Document %s does not validate against %s\n", + filename, dtdvalid); + else + xmlGenericError(xmlGenericErrorContext, + "Document %s does not validate against %s\n", + filename, dtdvalidfpi); + progresult = XMLLINT_ERR_VALID; + } + if ((timing) && (!repeat)) { + endTimer("Validating against DTD"); + } + xmlFreeValidCtxt(cvp); + xmlFreeDtd(dtd); + } + } else if (postvalid) { + xmlValidCtxtPtr cvp; + + if ((cvp = xmlNewValidCtxt()) == NULL) { + xmlGenericError(xmlGenericErrorContext, + "Couldn't allocate validation context\n"); + exit(-1); + } + + if ((timing) && (!repeat)) { + startTimer(); + } + cvp->userData = (void *) stderr; + cvp->error = (xmlValidityErrorFunc) fprintf; + cvp->warning = (xmlValidityWarningFunc) fprintf; + if (!xmlValidateDocument(cvp, doc)) { + xmlGenericError(xmlGenericErrorContext, + "Document %s does not validate\n", filename); + progresult = XMLLINT_ERR_VALID; + } + if ((timing) && (!repeat)) { + endTimer("Validating"); + } + xmlFreeValidCtxt(cvp); + } +#endif /* LIBXML_VALID_ENABLED */ +#ifdef LIBXML_SCHEMATRON_ENABLED + if (wxschematron != NULL) { + xmlSchematronValidCtxtPtr ctxt; + int ret; + int flag; + + if ((timing) && (!repeat)) { + startTimer(); + } + + if (debug) + flag = XML_SCHEMATRON_OUT_XML; + else + flag = XML_SCHEMATRON_OUT_TEXT; + if (noout) + flag |= XML_SCHEMATRON_OUT_QUIET; + ctxt = xmlSchematronNewValidCtxt(wxschematron, flag); +#if 0 + xmlSchematronSetValidErrors(ctxt, + (xmlSchematronValidityErrorFunc) fprintf, + (xmlSchematronValidityWarningFunc) fprintf, + stderr); +#endif + ret = xmlSchematronValidateDoc(ctxt, doc); + if (ret == 0) { + fprintf(stderr, "%s validates\n", filename); + } else if (ret > 0) { + fprintf(stderr, "%s fails to validate\n", filename); + progresult = XMLLINT_ERR_VALID; + } else { + fprintf(stderr, "%s validation generated an internal error\n", + filename); + progresult = XMLLINT_ERR_VALID; + } + xmlSchematronFreeValidCtxt(ctxt); + if ((timing) && (!repeat)) { + endTimer("Validating"); + } + } +#endif +#ifdef LIBXML_SCHEMAS_ENABLED + if (relaxngschemas != NULL) { + xmlRelaxNGValidCtxtPtr ctxt; + int ret; + + if ((timing) && (!repeat)) { + startTimer(); + } + + ctxt = xmlRelaxNGNewValidCtxt(relaxngschemas); + xmlRelaxNGSetValidErrors(ctxt, + (xmlRelaxNGValidityErrorFunc) fprintf, + (xmlRelaxNGValidityWarningFunc) fprintf, + stderr); + ret = xmlRelaxNGValidateDoc(ctxt, doc); + if (ret == 0) { + fprintf(stderr, "%s validates\n", filename); + } else if (ret > 0) { + fprintf(stderr, "%s fails to validate\n", filename); + progresult = XMLLINT_ERR_VALID; + } else { + fprintf(stderr, "%s validation generated an internal error\n", + filename); + progresult = XMLLINT_ERR_VALID; + } + xmlRelaxNGFreeValidCtxt(ctxt); + if ((timing) && (!repeat)) { + endTimer("Validating"); + } + } else if (wxschemas != NULL) { + xmlSchemaValidCtxtPtr ctxt; + int ret; + + if ((timing) && (!repeat)) { + startTimer(); + } + + ctxt = xmlSchemaNewValidCtxt(wxschemas); + xmlSchemaSetValidErrors(ctxt, + (xmlSchemaValidityErrorFunc) fprintf, + (xmlSchemaValidityWarningFunc) fprintf, + stderr); + ret = xmlSchemaValidateDoc(ctxt, doc); + if (ret == 0) { + fprintf(stderr, "%s validates\n", filename); + } else if (ret > 0) { + fprintf(stderr, "%s fails to validate\n", filename); + progresult = XMLLINT_ERR_VALID; + } else { + fprintf(stderr, "%s validation generated an internal error\n", + filename); + progresult = XMLLINT_ERR_VALID; + } + xmlSchemaFreeValidCtxt(ctxt); + if ((timing) && (!repeat)) { + endTimer("Validating"); + } + } +#endif + +#ifdef LIBXML_DEBUG_ENABLED + if ((debugent) && (!html)) + xmlDebugDumpEntities(stderr, doc); +#endif + + /* + * free it. + */ + if ((timing) && (!repeat)) { + startTimer(); + } + xmlFreeDoc(doc); + if ((timing) && (!repeat)) { + endTimer("Freeing"); + } +} + +/************************************************************************ + * * + * Usage and Main * + * * + ************************************************************************/ + +static void showVersion(const char *name) { + fprintf(stderr, "%s: using libxml version %s\n", name, xmlParserVersion); + fprintf(stderr, " compiled with: "); + if (xmlHasFeature(XML_WITH_THREAD)) fprintf(stderr, "Threads "); + if (xmlHasFeature(XML_WITH_TREE)) fprintf(stderr, "Tree "); + if (xmlHasFeature(XML_WITH_OUTPUT)) fprintf(stderr, "Output "); + if (xmlHasFeature(XML_WITH_PUSH)) fprintf(stderr, "Push "); + if (xmlHasFeature(XML_WITH_READER)) fprintf(stderr, "Reader "); + if (xmlHasFeature(XML_WITH_PATTERN)) fprintf(stderr, "Patterns "); + if (xmlHasFeature(XML_WITH_WRITER)) fprintf(stderr, "Writer "); + if (xmlHasFeature(XML_WITH_SAX1)) fprintf(stderr, "SAXv1 "); + if (xmlHasFeature(XML_WITH_FTP)) fprintf(stderr, "FTP "); + if (xmlHasFeature(XML_WITH_HTTP)) fprintf(stderr, "HTTP "); + if (xmlHasFeature(XML_WITH_VALID)) fprintf(stderr, "DTDValid "); + if (xmlHasFeature(XML_WITH_HTML)) fprintf(stderr, "HTML "); + if (xmlHasFeature(XML_WITH_LEGACY)) fprintf(stderr, "Legacy "); + if (xmlHasFeature(XML_WITH_C14N)) fprintf(stderr, "C14N "); + if (xmlHasFeature(XML_WITH_CATALOG)) fprintf(stderr, "Catalog "); + if (xmlHasFeature(XML_WITH_XPATH)) fprintf(stderr, "XPath "); + if (xmlHasFeature(XML_WITH_XPTR)) fprintf(stderr, "XPointer "); + if (xmlHasFeature(XML_WITH_XINCLUDE)) fprintf(stderr, "XInclude "); + if (xmlHasFeature(XML_WITH_ICONV)) fprintf(stderr, "Iconv "); + if (xmlHasFeature(XML_WITH_ISO8859X)) fprintf(stderr, "ISO8859X "); + if (xmlHasFeature(XML_WITH_UNICODE)) fprintf(stderr, "Unicode "); + if (xmlHasFeature(XML_WITH_REGEXP)) fprintf(stderr, "Regexps "); + if (xmlHasFeature(XML_WITH_AUTOMATA)) fprintf(stderr, "Automata "); + if (xmlHasFeature(XML_WITH_EXPR)) fprintf(stderr, "Expr "); + if (xmlHasFeature(XML_WITH_SCHEMAS)) fprintf(stderr, "Schemas "); + if (xmlHasFeature(XML_WITH_SCHEMATRON)) fprintf(stderr, "Schematron "); + if (xmlHasFeature(XML_WITH_MODULES)) fprintf(stderr, "Modules "); + if (xmlHasFeature(XML_WITH_DEBUG)) fprintf(stderr, "Debug "); + if (xmlHasFeature(XML_WITH_DEBUG_MEM)) fprintf(stderr, "MemDebug "); + if (xmlHasFeature(XML_WITH_DEBUG_RUN)) fprintf(stderr, "RunDebug "); + fprintf(stderr, "\n"); +} + +static void usage(const char *name) { + printf("Usage : %s [options] XMLfiles ...\n", name); +#ifdef LIBXML_OUTPUT_ENABLED + printf("\tParse the XML files and output the result of the parsing\n"); +#else + printf("\tParse the XML files\n"); +#endif /* LIBXML_OUTPUT_ENABLED */ + printf("\t--version : display the version of the XML library used\n"); +#ifdef LIBXML_DEBUG_ENABLED + printf("\t--debug : dump a debug tree of the in-memory document\n"); + printf("\t--shell : run a navigating shell\n"); + printf("\t--debugent : debug the entities defined in the document\n"); +#else +#ifdef LIBXML_READER_ENABLED + printf("\t--debug : dump the nodes content when using --stream\n"); +#endif /* LIBXML_READER_ENABLED */ +#endif +#ifdef LIBXML_TREE_ENABLED + printf("\t--copy : used to test the internal copy implementation\n"); +#endif /* LIBXML_TREE_ENABLED */ + printf("\t--recover : output what was parsable on broken XML documents\n"); + printf("\t--noent : substitute entity references by their value\n"); + printf("\t--noout : don't output the result tree\n"); + printf("\t--path 'paths': provide a set of paths for resources\n"); + printf("\t--load-trace : print trace of all external entites loaded\n"); + printf("\t--nonet : refuse to fetch DTDs or entities over network\n"); + printf("\t--nocompact : do not generate compact text nodes\n"); + printf("\t--htmlout : output results as HTML\n"); + printf("\t--nowrap : do not put HTML doc wrapper\n"); +#ifdef LIBXML_VALID_ENABLED + printf("\t--valid : validate the document in addition to std well-formed check\n"); + printf("\t--postvalid : do a posteriori validation, i.e after parsing\n"); + printf("\t--dtdvalid URL : do a posteriori validation against a given DTD\n"); + printf("\t--dtdvalidfpi FPI : same but name the DTD with a Public Identifier\n"); +#endif /* LIBXML_VALID_ENABLED */ + printf("\t--timing : print some timings\n"); + printf("\t--output file or -o file: save to a given file\n"); + printf("\t--repeat : repeat 100 times, for timing or profiling\n"); + printf("\t--insert : ad-hoc test for valid insertions\n"); +#ifdef LIBXML_OUTPUT_ENABLED +#ifdef HAVE_ZLIB_H + printf("\t--compress : turn on gzip compression of output\n"); +#endif +#endif /* LIBXML_OUTPUT_ENABLED */ +#ifdef LIBXML_HTML_ENABLED + printf("\t--html : use the HTML parser\n"); + printf("\t--xmlout : force to use the XML serializer when using --html\n"); +#endif +#ifdef LIBXML_PUSH_ENABLED + printf("\t--push : use the push mode of the parser\n"); +#endif /* LIBXML_PUSH_ENABLED */ +#ifdef HAVE_SYS_MMAN_H + printf("\t--memory : parse from memory\n"); +#endif + printf("\t--maxmem nbbytes : limits memory allocation to nbbytes bytes\n"); + printf("\t--nowarning : do not emit warnings from parser/validator\n"); + printf("\t--noblanks : drop (ignorable?) blanks spaces\n"); + printf("\t--nocdata : replace cdata section with text nodes\n"); +#ifdef LIBXML_OUTPUT_ENABLED + printf("\t--format : reformat/reindent the input\n"); + printf("\t--encode encoding : output in the given encoding\n"); + printf("\t--dropdtd : remove the DOCTYPE of the input docs\n"); +#endif /* LIBXML_OUTPUT_ENABLED */ + printf("\t--c14n : save in W3C canonical format (with comments)\n"); + printf("\t--exc-c14n : save in W3C exclusive canonical format (with comments)\n"); +#ifdef LIBXML_C14N_ENABLED +#endif /* LIBXML_C14N_ENABLED */ + printf("\t--nsclean : remove redundant namespace declarations\n"); + printf("\t--testIO : test user I/O support\n"); +#ifdef LIBXML_CATALOG_ENABLED + printf("\t--catalogs : use SGML catalogs from $SGML_CATALOG_FILES\n"); + printf("\t otherwise XML Catalogs starting from \n"); + printf("\t %s are activated by default\n", XML_XML_DEFAULT_CATALOG); + printf("\t--nocatalogs: deactivate all catalogs\n"); +#endif + printf("\t--auto : generate a small doc on the fly\n"); +#ifdef LIBXML_XINCLUDE_ENABLED + printf("\t--xinclude : do XInclude processing\n"); + printf("\t--noxincludenode : same but do not generate XInclude nodes\n"); +#endif + printf("\t--loaddtd : fetch external DTD\n"); + printf("\t--dtdattr : loaddtd + populate the tree with inherited attributes \n"); +#ifdef LIBXML_READER_ENABLED + printf("\t--stream : use the streaming interface to process very large files\n"); + printf("\t--walker : create a reader and walk though the resulting doc\n"); +#endif /* LIBXML_READER_ENABLED */ +#ifdef LIBXML_PATTERN_ENABLED + printf("\t--pattern pattern_value : test the pattern support\n"); +#endif + printf("\t--chkregister : verify the node registration code\n"); +#ifdef LIBXML_SCHEMAS_ENABLED + printf("\t--relaxng schema : do RelaxNG validation against the schema\n"); + printf("\t--schema schema : do validation against the WXS schema\n"); +#endif +#ifdef LIBXML_SCHEMATRON_ENABLED + printf("\t--schematron schema : do validation against a schematron\n"); +#endif +#ifdef LIBXML_SAX1_ENABLED + printf("\t--sax1: use the old SAX1 interfaces for processing\n"); +#endif + printf("\t--sax: do not build a tree but work just at the SAX level\n"); + + printf("\nLibxml project home page: http://xmlsoft.org/\n"); + printf("To report bugs or get some help check: http://xmlsoft.org/bugs.html\n"); +} + +static void registerNode(xmlNodePtr node) +{ + node->_private = malloc(sizeof(long)); + *(long*)node->_private = (long) 0x81726354; + nbregister++; +} + +static void deregisterNode(xmlNodePtr node) +{ + assert(node->_private != NULL); + assert(*(long*)node->_private == (long) 0x81726354); + free(node->_private); + nbregister--; +} + +int +main(int argc, char **argv) { + int i, acount; + int files = 0; + int version = 0; + const char* indent; + + if (argc <= 1) { + usage(argv[0]); + return(1); + } + LIBXML_TEST_VERSION + for (i = 1; i < argc ; i++) { + if (!strcmp(argv[i], "-")) + break; + + if (argv[i][0] != '-') + continue; + if ((!strcmp(argv[i], "-debug")) || (!strcmp(argv[i], "--debug"))) + debug++; + else +#ifdef LIBXML_DEBUG_ENABLED + if ((!strcmp(argv[i], "-shell")) || + (!strcmp(argv[i], "--shell"))) { + shell++; + noout = 1; + } else +#endif +#ifdef LIBXML_TREE_ENABLED + if ((!strcmp(argv[i], "-copy")) || (!strcmp(argv[i], "--copy"))) + copy++; + else +#endif /* LIBXML_TREE_ENABLED */ + if ((!strcmp(argv[i], "-recover")) || + (!strcmp(argv[i], "--recover"))) { + recovery++; + options |= XML_PARSE_RECOVER; + } else if ((!strcmp(argv[i], "-noent")) || + (!strcmp(argv[i], "--noent"))) { + noent++; + options |= XML_PARSE_NOENT; + } else if ((!strcmp(argv[i], "-nsclean")) || + (!strcmp(argv[i], "--nsclean"))) { + options |= XML_PARSE_NSCLEAN; + } else if ((!strcmp(argv[i], "-nocdata")) || + (!strcmp(argv[i], "--nocdata"))) { + options |= XML_PARSE_NOCDATA; + } else if ((!strcmp(argv[i], "-nodict")) || + (!strcmp(argv[i], "--nodict"))) { + options |= XML_PARSE_NODICT; + } else if ((!strcmp(argv[i], "-version")) || + (!strcmp(argv[i], "--version"))) { + showVersion(argv[0]); + version = 1; + } else if ((!strcmp(argv[i], "-noout")) || + (!strcmp(argv[i], "--noout"))) + noout++; +#ifdef LIBXML_OUTPUT_ENABLED + else if ((!strcmp(argv[i], "-o")) || + (!strcmp(argv[i], "-output")) || + (!strcmp(argv[i], "--output"))) { + i++; + output = argv[i]; + } +#endif /* LIBXML_OUTPUT_ENABLED */ + else if ((!strcmp(argv[i], "-htmlout")) || + (!strcmp(argv[i], "--htmlout"))) + htmlout++; + else if ((!strcmp(argv[i], "-nowrap")) || + (!strcmp(argv[i], "--nowrap"))) + nowrap++; +#ifdef LIBXML_HTML_ENABLED + else if ((!strcmp(argv[i], "-html")) || + (!strcmp(argv[i], "--html"))) { + html++; + } + else if ((!strcmp(argv[i], "-xmlout")) || + (!strcmp(argv[i], "--xmlout"))) { + xmlout++; + } +#endif /* LIBXML_HTML_ENABLED */ + else if ((!strcmp(argv[i], "-loaddtd")) || + (!strcmp(argv[i], "--loaddtd"))) { + loaddtd++; + options |= XML_PARSE_DTDLOAD; + } else if ((!strcmp(argv[i], "-dtdattr")) || + (!strcmp(argv[i], "--dtdattr"))) { + loaddtd++; + dtdattrs++; + options |= XML_PARSE_DTDATTR; + } +#ifdef LIBXML_VALID_ENABLED + else if ((!strcmp(argv[i], "-valid")) || + (!strcmp(argv[i], "--valid"))) { + valid++; + options |= XML_PARSE_DTDVALID; + } else if ((!strcmp(argv[i], "-postvalid")) || + (!strcmp(argv[i], "--postvalid"))) { + postvalid++; + loaddtd++; + options |= XML_PARSE_DTDLOAD; + } else if ((!strcmp(argv[i], "-dtdvalid")) || + (!strcmp(argv[i], "--dtdvalid"))) { + i++; + dtdvalid = argv[i]; + loaddtd++; + options |= XML_PARSE_DTDLOAD; + } else if ((!strcmp(argv[i], "-dtdvalidfpi")) || + (!strcmp(argv[i], "--dtdvalidfpi"))) { + i++; + dtdvalidfpi = argv[i]; + loaddtd++; + options |= XML_PARSE_DTDLOAD; + } +#endif /* LIBXML_VALID_ENABLED */ + else if ((!strcmp(argv[i], "-dropdtd")) || + (!strcmp(argv[i], "--dropdtd"))) + dropdtd++; + else if ((!strcmp(argv[i], "-insert")) || + (!strcmp(argv[i], "--insert"))) + insert++; + else if ((!strcmp(argv[i], "-timing")) || + (!strcmp(argv[i], "--timing"))) + timing++; + else if ((!strcmp(argv[i], "-auto")) || + (!strcmp(argv[i], "--auto"))) + generate++; + else if ((!strcmp(argv[i], "-repeat")) || + (!strcmp(argv[i], "--repeat"))) { + if (repeat) + repeat *= 10; + else + repeat = 100; + } +#ifdef LIBXML_PUSH_ENABLED + else if ((!strcmp(argv[i], "-push")) || + (!strcmp(argv[i], "--push"))) + push++; +#endif /* LIBXML_PUSH_ENABLED */ +#ifdef HAVE_SYS_MMAN_H + else if ((!strcmp(argv[i], "-memory")) || + (!strcmp(argv[i], "--memory"))) + memory++; +#endif + else if ((!strcmp(argv[i], "-testIO")) || + (!strcmp(argv[i], "--testIO"))) + testIO++; +#ifdef LIBXML_XINCLUDE_ENABLED + else if ((!strcmp(argv[i], "-xinclude")) || + (!strcmp(argv[i], "--xinclude"))) { + xinclude++; + options |= XML_PARSE_XINCLUDE; + } + else if ((!strcmp(argv[i], "-noxincludenode")) || + (!strcmp(argv[i], "--noxincludenode"))) { + xinclude++; + options |= XML_PARSE_XINCLUDE; + options |= XML_PARSE_NOXINCNODE; + } +#endif +#ifdef LIBXML_OUTPUT_ENABLED +#ifdef HAVE_ZLIB_H + else if ((!strcmp(argv[i], "-compress")) || + (!strcmp(argv[i], "--compress"))) { + compress++; + xmlSetCompressMode(9); + } +#endif +#endif /* LIBXML_OUTPUT_ENABLED */ + else if ((!strcmp(argv[i], "-nowarning")) || + (!strcmp(argv[i], "--nowarning"))) { + xmlGetWarningsDefaultValue = 0; + xmlPedanticParserDefault(0); + options |= XML_PARSE_NOWARNING; + } + else if ((!strcmp(argv[i], "-pedantic")) || + (!strcmp(argv[i], "--pedantic"))) { + xmlGetWarningsDefaultValue = 1; + xmlPedanticParserDefault(1); + options |= XML_PARSE_PEDANTIC; + } +#ifdef LIBXML_DEBUG_ENABLED + else if ((!strcmp(argv[i], "-debugent")) || + (!strcmp(argv[i], "--debugent"))) { + debugent++; + xmlParserDebugEntities = 1; + } +#endif +#ifdef LIBXML_C14N_ENABLED + else if ((!strcmp(argv[i], "-c14n")) || + (!strcmp(argv[i], "--c14n"))) { + canonical++; + options |= XML_PARSE_NOENT | XML_PARSE_DTDATTR | XML_PARSE_DTDLOAD; + } + else if ((!strcmp(argv[i], "-exc-c14n")) || + (!strcmp(argv[i], "--exc-c14n"))) { + exc_canonical++; + options |= XML_PARSE_NOENT | XML_PARSE_DTDATTR | XML_PARSE_DTDLOAD; + } +#endif +#ifdef LIBXML_CATALOG_ENABLED + else if ((!strcmp(argv[i], "-catalogs")) || + (!strcmp(argv[i], "--catalogs"))) { + catalogs++; + } else if ((!strcmp(argv[i], "-nocatalogs")) || + (!strcmp(argv[i], "--nocatalogs"))) { + nocatalogs++; + } +#endif + else if ((!strcmp(argv[i], "-encode")) || + (!strcmp(argv[i], "--encode"))) { + i++; + encoding = argv[i]; + /* + * OK it's for testing purposes + */ + xmlAddEncodingAlias("UTF-8", "DVEnc"); + } + else if ((!strcmp(argv[i], "-noblanks")) || + (!strcmp(argv[i], "--noblanks"))) { + noblanks++; + xmlKeepBlanksDefault(0); + } + else if ((!strcmp(argv[i], "-maxmem")) || + (!strcmp(argv[i], "--maxmem"))) { + i++; + if (sscanf(argv[i], "%d", &maxmem) == 1) { + xmlMemSetup(myFreeFunc, myMallocFunc, myReallocFunc, + myStrdupFunc); + } else { + maxmem = 0; + } + } + else if ((!strcmp(argv[i], "-format")) || + (!strcmp(argv[i], "--format"))) { + noblanks++; +#ifdef LIBXML_OUTPUT_ENABLED + format++; +#endif /* LIBXML_OUTPUT_ENABLED */ + xmlKeepBlanksDefault(0); + } +#ifdef LIBXML_READER_ENABLED + else if ((!strcmp(argv[i], "-stream")) || + (!strcmp(argv[i], "--stream"))) { + stream++; + } + else if ((!strcmp(argv[i], "-walker")) || + (!strcmp(argv[i], "--walker"))) { + walker++; + noout++; + } +#endif /* LIBXML_READER_ENABLED */ +#ifdef LIBXML_SAX1_ENABLED + else if ((!strcmp(argv[i], "-sax1")) || + (!strcmp(argv[i], "--sax1"))) { + sax1++; + } +#endif /* LIBXML_SAX1_ENABLED */ + else if ((!strcmp(argv[i], "-sax")) || + (!strcmp(argv[i], "--sax"))) { + sax++; + } + else if ((!strcmp(argv[i], "-chkregister")) || + (!strcmp(argv[i], "--chkregister"))) { + chkregister++; +#ifdef LIBXML_SCHEMAS_ENABLED + } else if ((!strcmp(argv[i], "-relaxng")) || + (!strcmp(argv[i], "--relaxng"))) { + i++; + relaxng = argv[i]; + noent++; + options |= XML_PARSE_NOENT; + } else if ((!strcmp(argv[i], "-schema")) || + (!strcmp(argv[i], "--schema"))) { + i++; + schema = argv[i]; + noent++; +#endif +#ifdef LIBXML_SCHEMATRON_ENABLED + } else if ((!strcmp(argv[i], "-schematron")) || + (!strcmp(argv[i], "--schematron"))) { + i++; + schematron = argv[i]; + noent++; +#endif + } else if ((!strcmp(argv[i], "-nonet")) || + (!strcmp(argv[i], "--nonet"))) { + options |= XML_PARSE_NONET; + xmlSetExternalEntityLoader(xmlNoNetExternalEntityLoader); + } else if ((!strcmp(argv[i], "-nocompact")) || + (!strcmp(argv[i], "--nocompact"))) { + options &= ~XML_PARSE_COMPACT; + } else if ((!strcmp(argv[i], "-load-trace")) || + (!strcmp(argv[i], "--load-trace"))) { + load_trace++; + } else if ((!strcmp(argv[i], "-path")) || + (!strcmp(argv[i], "--path"))) { + i++; + parsePath(BAD_CAST argv[i]); +#ifdef LIBXML_PATTERN_ENABLED + } else if ((!strcmp(argv[i], "-pattern")) || + (!strcmp(argv[i], "--pattern"))) { + i++; + pattern = argv[i]; +#endif + } else { + fprintf(stderr, "Unknown option %s\n", argv[i]); + usage(argv[0]); + return(1); + } + } + +#ifdef LIBXML_CATALOG_ENABLED + if (nocatalogs == 0) { + if (catalogs) { + const char *catal; + + catal = getenv("SGML_CATALOG_FILES"); + if (catal != NULL) { + xmlLoadCatalogs(catal); + } else { + fprintf(stderr, "Variable $SGML_CATALOG_FILES not set\n"); + } + } + } +#endif + +#ifdef LIBXML_SAX1_ENABLED + if (sax1) + xmlSAXDefaultVersion(1); + else + xmlSAXDefaultVersion(2); +#endif /* LIBXML_SAX1_ENABLED */ + + if (chkregister) { + xmlRegisterNodeDefault(registerNode); + xmlDeregisterNodeDefault(deregisterNode); + } + + indent = getenv("XMLLINT_INDENT"); + if(indent != NULL) { + xmlTreeIndentString = indent; + } + + + defaultEntityLoader = xmlGetExternalEntityLoader(); + xmlSetExternalEntityLoader(xmllintExternalEntityLoader); + + xmlLineNumbersDefault(1); + if (loaddtd != 0) + xmlLoadExtDtdDefaultValue |= XML_DETECT_IDS; + if (dtdattrs) + xmlLoadExtDtdDefaultValue |= XML_COMPLETE_ATTRS; + if (noent != 0) xmlSubstituteEntitiesDefault(1); +#ifdef LIBXML_VALID_ENABLED + if (valid != 0) xmlDoValidityCheckingDefaultValue = 1; +#endif /* LIBXML_VALID_ENABLED */ + if ((htmlout) && (!nowrap)) { + xmlGenericError(xmlGenericErrorContext, + "\n"); + xmlGenericError(xmlGenericErrorContext, + "%s output\n", + argv[0]); + xmlGenericError(xmlGenericErrorContext, + "

%s output

\n", + argv[0]); + } + +#ifdef LIBXML_SCHEMATRON_ENABLED + if ((schematron != NULL) && (sax == 0) +#ifdef LIBXML_READER_ENABLED + && (stream == 0) +#endif /* LIBXML_READER_ENABLED */ + ) { + xmlSchematronParserCtxtPtr ctxt; + + /* forces loading the DTDs */ + xmlLoadExtDtdDefaultValue |= 1; + options |= XML_PARSE_DTDLOAD; + if (timing) { + startTimer(); + } + ctxt = xmlSchematronNewParserCtxt(schematron); +#if 0 + xmlSchematronSetParserErrors(ctxt, + (xmlSchematronValidityErrorFunc) fprintf, + (xmlSchematronValidityWarningFunc) fprintf, + stderr); +#endif + wxschematron = xmlSchematronParse(ctxt); + if (wxschematron == NULL) { + xmlGenericError(xmlGenericErrorContext, + "Schematron schema %s failed to compile\n", schematron); + progresult = XMLLINT_ERR_SCHEMACOMP; + schematron = NULL; + } + xmlSchematronFreeParserCtxt(ctxt); + if (timing) { + endTimer("Compiling the schemas"); + } + } +#endif +#ifdef LIBXML_SCHEMAS_ENABLED + if ((relaxng != NULL) && (sax == 0) +#ifdef LIBXML_READER_ENABLED + && (stream == 0) +#endif /* LIBXML_READER_ENABLED */ + ) { + xmlRelaxNGParserCtxtPtr ctxt; + + /* forces loading the DTDs */ + xmlLoadExtDtdDefaultValue |= 1; + options |= XML_PARSE_DTDLOAD; + if (timing) { + startTimer(); + } + ctxt = xmlRelaxNGNewParserCtxt(relaxng); + xmlRelaxNGSetParserErrors(ctxt, + (xmlRelaxNGValidityErrorFunc) fprintf, + (xmlRelaxNGValidityWarningFunc) fprintf, + stderr); + relaxngschemas = xmlRelaxNGParse(ctxt); + if (relaxngschemas == NULL) { + xmlGenericError(xmlGenericErrorContext, + "Relax-NG schema %s failed to compile\n", relaxng); + progresult = XMLLINT_ERR_SCHEMACOMP; + relaxng = NULL; + } + xmlRelaxNGFreeParserCtxt(ctxt); + if (timing) { + endTimer("Compiling the schemas"); + } + } else if ((schema != NULL) +#ifdef LIBXML_READER_ENABLED + && (stream == 0) +#endif + ) { + xmlSchemaParserCtxtPtr ctxt; + + if (timing) { + startTimer(); + } + ctxt = xmlSchemaNewParserCtxt(schema); + xmlSchemaSetParserErrors(ctxt, + (xmlSchemaValidityErrorFunc) fprintf, + (xmlSchemaValidityWarningFunc) fprintf, + stderr); + wxschemas = xmlSchemaParse(ctxt); + if (wxschemas == NULL) { + xmlGenericError(xmlGenericErrorContext, + "WXS schema %s failed to compile\n", schema); + progresult = XMLLINT_ERR_SCHEMACOMP; + schema = NULL; + } + xmlSchemaFreeParserCtxt(ctxt); + if (timing) { + endTimer("Compiling the schemas"); + } + } +#endif /* LIBXML_SCHEMAS_ENABLED */ +#ifdef LIBXML_PATTERN_ENABLED + if ((pattern != NULL) +#ifdef LIBXML_READER_ENABLED + && (walker == 0) +#endif + ) { + patternc = xmlPatterncompile((const xmlChar *) pattern, NULL, 0, NULL); + if (patternc == NULL) { + xmlGenericError(xmlGenericErrorContext, + "Pattern %s failed to compile\n", pattern); + progresult = XMLLINT_ERR_SCHEMAPAT; + pattern = NULL; + } + } +#endif /* LIBXML_PATTERN_ENABLED */ + for (i = 1; i < argc ; i++) { + if ((!strcmp(argv[i], "-encode")) || + (!strcmp(argv[i], "--encode"))) { + i++; + continue; + } else if ((!strcmp(argv[i], "-o")) || + (!strcmp(argv[i], "-output")) || + (!strcmp(argv[i], "--output"))) { + i++; + continue; + } +#ifdef LIBXML_VALID_ENABLED + if ((!strcmp(argv[i], "-dtdvalid")) || + (!strcmp(argv[i], "--dtdvalid"))) { + i++; + continue; + } + if ((!strcmp(argv[i], "-path")) || + (!strcmp(argv[i], "--path"))) { + i++; + continue; + } + if ((!strcmp(argv[i], "-dtdvalidfpi")) || + (!strcmp(argv[i], "--dtdvalidfpi"))) { + i++; + continue; + } +#endif /* LIBXML_VALID_ENABLED */ + if ((!strcmp(argv[i], "-relaxng")) || + (!strcmp(argv[i], "--relaxng"))) { + i++; + continue; + } + if ((!strcmp(argv[i], "-maxmem")) || + (!strcmp(argv[i], "--maxmem"))) { + i++; + continue; + } + if ((!strcmp(argv[i], "-schema")) || + (!strcmp(argv[i], "--schema"))) { + i++; + continue; + } + if ((!strcmp(argv[i], "-schematron")) || + (!strcmp(argv[i], "--schematron"))) { + i++; + continue; + } +#ifdef LIBXML_PATTERN_ENABLED + if ((!strcmp(argv[i], "-pattern")) || + (!strcmp(argv[i], "--pattern"))) { + i++; + continue; + } +#endif + if ((timing) && (repeat)) + startTimer(); + /* Remember file names. "-" means stdin. */ + if ((argv[i][0] != '-') || (strcmp(argv[i], "-") == 0)) { + if (repeat) { + xmlParserCtxtPtr ctxt = NULL; + + for (acount = 0;acount < repeat;acount++) { +#ifdef LIBXML_READER_ENABLED + if (stream != 0) { + streamFile(argv[i]); + } else { +#endif /* LIBXML_READER_ENABLED */ + if (sax) { + testSAX(argv[i]); + } else { + if (ctxt == NULL) + ctxt = xmlNewParserCtxt(); + parseAndPrintFile(argv[i], ctxt); + } +#ifdef LIBXML_READER_ENABLED + } +#endif /* LIBXML_READER_ENABLED */ + } + if (ctxt != NULL) + xmlFreeParserCtxt(ctxt); + } else { + nbregister = 0; + +#ifdef LIBXML_READER_ENABLED + if (stream != 0) + streamFile(argv[i]); + else +#endif /* LIBXML_READER_ENABLED */ + if (sax) { + testSAX(argv[i]); + } else { + parseAndPrintFile(argv[i], NULL); + } + + if ((chkregister) && (nbregister != 0)) { + fprintf(stderr, "Registration count off: %d\n", nbregister); + progresult = XMLLINT_ERR_RDREGIS; + } + } + files ++; + if ((timing) && (repeat)) { + endTimer("%d iterations", repeat); + } + } + } + if (generate) + parseAndPrintFile(NULL, NULL); + if ((htmlout) && (!nowrap)) { + xmlGenericError(xmlGenericErrorContext, "\n"); + } + if ((files == 0) && (!generate) && (version == 0)) { + usage(argv[0]); + } +#ifdef LIBXML_SCHEMATRON_ENABLED + if (wxschematron != NULL) + xmlSchematronFree(wxschematron); +#endif +#ifdef LIBXML_SCHEMAS_ENABLED + if (relaxngschemas != NULL) + xmlRelaxNGFree(relaxngschemas); + if (wxschemas != NULL) + xmlSchemaFree(wxschemas); + xmlRelaxNGCleanupTypes(); +#endif +#ifdef LIBXML_PATTERN_ENABLED + if (patternc != NULL) + xmlFreePattern(patternc); +#endif + xmlCleanupParser(); + xmlMemoryDump(); + + return(progresult); +} + diff --git a/Extras/LibXML/xmlschemas.c b/Extras/LibXML/xmlschemas.c new file mode 100644 index 000000000..ff89e2ccb --- /dev/null +++ b/Extras/LibXML/xmlschemas.c @@ -0,0 +1,28407 @@ +/* + * schemas.c : implementation of the XML Schema handling and + * schema validity checking + * + * See Copyright for the status of this software. + * + * Daniel Veillard + */ + +/* + * TODO: + * - when types are redefined in includes, check that all + * types in the redef list are equal + * -> need a type equality operation. + * - if we don't intend to use the schema for schemas, we + * need to validate all schema attributes (ref, type, name) + * against their types. + * - Eliminate item creation for: ?? + * + * URGENT TODO: + * - For xsi-driven schema acquisition, augment the IDCs after every + * acquisition episode (xmlSchemaAugmentIDC). + * + * NOTES: + * - Elimated item creation for: , , + * , , , + * + * PROBLEMS: + * - http://lists.w3.org/Archives/Public/www-xml-schema-comments/2005JulSep/0337.html + * IDC XPath expression and chameleon includes: the targetNamespace is changed, so + * XPath will have trouble to resolve to this namespace, since not known. + * + * + * CONSTRAINTS: + * + * Schema Component Constraint: + * All Group Limited (cos-all-limited) + * Status: complete + * (1.2) + * In xmlSchemaGroupDefReferenceTermFixup() and + * (2) + * In xmlSchemaParseModelGroup() + * TODO: Actually this should go to component-level checks, + * but is done here due to performance. Move it to an other layer + * is schema construction via an API is implemented. + */ +#define IN_LIBXML +#include "libxml.h" + +#ifdef LIBXML_SCHEMAS_ENABLED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef LIBXML_PATTERN_ENABLED +#include +#endif +#ifdef LIBXML_READER_ENABLED +#include +#endif + +/* #define DEBUG 1 */ + +/* #define DEBUG_CONTENT 1 */ + +/* #define DEBUG_TYPE 1 */ + +/* #define DEBUG_CONTENT_REGEXP 1 */ + +/* #define DEBUG_AUTOMATA 1 */ + +/* #define DEBUG_IDC */ + +/* #define DEBUG_IDC_NODE_TABLE */ + +#ifdef DEBUG_IDC + #ifndef DEBUG_IDC_NODE_TABLE + #define DEBUG_IDC_NODE_TABLE + #endif +#endif + +/* #define ENABLE_PARTICLE_RESTRICTION 1 */ + +#define ENABLE_REDEFINE + +/* #define ENABLE_NAMED_LOCALS */ + +/* #define ENABLE_IDC_NODE_TABLES_TEST */ + +#define DUMP_CONTENT_MODEL + +#ifdef LIBXML_READER_ENABLED +/* #define XML_SCHEMA_READER_ENABLED */ +#endif + +#define UNBOUNDED (1 << 30) +#define TODO \ + xmlGenericError(xmlGenericErrorContext, \ + "Unimplemented block at %s:%d\n", \ + __FILE__, __LINE__); + +#define XML_SCHEMAS_NO_NAMESPACE (const xmlChar *) "##" + +/* + * The XML Schemas namespaces + */ +static const xmlChar *xmlSchemaNs = (const xmlChar *) + "http://www.w3.org/2001/XMLSchema"; + +static const xmlChar *xmlSchemaInstanceNs = (const xmlChar *) + "http://www.w3.org/2001/XMLSchema-instance"; + +static const xmlChar *xmlNamespaceNs = (const xmlChar *) + "http://www.w3.org/2000/xmlns/"; + +/* +* Come casting macros. +*/ +#define ACTXT_CAST (xmlSchemaAbstractCtxtPtr) +#define PCTXT_CAST (xmlSchemaParserCtxtPtr) +#define VCTXT_CAST (xmlSchemaValidCtxtPtr) +#define WXS_BASIC_CAST (xmlSchemaBasicItemPtr) +#define WXS_TREE_CAST (xmlSchemaTreeItemPtr) +#define WXS_PTC_CAST (xmlSchemaParticlePtr) +#define WXS_TYPE_CAST (xmlSchemaTypePtr) +#define WXS_ELEM_CAST (xmlSchemaElementPtr) +#define WXS_ATTR_GROUP_CAST (xmlSchemaAttributeGroupPtr) +#define WXS_ATTR_CAST (xmlSchemaAttributePtr) +#define WXS_ATTR_USE_CAST (xmlSchemaAttributeUsePtr) +#define WXS_ATTR_PROHIB_CAST (xmlSchemaAttributeUseProhibPtr) +#define WXS_MODEL_GROUPDEF_CAST (xmlSchemaModelGroupDefPtr) +#define WXS_MODEL_GROUP_CAST (xmlSchemaModelGroupPtr) +#define WXS_IDC_CAST (xmlSchemaIDCPtr) +#define WXS_QNAME_CAST (xmlSchemaQNameRefPtr) +#define WXS_LIST_CAST (xmlSchemaItemListPtr) + +/* +* Macros to query common properties of components. +*/ +#define WXS_ITEM_NODE(i) xmlSchemaGetComponentNode(WXS_BASIC_CAST (i)) + +#define WXS_ITEM_TYPE_NAME(i) xmlSchemaGetComponentTypeStr(WXS_BASIC_CAST (i)) +/* +* Macros for element declarations. +*/ +#define WXS_ELEM_TYPEDEF(e) (e)->subtypes + +#define WXS_SUBST_HEAD(item) (item)->refDecl +/* +* Macros for attribute declarations. +*/ +#define WXS_ATTR_TYPEDEF(a) (a)->subtypes +/* +* Macros for attribute uses. +*/ +#define WXS_ATTRUSE_DECL(au) WXS_ATTR_CAST (WXS_ATTR_USE_CAST (au))->attrDecl + +#define WXS_ATTRUSE_TYPEDEF(au) WXS_ATTR_TYPEDEF(WXS_ATTRUSE_DECL( WXS_ATTR_USE_CAST au)) + +#define WXS_ATTRUSE_DECL_NAME(au) (WXS_ATTRUSE_DECL(au))->name + +#define WXS_ATTRUSE_DECL_TNS(au) (WXS_ATTRUSE_DECL(au))->targetNamespace +/* +* Macros for attribute groups. +*/ +#define WXS_ATTR_GROUP_HAS_REFS(ag) ((WXS_ATTR_GROUP_CAST (ag))->flags & XML_SCHEMAS_ATTRGROUP_HAS_REFS) +#define WXS_ATTR_GROUP_EXPANDED(ag) ((WXS_ATTR_GROUP_CAST (ag))->flags & XML_SCHEMAS_ATTRGROUP_WILDCARD_BUILDED) +/* +* Macros for particles. +*/ +#define WXS_PARTICLE(p) WXS_PTC_CAST (p) + +#define WXS_PARTICLE_TERM(p) (WXS_PARTICLE(p))->children + +#define WXS_PARTICLE_MODEL(p) WXS_MODEL_GROUP_CAST WXS_PARTICLE(p)->children +/* +* Macros for model groups definitions. +*/ +#define WXS_MODELGROUPDEF_MODEL(mgd) (WXS_MODEL_GROUP_CAST (mgd))->children +/* +* Macros for model groups. +*/ +#define WXS_IS_MODEL_GROUP(i) \ + (((i)->type == XML_SCHEMA_TYPE_SEQUENCE) || \ + ((i)->type == XML_SCHEMA_TYPE_CHOICE) || \ + ((i)->type == XML_SCHEMA_TYPE_ALL)) + +#define WXS_MODELGROUP_PARTICLE(mg) WXS_PTC_CAST (mg)->children +/* +* Macros for schema buckets. +*/ +#define WXS_IS_BUCKET_INCREDEF(t) (((t) == XML_SCHEMA_SCHEMA_INCLUDE) || \ + ((t) == XML_SCHEMA_SCHEMA_REDEFINE)) + +#define WXS_IS_BUCKET_IMPMAIN(t) (((t) == XML_SCHEMA_SCHEMA_MAIN) || \ + ((t) == XML_SCHEMA_SCHEMA_IMPORT)) + +#define WXS_IMPBUCKET(b) ((xmlSchemaImportPtr) (b)) + +#define WXS_INCBUCKET(b) ((xmlSchemaIncludePtr) (b)) +/* +* Macros for complex/simple types. +*/ +#define WXS_IS_ANYTYPE(i) \ + (( (i)->type == XML_SCHEMA_TYPE_BASIC) && \ + ( (WXS_TYPE_CAST (i))->builtInType == XML_SCHEMAS_ANYTYPE)) + +#define WXS_IS_COMPLEX(i) \ + (((i)->type == XML_SCHEMA_TYPE_COMPLEX) || \ + ((i)->builtInType == XML_SCHEMAS_ANYTYPE)) + +#define WXS_IS_SIMPLE(item) \ + ((item->type == XML_SCHEMA_TYPE_SIMPLE) || \ + ((item->type == XML_SCHEMA_TYPE_BASIC) && \ + (item->builtInType != XML_SCHEMAS_ANYTYPE))) + +#define WXS_IS_ANY_SIMPLE_TYPE(i) \ + (((i)->type == XML_SCHEMA_TYPE_BASIC) && \ + ((i)->builtInType == XML_SCHEMAS_ANYSIMPLETYPE)) + +#define WXS_IS_RESTRICTION(t) \ + ((t)->flags & XML_SCHEMAS_TYPE_DERIVATION_METHOD_RESTRICTION) + +#define WXS_IS_EXTENSION(t) \ + ((t)->flags & XML_SCHEMAS_TYPE_DERIVATION_METHOD_EXTENSION) + +#define WXS_IS_TYPE_NOT_FIXED(i) \ + (((i)->type != XML_SCHEMA_TYPE_BASIC) && \ + (((i)->flags & XML_SCHEMAS_TYPE_INTERNAL_RESOLVED) == 0)) + +#define WXS_IS_TYPE_NOT_FIXED_1(item) \ + (((item)->type != XML_SCHEMA_TYPE_BASIC) && \ + (((item)->flags & XML_SCHEMAS_TYPE_FIXUP_1) == 0)) +/* +* Macros for exclusively for complex types. +*/ +#define WXS_HAS_COMPLEX_CONTENT(item) \ + ((item->contentType == XML_SCHEMA_CONTENT_MIXED) || \ + (item->contentType == XML_SCHEMA_CONTENT_EMPTY) || \ + (item->contentType == XML_SCHEMA_CONTENT_ELEMENTS)) + +#define WXS_HAS_SIMPLE_CONTENT(item) \ + ((item->contentType == XML_SCHEMA_CONTENT_SIMPLE) || \ + (item->contentType == XML_SCHEMA_CONTENT_BASIC)) + +#define WXS_HAS_MIXED_CONTENT(item) \ + (item->contentType == XML_SCHEMA_CONTENT_MIXED) + +#define WXS_EMPTIABLE(t) \ + (xmlSchemaIsParticleEmptiable(WXS_PTC_CAST (t)->subtypes)) + +#define WXS_TYPE_CONTENTTYPE(t) (t)->subtypes + +#define WXS_TYPE_PARTICLE(t) WXS_PTC_CAST (t)->subtypes + +#define WXS_TYPE_PARTICLE_TERM(t) WXS_PARTICLE_TERM(WXS_TYPE_PARTICLE(t)) +/* +* Macros for exclusively for simple types. +*/ +#define WXS_LIST_ITEMTYPE(t) (t)->subtypes + +#define WXS_IS_ATOMIC(t) (t->flags & XML_SCHEMAS_TYPE_VARIETY_ATOMIC) + +#define WXS_IS_LIST(t) (t->flags & XML_SCHEMAS_TYPE_VARIETY_LIST) + +#define WXS_IS_UNION(t) (t->flags & XML_SCHEMAS_TYPE_VARIETY_UNION) +/* +* Misc parser context macros. +*/ +#define WXS_CONSTRUCTOR(ctx) (ctx)->constructor + +#define WXS_HAS_BUCKETS(ctx) \ +( (WXS_CONSTRUCTOR((ctx))->buckets != NULL) && \ +(WXS_CONSTRUCTOR((ctx))->buckets->nbItems > 0) ) + +#define WXS_SUBST_GROUPS(ctx) WXS_CONSTRUCTOR((ctx))->substGroups + +#define WXS_BUCKET(ctx) WXS_CONSTRUCTOR((ctx))->bucket + +#define WXS_SCHEMA(ctx) (ctx)->schema + +#define WXS_ADD_LOCAL(ctx, item) \ + xmlSchemaAddItemSize(&(WXS_BUCKET(ctx)->locals), 10, item) + +#define WXS_ADD_GLOBAL(ctx, item) \ + xmlSchemaAddItemSize(&(WXS_BUCKET(ctx)->globals), 5, item) + +#define WXS_ADD_PENDING(ctx, item) \ + xmlSchemaAddItemSize(&((ctx)->constructor->pending), 10, item) +/* +* xmlSchemaItemList macros. +*/ +#define WXS_ILIST_IS_EMPTY(l) ((l == NULL) || ((l)->nbItems == 0)) +/* +* Misc macros. +*/ +#define IS_SCHEMA(node, type) \ + ((node != NULL) && (node->ns != NULL) && \ + (xmlStrEqual(node->name, (const xmlChar *) type)) && \ + (xmlStrEqual(node->ns->href, xmlSchemaNs))) + +#define FREE_AND_NULL(str) if ((str) != NULL) { xmlFree((xmlChar *) (str)); str = NULL; } + +/* +* Since we put the default/fixed values into the dict, we can +* use pointer comparison for those values. +* REMOVED: (xmlStrEqual((v1), (v2))) +*/ +#define WXS_ARE_DEFAULT_STR_EQUAL(v1, v2) ((v1) == (v2)) + +#define INODE_NILLED(item) (item->flags & XML_SCHEMA_ELEM_INFO_NILLED) + +#define CAN_PARSE_SCHEMA(b) (((b)->doc != NULL) && ((b)->parsed == 0)) + +#define HFAILURE if (res == -1) goto exit_failure; + +#define HERROR if (res != 0) goto exit_error; + +#define HSTOP(ctx) if ((ctx)->stop) goto exit; +/* +* Some flags used for various schema constraints. +*/ +#define SUBSET_RESTRICTION 1<<0 +#define SUBSET_EXTENSION 1<<1 +#define SUBSET_SUBSTITUTION 1<<2 +#define SUBSET_LIST 1<<3 +#define SUBSET_UNION 1<<4 + +typedef struct _xmlSchemaNodeInfo xmlSchemaNodeInfo; +typedef xmlSchemaNodeInfo *xmlSchemaNodeInfoPtr; + +typedef struct _xmlSchemaItemList xmlSchemaItemList; +typedef xmlSchemaItemList *xmlSchemaItemListPtr; +struct _xmlSchemaItemList { + void **items; /* used for dynamic addition of schemata */ + int nbItems; /* used for dynamic addition of schemata */ + int sizeItems; /* used for dynamic addition of schemata */ +}; + +#define XML_SCHEMA_CTXT_PARSER 1 +#define XML_SCHEMA_CTXT_VALIDATOR 2 + +typedef struct _xmlSchemaAbstractCtxt xmlSchemaAbstractCtxt; +typedef xmlSchemaAbstractCtxt *xmlSchemaAbstractCtxtPtr; +struct _xmlSchemaAbstractCtxt { + int type; /* E.g. XML_SCHEMA_CTXT_VALIDATOR */ +}; + +typedef struct _xmlSchemaBucket xmlSchemaBucket; +typedef xmlSchemaBucket *xmlSchemaBucketPtr; + +#define XML_SCHEMA_SCHEMA_MAIN 0 +#define XML_SCHEMA_SCHEMA_IMPORT 1 +#define XML_SCHEMA_SCHEMA_INCLUDE 2 +#define XML_SCHEMA_SCHEMA_REDEFINE 3 + +/** + * xmlSchemaSchemaRelation: + * + * Used to create a graph of schema relationships. + */ +typedef struct _xmlSchemaSchemaRelation xmlSchemaSchemaRelation; +typedef xmlSchemaSchemaRelation *xmlSchemaSchemaRelationPtr; +struct _xmlSchemaSchemaRelation { + xmlSchemaSchemaRelationPtr next; + int type; /* E.g. XML_SCHEMA_SCHEMA_IMPORT */ + const xmlChar *importNamespace; + xmlSchemaBucketPtr bucket; +}; + +#define XML_SCHEMA_BUCKET_MARKED 1<<0 +#define XML_SCHEMA_BUCKET_COMPS_ADDED 1<<1 + +struct _xmlSchemaBucket { + int type; + int flags; + const xmlChar *schemaLocation; + const xmlChar *origTargetNamespace; + const xmlChar *targetNamespace; + xmlDocPtr doc; + xmlSchemaSchemaRelationPtr relations; + int located; + int parsed; + int imported; + int preserveDoc; + xmlSchemaItemListPtr globals; /* Global components. */ + xmlSchemaItemListPtr locals; /* Local components. */ +}; + +/** + * xmlSchemaImport: + * (extends xmlSchemaBucket) + * + * Reflects a schema. Holds some information + * about the schema and its toplevel components. Duplicate + * toplevel components are not checked at this level. + */ +typedef struct _xmlSchemaImport xmlSchemaImport; +typedef xmlSchemaImport *xmlSchemaImportPtr; +struct _xmlSchemaImport { + int type; /* Main OR import OR include. */ + int flags; + const xmlChar *schemaLocation; /* The URI of the schema document. */ + /* For chameleon includes, @origTargetNamespace will be NULL */ + const xmlChar *origTargetNamespace; + /* + * For chameleon includes, @targetNamespace will be the + * targetNamespace of the including schema. + */ + const xmlChar *targetNamespace; + xmlDocPtr doc; /* The schema node-tree. */ + /* @relations will hold any included/imported/redefined schemas. */ + xmlSchemaSchemaRelationPtr relations; + int located; + int parsed; + int imported; + int preserveDoc; + xmlSchemaItemListPtr globals; + xmlSchemaItemListPtr locals; + /* The imported schema. */ + xmlSchemaPtr schema; +}; + +/* +* (extends xmlSchemaBucket) +*/ +typedef struct _xmlSchemaInclude xmlSchemaInclude; +typedef xmlSchemaInclude *xmlSchemaIncludePtr; +struct _xmlSchemaInclude { + int type; + int flags; + const xmlChar *schemaLocation; + const xmlChar *origTargetNamespace; + const xmlChar *targetNamespace; + xmlDocPtr doc; + xmlSchemaSchemaRelationPtr relations; + int located; + int parsed; + int imported; + int preserveDoc; + xmlSchemaItemListPtr globals; /* Global components. */ + xmlSchemaItemListPtr locals; /* Local components. */ + + /* The owning main or import schema bucket. */ + xmlSchemaImportPtr ownerImport; +}; + +/** + * xmlSchemaBasicItem: + * + * The abstract base type for schema components. + */ +typedef struct _xmlSchemaBasicItem xmlSchemaBasicItem; +typedef xmlSchemaBasicItem *xmlSchemaBasicItemPtr; +struct _xmlSchemaBasicItem { + xmlSchemaTypeType type; +}; + +/** + * xmlSchemaAnnotItem: + * + * The abstract base type for annotated schema components. + * (Extends xmlSchemaBasicItem) + */ +typedef struct _xmlSchemaAnnotItem xmlSchemaAnnotItem; +typedef xmlSchemaAnnotItem *xmlSchemaAnnotItemPtr; +struct _xmlSchemaAnnotItem { + xmlSchemaTypeType type; + xmlSchemaAnnotPtr annot; +}; + +/** + * xmlSchemaTreeItem: + * + * The abstract base type for tree-like structured schema components. + * (Extends xmlSchemaAnnotItem) + */ +typedef struct _xmlSchemaTreeItem xmlSchemaTreeItem; +typedef xmlSchemaTreeItem *xmlSchemaTreeItemPtr; +struct _xmlSchemaTreeItem { + xmlSchemaTypeType type; + xmlSchemaAnnotPtr annot; + xmlSchemaTreeItemPtr next; + xmlSchemaTreeItemPtr children; +}; + + +#define XML_SCHEMA_ATTR_USE_FIXED 1<<0 +/** + * xmlSchemaAttributeUsePtr: + * + * The abstract base type for tree-like structured schema components. + * (Extends xmlSchemaTreeItem) + */ +typedef struct _xmlSchemaAttributeUse xmlSchemaAttributeUse; +typedef xmlSchemaAttributeUse *xmlSchemaAttributeUsePtr; +struct _xmlSchemaAttributeUse { + xmlSchemaTypeType type; + xmlSchemaAnnotPtr annot; + xmlSchemaAttributeUsePtr next; /* The next attr. use. */ + /* + * The attr. decl. OR a QName-ref. to an attr. decl. OR + * a QName-ref. to an attribute group definition. + */ + xmlSchemaAttributePtr attrDecl; + + int flags; + xmlNodePtr node; + int occurs; /* required, optional */ + const xmlChar * defValue; + xmlSchemaValPtr defVal; +}; + +/** + * xmlSchemaAttributeUseProhibPtr: + * + * A helper component to reflect attribute prohibitions. + * (Extends xmlSchemaBasicItem) + */ +typedef struct _xmlSchemaAttributeUseProhib xmlSchemaAttributeUseProhib; +typedef xmlSchemaAttributeUseProhib *xmlSchemaAttributeUseProhibPtr; +struct _xmlSchemaAttributeUseProhib { + xmlSchemaTypeType type; /* == XML_SCHEMA_EXTRA_ATTR_USE_PROHIB */ + xmlNodePtr node; + const xmlChar *name; + const xmlChar *targetNamespace; + int isRef; +}; + +/** + * xmlSchemaRedef: + */ +typedef struct _xmlSchemaRedef xmlSchemaRedef; +typedef xmlSchemaRedef *xmlSchemaRedefPtr; +struct _xmlSchemaRedef { + xmlSchemaRedefPtr next; + xmlSchemaBasicItemPtr item; /* The redefining component. */ + xmlSchemaBasicItemPtr reference; /* The referencing component. */ + xmlSchemaBasicItemPtr target; /* The to-be-redefined component. */ + const xmlChar *refName; /* The name of the to-be-redefined component. */ + const xmlChar *refTargetNs; /* The target namespace of the + to-be-redefined comp. */ + xmlSchemaBucketPtr targetBucket; /* The redefined schema. */ +}; + +/** + * xmlSchemaConstructionCtxt: + */ +typedef struct _xmlSchemaConstructionCtxt xmlSchemaConstructionCtxt; +typedef xmlSchemaConstructionCtxt *xmlSchemaConstructionCtxtPtr; +struct _xmlSchemaConstructionCtxt { + xmlSchemaPtr mainSchema; /* The main schema. */ + xmlSchemaBucketPtr mainBucket; /* The main schema bucket */ + xmlDictPtr dict; + xmlSchemaItemListPtr buckets; /* List of schema buckets. */ + /* xmlSchemaItemListPtr relations; */ /* List of schema relations. */ + xmlSchemaBucketPtr bucket; /* The current schema bucket */ + xmlSchemaItemListPtr pending; /* All Components of all schemas that + need to be fixed. */ + xmlHashTablePtr substGroups; + xmlSchemaRedefPtr redefs; + xmlSchemaRedefPtr lastRedef; +}; + +#define XML_SCHEMAS_PARSE_ERROR 1 +#define SCHEMAS_PARSE_OPTIONS XML_PARSE_NOENT + +struct _xmlSchemaParserCtxt { + int type; + void *errCtxt; /* user specific error context */ + xmlSchemaValidityErrorFunc error; /* the callback in case of errors */ + xmlSchemaValidityWarningFunc warning; /* the callback in case of warning */ + int err; + int nberrors; + xmlStructuredErrorFunc serror; + + xmlSchemaConstructionCtxtPtr constructor; + int ownsConstructor; /* TODO: Move this to parser *flags*. */ + + /* xmlSchemaPtr topschema; */ + /* xmlHashTablePtr namespaces; */ + + xmlSchemaPtr schema; /* The main schema in use */ + int counter; + + const xmlChar *URL; + xmlDocPtr doc; + int preserve; /* Whether the doc should be freed */ + + const char *buffer; + int size; + + /* + * Used to build complex element content models + */ + xmlAutomataPtr am; + xmlAutomataStatePtr start; + xmlAutomataStatePtr end; + xmlAutomataStatePtr state; + + xmlDictPtr dict; /* dictionnary for interned string names */ + xmlSchemaTypePtr ctxtType; /* The current context simple/complex type */ + int options; + xmlSchemaValidCtxtPtr vctxt; + int isS4S; + int isRedefine; + int xsiAssemble; + int stop; /* If the parser should stop; i.e. a critical error. */ + const xmlChar *targetNamespace; + xmlSchemaBucketPtr redefined; /* The schema to be redefined. */ + + xmlSchemaRedefPtr redef; /* Used for redefinitions. */ + int redefCounter; /* Used for redefinitions. */ + xmlSchemaItemListPtr attrProhibs; +}; + +/** + * xmlSchemaQNameRef: + * + * A component reference item (not a schema component) + * (Extends xmlSchemaBasicItem) + */ +typedef struct _xmlSchemaQNameRef xmlSchemaQNameRef; +typedef xmlSchemaQNameRef *xmlSchemaQNameRefPtr; +struct _xmlSchemaQNameRef { + xmlSchemaTypeType type; + xmlSchemaBasicItemPtr item; /* The resolved referenced item. */ + xmlSchemaTypeType itemType; + const xmlChar *name; + const xmlChar *targetNamespace; + xmlNodePtr node; +}; + +/** + * xmlSchemaParticle: + * + * A particle component. + * (Extends xmlSchemaTreeItem) + */ +typedef struct _xmlSchemaParticle xmlSchemaParticle; +typedef xmlSchemaParticle *xmlSchemaParticlePtr; +struct _xmlSchemaParticle { + xmlSchemaTypeType type; + xmlSchemaAnnotPtr annot; + xmlSchemaTreeItemPtr next; /* next particle */ + xmlSchemaTreeItemPtr children; /* the "term" (e.g. a model group, + a group definition, a XML_SCHEMA_EXTRA_QNAMEREF (if a reference), + etc.) */ + int minOccurs; + int maxOccurs; + xmlNodePtr node; +}; + +/** + * xmlSchemaModelGroup: + * + * A model group component. + * (Extends xmlSchemaTreeItem) + */ +typedef struct _xmlSchemaModelGroup xmlSchemaModelGroup; +typedef xmlSchemaModelGroup *xmlSchemaModelGroupPtr; +struct _xmlSchemaModelGroup { + xmlSchemaTypeType type; /* XML_SCHEMA_TYPE_SEQUENCE, XML_SCHEMA_TYPE_CHOICE, XML_SCHEMA_TYPE_ALL */ + xmlSchemaAnnotPtr annot; + xmlSchemaTreeItemPtr next; /* not used */ + xmlSchemaTreeItemPtr children; /* first particle (OR "element decl" OR "wildcard") */ + xmlNodePtr node; +}; + +#define XML_SCHEMA_MODEL_GROUP_DEF_MARKED 1<<0 +#define XML_SCHEMA_MODEL_GROUP_DEF_REDEFINED 1<<1 +/** + * xmlSchemaModelGroupDef: + * + * A model group definition component. + * (Extends xmlSchemaTreeItem) + */ +typedef struct _xmlSchemaModelGroupDef xmlSchemaModelGroupDef; +typedef xmlSchemaModelGroupDef *xmlSchemaModelGroupDefPtr; +struct _xmlSchemaModelGroupDef { + xmlSchemaTypeType type; /* XML_SCHEMA_TYPE_GROUP */ + xmlSchemaAnnotPtr annot; + xmlSchemaTreeItemPtr next; /* not used */ + xmlSchemaTreeItemPtr children; /* the "model group" */ + const xmlChar *name; + const xmlChar *targetNamespace; + xmlNodePtr node; + int flags; +}; + +typedef struct _xmlSchemaIDC xmlSchemaIDC; +typedef xmlSchemaIDC *xmlSchemaIDCPtr; + +/** + * xmlSchemaIDCSelect: + * + * The identity-constraint "field" and "selector" item, holding the + * XPath expression. + */ +typedef struct _xmlSchemaIDCSelect xmlSchemaIDCSelect; +typedef xmlSchemaIDCSelect *xmlSchemaIDCSelectPtr; +struct _xmlSchemaIDCSelect { + xmlSchemaIDCSelectPtr next; + xmlSchemaIDCPtr idc; + int index; /* an index position if significant for IDC key-sequences */ + const xmlChar *xpath; /* the XPath expression */ + void *xpathComp; /* the compiled XPath expression */ +}; + +/** + * xmlSchemaIDC: + * + * The identity-constraint definition component. + * (Extends xmlSchemaAnnotItem) + */ + +struct _xmlSchemaIDC { + xmlSchemaTypeType type; + xmlSchemaAnnotPtr annot; + xmlSchemaIDCPtr next; + xmlNodePtr node; + const xmlChar *name; + const xmlChar *targetNamespace; + xmlSchemaIDCSelectPtr selector; + xmlSchemaIDCSelectPtr fields; + int nbFields; + xmlSchemaQNameRefPtr ref; +}; + +/** + * xmlSchemaIDCAug: + * + * The augmented IDC information used for validation. + */ +typedef struct _xmlSchemaIDCAug xmlSchemaIDCAug; +typedef xmlSchemaIDCAug *xmlSchemaIDCAugPtr; +struct _xmlSchemaIDCAug { + xmlSchemaIDCAugPtr next; /* next in a list */ + xmlSchemaIDCPtr def; /* the IDC definition */ + int keyrefDepth; /* the lowest tree level to which IDC + tables need to be bubbled upwards */ +}; + +/** + * xmlSchemaPSVIIDCKeySequence: + * + * The key sequence of a node table item. + */ +typedef struct _xmlSchemaPSVIIDCKey xmlSchemaPSVIIDCKey; +typedef xmlSchemaPSVIIDCKey *xmlSchemaPSVIIDCKeyPtr; +struct _xmlSchemaPSVIIDCKey { + xmlSchemaTypePtr type; + xmlSchemaValPtr val; +}; + +/** + * xmlSchemaPSVIIDCNode: + * + * The node table item of a node table. + */ +typedef struct _xmlSchemaPSVIIDCNode xmlSchemaPSVIIDCNode; +typedef xmlSchemaPSVIIDCNode *xmlSchemaPSVIIDCNodePtr; +struct _xmlSchemaPSVIIDCNode { + xmlNodePtr node; + xmlSchemaPSVIIDCKeyPtr *keys; + int nodeLine; + int nodeQNameID; + +}; + +/** + * xmlSchemaPSVIIDCBinding: + * + * The identity-constraint binding item of the [identity-constraint table]. + */ +typedef struct _xmlSchemaPSVIIDCBinding xmlSchemaPSVIIDCBinding; +typedef xmlSchemaPSVIIDCBinding *xmlSchemaPSVIIDCBindingPtr; +struct _xmlSchemaPSVIIDCBinding { + xmlSchemaPSVIIDCBindingPtr next; /* next binding of a specific node */ + xmlSchemaIDCPtr definition; /* the IDC definition */ + xmlSchemaPSVIIDCNodePtr *nodeTable; /* array of key-sequences */ + int nbNodes; /* number of entries in the node table */ + int sizeNodes; /* size of the node table */ + xmlSchemaItemListPtr dupls; +}; + + +#define XPATH_STATE_OBJ_TYPE_IDC_SELECTOR 1 +#define XPATH_STATE_OBJ_TYPE_IDC_FIELD 2 + +#define XPATH_STATE_OBJ_MATCHES -2 +#define XPATH_STATE_OBJ_BLOCKED -3 + +typedef struct _xmlSchemaIDCMatcher xmlSchemaIDCMatcher; +typedef xmlSchemaIDCMatcher *xmlSchemaIDCMatcherPtr; + +/** + * xmlSchemaIDCStateObj: + * + * The state object used to evaluate XPath expressions. + */ +typedef struct _xmlSchemaIDCStateObj xmlSchemaIDCStateObj; +typedef xmlSchemaIDCStateObj *xmlSchemaIDCStateObjPtr; +struct _xmlSchemaIDCStateObj { + int type; + xmlSchemaIDCStateObjPtr next; /* next if in a list */ + int depth; /* depth of creation */ + int *history; /* list of (depth, state-id) tuples */ + int nbHistory; + int sizeHistory; + xmlSchemaIDCMatcherPtr matcher; /* the correspondent field/selector + matcher */ + xmlSchemaIDCSelectPtr sel; + void *xpathCtxt; +}; + +#define IDC_MATCHER 0 + +/** + * xmlSchemaIDCMatcher: + * + * Used to evaluate IDC selectors (and fields). + */ +struct _xmlSchemaIDCMatcher { + int type; + int depth; /* the tree depth at creation time */ + xmlSchemaIDCMatcherPtr next; /* next in the list */ + xmlSchemaIDCAugPtr aidc; /* the augmented IDC item */ + int idcType; + xmlSchemaPSVIIDCKeyPtr **keySeqs; /* the key-sequences of the target + elements */ + int sizeKeySeqs; + int targetDepth; + xmlSchemaItemListPtr targets; /* list of target-node + (xmlSchemaPSVIIDCNodePtr) entries */ +}; + +/* +* Element info flags. +*/ +#define XML_SCHEMA_NODE_INFO_FLAG_OWNED_NAMES 1<<0 +#define XML_SCHEMA_NODE_INFO_FLAG_OWNED_VALUES 1<<1 +#define XML_SCHEMA_ELEM_INFO_NILLED 1<<2 +#define XML_SCHEMA_ELEM_INFO_LOCAL_TYPE 1<<3 + +#define XML_SCHEMA_NODE_INFO_VALUE_NEEDED 1<<4 +#define XML_SCHEMA_ELEM_INFO_EMPTY 1<<5 +#define XML_SCHEMA_ELEM_INFO_HAS_CONTENT 1<<6 + +#define XML_SCHEMA_ELEM_INFO_HAS_ELEM_CONTENT 1<<7 +#define XML_SCHEMA_ELEM_INFO_ERR_BAD_CONTENT 1<<8 +#define XML_SCHEMA_NODE_INFO_ERR_NOT_EXPECTED 1<<9 +#define XML_SCHEMA_NODE_INFO_ERR_BAD_TYPE 1<<10 + +/** + * xmlSchemaNodeInfo: + * + * Holds information of an element node. + */ +struct _xmlSchemaNodeInfo { + int nodeType; + xmlNodePtr node; + int nodeLine; + const xmlChar *localName; + const xmlChar *nsName; + const xmlChar *value; + xmlSchemaValPtr val; /* the pre-computed value if any */ + xmlSchemaTypePtr typeDef; /* the complex/simple type definition if any */ + + int flags; /* combination of node info flags */ + + int valNeeded; + int normVal; + + xmlSchemaElementPtr decl; /* the element/attribute declaration */ + int depth; + xmlSchemaPSVIIDCBindingPtr idcTable; /* the table of PSVI IDC bindings + for the scope element*/ + xmlSchemaIDCMatcherPtr idcMatchers; /* the IDC matchers for the scope + element */ + xmlRegExecCtxtPtr regexCtxt; + + const xmlChar **nsBindings; /* Namespace bindings on this element */ + int nbNsBindings; + int sizeNsBindings; + + int hasKeyrefs; + int appliedXPath; /* Indicates that an XPath has been applied. */ +}; + +#define XML_SCHEMAS_ATTR_UNKNOWN 1 +#define XML_SCHEMAS_ATTR_ASSESSED 2 +#define XML_SCHEMAS_ATTR_PROHIBITED 3 +#define XML_SCHEMAS_ATTR_ERR_MISSING 4 +#define XML_SCHEMAS_ATTR_INVALID_VALUE 5 +#define XML_SCHEMAS_ATTR_ERR_NO_TYPE 6 +#define XML_SCHEMAS_ATTR_ERR_FIXED_VALUE 7 +#define XML_SCHEMAS_ATTR_DEFAULT 8 +#define XML_SCHEMAS_ATTR_VALIDATE_VALUE 9 +#define XML_SCHEMAS_ATTR_ERR_WILD_STRICT_NO_DECL 10 +#define XML_SCHEMAS_ATTR_HAS_ATTR_USE 11 +#define XML_SCHEMAS_ATTR_HAS_ATTR_DECL 12 +#define XML_SCHEMAS_ATTR_WILD_SKIP 13 +#define XML_SCHEMAS_ATTR_WILD_LAX_NO_DECL 14 +#define XML_SCHEMAS_ATTR_ERR_WILD_DUPLICATE_ID 15 +#define XML_SCHEMAS_ATTR_ERR_WILD_AND_USE_ID 16 +#define XML_SCHEMAS_ATTR_META 17 +/* +* @metaType values of xmlSchemaAttrInfo. +*/ +#define XML_SCHEMA_ATTR_INFO_META_XSI_TYPE 1 +#define XML_SCHEMA_ATTR_INFO_META_XSI_NIL 2 +#define XML_SCHEMA_ATTR_INFO_META_XSI_SCHEMA_LOC 3 +#define XML_SCHEMA_ATTR_INFO_META_XSI_NO_NS_SCHEMA_LOC 4 +#define XML_SCHEMA_ATTR_INFO_META_XMLNS 5 + +typedef struct _xmlSchemaAttrInfo xmlSchemaAttrInfo; +typedef xmlSchemaAttrInfo *xmlSchemaAttrInfoPtr; +struct _xmlSchemaAttrInfo { + int nodeType; + xmlNodePtr node; + int nodeLine; + const xmlChar *localName; + const xmlChar *nsName; + const xmlChar *value; + xmlSchemaValPtr val; /* the pre-computed value if any */ + xmlSchemaTypePtr typeDef; /* the complex/simple type definition if any */ + int flags; /* combination of node info flags */ + + xmlSchemaAttributePtr decl; /* the attribute declaration */ + xmlSchemaAttributeUsePtr use; /* the attribute use */ + int state; + int metaType; + const xmlChar *vcValue; /* the value constraint value */ + xmlSchemaNodeInfoPtr parent; +}; + + +#define XML_SCHEMA_VALID_CTXT_FLAG_STREAM 1 +/** + * xmlSchemaValidCtxt: + * + * A Schemas validation context + */ +struct _xmlSchemaValidCtxt { + int type; + void *errCtxt; /* user specific data block */ + xmlSchemaValidityErrorFunc error; /* the callback in case of errors */ + xmlSchemaValidityWarningFunc warning; /* the callback in case of warning */ + xmlStructuredErrorFunc serror; + + xmlSchemaPtr schema; /* The schema in use */ + xmlDocPtr doc; + xmlParserInputBufferPtr input; + xmlCharEncoding enc; + xmlSAXHandlerPtr sax; + xmlParserCtxtPtr parserCtxt; + void *user_data; /* TODO: What is this for? */ + + int err; + int nberrors; + + xmlNodePtr node; + xmlNodePtr cur; + /* xmlSchemaTypePtr type; */ + + xmlRegExecCtxtPtr regexp; + xmlSchemaValPtr value; + + int valueWS; + int options; + xmlNodePtr validationRoot; + xmlSchemaParserCtxtPtr pctxt; + int xsiAssemble; + + int depth; + xmlSchemaNodeInfoPtr *elemInfos; /* array of element informations */ + int sizeElemInfos; + xmlSchemaNodeInfoPtr inode; /* the current element information */ + + xmlSchemaIDCAugPtr aidcs; /* a list of augmented IDC informations */ + + xmlSchemaIDCStateObjPtr xpathStates; /* first active state object. */ + xmlSchemaIDCStateObjPtr xpathStatePool; /* first stored state object. */ + + xmlSchemaPSVIIDCNodePtr *idcNodes; /* list of all IDC node-table entries*/ + int nbIdcNodes; + int sizeIdcNodes; + + xmlSchemaPSVIIDCKeyPtr *idcKeys; /* list of all IDC node-table entries */ + int nbIdcKeys; + int sizeIdcKeys; + + int flags; + + xmlDictPtr dict; + +#ifdef LIBXML_READER_ENABLED + xmlTextReaderPtr reader; +#endif + + xmlSchemaAttrInfoPtr *attrInfos; + int nbAttrInfos; + int sizeAttrInfos; + + int skipDepth; + xmlSchemaItemListPtr nodeQNames; + int hasKeyrefs; + int createIDCNodeTables; + int psviExposeIDCNodeTables; +}; + +/** + * xmlSchemaSubstGroup: + * + * + */ +typedef struct _xmlSchemaSubstGroup xmlSchemaSubstGroup; +typedef xmlSchemaSubstGroup *xmlSchemaSubstGroupPtr; +struct _xmlSchemaSubstGroup { + xmlSchemaElementPtr head; + xmlSchemaItemListPtr members; +}; + +/************************************************************************ + * * + * Some predeclarations * + * * + ************************************************************************/ + +static int xmlSchemaParseInclude(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaPtr schema, + xmlNodePtr node); +static int xmlSchemaParseRedefine(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaPtr schema, + xmlNodePtr node); +static int +xmlSchemaTypeFixup(xmlSchemaTypePtr type, + xmlSchemaAbstractCtxtPtr ctxt); +static const xmlChar * +xmlSchemaFacetTypeToString(xmlSchemaTypeType type); +static int +xmlSchemaParseImport(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, + xmlNodePtr node); +static int +xmlSchemaCheckFacetValues(xmlSchemaTypePtr typeDecl, + xmlSchemaParserCtxtPtr ctxt); +static void +xmlSchemaClearValidCtxt(xmlSchemaValidCtxtPtr vctxt); +static xmlSchemaWhitespaceValueType +xmlSchemaGetWhiteSpaceFacetValue(xmlSchemaTypePtr type); +static xmlSchemaTreeItemPtr +xmlSchemaParseModelGroup(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, + xmlNodePtr node, xmlSchemaTypeType type, + int withParticle); +static const xmlChar * +xmlSchemaGetComponentTypeStr(xmlSchemaBasicItemPtr item); +static xmlSchemaTypeLinkPtr +xmlSchemaGetUnionSimpleTypeMemberTypes(xmlSchemaTypePtr type); +static void +xmlSchemaInternalErr(xmlSchemaAbstractCtxtPtr actxt, + const char *funcName, + const char *message); +static int +xmlSchemaCheckCOSSTDerivedOK(xmlSchemaAbstractCtxtPtr ctxt, + xmlSchemaTypePtr type, + xmlSchemaTypePtr baseType, + int subset); +static void +xmlSchemaCheckElementDeclComponent(xmlSchemaElementPtr elemDecl, + xmlSchemaParserCtxtPtr ctxt); +static void +xmlSchemaComponentListFree(xmlSchemaItemListPtr list); +static xmlSchemaQNameRefPtr +xmlSchemaParseAttributeGroupRef(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaPtr schema, + xmlNodePtr node); + +/************************************************************************ + * * + * Helper functions * + * * + ************************************************************************/ + +/** + * xmlSchemaItemTypeToStr: + * @type: the type of the schema item + * + * Returns the component name of a schema item. + */ +static const xmlChar * +xmlSchemaItemTypeToStr(xmlSchemaTypeType type) +{ + switch (type) { + case XML_SCHEMA_TYPE_BASIC: + return(BAD_CAST "simple type definition"); + case XML_SCHEMA_TYPE_SIMPLE: + return(BAD_CAST "simple type definition"); + case XML_SCHEMA_TYPE_COMPLEX: + return(BAD_CAST "complex type definition"); + case XML_SCHEMA_TYPE_ELEMENT: + return(BAD_CAST "element declaration"); + case XML_SCHEMA_TYPE_ATTRIBUTE_USE: + return(BAD_CAST "attribute use"); + case XML_SCHEMA_TYPE_ATTRIBUTE: + return(BAD_CAST "attribute declaration"); + case XML_SCHEMA_TYPE_GROUP: + return(BAD_CAST "model group definition"); + case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: + return(BAD_CAST "attribute group definition"); + case XML_SCHEMA_TYPE_NOTATION: + return(BAD_CAST "notation declaration"); + case XML_SCHEMA_TYPE_SEQUENCE: + return(BAD_CAST "model group (sequence)"); + case XML_SCHEMA_TYPE_CHOICE: + return(BAD_CAST "model group (choice)"); + case XML_SCHEMA_TYPE_ALL: + return(BAD_CAST "model group (all)"); + case XML_SCHEMA_TYPE_PARTICLE: + return(BAD_CAST "particle"); + case XML_SCHEMA_TYPE_IDC_UNIQUE: + return(BAD_CAST "unique identity-constraint"); + /* return(BAD_CAST "IDC (unique)"); */ + case XML_SCHEMA_TYPE_IDC_KEY: + return(BAD_CAST "key identity-constraint"); + /* return(BAD_CAST "IDC (key)"); */ + case XML_SCHEMA_TYPE_IDC_KEYREF: + return(BAD_CAST "keyref identity-constraint"); + /* return(BAD_CAST "IDC (keyref)"); */ + case XML_SCHEMA_TYPE_ANY: + return(BAD_CAST "wildcard (any)"); + case XML_SCHEMA_EXTRA_QNAMEREF: + return(BAD_CAST "[helper component] QName reference"); + case XML_SCHEMA_EXTRA_ATTR_USE_PROHIB: + return(BAD_CAST "[helper component] attribute use prohibition"); + default: + return(BAD_CAST "Not a schema component"); + } +} + +/** + * xmlSchemaGetComponentTypeStr: + * @type: the type of the schema item + * + * Returns the component name of a schema item. + */ +static const xmlChar * +xmlSchemaGetComponentTypeStr(xmlSchemaBasicItemPtr item) +{ + switch (item->type) { + case XML_SCHEMA_TYPE_BASIC: + if (WXS_IS_COMPLEX(WXS_TYPE_CAST item)) + return(BAD_CAST "complex type definition"); + else + return(BAD_CAST "simple type definition"); + default: + return(xmlSchemaItemTypeToStr(item->type)); + } +} + +/** + * xmlSchemaGetComponentNode: + * @item: a schema component + * + * Returns node associated with the schema component. + * NOTE that such a node need not be available; plus, a component's + * node need not to reflect the component directly, since there is no + * one-to-one relationship between the XML Schema representation and + * the component representation. + */ +static xmlNodePtr +xmlSchemaGetComponentNode(xmlSchemaBasicItemPtr item) +{ + switch (item->type) { + case XML_SCHEMA_TYPE_ELEMENT: + return (((xmlSchemaElementPtr) item)->node); + case XML_SCHEMA_TYPE_ATTRIBUTE: + return (((xmlSchemaAttributePtr) item)->node); + case XML_SCHEMA_TYPE_COMPLEX: + case XML_SCHEMA_TYPE_SIMPLE: + return (((xmlSchemaTypePtr) item)->node); + case XML_SCHEMA_TYPE_ANY: + case XML_SCHEMA_TYPE_ANY_ATTRIBUTE: + return (((xmlSchemaWildcardPtr) item)->node); + case XML_SCHEMA_TYPE_PARTICLE: + return (((xmlSchemaParticlePtr) item)->node); + case XML_SCHEMA_TYPE_SEQUENCE: + case XML_SCHEMA_TYPE_CHOICE: + case XML_SCHEMA_TYPE_ALL: + return (((xmlSchemaModelGroupPtr) item)->node); + case XML_SCHEMA_TYPE_GROUP: + return (((xmlSchemaModelGroupDefPtr) item)->node); + case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: + return (((xmlSchemaAttributeGroupPtr) item)->node); + case XML_SCHEMA_TYPE_IDC_UNIQUE: + case XML_SCHEMA_TYPE_IDC_KEY: + case XML_SCHEMA_TYPE_IDC_KEYREF: + return (((xmlSchemaIDCPtr) item)->node); + case XML_SCHEMA_EXTRA_QNAMEREF: + return(((xmlSchemaQNameRefPtr) item)->node); + /* TODO: What to do with NOTATIONs? + case XML_SCHEMA_TYPE_NOTATION: + return (((xmlSchemaNotationPtr) item)->node); + */ + case XML_SCHEMA_TYPE_ATTRIBUTE_USE: + return (((xmlSchemaAttributeUsePtr) item)->node); + default: + return (NULL); + } +} + +#if 0 +/** + * xmlSchemaGetNextComponent: + * @item: a schema component + * + * Returns the next sibling of the schema component. + */ +static xmlSchemaBasicItemPtr +xmlSchemaGetNextComponent(xmlSchemaBasicItemPtr item) +{ + switch (item->type) { + case XML_SCHEMA_TYPE_ELEMENT: + return ((xmlSchemaBasicItemPtr) ((xmlSchemaElementPtr) item)->next); + case XML_SCHEMA_TYPE_ATTRIBUTE: + return ((xmlSchemaBasicItemPtr) ((xmlSchemaAttributePtr) item)->next); + case XML_SCHEMA_TYPE_COMPLEX: + case XML_SCHEMA_TYPE_SIMPLE: + return ((xmlSchemaBasicItemPtr) ((xmlSchemaTypePtr) item)->next); + case XML_SCHEMA_TYPE_ANY: + case XML_SCHEMA_TYPE_ANY_ATTRIBUTE: + return (NULL); + case XML_SCHEMA_TYPE_PARTICLE: + return ((xmlSchemaBasicItemPtr) ((xmlSchemaParticlePtr) item)->next); + case XML_SCHEMA_TYPE_SEQUENCE: + case XML_SCHEMA_TYPE_CHOICE: + case XML_SCHEMA_TYPE_ALL: + return (NULL); + case XML_SCHEMA_TYPE_GROUP: + return (NULL); + case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: + return ((xmlSchemaBasicItemPtr) ((xmlSchemaAttributeGroupPtr) item)->next); + case XML_SCHEMA_TYPE_IDC_UNIQUE: + case XML_SCHEMA_TYPE_IDC_KEY: + case XML_SCHEMA_TYPE_IDC_KEYREF: + return ((xmlSchemaBasicItemPtr) ((xmlSchemaIDCPtr) item)->next); + default: + return (NULL); + } +} +#endif + + +/** + * xmlSchemaFormatQName: + * @buf: the string buffer + * @namespaceName: the namespace name + * @localName: the local name + * + * Returns the given QName in the format "{namespaceName}localName" or + * just "localName" if @namespaceName is NULL. + * + * Returns the localName if @namespaceName is NULL, a formatted + * string otherwise. + */ +static const xmlChar* +xmlSchemaFormatQName(xmlChar **buf, + const xmlChar *namespaceName, + const xmlChar *localName) +{ + FREE_AND_NULL(*buf) + if (namespaceName != NULL) { + *buf = xmlStrdup(BAD_CAST "{"); + *buf = xmlStrcat(*buf, namespaceName); + *buf = xmlStrcat(*buf, BAD_CAST "}"); + } + if (localName != NULL) { + if (namespaceName == NULL) + return(localName); + *buf = xmlStrcat(*buf, localName); + } else { + *buf = xmlStrcat(*buf, BAD_CAST "(NULL)"); + } + return ((const xmlChar *) *buf); +} + +static const xmlChar* +xmlSchemaFormatQNameNs(xmlChar **buf, xmlNsPtr ns, const xmlChar *localName) +{ + if (ns != NULL) + return (xmlSchemaFormatQName(buf, ns->href, localName)); + else + return (xmlSchemaFormatQName(buf, NULL, localName)); +} + +static const xmlChar * +xmlSchemaGetComponentName(xmlSchemaBasicItemPtr item) +{ + switch (item->type) { + case XML_SCHEMA_TYPE_ELEMENT: + return (((xmlSchemaElementPtr) item)->name); + case XML_SCHEMA_TYPE_ATTRIBUTE: + return (((xmlSchemaAttributePtr) item)->name); + case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: + return (((xmlSchemaAttributeGroupPtr) item)->name); + case XML_SCHEMA_TYPE_BASIC: + case XML_SCHEMA_TYPE_SIMPLE: + case XML_SCHEMA_TYPE_COMPLEX: + return (((xmlSchemaTypePtr) item)->name); + case XML_SCHEMA_TYPE_GROUP: + return (((xmlSchemaModelGroupDefPtr) item)->name); + case XML_SCHEMA_TYPE_IDC_KEY: + case XML_SCHEMA_TYPE_IDC_UNIQUE: + case XML_SCHEMA_TYPE_IDC_KEYREF: + return (((xmlSchemaIDCPtr) item)->name); + case XML_SCHEMA_TYPE_ATTRIBUTE_USE: + if (WXS_ATTRUSE_DECL(item) != NULL) { + return(xmlSchemaGetComponentName( + WXS_BASIC_CAST WXS_ATTRUSE_DECL(item))); + } else + return(NULL); + case XML_SCHEMA_EXTRA_QNAMEREF: + return (((xmlSchemaQNameRefPtr) item)->name); + case XML_SCHEMA_TYPE_NOTATION: + return (((xmlSchemaNotationPtr) item)->name); + default: + /* + * Other components cannot have names. + */ + break; + } + return (NULL); +} + +#define xmlSchemaGetQNameRefName(r) (WXS_QNAME_CAST (r))->name +#define xmlSchemaGetQNameRefTargetNs(r) (WXS_QNAME_CAST (r))->targetNamespace +/* +static const xmlChar * +xmlSchemaGetQNameRefName(void *ref) +{ + return(((xmlSchemaQNameRefPtr) ref)->name); +} + +static const xmlChar * +xmlSchemaGetQNameRefTargetNs(void *ref) +{ + return(((xmlSchemaQNameRefPtr) ref)->targetNamespace); +} +*/ + +static const xmlChar * +xmlSchemaGetComponentTargetNs(xmlSchemaBasicItemPtr item) +{ + switch (item->type) { + case XML_SCHEMA_TYPE_ELEMENT: + return (((xmlSchemaElementPtr) item)->targetNamespace); + case XML_SCHEMA_TYPE_ATTRIBUTE: + return (((xmlSchemaAttributePtr) item)->targetNamespace); + case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: + return (((xmlSchemaAttributeGroupPtr) item)->targetNamespace); + case XML_SCHEMA_TYPE_BASIC: + return (BAD_CAST "http://www.w3.org/2001/XMLSchema"); + case XML_SCHEMA_TYPE_SIMPLE: + case XML_SCHEMA_TYPE_COMPLEX: + return (((xmlSchemaTypePtr) item)->targetNamespace); + case XML_SCHEMA_TYPE_GROUP: + return (((xmlSchemaModelGroupDefPtr) item)->targetNamespace); + case XML_SCHEMA_TYPE_IDC_KEY: + case XML_SCHEMA_TYPE_IDC_UNIQUE: + case XML_SCHEMA_TYPE_IDC_KEYREF: + return (((xmlSchemaIDCPtr) item)->targetNamespace); + case XML_SCHEMA_TYPE_ATTRIBUTE_USE: + if (WXS_ATTRUSE_DECL(item) != NULL) { + return(xmlSchemaGetComponentTargetNs( + WXS_BASIC_CAST WXS_ATTRUSE_DECL(item))); + } + /* TODO: Will returning NULL break something? */ + break; + case XML_SCHEMA_EXTRA_QNAMEREF: + return (((xmlSchemaQNameRefPtr) item)->targetNamespace); + case XML_SCHEMA_TYPE_NOTATION: + return (((xmlSchemaNotationPtr) item)->targetNamespace); + default: + /* + * Other components cannot have names. + */ + break; + } + return (NULL); +} + +static const xmlChar* +xmlSchemaGetComponentQName(xmlChar **buf, + void *item) +{ + return (xmlSchemaFormatQName(buf, + xmlSchemaGetComponentTargetNs((xmlSchemaBasicItemPtr) item), + xmlSchemaGetComponentName((xmlSchemaBasicItemPtr) item))); +} + +static const xmlChar* +xmlSchemaGetComponentDesignation(xmlChar **buf, void *item) +{ + xmlChar *str = NULL; + + *buf = xmlStrcat(*buf, WXS_ITEM_TYPE_NAME(item)); + *buf = xmlStrcat(*buf, BAD_CAST " '"); + *buf = xmlStrcat(*buf, xmlSchemaGetComponentQName(&str, + (xmlSchemaBasicItemPtr) item)); + *buf = xmlStrcat(*buf, BAD_CAST "'"); + FREE_AND_NULL(str); + return(*buf); +} + +static const xmlChar* +xmlSchemaGetIDCDesignation(xmlChar **buf, xmlSchemaIDCPtr idc) +{ + return(xmlSchemaGetComponentDesignation(buf, idc)); +} + +/** + * xmlSchemaWildcardPCToString: + * @pc: the type of processContents + * + * Returns a string representation of the type of + * processContents. + */ +static const xmlChar * +xmlSchemaWildcardPCToString(int pc) +{ + switch (pc) { + case XML_SCHEMAS_ANY_SKIP: + return (BAD_CAST "skip"); + case XML_SCHEMAS_ANY_LAX: + return (BAD_CAST "lax"); + case XML_SCHEMAS_ANY_STRICT: + return (BAD_CAST "strict"); + default: + return (BAD_CAST "invalid process contents"); + } +} + +/** + * xmlSchemaGetCanonValueWhtspExt: + * @val: the precomputed value + * @retValue: the returned value + * @ws: the whitespace type of the value + * + * Get a the cononical representation of the value. + * The caller has to free the returned retValue. + * + * Returns 0 if the value could be built and -1 in case of + * API errors or if the value type is not supported yet. + */ +static int +xmlSchemaGetCanonValueWhtspExt(xmlSchemaValPtr val, + xmlSchemaWhitespaceValueType ws, + xmlChar **retValue) +{ + int list; + xmlSchemaValType valType; + const xmlChar *value, *value2 = NULL; + + + if ((retValue == NULL) || (val == NULL)) + return (-1); + list = xmlSchemaValueGetNext(val) ? 1 : 0; + *retValue = NULL; + do { + value = NULL; + valType = xmlSchemaGetValType(val); + switch (valType) { + case XML_SCHEMAS_STRING: + case XML_SCHEMAS_NORMSTRING: + case XML_SCHEMAS_ANYSIMPLETYPE: + value = xmlSchemaValueGetAsString(val); + if (value != NULL) { + if (ws == XML_SCHEMA_WHITESPACE_COLLAPSE) + value2 = xmlSchemaCollapseString(value); + else if (ws == XML_SCHEMA_WHITESPACE_REPLACE) + value2 = xmlSchemaWhiteSpaceReplace(value); + if (value2 != NULL) + value = value2; + } + break; + default: + if (xmlSchemaGetCanonValue(val, &value2) == -1) { + if (value2 != NULL) + xmlFree((xmlChar *) value2); + goto internal_error; + } + value = value2; + } + if (*retValue == NULL) + if (value == NULL) { + if (! list) + *retValue = xmlStrdup(BAD_CAST ""); + } else + *retValue = xmlStrdup(value); + else if (value != NULL) { + /* List. */ + *retValue = xmlStrcat((xmlChar *) *retValue, BAD_CAST " "); + *retValue = xmlStrcat((xmlChar *) *retValue, value); + } + FREE_AND_NULL(value2) + val = xmlSchemaValueGetNext(val); + } while (val != NULL); + + return (0); +internal_error: + if (*retValue != NULL) + xmlFree((xmlChar *) (*retValue)); + if (value2 != NULL) + xmlFree((xmlChar *) value2); + return (-1); +} + +/** + * xmlSchemaFormatItemForReport: + * @buf: the string buffer + * @itemDes: the designation of the item + * @itemName: the name of the item + * @item: the item as an object + * @itemNode: the node of the item + * @local: the local name + * @parsing: if the function is used during the parse + * + * Returns a representation of the given item used + * for error reports. + * + * The following order is used to build the resulting + * designation if the arguments are not NULL: + * 1a. If itemDes not NULL -> itemDes + * 1b. If (itemDes not NULL) and (itemName not NULL) + * -> itemDes + itemName + * 2. If the preceding was NULL and (item not NULL) -> item + * 3. If the preceding was NULL and (itemNode not NULL) -> itemNode + * + * If the itemNode is an attribute node, the name of the attribute + * will be appended to the result. + * + * Returns the formatted string and sets @buf to the resulting value. + */ +static xmlChar* +xmlSchemaFormatItemForReport(xmlChar **buf, + const xmlChar *itemDes, + xmlSchemaBasicItemPtr item, + xmlNodePtr itemNode) +{ + xmlChar *str = NULL; + int named = 1; + + if (*buf != NULL) { + xmlFree(*buf); + *buf = NULL; + } + + if (itemDes != NULL) { + *buf = xmlStrdup(itemDes); + } else if (item != NULL) { + switch (item->type) { + case XML_SCHEMA_TYPE_BASIC: { + xmlSchemaTypePtr type = WXS_TYPE_CAST item; + + if (WXS_IS_ATOMIC(type)) + *buf = xmlStrdup(BAD_CAST "atomic type 'xs:"); + else if (WXS_IS_LIST(type)) + *buf = xmlStrdup(BAD_CAST "list type 'xs:"); + else if (WXS_IS_UNION(type)) + *buf = xmlStrdup(BAD_CAST "union type 'xs:"); + else + *buf = xmlStrdup(BAD_CAST "simple type 'xs:"); + *buf = xmlStrcat(*buf, type->name); + *buf = xmlStrcat(*buf, BAD_CAST "'"); + } + break; + case XML_SCHEMA_TYPE_SIMPLE: { + xmlSchemaTypePtr type = WXS_TYPE_CAST item; + + if (type->flags & XML_SCHEMAS_TYPE_GLOBAL) { + *buf = xmlStrdup(BAD_CAST""); + } else { + *buf = xmlStrdup(BAD_CAST "local "); + } + if (WXS_IS_ATOMIC(type)) + *buf = xmlStrcat(*buf, BAD_CAST "atomic type"); + else if (WXS_IS_LIST(type)) + *buf = xmlStrcat(*buf, BAD_CAST "list type"); + else if (WXS_IS_UNION(type)) + *buf = xmlStrcat(*buf, BAD_CAST "union type"); + else + *buf = xmlStrcat(*buf, BAD_CAST "simple type"); + if (type->flags & XML_SCHEMAS_TYPE_GLOBAL) { + *buf = xmlStrcat(*buf, BAD_CAST " '"); + *buf = xmlStrcat(*buf, type->name); + *buf = xmlStrcat(*buf, BAD_CAST "'"); + } + } + break; + case XML_SCHEMA_TYPE_COMPLEX: { + xmlSchemaTypePtr type = WXS_TYPE_CAST item; + + if (type->flags & XML_SCHEMAS_TYPE_GLOBAL) + *buf = xmlStrdup(BAD_CAST ""); + else + *buf = xmlStrdup(BAD_CAST "local "); + *buf = xmlStrcat(*buf, BAD_CAST "complex type"); + if (type->flags & XML_SCHEMAS_TYPE_GLOBAL) { + *buf = xmlStrcat(*buf, BAD_CAST " '"); + *buf = xmlStrcat(*buf, type->name); + *buf = xmlStrcat(*buf, BAD_CAST "'"); + } + } + break; + case XML_SCHEMA_TYPE_ATTRIBUTE_USE: { + xmlSchemaAttributeUsePtr ause; + + ause = WXS_ATTR_USE_CAST item; + *buf = xmlStrdup(BAD_CAST "attribute use "); + if (WXS_ATTRUSE_DECL(ause) != NULL) { + *buf = xmlStrcat(*buf, BAD_CAST "'"); + *buf = xmlStrcat(*buf, + xmlSchemaGetComponentQName(&str, WXS_ATTRUSE_DECL(ause))); + FREE_AND_NULL(str) + *buf = xmlStrcat(*buf, BAD_CAST "'"); + } else { + *buf = xmlStrcat(*buf, BAD_CAST "(unknown)"); + } + } + break; + case XML_SCHEMA_TYPE_ATTRIBUTE: { + xmlSchemaAttributePtr attr; + + attr = (xmlSchemaAttributePtr) item; + *buf = xmlStrdup(BAD_CAST "attribute decl."); + *buf = xmlStrcat(*buf, BAD_CAST " '"); + *buf = xmlStrcat(*buf, xmlSchemaFormatQName(&str, + attr->targetNamespace, attr->name)); + FREE_AND_NULL(str) + *buf = xmlStrcat(*buf, BAD_CAST "'"); + } + break; + case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: + xmlSchemaGetComponentDesignation(buf, item); + break; + case XML_SCHEMA_TYPE_ELEMENT: { + xmlSchemaElementPtr elem; + + elem = (xmlSchemaElementPtr) item; + *buf = xmlStrdup(BAD_CAST "element decl."); + *buf = xmlStrcat(*buf, BAD_CAST " '"); + *buf = xmlStrcat(*buf, xmlSchemaFormatQName(&str, + elem->targetNamespace, elem->name)); + *buf = xmlStrcat(*buf, BAD_CAST "'"); + } + break; + case XML_SCHEMA_TYPE_IDC_UNIQUE: + case XML_SCHEMA_TYPE_IDC_KEY: + case XML_SCHEMA_TYPE_IDC_KEYREF: + if (item->type == XML_SCHEMA_TYPE_IDC_UNIQUE) + *buf = xmlStrdup(BAD_CAST "unique '"); + else if (item->type == XML_SCHEMA_TYPE_IDC_KEY) + *buf = xmlStrdup(BAD_CAST "key '"); + else + *buf = xmlStrdup(BAD_CAST "keyRef '"); + *buf = xmlStrcat(*buf, ((xmlSchemaIDCPtr) item)->name); + *buf = xmlStrcat(*buf, BAD_CAST "'"); + break; + case XML_SCHEMA_TYPE_ANY: + case XML_SCHEMA_TYPE_ANY_ATTRIBUTE: + *buf = xmlStrdup(xmlSchemaWildcardPCToString( + ((xmlSchemaWildcardPtr) item)->processContents)); + *buf = xmlStrcat(*buf, BAD_CAST " wildcard"); + break; + case XML_SCHEMA_FACET_MININCLUSIVE: + case XML_SCHEMA_FACET_MINEXCLUSIVE: + case XML_SCHEMA_FACET_MAXINCLUSIVE: + case XML_SCHEMA_FACET_MAXEXCLUSIVE: + case XML_SCHEMA_FACET_TOTALDIGITS: + case XML_SCHEMA_FACET_FRACTIONDIGITS: + case XML_SCHEMA_FACET_PATTERN: + case XML_SCHEMA_FACET_ENUMERATION: + case XML_SCHEMA_FACET_WHITESPACE: + case XML_SCHEMA_FACET_LENGTH: + case XML_SCHEMA_FACET_MAXLENGTH: + case XML_SCHEMA_FACET_MINLENGTH: + *buf = xmlStrdup(BAD_CAST "facet '"); + *buf = xmlStrcat(*buf, xmlSchemaFacetTypeToString(item->type)); + *buf = xmlStrcat(*buf, BAD_CAST "'"); + break; + case XML_SCHEMA_TYPE_GROUP: { + *buf = xmlStrdup(BAD_CAST "model group def."); + *buf = xmlStrcat(*buf, BAD_CAST " '"); + *buf = xmlStrcat(*buf, xmlSchemaGetComponentQName(&str, item)); + *buf = xmlStrcat(*buf, BAD_CAST "'"); + FREE_AND_NULL(str) + } + break; + case XML_SCHEMA_TYPE_SEQUENCE: + case XML_SCHEMA_TYPE_CHOICE: + case XML_SCHEMA_TYPE_ALL: + case XML_SCHEMA_TYPE_PARTICLE: + *buf = xmlStrdup(WXS_ITEM_TYPE_NAME(item)); + break; + case XML_SCHEMA_TYPE_NOTATION: { + *buf = xmlStrdup(WXS_ITEM_TYPE_NAME(item)); + *buf = xmlStrcat(*buf, BAD_CAST " '"); + *buf = xmlStrcat(*buf, xmlSchemaGetComponentQName(&str, item)); + *buf = xmlStrcat(*buf, BAD_CAST "'"); + FREE_AND_NULL(str); + } + default: + named = 0; + } + } else + named = 0; + + if ((named == 0) && (itemNode != NULL)) { + xmlNodePtr elem; + + if (itemNode->type == XML_ATTRIBUTE_NODE) + elem = itemNode->parent; + else + elem = itemNode; + *buf = xmlStrdup(BAD_CAST "Element '"); + if (elem->ns != NULL) { + *buf = xmlStrcat(*buf, + xmlSchemaFormatQName(&str, elem->ns->href, elem->name)); + FREE_AND_NULL(str) + } else + *buf = xmlStrcat(*buf, elem->name); + *buf = xmlStrcat(*buf, BAD_CAST "'"); + + } + if ((itemNode != NULL) && (itemNode->type == XML_ATTRIBUTE_NODE)) { + *buf = xmlStrcat(*buf, BAD_CAST ", attribute '"); + if (itemNode->ns != NULL) { + *buf = xmlStrcat(*buf, xmlSchemaFormatQName(&str, + itemNode->ns->href, itemNode->name)); + FREE_AND_NULL(str) + } else + *buf = xmlStrcat(*buf, itemNode->name); + *buf = xmlStrcat(*buf, BAD_CAST "'"); + } + FREE_AND_NULL(str) + + return (*buf); +} + +/** + * xmlSchemaFormatFacetEnumSet: + * @buf: the string buffer + * @type: the type holding the enumeration facets + * + * Builds a string consisting of all enumeration elements. + * + * Returns a string of all enumeration elements. + */ +static const xmlChar * +xmlSchemaFormatFacetEnumSet(xmlSchemaAbstractCtxtPtr actxt, + xmlChar **buf, xmlSchemaTypePtr type) +{ + xmlSchemaFacetPtr facet; + xmlSchemaWhitespaceValueType ws; + xmlChar *value = NULL; + int res, found = 0; + + if (*buf != NULL) + xmlFree(*buf); + *buf = NULL; + + do { + /* + * Use the whitespace type of the base type. + */ + ws = xmlSchemaGetWhiteSpaceFacetValue(type->baseType); + for (facet = type->facets; facet != NULL; facet = facet->next) { + if (facet->type != XML_SCHEMA_FACET_ENUMERATION) + continue; + found = 1; + res = xmlSchemaGetCanonValueWhtspExt(facet->val, + ws, &value); + if (res == -1) { + xmlSchemaInternalErr(actxt, + "xmlSchemaFormatFacetEnumSet", + "compute the canonical lexical representation"); + if (*buf != NULL) + xmlFree(*buf); + *buf = NULL; + return (NULL); + } + if (*buf == NULL) + *buf = xmlStrdup(BAD_CAST "'"); + else + *buf = xmlStrcat(*buf, BAD_CAST ", '"); + *buf = xmlStrcat(*buf, BAD_CAST value); + *buf = xmlStrcat(*buf, BAD_CAST "'"); + if (value != NULL) { + xmlFree((xmlChar *)value); + value = NULL; + } + } + /* + * The enumeration facet of a type restricts the enumeration + * facet of the ancestor type; i.e., such restricted enumerations + * do not belong to the set of the given type. Thus we break + * on the first found enumeration. + */ + if (found) + break; + type = type->baseType; + } while ((type != NULL) && (type->type != XML_SCHEMA_TYPE_BASIC)); + + return ((const xmlChar *) *buf); +} + +/************************************************************************ + * * + * Error functions * + * * + ************************************************************************/ + +#if 0 +static void +xmlSchemaErrMemory(const char *msg) +{ + __xmlSimpleError(XML_FROM_SCHEMASP, XML_ERR_NO_MEMORY, NULL, NULL, + msg); +} +#endif + +static void +xmlSchemaPSimpleErr(const char *msg) +{ + __xmlSimpleError(XML_FROM_SCHEMASP, XML_ERR_NO_MEMORY, NULL, NULL, + msg); +} + +/** + * xmlSchemaPErrMemory: + * @node: a context node + * @extra: extra informations + * + * Handle an out of memory condition + */ +static void +xmlSchemaPErrMemory(xmlSchemaParserCtxtPtr ctxt, + const char *extra, xmlNodePtr node) +{ + if (ctxt != NULL) + ctxt->nberrors++; + __xmlSimpleError(XML_FROM_SCHEMASP, XML_ERR_NO_MEMORY, node, NULL, + extra); +} + +/** + * xmlSchemaPErr: + * @ctxt: the parsing context + * @node: the context node + * @error: the error code + * @msg: the error message + * @str1: extra data + * @str2: extra data + * + * Handle a parser error + */ +static void +xmlSchemaPErr(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node, int error, + const char *msg, const xmlChar * str1, const xmlChar * str2) +{ + xmlGenericErrorFunc channel = NULL; + xmlStructuredErrorFunc schannel = NULL; + void *data = NULL; + + if (ctxt != NULL) { + ctxt->nberrors++; + ctxt->err = error; + channel = ctxt->error; + data = ctxt->errCtxt; + schannel = ctxt->serror; + } + __xmlRaiseError(schannel, channel, data, ctxt, node, XML_FROM_SCHEMASP, + error, XML_ERR_ERROR, NULL, 0, + (const char *) str1, (const char *) str2, NULL, 0, 0, + msg, str1, str2); +} + +/** + * xmlSchemaPErr2: + * @ctxt: the parsing context + * @node: the context node + * @node: the current child + * @error: the error code + * @msg: the error message + * @str1: extra data + * @str2: extra data + * + * Handle a parser error + */ +static void +xmlSchemaPErr2(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node, + xmlNodePtr child, int error, + const char *msg, const xmlChar * str1, const xmlChar * str2) +{ + if (child != NULL) + xmlSchemaPErr(ctxt, child, error, msg, str1, str2); + else + xmlSchemaPErr(ctxt, node, error, msg, str1, str2); +} + + +/** + * xmlSchemaPErrExt: + * @ctxt: the parsing context + * @node: the context node + * @error: the error code + * @strData1: extra data + * @strData2: extra data + * @strData3: extra data + * @msg: the message + * @str1: extra parameter for the message display + * @str2: extra parameter for the message display + * @str3: extra parameter for the message display + * @str4: extra parameter for the message display + * @str5: extra parameter for the message display + * + * Handle a parser error + */ +static void +xmlSchemaPErrExt(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node, int error, + const xmlChar * strData1, const xmlChar * strData2, + const xmlChar * strData3, const char *msg, const xmlChar * str1, + const xmlChar * str2, const xmlChar * str3, const xmlChar * str4, + const xmlChar * str5) +{ + + xmlGenericErrorFunc channel = NULL; + xmlStructuredErrorFunc schannel = NULL; + void *data = NULL; + + if (ctxt != NULL) { + ctxt->nberrors++; + ctxt->err = error; + channel = ctxt->error; + data = ctxt->errCtxt; + schannel = ctxt->serror; + } + __xmlRaiseError(schannel, channel, data, ctxt, node, XML_FROM_SCHEMASP, + error, XML_ERR_ERROR, NULL, 0, + (const char *) strData1, (const char *) strData2, + (const char *) strData3, 0, 0, msg, str1, str2, + str3, str4, str5); +} + +/************************************************************************ + * * + * Allround error functions * + * * + ************************************************************************/ + +/** + * xmlSchemaVTypeErrMemory: + * @node: a context node + * @extra: extra informations + * + * Handle an out of memory condition + */ +static void +xmlSchemaVErrMemory(xmlSchemaValidCtxtPtr ctxt, + const char *extra, xmlNodePtr node) +{ + if (ctxt != NULL) { + ctxt->nberrors++; + ctxt->err = XML_SCHEMAV_INTERNAL; + } + __xmlSimpleError(XML_FROM_SCHEMASV, XML_ERR_NO_MEMORY, node, NULL, + extra); +} + +static void +xmlSchemaPSimpleInternalErr(xmlNodePtr node, + const char *msg, const xmlChar *str) +{ + __xmlSimpleError(XML_FROM_SCHEMASP, XML_SCHEMAP_INTERNAL, node, + msg, (const char *) str); +} + +#define WXS_ERROR_TYPE_ERROR 1 +#define WXS_ERROR_TYPE_WARNING 2 +/** + * xmlSchemaErr3: + * @ctxt: the validation context + * @node: the context node + * @error: the error code + * @msg: the error message + * @str1: extra data + * @str2: extra data + * @str3: extra data + * + * Handle a validation error + */ +static void +xmlSchemaErr4Line(xmlSchemaAbstractCtxtPtr ctxt, + xmlErrorLevel errorLevel, + int error, xmlNodePtr node, int line, const char *msg, + const xmlChar *str1, const xmlChar *str2, + const xmlChar *str3, const xmlChar *str4) +{ + xmlStructuredErrorFunc schannel = NULL; + xmlGenericErrorFunc channel = NULL; + void *data = NULL; + + if (ctxt != NULL) { + if (ctxt->type == XML_SCHEMA_CTXT_VALIDATOR) { + xmlSchemaValidCtxtPtr vctxt = (xmlSchemaValidCtxtPtr) ctxt; + const char *file = NULL; + if (errorLevel != XML_ERR_WARNING) { + vctxt->nberrors++; + vctxt->err = error; + channel = vctxt->error; + } else { + channel = vctxt->warning; + } + schannel = vctxt->serror; + data = vctxt->errCtxt; + + /* + * Error node. If we specify a line number, then + * do not channel any node to the error function. + */ + if (line == 0) { + if ((node == NULL) && + (vctxt->depth >= 0) && + (vctxt->inode != NULL)) { + node = vctxt->inode->node; + } + /* + * Get filename and line if no node-tree. + */ + if ((node == NULL) && + (vctxt->parserCtxt != NULL) && + (vctxt->parserCtxt->input != NULL)) { + file = vctxt->parserCtxt->input->filename; + line = vctxt->parserCtxt->input->line; + } + } else { + /* + * Override the given node's (if any) position + * and channel only the given line number. + */ + node = NULL; + /* + * Get filename. + */ + if (vctxt->doc != NULL) + file = (const char *) vctxt->doc->URL; + else if ((vctxt->parserCtxt != NULL) && + (vctxt->parserCtxt->input != NULL)) + file = vctxt->parserCtxt->input->filename; + } + __xmlRaiseError(schannel, channel, data, ctxt, + node, XML_FROM_SCHEMASV, + error, errorLevel, file, line, + (const char *) str1, (const char *) str2, + (const char *) str3, 0, 0, msg, str1, str2, str3, str4); + + } else if (ctxt->type == XML_SCHEMA_CTXT_PARSER) { + xmlSchemaParserCtxtPtr pctxt = (xmlSchemaParserCtxtPtr) ctxt; + if (errorLevel != XML_ERR_WARNING) { + pctxt->nberrors++; + pctxt->err = error; + channel = pctxt->error; + } else { + channel = pctxt->warning; + } + schannel = pctxt->serror; + data = pctxt->errCtxt; + __xmlRaiseError(schannel, channel, data, ctxt, + node, XML_FROM_SCHEMASP, error, + errorLevel, NULL, 0, + (const char *) str1, (const char *) str2, + (const char *) str3, 0, 0, msg, str1, str2, str3, str4); + } else { + TODO + } + } +} + +/** + * xmlSchemaErr3: + * @ctxt: the validation context + * @node: the context node + * @error: the error code + * @msg: the error message + * @str1: extra data + * @str2: extra data + * @str3: extra data + * + * Handle a validation error + */ +static void +xmlSchemaErr3(xmlSchemaAbstractCtxtPtr actxt, + int error, xmlNodePtr node, const char *msg, + const xmlChar *str1, const xmlChar *str2, const xmlChar *str3) +{ + xmlSchemaErr4Line(actxt, XML_ERR_ERROR, error, node, 0, + msg, str1, str2, str3, NULL); +} + +static void +xmlSchemaErr4(xmlSchemaAbstractCtxtPtr actxt, + int error, xmlNodePtr node, const char *msg, + const xmlChar *str1, const xmlChar *str2, + const xmlChar *str3, const xmlChar *str4) +{ + xmlSchemaErr4Line(actxt, XML_ERR_ERROR, error, node, 0, + msg, str1, str2, str3, str4); +} + +static void +xmlSchemaErr(xmlSchemaAbstractCtxtPtr actxt, + int error, xmlNodePtr node, const char *msg, + const xmlChar *str1, const xmlChar *str2) +{ + xmlSchemaErr4(actxt, error, node, msg, str1, str2, NULL, NULL); +} + +static xmlChar * +xmlSchemaFormatNodeForError(xmlChar ** msg, + xmlSchemaAbstractCtxtPtr actxt, + xmlNodePtr node) +{ + xmlChar *str = NULL; + + *msg = NULL; + if ((node != NULL) && + (node->type != XML_ELEMENT_NODE) && + (node->type != XML_ATTRIBUTE_NODE)) + { + /* + * Don't try to format other nodes than element and + * attribute nodes. + * Play save and return an empty string. + */ + *msg = xmlStrdup(BAD_CAST ""); + return(*msg); + } + if (node != NULL) { + /* + * Work on tree nodes. + */ + if (node->type == XML_ATTRIBUTE_NODE) { + xmlNodePtr elem = node->parent; + + *msg = xmlStrdup(BAD_CAST "Element '"); + if (elem->ns != NULL) + *msg = xmlStrcat(*msg, xmlSchemaFormatQName(&str, + elem->ns->href, elem->name)); + else + *msg = xmlStrcat(*msg, xmlSchemaFormatQName(&str, + NULL, elem->name)); + FREE_AND_NULL(str); + *msg = xmlStrcat(*msg, BAD_CAST "', "); + *msg = xmlStrcat(*msg, BAD_CAST "attribute '"); + } else { + *msg = xmlStrdup(BAD_CAST "Element '"); + } + if (node->ns != NULL) + *msg = xmlStrcat(*msg, xmlSchemaFormatQName(&str, + node->ns->href, node->name)); + else + *msg = xmlStrcat(*msg, xmlSchemaFormatQName(&str, + NULL, node->name)); + FREE_AND_NULL(str); + *msg = xmlStrcat(*msg, BAD_CAST "': "); + } else if (actxt->type == XML_SCHEMA_CTXT_VALIDATOR) { + xmlSchemaValidCtxtPtr vctxt = (xmlSchemaValidCtxtPtr) actxt; + /* + * Work on node infos. + */ + if (vctxt->inode->nodeType == XML_ATTRIBUTE_NODE) { + xmlSchemaNodeInfoPtr ielem = + vctxt->elemInfos[vctxt->depth]; + + *msg = xmlStrdup(BAD_CAST "Element '"); + *msg = xmlStrcat(*msg, xmlSchemaFormatQName(&str, + ielem->nsName, ielem->localName)); + FREE_AND_NULL(str); + *msg = xmlStrcat(*msg, BAD_CAST "', "); + *msg = xmlStrcat(*msg, BAD_CAST "attribute '"); + } else { + *msg = xmlStrdup(BAD_CAST "Element '"); + } + *msg = xmlStrcat(*msg, xmlSchemaFormatQName(&str, + vctxt->inode->nsName, vctxt->inode->localName)); + FREE_AND_NULL(str); + *msg = xmlStrcat(*msg, BAD_CAST "': "); + } else if (actxt->type == XML_SCHEMA_CTXT_PARSER) { + /* + * Hmm, no node while parsing? + * Return an empty string, in case NULL will break something. + */ + *msg = xmlStrdup(BAD_CAST ""); + } else { + TODO + return (NULL); + } + /* + * VAL TODO: The output of the given schema component is currently + * disabled. + */ +#if 0 + if ((type != NULL) && (xmlSchemaIsGlobalItem(type))) { + *msg = xmlStrcat(*msg, BAD_CAST " ["); + *msg = xmlStrcat(*msg, xmlSchemaFormatItemForReport(&str, + NULL, type, NULL, 0)); + FREE_AND_NULL(str) + *msg = xmlStrcat(*msg, BAD_CAST "]"); + } +#endif + return (*msg); +} + +static void +xmlSchemaInternalErr2(xmlSchemaAbstractCtxtPtr actxt, + const char *funcName, + const char *message, + const xmlChar *str1, + const xmlChar *str2) +{ + xmlChar *msg = NULL; + + if (actxt == NULL) + return; + msg = xmlStrdup(BAD_CAST "Internal error: "); + msg = xmlStrcat(msg, BAD_CAST funcName); + msg = xmlStrcat(msg, BAD_CAST ", "); + msg = xmlStrcat(msg, BAD_CAST message); + msg = xmlStrcat(msg, BAD_CAST ".\n"); + + if (actxt->type == XML_SCHEMA_CTXT_VALIDATOR) + xmlSchemaErr(actxt, XML_SCHEMAV_INTERNAL, NULL, + (const char *) msg, str1, str2); + + else if (actxt->type == XML_SCHEMA_CTXT_PARSER) + xmlSchemaErr(actxt, XML_SCHEMAP_INTERNAL, NULL, + (const char *) msg, str1, str2); + + FREE_AND_NULL(msg) +} + +static void +xmlSchemaInternalErr(xmlSchemaAbstractCtxtPtr actxt, + const char *funcName, + const char *message) +{ + xmlSchemaInternalErr2(actxt, funcName, message, NULL, NULL); +} + +#if 0 +static void +xmlSchemaPInternalErr(xmlSchemaParserCtxtPtr pctxt, + const char *funcName, + const char *message, + const xmlChar *str1, + const xmlChar *str2) +{ + xmlSchemaInternalErr2(ACTXT_CAST pctxt, funcName, message, + str1, str2); +} +#endif + +static void +xmlSchemaCustomErr4(xmlSchemaAbstractCtxtPtr actxt, + xmlParserErrors error, + xmlNodePtr node, + xmlSchemaBasicItemPtr item, + const char *message, + const xmlChar *str1, const xmlChar *str2, + const xmlChar *str3, const xmlChar *str4) +{ + xmlChar *msg = NULL; + + if ((node == NULL) && (item != NULL) && + (actxt->type == XML_SCHEMA_CTXT_PARSER)) { + node = WXS_ITEM_NODE(item); + xmlSchemaFormatItemForReport(&msg, NULL, item, NULL); + msg = xmlStrcat(msg, BAD_CAST ": "); + } else + xmlSchemaFormatNodeForError(&msg, actxt, node); + msg = xmlStrcat(msg, (const xmlChar *) message); + msg = xmlStrcat(msg, BAD_CAST ".\n"); + xmlSchemaErr4(actxt, error, node, + (const char *) msg, str1, str2, str3, str4); + FREE_AND_NULL(msg) +} + +static void +xmlSchemaCustomErr(xmlSchemaAbstractCtxtPtr actxt, + xmlParserErrors error, + xmlNodePtr node, + xmlSchemaBasicItemPtr item, + const char *message, + const xmlChar *str1, + const xmlChar *str2) +{ + xmlSchemaCustomErr4(actxt, error, node, item, + message, str1, str2, NULL, NULL); +} + + + +static void +xmlSchemaCustomWarning(xmlSchemaAbstractCtxtPtr actxt, + xmlParserErrors error, + xmlNodePtr node, + xmlSchemaTypePtr type ATTRIBUTE_UNUSED, + const char *message, + const xmlChar *str1, + const xmlChar *str2, + const xmlChar *str3) +{ + xmlChar *msg = NULL; + + xmlSchemaFormatNodeForError(&msg, actxt, node); + msg = xmlStrcat(msg, (const xmlChar *) message); + msg = xmlStrcat(msg, BAD_CAST ".\n"); + + /* URGENT TODO: Set the error code to something sane. */ + xmlSchemaErr4Line(actxt, XML_ERR_WARNING, error, node, 0, + (const char *) msg, str1, str2, str3, NULL); + + FREE_AND_NULL(msg) +} + + + +static void +xmlSchemaKeyrefErr(xmlSchemaValidCtxtPtr vctxt, + xmlParserErrors error, + xmlSchemaPSVIIDCNodePtr idcNode, + xmlSchemaTypePtr type ATTRIBUTE_UNUSED, + const char *message, + const xmlChar *str1, + const xmlChar *str2) +{ + xmlChar *msg = NULL, *qname = NULL; + + msg = xmlStrdup(BAD_CAST "Element '%s': "); + msg = xmlStrcat(msg, (const xmlChar *) message); + msg = xmlStrcat(msg, BAD_CAST ".\n"); + xmlSchemaErr4Line(ACTXT_CAST vctxt, XML_ERR_ERROR, + error, NULL, idcNode->nodeLine, (const char *) msg, + xmlSchemaFormatQName(&qname, + vctxt->nodeQNames->items[idcNode->nodeQNameID +1], + vctxt->nodeQNames->items[idcNode->nodeQNameID]), + str1, str2, NULL); + FREE_AND_NULL(qname); + FREE_AND_NULL(msg); +} + +static int +xmlSchemaEvalErrorNodeType(xmlSchemaAbstractCtxtPtr actxt, + xmlNodePtr node) +{ + if (node != NULL) + return (node->type); + if ((actxt->type == XML_SCHEMA_CTXT_VALIDATOR) && + (((xmlSchemaValidCtxtPtr) actxt)->inode != NULL)) + return ( ((xmlSchemaValidCtxtPtr) actxt)->inode->nodeType); + return (-1); +} + +static int +xmlSchemaIsGlobalItem(xmlSchemaTypePtr item) +{ + switch (item->type) { + case XML_SCHEMA_TYPE_COMPLEX: + case XML_SCHEMA_TYPE_SIMPLE: + if (item->flags & XML_SCHEMAS_TYPE_GLOBAL) + return(1); + break; + case XML_SCHEMA_TYPE_GROUP: + return (1); + case XML_SCHEMA_TYPE_ELEMENT: + if ( ((xmlSchemaElementPtr) item)->flags & + XML_SCHEMAS_ELEM_GLOBAL) + return(1); + break; + case XML_SCHEMA_TYPE_ATTRIBUTE: + if ( ((xmlSchemaAttributePtr) item)->flags & + XML_SCHEMAS_ATTR_GLOBAL) + return(1); + break; + /* Note that attribute groups are always global. */ + default: + return(1); + } + return (0); +} + +static void +xmlSchemaSimpleTypeErr(xmlSchemaAbstractCtxtPtr actxt, + xmlParserErrors error, + xmlNodePtr node, + const xmlChar *value, + xmlSchemaTypePtr type, + int displayValue) +{ + xmlChar *msg = NULL; + + xmlSchemaFormatNodeForError(&msg, actxt, node); + + if (displayValue || (xmlSchemaEvalErrorNodeType(actxt, node) == + XML_ATTRIBUTE_NODE)) + msg = xmlStrcat(msg, BAD_CAST "'%s' is not a valid value of "); + else + msg = xmlStrcat(msg, BAD_CAST "The character content is not a valid " + "value of "); + + if (! xmlSchemaIsGlobalItem(type)) + msg = xmlStrcat(msg, BAD_CAST "the local "); + else + msg = xmlStrcat(msg, BAD_CAST "the "); + + if (WXS_IS_ATOMIC(type)) + msg = xmlStrcat(msg, BAD_CAST "atomic type"); + else if (WXS_IS_LIST(type)) + msg = xmlStrcat(msg, BAD_CAST "list type"); + else if (WXS_IS_UNION(type)) + msg = xmlStrcat(msg, BAD_CAST "union type"); + + if (xmlSchemaIsGlobalItem(type)) { + xmlChar *str = NULL; + msg = xmlStrcat(msg, BAD_CAST " '"); + if (type->builtInType != 0) { + msg = xmlStrcat(msg, BAD_CAST "xs:"); + msg = xmlStrcat(msg, type->name); + } else + msg = xmlStrcat(msg, + xmlSchemaFormatQName(&str, + type->targetNamespace, type->name)); + msg = xmlStrcat(msg, BAD_CAST "'"); + FREE_AND_NULL(str); + } + msg = xmlStrcat(msg, BAD_CAST ".\n"); + if (displayValue || (xmlSchemaEvalErrorNodeType(actxt, node) == + XML_ATTRIBUTE_NODE)) + xmlSchemaErr(actxt, error, node, (const char *) msg, value, NULL); + else + xmlSchemaErr(actxt, error, node, (const char *) msg, NULL, NULL); + FREE_AND_NULL(msg) +} + +static const xmlChar * +xmlSchemaFormatErrorNodeQName(xmlChar ** str, + xmlSchemaNodeInfoPtr ni, + xmlNodePtr node) +{ + if (node != NULL) { + if (node->ns != NULL) + return (xmlSchemaFormatQName(str, node->ns->href, node->name)); + else + return (xmlSchemaFormatQName(str, NULL, node->name)); + } else if (ni != NULL) + return (xmlSchemaFormatQName(str, ni->nsName, ni->localName)); + return (NULL); +} + +static void +xmlSchemaIllegalAttrErr(xmlSchemaAbstractCtxtPtr actxt, + xmlParserErrors error, + xmlSchemaAttrInfoPtr ni, + xmlNodePtr node) +{ + xmlChar *msg = NULL, *str = NULL; + + xmlSchemaFormatNodeForError(&msg, actxt, node); + msg = xmlStrcat(msg, BAD_CAST "The attribute '%s' is not allowed.\n"); + xmlSchemaErr(actxt, error, node, (const char *) msg, + xmlSchemaFormatErrorNodeQName(&str, (xmlSchemaNodeInfoPtr) ni, node), + NULL); + FREE_AND_NULL(str) + FREE_AND_NULL(msg) +} + +static void +xmlSchemaComplexTypeErr(xmlSchemaAbstractCtxtPtr actxt, + xmlParserErrors error, + xmlNodePtr node, + xmlSchemaTypePtr type ATTRIBUTE_UNUSED, + const char *message, + int nbval, + int nbneg, + xmlChar **values) +{ + xmlChar *str = NULL, *msg = NULL; + xmlChar *localName, *nsName; + const xmlChar *cur, *end; + int i; + + xmlSchemaFormatNodeForError(&msg, actxt, node); + msg = xmlStrcat(msg, (const xmlChar *) message); + msg = xmlStrcat(msg, BAD_CAST "."); + /* + * Note that is does not make sense to report that we have a + * wildcard here, since the wildcard might be unfolded into + * multiple transitions. + */ + if (nbval + nbneg > 0) { + if (nbval + nbneg > 1) { + str = xmlStrdup(BAD_CAST " Expected is one of ( "); + } else + str = xmlStrdup(BAD_CAST " Expected is ( "); + nsName = NULL; + + for (i = 0; i < nbval + nbneg; i++) { + cur = values[i]; + if (cur == NULL) + continue; + if ((cur[0] == 'n') && (cur[1] == 'o') && (cur[2] == 't') && + (cur[3] == ' ')) { + cur += 4; + str = xmlStrcat(str, BAD_CAST "##other"); + } + /* + * Get the local name. + */ + localName = NULL; + + end = cur; + if (*end == '*') { + localName = xmlStrdup(BAD_CAST "*"); + end++; + } else { + while ((*end != 0) && (*end != '|')) + end++; + localName = xmlStrncat(localName, BAD_CAST cur, end - cur); + } + if (*end != 0) { + end++; + /* + * Skip "*|*" if they come with negated expressions, since + * they represent the same negated wildcard. + */ + if ((nbneg == 0) || (*end != '*') || (*localName != '*')) { + /* + * Get the namespace name. + */ + cur = end; + if (*end == '*') { + nsName = xmlStrdup(BAD_CAST "{*}"); + } else { + while (*end != 0) + end++; + + if (i >= nbval) + nsName = xmlStrdup(BAD_CAST "{##other:"); + else + nsName = xmlStrdup(BAD_CAST "{"); + + nsName = xmlStrncat(nsName, BAD_CAST cur, end - cur); + nsName = xmlStrcat(nsName, BAD_CAST "}"); + } + str = xmlStrcat(str, BAD_CAST nsName); + FREE_AND_NULL(nsName) + } else { + FREE_AND_NULL(localName); + continue; + } + } + str = xmlStrcat(str, BAD_CAST localName); + FREE_AND_NULL(localName); + + if (i < nbval + nbneg -1) + str = xmlStrcat(str, BAD_CAST ", "); + } + str = xmlStrcat(str, BAD_CAST " ).\n"); + msg = xmlStrcat(msg, BAD_CAST str); + FREE_AND_NULL(str) + } else + msg = xmlStrcat(msg, BAD_CAST "\n"); + xmlSchemaErr(actxt, error, node, (const char *) msg, NULL, NULL); + xmlFree(msg); +} + +static void +xmlSchemaFacetErr(xmlSchemaAbstractCtxtPtr actxt, + xmlParserErrors error, + xmlNodePtr node, + const xmlChar *value, + unsigned long length, + xmlSchemaTypePtr type, + xmlSchemaFacetPtr facet, + const char *message, + const xmlChar *str1, + const xmlChar *str2) +{ + xmlChar *str = NULL, *msg = NULL; + xmlSchemaTypeType facetType; + int nodeType = xmlSchemaEvalErrorNodeType(actxt, node); + + xmlSchemaFormatNodeForError(&msg, actxt, node); + if (error == XML_SCHEMAV_CVC_ENUMERATION_VALID) { + facetType = XML_SCHEMA_FACET_ENUMERATION; + /* + * If enumerations are validated, one must not expect the + * facet to be given. + */ + } else + facetType = facet->type; + msg = xmlStrcat(msg, BAD_CAST "["); + msg = xmlStrcat(msg, BAD_CAST "facet '"); + msg = xmlStrcat(msg, xmlSchemaFacetTypeToString(facetType)); + msg = xmlStrcat(msg, BAD_CAST "'] "); + if (message == NULL) { + /* + * Use a default message. + */ + if ((facetType == XML_SCHEMA_FACET_LENGTH) || + (facetType == XML_SCHEMA_FACET_MINLENGTH) || + (facetType == XML_SCHEMA_FACET_MAXLENGTH)) { + + char len[25], actLen[25]; + + /* FIXME, TODO: What is the max expected string length of the + * this value? + */ + if (nodeType == XML_ATTRIBUTE_NODE) + msg = xmlStrcat(msg, BAD_CAST "The value '%s' has a length of '%s'; "); + else + msg = xmlStrcat(msg, BAD_CAST "The value has a length of '%s'; "); + + snprintf(len, 24, "%lu", xmlSchemaGetFacetValueAsULong(facet)); + snprintf(actLen, 24, "%lu", length); + + if (facetType == XML_SCHEMA_FACET_LENGTH) + msg = xmlStrcat(msg, + BAD_CAST "this differs from the allowed length of '%s'.\n"); + else if (facetType == XML_SCHEMA_FACET_MAXLENGTH) + msg = xmlStrcat(msg, + BAD_CAST "this exceeds the allowed maximum length of '%s'.\n"); + else if (facetType == XML_SCHEMA_FACET_MINLENGTH) + msg = xmlStrcat(msg, + BAD_CAST "this underruns the allowed minimum length of '%s'.\n"); + + if (nodeType == XML_ATTRIBUTE_NODE) + xmlSchemaErr3(actxt, error, node, (const char *) msg, + value, (const xmlChar *) actLen, (const xmlChar *) len); + else + xmlSchemaErr(actxt, error, node, (const char *) msg, + (const xmlChar *) actLen, (const xmlChar *) len); + + } else if (facetType == XML_SCHEMA_FACET_ENUMERATION) { + msg = xmlStrcat(msg, BAD_CAST "The value '%s' is not an element " + "of the set {%s}.\n"); + xmlSchemaErr(actxt, error, node, (const char *) msg, value, + xmlSchemaFormatFacetEnumSet(actxt, &str, type)); + } else if (facetType == XML_SCHEMA_FACET_PATTERN) { + msg = xmlStrcat(msg, BAD_CAST "The value '%s' is not accepted " + "by the pattern '%s'.\n"); + xmlSchemaErr(actxt, error, node, (const char *) msg, value, + facet->value); + } else if (facetType == XML_SCHEMA_FACET_MININCLUSIVE) { + msg = xmlStrcat(msg, BAD_CAST "The value '%s' is less than the " + "minimum value allowed ('%s').\n"); + xmlSchemaErr(actxt, error, node, (const char *) msg, value, + facet->value); + } else if (facetType == XML_SCHEMA_FACET_MAXINCLUSIVE) { + msg = xmlStrcat(msg, BAD_CAST "The value '%s' is greater than the " + "maximum value allowed ('%s').\n"); + xmlSchemaErr(actxt, error, node, (const char *) msg, value, + facet->value); + } else if (facetType == XML_SCHEMA_FACET_MINEXCLUSIVE) { + msg = xmlStrcat(msg, BAD_CAST "The value '%s' must be greater than " + "'%s'.\n"); + xmlSchemaErr(actxt, error, node, (const char *) msg, value, + facet->value); + } else if (facetType == XML_SCHEMA_FACET_MAXEXCLUSIVE) { + msg = xmlStrcat(msg, BAD_CAST "The value '%s' must be less than " + "'%s'.\n"); + xmlSchemaErr(actxt, error, node, (const char *) msg, value, + facet->value); + } else if (facetType == XML_SCHEMA_FACET_TOTALDIGITS) { + msg = xmlStrcat(msg, BAD_CAST "The value '%s' has more " + "digits than are allowed ('%s').\n"); + xmlSchemaErr(actxt, error, node, (const char*) msg, value, + facet->value); + } else if (facetType == XML_SCHEMA_FACET_FRACTIONDIGITS) { + msg = xmlStrcat(msg, BAD_CAST "The value '%s' has more fractional " + "digits than are allowed ('%s').\n"); + xmlSchemaErr(actxt, error, node, (const char*) msg, value, + facet->value); + } else if (nodeType == XML_ATTRIBUTE_NODE) { + msg = xmlStrcat(msg, BAD_CAST "The value '%s' is not facet-valid.\n"); + xmlSchemaErr(actxt, error, node, (const char *) msg, value, NULL); + } else { + msg = xmlStrcat(msg, BAD_CAST "The value is not facet-valid.\n"); + xmlSchemaErr(actxt, error, node, (const char *) msg, NULL, NULL); + } + } else { + msg = xmlStrcat(msg, (const xmlChar *) message); + msg = xmlStrcat(msg, BAD_CAST ".\n"); + xmlSchemaErr(actxt, error, node, (const char *) msg, str1, str2); + } + FREE_AND_NULL(str) + xmlFree(msg); +} + +#define VERROR(err, type, msg) \ + xmlSchemaCustomErr(ACTXT_CAST vctxt, err, NULL, type, msg, NULL, NULL); + +#define VERROR_INT(func, msg) xmlSchemaInternalErr(ACTXT_CAST vctxt, func, msg); + +#define PERROR_INT(func, msg) xmlSchemaInternalErr(ACTXT_CAST pctxt, func, msg); +#define PERROR_INT2(func, msg) xmlSchemaInternalErr(ACTXT_CAST ctxt, func, msg); + +#define AERROR_INT(func, msg) xmlSchemaInternalErr(actxt, func, msg); + + +/** + * xmlSchemaPMissingAttrErr: + * @ctxt: the schema validation context + * @ownerDes: the designation of the owner + * @ownerName: the name of the owner + * @ownerItem: the owner as a schema object + * @ownerElem: the owner as an element node + * @node: the parent element node of the missing attribute node + * @type: the corresponding type of the attribute node + * + * Reports an illegal attribute. + */ +static void +xmlSchemaPMissingAttrErr(xmlSchemaParserCtxtPtr ctxt, + xmlParserErrors error, + xmlSchemaBasicItemPtr ownerItem, + xmlNodePtr ownerElem, + const char *name, + const char *message) +{ + xmlChar *des = NULL; + + xmlSchemaFormatItemForReport(&des, NULL, ownerItem, ownerElem); + + if (message != NULL) + xmlSchemaPErr(ctxt, ownerElem, error, "%s: %s.\n", BAD_CAST des, BAD_CAST message); + else + xmlSchemaPErr(ctxt, ownerElem, error, + "%s: The attribute '%s' is required but missing.\n", + BAD_CAST des, BAD_CAST name); + FREE_AND_NULL(des); +} + + +/** + * xmlSchemaPResCompAttrErr: + * @ctxt: the schema validation context + * @error: the error code + * @ownerDes: the designation of the owner + * @ownerItem: the owner as a schema object + * @ownerElem: the owner as an element node + * @name: the name of the attribute holding the QName + * @refName: the referenced local name + * @refURI: the referenced namespace URI + * @message: optional message + * + * Used to report QName attribute values that failed to resolve + * to schema components. + */ +static void +xmlSchemaPResCompAttrErr(xmlSchemaParserCtxtPtr ctxt, + xmlParserErrors error, + xmlSchemaBasicItemPtr ownerItem, + xmlNodePtr ownerElem, + const char *name, + const xmlChar *refName, + const xmlChar *refURI, + xmlSchemaTypeType refType, + const char *refTypeStr) +{ + xmlChar *des = NULL, *strA = NULL; + + xmlSchemaFormatItemForReport(&des, NULL, ownerItem, ownerElem); + if (refTypeStr == NULL) + refTypeStr = (const char *) xmlSchemaItemTypeToStr(refType); + xmlSchemaPErrExt(ctxt, ownerElem, error, + NULL, NULL, NULL, + "%s, attribute '%s': The QName value '%s' does not resolve to a(n) " + "%s.\n", BAD_CAST des, BAD_CAST name, + xmlSchemaFormatQName(&strA, refURI, refName), + BAD_CAST refTypeStr, NULL); + FREE_AND_NULL(des) + FREE_AND_NULL(strA) +} + +/** + * xmlSchemaPCustomAttrErr: + * @ctxt: the schema parser context + * @error: the error code + * @ownerDes: the designation of the owner + * @ownerItem: the owner as a schema object + * @attr: the illegal attribute node + * + * Reports an illegal attribute during the parse. + */ +static void +xmlSchemaPCustomAttrErr(xmlSchemaParserCtxtPtr ctxt, + xmlParserErrors error, + xmlChar **ownerDes, + xmlSchemaBasicItemPtr ownerItem, + xmlAttrPtr attr, + const char *msg) +{ + xmlChar *des = NULL; + + if (ownerDes == NULL) + xmlSchemaFormatItemForReport(&des, NULL, ownerItem, attr->parent); + else if (*ownerDes == NULL) { + xmlSchemaFormatItemForReport(ownerDes, NULL, ownerItem, attr->parent); + des = *ownerDes; + } else + des = *ownerDes; + if (attr == NULL) { + xmlSchemaPErrExt(ctxt, NULL, error, NULL, NULL, NULL, + "%s, attribute '%s': %s.\n", + BAD_CAST des, (const xmlChar *) "Unknown", + (const xmlChar *) msg, NULL, NULL); + } else { + xmlSchemaPErrExt(ctxt, (xmlNodePtr) attr, error, NULL, NULL, NULL, + "%s, attribute '%s': %s.\n", + BAD_CAST des, attr->name, (const xmlChar *) msg, NULL, NULL); + } + if (ownerDes == NULL) + FREE_AND_NULL(des); +} + +/** + * xmlSchemaPIllegalAttrErr: + * @ctxt: the schema parser context + * @error: the error code + * @ownerDes: the designation of the attribute's owner + * @ownerItem: the attribute's owner item + * @attr: the illegal attribute node + * + * Reports an illegal attribute during the parse. + */ +static void +xmlSchemaPIllegalAttrErr(xmlSchemaParserCtxtPtr ctxt, + xmlParserErrors error, + xmlSchemaBasicItemPtr ownerComp ATTRIBUTE_UNUSED, + xmlAttrPtr attr) +{ + xmlChar *strA = NULL, *strB = NULL; + + xmlSchemaFormatNodeForError(&strA, ACTXT_CAST ctxt, attr->parent); + xmlSchemaErr4(ACTXT_CAST ctxt, error, (xmlNodePtr) attr, + "%sThe attribute '%s' is not allowed.\n", BAD_CAST strA, + xmlSchemaFormatQNameNs(&strB, attr->ns, attr->name), + NULL, NULL); + FREE_AND_NULL(strA); + FREE_AND_NULL(strB); +} + +/** + * xmlSchemaPCustomErr: + * @ctxt: the schema parser context + * @error: the error code + * @itemDes: the designation of the schema item + * @item: the schema item + * @itemElem: the node of the schema item + * @message: the error message + * @str1: an optional param for the error message + * @str2: an optional param for the error message + * @str3: an optional param for the error message + * + * Reports an error during parsing. + */ +static void +xmlSchemaPCustomErrExt(xmlSchemaParserCtxtPtr ctxt, + xmlParserErrors error, + xmlSchemaBasicItemPtr item, + xmlNodePtr itemElem, + const char *message, + const xmlChar *str1, + const xmlChar *str2, + const xmlChar *str3) +{ + xmlChar *des = NULL, *msg = NULL; + + xmlSchemaFormatItemForReport(&des, NULL, item, itemElem); + msg = xmlStrdup(BAD_CAST "%s: "); + msg = xmlStrcat(msg, (const xmlChar *) message); + msg = xmlStrcat(msg, BAD_CAST ".\n"); + if ((itemElem == NULL) && (item != NULL)) + itemElem = WXS_ITEM_NODE(item); + xmlSchemaPErrExt(ctxt, itemElem, error, NULL, NULL, NULL, + (const char *) msg, BAD_CAST des, str1, str2, str3, NULL); + FREE_AND_NULL(des); + FREE_AND_NULL(msg); +} + +/** + * xmlSchemaPCustomErr: + * @ctxt: the schema parser context + * @error: the error code + * @itemDes: the designation of the schema item + * @item: the schema item + * @itemElem: the node of the schema item + * @message: the error message + * @str1: the optional param for the error message + * + * Reports an error during parsing. + */ +static void +xmlSchemaPCustomErr(xmlSchemaParserCtxtPtr ctxt, + xmlParserErrors error, + xmlSchemaBasicItemPtr item, + xmlNodePtr itemElem, + const char *message, + const xmlChar *str1) +{ + xmlSchemaPCustomErrExt(ctxt, error, item, itemElem, message, + str1, NULL, NULL); +} + +/** + * xmlSchemaPAttrUseErr: + * @ctxt: the schema parser context + * @error: the error code + * @itemDes: the designation of the schema type + * @item: the schema type + * @itemElem: the node of the schema type + * @attr: the invalid schema attribute + * @message: the error message + * @str1: the optional param for the error message + * + * Reports an attribute use error during parsing. + */ +static void +xmlSchemaPAttrUseErr4(xmlSchemaParserCtxtPtr ctxt, + xmlParserErrors error, + xmlNodePtr node, + xmlSchemaBasicItemPtr ownerItem, + const xmlSchemaAttributeUsePtr attruse, + const char *message, + const xmlChar *str1, const xmlChar *str2, + const xmlChar *str3,const xmlChar *str4) +{ + xmlChar *str = NULL, *msg = NULL; + + xmlSchemaFormatItemForReport(&msg, NULL, ownerItem, NULL); + msg = xmlStrcat(msg, BAD_CAST ", "); + msg = xmlStrcat(msg, + BAD_CAST xmlSchemaFormatItemForReport(&str, NULL, + WXS_BASIC_CAST attruse, NULL)); + FREE_AND_NULL(str); + msg = xmlStrcat(msg, BAD_CAST ": "); + msg = xmlStrcat(msg, (const xmlChar *) message); + msg = xmlStrcat(msg, BAD_CAST ".\n"); + xmlSchemaErr4(ACTXT_CAST ctxt, error, node, + (const char *) msg, str1, str2, str3, str4); + xmlFree(msg); +} + +/** + * xmlSchemaPIllegalFacetAtomicErr: + * @ctxt: the schema parser context + * @error: the error code + * @type: the schema type + * @baseType: the base type of type + * @facet: the illegal facet + * + * Reports an illegal facet for atomic simple types. + */ +static void +xmlSchemaPIllegalFacetAtomicErr(xmlSchemaParserCtxtPtr ctxt, + xmlParserErrors error, + xmlSchemaTypePtr type, + xmlSchemaTypePtr baseType, + xmlSchemaFacetPtr facet) +{ + xmlChar *des = NULL, *strT = NULL; + + xmlSchemaFormatItemForReport(&des, NULL, WXS_BASIC_CAST type, type->node); + xmlSchemaPErrExt(ctxt, type->node, error, NULL, NULL, NULL, + "%s: The facet '%s' is not allowed on types derived from the " + "type %s.\n", + BAD_CAST des, xmlSchemaFacetTypeToString(facet->type), + xmlSchemaFormatItemForReport(&strT, NULL, WXS_BASIC_CAST baseType, NULL), + NULL, NULL); + FREE_AND_NULL(des); + FREE_AND_NULL(strT); +} + +/** + * xmlSchemaPIllegalFacetListUnionErr: + * @ctxt: the schema parser context + * @error: the error code + * @itemDes: the designation of the schema item involved + * @item: the schema item involved + * @facet: the illegal facet + * + * Reports an illegal facet for and . + */ +static void +xmlSchemaPIllegalFacetListUnionErr(xmlSchemaParserCtxtPtr ctxt, + xmlParserErrors error, + xmlSchemaTypePtr type, + xmlSchemaFacetPtr facet) +{ + xmlChar *des = NULL; + + xmlSchemaFormatItemForReport(&des, NULL, WXS_BASIC_CAST type, + type->node); + xmlSchemaPErr(ctxt, type->node, error, + "%s: The facet '%s' is not allowed.\n", + BAD_CAST des, xmlSchemaFacetTypeToString(facet->type)); + FREE_AND_NULL(des); +} + +/** + * xmlSchemaPMutualExclAttrErr: + * @ctxt: the schema validation context + * @error: the error code + * @elemDes: the designation of the parent element node + * @attr: the bad attribute node + * @type: the corresponding type of the attribute node + * + * Reports an illegal attribute. + */ +static void +xmlSchemaPMutualExclAttrErr(xmlSchemaParserCtxtPtr ctxt, + xmlParserErrors error, + xmlSchemaBasicItemPtr ownerItem, + xmlAttrPtr attr, + const char *name1, + const char *name2) +{ + xmlChar *des = NULL; + + xmlSchemaFormatItemForReport(&des, NULL, WXS_BASIC_CAST ownerItem, attr->parent); + xmlSchemaPErrExt(ctxt, (xmlNodePtr) attr, error, NULL, NULL, NULL, + "%s: The attributes '%s' and '%s' are mutually exclusive.\n", + BAD_CAST des, BAD_CAST name1, BAD_CAST name2, NULL, NULL); + FREE_AND_NULL(des); +} + +/** + * xmlSchemaPSimpleTypeErr: + * @ctxt: the schema validation context + * @error: the error code + * @type: the type specifier + * @ownerDes: the designation of the owner + * @ownerItem: the schema object if existent + * @node: the validated node + * @value: the validated value + * + * Reports a simple type validation error. + * TODO: Should this report the value of an element as well? + */ +static void +xmlSchemaPSimpleTypeErr(xmlSchemaParserCtxtPtr ctxt, + xmlParserErrors error, + xmlSchemaBasicItemPtr ownerItem ATTRIBUTE_UNUSED, + xmlNodePtr node, + xmlSchemaTypePtr type, + const char *expected, + const xmlChar *value, + const char *message, + const xmlChar *str1, + const xmlChar *str2) +{ + xmlChar *msg = NULL; + + xmlSchemaFormatNodeForError(&msg, ACTXT_CAST ctxt, node); + if (message == NULL) { + /* + * Use default messages. + */ + if (type != NULL) { + if (node->type == XML_ATTRIBUTE_NODE) + msg = xmlStrcat(msg, BAD_CAST "'%s' is not a valid value of "); + else + msg = xmlStrcat(msg, BAD_CAST "The character content is not a " + "valid value of "); + if (! xmlSchemaIsGlobalItem(type)) + msg = xmlStrcat(msg, BAD_CAST "the local "); + else + msg = xmlStrcat(msg, BAD_CAST "the "); + + if (WXS_IS_ATOMIC(type)) + msg = xmlStrcat(msg, BAD_CAST "atomic type"); + else if (WXS_IS_LIST(type)) + msg = xmlStrcat(msg, BAD_CAST "list type"); + else if (WXS_IS_UNION(type)) + msg = xmlStrcat(msg, BAD_CAST "union type"); + + if (xmlSchemaIsGlobalItem(type)) { + xmlChar *str = NULL; + msg = xmlStrcat(msg, BAD_CAST " '"); + if (type->builtInType != 0) { + msg = xmlStrcat(msg, BAD_CAST "xs:"); + msg = xmlStrcat(msg, type->name); + } else + msg = xmlStrcat(msg, + xmlSchemaFormatQName(&str, + type->targetNamespace, type->name)); + msg = xmlStrcat(msg, BAD_CAST "'."); + FREE_AND_NULL(str); + } + } else { + if (node->type == XML_ATTRIBUTE_NODE) + msg = xmlStrcat(msg, BAD_CAST "The value '%s' is not valid."); + else + msg = xmlStrcat(msg, BAD_CAST "The character content is not " + "valid."); + } + if (expected) { + msg = xmlStrcat(msg, BAD_CAST " Expected is '"); + msg = xmlStrcat(msg, BAD_CAST expected); + msg = xmlStrcat(msg, BAD_CAST "'.\n"); + } else + msg = xmlStrcat(msg, BAD_CAST "\n"); + if (node->type == XML_ATTRIBUTE_NODE) + xmlSchemaPErr(ctxt, node, error, (const char *) msg, value, NULL); + else + xmlSchemaPErr(ctxt, node, error, (const char *) msg, NULL, NULL); + } else { + msg = xmlStrcat(msg, BAD_CAST message); + msg = xmlStrcat(msg, BAD_CAST ".\n"); + xmlSchemaPErrExt(ctxt, node, error, NULL, NULL, NULL, + (const char*) msg, str1, str2, NULL, NULL, NULL); + } + /* Cleanup. */ + FREE_AND_NULL(msg) +} + +/** + * xmlSchemaPContentErr: + * @ctxt: the schema parser context + * @error: the error code + * @onwerDes: the designation of the holder of the content + * @ownerItem: the owner item of the holder of the content + * @ownerElem: the node of the holder of the content + * @child: the invalid child node + * @message: the optional error message + * @content: the optional string describing the correct content + * + * Reports an error concerning the content of a schema element. + */ +static void +xmlSchemaPContentErr(xmlSchemaParserCtxtPtr ctxt, + xmlParserErrors error, + xmlSchemaBasicItemPtr ownerItem, + xmlNodePtr ownerElem, + xmlNodePtr child, + const char *message, + const char *content) +{ + xmlChar *des = NULL; + + xmlSchemaFormatItemForReport(&des, NULL, ownerItem, ownerElem); + if (message != NULL) + xmlSchemaPErr2(ctxt, ownerElem, child, error, + "%s: %s.\n", + BAD_CAST des, BAD_CAST message); + else { + if (content != NULL) { + xmlSchemaPErr2(ctxt, ownerElem, child, error, + "%s: The content is not valid. Expected is %s.\n", + BAD_CAST des, BAD_CAST content); + } else { + xmlSchemaPErr2(ctxt, ownerElem, child, error, + "%s: The content is not valid.\n", + BAD_CAST des, NULL); + } + } + FREE_AND_NULL(des) +} + +/************************************************************************ + * * + * Streamable error functions * + * * + ************************************************************************/ + + + + +/************************************************************************ + * * + * Validation helper functions * + * * + ************************************************************************/ + + +/************************************************************************ + * * + * Allocation functions * + * * + ************************************************************************/ + +/** + * xmlSchemaNewSchemaForParserCtxt: + * @ctxt: a schema validation context + * + * Allocate a new Schema structure. + * + * Returns the newly allocated structure or NULL in case or error + */ +static xmlSchemaPtr +xmlSchemaNewSchema(xmlSchemaParserCtxtPtr ctxt) +{ + xmlSchemaPtr ret; + + ret = (xmlSchemaPtr) xmlMalloc(sizeof(xmlSchema)); + if (ret == NULL) { + xmlSchemaPErrMemory(ctxt, "allocating schema", NULL); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchema)); + ret->dict = ctxt->dict; + xmlDictReference(ret->dict); + + return (ret); +} + +/** + * xmlSchemaNewFacet: + * + * Allocate a new Facet structure. + * + * Returns the newly allocated structure or NULL in case or error + */ +xmlSchemaFacetPtr +xmlSchemaNewFacet(void) +{ + xmlSchemaFacetPtr ret; + + ret = (xmlSchemaFacetPtr) xmlMalloc(sizeof(xmlSchemaFacet)); + if (ret == NULL) { + return (NULL); + } + memset(ret, 0, sizeof(xmlSchemaFacet)); + + return (ret); +} + +/** + * xmlSchemaNewAnnot: + * @ctxt: a schema validation context + * @node: a node + * + * Allocate a new annotation structure. + * + * Returns the newly allocated structure or NULL in case or error + */ +static xmlSchemaAnnotPtr +xmlSchemaNewAnnot(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node) +{ + xmlSchemaAnnotPtr ret; + + ret = (xmlSchemaAnnotPtr) xmlMalloc(sizeof(xmlSchemaAnnot)); + if (ret == NULL) { + xmlSchemaPErrMemory(ctxt, "allocating annotation", node); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchemaAnnot)); + ret->content = node; + return (ret); +} + +static xmlSchemaItemListPtr +xmlSchemaItemListCreate(void) +{ + xmlSchemaItemListPtr ret; + + ret = xmlMalloc(sizeof(xmlSchemaItemList)); + if (ret == NULL) { + xmlSchemaPErrMemory(NULL, + "allocating an item list structure", NULL); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchemaItemList)); + return (ret); +} + +static void +xmlSchemaItemListClear(xmlSchemaItemListPtr list) +{ + if (list->items != NULL) { + xmlFree(list->items); + list->items = NULL; + } + list->nbItems = 0; + list->sizeItems = 0; +} + +static int +xmlSchemaItemListAdd(xmlSchemaItemListPtr list, void *item) +{ + if (list->items == NULL) { + list->items = (void **) xmlMalloc( + 20 * sizeof(void *)); + if (list->items == NULL) { + xmlSchemaPErrMemory(NULL, "allocating new item list", NULL); + return(-1); + } + list->sizeItems = 20; + } else if (list->sizeItems <= list->nbItems) { + list->sizeItems *= 2; + list->items = (void **) xmlRealloc(list->items, + list->sizeItems * sizeof(void *)); + if (list->items == NULL) { + xmlSchemaPErrMemory(NULL, "growing item list", NULL); + list->sizeItems = 0; + return(-1); + } + } + list->items[list->nbItems++] = item; + return(0); +} + +static int +xmlSchemaItemListAddSize(xmlSchemaItemListPtr list, + int initialSize, + void *item) +{ + if (list->items == NULL) { + if (initialSize <= 0) + initialSize = 1; + list->items = (void **) xmlMalloc( + initialSize * sizeof(void *)); + if (list->items == NULL) { + xmlSchemaPErrMemory(NULL, "allocating new item list", NULL); + return(-1); + } + list->sizeItems = initialSize; + } else if (list->sizeItems <= list->nbItems) { + list->sizeItems *= 2; + list->items = (void **) xmlRealloc(list->items, + list->sizeItems * sizeof(void *)); + if (list->items == NULL) { + xmlSchemaPErrMemory(NULL, "growing item list", NULL); + list->sizeItems = 0; + return(-1); + } + } + list->items[list->nbItems++] = item; + return(0); +} + +static int +xmlSchemaItemListInsert(xmlSchemaItemListPtr list, void *item, int idx) +{ + if (list->items == NULL) { + list->items = (void **) xmlMalloc( + 20 * sizeof(void *)); + if (list->items == NULL) { + xmlSchemaPErrMemory(NULL, "allocating new item list", NULL); + return(-1); + } + list->sizeItems = 20; + } else if (list->sizeItems <= list->nbItems) { + list->sizeItems *= 2; + list->items = (void **) xmlRealloc(list->items, + list->sizeItems * sizeof(void *)); + if (list->items == NULL) { + xmlSchemaPErrMemory(NULL, "growing item list", NULL); + list->sizeItems = 0; + return(-1); + } + } + /* + * Just append if the index is greater/equal than the item count. + */ + if (idx >= list->nbItems) { + list->items[list->nbItems++] = item; + } else { + int i; + for (i = list->nbItems; i > idx; i--) + list->items[i] = list->items[i-1]; + list->items[idx] = item; + list->nbItems++; + } + return(0); +} + +#if 0 /* enable if ever needed */ +static int +xmlSchemaItemListInsertSize(xmlSchemaItemListPtr list, + int initialSize, + void *item, + int idx) +{ + if (list->items == NULL) { + if (initialSize <= 0) + initialSize = 1; + list->items = (void **) xmlMalloc( + initialSize * sizeof(void *)); + if (list->items == NULL) { + xmlSchemaPErrMemory(NULL, "allocating new item list", NULL); + return(-1); + } + list->sizeItems = initialSize; + } else if (list->sizeItems <= list->nbItems) { + list->sizeItems *= 2; + list->items = (void **) xmlRealloc(list->items, + list->sizeItems * sizeof(void *)); + if (list->items == NULL) { + xmlSchemaPErrMemory(NULL, "growing item list", NULL); + list->sizeItems = 0; + return(-1); + } + } + /* + * Just append if the index is greater/equal than the item count. + */ + if (idx >= list->nbItems) { + list->items[list->nbItems++] = item; + } else { + int i; + for (i = list->nbItems; i > idx; i--) + list->items[i] = list->items[i-1]; + list->items[idx] = item; + list->nbItems++; + } + return(0); +} +#endif + +static int +xmlSchemaItemListRemove(xmlSchemaItemListPtr list, int idx) +{ + int i; + if ((list->items == NULL) || (idx >= list->nbItems)) { + xmlSchemaPSimpleErr("Internal error: xmlSchemaItemListRemove, " + "index error.\n"); + return(-1); + } + + if (list->nbItems == 1) { + /* TODO: Really free the list? */ + xmlFree(list->items); + list->items = NULL; + list->nbItems = 0; + list->sizeItems = 0; + } else if (list->nbItems -1 == idx) { + list->nbItems--; + } else { + for (i = idx; i < list->nbItems -1; i++) + list->items[i] = list->items[i+1]; + list->nbItems--; + } + return(0); +} + +/** + * xmlSchemaItemListFree: + * @annot: a schema type structure + * + * Deallocate a annotation structure + */ +static void +xmlSchemaItemListFree(xmlSchemaItemListPtr list) +{ + if (list == NULL) + return; + if (list->items != NULL) + xmlFree(list->items); + xmlFree(list); +} + +static void +xmlSchemaBucketFree(xmlSchemaBucketPtr bucket) +{ + if (bucket == NULL) + return; + if (bucket->globals != NULL) { + xmlSchemaComponentListFree(bucket->globals); + xmlSchemaItemListFree(bucket->globals); + } + if (bucket->locals != NULL) { + xmlSchemaComponentListFree(bucket->locals); + xmlSchemaItemListFree(bucket->locals); + } + if (bucket->relations != NULL) { + xmlSchemaSchemaRelationPtr prev, cur = bucket->relations; + do { + prev = cur; + cur = cur->next; + xmlFree(prev); + } while (cur != NULL); + } + if ((! bucket->preserveDoc) && (bucket->doc != NULL)) { + xmlFreeDoc(bucket->doc); + } + if (bucket->type == XML_SCHEMA_SCHEMA_IMPORT) { + if (WXS_IMPBUCKET(bucket)->schema != NULL) + xmlSchemaFree(WXS_IMPBUCKET(bucket)->schema); + } + xmlFree(bucket); +} + +static xmlSchemaBucketPtr +xmlSchemaBucketCreate(xmlSchemaParserCtxtPtr pctxt, + int type, const xmlChar *targetNamespace) +{ + xmlSchemaBucketPtr ret; + int size; + xmlSchemaPtr mainSchema; + + if (WXS_CONSTRUCTOR(pctxt)->mainSchema == NULL) { + PERROR_INT("xmlSchemaBucketCreate", + "no main schema on constructor"); + return(NULL); + } + mainSchema = WXS_CONSTRUCTOR(pctxt)->mainSchema; + /* Create the schema bucket. */ + if (WXS_IS_BUCKET_INCREDEF(type)) + size = sizeof(xmlSchemaInclude); + else + size = sizeof(xmlSchemaImport); + ret = (xmlSchemaBucketPtr) xmlMalloc(size); + if (ret == NULL) { + xmlSchemaPErrMemory(NULL, "allocating schema bucket", NULL); + return(NULL); + } + memset(ret, 0, size); + ret->targetNamespace = targetNamespace; + ret->type = type; + ret->globals = xmlSchemaItemListCreate(); + if (ret->globals == NULL) { + xmlFree(ret); + return(NULL); + } + ret->locals = xmlSchemaItemListCreate(); + if (ret->locals == NULL) { + xmlFree(ret); + return(NULL); + } + /* + * The following will assure that only the first bucket is marked as + * XML_SCHEMA_SCHEMA_MAIN and it points to the *main* schema. + * For each following import buckets an xmlSchema will be created. + * An xmlSchema will be created for every distinct targetNamespace. + * We assign the targetNamespace to the schemata here. + */ + if (! WXS_HAS_BUCKETS(pctxt)) { + if (WXS_IS_BUCKET_INCREDEF(type)) { + PERROR_INT("xmlSchemaBucketCreate", + "first bucket but it's an include or redefine"); + xmlSchemaBucketFree(ret); + return(NULL); + } + /* Force the type to be XML_SCHEMA_SCHEMA_MAIN. */ + ret->type = XML_SCHEMA_SCHEMA_MAIN; + /* Point to the *main* schema. */ + WXS_CONSTRUCTOR(pctxt)->mainBucket = ret; + WXS_IMPBUCKET(ret)->schema = mainSchema; + /* + * Ensure that the main schema gets a targetNamespace. + */ + mainSchema->targetNamespace = targetNamespace; + } else { + if (type == XML_SCHEMA_SCHEMA_MAIN) { + PERROR_INT("xmlSchemaBucketCreate", + "main bucket but it's not the first one"); + xmlSchemaBucketFree(ret); + return(NULL); + } else if (type == XML_SCHEMA_SCHEMA_IMPORT) { + /* + * Create a schema for imports and assign the + * targetNamespace. + */ + WXS_IMPBUCKET(ret)->schema = xmlSchemaNewSchema(pctxt); + if (WXS_IMPBUCKET(ret)->schema == NULL) { + xmlSchemaBucketFree(ret); + return(NULL); + } + WXS_IMPBUCKET(ret)->schema->targetNamespace = targetNamespace; + } + } + if (WXS_IS_BUCKET_IMPMAIN(type)) { + int res; + /* + * Imports go into the "schemasImports" slot of the main *schema*. + * Note that we create an import entry for the main schema as well; i.e., + * even if there's only one schema, we'll get an import. + */ + if (mainSchema->schemasImports == NULL) { + mainSchema->schemasImports = xmlHashCreateDict(5, + WXS_CONSTRUCTOR(pctxt)->dict); + if (mainSchema->schemasImports == NULL) { + xmlSchemaBucketFree(ret); + return(NULL); + } + } + if (targetNamespace == NULL) + res = xmlHashAddEntry(mainSchema->schemasImports, + XML_SCHEMAS_NO_NAMESPACE, ret); + else + res = xmlHashAddEntry(mainSchema->schemasImports, + targetNamespace, ret); + if (res != 0) { + PERROR_INT("xmlSchemaBucketCreate", + "failed to add the schema bucket to the hash"); + xmlSchemaBucketFree(ret); + return(NULL); + } + } else { + /* Set the @ownerImport of an include bucket. */ + if (WXS_IS_BUCKET_IMPMAIN(WXS_CONSTRUCTOR(pctxt)->bucket->type)) + WXS_INCBUCKET(ret)->ownerImport = + WXS_IMPBUCKET(WXS_CONSTRUCTOR(pctxt)->bucket); + else + WXS_INCBUCKET(ret)->ownerImport = + WXS_INCBUCKET(WXS_CONSTRUCTOR(pctxt)->bucket)->ownerImport; + + /* Includes got into the "includes" slot of the *main* schema. */ + if (mainSchema->includes == NULL) { + mainSchema->includes = xmlSchemaItemListCreate(); + if (mainSchema->includes == NULL) { + xmlSchemaBucketFree(ret); + return(NULL); + } + } + xmlSchemaItemListAdd(mainSchema->includes, ret); + } + /* + * Add to list of all buckets; this is used for lookup + * during schema construction time only. + */ + if (xmlSchemaItemListAdd(WXS_CONSTRUCTOR(pctxt)->buckets, ret) == -1) + return(NULL); + return(ret); +} + +static int +xmlSchemaAddItemSize(xmlSchemaItemListPtr *list, int initialSize, void *item) +{ + if (*list == NULL) { + *list = xmlSchemaItemListCreate(); + if (*list == NULL) + return(-1); + } + xmlSchemaItemListAddSize(*list, initialSize, item); + return(0); +} + +/** + * xmlSchemaFreeAnnot: + * @annot: a schema type structure + * + * Deallocate a annotation structure + */ +static void +xmlSchemaFreeAnnot(xmlSchemaAnnotPtr annot) +{ + if (annot == NULL) + return; + if (annot->next == NULL) { + xmlFree(annot); + } else { + xmlSchemaAnnotPtr prev; + + do { + prev = annot; + annot = annot->next; + xmlFree(prev); + } while (annot != NULL); + } +} + +/** + * xmlSchemaFreeNotation: + * @schema: a schema notation structure + * + * Deallocate a Schema Notation structure. + */ +static void +xmlSchemaFreeNotation(xmlSchemaNotationPtr nota) +{ + if (nota == NULL) + return; + xmlFree(nota); +} + +/** + * xmlSchemaFreeAttribute: + * @attr: an attribute declaration + * + * Deallocates an attribute declaration structure. + */ +static void +xmlSchemaFreeAttribute(xmlSchemaAttributePtr attr) +{ + if (attr == NULL) + return; + if (attr->annot != NULL) + xmlSchemaFreeAnnot(attr->annot); + if (attr->defVal != NULL) + xmlSchemaFreeValue(attr->defVal); + xmlFree(attr); +} + +/** + * xmlSchemaFreeAttributeUse: + * @use: an attribute use + * + * Deallocates an attribute use structure. + */ +static void +xmlSchemaFreeAttributeUse(xmlSchemaAttributeUsePtr use) +{ + if (use == NULL) + return; + if (use->annot != NULL) + xmlSchemaFreeAnnot(use->annot); + if (use->defVal != NULL) + xmlSchemaFreeValue(use->defVal); + xmlFree(use); +} + +/** + * xmlSchemaFreeAttributeUseProhib: + * @prohib: an attribute use prohibition + * + * Deallocates an attribute use structure. + */ +static void +xmlSchemaFreeAttributeUseProhib(xmlSchemaAttributeUseProhibPtr prohib) +{ + if (prohib == NULL) + return; + xmlFree(prohib); +} + +/** + * xmlSchemaFreeWildcardNsSet: + * set: a schema wildcard namespace + * + * Deallocates a list of wildcard constraint structures. + */ +static void +xmlSchemaFreeWildcardNsSet(xmlSchemaWildcardNsPtr set) +{ + xmlSchemaWildcardNsPtr next; + + while (set != NULL) { + next = set->next; + xmlFree(set); + set = next; + } +} + +/** + * xmlSchemaFreeWildcard: + * @wildcard: a wildcard structure + * + * Deallocates a wildcard structure. + */ +void +xmlSchemaFreeWildcard(xmlSchemaWildcardPtr wildcard) +{ + if (wildcard == NULL) + return; + if (wildcard->annot != NULL) + xmlSchemaFreeAnnot(wildcard->annot); + if (wildcard->nsSet != NULL) + xmlSchemaFreeWildcardNsSet(wildcard->nsSet); + if (wildcard->negNsSet != NULL) + xmlFree(wildcard->negNsSet); + xmlFree(wildcard); +} + +/** + * xmlSchemaFreeAttributeGroup: + * @schema: a schema attribute group structure + * + * Deallocate a Schema Attribute Group structure. + */ +static void +xmlSchemaFreeAttributeGroup(xmlSchemaAttributeGroupPtr attrGr) +{ + if (attrGr == NULL) + return; + if (attrGr->annot != NULL) + xmlSchemaFreeAnnot(attrGr->annot); + if (attrGr->attrUses != NULL) + xmlSchemaItemListFree(WXS_LIST_CAST attrGr->attrUses); + xmlFree(attrGr); +} + +/** + * xmlSchemaFreeQNameRef: + * @item: a QName reference structure + * + * Deallocatea a QName reference structure. + */ +static void +xmlSchemaFreeQNameRef(xmlSchemaQNameRefPtr item) +{ + xmlFree(item); +} + +/** + * xmlSchemaFreeTypeLinkList: + * @alink: a type link + * + * Deallocate a list of types. + */ +static void +xmlSchemaFreeTypeLinkList(xmlSchemaTypeLinkPtr link) +{ + xmlSchemaTypeLinkPtr next; + + while (link != NULL) { + next = link->next; + xmlFree(link); + link = next; + } +} + +static void +xmlSchemaFreeIDCStateObjList(xmlSchemaIDCStateObjPtr sto) +{ + xmlSchemaIDCStateObjPtr next; + while (sto != NULL) { + next = sto->next; + if (sto->history != NULL) + xmlFree(sto->history); + if (sto->xpathCtxt != NULL) + xmlFreeStreamCtxt((xmlStreamCtxtPtr) sto->xpathCtxt); + xmlFree(sto); + sto = next; + } +} + +/** + * xmlSchemaFreeIDC: + * @idc: a identity-constraint definition + * + * Deallocates an identity-constraint definition. + */ +static void +xmlSchemaFreeIDC(xmlSchemaIDCPtr idcDef) +{ + xmlSchemaIDCSelectPtr cur, prev; + + if (idcDef == NULL) + return; + if (idcDef->annot != NULL) + xmlSchemaFreeAnnot(idcDef->annot); + /* Selector */ + if (idcDef->selector != NULL) { + if (idcDef->selector->xpathComp != NULL) + xmlFreePattern((xmlPatternPtr) idcDef->selector->xpathComp); + xmlFree(idcDef->selector); + } + /* Fields */ + if (idcDef->fields != NULL) { + cur = idcDef->fields; + do { + prev = cur; + cur = cur->next; + if (prev->xpathComp != NULL) + xmlFreePattern((xmlPatternPtr) prev->xpathComp); + xmlFree(prev); + } while (cur != NULL); + } + xmlFree(idcDef); +} + +/** + * xmlSchemaFreeElement: + * @schema: a schema element structure + * + * Deallocate a Schema Element structure. + */ +static void +xmlSchemaFreeElement(xmlSchemaElementPtr elem) +{ + if (elem == NULL) + return; + if (elem->annot != NULL) + xmlSchemaFreeAnnot(elem->annot); + if (elem->contModel != NULL) + xmlRegFreeRegexp(elem->contModel); + if (elem->defVal != NULL) + xmlSchemaFreeValue(elem->defVal); + xmlFree(elem); +} + +/** + * xmlSchemaFreeFacet: + * @facet: a schema facet structure + * + * Deallocate a Schema Facet structure. + */ +void +xmlSchemaFreeFacet(xmlSchemaFacetPtr facet) +{ + if (facet == NULL) + return; + if (facet->val != NULL) + xmlSchemaFreeValue(facet->val); + if (facet->regexp != NULL) + xmlRegFreeRegexp(facet->regexp); + if (facet->annot != NULL) + xmlSchemaFreeAnnot(facet->annot); + xmlFree(facet); +} + +/** + * xmlSchemaFreeType: + * @type: a schema type structure + * + * Deallocate a Schema Type structure. + */ +void +xmlSchemaFreeType(xmlSchemaTypePtr type) +{ + if (type == NULL) + return; + if (type->annot != NULL) + xmlSchemaFreeAnnot(type->annot); + if (type->facets != NULL) { + xmlSchemaFacetPtr facet, next; + + facet = type->facets; + while (facet != NULL) { + next = facet->next; + xmlSchemaFreeFacet(facet); + facet = next; + } + } + if (type->attrUses != NULL) + xmlSchemaItemListFree((xmlSchemaItemListPtr) type->attrUses); + if (type->memberTypes != NULL) + xmlSchemaFreeTypeLinkList(type->memberTypes); + if (type->facetSet != NULL) { + xmlSchemaFacetLinkPtr next, link; + + link = type->facetSet; + do { + next = link->next; + xmlFree(link); + link = next; + } while (link != NULL); + } + if (type->contModel != NULL) + xmlRegFreeRegexp(type->contModel); + xmlFree(type); +} + +/** + * xmlSchemaFreeModelGroupDef: + * @item: a schema model group definition + * + * Deallocates a schema model group definition. + */ +static void +xmlSchemaFreeModelGroupDef(xmlSchemaModelGroupDefPtr item) +{ + if (item->annot != NULL) + xmlSchemaFreeAnnot(item->annot); + xmlFree(item); +} + +/** + * xmlSchemaFreeModelGroup: + * @item: a schema model group + * + * Deallocates a schema model group structure. + */ +static void +xmlSchemaFreeModelGroup(xmlSchemaModelGroupPtr item) +{ + if (item->annot != NULL) + xmlSchemaFreeAnnot(item->annot); + xmlFree(item); +} + +static void +xmlSchemaComponentListFree(xmlSchemaItemListPtr list) +{ + if ((list == NULL) || (list->nbItems == 0)) + return; + { + xmlSchemaTreeItemPtr item; + xmlSchemaTreeItemPtr *items = (xmlSchemaTreeItemPtr *) list->items; + int i; + + for (i = 0; i < list->nbItems; i++) { + item = items[i]; + if (item == NULL) + continue; + switch (item->type) { + case XML_SCHEMA_TYPE_SIMPLE: + case XML_SCHEMA_TYPE_COMPLEX: + xmlSchemaFreeType((xmlSchemaTypePtr) item); + break; + case XML_SCHEMA_TYPE_ATTRIBUTE: + xmlSchemaFreeAttribute((xmlSchemaAttributePtr) item); + break; + case XML_SCHEMA_TYPE_ATTRIBUTE_USE: + xmlSchemaFreeAttributeUse((xmlSchemaAttributeUsePtr) item); + break; + case XML_SCHEMA_EXTRA_ATTR_USE_PROHIB: + xmlSchemaFreeAttributeUseProhib( + (xmlSchemaAttributeUseProhibPtr) item); + break; + case XML_SCHEMA_TYPE_ELEMENT: + xmlSchemaFreeElement((xmlSchemaElementPtr) item); + break; + case XML_SCHEMA_TYPE_PARTICLE: + if (item->annot != NULL) + xmlSchemaFreeAnnot(item->annot); + xmlFree(item); + break; + case XML_SCHEMA_TYPE_SEQUENCE: + case XML_SCHEMA_TYPE_CHOICE: + case XML_SCHEMA_TYPE_ALL: + xmlSchemaFreeModelGroup((xmlSchemaModelGroupPtr) item); + break; + case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: + xmlSchemaFreeAttributeGroup( + (xmlSchemaAttributeGroupPtr) item); + break; + case XML_SCHEMA_TYPE_GROUP: + xmlSchemaFreeModelGroupDef( + (xmlSchemaModelGroupDefPtr) item); + break; + case XML_SCHEMA_TYPE_ANY: + case XML_SCHEMA_TYPE_ANY_ATTRIBUTE: + xmlSchemaFreeWildcard((xmlSchemaWildcardPtr) item); + break; + case XML_SCHEMA_TYPE_IDC_KEY: + case XML_SCHEMA_TYPE_IDC_UNIQUE: + case XML_SCHEMA_TYPE_IDC_KEYREF: + xmlSchemaFreeIDC((xmlSchemaIDCPtr) item); + break; + case XML_SCHEMA_TYPE_NOTATION: + xmlSchemaFreeNotation((xmlSchemaNotationPtr) item); + break; + case XML_SCHEMA_EXTRA_QNAMEREF: + xmlSchemaFreeQNameRef((xmlSchemaQNameRefPtr) item); + break; + default: { + /* TODO: This should never be hit. */ + xmlSchemaPSimpleInternalErr(NULL, + "Internal error: xmlSchemaComponentListFree, " + "unexpected component type '%s'\n", + (const xmlChar *) WXS_ITEM_TYPE_NAME(item)); + } + break; + } + } + list->nbItems = 0; + } +} + +/** + * xmlSchemaFree: + * @schema: a schema structure + * + * Deallocate a Schema structure. + */ +void +xmlSchemaFree(xmlSchemaPtr schema) +{ + if (schema == NULL) + return; + /* @volatiles is not used anymore :-/ */ + if (schema->volatiles != NULL) + TODO + /* + * Note that those slots are not responsible for freeing + * schema components anymore; this will now be done by + * the schema buckets. + */ + if (schema->notaDecl != NULL) + xmlHashFree(schema->notaDecl, NULL); + if (schema->attrDecl != NULL) + xmlHashFree(schema->attrDecl, NULL); + if (schema->attrgrpDecl != NULL) + xmlHashFree(schema->attrgrpDecl, NULL); + if (schema->elemDecl != NULL) + xmlHashFree(schema->elemDecl, NULL); + if (schema->typeDecl != NULL) + xmlHashFree(schema->typeDecl, NULL); + if (schema->groupDecl != NULL) + xmlHashFree(schema->groupDecl, NULL); + if (schema->idcDef != NULL) + xmlHashFree(schema->idcDef, NULL); + + if (schema->schemasImports != NULL) + xmlHashFree(schema->schemasImports, + (xmlHashDeallocator) xmlSchemaBucketFree); + if (schema->includes != NULL) { + xmlSchemaItemListPtr list = (xmlSchemaItemListPtr) schema->includes; + int i; + for (i = 0; i < list->nbItems; i++) { + xmlSchemaBucketFree((xmlSchemaBucketPtr) list->items[i]); + } + xmlSchemaItemListFree(list); + } + if (schema->annot != NULL) + xmlSchemaFreeAnnot(schema->annot); + /* Never free the doc here, since this will be done by the buckets. */ + + xmlDictFree(schema->dict); + xmlFree(schema); +} + +/************************************************************************ + * * + * Debug functions * + * * + ************************************************************************/ + +#ifdef LIBXML_OUTPUT_ENABLED + +static void +xmlSchemaTypeDump(xmlSchemaTypePtr type, FILE * output); /* forward */ + +/** + * xmlSchemaElementDump: + * @elem: an element + * @output: the file output + * + * Dump the element + */ +static void +xmlSchemaElementDump(xmlSchemaElementPtr elem, FILE * output, + const xmlChar * name ATTRIBUTE_UNUSED, + const xmlChar * namespace ATTRIBUTE_UNUSED, + const xmlChar * context ATTRIBUTE_UNUSED) +{ + if (elem == NULL) + return; + + + fprintf(output, "Element"); + if (elem->flags & XML_SCHEMAS_ELEM_GLOBAL) + fprintf(output, " (global)"); + fprintf(output, ": '%s' ", elem->name); + if (namespace != NULL) + fprintf(output, "ns '%s'", namespace); + fprintf(output, "\n"); +#if 0 + if ((elem->minOccurs != 1) || (elem->maxOccurs != 1)) { + fprintf(output, " min %d ", elem->minOccurs); + if (elem->maxOccurs >= UNBOUNDED) + fprintf(output, "max: unbounded\n"); + else if (elem->maxOccurs != 1) + fprintf(output, "max: %d\n", elem->maxOccurs); + else + fprintf(output, "\n"); + } +#endif + /* + * Misc other properties. + */ + if ((elem->flags & XML_SCHEMAS_ELEM_NILLABLE) || + (elem->flags & XML_SCHEMAS_ELEM_ABSTRACT) || + (elem->flags & XML_SCHEMAS_ELEM_FIXED) || + (elem->flags & XML_SCHEMAS_ELEM_DEFAULT)) { + fprintf(output, " props: "); + if (elem->flags & XML_SCHEMAS_ELEM_FIXED) + fprintf(output, "[fixed] "); + if (elem->flags & XML_SCHEMAS_ELEM_DEFAULT) + fprintf(output, "[default] "); + if (elem->flags & XML_SCHEMAS_ELEM_ABSTRACT) + fprintf(output, "[abstract] "); + if (elem->flags & XML_SCHEMAS_ELEM_NILLABLE) + fprintf(output, "[nillable] "); + fprintf(output, "\n"); + } + /* + * Default/fixed value. + */ + if (elem->value != NULL) + fprintf(output, " value: '%s'\n", elem->value); + /* + * Type. + */ + if (elem->namedType != NULL) { + fprintf(output, " type: '%s' ", elem->namedType); + if (elem->namedTypeNs != NULL) + fprintf(output, "ns '%s'\n", elem->namedTypeNs); + else + fprintf(output, "\n"); + } else if (elem->subtypes != NULL) { + /* + * Dump local types. + */ + xmlSchemaTypeDump(elem->subtypes, output); + } + /* + * Substitution group. + */ + if (elem->substGroup != NULL) { + fprintf(output, " substitutionGroup: '%s' ", elem->substGroup); + if (elem->substGroupNs != NULL) + fprintf(output, "ns '%s'\n", elem->substGroupNs); + else + fprintf(output, "\n"); + } +} + +/** + * xmlSchemaAnnotDump: + * @output: the file output + * @annot: a annotation + * + * Dump the annotation + */ +static void +xmlSchemaAnnotDump(FILE * output, xmlSchemaAnnotPtr annot) +{ + xmlChar *content; + + if (annot == NULL) + return; + + content = xmlNodeGetContent(annot->content); + if (content != NULL) { + fprintf(output, " Annot: %s\n", content); + xmlFree(content); + } else + fprintf(output, " Annot: empty\n"); +} + +/** + * xmlSchemaContentModelDump: + * @particle: the schema particle + * @output: the file output + * @depth: the depth used for intentation + * + * Dump a SchemaType structure + */ +static void +xmlSchemaContentModelDump(xmlSchemaParticlePtr particle, FILE * output, int depth) +{ + xmlChar *str = NULL; + xmlSchemaTreeItemPtr term; + char shift[100]; + int i; + + if (particle == NULL) + return; + for (i = 0;((i < depth) && (i < 25));i++) + shift[2 * i] = shift[2 * i + 1] = ' '; + shift[2 * i] = shift[2 * i + 1] = 0; + fprintf(output, shift); + if (particle->children == NULL) { + fprintf(output, "MISSING particle term\n"); + return; + } + term = particle->children; + if (term == NULL) { + fprintf(output, "(NULL)"); + } else { + switch (term->type) { + case XML_SCHEMA_TYPE_ELEMENT: + fprintf(output, "ELEM '%s'", xmlSchemaFormatQName(&str, + ((xmlSchemaElementPtr)term)->targetNamespace, + ((xmlSchemaElementPtr)term)->name)); + FREE_AND_NULL(str); + break; + case XML_SCHEMA_TYPE_SEQUENCE: + fprintf(output, "SEQUENCE"); + break; + case XML_SCHEMA_TYPE_CHOICE: + fprintf(output, "CHOICE"); + break; + case XML_SCHEMA_TYPE_ALL: + fprintf(output, "ALL"); + break; + case XML_SCHEMA_TYPE_ANY: + fprintf(output, "ANY"); + break; + default: + fprintf(output, "UNKNOWN\n"); + return; + } + } + if (particle->minOccurs != 1) + fprintf(output, " min: %d", particle->minOccurs); + if (particle->maxOccurs >= UNBOUNDED) + fprintf(output, " max: unbounded"); + else if (particle->maxOccurs != 1) + fprintf(output, " max: %d", particle->maxOccurs); + fprintf(output, "\n"); + if (term && + ((term->type == XML_SCHEMA_TYPE_SEQUENCE) || + (term->type == XML_SCHEMA_TYPE_CHOICE) || + (term->type == XML_SCHEMA_TYPE_ALL)) && + (term->children != NULL)) { + xmlSchemaContentModelDump((xmlSchemaParticlePtr) term->children, + output, depth +1); + } + if (particle->next != NULL) + xmlSchemaContentModelDump((xmlSchemaParticlePtr) particle->next, + output, depth); +} + +/** + * xmlSchemaAttrUsesDump: + * @uses: attribute uses list + * @output: the file output + * + * Dumps a list of attribute use components. + */ +static void +xmlSchemaAttrUsesDump(xmlSchemaItemListPtr uses, FILE * output) +{ + xmlSchemaAttributeUsePtr use; + xmlSchemaAttributeUseProhibPtr prohib; + xmlSchemaQNameRefPtr ref; + const xmlChar *name, *tns; + xmlChar *str = NULL; + int i; + + if ((uses == NULL) || (uses->nbItems == 0)) + return; + + fprintf(output, " attributes:\n"); + for (i = 0; i < uses->nbItems; i++) { + use = uses->items[i]; + if (use->type == XML_SCHEMA_EXTRA_ATTR_USE_PROHIB) { + fprintf(output, " [prohibition] "); + prohib = (xmlSchemaAttributeUseProhibPtr) use; + name = prohib->name; + tns = prohib->targetNamespace; + } else if (use->type == XML_SCHEMA_EXTRA_QNAMEREF) { + fprintf(output, " [reference] "); + ref = (xmlSchemaQNameRefPtr) use; + name = ref->name; + tns = ref->targetNamespace; + } else { + fprintf(output, " [use] "); + name = WXS_ATTRUSE_DECL_NAME(use); + tns = WXS_ATTRUSE_DECL_TNS(use); + } + fprintf(output, "'%s'\n", + (const char *) xmlSchemaFormatQName(&str, tns, name)); + FREE_AND_NULL(str); + } +} + +/** + * xmlSchemaTypeDump: + * @output: the file output + * @type: a type structure + * + * Dump a SchemaType structure + */ +static void +xmlSchemaTypeDump(xmlSchemaTypePtr type, FILE * output) +{ + if (type == NULL) { + fprintf(output, "Type: NULL\n"); + return; + } + fprintf(output, "Type: "); + if (type->name != NULL) + fprintf(output, "'%s' ", type->name); + else + fprintf(output, "(no name) "); + if (type->targetNamespace != NULL) + fprintf(output, "ns '%s' ", type->targetNamespace); + switch (type->type) { + case XML_SCHEMA_TYPE_BASIC: + fprintf(output, "[basic] "); + break; + case XML_SCHEMA_TYPE_SIMPLE: + fprintf(output, "[simple] "); + break; + case XML_SCHEMA_TYPE_COMPLEX: + fprintf(output, "[complex] "); + break; + case XML_SCHEMA_TYPE_SEQUENCE: + fprintf(output, "[sequence] "); + break; + case XML_SCHEMA_TYPE_CHOICE: + fprintf(output, "[choice] "); + break; + case XML_SCHEMA_TYPE_ALL: + fprintf(output, "[all] "); + break; + case XML_SCHEMA_TYPE_UR: + fprintf(output, "[ur] "); + break; + case XML_SCHEMA_TYPE_RESTRICTION: + fprintf(output, "[restriction] "); + break; + case XML_SCHEMA_TYPE_EXTENSION: + fprintf(output, "[extension] "); + break; + default: + fprintf(output, "[unknown type %d] ", type->type); + break; + } + fprintf(output, "content: "); + switch (type->contentType) { + case XML_SCHEMA_CONTENT_UNKNOWN: + fprintf(output, "[unknown] "); + break; + case XML_SCHEMA_CONTENT_EMPTY: + fprintf(output, "[empty] "); + break; + case XML_SCHEMA_CONTENT_ELEMENTS: + fprintf(output, "[element] "); + break; + case XML_SCHEMA_CONTENT_MIXED: + fprintf(output, "[mixed] "); + break; + case XML_SCHEMA_CONTENT_MIXED_OR_ELEMENTS: + /* not used. */ + break; + case XML_SCHEMA_CONTENT_BASIC: + fprintf(output, "[basic] "); + break; + case XML_SCHEMA_CONTENT_SIMPLE: + fprintf(output, "[simple] "); + break; + case XML_SCHEMA_CONTENT_ANY: + fprintf(output, "[any] "); + break; + } + fprintf(output, "\n"); + if (type->base != NULL) { + fprintf(output, " base type: '%s'", type->base); + if (type->baseNs != NULL) + fprintf(output, " ns '%s'\n", type->baseNs); + else + fprintf(output, "\n"); + } + if (type->attrUses != NULL) + xmlSchemaAttrUsesDump(type->attrUses, output); + if (type->annot != NULL) + xmlSchemaAnnotDump(output, type->annot); +#ifdef DUMP_CONTENT_MODEL + if ((type->type == XML_SCHEMA_TYPE_COMPLEX) && + (type->subtypes != NULL)) { + xmlSchemaContentModelDump((xmlSchemaParticlePtr) type->subtypes, + output, 1); + } +#endif +} + +/** + * xmlSchemaDump: + * @output: the file output + * @schema: a schema structure + * + * Dump a Schema structure. + */ +void +xmlSchemaDump(FILE * output, xmlSchemaPtr schema) +{ + if (output == NULL) + return; + if (schema == NULL) { + fprintf(output, "Schemas: NULL\n"); + return; + } + fprintf(output, "Schemas: "); + if (schema->name != NULL) + fprintf(output, "%s, ", schema->name); + else + fprintf(output, "no name, "); + if (schema->targetNamespace != NULL) + fprintf(output, "%s", (const char *) schema->targetNamespace); + else + fprintf(output, "no target namespace"); + fprintf(output, "\n"); + if (schema->annot != NULL) + xmlSchemaAnnotDump(output, schema->annot); + xmlHashScan(schema->typeDecl, (xmlHashScanner) xmlSchemaTypeDump, + output); + xmlHashScanFull(schema->elemDecl, + (xmlHashScannerFull) xmlSchemaElementDump, output); +} + +#ifdef DEBUG_IDC_NODE_TABLE +/** + * xmlSchemaDebugDumpIDCTable: + * @vctxt: the WXS validation context + * + * Displays the current IDC table for debug purposes. + */ +static void +xmlSchemaDebugDumpIDCTable(FILE * output, + const xmlChar *namespaceName, + const xmlChar *localName, + xmlSchemaPSVIIDCBindingPtr bind) +{ + xmlChar *str = NULL; + const xmlChar *value; + xmlSchemaPSVIIDCNodePtr tab; + xmlSchemaPSVIIDCKeyPtr key; + int i, j, res; + + fprintf(output, "IDC: TABLES on '%s'\n", + xmlSchemaFormatQName(&str, namespaceName, localName)); + FREE_AND_NULL(str) + + if (bind == NULL) + return; + do { + fprintf(output, "IDC: BINDING '%s' (%d)\n", + xmlSchemaGetComponentQName(&str, + bind->definition), bind->nbNodes); + FREE_AND_NULL(str) + for (i = 0; i < bind->nbNodes; i++) { + tab = bind->nodeTable[i]; + fprintf(output, " ( "); + for (j = 0; j < bind->definition->nbFields; j++) { + key = tab->keys[j]; + if ((key != NULL) && (key->val != NULL)) { + res = xmlSchemaGetCanonValue(key->val, &value); + if (res >= 0) + fprintf(output, "'%s' ", value); + else + fprintf(output, "CANON-VALUE-FAILED "); + if (res == 0) + FREE_AND_NULL(value) + } else if (key != NULL) + fprintf(output, "(no val), "); + else + fprintf(output, "(key missing), "); + } + fprintf(output, ")\n"); + } + if (bind->dupls && bind->dupls->nbItems) { + fprintf(output, "IDC: dupls (%d):\n", bind->dupls->nbItems); + for (i = 0; i < bind->dupls->nbItems; i++) { + tab = bind->dupls->items[i]; + fprintf(output, " ( "); + for (j = 0; j < bind->definition->nbFields; j++) { + key = tab->keys[j]; + if ((key != NULL) && (key->val != NULL)) { + res = xmlSchemaGetCanonValue(key->val, &value); + if (res >= 0) + fprintf(output, "'%s' ", value); + else + fprintf(output, "CANON-VALUE-FAILED "); + if (res == 0) + FREE_AND_NULL(value) + } else if (key != NULL) + fprintf(output, "(no val), "); + else + fprintf(output, "(key missing), "); + } + fprintf(output, ")\n"); + } + } + bind = bind->next; + } while (bind != NULL); +} +#endif /* DEBUG_IDC */ +#endif /* LIBXML_OUTPUT_ENABLED */ + +/************************************************************************ + * * + * Utilities * + * * + ************************************************************************/ + +/** + * xmlSchemaGetPropNode: + * @node: the element node + * @name: the name of the attribute + * + * Seeks an attribute with a name of @name in + * no namespace. + * + * Returns the attribute or NULL if not present. + */ +static xmlAttrPtr +xmlSchemaGetPropNode(xmlNodePtr node, const char *name) +{ + xmlAttrPtr prop; + + if ((node == NULL) || (name == NULL)) + return(NULL); + prop = node->properties; + while (prop != NULL) { + if ((prop->ns == NULL) && xmlStrEqual(prop->name, BAD_CAST name)) + return(prop); + prop = prop->next; + } + return (NULL); +} + +/** + * xmlSchemaGetPropNodeNs: + * @node: the element node + * @uri: the uri + * @name: the name of the attribute + * + * Seeks an attribute with a local name of @name and + * a namespace URI of @uri. + * + * Returns the attribute or NULL if not present. + */ +static xmlAttrPtr +xmlSchemaGetPropNodeNs(xmlNodePtr node, const char *uri, const char *name) +{ + xmlAttrPtr prop; + + if ((node == NULL) || (name == NULL)) + return(NULL); + prop = node->properties; + while (prop != NULL) { + if ((prop->ns != NULL) && + xmlStrEqual(prop->name, BAD_CAST name) && + xmlStrEqual(prop->ns->href, BAD_CAST uri)) + return(prop); + prop = prop->next; + } + return (NULL); +} + +static const xmlChar * +xmlSchemaGetNodeContent(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node) +{ + xmlChar *val; + const xmlChar *ret; + + val = xmlNodeGetContent(node); + if (val == NULL) + val = xmlStrdup((xmlChar *)""); + ret = xmlDictLookup(ctxt->dict, val, -1); + xmlFree(val); + return(ret); +} + +static const xmlChar * +xmlSchemaGetNodeContentNoDict(xmlNodePtr node) +{ + return((const xmlChar*) xmlNodeGetContent(node)); +} + +/** + * xmlSchemaGetProp: + * @ctxt: the parser context + * @node: the node + * @name: the property name + * + * Read a attribute value and internalize the string + * + * Returns the string or NULL if not present. + */ +static const xmlChar * +xmlSchemaGetProp(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node, + const char *name) +{ + xmlChar *val; + const xmlChar *ret; + + val = xmlGetNoNsProp(node, BAD_CAST name); + if (val == NULL) + return(NULL); + ret = xmlDictLookup(ctxt->dict, val, -1); + xmlFree(val); + return(ret); +} + +/************************************************************************ + * * + * Parsing functions * + * * + ************************************************************************/ + +#define WXS_FIND_GLOBAL_ITEM(slot) \ + if (xmlStrEqual(nsName, schema->targetNamespace)) { \ + ret = xmlHashLookup(schema->slot, name); \ + if (ret != NULL) goto exit; \ + } \ + if (xmlHashSize(schema->schemasImports) > 1) { \ + xmlSchemaImportPtr import; \ + if (nsName == NULL) \ + import = xmlHashLookup(schema->schemasImports, \ + XML_SCHEMAS_NO_NAMESPACE); \ + else \ + import = xmlHashLookup(schema->schemasImports, nsName); \ + if (import == NULL) \ + goto exit; \ + ret = xmlHashLookup(import->schema->slot, name); \ + } + +/** + * xmlSchemaGetElem: + * @schema: the schema context + * @name: the element name + * @ns: the element namespace + * + * Lookup a global element declaration in the schema. + * + * Returns the element declaration or NULL if not found. + */ +static xmlSchemaElementPtr +xmlSchemaGetElem(xmlSchemaPtr schema, const xmlChar * name, + const xmlChar * nsName) +{ + xmlSchemaElementPtr ret = NULL; + + if ((name == NULL) || (schema == NULL)) + return(NULL); + if (schema != NULL) { + WXS_FIND_GLOBAL_ITEM(elemDecl) + } +exit: +#ifdef DEBUG + if (ret == NULL) { + if (nsName == NULL) + fprintf(stderr, "Unable to lookup element decl. %s", name); + else + fprintf(stderr, "Unable to lookup element decl. %s:%s", name, + nsName); + } +#endif + return (ret); +} + +/** + * xmlSchemaGetType: + * @schema: the main schema + * @name: the type's name + * nsName: the type's namespace + * + * Lookup a type in the schemas or the predefined types + * + * Returns the group definition or NULL if not found. + */ +static xmlSchemaTypePtr +xmlSchemaGetType(xmlSchemaPtr schema, const xmlChar * name, + const xmlChar * nsName) +{ + xmlSchemaTypePtr ret = NULL; + + if (name == NULL) + return (NULL); + /* First try the built-in types. */ + if ((nsName != NULL) && xmlStrEqual(nsName, xmlSchemaNs)) { + ret = xmlSchemaGetPredefinedType(name, nsName); + if (ret != NULL) + goto exit; + /* + * Note that we try the parsed schemas as well here + * since one might have parsed the S4S, which contain more + * than the built-in types. + * TODO: Can we optimize this? + */ + } + if (schema != NULL) { + WXS_FIND_GLOBAL_ITEM(typeDecl) + } +exit: + +#ifdef DEBUG + if (ret == NULL) { + if (nsName == NULL) + fprintf(stderr, "Unable to lookup type %s", name); + else + fprintf(stderr, "Unable to lookup type %s:%s", name, + nsName); + } +#endif + return (ret); +} + +/** + * xmlSchemaGetAttributeDecl: + * @schema: the context of the schema + * @name: the name of the attribute + * @ns: the target namespace of the attribute + * + * Lookup a an attribute in the schema or imported schemas + * + * Returns the attribute declaration or NULL if not found. + */ +static xmlSchemaAttributePtr +xmlSchemaGetAttributeDecl(xmlSchemaPtr schema, const xmlChar * name, + const xmlChar * nsName) +{ + xmlSchemaAttributePtr ret = NULL; + + if ((name == NULL) || (schema == NULL)) + return (NULL); + if (schema != NULL) { + WXS_FIND_GLOBAL_ITEM(attrDecl) + } +exit: +#ifdef DEBUG + if (ret == NULL) { + if (nsName == NULL) + fprintf(stderr, "Unable to lookup attribute %s", name); + else + fprintf(stderr, "Unable to lookup attribute %s:%s", name, + nsName); + } +#endif + return (ret); +} + +/** + * xmlSchemaGetAttributeGroup: + * @schema: the context of the schema + * @name: the name of the attribute group + * @ns: the target namespace of the attribute group + * + * Lookup a an attribute group in the schema or imported schemas + * + * Returns the attribute group definition or NULL if not found. + */ +static xmlSchemaAttributeGroupPtr +xmlSchemaGetAttributeGroup(xmlSchemaPtr schema, const xmlChar * name, + const xmlChar * nsName) +{ + xmlSchemaAttributeGroupPtr ret = NULL; + + if ((name == NULL) || (schema == NULL)) + return (NULL); + if (schema != NULL) { + WXS_FIND_GLOBAL_ITEM(attrgrpDecl) + } +exit: + /* TODO: + if ((ret != NULL) && (ret->redef != NULL)) { + * Return the last redefinition. * + ret = ret->redef; + } + */ +#ifdef DEBUG + if (ret == NULL) { + if (nsName == NULL) + fprintf(stderr, "Unable to lookup attribute group %s", name); + else + fprintf(stderr, "Unable to lookup attribute group %s:%s", name, + nsName); + } +#endif + return (ret); +} + +/** + * xmlSchemaGetGroup: + * @schema: the context of the schema + * @name: the name of the group + * @ns: the target namespace of the group + * + * Lookup a group in the schema or imported schemas + * + * Returns the group definition or NULL if not found. + */ +static xmlSchemaModelGroupDefPtr +xmlSchemaGetGroup(xmlSchemaPtr schema, const xmlChar * name, + const xmlChar * nsName) +{ + xmlSchemaModelGroupDefPtr ret = NULL; + + if ((name == NULL) || (schema == NULL)) + return (NULL); + if (schema != NULL) { + WXS_FIND_GLOBAL_ITEM(groupDecl) + } +exit: + +#ifdef DEBUG + if (ret == NULL) { + if (nsName == NULL) + fprintf(stderr, "Unable to lookup group %s", name); + else + fprintf(stderr, "Unable to lookup group %s:%s", name, + nsName); + } +#endif + return (ret); +} + +static xmlSchemaNotationPtr +xmlSchemaGetNotation(xmlSchemaPtr schema, + const xmlChar *name, + const xmlChar *nsName) +{ + xmlSchemaNotationPtr ret = NULL; + + if ((name == NULL) || (schema == NULL)) + return (NULL); + if (schema != NULL) { + WXS_FIND_GLOBAL_ITEM(notaDecl) + } +exit: + return (ret); +} + +static xmlSchemaIDCPtr +xmlSchemaGetIDC(xmlSchemaPtr schema, + const xmlChar *name, + const xmlChar *nsName) +{ + xmlSchemaIDCPtr ret = NULL; + + if ((name == NULL) || (schema == NULL)) + return (NULL); + if (schema != NULL) { + WXS_FIND_GLOBAL_ITEM(idcDef) + } +exit: + return (ret); +} + +/** + * xmlSchemaGetNamedComponent: + * @schema: the schema + * @name: the name of the group + * @ns: the target namespace of the group + * + * Lookup a group in the schema or imported schemas + * + * Returns the group definition or NULL if not found. + */ +static xmlSchemaBasicItemPtr +xmlSchemaGetNamedComponent(xmlSchemaPtr schema, + xmlSchemaTypeType itemType, + const xmlChar *name, + const xmlChar *targetNs) +{ + switch (itemType) { + case XML_SCHEMA_TYPE_GROUP: + return ((xmlSchemaBasicItemPtr) xmlSchemaGetGroup(schema, + name, targetNs)); + case XML_SCHEMA_TYPE_ELEMENT: + return ((xmlSchemaBasicItemPtr) xmlSchemaGetElem(schema, + name, targetNs)); + default: + TODO + return (NULL); + } +} + +/************************************************************************ + * * + * Parsing functions * + * * + ************************************************************************/ + +#define IS_BLANK_NODE(n) \ + (((n)->type == XML_TEXT_NODE) && (xmlSchemaIsBlank((n)->content, -1))) + +/** + * xmlSchemaIsBlank: + * @str: a string + * @len: the length of the string or -1 + * + * Check if a string is ignorable + * + * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise + */ +static int +xmlSchemaIsBlank(xmlChar * str, int len) +{ + if (str == NULL) + return (1); + if (len < 0) { + while (*str != 0) { + if (!(IS_BLANK_CH(*str))) + return (0); + str++; + } + } else while ((*str != 0) && (len != 0)) { + if (!(IS_BLANK_CH(*str))) + return (0); + str++; + len--; + } + + return (1); +} + +#define WXS_COMP_NAME(c, t) ((t) (c))->name +#define WXS_COMP_TNS(c, t) ((t) (c))->targetNamespace +/* +* xmlSchemaFindRedefCompInGraph: +* ATTENTION TODO: This uses pointer comp. for strings. +*/ +static xmlSchemaBasicItemPtr +xmlSchemaFindRedefCompInGraph(xmlSchemaBucketPtr bucket, + xmlSchemaTypeType type, + const xmlChar *name, + const xmlChar *nsName) +{ + xmlSchemaBasicItemPtr ret; + int i; + + if ((bucket == NULL) || (name == NULL)) + return(NULL); + if ((bucket->globals == NULL) || + (bucket->globals->nbItems == 0)) + goto subschemas; + /* + * Search in global components. + */ + for (i = 0; i < bucket->globals->nbItems; i++) { + ret = bucket->globals->items[i]; + if (ret->type == type) { + switch (type) { + case XML_SCHEMA_TYPE_COMPLEX: + case XML_SCHEMA_TYPE_SIMPLE: + if ((WXS_COMP_NAME(ret, xmlSchemaTypePtr) == name) && + (WXS_COMP_TNS(ret, xmlSchemaTypePtr) == + nsName)) + { + return(ret); + } + break; + case XML_SCHEMA_TYPE_GROUP: + if ((WXS_COMP_NAME(ret, + xmlSchemaModelGroupDefPtr) == name) && + (WXS_COMP_TNS(ret, + xmlSchemaModelGroupDefPtr) == nsName)) + { + return(ret); + } + break; + case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: + if ((WXS_COMP_NAME(ret, + xmlSchemaAttributeGroupPtr) == name) && + (WXS_COMP_TNS(ret, + xmlSchemaAttributeGroupPtr) == nsName)) + { + return(ret); + } + break; + default: + /* Should not be hit. */ + return(NULL); + } + } + } +subschemas: + /* + * Process imported/included schemas. + */ + if (bucket->relations != NULL) { + xmlSchemaSchemaRelationPtr rel = bucket->relations; + + /* + * TODO: Marking the bucket will not avoid multiple searches + * in the same schema, but avoids at least circularity. + */ + bucket->flags |= XML_SCHEMA_BUCKET_MARKED; + do { + if ((rel->bucket != NULL) && + ((rel->bucket->flags & XML_SCHEMA_BUCKET_MARKED) == 0)) { + ret = xmlSchemaFindRedefCompInGraph(rel->bucket, + type, name, nsName); + if (ret != NULL) + return(ret); + } + rel = rel->next; + } while (rel != NULL); + bucket->flags ^= XML_SCHEMA_BUCKET_MARKED; + } + return(NULL); +} + +/** + * xmlSchemaAddNotation: + * @ctxt: a schema parser context + * @schema: the schema being built + * @name: the item name + * + * Add an XML schema annotation declaration + * *WARNING* this interface is highly subject to change + * + * Returns the new struture or NULL in case of error + */ +static xmlSchemaNotationPtr +xmlSchemaAddNotation(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, + const xmlChar *name, const xmlChar *nsName, + xmlNodePtr node ATTRIBUTE_UNUSED) +{ + xmlSchemaNotationPtr ret = NULL; + + if ((ctxt == NULL) || (schema == NULL) || (name == NULL)) + return (NULL); + + ret = (xmlSchemaNotationPtr) xmlMalloc(sizeof(xmlSchemaNotation)); + if (ret == NULL) { + xmlSchemaPErrMemory(ctxt, "add annotation", NULL); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchemaNotation)); + ret->type = XML_SCHEMA_TYPE_NOTATION; + ret->name = name; + ret->targetNamespace = nsName; + /* TODO: do we need the node to be set? + * ret->node = node;*/ + WXS_ADD_GLOBAL(ctxt, ret); + return (ret); +} + +/** + * xmlSchemaAddAttribute: + * @ctxt: a schema parser context + * @schema: the schema being built + * @name: the item name + * @namespace: the namespace + * + * Add an XML schema Attrribute declaration + * *WARNING* this interface is highly subject to change + * + * Returns the new struture or NULL in case of error + */ +static xmlSchemaAttributePtr +xmlSchemaAddAttribute(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, + const xmlChar * name, const xmlChar * nsName, + xmlNodePtr node, int topLevel) +{ + xmlSchemaAttributePtr ret = NULL; + + if ((ctxt == NULL) || (schema == NULL)) + return (NULL); + + ret = (xmlSchemaAttributePtr) xmlMalloc(sizeof(xmlSchemaAttribute)); + if (ret == NULL) { + xmlSchemaPErrMemory(ctxt, "allocating attribute", NULL); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchemaAttribute)); + ret->type = XML_SCHEMA_TYPE_ATTRIBUTE; + ret->node = node; + ret->name = name; + ret->targetNamespace = nsName; + + if (topLevel) + WXS_ADD_GLOBAL(ctxt, ret); + else + WXS_ADD_LOCAL(ctxt, ret); + WXS_ADD_PENDING(ctxt, ret); + return (ret); +} + +/** + * xmlSchemaAddAttributeUse: + * @ctxt: a schema parser context + * @schema: the schema being built + * @name: the item name + * @namespace: the namespace + * + * Add an XML schema Attrribute declaration + * *WARNING* this interface is highly subject to change + * + * Returns the new struture or NULL in case of error + */ +static xmlSchemaAttributeUsePtr +xmlSchemaAddAttributeUse(xmlSchemaParserCtxtPtr pctxt, + xmlNodePtr node) +{ + xmlSchemaAttributeUsePtr ret = NULL; + + if (pctxt == NULL) + return (NULL); + + ret = (xmlSchemaAttributeUsePtr) xmlMalloc(sizeof(xmlSchemaAttributeUse)); + if (ret == NULL) { + xmlSchemaPErrMemory(pctxt, "allocating attribute", NULL); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchemaAttributeUse)); + ret->type = XML_SCHEMA_TYPE_ATTRIBUTE_USE; + ret->node = node; + + WXS_ADD_LOCAL(pctxt, ret); + return (ret); +} + +/* +* xmlSchemaAddRedef: +* +* Adds a redefinition information. This is used at a later stage to: +* resolve references to the redefined components and to check constraints. +*/ +static xmlSchemaRedefPtr +xmlSchemaAddRedef(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaBucketPtr targetBucket, + void *item, + const xmlChar *refName, + const xmlChar *refTargetNs) +{ + xmlSchemaRedefPtr ret; + + ret = (xmlSchemaRedefPtr) + xmlMalloc(sizeof(xmlSchemaRedef)); + if (ret == NULL) { + xmlSchemaPErrMemory(pctxt, + "allocating redefinition info", NULL); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchemaRedef)); + ret->item = item; + ret->targetBucket = targetBucket; + ret->refName = refName; + ret->refTargetNs = refTargetNs; + if (WXS_CONSTRUCTOR(pctxt)->redefs == NULL) + WXS_CONSTRUCTOR(pctxt)->redefs = ret; + else + WXS_CONSTRUCTOR(pctxt)->lastRedef->next = ret; + WXS_CONSTRUCTOR(pctxt)->lastRedef = ret; + + return (ret); +} + +/** + * xmlSchemaAddAttributeGroupDefinition: + * @ctxt: a schema parser context + * @schema: the schema being built + * @name: the item name + * @nsName: the target namespace + * @node: the corresponding node + * + * Add an XML schema Attrribute Group definition. + * + * Returns the new struture or NULL in case of error + */ +static xmlSchemaAttributeGroupPtr +xmlSchemaAddAttributeGroupDefinition(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaPtr schema ATTRIBUTE_UNUSED, + const xmlChar *name, + const xmlChar *nsName, + xmlNodePtr node) +{ + xmlSchemaAttributeGroupPtr ret = NULL; + + if ((pctxt == NULL) || (name == NULL)) + return (NULL); + + ret = (xmlSchemaAttributeGroupPtr) + xmlMalloc(sizeof(xmlSchemaAttributeGroup)); + if (ret == NULL) { + xmlSchemaPErrMemory(pctxt, "allocating attribute group", NULL); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchemaAttributeGroup)); + ret->type = XML_SCHEMA_TYPE_ATTRIBUTEGROUP; + ret->name = name; + ret->targetNamespace = nsName; + ret->node = node; + + /* TODO: Remove the flag. */ + ret->flags |= XML_SCHEMAS_ATTRGROUP_GLOBAL; + if (pctxt->isRedefine) { + pctxt->redef = xmlSchemaAddRedef(pctxt, pctxt->redefined, + ret, name, nsName); + if (pctxt->redef == NULL) { + xmlFree(ret); + return(NULL); + } + pctxt->redefCounter = 0; + } + WXS_ADD_GLOBAL(pctxt, ret); + WXS_ADD_PENDING(pctxt, ret); + return (ret); +} + +/** + * xmlSchemaAddElement: + * @ctxt: a schema parser context + * @schema: the schema being built + * @name: the type name + * @namespace: the type namespace + * + * Add an XML schema Element declaration + * *WARNING* this interface is highly subject to change + * + * Returns the new struture or NULL in case of error + */ +static xmlSchemaElementPtr +xmlSchemaAddElement(xmlSchemaParserCtxtPtr ctxt, + const xmlChar * name, const xmlChar * nsName, + xmlNodePtr node, int topLevel) +{ + xmlSchemaElementPtr ret = NULL; + + if ((ctxt == NULL) || (name == NULL)) + return (NULL); + + ret = (xmlSchemaElementPtr) xmlMalloc(sizeof(xmlSchemaElement)); + if (ret == NULL) { + xmlSchemaPErrMemory(ctxt, "allocating element", NULL); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchemaElement)); + ret->type = XML_SCHEMA_TYPE_ELEMENT; + ret->name = name; + ret->targetNamespace = nsName; + ret->node = node; + + if (topLevel) + WXS_ADD_GLOBAL(ctxt, ret); + else + WXS_ADD_LOCAL(ctxt, ret); + WXS_ADD_PENDING(ctxt, ret); + return (ret); +} + +/** + * xmlSchemaAddType: + * @ctxt: a schema parser context + * @schema: the schema being built + * @name: the item name + * @namespace: the namespace + * + * Add an XML schema item + * *WARNING* this interface is highly subject to change + * + * Returns the new struture or NULL in case of error + */ +static xmlSchemaTypePtr +xmlSchemaAddType(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, + xmlSchemaTypeType type, + const xmlChar * name, const xmlChar * nsName, + xmlNodePtr node, int topLevel) +{ + xmlSchemaTypePtr ret = NULL; + + if ((ctxt == NULL) || (schema == NULL)) + return (NULL); + + ret = (xmlSchemaTypePtr) xmlMalloc(sizeof(xmlSchemaType)); + if (ret == NULL) { + xmlSchemaPErrMemory(ctxt, "allocating type", NULL); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchemaType)); + ret->type = type; + ret->name = name; + ret->targetNamespace = nsName; + ret->node = node; + if (topLevel) { + if (ctxt->isRedefine) { + ctxt->redef = xmlSchemaAddRedef(ctxt, ctxt->redefined, + ret, name, nsName); + if (ctxt->redef == NULL) { + xmlFree(ret); + return(NULL); + } + ctxt->redefCounter = 0; + } + WXS_ADD_GLOBAL(ctxt, ret); + } else + WXS_ADD_LOCAL(ctxt, ret); + WXS_ADD_PENDING(ctxt, ret); + return (ret); +} + +static xmlSchemaQNameRefPtr +xmlSchemaNewQNameRef(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaTypeType refType, + const xmlChar *refName, + const xmlChar *refNs) +{ + xmlSchemaQNameRefPtr ret; + + ret = (xmlSchemaQNameRefPtr) + xmlMalloc(sizeof(xmlSchemaQNameRef)); + if (ret == NULL) { + xmlSchemaPErrMemory(pctxt, + "allocating QName reference item", NULL); + return (NULL); + } + ret->node = NULL; + ret->type = XML_SCHEMA_EXTRA_QNAMEREF; + ret->name = refName; + ret->targetNamespace = refNs; + ret->item = NULL; + ret->itemType = refType; + /* + * Store the reference item in the schema. + */ + WXS_ADD_LOCAL(pctxt, ret); + return (ret); +} + +static xmlSchemaAttributeUseProhibPtr +xmlSchemaAddAttributeUseProhib(xmlSchemaParserCtxtPtr pctxt) +{ + xmlSchemaAttributeUseProhibPtr ret; + + ret = (xmlSchemaAttributeUseProhibPtr) + xmlMalloc(sizeof(xmlSchemaAttributeUseProhib)); + if (ret == NULL) { + xmlSchemaPErrMemory(pctxt, + "allocating attribute use prohibition", NULL); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchemaAttributeUseProhib)); + ret->type = XML_SCHEMA_EXTRA_ATTR_USE_PROHIB; + WXS_ADD_LOCAL(pctxt, ret); + return (ret); +} + + +/** + * xmlSchemaAddModelGroup: + * @ctxt: a schema parser context + * @schema: the schema being built + * @type: the "compositor" type of the model group + * @node: the node in the schema doc + * + * Adds a schema model group + * *WARNING* this interface is highly subject to change + * + * Returns the new struture or NULL in case of error + */ +static xmlSchemaModelGroupPtr +xmlSchemaAddModelGroup(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaPtr schema, + xmlSchemaTypeType type, + xmlNodePtr node) +{ + xmlSchemaModelGroupPtr ret = NULL; + + if ((ctxt == NULL) || (schema == NULL)) + return (NULL); + + ret = (xmlSchemaModelGroupPtr) + xmlMalloc(sizeof(xmlSchemaModelGroup)); + if (ret == NULL) { + xmlSchemaPErrMemory(ctxt, "allocating model group component", + NULL); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchemaModelGroup)); + ret->type = type; + ret->node = node; + WXS_ADD_LOCAL(ctxt, ret); + if ((type == XML_SCHEMA_TYPE_SEQUENCE) || + (type == XML_SCHEMA_TYPE_CHOICE)) + WXS_ADD_PENDING(ctxt, ret); + return (ret); +} + + +/** + * xmlSchemaAddParticle: + * @ctxt: a schema parser context + * @schema: the schema being built + * @node: the corresponding node in the schema doc + * @min: the minOccurs + * @max: the maxOccurs + * + * Adds an XML schema particle component. + * *WARNING* this interface is highly subject to change + * + * Returns the new struture or NULL in case of error + */ +static xmlSchemaParticlePtr +xmlSchemaAddParticle(xmlSchemaParserCtxtPtr ctxt, + xmlNodePtr node, int min, int max) +{ + xmlSchemaParticlePtr ret = NULL; + if (ctxt == NULL) + return (NULL); + +#ifdef DEBUG + fprintf(stderr, "Adding particle component\n"); +#endif + ret = (xmlSchemaParticlePtr) + xmlMalloc(sizeof(xmlSchemaParticle)); + if (ret == NULL) { + xmlSchemaPErrMemory(ctxt, "allocating particle component", + NULL); + return (NULL); + } + ret->type = XML_SCHEMA_TYPE_PARTICLE; + ret->annot = NULL; + ret->node = node; + ret->minOccurs = min; + ret->maxOccurs = max; + ret->next = NULL; + ret->children = NULL; + + WXS_ADD_LOCAL(ctxt, ret); + /* + * Note that addition to pending components will be done locally + * to the specific parsing function, since the most particles + * need not to be fixed up (i.e. the reference to be resolved). + * REMOVED: WXS_ADD_PENDING(ctxt, ret); + */ + return (ret); +} + +/** + * xmlSchemaAddModelGroupDefinition: + * @ctxt: a schema validation context + * @schema: the schema being built + * @name: the group name + * + * Add an XML schema Group definition + * + * Returns the new struture or NULL in case of error + */ +static xmlSchemaModelGroupDefPtr +xmlSchemaAddModelGroupDefinition(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaPtr schema, + const xmlChar *name, + const xmlChar *nsName, + xmlNodePtr node) +{ + xmlSchemaModelGroupDefPtr ret = NULL; + + if ((ctxt == NULL) || (schema == NULL) || (name == NULL)) + return (NULL); + + ret = (xmlSchemaModelGroupDefPtr) + xmlMalloc(sizeof(xmlSchemaModelGroupDef)); + if (ret == NULL) { + xmlSchemaPErrMemory(ctxt, "adding group", NULL); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchemaModelGroupDef)); + ret->name = name; + ret->type = XML_SCHEMA_TYPE_GROUP; + ret->node = node; + ret->targetNamespace = nsName; + + if (ctxt->isRedefine) { + ctxt->redef = xmlSchemaAddRedef(ctxt, ctxt->redefined, + ret, name, nsName); + if (ctxt->redef == NULL) { + xmlFree(ret); + return(NULL); + } + ctxt->redefCounter = 0; + } + WXS_ADD_GLOBAL(ctxt, ret); + WXS_ADD_PENDING(ctxt, ret); + return (ret); +} + +/** + * xmlSchemaNewWildcardNs: + * @ctxt: a schema validation context + * + * Creates a new wildcard namespace constraint. + * + * Returns the new struture or NULL in case of error + */ +static xmlSchemaWildcardNsPtr +xmlSchemaNewWildcardNsConstraint(xmlSchemaParserCtxtPtr ctxt) +{ + xmlSchemaWildcardNsPtr ret; + + ret = (xmlSchemaWildcardNsPtr) + xmlMalloc(sizeof(xmlSchemaWildcardNs)); + if (ret == NULL) { + xmlSchemaPErrMemory(ctxt, "creating wildcard namespace constraint", NULL); + return (NULL); + } + ret->value = NULL; + ret->next = NULL; + return (ret); +} + +static xmlSchemaIDCPtr +xmlSchemaAddIDC(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, + const xmlChar *name, const xmlChar *nsName, + int category, xmlNodePtr node) +{ + xmlSchemaIDCPtr ret = NULL; + + if ((ctxt == NULL) || (schema == NULL) || (name == NULL)) + return (NULL); + + ret = (xmlSchemaIDCPtr) xmlMalloc(sizeof(xmlSchemaIDC)); + if (ret == NULL) { + xmlSchemaPErrMemory(ctxt, + "allocating an identity-constraint definition", NULL); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchemaIDC)); + /* The target namespace of the parent element declaration. */ + ret->targetNamespace = nsName; + ret->name = name; + ret->type = category; + ret->node = node; + + WXS_ADD_GLOBAL(ctxt, ret); + /* + * Only keyrefs need to be fixup up. + */ + if (category == XML_SCHEMA_TYPE_IDC_KEYREF) + WXS_ADD_PENDING(ctxt, ret); + return (ret); +} + +/** + * xmlSchemaAddWildcard: + * @ctxt: a schema validation context + * @schema: a schema + * + * Adds a wildcard. + * It corresponds to a xsd:anyAttribute and xsd:any. + * + * Returns the new struture or NULL in case of error + */ +static xmlSchemaWildcardPtr +xmlSchemaAddWildcard(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, + xmlSchemaTypeType type, xmlNodePtr node) +{ + xmlSchemaWildcardPtr ret = NULL; + + if ((ctxt == NULL) || (schema == NULL)) + return (NULL); + + ret = (xmlSchemaWildcardPtr) xmlMalloc(sizeof(xmlSchemaWildcard)); + if (ret == NULL) { + xmlSchemaPErrMemory(ctxt, "adding wildcard", NULL); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchemaWildcard)); + ret->type = type; + ret->node = node; + WXS_ADD_LOCAL(ctxt, ret); + return (ret); +} + +static void +xmlSchemaSubstGroupFree(xmlSchemaSubstGroupPtr group) +{ + if (group == NULL) + return; + if (group->members != NULL) + xmlSchemaItemListFree(group->members); + xmlFree(group); +} + +static xmlSchemaSubstGroupPtr +xmlSchemaSubstGroupAdd(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaElementPtr head) +{ + xmlSchemaSubstGroupPtr ret; + + /* Init subst group hash. */ + if (WXS_SUBST_GROUPS(pctxt) == NULL) { + WXS_SUBST_GROUPS(pctxt) = xmlHashCreateDict(10, pctxt->dict); + if (WXS_SUBST_GROUPS(pctxt) == NULL) + return(NULL); + } + /* Create a new substitution group. */ + ret = (xmlSchemaSubstGroupPtr) xmlMalloc(sizeof(xmlSchemaSubstGroup)); + if (ret == NULL) { + xmlSchemaPErrMemory(NULL, + "allocating a substitution group container", NULL); + return(NULL); + } + memset(ret, 0, sizeof(xmlSchemaSubstGroup)); + ret->head = head; + /* Create list of members. */ + ret->members = xmlSchemaItemListCreate(); + if (ret->members == NULL) { + xmlSchemaSubstGroupFree(ret); + return(NULL); + } + /* Add subst group to hash. */ + if (xmlHashAddEntry2(WXS_SUBST_GROUPS(pctxt), + head->name, head->targetNamespace, ret) != 0) { + PERROR_INT("xmlSchemaSubstGroupAdd", + "failed to add a new substitution container"); + xmlSchemaSubstGroupFree(ret); + return(NULL); + } + return(ret); +} + +static xmlSchemaSubstGroupPtr +xmlSchemaSubstGroupGet(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaElementPtr head) +{ + if (WXS_SUBST_GROUPS(pctxt) == NULL) + return(NULL); + return(xmlHashLookup2(WXS_SUBST_GROUPS(pctxt), + head->name, head->targetNamespace)); + +} + +/** + * xmlSchemaAddElementSubstitutionMember: + * @pctxt: a schema parser context + * @head: the head of the substitution group + * @member: the new member of the substitution group + * + * Allocate a new annotation structure. + * + * Returns the newly allocated structure or NULL in case or error + */ +static int +xmlSchemaAddElementSubstitutionMember(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaElementPtr head, + xmlSchemaElementPtr member) +{ + xmlSchemaSubstGroupPtr substGroup = NULL; + + if ((pctxt == NULL) || (head == NULL) || (member == NULL)) + return (-1); + + substGroup = xmlSchemaSubstGroupGet(pctxt, head); + if (substGroup == NULL) + substGroup = xmlSchemaSubstGroupAdd(pctxt, head); + if (substGroup == NULL) + return(-1); + if (xmlSchemaItemListAdd(substGroup->members, member) == -1) + return(-1); + return(0); +} + +/************************************************************************ + * * + * Utilities for parsing * + * * + ************************************************************************/ + +/** + * xmlSchemaPValAttrNodeQNameValue: + * @ctxt: a schema parser context + * @schema: the schema context + * @ownerDes: the designation of the parent element + * @ownerItem: the parent as a schema object + * @value: the QName value + * @local: the resulting local part if found, the attribute value otherwise + * @uri: the resulting namespace URI if found + * + * Extracts the local name and the URI of a QName value and validates it. + * This one is intended to be used on attribute values that + * should resolve to schema components. + * + * Returns 0, in case the QName is valid, a positive error code + * if not valid and -1 if an internal error occurs. + */ +static int +xmlSchemaPValAttrNodeQNameValue(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaPtr schema, + xmlSchemaBasicItemPtr ownerItem, + xmlAttrPtr attr, + const xmlChar *value, + const xmlChar **uri, + const xmlChar **local) +{ + const xmlChar *pref; + xmlNsPtr ns; + int len, ret; + + *uri = NULL; + *local = NULL; + ret = xmlValidateQName(value, 1); + if (ret > 0) { + xmlSchemaPSimpleTypeErr(ctxt, + XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, + ownerItem, (xmlNodePtr) attr, + xmlSchemaGetBuiltInType(XML_SCHEMAS_QNAME), + NULL, value, NULL, NULL, NULL); + *local = value; + return (ctxt->err); + } else if (ret < 0) + return (-1); + + if (!strchr((char *) value, ':')) { + ns = xmlSearchNs(attr->doc, attr->parent, NULL); + if (ns) + *uri = xmlDictLookup(ctxt->dict, ns->href, -1); + else if (schema->flags & XML_SCHEMAS_INCLUDING_CONVERT_NS) { + /* TODO: move XML_SCHEMAS_INCLUDING_CONVERT_NS to the + * parser context. */ + /* + * This one takes care of included schemas with no + * target namespace. + */ + *uri = ctxt->targetNamespace; + } + *local = xmlDictLookup(ctxt->dict, value, -1); + return (0); + } + /* + * At this point xmlSplitQName3 has to return a local name. + */ + *local = xmlSplitQName3(value, &len); + *local = xmlDictLookup(ctxt->dict, *local, -1); + pref = xmlDictLookup(ctxt->dict, value, len); + ns = xmlSearchNs(attr->doc, attr->parent, pref); + if (ns == NULL) { + xmlSchemaPSimpleTypeErr(ctxt, + XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, + ownerItem, (xmlNodePtr) attr, + xmlSchemaGetBuiltInType(XML_SCHEMAS_QNAME), NULL, value, + "The value '%s' of simple type 'xs:QName' has no " + "corresponding namespace declaration in scope", value, NULL); + return (ctxt->err); + } else { + *uri = xmlDictLookup(ctxt->dict, ns->href, -1); + } + return (0); +} + +/** + * xmlSchemaPValAttrNodeQName: + * @ctxt: a schema parser context + * @schema: the schema context + * @ownerDes: the designation of the owner element + * @ownerItem: the owner as a schema object + * @attr: the attribute node + * @local: the resulting local part if found, the attribute value otherwise + * @uri: the resulting namespace URI if found + * + * Extracts and validates the QName of an attribute value. + * This one is intended to be used on attribute values that + * should resolve to schema components. + * + * Returns 0, in case the QName is valid, a positive error code + * if not valid and -1 if an internal error occurs. + */ +static int +xmlSchemaPValAttrNodeQName(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaPtr schema, + xmlSchemaBasicItemPtr ownerItem, + xmlAttrPtr attr, + const xmlChar **uri, + const xmlChar **local) +{ + const xmlChar *value; + + value = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); + return (xmlSchemaPValAttrNodeQNameValue(ctxt, schema, + ownerItem, attr, value, uri, local)); +} + +/** + * xmlSchemaPValAttrQName: + * @ctxt: a schema parser context + * @schema: the schema context + * @ownerDes: the designation of the parent element + * @ownerItem: the owner as a schema object + * @ownerElem: the parent node of the attribute + * @name: the name of the attribute + * @local: the resulting local part if found, the attribute value otherwise + * @uri: the resulting namespace URI if found + * + * Extracts and validates the QName of an attribute value. + * + * Returns 0, in case the QName is valid, a positive error code + * if not valid and -1 if an internal error occurs. + */ +static int +xmlSchemaPValAttrQName(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaPtr schema, + xmlSchemaBasicItemPtr ownerItem, + xmlNodePtr ownerElem, + const char *name, + const xmlChar **uri, + const xmlChar **local) +{ + xmlAttrPtr attr; + + attr = xmlSchemaGetPropNode(ownerElem, name); + if (attr == NULL) { + *local = NULL; + *uri = NULL; + return (0); + } + return (xmlSchemaPValAttrNodeQName(ctxt, schema, + ownerItem, attr, uri, local)); +} + +/** + * xmlSchemaPValAttrID: + * @ctxt: a schema parser context + * @schema: the schema context + * @ownerDes: the designation of the parent element + * @ownerItem: the owner as a schema object + * @ownerElem: the parent node of the attribute + * @name: the name of the attribute + * + * Extracts and validates the ID of an attribute value. + * + * Returns 0, in case the ID is valid, a positive error code + * if not valid and -1 if an internal error occurs. + */ +static int +xmlSchemaPValAttrNodeID(xmlSchemaParserCtxtPtr ctxt, xmlAttrPtr attr) +{ + int ret; + const xmlChar *value; + + if (attr == NULL) + return(0); + value = xmlSchemaGetNodeContentNoDict((xmlNodePtr) attr); + ret = xmlValidateNCName(value, 1); + if (ret == 0) { + /* + * NOTE: the IDness might have already be declared in the DTD + */ + if (attr->atype != XML_ATTRIBUTE_ID) { + xmlIDPtr res; + xmlChar *strip; + + /* + * TODO: Use xmlSchemaStrip here; it's not exported at this + * moment. + */ + strip = xmlSchemaCollapseString(value); + if (strip != NULL) { + xmlFree((xmlChar *) value); + value = strip; + } + res = xmlAddID(NULL, attr->doc, value, attr); + if (res == NULL) { + ret = XML_SCHEMAP_S4S_ATTR_INVALID_VALUE; + xmlSchemaPSimpleTypeErr(ctxt, + XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, + NULL, (xmlNodePtr) attr, + xmlSchemaGetBuiltInType(XML_SCHEMAS_ID), + NULL, NULL, "Duplicate value '%s' of simple " + "type 'xs:ID'", value, NULL); + } else + attr->atype = XML_ATTRIBUTE_ID; + } + } else if (ret > 0) { + ret = XML_SCHEMAP_S4S_ATTR_INVALID_VALUE; + xmlSchemaPSimpleTypeErr(ctxt, + XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, + NULL, (xmlNodePtr) attr, + xmlSchemaGetBuiltInType(XML_SCHEMAS_ID), + NULL, NULL, "The value '%s' of simple type 'xs:ID' is " + "not a valid 'xs:NCName'", + value, NULL); + } + if (value != NULL) + xmlFree((xmlChar *)value); + + return (ret); +} + +static int +xmlSchemaPValAttrID(xmlSchemaParserCtxtPtr ctxt, + xmlNodePtr ownerElem, + const xmlChar *name) +{ + xmlAttrPtr attr; + + attr = xmlSchemaGetPropNode(ownerElem, (const char *) name); + if (attr == NULL) + return(0); + return(xmlSchemaPValAttrNodeID(ctxt, attr)); + +} + +/** + * xmlGetMaxOccurs: + * @ctxt: a schema validation context + * @node: a subtree containing XML Schema informations + * + * Get the maxOccurs property + * + * Returns the default if not found, or the value + */ +static int +xmlGetMaxOccurs(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node, + int min, int max, int def, const char *expected) +{ + const xmlChar *val, *cur; + int ret = 0; + xmlAttrPtr attr; + + attr = xmlSchemaGetPropNode(node, "maxOccurs"); + if (attr == NULL) + return (def); + val = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); + + if (xmlStrEqual(val, (const xmlChar *) "unbounded")) { + if (max != UNBOUNDED) { + xmlSchemaPSimpleTypeErr(ctxt, + XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, + /* XML_SCHEMAP_INVALID_MINOCCURS, */ + NULL, (xmlNodePtr) attr, NULL, expected, + val, NULL, NULL, NULL); + return (def); + } else + return (UNBOUNDED); /* encoding it with -1 might be another option */ + } + + cur = val; + while (IS_BLANK_CH(*cur)) + cur++; + if (*cur == 0) { + xmlSchemaPSimpleTypeErr(ctxt, + XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, + /* XML_SCHEMAP_INVALID_MINOCCURS, */ + NULL, (xmlNodePtr) attr, NULL, expected, + val, NULL, NULL, NULL); + return (def); + } + while ((*cur >= '0') && (*cur <= '9')) { + ret = ret * 10 + (*cur - '0'); + cur++; + } + while (IS_BLANK_CH(*cur)) + cur++; + /* + * TODO: Restrict the maximal value to Integer. + */ + if ((*cur != 0) || (ret < min) || ((max != -1) && (ret > max))) { + xmlSchemaPSimpleTypeErr(ctxt, + XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, + /* XML_SCHEMAP_INVALID_MINOCCURS, */ + NULL, (xmlNodePtr) attr, NULL, expected, + val, NULL, NULL, NULL); + return (def); + } + return (ret); +} + +/** + * xmlGetMinOccurs: + * @ctxt: a schema validation context + * @node: a subtree containing XML Schema informations + * + * Get the minOccurs property + * + * Returns the default if not found, or the value + */ +static int +xmlGetMinOccurs(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node, + int min, int max, int def, const char *expected) +{ + const xmlChar *val, *cur; + int ret = 0; + xmlAttrPtr attr; + + attr = xmlSchemaGetPropNode(node, "minOccurs"); + if (attr == NULL) + return (def); + val = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); + cur = val; + while (IS_BLANK_CH(*cur)) + cur++; + if (*cur == 0) { + xmlSchemaPSimpleTypeErr(ctxt, + XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, + /* XML_SCHEMAP_INVALID_MINOCCURS, */ + NULL, (xmlNodePtr) attr, NULL, expected, + val, NULL, NULL, NULL); + return (def); + } + while ((*cur >= '0') && (*cur <= '9')) { + ret = ret * 10 + (*cur - '0'); + cur++; + } + while (IS_BLANK_CH(*cur)) + cur++; + /* + * TODO: Restrict the maximal value to Integer. + */ + if ((*cur != 0) || (ret < min) || ((max != -1) && (ret > max))) { + xmlSchemaPSimpleTypeErr(ctxt, + XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, + /* XML_SCHEMAP_INVALID_MINOCCURS, */ + NULL, (xmlNodePtr) attr, NULL, expected, + val, NULL, NULL, NULL); + return (def); + } + return (ret); +} + +/** + * xmlSchemaPGetBoolNodeValue: + * @ctxt: a schema validation context + * @ownerDes: owner designation + * @ownerItem: the owner as a schema item + * @node: the node holding the value + * + * Converts a boolean string value into 1 or 0. + * + * Returns 0 or 1. + */ +static int +xmlSchemaPGetBoolNodeValue(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaBasicItemPtr ownerItem, + xmlNodePtr node) +{ + xmlChar *value = NULL; + int res = 0; + + value = xmlNodeGetContent(node); + /* + * 3.2.2.1 Lexical representation + * An instance of a datatype that is defined as ·boolean· + * can have the following legal literals {true, false, 1, 0}. + */ + if (xmlStrEqual(BAD_CAST value, BAD_CAST "true")) + res = 1; + else if (xmlStrEqual(BAD_CAST value, BAD_CAST "false")) + res = 0; + else if (xmlStrEqual(BAD_CAST value, BAD_CAST "1")) + res = 1; + else if (xmlStrEqual(BAD_CAST value, BAD_CAST "0")) + res = 0; + else { + xmlSchemaPSimpleTypeErr(ctxt, + XML_SCHEMAP_INVALID_BOOLEAN, + ownerItem, node, + xmlSchemaGetBuiltInType(XML_SCHEMAS_BOOLEAN), + NULL, BAD_CAST value, + NULL, NULL, NULL); + } + if (value != NULL) + xmlFree(value); + return (res); +} + +/** + * xmlGetBooleanProp: + * @ctxt: a schema validation context + * @node: a subtree containing XML Schema informations + * @name: the attribute name + * @def: the default value + * + * Evaluate if a boolean property is set + * + * Returns the default if not found, 0 if found to be false, + * 1 if found to be true + */ +static int +xmlGetBooleanProp(xmlSchemaParserCtxtPtr ctxt, + xmlNodePtr node, + const char *name, int def) +{ + const xmlChar *val; + + val = xmlSchemaGetProp(ctxt, node, name); + if (val == NULL) + return (def); + /* + * 3.2.2.1 Lexical representation + * An instance of a datatype that is defined as ·boolean· + * can have the following legal literals {true, false, 1, 0}. + */ + if (xmlStrEqual(val, BAD_CAST "true")) + def = 1; + else if (xmlStrEqual(val, BAD_CAST "false")) + def = 0; + else if (xmlStrEqual(val, BAD_CAST "1")) + def = 1; + else if (xmlStrEqual(val, BAD_CAST "0")) + def = 0; + else { + xmlSchemaPSimpleTypeErr(ctxt, + XML_SCHEMAP_INVALID_BOOLEAN, + NULL, + (xmlNodePtr) xmlSchemaGetPropNode(node, name), + xmlSchemaGetBuiltInType(XML_SCHEMAS_BOOLEAN), + NULL, val, NULL, NULL, NULL); + } + return (def); +} + +/************************************************************************ + * * + * Shema extraction from an Infoset * + * * + ************************************************************************/ +static xmlSchemaTypePtr xmlSchemaParseSimpleType(xmlSchemaParserCtxtPtr + ctxt, xmlSchemaPtr schema, + xmlNodePtr node, + int topLevel); +static xmlSchemaTypePtr xmlSchemaParseComplexType(xmlSchemaParserCtxtPtr + ctxt, + xmlSchemaPtr schema, + xmlNodePtr node, + int topLevel); +static xmlSchemaTypePtr xmlSchemaParseRestriction(xmlSchemaParserCtxtPtr + ctxt, + xmlSchemaPtr schema, + xmlNodePtr node, + xmlSchemaTypeType parentType); +static xmlSchemaBasicItemPtr +xmlSchemaParseLocalAttribute(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaPtr schema, + xmlNodePtr node, + xmlSchemaItemListPtr uses, + int parentType); +static xmlSchemaTypePtr xmlSchemaParseList(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaPtr schema, + xmlNodePtr node); +static xmlSchemaWildcardPtr +xmlSchemaParseAnyAttribute(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaPtr schema, xmlNodePtr node); + +/** + * xmlSchemaPValAttrNodeValue: + * + * @ctxt: a schema parser context + * @ownerDes: the designation of the parent element + * @ownerItem: the schema object owner if existent + * @attr: the schema attribute node being validated + * @value: the value + * @type: the built-in type to be validated against + * + * Validates a value against the given built-in type. + * This one is intended to be used internally for validation + * of schema attribute values during parsing of the schema. + * + * Returns 0 if the value is valid, a positive error code + * number otherwise and -1 in case of an internal or API error. + */ +static int +xmlSchemaPValAttrNodeValue(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaBasicItemPtr ownerItem, + xmlAttrPtr attr, + const xmlChar *value, + xmlSchemaTypePtr type) +{ + + int ret = 0; + + /* + * NOTE: Should we move this to xmlschematypes.c? Hmm, but this + * one is really meant to be used internally, so better not. + */ + if ((pctxt == NULL) || (type == NULL) || (attr == NULL)) + return (-1); + if (type->type != XML_SCHEMA_TYPE_BASIC) { + PERROR_INT("xmlSchemaPValAttrNodeValue", + "the given type is not a built-in type"); + return (-1); + } + switch (type->builtInType) { + case XML_SCHEMAS_NCNAME: + case XML_SCHEMAS_QNAME: + case XML_SCHEMAS_ANYURI: + case XML_SCHEMAS_TOKEN: + case XML_SCHEMAS_LANGUAGE: + ret = xmlSchemaValPredefTypeNode(type, value, NULL, + (xmlNodePtr) attr); + break; + default: { + PERROR_INT("xmlSchemaPValAttrNodeValue", + "validation using the given type is not supported while " + "parsing a schema"); + return (-1); + } + } + /* + * TODO: Should we use the S4S error codes instead? + */ + if (ret < 0) { + PERROR_INT("xmlSchemaPValAttrNodeValue", + "failed to validate a schema attribute value"); + return (-1); + } else if (ret > 0) { + if (WXS_IS_LIST(type)) + ret = XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_2; + else + ret = XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_1; + xmlSchemaPSimpleTypeErr(pctxt, + ret, ownerItem, (xmlNodePtr) attr, + type, NULL, value, NULL, NULL, NULL); + } + return (ret); +} + +/** + * xmlSchemaPValAttrNode: + * + * @ctxt: a schema parser context + * @ownerDes: the designation of the parent element + * @ownerItem: the schema object owner if existent + * @attr: the schema attribute node being validated + * @type: the built-in type to be validated against + * @value: the resulting value if any + * + * Extracts and validates a value against the given built-in type. + * This one is intended to be used internally for validation + * of schema attribute values during parsing of the schema. + * + * Returns 0 if the value is valid, a positive error code + * number otherwise and -1 in case of an internal or API error. + */ +static int +xmlSchemaPValAttrNode(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaBasicItemPtr ownerItem, + xmlAttrPtr attr, + xmlSchemaTypePtr type, + const xmlChar **value) +{ + const xmlChar *val; + + if ((ctxt == NULL) || (type == NULL) || (attr == NULL)) + return (-1); + + val = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); + if (value != NULL) + *value = val; + + return (xmlSchemaPValAttrNodeValue(ctxt, ownerItem, attr, + val, type)); +} + +/** + * xmlSchemaPValAttr: + * + * @ctxt: a schema parser context + * @node: the element node of the attribute + * @ownerDes: the designation of the parent element + * @ownerItem: the schema object owner if existent + * @ownerElem: the owner element node + * @name: the name of the schema attribute node + * @type: the built-in type to be validated against + * @value: the resulting value if any + * + * Extracts and validates a value against the given built-in type. + * This one is intended to be used internally for validation + * of schema attribute values during parsing of the schema. + * + * Returns 0 if the value is valid, a positive error code + * number otherwise and -1 in case of an internal or API error. + */ +static int +xmlSchemaPValAttr(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaBasicItemPtr ownerItem, + xmlNodePtr ownerElem, + const char *name, + xmlSchemaTypePtr type, + const xmlChar **value) +{ + xmlAttrPtr attr; + + if ((ctxt == NULL) || (type == NULL)) { + if (value != NULL) + *value = NULL; + return (-1); + } + if (type->type != XML_SCHEMA_TYPE_BASIC) { + if (value != NULL) + *value = NULL; + xmlSchemaPErr(ctxt, ownerElem, + XML_SCHEMAP_INTERNAL, + "Internal error: xmlSchemaPValAttr, the given " + "type '%s' is not a built-in type.\n", + type->name, NULL); + return (-1); + } + attr = xmlSchemaGetPropNode(ownerElem, name); + if (attr == NULL) { + if (value != NULL) + *value = NULL; + return (0); + } + return (xmlSchemaPValAttrNode(ctxt, ownerItem, attr, + type, value)); +} + +static int +xmlSchemaCheckReference(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaPtr schema ATTRIBUTE_UNUSED, + xmlNodePtr node, + xmlAttrPtr attr, + const xmlChar *namespaceName) +{ + /* TODO: Pointer comparison instead? */ + if (xmlStrEqual(pctxt->targetNamespace, namespaceName)) + return (0); + if (xmlStrEqual(xmlSchemaNs, namespaceName)) + return (0); + /* + * Check if the referenced namespace was ed. + */ + if (WXS_BUCKET(pctxt)->relations != NULL) { + xmlSchemaSchemaRelationPtr rel; + + rel = WXS_BUCKET(pctxt)->relations; + do { + if (WXS_IS_BUCKET_IMPMAIN(rel->type) && + xmlStrEqual(namespaceName, rel->importNamespace)) + return (0); + rel = rel->next; + } while (rel != NULL); + } + /* + * No matching ed namespace found. + */ + { + xmlNodePtr n = (attr != NULL) ? (xmlNodePtr) attr : node; + + if (namespaceName == NULL) + xmlSchemaCustomErr(ACTXT_CAST pctxt, + XML_SCHEMAP_SRC_RESOLVE, n, NULL, + "References from this schema to components in no " + "namespace are not allowed, since not indicated by an " + "import statement", NULL, NULL); + else + xmlSchemaCustomErr(ACTXT_CAST pctxt, + XML_SCHEMAP_SRC_RESOLVE, n, NULL, + "References from this schema to components in the " + "namespace '%s' are not allowed, since not indicated by an " + "import statement", namespaceName, NULL); + } + return (XML_SCHEMAP_SRC_RESOLVE); +} + +/** + * xmlSchemaParseLocalAttributes: + * @ctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * @type: the hosting type where the attributes will be anchored + * + * Parses attribute uses and attribute declarations and + * attribute group references. + */ +static int +xmlSchemaParseLocalAttributes(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, + xmlNodePtr *child, xmlSchemaItemListPtr *list, + int parentType, int *hasRefs) +{ + void *item; + + while ((IS_SCHEMA((*child), "attribute")) || + (IS_SCHEMA((*child), "attributeGroup"))) { + if (IS_SCHEMA((*child), "attribute")) { + item = xmlSchemaParseLocalAttribute(ctxt, schema, *child, + *list, parentType); + } else { + item = xmlSchemaParseAttributeGroupRef(ctxt, schema, *child); + if ((item != NULL) && (hasRefs != NULL)) + *hasRefs = 1; + } + if (item != NULL) { + if (*list == NULL) { + /* TODO: Customize grow factor. */ + *list = xmlSchemaItemListCreate(); + if (*list == NULL) + return(-1); + } + if (xmlSchemaItemListAddSize(*list, 2, item) == -1) + return(-1); + } + *child = (*child)->next; + } + return (0); +} + +/** + * xmlSchemaParseAnnotation: + * @ctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * + * parse a XML schema Attrribute declaration + * *WARNING* this interface is highly subject to change + * + * Returns -1 in case of error, 0 if the declaration is improper and + * 1 in case of success. + */ +static xmlSchemaAnnotPtr +xmlSchemaParseAnnotation(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node, int needed) +{ + xmlSchemaAnnotPtr ret; + xmlNodePtr child = NULL; + xmlAttrPtr attr; + int barked = 0; + + /* + * INFO: S4S completed. + */ + /* + * id = ID + * {any attributes with non-schema namespace . . .}> + * Content: (appinfo | documentation)* + */ + if ((ctxt == NULL) || (node == NULL)) + return (NULL); + if (needed) + ret = xmlSchemaNewAnnot(ctxt, node); + else + ret = NULL; + attr = node->properties; + while (attr != NULL) { + if (((attr->ns == NULL) && + (!xmlStrEqual(attr->name, BAD_CAST "id"))) || + ((attr->ns != NULL) && + xmlStrEqual(attr->ns->href, xmlSchemaNs))) { + + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); + /* + * And now for the children... + */ + child = node->children; + while (child != NULL) { + if (IS_SCHEMA(child, "appinfo")) { + /* TODO: make available the content of "appinfo". */ + /* + * source = anyURI + * {any attributes with non-schema namespace . . .}> + * Content: ({any})* + */ + attr = child->properties; + while (attr != NULL) { + if (((attr->ns == NULL) && + (!xmlStrEqual(attr->name, BAD_CAST "source"))) || + ((attr->ns != NULL) && + xmlStrEqual(attr->ns->href, xmlSchemaNs))) { + + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + xmlSchemaPValAttr(ctxt, NULL, child, "source", + xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYURI), NULL); + child = child->next; + } else if (IS_SCHEMA(child, "documentation")) { + /* TODO: make available the content of "documentation". */ + /* + * source = anyURI + * {any attributes with non-schema namespace . . .}> + * Content: ({any})* + */ + attr = child->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if (!xmlStrEqual(attr->name, BAD_CAST "source")) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else { + if (xmlStrEqual(attr->ns->href, xmlSchemaNs) || + (xmlStrEqual(attr->name, BAD_CAST "lang") && + (!xmlStrEqual(attr->ns->href, XML_XML_NAMESPACE)))) { + + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } + attr = attr->next; + } + /* + * Attribute "xml:lang". + */ + attr = xmlSchemaGetPropNodeNs(child, (const char *) XML_XML_NAMESPACE, "lang"); + if (attr != NULL) + xmlSchemaPValAttrNode(ctxt, NULL, attr, + xmlSchemaGetBuiltInType(XML_SCHEMAS_LANGUAGE), NULL); + child = child->next; + } else { + if (!barked) + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, NULL, "(appinfo | documentation)*"); + barked = 1; + child = child->next; + } + } + + return (ret); +} + +/** + * xmlSchemaParseFacet: + * @ctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * + * parse a XML schema Facet declaration + * *WARNING* this interface is highly subject to change + * + * Returns the new type structure or NULL in case of error + */ +static xmlSchemaFacetPtr +xmlSchemaParseFacet(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, + xmlNodePtr node) +{ + xmlSchemaFacetPtr facet; + xmlNodePtr child = NULL; + const xmlChar *value; + + if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) + return (NULL); + + facet = xmlSchemaNewFacet(); + if (facet == NULL) { + xmlSchemaPErrMemory(ctxt, "allocating facet", node); + return (NULL); + } + facet->node = node; + value = xmlSchemaGetProp(ctxt, node, "value"); + if (value == NULL) { + xmlSchemaPErr2(ctxt, node, child, XML_SCHEMAP_FACET_NO_VALUE, + "Facet %s has no value\n", node->name, NULL); + xmlSchemaFreeFacet(facet); + return (NULL); + } + if (IS_SCHEMA(node, "minInclusive")) { + facet->type = XML_SCHEMA_FACET_MININCLUSIVE; + } else if (IS_SCHEMA(node, "minExclusive")) { + facet->type = XML_SCHEMA_FACET_MINEXCLUSIVE; + } else if (IS_SCHEMA(node, "maxInclusive")) { + facet->type = XML_SCHEMA_FACET_MAXINCLUSIVE; + } else if (IS_SCHEMA(node, "maxExclusive")) { + facet->type = XML_SCHEMA_FACET_MAXEXCLUSIVE; + } else if (IS_SCHEMA(node, "totalDigits")) { + facet->type = XML_SCHEMA_FACET_TOTALDIGITS; + } else if (IS_SCHEMA(node, "fractionDigits")) { + facet->type = XML_SCHEMA_FACET_FRACTIONDIGITS; + } else if (IS_SCHEMA(node, "pattern")) { + facet->type = XML_SCHEMA_FACET_PATTERN; + } else if (IS_SCHEMA(node, "enumeration")) { + facet->type = XML_SCHEMA_FACET_ENUMERATION; + } else if (IS_SCHEMA(node, "whiteSpace")) { + facet->type = XML_SCHEMA_FACET_WHITESPACE; + } else if (IS_SCHEMA(node, "length")) { + facet->type = XML_SCHEMA_FACET_LENGTH; + } else if (IS_SCHEMA(node, "maxLength")) { + facet->type = XML_SCHEMA_FACET_MAXLENGTH; + } else if (IS_SCHEMA(node, "minLength")) { + facet->type = XML_SCHEMA_FACET_MINLENGTH; + } else { + xmlSchemaPErr2(ctxt, node, child, XML_SCHEMAP_UNKNOWN_FACET_TYPE, + "Unknown facet type %s\n", node->name, NULL); + xmlSchemaFreeFacet(facet); + return (NULL); + } + xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); + facet->value = value; + if ((facet->type != XML_SCHEMA_FACET_PATTERN) && + (facet->type != XML_SCHEMA_FACET_ENUMERATION)) { + const xmlChar *fixed; + + fixed = xmlSchemaGetProp(ctxt, node, "fixed"); + if (fixed != NULL) { + if (xmlStrEqual(fixed, BAD_CAST "true")) + facet->fixed = 1; + } + } + child = node->children; + + if (IS_SCHEMA(child, "annotation")) { + facet->annot = xmlSchemaParseAnnotation(ctxt, child, 1); + child = child->next; + } + if (child != NULL) { + xmlSchemaPErr2(ctxt, node, child, XML_SCHEMAP_UNKNOWN_FACET_CHILD, + "Facet %s has unexpected child content\n", + node->name, NULL); + } + return (facet); +} + +/** + * xmlSchemaParseWildcardNs: + * @ctxt: a schema parser context + * @wildc: the wildcard, already created + * @node: a subtree containing XML Schema informations + * + * Parses the attribute "processContents" and "namespace" + * of a xsd:anyAttribute and xsd:any. + * *WARNING* this interface is highly subject to change + * + * Returns 0 if everything goes fine, a positive error code + * if something is not valid and -1 if an internal error occurs. + */ +static int +xmlSchemaParseWildcardNs(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaPtr schema ATTRIBUTE_UNUSED, + xmlSchemaWildcardPtr wildc, + xmlNodePtr node) +{ + const xmlChar *pc, *ns, *dictnsItem; + int ret = 0; + xmlChar *nsItem; + xmlSchemaWildcardNsPtr tmp, lastNs = NULL; + xmlAttrPtr attr; + + pc = xmlSchemaGetProp(ctxt, node, "processContents"); + if ((pc == NULL) + || (xmlStrEqual(pc, (const xmlChar *) "strict"))) { + wildc->processContents = XML_SCHEMAS_ANY_STRICT; + } else if (xmlStrEqual(pc, (const xmlChar *) "skip")) { + wildc->processContents = XML_SCHEMAS_ANY_SKIP; + } else if (xmlStrEqual(pc, (const xmlChar *) "lax")) { + wildc->processContents = XML_SCHEMAS_ANY_LAX; + } else { + xmlSchemaPSimpleTypeErr(ctxt, + XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, + NULL, node, + NULL, "(strict | skip | lax)", pc, + NULL, NULL, NULL); + wildc->processContents = XML_SCHEMAS_ANY_STRICT; + ret = XML_SCHEMAP_S4S_ATTR_INVALID_VALUE; + } + /* + * Build the namespace constraints. + */ + attr = xmlSchemaGetPropNode(node, "namespace"); + ns = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); + if ((attr == NULL) || (xmlStrEqual(ns, BAD_CAST "##any"))) + wildc->any = 1; + else if (xmlStrEqual(ns, BAD_CAST "##other")) { + wildc->negNsSet = xmlSchemaNewWildcardNsConstraint(ctxt); + if (wildc->negNsSet == NULL) { + return (-1); + } + wildc->negNsSet->value = ctxt->targetNamespace; + } else { + const xmlChar *end, *cur; + + cur = ns; + do { + while (IS_BLANK_CH(*cur)) + cur++; + end = cur; + while ((*end != 0) && (!(IS_BLANK_CH(*end)))) + end++; + if (end == cur) + break; + nsItem = xmlStrndup(cur, end - cur); + if ((xmlStrEqual(nsItem, BAD_CAST "##other")) || + (xmlStrEqual(nsItem, BAD_CAST "##any"))) { + xmlSchemaPSimpleTypeErr(ctxt, + XML_SCHEMAP_WILDCARD_INVALID_NS_MEMBER, + NULL, (xmlNodePtr) attr, + NULL, + "((##any | ##other) | List of (xs:anyURI | " + "(##targetNamespace | ##local)))", + nsItem, NULL, NULL, NULL); + ret = XML_SCHEMAP_WILDCARD_INVALID_NS_MEMBER; + } else { + if (xmlStrEqual(nsItem, BAD_CAST "##targetNamespace")) { + dictnsItem = ctxt->targetNamespace; + } else if (xmlStrEqual(nsItem, BAD_CAST "##local")) { + dictnsItem = NULL; + } else { + /* + * Validate the item (anyURI). + */ + xmlSchemaPValAttrNodeValue(ctxt, NULL, attr, + nsItem, xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYURI)); + dictnsItem = xmlDictLookup(ctxt->dict, nsItem, -1); + } + /* + * Avoid dublicate namespaces. + */ + tmp = wildc->nsSet; + while (tmp != NULL) { + if (dictnsItem == tmp->value) + break; + tmp = tmp->next; + } + if (tmp == NULL) { + tmp = xmlSchemaNewWildcardNsConstraint(ctxt); + if (tmp == NULL) { + xmlFree(nsItem); + return (-1); + } + tmp->value = dictnsItem; + tmp->next = NULL; + if (wildc->nsSet == NULL) + wildc->nsSet = tmp; + else + lastNs->next = tmp; + lastNs = tmp; + } + + } + xmlFree(nsItem); + cur = end; + } while (*cur != 0); + } + return (ret); +} + +static int +xmlSchemaPCheckParticleCorrect_2(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaParticlePtr item ATTRIBUTE_UNUSED, + xmlNodePtr node, + int minOccurs, + int maxOccurs) { + + if ((maxOccurs == 0) && ( minOccurs == 0)) + return (0); + if (maxOccurs != UNBOUNDED) { + /* + * TODO: Maybe we should better not create the particle, + * if min/max is invalid, since it could confuse the build of the + * content model. + */ + /* + * 3.9.6 Schema Component Constraint: Particle Correct + * + */ + if (maxOccurs < 1) { + /* + * 2.2 {max occurs} must be greater than or equal to 1. + */ + xmlSchemaPCustomAttrErr(ctxt, + XML_SCHEMAP_P_PROPS_CORRECT_2_2, + NULL, NULL, + xmlSchemaGetPropNode(node, "maxOccurs"), + "The value must be greater than or equal to 1"); + return (XML_SCHEMAP_P_PROPS_CORRECT_2_2); + } else if (minOccurs > maxOccurs) { + /* + * 2.1 {min occurs} must not be greater than {max occurs}. + */ + xmlSchemaPCustomAttrErr(ctxt, + XML_SCHEMAP_P_PROPS_CORRECT_2_1, + NULL, NULL, + xmlSchemaGetPropNode(node, "minOccurs"), + "The value must not be greater than the value of 'maxOccurs'"); + return (XML_SCHEMAP_P_PROPS_CORRECT_2_1); + } + } + return (0); +} + +/** + * xmlSchemaParseAny: + * @ctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * + * Parsea a XML schema element. A particle and wildcard + * will be created (except if minOccurs==maxOccurs==0, in this case + * nothing will be created). + * *WARNING* this interface is highly subject to change + * + * Returns the particle or NULL in case of error or if minOccurs==maxOccurs==0 + */ +static xmlSchemaParticlePtr +xmlSchemaParseAny(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, + xmlNodePtr node) +{ + xmlSchemaParticlePtr particle; + xmlNodePtr child = NULL; + xmlSchemaWildcardPtr wild; + int min, max; + xmlAttrPtr attr; + xmlSchemaAnnotPtr annot = NULL; + + if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) + return (NULL); + /* + * Check for illegal attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && + (!xmlStrEqual(attr->name, BAD_CAST "minOccurs")) && + (!xmlStrEqual(attr->name, BAD_CAST "maxOccurs")) && + (!xmlStrEqual(attr->name, BAD_CAST "namespace")) && + (!xmlStrEqual(attr->name, BAD_CAST "processContents"))) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); + /* + * minOccurs/maxOccurs. + */ + max = xmlGetMaxOccurs(ctxt, node, 0, UNBOUNDED, 1, + "(xs:nonNegativeInteger | unbounded)"); + min = xmlGetMinOccurs(ctxt, node, 0, -1, 1, + "xs:nonNegativeInteger"); + xmlSchemaPCheckParticleCorrect_2(ctxt, NULL, node, min, max); + /* + * Create & parse the wildcard. + */ + wild = xmlSchemaAddWildcard(ctxt, schema, XML_SCHEMA_TYPE_ANY, node); + if (wild == NULL) + return (NULL); + xmlSchemaParseWildcardNs(ctxt, schema, wild, node); + /* + * And now for the children... + */ + child = node->children; + if (IS_SCHEMA(child, "annotation")) { + annot = xmlSchemaParseAnnotation(ctxt, child, 1); + child = child->next; + } + if (child != NULL) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, + NULL, "(annotation?)"); + } + /* + * No component if minOccurs==maxOccurs==0. + */ + if ((min == 0) && (max == 0)) { + /* Don't free the wildcard, since it's already on the list. */ + return (NULL); + } + /* + * Create the particle. + */ + particle = xmlSchemaAddParticle(ctxt, node, min, max); + if (particle == NULL) + return (NULL); + particle->annot = annot; + particle->children = (xmlSchemaTreeItemPtr) wild; + + return (particle); +} + +/** + * xmlSchemaParseNotation: + * @ctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * + * parse a XML schema Notation declaration + * + * Returns the new structure or NULL in case of error + */ +static xmlSchemaNotationPtr +xmlSchemaParseNotation(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, + xmlNodePtr node) +{ + const xmlChar *name; + xmlSchemaNotationPtr ret; + xmlNodePtr child = NULL; + + if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) + return (NULL); + name = xmlSchemaGetProp(ctxt, node, "name"); + if (name == NULL) { + xmlSchemaPErr2(ctxt, node, child, XML_SCHEMAP_NOTATION_NO_NAME, + "Notation has no name\n", NULL, NULL); + return (NULL); + } + ret = xmlSchemaAddNotation(ctxt, schema, name, + ctxt->targetNamespace, node); + if (ret == NULL) + return (NULL); + xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); + + child = node->children; + if (IS_SCHEMA(child, "annotation")) { + ret->annot = xmlSchemaParseAnnotation(ctxt, child, 1); + child = child->next; + } + if (child != NULL) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, + NULL, "(annotation?)"); + } + + return (ret); +} + +/** + * xmlSchemaParseAnyAttribute: + * @ctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * + * parse a XML schema AnyAttrribute declaration + * *WARNING* this interface is highly subject to change + * + * Returns a wildcard or NULL. + */ +static xmlSchemaWildcardPtr +xmlSchemaParseAnyAttribute(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaPtr schema, xmlNodePtr node) +{ + xmlSchemaWildcardPtr ret; + xmlNodePtr child = NULL; + xmlAttrPtr attr; + + if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) + return (NULL); + + ret = xmlSchemaAddWildcard(ctxt, schema, XML_SCHEMA_TYPE_ANY_ATTRIBUTE, + node); + if (ret == NULL) { + return (NULL); + } + /* + * Check for illegal attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && + (!xmlStrEqual(attr->name, BAD_CAST "namespace")) && + (!xmlStrEqual(attr->name, BAD_CAST "processContents"))) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); + /* + * Parse the namespace list. + */ + if (xmlSchemaParseWildcardNs(ctxt, schema, ret, node) != 0) + return (NULL); + /* + * And now for the children... + */ + child = node->children; + if (IS_SCHEMA(child, "annotation")) { + ret->annot = xmlSchemaParseAnnotation(ctxt, child, 1); + child = child->next; + } + if (child != NULL) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, + NULL, "(annotation?)"); + } + + return (ret); +} + + +/** + * xmlSchemaParseAttribute: + * @ctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * + * parse a XML schema Attrribute declaration + * *WARNING* this interface is highly subject to change + * + * Returns the attribute declaration. + */ +static xmlSchemaBasicItemPtr +xmlSchemaParseLocalAttribute(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaPtr schema, + xmlNodePtr node, + xmlSchemaItemListPtr uses, + int parentType) +{ + const xmlChar *attrValue, *name = NULL, *ns = NULL; + xmlSchemaAttributeUsePtr use = NULL; + xmlNodePtr child = NULL; + xmlAttrPtr attr; + const xmlChar *tmpNs = NULL, *tmpName = NULL, *defValue = NULL; + int isRef = 0, occurs = XML_SCHEMAS_ATTR_USE_OPTIONAL; + int nberrors, hasForm = 0, defValueType = 0; + +#define WXS_ATTR_DEF_VAL_DEFAULT 1 +#define WXS_ATTR_DEF_VAL_FIXED 2 + + /* + * 3.2.3 Constraints on XML Representations of Attribute Declarations + */ + + if ((pctxt == NULL) || (schema == NULL) || (node == NULL)) + return (NULL); + attr = xmlSchemaGetPropNode(node, "ref"); + if (attr != NULL) { + if (xmlSchemaPValAttrNodeQName(pctxt, schema, + NULL, attr, &tmpNs, &tmpName) != 0) { + return (NULL); + } + if (xmlSchemaCheckReference(pctxt, schema, node, attr, tmpNs) != 0) + return(NULL); + isRef = 1; + } + nberrors = pctxt->nberrors; + /* + * Check for illegal attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if (isRef) { + if (xmlStrEqual(attr->name, BAD_CAST "id")) { + xmlSchemaPValAttrNodeID(pctxt, attr); + goto attr_next; + } else if (xmlStrEqual(attr->name, BAD_CAST "ref")) { + goto attr_next; + } + } else { + if (xmlStrEqual(attr->name, BAD_CAST "name")) { + goto attr_next; + } else if (xmlStrEqual(attr->name, BAD_CAST "id")) { + xmlSchemaPValAttrNodeID(pctxt, attr); + goto attr_next; + } else if (xmlStrEqual(attr->name, BAD_CAST "type")) { + xmlSchemaPValAttrNodeQName(pctxt, schema, NULL, + attr, &tmpNs, &tmpName); + goto attr_next; + } else if (xmlStrEqual(attr->name, BAD_CAST "form")) { + /* + * Evaluate the target namespace + */ + hasForm = 1; + attrValue = xmlSchemaGetNodeContent(pctxt, + (xmlNodePtr) attr); + if (xmlStrEqual(attrValue, BAD_CAST "qualified")) { + ns = pctxt->targetNamespace; + } else if (!xmlStrEqual(attrValue, BAD_CAST "unqualified")) + { + xmlSchemaPSimpleTypeErr(pctxt, + XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, + NULL, (xmlNodePtr) attr, + NULL, "(qualified | unqualified)", + attrValue, NULL, NULL, NULL); + } + goto attr_next; + } + } + if (xmlStrEqual(attr->name, BAD_CAST "use")) { + + attrValue = xmlSchemaGetNodeContent(pctxt, (xmlNodePtr) attr); + /* TODO: Maybe we need to normalize the value beforehand. */ + if (xmlStrEqual(attrValue, BAD_CAST "optional")) + occurs = XML_SCHEMAS_ATTR_USE_OPTIONAL; + else if (xmlStrEqual(attrValue, BAD_CAST "prohibited")) + occurs = XML_SCHEMAS_ATTR_USE_PROHIBITED; + else if (xmlStrEqual(attrValue, BAD_CAST "required")) + occurs = XML_SCHEMAS_ATTR_USE_REQUIRED; + else { + xmlSchemaPSimpleTypeErr(pctxt, + XML_SCHEMAP_INVALID_ATTR_USE, + NULL, (xmlNodePtr) attr, + NULL, "(optional | prohibited | required)", + attrValue, NULL, NULL, NULL); + } + goto attr_next; + } else if (xmlStrEqual(attr->name, BAD_CAST "default")) { + /* + * 3.2.3 : 1 + * default and fixed must not both be present. + */ + if (defValue) { + xmlSchemaPMutualExclAttrErr(pctxt, + XML_SCHEMAP_SRC_ATTRIBUTE_1, + NULL, attr, "default", "fixed"); + } else { + defValue = xmlSchemaGetNodeContent(pctxt, (xmlNodePtr) attr); + defValueType = WXS_ATTR_DEF_VAL_DEFAULT; + } + goto attr_next; + } else if (xmlStrEqual(attr->name, BAD_CAST "fixed")) { + /* + * 3.2.3 : 1 + * default and fixed must not both be present. + */ + if (defValue) { + xmlSchemaPMutualExclAttrErr(pctxt, + XML_SCHEMAP_SRC_ATTRIBUTE_1, + NULL, attr, "default", "fixed"); + } else { + defValue = xmlSchemaGetNodeContent(pctxt, (xmlNodePtr) attr); + defValueType = WXS_ATTR_DEF_VAL_FIXED; + } + goto attr_next; + } + } else if (! xmlStrEqual(attr->ns->href, xmlSchemaNs)) + goto attr_next; + + xmlSchemaPIllegalAttrErr(pctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + +attr_next: + attr = attr->next; + } + /* + * 3.2.3 : 2 + * If default and use are both present, use must have + * the actual value optional. + */ + if ((defValueType == WXS_ATTR_DEF_VAL_DEFAULT) && + (occurs != XML_SCHEMAS_ATTR_USE_OPTIONAL)) { + xmlSchemaPSimpleTypeErr(pctxt, + XML_SCHEMAP_SRC_ATTRIBUTE_2, + NULL, node, NULL, + "(optional | prohibited | required)", NULL, + "The value of the attribute 'use' must be 'optional' " + "if the attribute 'default' is present", + NULL, NULL); + } + /* + * We want correct attributes. + */ + if (nberrors != pctxt->nberrors) + return(NULL); + if (! isRef) { + xmlSchemaAttributePtr attrDecl; + + /* TODO: move XML_SCHEMAS_QUALIF_ATTR to the parser. */ + if ((! hasForm) && (schema->flags & XML_SCHEMAS_QUALIF_ATTR)) + ns = pctxt->targetNamespace; + /* + * 3.2.6 Schema Component Constraint: xsi: Not Allowed + * TODO: Move this to the component layer. + */ + if (xmlStrEqual(ns, xmlSchemaInstanceNs)) { + xmlSchemaCustomErr(ACTXT_CAST pctxt, + XML_SCHEMAP_NO_XSI, + node, NULL, + "The target namespace must not match '%s'", + xmlSchemaInstanceNs, NULL); + } + attr = xmlSchemaGetPropNode(node, "name"); + if (attr == NULL) { + xmlSchemaPMissingAttrErr(pctxt, XML_SCHEMAP_S4S_ATTR_MISSING, + NULL, node, "name", NULL); + return (NULL); + } + if (xmlSchemaPValAttrNode(pctxt, NULL, attr, + xmlSchemaGetBuiltInType(XML_SCHEMAS_NCNAME), &name) != 0) { + return (NULL); + } + /* + * 3.2.6 Schema Component Constraint: xmlns Not Allowed + * TODO: Move this to the component layer. + */ + if (xmlStrEqual(name, BAD_CAST "xmlns")) { + xmlSchemaPSimpleTypeErr(pctxt, + XML_SCHEMAP_NO_XMLNS, + NULL, (xmlNodePtr) attr, + xmlSchemaGetBuiltInType(XML_SCHEMAS_NCNAME), NULL, NULL, + "The value of the attribute must not match 'xmlns'", + NULL, NULL); + return (NULL); + } + if (occurs == XML_SCHEMAS_ATTR_USE_PROHIBITED) + goto check_children; + /* + * Create the attribute use component. + */ + use = xmlSchemaAddAttributeUse(pctxt, node); + if (use == NULL) + return(NULL); + use->occurs = occurs; + /* + * Create the attribute declaration. + */ + attrDecl = xmlSchemaAddAttribute(pctxt, schema, name, ns, node, 0); + if (attrDecl == NULL) + return (NULL); + if (tmpName != NULL) { + attrDecl->typeName = tmpName; + attrDecl->typeNs = tmpNs; + } + use->attrDecl = attrDecl; + /* + * Value constraint. + */ + if (defValue != NULL) { + attrDecl->defValue = defValue; + if (defValueType == WXS_ATTR_DEF_VAL_FIXED) + attrDecl->flags |= XML_SCHEMAS_ATTR_FIXED; + } + } else if (occurs != XML_SCHEMAS_ATTR_USE_PROHIBITED) { + xmlSchemaQNameRefPtr ref; + + /* + * Create the attribute use component. + */ + use = xmlSchemaAddAttributeUse(pctxt, node); + if (use == NULL) + return(NULL); + /* + * We need to resolve the reference at later stage. + */ + WXS_ADD_PENDING(pctxt, use); + use->occurs = occurs; + /* + * Create a QName reference to the attribute declaration. + */ + ref = xmlSchemaNewQNameRef(pctxt, XML_SCHEMA_TYPE_ATTRIBUTE, + tmpName, tmpNs); + if (ref == NULL) + return(NULL); + /* + * Assign the reference. This will be substituted for the + * referenced attribute declaration when the QName is resolved. + */ + use->attrDecl = WXS_ATTR_CAST ref; + /* + * Value constraint. + */ + if (defValue != NULL) + use->defValue = defValue; + if (defValueType == WXS_ATTR_DEF_VAL_FIXED) + use->flags |= XML_SCHEMA_ATTR_USE_FIXED; + } + +check_children: + /* + * And now for the children... + */ + child = node->children; + if (occurs == XML_SCHEMAS_ATTR_USE_PROHIBITED) { + xmlSchemaAttributeUseProhibPtr prohib; + + if (IS_SCHEMA(child, "annotation")) { + xmlSchemaParseAnnotation(pctxt, child, 0); + child = child->next; + } + if (child != NULL) { + xmlSchemaPContentErr(pctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, NULL, + "(annotation?)"); + } + /* + * Check for pointlessness of attribute prohibitions. + */ + if (parentType == XML_SCHEMA_TYPE_ATTRIBUTEGROUP) { + xmlSchemaCustomWarning(ACTXT_CAST pctxt, + XML_SCHEMAP_WARN_ATTR_POINTLESS_PROH, + node, NULL, + "Skipping attribute use prohibition, since it is " + "pointless inside an ", + NULL, NULL, NULL); + return(NULL); + } else if (parentType == XML_SCHEMA_TYPE_EXTENSION) { + xmlSchemaCustomWarning(ACTXT_CAST pctxt, + XML_SCHEMAP_WARN_ATTR_POINTLESS_PROH, + node, NULL, + "Skipping attribute use prohibition, since it is " + "pointless when extending a type", + NULL, NULL, NULL); + return(NULL); + } + if (! isRef) { + tmpName = name; + tmpNs = ns; + } + /* + * Check for duplicate attribute prohibitions. + */ + if (uses) { + int i; + + for (i = 0; i < uses->nbItems; i++) { + use = uses->items[i]; + if ((use->type == XML_SCHEMA_EXTRA_ATTR_USE_PROHIB) && + (tmpName == (WXS_ATTR_PROHIB_CAST use)->name) && + (tmpNs == (WXS_ATTR_PROHIB_CAST use)->targetNamespace)) + { + xmlChar *str = NULL; + + xmlSchemaCustomWarning(ACTXT_CAST pctxt, + XML_SCHEMAP_WARN_ATTR_POINTLESS_PROH, + node, NULL, + "Skipping duplicate attribute use prohibition '%s'", + xmlSchemaFormatQName(&str, tmpNs, tmpName), + NULL, NULL); + FREE_AND_NULL(str) + return(NULL); + } + } + } + /* + * Create the attribute prohibition helper component. + */ + prohib = xmlSchemaAddAttributeUseProhib(pctxt); + if (prohib == NULL) + return(NULL); + prohib->node = node; + prohib->name = tmpName; + prohib->targetNamespace = tmpNs; + if (isRef) { + /* + * We need at least to resolve to the attribute declaration. + */ + WXS_ADD_PENDING(pctxt, prohib); + } + return(WXS_BASIC_CAST prohib); + } else { + if (IS_SCHEMA(child, "annotation")) { + /* + * TODO: Should this go into the attr decl? + */ + use->annot = xmlSchemaParseAnnotation(pctxt, child, 1); + child = child->next; + } + if (isRef) { + if (child != NULL) { + if (IS_SCHEMA(child, "simpleType")) + /* + * 3.2.3 : 3.2 + * If ref is present, then all of , + * form and type must be absent. + */ + xmlSchemaPContentErr(pctxt, + XML_SCHEMAP_SRC_ATTRIBUTE_3_2, + NULL, node, child, NULL, + "(annotation?)"); + else + xmlSchemaPContentErr(pctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, NULL, + "(annotation?)"); + } + } else { + if (IS_SCHEMA(child, "simpleType")) { + if (WXS_ATTRUSE_DECL(use)->typeName != NULL) { + /* + * 3.2.3 : 4 + * type and must not both be present. + */ + xmlSchemaPContentErr(pctxt, XML_SCHEMAP_SRC_ATTRIBUTE_4, + NULL, node, child, + "The attribute 'type' and the child " + "are mutually exclusive", NULL); + } else + WXS_ATTRUSE_TYPEDEF(use) = + xmlSchemaParseSimpleType(pctxt, schema, child, 0); + child = child->next; + } + if (child != NULL) + xmlSchemaPContentErr(pctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, NULL, + "(annotation?, simpleType?)"); + } + } + return (WXS_BASIC_CAST use); +} + + +static xmlSchemaAttributePtr +xmlSchemaParseGlobalAttribute(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaPtr schema, + xmlNodePtr node) +{ + const xmlChar *attrValue; + xmlSchemaAttributePtr ret; + xmlNodePtr child = NULL; + xmlAttrPtr attr; + + /* + * Note that the w3c spec assumes the schema to be validated with schema + * for schemas beforehand. + * + * 3.2.3 Constraints on XML Representations of Attribute Declarations + */ + if ((pctxt == NULL) || (schema == NULL) || (node == NULL)) + return (NULL); + /* + * 3.2.3 : 3.1 + * One of ref or name must be present, but not both + */ + attr = xmlSchemaGetPropNode(node, "name"); + if (attr == NULL) { + xmlSchemaPMissingAttrErr(pctxt, XML_SCHEMAP_S4S_ATTR_MISSING, + NULL, node, "name", NULL); + return (NULL); + } + if (xmlSchemaPValAttrNode(pctxt, NULL, attr, + xmlSchemaGetBuiltInType(XML_SCHEMAS_NCNAME), &attrValue) != 0) { + return (NULL); + } + /* + * 3.2.6 Schema Component Constraint: xmlns Not Allowed + * TODO: Move this to the component layer. + */ + if (xmlStrEqual(attrValue, BAD_CAST "xmlns")) { + xmlSchemaPSimpleTypeErr(pctxt, + XML_SCHEMAP_NO_XMLNS, + NULL, (xmlNodePtr) attr, + xmlSchemaGetBuiltInType(XML_SCHEMAS_NCNAME), NULL, NULL, + "The value of the attribute must not match 'xmlns'", + NULL, NULL); + return (NULL); + } + /* + * 3.2.6 Schema Component Constraint: xsi: Not Allowed + * TODO: Move this to the component layer. + * Or better leave it here and add it to the component layer + * if we have a schema construction API. + */ + if (xmlStrEqual(pctxt->targetNamespace, xmlSchemaInstanceNs)) { + xmlSchemaCustomErr(ACTXT_CAST pctxt, + XML_SCHEMAP_NO_XSI, node, NULL, + "The target namespace must not match '%s'", + xmlSchemaInstanceNs, NULL); + } + + ret = xmlSchemaAddAttribute(pctxt, schema, attrValue, + pctxt->targetNamespace, node, 1); + if (ret == NULL) + return (NULL); + ret->flags |= XML_SCHEMAS_ATTR_GLOBAL; + + /* + * Check for illegal attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && + (!xmlStrEqual(attr->name, BAD_CAST "default")) && + (!xmlStrEqual(attr->name, BAD_CAST "fixed")) && + (!xmlStrEqual(attr->name, BAD_CAST "name")) && + (!xmlStrEqual(attr->name, BAD_CAST "type"))) + { + xmlSchemaPIllegalAttrErr(pctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + xmlSchemaPIllegalAttrErr(pctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + xmlSchemaPValAttrQName(pctxt, schema, NULL, + node, "type", &ret->typeNs, &ret->typeName); + + xmlSchemaPValAttrID(pctxt, node, BAD_CAST "id"); + /* + * Attribute "fixed". + */ + ret->defValue = xmlSchemaGetProp(pctxt, node, "fixed"); + if (ret->defValue != NULL) + ret->flags |= XML_SCHEMAS_ATTR_FIXED; + /* + * Attribute "default". + */ + attr = xmlSchemaGetPropNode(node, "default"); + if (attr != NULL) { + /* + * 3.2.3 : 1 + * default and fixed must not both be present. + */ + if (ret->flags & XML_SCHEMAS_ATTR_FIXED) { + xmlSchemaPMutualExclAttrErr(pctxt, XML_SCHEMAP_SRC_ATTRIBUTE_1, + WXS_BASIC_CAST ret, attr, "default", "fixed"); + } else + ret->defValue = xmlSchemaGetNodeContent(pctxt, (xmlNodePtr) attr); + } + /* + * And now for the children... + */ + child = node->children; + if (IS_SCHEMA(child, "annotation")) { + ret->annot = xmlSchemaParseAnnotation(pctxt, child, 1); + child = child->next; + } + if (IS_SCHEMA(child, "simpleType")) { + if (ret->typeName != NULL) { + /* + * 3.2.3 : 4 + * type and must not both be present. + */ + xmlSchemaPContentErr(pctxt, XML_SCHEMAP_SRC_ATTRIBUTE_4, + NULL, node, child, + "The attribute 'type' and the child " + "are mutually exclusive", NULL); + } else + ret->subtypes = xmlSchemaParseSimpleType(pctxt, schema, child, 0); + child = child->next; + } + if (child != NULL) + xmlSchemaPContentErr(pctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, NULL, + "(annotation?, simpleType?)"); + + return (ret); +} + +/** + * xmlSchemaParseAttributeGroupRef: + * @ctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * + * Parse an attribute group definition reference. + * Note that a reference to an attribute group does not + * correspond to any component at all. + * *WARNING* this interface is highly subject to change + * + * Returns the attribute group or NULL in case of error. + */ +static xmlSchemaQNameRefPtr +xmlSchemaParseAttributeGroupRef(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaPtr schema, + xmlNodePtr node) +{ + xmlSchemaQNameRefPtr ret; + xmlNodePtr child = NULL; + xmlAttrPtr attr; + const xmlChar *refNs = NULL, *ref = NULL; + + if ((pctxt == NULL) || (schema == NULL) || (node == NULL)) + return (NULL); + + attr = xmlSchemaGetPropNode(node, "ref"); + if (attr == NULL) { + xmlSchemaPMissingAttrErr(pctxt, + XML_SCHEMAP_S4S_ATTR_MISSING, + NULL, node, "ref", NULL); + return (NULL); + } + xmlSchemaPValAttrNodeQName(pctxt, schema, + NULL, attr, &refNs, &ref); + if (xmlSchemaCheckReference(pctxt, schema, node, attr, refNs) != 0) + return(NULL); + + /* + * Check for illegal attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if ((!xmlStrEqual(attr->name, BAD_CAST "ref")) && + (!xmlStrEqual(attr->name, BAD_CAST "id"))) + { + xmlSchemaPIllegalAttrErr(pctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + xmlSchemaPIllegalAttrErr(pctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + /* Attribute ID */ + xmlSchemaPValAttrID(pctxt, node, BAD_CAST "id"); + + /* + * And now for the children... + */ + child = node->children; + if (IS_SCHEMA(child, "annotation")) { + /* + * TODO: We do not have a place to store the annotation, do we? + */ + xmlSchemaParseAnnotation(pctxt, child, 0); + child = child->next; + } + if (child != NULL) { + xmlSchemaPContentErr(pctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, NULL, + "(annotation?)"); + } + + /* + * Handle attribute group redefinitions. + */ + if (pctxt->isRedefine && pctxt->redef && + (pctxt->redef->item->type == + XML_SCHEMA_TYPE_ATTRIBUTEGROUP) && + (ref == pctxt->redef->refName) && + (refNs == pctxt->redef->refTargetNs)) + { + /* + * SPEC src-redefine: + * (7.1) "If it has an among its contents + * the ·actual value· of whose ref [attribute] is the same + * as the ·actual value· of its own name attribute plus + * target namespace, then it must have exactly one such group." + */ + if (pctxt->redefCounter != 0) { + xmlChar *str = NULL; + + xmlSchemaCustomErr(ACTXT_CAST pctxt, + XML_SCHEMAP_SRC_REDEFINE, node, NULL, + "The redefining attribute group definition " + "'%s' must not contain more than one " + "reference to the redefined definition", + xmlSchemaFormatQName(&str, refNs, ref), NULL); + FREE_AND_NULL(str); + return(NULL); + } + pctxt->redefCounter++; + /* + * URGENT TODO: How to ensure that the reference will not be + * handled by the normal component resolution mechanism? + */ + ret = xmlSchemaNewQNameRef(pctxt, + XML_SCHEMA_TYPE_ATTRIBUTEGROUP, ref, refNs); + if (ret == NULL) + return(NULL); + ret->node = node; + pctxt->redef->reference = WXS_BASIC_CAST ret; + } else { + /* + * Create a QName-reference helper component. We will substitute this + * component for the attribute uses of the referenced attribute group + * definition. + */ + ret = xmlSchemaNewQNameRef(pctxt, + XML_SCHEMA_TYPE_ATTRIBUTEGROUP, ref, refNs); + if (ret == NULL) + return(NULL); + ret->node = node; + /* Add to pending items, to be able to resolve the reference. */ + WXS_ADD_PENDING(pctxt, ret); + } + return (ret); +} + +/** + * xmlSchemaParseAttributeGroupDefinition: + * @pctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * + * parse a XML schema Attribute Group declaration + * *WARNING* this interface is highly subject to change + * + * Returns the attribute group definition or NULL in case of error. + */ +static xmlSchemaAttributeGroupPtr +xmlSchemaParseAttributeGroupDefinition(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaPtr schema, + xmlNodePtr node) +{ + const xmlChar *name; + xmlSchemaAttributeGroupPtr ret; + xmlNodePtr child = NULL; + xmlAttrPtr attr; + int hasRefs = 0; + + if ((pctxt == NULL) || (schema == NULL) || (node == NULL)) + return (NULL); + + attr = xmlSchemaGetPropNode(node, "name"); + if (attr == NULL) { + xmlSchemaPMissingAttrErr(pctxt, + XML_SCHEMAP_S4S_ATTR_MISSING, + NULL, node, "name", NULL); + return (NULL); + } + /* + * The name is crucial, exit if invalid. + */ + if (xmlSchemaPValAttrNode(pctxt, + NULL, attr, + xmlSchemaGetBuiltInType(XML_SCHEMAS_NCNAME), &name) != 0) { + return (NULL); + } + ret = xmlSchemaAddAttributeGroupDefinition(pctxt, schema, + name, pctxt->targetNamespace, node); + if (ret == NULL) + return (NULL); + /* + * Check for illegal attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if ((!xmlStrEqual(attr->name, BAD_CAST "name")) && + (!xmlStrEqual(attr->name, BAD_CAST "id"))) + { + xmlSchemaPIllegalAttrErr(pctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + xmlSchemaPIllegalAttrErr(pctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + /* Attribute ID */ + xmlSchemaPValAttrID(pctxt, node, BAD_CAST "id"); + /* + * And now for the children... + */ + child = node->children; + if (IS_SCHEMA(child, "annotation")) { + ret->annot = xmlSchemaParseAnnotation(pctxt, child, 1); + child = child->next; + } + /* + * Parse contained attribute decls/refs. + */ + if (xmlSchemaParseLocalAttributes(pctxt, schema, &child, + (xmlSchemaItemListPtr *) &(ret->attrUses), + XML_SCHEMA_TYPE_ATTRIBUTEGROUP, &hasRefs) == -1) + return(NULL); + if (hasRefs) + ret->flags |= XML_SCHEMAS_ATTRGROUP_HAS_REFS; + /* + * Parse the attribute wildcard. + */ + if (IS_SCHEMA(child, "anyAttribute")) { + ret->attributeWildcard = xmlSchemaParseAnyAttribute(pctxt, + schema, child); + child = child->next; + } + if (child != NULL) { + xmlSchemaPContentErr(pctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, NULL, + "(annotation?, ((attribute | attributeGroup)*, anyAttribute?))"); + } + return (ret); +} + +/** + * xmlSchemaPValAttrFormDefault: + * @value: the value + * @flags: the flags to be modified + * @flagQualified: the specific flag for "qualified" + * + * Returns 0 if the value is valid, 1 otherwise. + */ +static int +xmlSchemaPValAttrFormDefault(const xmlChar *value, + int *flags, + int flagQualified) +{ + if (xmlStrEqual(value, BAD_CAST "qualified")) { + if ((*flags & flagQualified) == 0) + *flags |= flagQualified; + } else if (!xmlStrEqual(value, BAD_CAST "unqualified")) + return (1); + + return (0); +} + +/** + * xmlSchemaPValAttrBlockFinal: + * @value: the value + * @flags: the flags to be modified + * @flagAll: the specific flag for "#all" + * @flagExtension: the specific flag for "extension" + * @flagRestriction: the specific flag for "restriction" + * @flagSubstitution: the specific flag for "substitution" + * @flagList: the specific flag for "list" + * @flagUnion: the specific flag for "union" + * + * Validates the value of the attribute "final" and "block". The value + * is converted into the specified flag values and returned in @flags. + * + * Returns 0 if the value is valid, 1 otherwise. + */ + +static int +xmlSchemaPValAttrBlockFinal(const xmlChar *value, + int *flags, + int flagAll, + int flagExtension, + int flagRestriction, + int flagSubstitution, + int flagList, + int flagUnion) +{ + int ret = 0; + + /* + * TODO: This does not check for dublicate entries. + */ + if ((flags == NULL) || (value == NULL)) + return (-1); + if (value[0] == 0) + return (0); + if (xmlStrEqual(value, BAD_CAST "#all")) { + if (flagAll != -1) + *flags |= flagAll; + else { + if (flagExtension != -1) + *flags |= flagExtension; + if (flagRestriction != -1) + *flags |= flagRestriction; + if (flagSubstitution != -1) + *flags |= flagSubstitution; + if (flagList != -1) + *flags |= flagList; + if (flagUnion != -1) + *flags |= flagUnion; + } + } else { + const xmlChar *end, *cur = value; + xmlChar *item; + + do { + while (IS_BLANK_CH(*cur)) + cur++; + end = cur; + while ((*end != 0) && (!(IS_BLANK_CH(*end)))) + end++; + if (end == cur) + break; + item = xmlStrndup(cur, end - cur); + if (xmlStrEqual(item, BAD_CAST "extension")) { + if (flagExtension != -1) { + if ((*flags & flagExtension) == 0) + *flags |= flagExtension; + } else + ret = 1; + } else if (xmlStrEqual(item, BAD_CAST "restriction")) { + if (flagRestriction != -1) { + if ((*flags & flagRestriction) == 0) + *flags |= flagRestriction; + } else + ret = 1; + } else if (xmlStrEqual(item, BAD_CAST "substitution")) { + if (flagSubstitution != -1) { + if ((*flags & flagSubstitution) == 0) + *flags |= flagSubstitution; + } else + ret = 1; + } else if (xmlStrEqual(item, BAD_CAST "list")) { + if (flagList != -1) { + if ((*flags & flagList) == 0) + *flags |= flagList; + } else + ret = 1; + } else if (xmlStrEqual(item, BAD_CAST "union")) { + if (flagUnion != -1) { + if ((*flags & flagUnion) == 0) + *flags |= flagUnion; + } else + ret = 1; + } else + ret = 1; + if (item != NULL) + xmlFree(item); + cur = end; + } while ((ret == 0) && (*cur != 0)); + } + + return (ret); +} + +static int +xmlSchemaCheckCSelectorXPath(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaIDCPtr idc, + xmlSchemaIDCSelectPtr selector, + xmlAttrPtr attr, + int isField) +{ + xmlNodePtr node; + + /* + * c-selector-xpath: + * Schema Component Constraint: Selector Value OK + * + * TODO: 1 The {selector} must be a valid XPath expression, as defined + * in [XPath]. + */ + if (selector == NULL) { + xmlSchemaPErr(ctxt, idc->node, + XML_SCHEMAP_INTERNAL, + "Internal error: xmlSchemaCheckCSelectorXPath, " + "the selector is not specified.\n", NULL, NULL); + return (-1); + } + if (attr == NULL) + node = idc->node; + else + node = (xmlNodePtr) attr; + if (selector->xpath == NULL) { + xmlSchemaPCustomErr(ctxt, + /* TODO: Adjust error code. */ + XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, + NULL, node, + "The XPath expression of the selector is not valid", NULL); + return (XML_SCHEMAP_S4S_ATTR_INVALID_VALUE); + } else { + const xmlChar **nsArray = NULL; + xmlNsPtr *nsList = NULL; + /* + * Compile the XPath expression. + */ + /* + * TODO: We need the array of in-scope namespaces for compilation. + * TODO: Call xmlPatterncompile with different options for selector/ + * field. + */ + if (attr == NULL) + nsList = NULL; + else + nsList = xmlGetNsList(attr->doc, attr->parent); + /* + * Build an array of prefixes and namespaces. + */ + if (nsList != NULL) { + int i, count = 0; + + for (i = 0; nsList[i] != NULL; i++) + count++; + + nsArray = (const xmlChar **) xmlMalloc( + (count * 2 + 1) * sizeof(const xmlChar *)); + if (nsArray == NULL) { + xmlSchemaPErrMemory(ctxt, "allocating a namespace array", + NULL); + xmlFree(nsList); + return (-1); + } + for (i = 0; i < count; i++) { + nsArray[2 * i] = nsList[i]->href; + nsArray[2 * i + 1] = nsList[i]->prefix; + } + nsArray[count * 2] = NULL; + xmlFree(nsList); + } + /* + * TODO: Differentiate between "selector" and "field". + */ + if (isField) + selector->xpathComp = (void *) xmlPatterncompile(selector->xpath, + NULL, XML_PATTERN_XSFIELD, nsArray); + else + selector->xpathComp = (void *) xmlPatterncompile(selector->xpath, + NULL, XML_PATTERN_XSSEL, nsArray); + if (nsArray != NULL) + xmlFree((xmlChar **) nsArray); + + if (selector->xpathComp == NULL) { + xmlSchemaPCustomErr(ctxt, + /* TODO: Adjust error code? */ + XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, + NULL, node, + "The XPath expression '%s' could not be " + "compiled", selector->xpath); + return (XML_SCHEMAP_S4S_ATTR_INVALID_VALUE); + } + } + return (0); +} + +#define ADD_ANNOTATION(annot) \ + xmlSchemaAnnotPtr cur = item->annot; \ + if (item->annot == NULL) { \ + item->annot = annot; \ + return (annot); \ + } \ + cur = item->annot; \ + if (cur->next != NULL) { \ + cur = cur->next; \ + } \ + cur->next = annot; + +/** + * xmlSchemaAssignAnnotation: + * @item: the schema component + * @annot: the annotation + * + * Adds the annotation to the given schema component. + * + * Returns the given annotaion. + */ +static xmlSchemaAnnotPtr +xmlSchemaAddAnnotation(xmlSchemaAnnotItemPtr annItem, + xmlSchemaAnnotPtr annot) +{ + if ((annItem == NULL) || (annot == NULL)) + return (NULL); + switch (annItem->type) { + case XML_SCHEMA_TYPE_ELEMENT: { + xmlSchemaElementPtr item = (xmlSchemaElementPtr) annItem; + ADD_ANNOTATION(annot) + } + break; + case XML_SCHEMA_TYPE_ATTRIBUTE: { + xmlSchemaAttributePtr item = (xmlSchemaAttributePtr) annItem; + ADD_ANNOTATION(annot) + } + break; + case XML_SCHEMA_TYPE_ANY_ATTRIBUTE: + case XML_SCHEMA_TYPE_ANY: { + xmlSchemaWildcardPtr item = (xmlSchemaWildcardPtr) annItem; + ADD_ANNOTATION(annot) + } + break; + case XML_SCHEMA_TYPE_PARTICLE: + case XML_SCHEMA_TYPE_IDC_KEY: + case XML_SCHEMA_TYPE_IDC_KEYREF: + case XML_SCHEMA_TYPE_IDC_UNIQUE: { + xmlSchemaAnnotItemPtr item = (xmlSchemaAnnotItemPtr) annItem; + ADD_ANNOTATION(annot) + } + break; + case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: { + xmlSchemaAttributeGroupPtr item = + (xmlSchemaAttributeGroupPtr) annItem; + ADD_ANNOTATION(annot) + } + break; + case XML_SCHEMA_TYPE_NOTATION: { + xmlSchemaNotationPtr item = (xmlSchemaNotationPtr) annItem; + ADD_ANNOTATION(annot) + } + break; + case XML_SCHEMA_FACET_MININCLUSIVE: + case XML_SCHEMA_FACET_MINEXCLUSIVE: + case XML_SCHEMA_FACET_MAXINCLUSIVE: + case XML_SCHEMA_FACET_MAXEXCLUSIVE: + case XML_SCHEMA_FACET_TOTALDIGITS: + case XML_SCHEMA_FACET_FRACTIONDIGITS: + case XML_SCHEMA_FACET_PATTERN: + case XML_SCHEMA_FACET_ENUMERATION: + case XML_SCHEMA_FACET_WHITESPACE: + case XML_SCHEMA_FACET_LENGTH: + case XML_SCHEMA_FACET_MAXLENGTH: + case XML_SCHEMA_FACET_MINLENGTH: { + xmlSchemaFacetPtr item = (xmlSchemaFacetPtr) annItem; + ADD_ANNOTATION(annot) + } + break; + case XML_SCHEMA_TYPE_SIMPLE: + case XML_SCHEMA_TYPE_COMPLEX: { + xmlSchemaTypePtr item = (xmlSchemaTypePtr) annItem; + ADD_ANNOTATION(annot) + } + break; + case XML_SCHEMA_TYPE_GROUP: { + xmlSchemaModelGroupDefPtr item = (xmlSchemaModelGroupDefPtr) annItem; + ADD_ANNOTATION(annot) + } + break; + case XML_SCHEMA_TYPE_SEQUENCE: + case XML_SCHEMA_TYPE_CHOICE: + case XML_SCHEMA_TYPE_ALL: { + xmlSchemaModelGroupPtr item = (xmlSchemaModelGroupPtr) annItem; + ADD_ANNOTATION(annot) + } + break; + default: + xmlSchemaPCustomErr(NULL, + XML_SCHEMAP_INTERNAL, + NULL, NULL, + "Internal error: xmlSchemaAddAnnotation, " + "The item is not a annotated schema component", NULL); + break; + } + return (annot); +} + +/** + * xmlSchemaParseIDCSelectorAndField: + * @ctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * + * Parses a XML Schema identity-contraint definition's + * and elements. + * + * Returns the parsed identity-constraint definition. + */ +static xmlSchemaIDCSelectPtr +xmlSchemaParseIDCSelectorAndField(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaIDCPtr idc, + xmlNodePtr node, + int isField) +{ + xmlSchemaIDCSelectPtr item; + xmlNodePtr child = NULL; + xmlAttrPtr attr; + + /* + * Check for illegal attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && + (!xmlStrEqual(attr->name, BAD_CAST "xpath"))) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + /* + * Create the item. + */ + item = (xmlSchemaIDCSelectPtr) xmlMalloc(sizeof(xmlSchemaIDCSelect)); + if (item == NULL) { + xmlSchemaPErrMemory(ctxt, + "allocating a 'selector' of an identity-constraint definition", + NULL); + return (NULL); + } + memset(item, 0, sizeof(xmlSchemaIDCSelect)); + /* + * Attribute "xpath" (mandatory). + */ + attr = xmlSchemaGetPropNode(node, "xpath"); + if (attr == NULL) { + xmlSchemaPMissingAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_MISSING, + NULL, node, + "name", NULL); + } else { + item->xpath = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); + /* + * URGENT TODO: "field"s have an other syntax than "selector"s. + */ + + if (xmlSchemaCheckCSelectorXPath(ctxt, idc, item, attr, + isField) == -1) { + xmlSchemaPErr(ctxt, + (xmlNodePtr) attr, + XML_SCHEMAP_INTERNAL, + "Internal error: xmlSchemaParseIDCSelectorAndField, " + "validating the XPath expression of a IDC selector.\n", + NULL, NULL); + } + + } + xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); + /* + * And now for the children... + */ + child = node->children; + if (IS_SCHEMA(child, "annotation")) { + /* + * Add the annotation to the parent IDC. + */ + xmlSchemaAddAnnotation((xmlSchemaAnnotItemPtr) idc, + xmlSchemaParseAnnotation(ctxt, child, 1)); + child = child->next; + } + if (child != NULL) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, + NULL, "(annotation?)"); + } + + return (item); +} + +/** + * xmlSchemaParseIDC: + * @ctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * + * Parses a XML Schema identity-contraint definition. + * + * Returns the parsed identity-constraint definition. + */ +static xmlSchemaIDCPtr +xmlSchemaParseIDC(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaPtr schema, + xmlNodePtr node, + xmlSchemaTypeType idcCategory, + const xmlChar *targetNamespace) +{ + xmlSchemaIDCPtr item = NULL; + xmlNodePtr child = NULL; + xmlAttrPtr attr; + const xmlChar *name = NULL; + xmlSchemaIDCSelectPtr field = NULL, lastField = NULL; + + /* + * Check for illegal attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && + (!xmlStrEqual(attr->name, BAD_CAST "name")) && + ((idcCategory != XML_SCHEMA_TYPE_IDC_KEYREF) || + (!xmlStrEqual(attr->name, BAD_CAST "refer")))) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + /* + * Attribute "name" (mandatory). + */ + attr = xmlSchemaGetPropNode(node, "name"); + if (attr == NULL) { + xmlSchemaPMissingAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_MISSING, + NULL, node, + "name", NULL); + return (NULL); + } else if (xmlSchemaPValAttrNode(ctxt, + NULL, attr, + xmlSchemaGetBuiltInType(XML_SCHEMAS_NCNAME), &name) != 0) { + return (NULL); + } + /* Create the component. */ + item = xmlSchemaAddIDC(ctxt, schema, name, targetNamespace, + idcCategory, node); + if (item == NULL) + return(NULL); + + xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); + if (idcCategory == XML_SCHEMA_TYPE_IDC_KEYREF) { + /* + * Attribute "refer" (mandatory). + */ + attr = xmlSchemaGetPropNode(node, "refer"); + if (attr == NULL) { + xmlSchemaPMissingAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_MISSING, + NULL, node, + "refer", NULL); + } else { + /* + * Create a reference item. + */ + item->ref = xmlSchemaNewQNameRef(ctxt, XML_SCHEMA_TYPE_IDC_KEY, + NULL, NULL); + if (item->ref == NULL) + return (NULL); + xmlSchemaPValAttrNodeQName(ctxt, schema, + NULL, attr, + &(item->ref->targetNamespace), + &(item->ref->name)); + xmlSchemaCheckReference(ctxt, schema, node, attr, + item->ref->targetNamespace); + } + } + /* + * And now for the children... + */ + child = node->children; + if (IS_SCHEMA(child, "annotation")) { + item->annot = xmlSchemaParseAnnotation(ctxt, child, 1); + child = child->next; + } + if (child == NULL) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_MISSING, + NULL, node, child, + "A child element is missing", + "(annotation?, (selector, field+))"); + } + /* + * Child element . + */ + if (IS_SCHEMA(child, "selector")) { + item->selector = xmlSchemaParseIDCSelectorAndField(ctxt, + item, child, 0); + child = child->next; + /* + * Child elements . + */ + if (IS_SCHEMA(child, "field")) { + do { + field = xmlSchemaParseIDCSelectorAndField(ctxt, + item, child, 1); + if (field != NULL) { + field->index = item->nbFields; + item->nbFields++; + if (lastField != NULL) + lastField->next = field; + else + item->fields = field; + lastField = field; + } + child = child->next; + } while (IS_SCHEMA(child, "field")); + } else { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, + NULL, "(annotation?, (selector, field+))"); + } + } + if (child != NULL) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, + NULL, "(annotation?, (selector, field+))"); + } + + return (item); +} + +/** + * xmlSchemaParseElement: + * @ctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * @topLevel: indicates if this is global declaration + * + * Parses a XML schema element declaration. + * *WARNING* this interface is highly subject to change + * + * Returns the element declaration or a particle; NULL in case + * of an error or if the particle has minOccurs==maxOccurs==0. + */ +static xmlSchemaBasicItemPtr +xmlSchemaParseElement(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, + xmlNodePtr node, int *isElemRef, int topLevel) +{ + xmlSchemaElementPtr decl = NULL; + xmlSchemaParticlePtr particle = NULL; + xmlSchemaAnnotPtr annot = NULL; + xmlNodePtr child = NULL; + xmlAttrPtr attr, nameAttr; + int min, max, isRef = 0; + xmlChar *des = NULL; + + /* 3.3.3 Constraints on XML Representations of Element Declarations */ + /* TODO: Complete implementation of 3.3.6 */ + + if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) + return (NULL); + + if (isElemRef != NULL) + *isElemRef = 0; + /* + * If we get a "ref" attribute on a local we will assume it's + * a reference - even if there's a "name" attribute; this seems to be more + * robust. + */ + nameAttr = xmlSchemaGetPropNode(node, "name"); + attr = xmlSchemaGetPropNode(node, "ref"); + if ((topLevel) || (attr == NULL)) { + if (nameAttr == NULL) { + xmlSchemaPMissingAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_MISSING, + NULL, node, "name", NULL); + return (NULL); + } + } else + isRef = 1; + + xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); + child = node->children; + if (IS_SCHEMA(child, "annotation")) { + annot = xmlSchemaParseAnnotation(ctxt, child, 1); + child = child->next; + } + /* + * Skip particle part if a global declaration. + */ + if (topLevel) + goto declaration_part; + /* + * The particle part ================================================== + */ + min = xmlGetMinOccurs(ctxt, node, 0, -1, 1, "xs:nonNegativeInteger"); + max = xmlGetMaxOccurs(ctxt, node, 0, UNBOUNDED, 1, "(xs:nonNegativeInteger | unbounded)"); + xmlSchemaPCheckParticleCorrect_2(ctxt, NULL, node, min, max); + particle = xmlSchemaAddParticle(ctxt, node, min, max); + if (particle == NULL) + goto return_null; + + /* ret->flags |= XML_SCHEMAS_ELEM_REF; */ + + if (isRef) { + const xmlChar *refNs = NULL, *ref = NULL; + xmlSchemaQNameRefPtr refer = NULL; + /* + * The reference part ============================================= + */ + if (isElemRef != NULL) + *isElemRef = 1; + + xmlSchemaPValAttrNodeQName(ctxt, schema, + NULL, attr, &refNs, &ref); + xmlSchemaCheckReference(ctxt, schema, node, attr, refNs); + /* + * SPEC (3.3.3 : 2.1) "One of ref or name must be present, but not both" + */ + if (nameAttr != NULL) { + xmlSchemaPMutualExclAttrErr(ctxt, + XML_SCHEMAP_SRC_ELEMENT_2_1, NULL, nameAttr, "ref", "name"); + } + /* + * Check for illegal attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if (xmlStrEqual(attr->name, BAD_CAST "ref") || + xmlStrEqual(attr->name, BAD_CAST "name") || + xmlStrEqual(attr->name, BAD_CAST "id") || + xmlStrEqual(attr->name, BAD_CAST "maxOccurs") || + xmlStrEqual(attr->name, BAD_CAST "minOccurs")) + { + attr = attr->next; + continue; + } else { + /* SPEC (3.3.3 : 2.2) */ + xmlSchemaPCustomAttrErr(ctxt, + XML_SCHEMAP_SRC_ELEMENT_2_2, + NULL, NULL, attr, + "Only the attributes 'minOccurs', 'maxOccurs' and " + "'id' are allowed in addition to 'ref'"); + break; + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + /* + * No children except expected. + */ + if (child != NULL) { + xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, NULL, "(annotation?)"); + } + if ((min == 0) && (max == 0)) + goto return_null; + /* + * Create the reference item and attach it to the particle. + */ + refer = xmlSchemaNewQNameRef(ctxt, XML_SCHEMA_TYPE_ELEMENT, + ref, refNs); + if (refer == NULL) + goto return_null; + particle->children = (xmlSchemaTreeItemPtr) refer; + particle->annot = annot; + /* + * Add the particle to pending components, since the reference + * need to be resolved. + */ + WXS_ADD_PENDING(ctxt, particle); + return ((xmlSchemaBasicItemPtr) particle); + } + /* + * The declaration part =============================================== + */ +declaration_part: + { + const xmlChar *ns = NULL, *fixed, *name, *attrValue; + xmlSchemaIDCPtr curIDC = NULL, lastIDC = NULL; + + if (xmlSchemaPValAttrNode(ctxt, NULL, nameAttr, + xmlSchemaGetBuiltInType(XML_SCHEMAS_NCNAME), &name) != 0) + goto return_null; + /* + * Evaluate the target namespace. + */ + if (topLevel) { + ns = ctxt->targetNamespace; + } else { + attr = xmlSchemaGetPropNode(node, "form"); + if (attr != NULL) { + attrValue = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); + if (xmlStrEqual(attrValue, BAD_CAST "qualified")) { + ns = ctxt->targetNamespace; + } else if (!xmlStrEqual(attrValue, BAD_CAST "unqualified")) { + xmlSchemaPSimpleTypeErr(ctxt, + XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, + NULL, (xmlNodePtr) attr, + NULL, "(qualified | unqualified)", + attrValue, NULL, NULL, NULL); + } + } else if (schema->flags & XML_SCHEMAS_QUALIF_ELEM) + ns = ctxt->targetNamespace; + } + decl = xmlSchemaAddElement(ctxt, name, ns, node, topLevel); + if (decl == NULL) { + goto return_null; + } + /* + * Check for illegal attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if ((!xmlStrEqual(attr->name, BAD_CAST "name")) && + (!xmlStrEqual(attr->name, BAD_CAST "type")) && + (!xmlStrEqual(attr->name, BAD_CAST "id")) && + (!xmlStrEqual(attr->name, BAD_CAST "default")) && + (!xmlStrEqual(attr->name, BAD_CAST "fixed")) && + (!xmlStrEqual(attr->name, BAD_CAST "block")) && + (!xmlStrEqual(attr->name, BAD_CAST "nillable"))) + { + if (topLevel == 0) { + if ((!xmlStrEqual(attr->name, BAD_CAST "maxOccurs")) && + (!xmlStrEqual(attr->name, BAD_CAST "minOccurs")) && + (!xmlStrEqual(attr->name, BAD_CAST "form"))) + { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else if ((!xmlStrEqual(attr->name, BAD_CAST "final")) && + (!xmlStrEqual(attr->name, BAD_CAST "abstract")) && + (!xmlStrEqual(attr->name, BAD_CAST "substitutionGroup"))) { + + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + /* + * Extract/validate attributes. + */ + if (topLevel) { + /* + * Process top attributes of global element declarations here. + */ + decl->flags |= XML_SCHEMAS_ELEM_GLOBAL; + decl->flags |= XML_SCHEMAS_ELEM_TOPLEVEL; + xmlSchemaPValAttrQName(ctxt, schema, + NULL, node, "substitutionGroup", + &(decl->substGroupNs), &(decl->substGroup)); + if (xmlGetBooleanProp(ctxt, node, "abstract", 0)) + decl->flags |= XML_SCHEMAS_ELEM_ABSTRACT; + /* + * Attribute "final". + */ + attr = xmlSchemaGetPropNode(node, "final"); + if (attr == NULL) { + if (schema->flags & XML_SCHEMAS_FINAL_DEFAULT_EXTENSION) + decl->flags |= XML_SCHEMAS_ELEM_FINAL_EXTENSION; + if (schema->flags & XML_SCHEMAS_FINAL_DEFAULT_RESTRICTION) + decl->flags |= XML_SCHEMAS_ELEM_FINAL_RESTRICTION; + } else { + attrValue = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); + if (xmlSchemaPValAttrBlockFinal(attrValue, &(decl->flags), + -1, + XML_SCHEMAS_ELEM_FINAL_EXTENSION, + XML_SCHEMAS_ELEM_FINAL_RESTRICTION, -1, -1, -1) != 0) { + xmlSchemaPSimpleTypeErr(ctxt, + XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, + NULL, (xmlNodePtr) attr, + NULL, "(#all | List of (extension | restriction))", + attrValue, NULL, NULL, NULL); + } + } + } + /* + * Attribute "block". + */ + attr = xmlSchemaGetPropNode(node, "block"); + if (attr == NULL) { + /* + * Apply default "block" values. + */ + if (schema->flags & XML_SCHEMAS_BLOCK_DEFAULT_RESTRICTION) + decl->flags |= XML_SCHEMAS_ELEM_BLOCK_RESTRICTION; + if (schema->flags & XML_SCHEMAS_BLOCK_DEFAULT_EXTENSION) + decl->flags |= XML_SCHEMAS_ELEM_BLOCK_EXTENSION; + if (schema->flags & XML_SCHEMAS_BLOCK_DEFAULT_SUBSTITUTION) + decl->flags |= XML_SCHEMAS_ELEM_BLOCK_SUBSTITUTION; + } else { + attrValue = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); + if (xmlSchemaPValAttrBlockFinal(attrValue, &(decl->flags), + -1, + XML_SCHEMAS_ELEM_BLOCK_EXTENSION, + XML_SCHEMAS_ELEM_BLOCK_RESTRICTION, + XML_SCHEMAS_ELEM_BLOCK_SUBSTITUTION, -1, -1) != 0) { + xmlSchemaPSimpleTypeErr(ctxt, + XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, + NULL, (xmlNodePtr) attr, + NULL, "(#all | List of (extension | " + "restriction | substitution))", attrValue, + NULL, NULL, NULL); + } + } + if (xmlGetBooleanProp(ctxt, node, "nillable", 0)) + decl->flags |= XML_SCHEMAS_ELEM_NILLABLE; + + attr = xmlSchemaGetPropNode(node, "type"); + if (attr != NULL) { + xmlSchemaPValAttrNodeQName(ctxt, schema, + NULL, attr, + &(decl->namedTypeNs), &(decl->namedType)); + xmlSchemaCheckReference(ctxt, schema, node, + attr, decl->namedTypeNs); + } + decl->value = xmlSchemaGetProp(ctxt, node, "default"); + attr = xmlSchemaGetPropNode(node, "fixed"); + if (attr != NULL) { + fixed = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); + if (decl->value != NULL) { + /* + * 3.3.3 : 1 + * default and fixed must not both be present. + */ + xmlSchemaPMutualExclAttrErr(ctxt, + XML_SCHEMAP_SRC_ELEMENT_1, + NULL, attr, "default", "fixed"); + } else { + decl->flags |= XML_SCHEMAS_ELEM_FIXED; + decl->value = fixed; + } + } + /* + * And now for the children... + */ + if (IS_SCHEMA(child, "complexType")) { + /* + * 3.3.3 : 3 + * "type" and either or are mutually + * exclusive + */ + if (decl->namedType != NULL) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_SRC_ELEMENT_3, + NULL, node, child, + "The attribute 'type' and the child are " + "mutually exclusive", NULL); + } else + WXS_ELEM_TYPEDEF(decl) = xmlSchemaParseComplexType(ctxt, schema, child, 0); + child = child->next; + } else if (IS_SCHEMA(child, "simpleType")) { + /* + * 3.3.3 : 3 + * "type" and either or are + * mutually exclusive + */ + if (decl->namedType != NULL) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_SRC_ELEMENT_3, + NULL, node, child, + "The attribute 'type' and the child are " + "mutually exclusive", NULL); + } else + WXS_ELEM_TYPEDEF(decl) = xmlSchemaParseSimpleType(ctxt, schema, child, 0); + child = child->next; + } + while ((IS_SCHEMA(child, "unique")) || + (IS_SCHEMA(child, "key")) || (IS_SCHEMA(child, "keyref"))) { + if (IS_SCHEMA(child, "unique")) { + curIDC = xmlSchemaParseIDC(ctxt, schema, child, + XML_SCHEMA_TYPE_IDC_UNIQUE, decl->targetNamespace); + } else if (IS_SCHEMA(child, "key")) { + curIDC = xmlSchemaParseIDC(ctxt, schema, child, + XML_SCHEMA_TYPE_IDC_KEY, decl->targetNamespace); + } else if (IS_SCHEMA(child, "keyref")) { + curIDC = xmlSchemaParseIDC(ctxt, schema, child, + XML_SCHEMA_TYPE_IDC_KEYREF, decl->targetNamespace); + } + if (lastIDC != NULL) + lastIDC->next = curIDC; + else + decl->idcs = (void *) curIDC; + lastIDC = curIDC; + child = child->next; + } + if (child != NULL) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, + NULL, "(annotation?, ((simpleType | complexType)?, " + "(unique | key | keyref)*))"); + } + decl->annot = annot; + } + /* + * NOTE: Element Declaration Representation OK 4. will be checked at a + * different layer. + */ + FREE_AND_NULL(des) + if (topLevel) + return ((xmlSchemaBasicItemPtr) decl); + else { + particle->children = (xmlSchemaTreeItemPtr) decl; + return ((xmlSchemaBasicItemPtr) particle); + } + +return_null: + FREE_AND_NULL(des); + if (annot != NULL) { + if (particle != NULL) + particle->annot = NULL; + if (decl != NULL) + decl->annot = NULL; + xmlSchemaFreeAnnot(annot); + } + return (NULL); +} + +/** + * xmlSchemaParseUnion: + * @ctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * + * parse a XML schema Union definition + * *WARNING* this interface is highly subject to change + * + * Returns -1 in case of internal error, 0 in case of success and a positive + * error code otherwise. + */ +static int +xmlSchemaParseUnion(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, + xmlNodePtr node) +{ + xmlSchemaTypePtr type; + xmlNodePtr child = NULL; + xmlAttrPtr attr; + const xmlChar *cur = NULL; + + if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) + return (-1); + /* Not a component, don't create it. */ + type = ctxt->ctxtType; + /* + * Mark the simple type as being of variety "union". + */ + type->flags |= XML_SCHEMAS_TYPE_VARIETY_UNION; + /* + * SPEC (Base type) (2) "If the or alternative is chosen, + * then the ·simple ur-type definition·." + */ + type->baseType = xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYSIMPLETYPE); + /* + * Check for illegal attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && + (!xmlStrEqual(attr->name, BAD_CAST "memberTypes"))) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); + /* + * Attribute "memberTypes". This is a list of QNames. + * TODO: Check the value to contain anything. + */ + attr = xmlSchemaGetPropNode(node, "memberTypes"); + if (attr != NULL) { + const xmlChar *end; + xmlChar *tmp; + const xmlChar *localName, *nsName; + xmlSchemaTypeLinkPtr link, lastLink = NULL; + xmlSchemaQNameRefPtr ref; + + cur = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); + type->base = cur; + do { + while (IS_BLANK_CH(*cur)) + cur++; + end = cur; + while ((*end != 0) && (!(IS_BLANK_CH(*end)))) + end++; + if (end == cur) + break; + tmp = xmlStrndup(cur, end - cur); + if (xmlSchemaPValAttrNodeQNameValue(ctxt, schema, + NULL, attr, BAD_CAST tmp, &nsName, &localName) == 0) { + /* + * Create the member type link. + */ + link = (xmlSchemaTypeLinkPtr) + xmlMalloc(sizeof(xmlSchemaTypeLink)); + if (link == NULL) { + xmlSchemaPErrMemory(ctxt, "xmlSchemaParseUnion, " + "allocating a type link", NULL); + return (-1); + } + link->type = NULL; + link->next = NULL; + if (lastLink == NULL) + type->memberTypes = link; + else + lastLink->next = link; + lastLink = link; + /* + * Create a reference item. + */ + ref = xmlSchemaNewQNameRef(ctxt, XML_SCHEMA_TYPE_SIMPLE, + localName, nsName); + if (ref == NULL) { + FREE_AND_NULL(tmp) + return (-1); + } + /* + * Assign the reference to the link, it will be resolved + * later during fixup of the union simple type. + */ + link->type = (xmlSchemaTypePtr) ref; + } + FREE_AND_NULL(tmp) + cur = end; + } while (*cur != 0); + + } + /* + * And now for the children... + */ + child = node->children; + if (IS_SCHEMA(child, "annotation")) { + /* + * Add the annotation to the simple type ancestor. + */ + xmlSchemaAddAnnotation((xmlSchemaAnnotItemPtr) type, + xmlSchemaParseAnnotation(ctxt, child, 1)); + child = child->next; + } + if (IS_SCHEMA(child, "simpleType")) { + xmlSchemaTypePtr subtype, last = NULL; + + /* + * Anchor the member types in the "subtypes" field of the + * simple type. + */ + while (IS_SCHEMA(child, "simpleType")) { + subtype = (xmlSchemaTypePtr) + xmlSchemaParseSimpleType(ctxt, schema, child, 0); + if (subtype != NULL) { + if (last == NULL) { + type->subtypes = subtype; + last = subtype; + } else { + last->next = subtype; + last = subtype; + } + last->next = NULL; + } + child = child->next; + } + } + if (child != NULL) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, NULL, "(annotation?, simpleType*)"); + } + if ((attr == NULL) && (type->subtypes == NULL)) { + /* + * src-union-memberTypes-or-simpleTypes + * Either the memberTypes [attribute] of the element must + * be non-empty or there must be at least one simpleType [child]. + */ + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_SRC_UNION_MEMBERTYPES_OR_SIMPLETYPES, + NULL, node, + "Either the attribute 'memberTypes' or " + "at least one child must be present", NULL); + } + return (0); +} + +/** + * xmlSchemaParseList: + * @ctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * + * parse a XML schema List definition + * *WARNING* this interface is highly subject to change + * + * Returns -1 in case of error, 0 if the declaration is improper and + * 1 in case of success. + */ +static xmlSchemaTypePtr +xmlSchemaParseList(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, + xmlNodePtr node) +{ + xmlSchemaTypePtr type; + xmlNodePtr child = NULL; + xmlAttrPtr attr; + + if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) + return (NULL); + /* Not a component, don't create it. */ + type = ctxt->ctxtType; + /* + * Mark the type as being of variety "list". + */ + type->flags |= XML_SCHEMAS_TYPE_VARIETY_LIST; + /* + * SPEC (Base type) (2) "If the or alternative is chosen, + * then the ·simple ur-type definition·." + */ + type->baseType = xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYSIMPLETYPE); + /* + * Check for illegal attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && + (!xmlStrEqual(attr->name, BAD_CAST "itemType"))) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + + xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); + + /* + * Attribute "itemType". NOTE that we will use the "ref" and "refNs" + * fields for holding the reference to the itemType. + * + * REVAMP TODO: Use the "base" and "baseNs" fields, since we will remove + * the "ref" fields. + */ + xmlSchemaPValAttrQName(ctxt, schema, NULL, + node, "itemType", &(type->baseNs), &(type->base)); + /* + * And now for the children... + */ + child = node->children; + if (IS_SCHEMA(child, "annotation")) { + xmlSchemaAddAnnotation((xmlSchemaAnnotItemPtr) type, + xmlSchemaParseAnnotation(ctxt, child, 1)); + child = child->next; + } + if (IS_SCHEMA(child, "simpleType")) { + /* + * src-list-itemType-or-simpleType + * Either the itemType [attribute] or the [child] of + * the element must be present, but not both. + */ + if (type->base != NULL) { + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_SRC_SIMPLE_TYPE_1, + NULL, node, + "The attribute 'itemType' and the child " + "are mutually exclusive", NULL); + } else { + type->subtypes = xmlSchemaParseSimpleType(ctxt, schema, child, 0); + } + child = child->next; + } else if (type->base == NULL) { + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_SRC_SIMPLE_TYPE_1, + NULL, node, + "Either the attribute 'itemType' or the child " + "must be present", NULL); + } + if (child != NULL) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, NULL, "(annotation?, simpleType?)"); + } + if ((type->base == NULL) && + (type->subtypes == NULL) && + (xmlSchemaGetPropNode(node, "itemType") == NULL)) { + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_SRC_SIMPLE_TYPE_1, + NULL, node, + "Either the attribute 'itemType' or the child " + "must be present", NULL); + } + return (NULL); +} + +/** + * xmlSchemaParseSimpleType: + * @ctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * + * parse a XML schema Simple Type definition + * *WARNING* this interface is highly subject to change + * + * Returns -1 in case of error, 0 if the declaration is improper and + * 1 in case of success. + */ +static xmlSchemaTypePtr +xmlSchemaParseSimpleType(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, + xmlNodePtr node, int topLevel) +{ + xmlSchemaTypePtr type, oldCtxtType; + xmlNodePtr child = NULL; + const xmlChar *attrValue = NULL; + xmlAttrPtr attr; + int hasRestriction = 0; + + if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) + return (NULL); + + if (topLevel) { + attr = xmlSchemaGetPropNode(node, "name"); + if (attr == NULL) { + xmlSchemaPMissingAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_MISSING, + NULL, node, + "name", NULL); + return (NULL); + } else { + if (xmlSchemaPValAttrNode(ctxt, + NULL, attr, + xmlSchemaGetBuiltInType(XML_SCHEMAS_NCNAME), &attrValue) != 0) + return (NULL); + /* + * Skip built-in types. + */ + if (ctxt->isS4S) { + xmlSchemaTypePtr biType; + + if (ctxt->isRedefine) { + /* + * REDEFINE: Disallow redefinition of built-in-types. + * TODO: It seems that the spec does not say anything + * about this case. + */ + xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_SRC_REDEFINE, + NULL, node, + "Redefinition of built-in simple types is not " + "supported", NULL); + return(NULL); + } + biType = xmlSchemaGetPredefinedType(attrValue, xmlSchemaNs); + if (biType != NULL) + return (biType); + } + } + } + /* + * TargetNamespace: + * SPEC "The ·actual value· of the targetNamespace [attribute] + * of the ancestor element information item if present, + * otherwise ·absent·. + */ + if (topLevel == 0) { +#ifdef ENABLE_NAMED_LOCALS + char buf[40]; +#endif + /* + * Parse as local simple type definition. + */ +#ifdef ENABLE_NAMED_LOCALS + snprintf(buf, 39, "#ST%d", ctxt->counter++ + 1); + type = xmlSchemaAddType(ctxt, schema, + XML_SCHEMA_TYPE_SIMPLE, + xmlDictLookup(ctxt->dict, (const xmlChar *)buf, -1), + ctxt->targetNamespace, node, 0); +#else + type = xmlSchemaAddType(ctxt, schema, + XML_SCHEMA_TYPE_SIMPLE, + NULL, ctxt->targetNamespace, node, 0); +#endif + if (type == NULL) + return (NULL); + type->type = XML_SCHEMA_TYPE_SIMPLE; + type->contentType = XML_SCHEMA_CONTENT_SIMPLE; + /* + * Check for illegal attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if (!xmlStrEqual(attr->name, BAD_CAST "id")) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + } else { + /* + * Parse as global simple type definition. + * + * Note that attrValue is the value of the attribute "name" here. + */ + type = xmlSchemaAddType(ctxt, schema, XML_SCHEMA_TYPE_SIMPLE, + attrValue, ctxt->targetNamespace, node, 1); + if (type == NULL) + return (NULL); + type->type = XML_SCHEMA_TYPE_SIMPLE; + type->contentType = XML_SCHEMA_CONTENT_SIMPLE; + type->flags |= XML_SCHEMAS_TYPE_GLOBAL; + /* + * Check for illegal attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && + (!xmlStrEqual(attr->name, BAD_CAST "name")) && + (!xmlStrEqual(attr->name, BAD_CAST "final"))) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + /* + * Attribute "final". + */ + attr = xmlSchemaGetPropNode(node, "final"); + if (attr == NULL) { + if (schema->flags & XML_SCHEMAS_FINAL_DEFAULT_RESTRICTION) + type->flags |= XML_SCHEMAS_TYPE_FINAL_RESTRICTION; + if (schema->flags & XML_SCHEMAS_FINAL_DEFAULT_LIST) + type->flags |= XML_SCHEMAS_TYPE_FINAL_LIST; + if (schema->flags & XML_SCHEMAS_FINAL_DEFAULT_UNION) + type->flags |= XML_SCHEMAS_TYPE_FINAL_UNION; + } else { + attrValue = xmlSchemaGetProp(ctxt, node, "final"); + if (xmlSchemaPValAttrBlockFinal(attrValue, &(type->flags), + -1, -1, XML_SCHEMAS_TYPE_FINAL_RESTRICTION, -1, + XML_SCHEMAS_TYPE_FINAL_LIST, + XML_SCHEMAS_TYPE_FINAL_UNION) != 0) { + + xmlSchemaPSimpleTypeErr(ctxt, + XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, + WXS_BASIC_CAST type, (xmlNodePtr) attr, + NULL, "(#all | List of (list | union | restriction)", + attrValue, NULL, NULL, NULL); + } + } + } + type->targetNamespace = ctxt->targetNamespace; + xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); + /* + * And now for the children... + */ + oldCtxtType = ctxt->ctxtType; + + ctxt->ctxtType = type; + + child = node->children; + if (IS_SCHEMA(child, "annotation")) { + type->annot = xmlSchemaParseAnnotation(ctxt, child, 1); + child = child->next; + } + if (child == NULL) { + xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_MISSING, + NULL, node, child, NULL, + "(annotation?, (restriction | list | union))"); + } else if (IS_SCHEMA(child, "restriction")) { + xmlSchemaParseRestriction(ctxt, schema, child, + XML_SCHEMA_TYPE_SIMPLE); + hasRestriction = 1; + child = child->next; + } else if (IS_SCHEMA(child, "list")) { + xmlSchemaParseList(ctxt, schema, child); + child = child->next; + } else if (IS_SCHEMA(child, "union")) { + xmlSchemaParseUnion(ctxt, schema, child); + child = child->next; + } + if (child != NULL) { + xmlSchemaPContentErr(ctxt, XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, NULL, + "(annotation?, (restriction | list | union))"); + } + /* + * REDEFINE: SPEC src-redefine (5) + * "Within the [children], each must have a + * among its [children] ... the ·actual value· of whose + * base [attribute] must be the same as the ·actual value· of its own + * name attribute plus target namespace;" + */ + if (topLevel && ctxt->isRedefine && (! hasRestriction)) { + xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_SRC_REDEFINE, + NULL, node, "This is a redefinition, thus the " + " must have a child", NULL); + } + + ctxt->ctxtType = oldCtxtType; + return (type); +} + +/** + * xmlSchemaParseModelGroupDefRef: + * @ctxt: the parser context + * @schema: the schema being built + * @node: the node + * + * Parses a reference to a model group definition. + * + * We will return a particle component with a qname-component or + * NULL in case of an error. + */ +static xmlSchemaTreeItemPtr +xmlSchemaParseModelGroupDefRef(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaPtr schema, + xmlNodePtr node) +{ + xmlSchemaParticlePtr item; + xmlNodePtr child = NULL; + xmlAttrPtr attr; + const xmlChar *ref = NULL, *refNs = NULL; + int min, max; + + if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) + return (NULL); + + attr = xmlSchemaGetPropNode(node, "ref"); + if (attr == NULL) { + xmlSchemaPMissingAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_MISSING, + NULL, node, "ref", NULL); + return (NULL); + } else if (xmlSchemaPValAttrNodeQName(ctxt, schema, NULL, + attr, &refNs, &ref) != 0) { + return (NULL); + } + xmlSchemaCheckReference(ctxt, schema, node, attr, refNs); + min = xmlGetMinOccurs(ctxt, node, 0, -1, 1, "xs:nonNegativeInteger"); + max = xmlGetMaxOccurs(ctxt, node, 0, UNBOUNDED, 1, + "(xs:nonNegativeInteger | unbounded)"); + /* + * Check for illegal attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if ((!xmlStrEqual(attr->name, BAD_CAST "ref")) && + (!xmlStrEqual(attr->name, BAD_CAST "id")) && + (!xmlStrEqual(attr->name, BAD_CAST "minOccurs")) && + (!xmlStrEqual(attr->name, BAD_CAST "maxOccurs"))) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); + item = xmlSchemaAddParticle(ctxt, node, min, max); + if (item == NULL) + return (NULL); + /* + * Create a qname-reference and set as the term; it will be substituted + * for the model group after the reference has been resolved. + */ + item->children = (xmlSchemaTreeItemPtr) + xmlSchemaNewQNameRef(ctxt, XML_SCHEMA_TYPE_GROUP, ref, refNs); + xmlSchemaPCheckParticleCorrect_2(ctxt, item, node, min, max); + /* + * And now for the children... + */ + child = node->children; + /* TODO: Is annotation even allowed for a model group reference? */ + if (IS_SCHEMA(child, "annotation")) { + /* + * TODO: What to do exactly with the annotation? + */ + item->annot = xmlSchemaParseAnnotation(ctxt, child, 1); + child = child->next; + } + if (child != NULL) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, NULL, + "(annotation?)"); + } + /* + * Corresponds to no component at all if minOccurs==maxOccurs==0. + */ + if ((min == 0) && (max == 0)) + return (NULL); + + return ((xmlSchemaTreeItemPtr) item); +} + +/** + * xmlSchemaParseModelGroupDefinition: + * @ctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * + * Parses a XML schema model group definition. + * + * Note that the contraint src-redefine (6.2) can't be applied until + * references have been resolved. So we will do this at the + * component fixup level. + * + * *WARNING* this interface is highly subject to change + * + * Returns -1 in case of error, 0 if the declaration is improper and + * 1 in case of success. + */ +static xmlSchemaModelGroupDefPtr +xmlSchemaParseModelGroupDefinition(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaPtr schema, + xmlNodePtr node) +{ + xmlSchemaModelGroupDefPtr item; + xmlNodePtr child = NULL; + xmlAttrPtr attr; + const xmlChar *name; + + if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) + return (NULL); + + attr = xmlSchemaGetPropNode(node, "name"); + if (attr == NULL) { + xmlSchemaPMissingAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_MISSING, + NULL, node, + "name", NULL); + return (NULL); + } else if (xmlSchemaPValAttrNode(ctxt, NULL, attr, + xmlSchemaGetBuiltInType(XML_SCHEMAS_NCNAME), &name) != 0) { + return (NULL); + } + item = xmlSchemaAddModelGroupDefinition(ctxt, schema, name, + ctxt->targetNamespace, node); + if (item == NULL) + return (NULL); + /* + * Check for illegal attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if ((!xmlStrEqual(attr->name, BAD_CAST "name")) && + (!xmlStrEqual(attr->name, BAD_CAST "id"))) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); + /* + * And now for the children... + */ + child = node->children; + if (IS_SCHEMA(child, "annotation")) { + item->annot = xmlSchemaParseAnnotation(ctxt, child, 1); + child = child->next; + } + if (IS_SCHEMA(child, "all")) { + item->children = xmlSchemaParseModelGroup(ctxt, schema, child, + XML_SCHEMA_TYPE_ALL, 0); + child = child->next; + } else if (IS_SCHEMA(child, "choice")) { + item->children = xmlSchemaParseModelGroup(ctxt, schema, child, + XML_SCHEMA_TYPE_CHOICE, 0); + child = child->next; + } else if (IS_SCHEMA(child, "sequence")) { + item->children = xmlSchemaParseModelGroup(ctxt, schema, child, + XML_SCHEMA_TYPE_SEQUENCE, 0); + child = child->next; + } + + + + if (child != NULL) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, NULL, + "(annotation?, (all | choice | sequence)?)"); + } + return (item); +} + +/** + * xmlSchemaCleanupDoc: + * @ctxt: a schema validation context + * @node: the root of the document. + * + * removes unwanted nodes in a schemas document tree + */ +static void +xmlSchemaCleanupDoc(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr root) +{ + xmlNodePtr delete, cur; + + if ((ctxt == NULL) || (root == NULL)) return; + + /* + * Remove all the blank text nodes + */ + delete = NULL; + cur = root; + while (cur != NULL) { + if (delete != NULL) { + xmlUnlinkNode(delete); + xmlFreeNode(delete); + delete = NULL; + } + if (cur->type == XML_TEXT_NODE) { + if (IS_BLANK_NODE(cur)) { + if (xmlNodeGetSpacePreserve(cur) != 1) { + delete = cur; + } + } + } else if ((cur->type != XML_ELEMENT_NODE) && + (cur->type != XML_CDATA_SECTION_NODE)) { + delete = cur; + goto skip_children; + } + + /* + * Skip to next node + */ + if (cur->children != NULL) { + if ((cur->children->type != XML_ENTITY_DECL) && + (cur->children->type != XML_ENTITY_REF_NODE) && + (cur->children->type != XML_ENTITY_NODE)) { + cur = cur->children; + continue; + } + } + skip_children: + if (cur->next != NULL) { + cur = cur->next; + continue; + } + + do { + cur = cur->parent; + if (cur == NULL) + break; + if (cur == root) { + cur = NULL; + break; + } + if (cur->next != NULL) { + cur = cur->next; + break; + } + } while (cur != NULL); + } + if (delete != NULL) { + xmlUnlinkNode(delete); + xmlFreeNode(delete); + delete = NULL; + } +} + + +static void +xmlSchemaClearSchemaDefaults(xmlSchemaPtr schema) +{ + if (schema->flags & XML_SCHEMAS_QUALIF_ELEM) + schema->flags ^= XML_SCHEMAS_QUALIF_ELEM; + + if (schema->flags & XML_SCHEMAS_QUALIF_ATTR) + schema->flags ^= XML_SCHEMAS_QUALIF_ATTR; + + if (schema->flags & XML_SCHEMAS_FINAL_DEFAULT_EXTENSION) + schema->flags ^= XML_SCHEMAS_FINAL_DEFAULT_EXTENSION; + if (schema->flags & XML_SCHEMAS_FINAL_DEFAULT_RESTRICTION) + schema->flags ^= XML_SCHEMAS_FINAL_DEFAULT_RESTRICTION; + if (schema->flags & XML_SCHEMAS_FINAL_DEFAULT_LIST) + schema->flags ^= XML_SCHEMAS_FINAL_DEFAULT_LIST; + if (schema->flags & XML_SCHEMAS_FINAL_DEFAULT_UNION) + schema->flags ^= XML_SCHEMAS_FINAL_DEFAULT_UNION; + + if (schema->flags & XML_SCHEMAS_BLOCK_DEFAULT_EXTENSION) + schema->flags ^= XML_SCHEMAS_BLOCK_DEFAULT_EXTENSION; + if (schema->flags & XML_SCHEMAS_BLOCK_DEFAULT_RESTRICTION) + schema->flags ^= XML_SCHEMAS_BLOCK_DEFAULT_RESTRICTION; + if (schema->flags & XML_SCHEMAS_BLOCK_DEFAULT_SUBSTITUTION) + schema->flags ^= XML_SCHEMAS_BLOCK_DEFAULT_SUBSTITUTION; +} + +static int +xmlSchemaParseSchemaElement(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaPtr schema, + xmlNodePtr node) +{ + xmlAttrPtr attr; + const xmlChar *val; + int res = 0, oldErrs = ctxt->nberrors; + + /* + * Those flags should be moved to the parser context flags, + * since they are not visible at the component level. I.e. + * they are used if processing schema *documents* only. + */ + res = xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); + HFAILURE; + + /* + * Since the version is of type xs:token, we won't bother to + * check it. + */ + /* REMOVED: + attr = xmlSchemaGetPropNode(node, "version"); + if (attr != NULL) { + res = xmlSchemaPValAttrNode(ctxt, NULL, NULL, attr, + xmlSchemaGetBuiltInType(XML_SCHEMAS_TOKEN), &val); + HFAILURE; + } + */ + attr = xmlSchemaGetPropNode(node, "targetNamespace"); + if (attr != NULL) { + res = xmlSchemaPValAttrNode(ctxt, NULL, attr, + xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYURI), NULL); + HFAILURE; + if (res != 0) { + ctxt->stop = XML_SCHEMAP_S4S_ATTR_INVALID_VALUE; + goto exit; + } + } + attr = xmlSchemaGetPropNode(node, "elementFormDefault"); + if (attr != NULL) { + val = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); + res = xmlSchemaPValAttrFormDefault(val, &schema->flags, + XML_SCHEMAS_QUALIF_ELEM); + HFAILURE; + if (res != 0) { + xmlSchemaPSimpleTypeErr(ctxt, + XML_SCHEMAP_ELEMFORMDEFAULT_VALUE, + NULL, (xmlNodePtr) attr, NULL, + "(qualified | unqualified)", val, NULL, NULL, NULL); + } + } + attr = xmlSchemaGetPropNode(node, "attributeFormDefault"); + if (attr != NULL) { + val = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); + res = xmlSchemaPValAttrFormDefault(val, &schema->flags, + XML_SCHEMAS_QUALIF_ATTR); + HFAILURE; + if (res != 0) { + xmlSchemaPSimpleTypeErr(ctxt, + XML_SCHEMAP_ATTRFORMDEFAULT_VALUE, + NULL, (xmlNodePtr) attr, NULL, + "(qualified | unqualified)", val, NULL, NULL, NULL); + } + } + attr = xmlSchemaGetPropNode(node, "finalDefault"); + if (attr != NULL) { + val = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); + res = xmlSchemaPValAttrBlockFinal(val, &(schema->flags), -1, + XML_SCHEMAS_FINAL_DEFAULT_EXTENSION, + XML_SCHEMAS_FINAL_DEFAULT_RESTRICTION, + -1, + XML_SCHEMAS_FINAL_DEFAULT_LIST, + XML_SCHEMAS_FINAL_DEFAULT_UNION); + HFAILURE; + if (res != 0) { + xmlSchemaPSimpleTypeErr(ctxt, + XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, + NULL, (xmlNodePtr) attr, NULL, + "(#all | List of (extension | restriction | list | union))", + val, NULL, NULL, NULL); + } + } + attr = xmlSchemaGetPropNode(node, "blockDefault"); + if (attr != NULL) { + val = xmlSchemaGetNodeContent(ctxt, (xmlNodePtr) attr); + res = xmlSchemaPValAttrBlockFinal(val, &(schema->flags), -1, + XML_SCHEMAS_BLOCK_DEFAULT_EXTENSION, + XML_SCHEMAS_BLOCK_DEFAULT_RESTRICTION, + XML_SCHEMAS_BLOCK_DEFAULT_SUBSTITUTION, -1, -1); + HFAILURE; + if (res != 0) { + xmlSchemaPSimpleTypeErr(ctxt, + XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, + NULL, (xmlNodePtr) attr, NULL, + "(#all | List of (extension | restriction | substitution))", + val, NULL, NULL, NULL); + } + } + +exit: + if (oldErrs != ctxt->nberrors) + res = ctxt->err; + return(res); +exit_failure: + return(-1); +} + +/** + * xmlSchemaParseSchemaTopLevel: + * @ctxt: a schema validation context + * @schema: the schemas + * @nodes: the list of top level nodes + * + * Returns the internal XML Schema structure built from the resource or + * NULL in case of error + */ +static int +xmlSchemaParseSchemaTopLevel(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaPtr schema, xmlNodePtr nodes) +{ + xmlNodePtr child; + xmlSchemaAnnotPtr annot; + int res = 0, oldErrs, tmpOldErrs; + + if ((ctxt == NULL) || (schema == NULL) || (nodes == NULL)) + return(-1); + + oldErrs = ctxt->nberrors; + child = nodes; + while ((IS_SCHEMA(child, "include")) || + (IS_SCHEMA(child, "import")) || + (IS_SCHEMA(child, "redefine")) || + (IS_SCHEMA(child, "annotation"))) { + if (IS_SCHEMA(child, "annotation")) { + annot = xmlSchemaParseAnnotation(ctxt, child, 1); + if (schema->annot == NULL) + schema->annot = annot; + else + xmlSchemaFreeAnnot(annot); + } else if (IS_SCHEMA(child, "import")) { + tmpOldErrs = ctxt->nberrors; + res = xmlSchemaParseImport(ctxt, schema, child); + HFAILURE; + HSTOP(ctxt); + if (tmpOldErrs != ctxt->nberrors) + goto exit; + } else if (IS_SCHEMA(child, "include")) { + tmpOldErrs = ctxt->nberrors; + res = xmlSchemaParseInclude(ctxt, schema, child); + HFAILURE; + HSTOP(ctxt); + if (tmpOldErrs != ctxt->nberrors) + goto exit; + } else if (IS_SCHEMA(child, "redefine")) { + tmpOldErrs = ctxt->nberrors; + res = xmlSchemaParseRedefine(ctxt, schema, child); + HFAILURE; + HSTOP(ctxt); + if (tmpOldErrs != ctxt->nberrors) + goto exit; + } + child = child->next; + } + /* + * URGENT TODO: Change the functions to return int results. + * We need especially to catch internal errors. + */ + while (child != NULL) { + if (IS_SCHEMA(child, "complexType")) { + xmlSchemaParseComplexType(ctxt, schema, child, 1); + child = child->next; + } else if (IS_SCHEMA(child, "simpleType")) { + xmlSchemaParseSimpleType(ctxt, schema, child, 1); + child = child->next; + } else if (IS_SCHEMA(child, "element")) { + xmlSchemaParseElement(ctxt, schema, child, NULL, 1); + child = child->next; + } else if (IS_SCHEMA(child, "attribute")) { + xmlSchemaParseGlobalAttribute(ctxt, schema, child); + child = child->next; + } else if (IS_SCHEMA(child, "attributeGroup")) { + xmlSchemaParseAttributeGroupDefinition(ctxt, schema, child); + child = child->next; + } else if (IS_SCHEMA(child, "group")) { + xmlSchemaParseModelGroupDefinition(ctxt, schema, child); + child = child->next; + } else if (IS_SCHEMA(child, "notation")) { + xmlSchemaParseNotation(ctxt, schema, child); + child = child->next; + } else { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, child->parent, child, + NULL, "((include | import | redefine | annotation)*, " + "(((simpleType | complexType | group | attributeGroup) " + "| element | attribute | notation), annotation*)*)"); + child = child->next; + } + while (IS_SCHEMA(child, "annotation")) { + /* + * TODO: We should add all annotations. + */ + annot = xmlSchemaParseAnnotation(ctxt, child, 1); + if (schema->annot == NULL) + schema->annot = annot; + else + xmlSchemaFreeAnnot(annot); + child = child->next; + } + } +exit: + ctxt->ctxtType = NULL; + if (oldErrs != ctxt->nberrors) + res = ctxt->err; + return(res); +exit_failure: + return(-1); +} + +static xmlSchemaSchemaRelationPtr +xmlSchemaSchemaRelationCreate(void) +{ + xmlSchemaSchemaRelationPtr ret; + + ret = (xmlSchemaSchemaRelationPtr) + xmlMalloc(sizeof(xmlSchemaSchemaRelation)); + if (ret == NULL) { + xmlSchemaPErrMemory(NULL, "allocating schema relation", NULL); + return(NULL); + } + memset(ret, 0, sizeof(xmlSchemaSchemaRelation)); + return(ret); +} + +#if 0 +static void +xmlSchemaSchemaRelationFree(xmlSchemaSchemaRelationPtr rel) +{ + xmlFree(rel); +} +#endif + +static void +xmlSchemaRedefListFree(xmlSchemaRedefPtr redef) +{ + xmlSchemaRedefPtr prev; + + while (redef != NULL) { + prev = redef; + redef = redef->next; + xmlFree(prev); + } +} + +static void +xmlSchemaConstructionCtxtFree(xmlSchemaConstructionCtxtPtr con) +{ + /* + * After the construction context has been freed, there will be + * no schema graph available any more. Only the schema buckets + * will stay alive, which are put into the "schemasImports" and + * "includes" slots of the xmlSchema. + */ + if (con->buckets != NULL) + xmlSchemaItemListFree(con->buckets); + if (con->pending != NULL) + xmlSchemaItemListFree(con->pending); + if (con->substGroups != NULL) + xmlHashFree(con->substGroups, + (xmlHashDeallocator) xmlSchemaSubstGroupFree); + if (con->redefs != NULL) + xmlSchemaRedefListFree(con->redefs); + if (con->dict != NULL) + xmlDictFree(con->dict); + xmlFree(con); +} + +static xmlSchemaConstructionCtxtPtr +xmlSchemaConstructionCtxtCreate(xmlDictPtr dict) +{ + xmlSchemaConstructionCtxtPtr ret; + + ret = (xmlSchemaConstructionCtxtPtr) + xmlMalloc(sizeof(xmlSchemaConstructionCtxt)); + if (ret == NULL) { + xmlSchemaPErrMemory(NULL, + "allocating schema construction context", NULL); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchemaConstructionCtxt)); + + ret->buckets = xmlSchemaItemListCreate(); + if (ret->buckets == NULL) { + xmlSchemaPErrMemory(NULL, + "allocating list of schema buckets", NULL); + xmlFree(ret); + return (NULL); + } + ret->pending = xmlSchemaItemListCreate(); + if (ret->pending == NULL) { + xmlSchemaPErrMemory(NULL, + "allocating list of pending global components", NULL); + xmlSchemaConstructionCtxtFree(ret); + return (NULL); + } + ret->dict = dict; + xmlDictReference(dict); + return(ret); +} + +static xmlSchemaParserCtxtPtr +xmlSchemaParserCtxtCreate(void) +{ + xmlSchemaParserCtxtPtr ret; + + ret = (xmlSchemaParserCtxtPtr) xmlMalloc(sizeof(xmlSchemaParserCtxt)); + if (ret == NULL) { + xmlSchemaPErrMemory(NULL, "allocating schema parser context", + NULL); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchemaParserCtxt)); + ret->type = XML_SCHEMA_CTXT_PARSER; + ret->attrProhibs = xmlSchemaItemListCreate(); + if (ret->attrProhibs == NULL) { + xmlFree(ret); + return(NULL); + } + return(ret); +} + +/** + * xmlSchemaNewParserCtxtUseDict: + * @URL: the location of the schema + * @dict: the dictionary to be used + * + * Create an XML Schemas parse context for that file/resource expected + * to contain an XML Schemas file. + * + * Returns the parser context or NULL in case of error + */ +static xmlSchemaParserCtxtPtr +xmlSchemaNewParserCtxtUseDict(const char *URL, xmlDictPtr dict) +{ + xmlSchemaParserCtxtPtr ret; + + ret = xmlSchemaParserCtxtCreate(); + if (ret == NULL) + return (NULL); + ret->dict = dict; + xmlDictReference(dict); + if (URL != NULL) + ret->URL = xmlDictLookup(dict, (const xmlChar *) URL, -1); + return (ret); +} + +static int +xmlSchemaCreatePCtxtOnVCtxt(xmlSchemaValidCtxtPtr vctxt) +{ + if (vctxt->pctxt == NULL) { + if (vctxt->schema != NULL) + vctxt->pctxt = + xmlSchemaNewParserCtxtUseDict("*", vctxt->schema->dict); + else + vctxt->pctxt = xmlSchemaNewParserCtxt("*"); + if (vctxt->pctxt == NULL) { + VERROR_INT("xmlSchemaCreatePCtxtOnVCtxt", + "failed to create a temp. parser context"); + return (-1); + } + /* TODO: Pass user data. */ + xmlSchemaSetParserErrors(vctxt->pctxt, vctxt->error, + vctxt->warning, vctxt->errCtxt); + xmlSchemaSetParserStructuredErrors(vctxt->pctxt, vctxt->serror, + vctxt->errCtxt); + } + return (0); +} + +/** + * xmlSchemaGetSchemaBucket: + * @pctxt: the schema parser context + * @schemaLocation: the URI of the schema document + * + * Returns a schema bucket if it was already parsed. + * + * Returns a schema bucket if it was already parsed from + * @schemaLocation, NULL otherwise. + */ +static xmlSchemaBucketPtr +xmlSchemaGetSchemaBucket(xmlSchemaParserCtxtPtr pctxt, + const xmlChar *schemaLocation) +{ + xmlSchemaBucketPtr cur; + xmlSchemaItemListPtr list; + + list = pctxt->constructor->buckets; + if (list->nbItems == 0) + return(NULL); + else { + int i; + for (i = 0; i < list->nbItems; i++) { + cur = (xmlSchemaBucketPtr) list->items[i]; + /* Pointer comparison! */ + if (cur->schemaLocation == schemaLocation) + return(cur); + } + } + return(NULL); +} + +static xmlSchemaBucketPtr +xmlSchemaGetChameleonSchemaBucket(xmlSchemaParserCtxtPtr pctxt, + const xmlChar *schemaLocation, + const xmlChar *targetNamespace) +{ + xmlSchemaBucketPtr cur; + xmlSchemaItemListPtr list; + + list = pctxt->constructor->buckets; + if (list->nbItems == 0) + return(NULL); + else { + int i; + for (i = 0; i < list->nbItems; i++) { + cur = (xmlSchemaBucketPtr) list->items[i]; + /* Pointer comparison! */ + if ((cur->origTargetNamespace == NULL) && + (cur->schemaLocation == schemaLocation) && + (cur->targetNamespace == targetNamespace)) + return(cur); + } + } + return(NULL); +} + + +#define IS_BAD_SCHEMA_DOC(b) \ + (((b)->doc == NULL) && ((b)->schemaLocation != NULL)) + +static xmlSchemaBucketPtr +xmlSchemaGetSchemaBucketByTNS(xmlSchemaParserCtxtPtr pctxt, + const xmlChar *targetNamespace, + int imported) +{ + xmlSchemaBucketPtr cur; + xmlSchemaItemListPtr list; + + list = pctxt->constructor->buckets; + if (list->nbItems == 0) + return(NULL); + else { + int i; + for (i = 0; i < list->nbItems; i++) { + cur = (xmlSchemaBucketPtr) list->items[i]; + if ((! IS_BAD_SCHEMA_DOC(cur)) && + (cur->origTargetNamespace == targetNamespace) && + ((imported && cur->imported) || + ((!imported) && (!cur->imported)))) + return(cur); + } + } + return(NULL); +} + +static int +xmlSchemaParseNewDocWithContext(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaPtr schema, + xmlSchemaBucketPtr bucket) +{ + int oldFlags; + xmlDocPtr oldDoc; + xmlNodePtr node; + int ret, oldErrs; + xmlSchemaBucketPtr oldbucket = pctxt->constructor->bucket; + + /* + * Save old values; reset the *main* schema. + * URGENT TODO: This is not good; move the per-document information + * to the parser. Get rid of passing the main schema to the + * parsing functions. + */ + oldFlags = schema->flags; + oldDoc = schema->doc; + if (schema->flags != 0) + xmlSchemaClearSchemaDefaults(schema); + schema->doc = bucket->doc; + pctxt->schema = schema; + /* + * Keep the current target namespace on the parser *not* on the + * main schema. + */ + pctxt->targetNamespace = bucket->targetNamespace; + WXS_CONSTRUCTOR(pctxt)->bucket = bucket; + + if ((bucket->targetNamespace != NULL) && + xmlStrEqual(bucket->targetNamespace, xmlSchemaNs)) { + /* + * We are parsing the schema for schemas! + */ + pctxt->isS4S = 1; + } + /* Mark it as parsed, even if parsing fails. */ + bucket->parsed++; + /* Compile the schema doc. */ + node = xmlDocGetRootElement(bucket->doc); + ret = xmlSchemaParseSchemaElement(pctxt, schema, node); + if (ret != 0) + goto exit; + /* An empty schema; just get out. */ + if (node->children == NULL) + goto exit; + oldErrs = pctxt->nberrors; + ret = xmlSchemaParseSchemaTopLevel(pctxt, schema, node->children); + if (ret != 0) + goto exit; + /* + * TODO: Not nice, but I'm not 100% sure we will get always an error + * as a result of the obove functions; so better rely on pctxt->err + * as well. + */ + if ((ret == 0) && (oldErrs != pctxt->nberrors)) { + ret = pctxt->err; + goto exit; + } + +exit: + WXS_CONSTRUCTOR(pctxt)->bucket = oldbucket; + /* Restore schema values. */ + schema->doc = oldDoc; + schema->flags = oldFlags; + return(ret); +} + +static int +xmlSchemaParseNewDoc(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaPtr schema, + xmlSchemaBucketPtr bucket) +{ + xmlSchemaParserCtxtPtr newpctxt; + int res = 0; + + if (bucket == NULL) + return(0); + if (bucket->parsed) { + PERROR_INT("xmlSchemaParseNewDoc", + "reparsing a schema doc"); + return(-1); + } + if (bucket->doc == NULL) { + PERROR_INT("xmlSchemaParseNewDoc", + "parsing a schema doc, but there's no doc"); + return(-1); + } + if (pctxt->constructor == NULL) { + PERROR_INT("xmlSchemaParseNewDoc", + "no constructor"); + return(-1); + } + /* Create and init the temporary parser context. */ + newpctxt = xmlSchemaNewParserCtxtUseDict( + (const char *) bucket->schemaLocation, pctxt->dict); + if (newpctxt == NULL) + return(-1); + newpctxt->constructor = pctxt->constructor; + /* + * TODO: Can we avoid that the parser knows about the main schema? + * It would be better if he knows about the current schema bucket + * only. + */ + newpctxt->schema = schema; + xmlSchemaSetParserErrors(newpctxt, pctxt->error, pctxt->warning, + pctxt->errCtxt); + xmlSchemaSetParserStructuredErrors(newpctxt, pctxt->serror, + pctxt->errCtxt); + newpctxt->counter = pctxt->counter; + + + res = xmlSchemaParseNewDocWithContext(newpctxt, schema, bucket); + + /* Channel back errors and cleanup the temporary parser context. */ + if (res != 0) + pctxt->err = res; + pctxt->nberrors += newpctxt->nberrors; + pctxt->counter = newpctxt->counter; + newpctxt->constructor = NULL; + /* Free the parser context. */ + xmlSchemaFreeParserCtxt(newpctxt); + return(res); +} + +static void +xmlSchemaSchemaRelationAddChild(xmlSchemaBucketPtr bucket, + xmlSchemaSchemaRelationPtr rel) +{ + xmlSchemaSchemaRelationPtr cur = bucket->relations; + + if (cur == NULL) { + bucket->relations = rel; + return; + } + while (cur->next != NULL) + cur = cur->next; + cur->next = rel; +} + + +static const xmlChar * +xmlSchemaBuildAbsoluteURI(xmlDictPtr dict, const xmlChar* location, + xmlNodePtr ctxtNode) +{ + /* + * Build an absolue location URI. + */ + if (location != NULL) { + if (ctxtNode == NULL) + return(location); + else { + xmlChar *base, *URI; + const xmlChar *ret = NULL; + + base = xmlNodeGetBase(ctxtNode->doc, ctxtNode); + if (base == NULL) { + URI = xmlBuildURI(location, ctxtNode->doc->URL); + } else { + URI = xmlBuildURI(location, base); + xmlFree(base); + } + if (URI != NULL) { + ret = xmlDictLookup(dict, URI, -1); + xmlFree(URI); + return(ret); + } + } + } + return(NULL); +} + + + +/** + * xmlSchemaAddSchemaDoc: + * @pctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * + * Parse an included (and to-be-redefined) XML schema document. + * + * Returns 0 on success, a positive error code on errors and + * -1 in case of an internal or API error. + */ + +static int +xmlSchemaAddSchemaDoc(xmlSchemaParserCtxtPtr pctxt, + int type, /* import or include or redefine */ + const xmlChar *schemaLocation, + xmlDocPtr schemaDoc, + const char *schemaBuffer, + int schemaBufferLen, + xmlNodePtr invokingNode, + const xmlChar *sourceTargetNamespace, + const xmlChar *importNamespace, + xmlSchemaBucketPtr *bucket) +{ + const xmlChar *targetNamespace = NULL; + xmlSchemaSchemaRelationPtr relation = NULL; + xmlDocPtr doc = NULL; + int res = 0, err = 0, located = 0, preserveDoc = 0; + xmlSchemaBucketPtr bkt = NULL; + + if (bucket != NULL) + *bucket = NULL; + + switch (type) { + case XML_SCHEMA_SCHEMA_IMPORT: + case XML_SCHEMA_SCHEMA_MAIN: + err = XML_SCHEMAP_SRC_IMPORT; + break; + case XML_SCHEMA_SCHEMA_INCLUDE: + err = XML_SCHEMAP_SRC_INCLUDE; + break; + case XML_SCHEMA_SCHEMA_REDEFINE: + err = XML_SCHEMAP_SRC_REDEFINE; + break; + } + + + /* Special handling for the main schema: + * skip the location and relation logic and just parse the doc. + * We need just a bucket to be returned in this case. + */ + if ((type == XML_SCHEMA_SCHEMA_MAIN) || (! WXS_HAS_BUCKETS(pctxt))) + goto doc_load; + + /* Note that we expect the location to be an absulute URI. */ + if (schemaLocation != NULL) { + bkt = xmlSchemaGetSchemaBucket(pctxt, schemaLocation); + if ((bkt != NULL) && + (pctxt->constructor->bucket == bkt)) { + /* Report self-imports/inclusions/redefinitions. */ + + xmlSchemaCustomErr(ACTXT_CAST pctxt, err, + invokingNode, NULL, + "The schema must not import/include/redefine itself", + NULL, NULL); + goto exit; + } + } + /* + * Create a relation for the graph of schemas. + */ + relation = xmlSchemaSchemaRelationCreate(); + if (relation == NULL) + return(-1); + xmlSchemaSchemaRelationAddChild(pctxt->constructor->bucket, + relation); + relation->type = type; + + /* + * Save the namespace import information. + */ + if (WXS_IS_BUCKET_IMPMAIN(type)) { + relation->importNamespace = importNamespace; + if (schemaLocation == NULL) { + /* + * No location; this is just an import of the namespace. + * Note that we don't assign a bucket to the relation + * in this case. + */ + goto exit; + } + targetNamespace = importNamespace; + } + + /* Did we already fetch the doc? */ + if (bkt != NULL) { + /* TODO: The following nasty cases will produce an error. */ + if ((WXS_IS_BUCKET_IMPMAIN(type)) && (! bkt->imported)) { + /* We included/redefined and then try to import a schema. */ + xmlSchemaCustomErr(ACTXT_CAST pctxt, err, + invokingNode, NULL, + "The schema document '%s' cannot be imported, since " + "it was already included or redefined", + schemaLocation, NULL); + goto exit; + } else if ((! WXS_IS_BUCKET_IMPMAIN(type)) && (bkt->imported)) { + /* We imported and then try to include/redefine a schema. */ + xmlSchemaCustomErr(ACTXT_CAST pctxt, err, + invokingNode, NULL, + "The schema document '%s' cannot be included or " + "redefined, since it was already imported", + schemaLocation, NULL); + goto exit; + } + } + + if (WXS_IS_BUCKET_IMPMAIN(type)) { + /* + * Given that the schemaLocation [attribute] is only a hint, it is open + * to applications to ignore all but the first for a given + * namespace, regardless of the ·actual value· of schemaLocation, but + * such a strategy risks missing useful information when new + * schemaLocations are offered. + * + * We will use the first that comes with a location. + * Further s *with* a location, will result in an error. + * TODO: Better would be to just report a warning here, but + * we'll try it this way until someone complains. + * + * Schema Document Location Strategy: + * 3 Based on the namespace name, identify an existing schema document, + * either as a resource which is an XML document or a element + * information item, in some local schema repository; + * 5 Attempt to resolve the namespace name to locate such a resource. + * + * NOTE: (3) and (5) are not supported. + */ + if (bkt != NULL) { + relation->bucket = bkt; + goto exit; + } + bkt = xmlSchemaGetSchemaBucketByTNS(pctxt, + importNamespace, 1); + + if (bkt != NULL) { + relation->bucket = bkt; + if (bkt->schemaLocation == NULL) { + /* First given location of the schema; load the doc. */ + bkt->schemaLocation = schemaLocation; + } else { + if (!xmlStrEqual(schemaLocation, + bkt->schemaLocation)) { + /* + * Additional location given; just skip it. + * URGENT TODO: We should report a warning here. + * res = XML_SCHEMAP_SRC_IMPORT; + */ + xmlSchemaCustomWarning(ACTXT_CAST pctxt, + XML_SCHEMAP_WARN_SKIP_SCHEMA, + invokingNode, NULL, + "Skipping import of schema located at '%s' for the " + "namespace '%s', since this namespace was already " + "imported with the schema located at '%s'", + schemaLocation, importNamespace, bkt->schemaLocation); + } + goto exit; + } + } + /* + * No bucket + first location: load the doc and create a + * bucket. + */ + } else { + /* and */ + if (bkt != NULL) { + + if ((bkt->origTargetNamespace == NULL) && + (bkt->targetNamespace != sourceTargetNamespace)) { + xmlSchemaBucketPtr chamel; + + /* + * Chameleon include/redefine: skip loading only if it was + * aleady build for the targetNamespace of the including + * schema. + */ + /* + * URGENT TODO: If the schema is a chameleon-include then copy + * the components into the including schema and modify the + * targetNamespace of those components, do nothing otherwise. + * NOTE: This is currently worked-around by compiling the + * chameleon for every destinct including targetNamespace; thus + * not performant at the moment. + * TODO: Check when the namespace in wildcards for chameleons + * needs to be converted: before we built wildcard intersections + * or after. + * Answer: after! + */ + chamel = xmlSchemaGetChameleonSchemaBucket(pctxt, + schemaLocation, sourceTargetNamespace); + if (chamel != NULL) { + /* A fitting chameleon was already parsed; NOP. */ + relation->bucket = chamel; + goto exit; + } + /* + * We need to parse the chameleon again for a different + * targetNamespace. + * CHAMELEON TODO: Optimize this by only parsing the + * chameleon once, and then copying the components to + * the new targetNamespace. + */ + bkt = NULL; + } else { + relation->bucket = bkt; + goto exit; + } + } + } + if ((bkt != NULL) && (bkt->doc != NULL)) { + PERROR_INT("xmlSchemaAddSchemaDoc", + "trying to load a schema doc, but a doc is already " + "assigned to the schema bucket"); + goto exit_failure; + } + +doc_load: + /* + * Load the document. + */ + if (schemaDoc != NULL) { + doc = schemaDoc; + /* Don' free this one, since it was provided by the caller. */ + preserveDoc = 1; + /* TODO: Does the context or the doc hold the location? */ + if (schemaDoc->URL != NULL) + schemaLocation = xmlDictLookup(pctxt->dict, + schemaDoc->URL, -1); + + } else if ((schemaLocation != NULL) || (schemaBuffer != NULL)) { + xmlParserCtxtPtr parserCtxt; + + parserCtxt = xmlNewParserCtxt(); + if (parserCtxt == NULL) { + xmlSchemaPErrMemory(NULL, "xmlSchemaGetDoc, " + "allocating a parser context", NULL); + goto exit_failure; + } + if ((pctxt->dict != NULL) && (parserCtxt->dict != NULL)) { + /* + * TODO: Do we have to burden the schema parser dict with all + * the content of the schema doc? + */ + xmlDictFree(parserCtxt->dict); + parserCtxt->dict = pctxt->dict; + xmlDictReference(parserCtxt->dict); + } + if (schemaLocation != NULL) { + /* Parse from file. */ + doc = xmlCtxtReadFile(parserCtxt, (const char *) schemaLocation, + NULL, SCHEMAS_PARSE_OPTIONS); + } else if (schemaBuffer != NULL) { + /* Parse from memory buffer. */ + doc = xmlCtxtReadMemory(parserCtxt, schemaBuffer, schemaBufferLen, + NULL, NULL, SCHEMAS_PARSE_OPTIONS); + schemaLocation = xmlStrdup(BAD_CAST "in_memory_buffer"); + if (doc != NULL) + doc->URL = schemaLocation; + } + /* + * For : + * 2.1 The referent is (a fragment of) a resource which is an + * XML document (see clause 1.1), which in turn corresponds to + * a element information item in a well-formed information + * set, which in turn corresponds to a valid schema. + * TODO: (2.1) fragments of XML documents are not supported. + * + * 2.2 The referent is a element information item in + * a well-formed information set, which in turn corresponds + * to a valid schema. + * TODO: (2.2) is not supported. + */ + if (doc == NULL) { + xmlErrorPtr lerr; + lerr = xmlGetLastError(); + /* + * Check if this a parser error, or if the document could + * just not be located. + * TODO: Try to find specific error codes to react only on + * localisation failures. + */ + if ((lerr == NULL) || (lerr->domain != XML_FROM_IO)) { + /* + * We assume a parser error here. + */ + located = 1; + /* TODO: Error code ?? */ + res = XML_SCHEMAP_SRC_IMPORT_2_1; + xmlSchemaCustomErr(ACTXT_CAST pctxt, res, + invokingNode, NULL, + "Failed to parse the XML resource '%s'", + schemaLocation, NULL); + } + } + xmlFreeParserCtxt(parserCtxt); + if ((doc == NULL) && located) + goto exit_error; + } else { + xmlSchemaPErr(pctxt, NULL, + XML_SCHEMAP_NOTHING_TO_PARSE, + "No information for parsing was provided with the " + "given schema parser context.\n", + NULL, NULL); + goto exit_failure; + } + /* + * Preprocess the document. + */ + if (doc != NULL) { + xmlNodePtr docElem = NULL; + + located = 1; + docElem = xmlDocGetRootElement(doc); + if (docElem == NULL) { + xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_NOROOT, + invokingNode, NULL, + "The document '%s' has no document element", + schemaLocation, NULL); + goto exit_error; + } + /* + * Remove all the blank text nodes. + */ + xmlSchemaCleanupDoc(pctxt, docElem); + /* + * Check the schema's top level element. + */ + if (!IS_SCHEMA(docElem, "schema")) { + xmlSchemaCustomErr(ACTXT_CAST pctxt, XML_SCHEMAP_NOT_SCHEMA, + invokingNode, NULL, + "The XML document '%s' is not a schema document", + schemaLocation, NULL); + goto exit_error; + } + /* + * Note that we don't apply a type check for the + * targetNamespace value here. + */ + targetNamespace = xmlSchemaGetProp(pctxt, docElem, + "targetNamespace"); + } + +/* after_doc_loading: */ + if ((bkt == NULL) && located) { + /* Only create a bucket if the schema was located. */ + bkt = xmlSchemaBucketCreate(pctxt, type, + targetNamespace); + if (bkt == NULL) + goto exit_failure; + } + if (bkt != NULL) { + bkt->schemaLocation = schemaLocation; + bkt->located = located; + if (doc != NULL) { + bkt->doc = doc; + bkt->targetNamespace = targetNamespace; + bkt->origTargetNamespace = targetNamespace; + if (preserveDoc) + bkt->preserveDoc = 1; + } + if (WXS_IS_BUCKET_IMPMAIN(type)) + bkt->imported++; + /* + * Add it to the graph of schemas. + */ + if (relation != NULL) + relation->bucket = bkt; + } + +exit: + /* + * Return the bucket explicitely; this is needed for the + * main schema. + */ + if (bucket != NULL) + *bucket = bkt; + return (0); + +exit_error: + if ((doc != NULL) && (! preserveDoc)) { + xmlFreeDoc(doc); + if (bkt != NULL) + bkt->doc = NULL; + } + return(pctxt->err); + +exit_failure: + if ((doc != NULL) && (! preserveDoc)) { + xmlFreeDoc(doc); + if (bkt != NULL) + bkt->doc = NULL; + } + return (-1); +} + +/** + * xmlSchemaParseImport: + * @ctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * + * parse a XML schema Import definition + * *WARNING* this interface is highly subject to change + * + * Returns 0 in case of success, a positive error code if + * not valid and -1 in case of an internal error. + */ +static int +xmlSchemaParseImport(xmlSchemaParserCtxtPtr pctxt, xmlSchemaPtr schema, + xmlNodePtr node) +{ + xmlNodePtr child; + const xmlChar *namespaceName = NULL, *schemaLocation = NULL; + const xmlChar *thisTargetNamespace; + xmlAttrPtr attr; + int ret = 0; + xmlSchemaBucketPtr bucket = NULL; + + if ((pctxt == NULL) || (schema == NULL) || (node == NULL)) + return (-1); + + /* + * Check for illegal attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && + (!xmlStrEqual(attr->name, BAD_CAST "namespace")) && + (!xmlStrEqual(attr->name, BAD_CAST "schemaLocation"))) { + xmlSchemaPIllegalAttrErr(pctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + xmlSchemaPIllegalAttrErr(pctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + /* + * Extract and validate attributes. + */ + if (xmlSchemaPValAttr(pctxt, NULL, node, + "namespace", xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYURI), + &namespaceName) != 0) { + xmlSchemaPSimpleTypeErr(pctxt, + XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, + NULL, node, + xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYURI), + NULL, namespaceName, NULL, NULL, NULL); + return (pctxt->err); + } + + if (xmlSchemaPValAttr(pctxt, NULL, node, + "schemaLocation", xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYURI), + &schemaLocation) != 0) { + xmlSchemaPSimpleTypeErr(pctxt, + XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, + NULL, node, + xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYURI), + NULL, namespaceName, NULL, NULL, NULL); + return (pctxt->err); + } + /* + * And now for the children... + */ + child = node->children; + if (IS_SCHEMA(child, "annotation")) { + /* + * the annotation here is simply discarded ... + * TODO: really? + */ + child = child->next; + } + if (child != NULL) { + xmlSchemaPContentErr(pctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, NULL, + "(annotation?)"); + } + /* + * Apply additional constraints. + * + * Note that it is important to use the original @targetNamespace + * (or none at all), to rule out imports of schemas _with_ a + * @targetNamespace if the importing schema is a chameleon schema + * (with no @targetNamespace). + */ + thisTargetNamespace = WXS_BUCKET(pctxt)->origTargetNamespace; + if (namespaceName != NULL) { + /* + * 1.1 If the namespace [attribute] is present, then its ·actual value· + * must not match the ·actual value· of the enclosing 's + * targetNamespace [attribute]. + */ + if (xmlStrEqual(thisTargetNamespace, namespaceName)) { + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_SRC_IMPORT_1_1, + NULL, node, + "The value of the attribute 'namespace' must not match " + "the target namespace '%s' of the importing schema", + thisTargetNamespace); + return (pctxt->err); + } + } else { + /* + * 1.2 If the namespace [attribute] is not present, then the enclosing + * must have a targetNamespace [attribute]. + */ + if (thisTargetNamespace == NULL) { + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_SRC_IMPORT_1_2, + NULL, node, + "The attribute 'namespace' must be existent if " + "the importing schema has no target namespace", + NULL); + return (pctxt->err); + } + } + /* + * Locate and acquire the schema document. + */ + if (schemaLocation != NULL) + schemaLocation = xmlSchemaBuildAbsoluteURI(pctxt->dict, + schemaLocation, node); + ret = xmlSchemaAddSchemaDoc(pctxt, XML_SCHEMA_SCHEMA_IMPORT, + schemaLocation, NULL, NULL, 0, node, thisTargetNamespace, + namespaceName, &bucket); + + if (ret != 0) + return(ret); + + /* + * For : "It is *not* an error for the application + * schema reference strategy to fail." + * So just don't parse if no schema document was found. + * Note that we will get no bucket if the schema could not be + * located or if there was no schemaLocation. + */ + if ((bucket == NULL) && (schemaLocation != NULL)) { + xmlSchemaCustomWarning(ACTXT_CAST pctxt, + XML_SCHEMAP_WARN_UNLOCATED_SCHEMA, + node, NULL, + "Failed to locate a schema at location '%s'. " + "Skipping the import", schemaLocation, NULL, NULL); + } + + if ((bucket != NULL) && CAN_PARSE_SCHEMA(bucket)) { + ret = xmlSchemaParseNewDoc(pctxt, schema, bucket); + } + + return (ret); +} + +static int +xmlSchemaParseIncludeOrRedefineAttrs(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaPtr schema, + xmlNodePtr node, + xmlChar **schemaLocation, + int type) +{ + xmlAttrPtr attr; + + if ((pctxt == NULL) || (schema == NULL) || (node == NULL) || + (schemaLocation == NULL)) + return (-1); + + *schemaLocation = NULL; + /* + * Check for illegal attributes. + * Applies for both and . + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && + (!xmlStrEqual(attr->name, BAD_CAST "schemaLocation"))) { + xmlSchemaPIllegalAttrErr(pctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + xmlSchemaPIllegalAttrErr(pctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + xmlSchemaPValAttrID(pctxt, node, BAD_CAST "id"); + /* + * Preliminary step, extract the URI-Reference and make an URI + * from the base. + */ + /* + * Attribute "schemaLocation" is mandatory. + */ + attr = xmlSchemaGetPropNode(node, "schemaLocation"); + if (attr != NULL) { + xmlChar *base = NULL; + xmlChar *uri = NULL; + + if (xmlSchemaPValAttrNode(pctxt, NULL, attr, + xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYURI), + (const xmlChar **) schemaLocation) != 0) + goto exit_error; + base = xmlNodeGetBase(node->doc, node); + if (base == NULL) { + uri = xmlBuildURI(*schemaLocation, node->doc->URL); + } else { + uri = xmlBuildURI(*schemaLocation, base); + xmlFree(base); + } + if (uri == NULL) { + PERROR_INT("xmlSchemaParseIncludeOrRedefine", + "could not build an URI from the schemaLocation") + goto exit_failure; + } + (*schemaLocation) = (xmlChar *) xmlDictLookup(pctxt->dict, uri, -1); + xmlFree(uri); + } else { + xmlSchemaPMissingAttrErr(pctxt, + XML_SCHEMAP_S4S_ATTR_MISSING, + NULL, node, "schemaLocation", NULL); + goto exit_error; + } + /* + * Report self-inclusion and self-redefinition. + */ + if (xmlStrEqual(*schemaLocation, pctxt->URL)) { + if (type == XML_SCHEMA_SCHEMA_REDEFINE) { + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_SRC_REDEFINE, + NULL, node, + "The schema document '%s' cannot redefine itself.", + *schemaLocation); + } else { + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_SRC_INCLUDE, + NULL, node, + "The schema document '%s' cannot include itself.", + *schemaLocation); + } + goto exit_error; + } + + return(0); +exit_error: + return(pctxt->err); +exit_failure: + return(-1); +} + +static int +xmlSchemaParseIncludeOrRedefine(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaPtr schema, + xmlNodePtr node, + int type) +{ + xmlNodePtr child = NULL; + const xmlChar *schemaLocation = NULL; + int res = 0; /* hasRedefinitions = 0 */ + int isChameleon = 0, wasChameleon = 0; + xmlSchemaBucketPtr bucket = NULL; + + if ((pctxt == NULL) || (schema == NULL) || (node == NULL)) + return (-1); + + /* + * Parse attributes. Note that the returned schemaLocation will + * be already converted to an absolute URI. + */ + res = xmlSchemaParseIncludeOrRedefineAttrs(pctxt, schema, + node, (xmlChar **) (&schemaLocation), type); + if (res != 0) + return(res); + /* + * Load and add the schema document. + */ + res = xmlSchemaAddSchemaDoc(pctxt, type, schemaLocation, NULL, + NULL, 0, node, pctxt->targetNamespace, NULL, &bucket); + if (res != 0) + return(res); + /* + * If we get no schema bucket back, then this means that the schema + * document could not be located or was broken XML or was not + * a schema document. + */ + if ((bucket == NULL) || (bucket->doc == NULL)) { + if (type == XML_SCHEMA_SCHEMA_INCLUDE) { + /* + * WARNING for : + * We will raise an error if the schema cannot be located + * for inclusions, since the that was the feedback from the + * schema people. I.e. the following spec piece will *not* be + * satisfied: + * SPEC src-include: "It is not an error for the ·actual value· of the + * schemaLocation [attribute] to fail to resolve it all, in which + * case no corresponding inclusion is performed. + * So do we need a warning report here?" + */ + res = XML_SCHEMAP_SRC_INCLUDE; + xmlSchemaCustomErr(ACTXT_CAST pctxt, res, + node, NULL, + "Failed to load the document '%s' for inclusion", + schemaLocation, NULL); + } else { + /* + * NOTE: This was changed to raise an error even if no redefinitions + * are specified. + * + * SPEC src-redefine (1) + * "If there are any element information items among the [children] + * other than then the ·actual value· of the + * schemaLocation [attribute] must successfully resolve." + * TODO: Ask the WG if a the location has always to resolve + * here as well! + */ + res = XML_SCHEMAP_SRC_REDEFINE; + xmlSchemaCustomErr(ACTXT_CAST pctxt, res, + node, NULL, + "Failed to load the document '%s' for redefinition", + schemaLocation, NULL); + } + } else { + /* + * Check targetNamespace sanity before parsing the new schema. + * TODO: Note that we won't check further content if the + * targetNamespace was bad. + */ + if (bucket->origTargetNamespace != NULL) { + /* + * SPEC src-include (2.1) + * "SII has a targetNamespace [attribute], and its ·actual + * value· is identical to the ·actual value· of the targetNamespace + * [attribute] of SII’ (which must have such an [attribute])." + */ + if (pctxt->targetNamespace == NULL) { + xmlSchemaCustomErr(ACTXT_CAST pctxt, + XML_SCHEMAP_SRC_INCLUDE, + node, NULL, + "The target namespace of the included/redefined schema " + "'%s' has to be absent, since the including/redefining " + "schema has no target namespace", + schemaLocation, NULL); + goto exit_error; + } else if (!xmlStrEqual(bucket->origTargetNamespace, + pctxt->targetNamespace)) { + /* TODO: Change error function. */ + xmlSchemaPCustomErrExt(pctxt, + XML_SCHEMAP_SRC_INCLUDE, + NULL, node, + "The target namespace '%s' of the included/redefined " + "schema '%s' differs from '%s' of the " + "including/redefining schema", + bucket->origTargetNamespace, schemaLocation, + pctxt->targetNamespace); + goto exit_error; + } + } else if (pctxt->targetNamespace != NULL) { + /* + * Chameleons: the original target namespace will + * differ from the resulting namespace. + */ + isChameleon = 1; + if (bucket->parsed && + (bucket->targetNamespace != pctxt->targetNamespace)) { + /* + * This is a sanity check, I dunno yet if this can happen. + */ + PERROR_INT("xmlSchemaParseIncludeOrRedefine", + "trying to use an already parsed schema for a " + "different targetNamespace"); + return(-1); + } + bucket->targetNamespace = pctxt->targetNamespace; + } + } + /* + * Parse the schema. + */ + if (bucket && (!bucket->parsed) && (bucket->doc != NULL)) { + if (isChameleon) { + /* TODO: Get rid of this flag on the schema itself. */ + if ((schema->flags & XML_SCHEMAS_INCLUDING_CONVERT_NS) == 0) { + schema->flags |= XML_SCHEMAS_INCLUDING_CONVERT_NS; + } else + wasChameleon = 1; + } + xmlSchemaParseNewDoc(pctxt, schema, bucket); + /* Restore chameleon flag. */ + if (isChameleon && (!wasChameleon)) + schema->flags ^= XML_SCHEMAS_INCLUDING_CONVERT_NS; + } + /* + * And now for the children... + */ + child = node->children; + if (type == XML_SCHEMA_SCHEMA_REDEFINE) { + /* + * Parse (simpleType | complexType | group | attributeGroup))* + */ + pctxt->redefined = bucket; + /* + * How to proceed if the redefined schema was not located? + */ + pctxt->isRedefine = 1; + while (IS_SCHEMA(child, "annotation") || + IS_SCHEMA(child, "simpleType") || + IS_SCHEMA(child, "complexType") || + IS_SCHEMA(child, "group") || + IS_SCHEMA(child, "attributeGroup")) { + if (IS_SCHEMA(child, "annotation")) { + /* + * TODO: discard or not? + */ + } else if (IS_SCHEMA(child, "simpleType")) { + xmlSchemaParseSimpleType(pctxt, schema, child, 1); + } else if (IS_SCHEMA(child, "complexType")) { + xmlSchemaParseComplexType(pctxt, schema, child, 1); + /* hasRedefinitions = 1; */ + } else if (IS_SCHEMA(child, "group")) { + /* hasRedefinitions = 1; */ + xmlSchemaParseModelGroupDefinition(pctxt, + schema, child); + } else if (IS_SCHEMA(child, "attributeGroup")) { + /* hasRedefinitions = 1; */ + xmlSchemaParseAttributeGroupDefinition(pctxt, schema, + child); + } + child = child->next; + } + pctxt->redefined = NULL; + pctxt->isRedefine = 0; + } else { + if (IS_SCHEMA(child, "annotation")) { + /* + * TODO: discard or not? + */ + child = child->next; + } + } + if (child != NULL) { + res = XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED; + if (type == XML_SCHEMA_SCHEMA_REDEFINE) { + xmlSchemaPContentErr(pctxt, res, + NULL, node, child, NULL, + "(annotation | (simpleType | complexType | group | attributeGroup))*"); + } else { + xmlSchemaPContentErr(pctxt, res, + NULL, node, child, NULL, + "(annotation?)"); + } + } + return(res); + +exit_error: + return(pctxt->err); +} + +static int +xmlSchemaParseRedefine(xmlSchemaParserCtxtPtr pctxt, xmlSchemaPtr schema, + xmlNodePtr node) +{ + int res; +#ifndef ENABLE_REDEFINE + TODO + return(0); +#endif + res = xmlSchemaParseIncludeOrRedefine(pctxt, schema, node, + XML_SCHEMA_SCHEMA_REDEFINE); + if (res != 0) + return(res); + return(0); +} + +static int +xmlSchemaParseInclude(xmlSchemaParserCtxtPtr pctxt, xmlSchemaPtr schema, + xmlNodePtr node) +{ + int res; + + res = xmlSchemaParseIncludeOrRedefine(pctxt, schema, node, + XML_SCHEMA_SCHEMA_INCLUDE); + if (res != 0) + return(res); + return(0); +} + +/** + * xmlSchemaParseModelGroup: + * @ctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * @type: the "compositor" type + * @particleNeeded: if a a model group with a particle + * + * parse a XML schema Sequence definition. + * Applies parts of: + * Schema Representation Constraint: + * Redefinition Constraints and Semantics (src-redefine) + * (6.1), (6.1.1), (6.1.2) + * + * Schema Component Constraint: + * All Group Limited (cos-all-limited) (2) + * TODO: Actually this should go to component-level checks, + * but is done here due to performance. Move it to an other layer + * is schema construction via an API is implemented. + * + * *WARNING* this interface is highly subject to change + * + * Returns -1 in case of error, 0 if the declaration is improper and + * 1 in case of success. + */ +static xmlSchemaTreeItemPtr +xmlSchemaParseModelGroup(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, + xmlNodePtr node, xmlSchemaTypeType type, + int withParticle) +{ + xmlSchemaModelGroupPtr item; + xmlSchemaParticlePtr particle = NULL; + xmlNodePtr child = NULL; + xmlAttrPtr attr; + int min = 1, max = 1, isElemRef, hasRefs = 0; + + if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) + return (NULL); + /* + * Create a model group with the given compositor. + */ + item = xmlSchemaAddModelGroup(ctxt, schema, type, node); + if (item == NULL) + return (NULL); + + if (withParticle) { + if (type == XML_SCHEMA_TYPE_ALL) { + min = xmlGetMinOccurs(ctxt, node, 0, 1, 1, "(0 | 1)"); + max = xmlGetMaxOccurs(ctxt, node, 1, 1, 1, "1"); + } else { + /* choice + sequence */ + min = xmlGetMinOccurs(ctxt, node, 0, -1, 1, "xs:nonNegativeInteger"); + max = xmlGetMaxOccurs(ctxt, node, 0, UNBOUNDED, 1, + "(xs:nonNegativeInteger | unbounded)"); + } + xmlSchemaPCheckParticleCorrect_2(ctxt, NULL, node, min, max); + /* + * Create a particle + */ + particle = xmlSchemaAddParticle(ctxt, node, min, max); + if (particle == NULL) + return (NULL); + particle->children = (xmlSchemaTreeItemPtr) item; + /* + * Check for illegal attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && + (!xmlStrEqual(attr->name, BAD_CAST "maxOccurs")) && + (!xmlStrEqual(attr->name, BAD_CAST "minOccurs"))) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + } else { + /* + * Check for illegal attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if (!xmlStrEqual(attr->name, BAD_CAST "id")) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + } + + /* + * Extract and validate attributes. + */ + xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); + /* + * And now for the children... + */ + child = node->children; + if (IS_SCHEMA(child, "annotation")) { + item->annot = xmlSchemaParseAnnotation(ctxt, child, 1); + child = child->next; + } + if (type == XML_SCHEMA_TYPE_ALL) { + xmlSchemaParticlePtr part, last = NULL; + + while (IS_SCHEMA(child, "element")) { + part = (xmlSchemaParticlePtr) xmlSchemaParseElement(ctxt, + schema, child, &isElemRef, 0); + /* + * SPEC cos-all-limited (2) + * "The {max occurs} of all the particles in the {particles} + * of the ('all') group must be 0 or 1. + */ + if (part != NULL) { + if (isElemRef) + hasRefs++; + if (part->minOccurs > 1) { + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_COS_ALL_LIMITED, + NULL, child, + "Invalid value for minOccurs (must be 0 or 1)", + NULL); + /* Reset to 1. */ + part->minOccurs = 1; + } + if (part->maxOccurs > 1) { + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_COS_ALL_LIMITED, + NULL, child, + "Invalid value for maxOccurs (must be 0 or 1)", + NULL); + /* Reset to 1. */ + part->maxOccurs = 1; + } + if (last == NULL) + item->children = (xmlSchemaTreeItemPtr) part; + else + last->next = (xmlSchemaTreeItemPtr) part; + last = part; + } + child = child->next; + } + if (child != NULL) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, NULL, + "(annotation?, (annotation?, element*)"); + } + } else { + /* choice + sequence */ + xmlSchemaTreeItemPtr part = NULL, last = NULL; + + while ((IS_SCHEMA(child, "element")) || + (IS_SCHEMA(child, "group")) || + (IS_SCHEMA(child, "any")) || + (IS_SCHEMA(child, "choice")) || + (IS_SCHEMA(child, "sequence"))) { + + if (IS_SCHEMA(child, "element")) { + part = (xmlSchemaTreeItemPtr) + xmlSchemaParseElement(ctxt, schema, child, &isElemRef, 0); + if (part && isElemRef) + hasRefs++; + } else if (IS_SCHEMA(child, "group")) { + part = + xmlSchemaParseModelGroupDefRef(ctxt, schema, child); + if (part != NULL) + hasRefs++; + /* + * Handle redefinitions. + */ + if (ctxt->isRedefine && ctxt->redef && + (ctxt->redef->item->type == XML_SCHEMA_TYPE_GROUP) && + part && part->children) + { + if ((xmlSchemaGetQNameRefName(part->children) == + ctxt->redef->refName) && + (xmlSchemaGetQNameRefTargetNs(part->children) == + ctxt->redef->refTargetNs)) + { + /* + * SPEC src-redefine: + * (6.1) "If it has a among its contents at + * some level the ·actual value· of whose ref + * [attribute] is the same as the ·actual value· of + * its own name attribute plus target namespace, then + * all of the following must be true:" + * (6.1.1) "It must have exactly one such group." + */ + if (ctxt->redefCounter != 0) { + xmlChar *str = NULL; + + xmlSchemaCustomErr(ACTXT_CAST ctxt, + XML_SCHEMAP_SRC_REDEFINE, child, NULL, + "The redefining model group definition " + "'%s' must not contain more than one " + "reference to the redefined definition", + xmlSchemaFormatQName(&str, + ctxt->redef->refTargetNs, + ctxt->redef->refName), + NULL); + FREE_AND_NULL(str) + part = NULL; + } else if (((WXS_PARTICLE(part))->minOccurs != 1) || + ((WXS_PARTICLE(part))->maxOccurs != 1)) + { + xmlChar *str = NULL; + /* + * SPEC src-redefine: + * (6.1.2) "The ·actual value· of both that + * group's minOccurs and maxOccurs [attribute] + * must be 1 (or ·absent·). + */ + xmlSchemaCustomErr(ACTXT_CAST ctxt, + XML_SCHEMAP_SRC_REDEFINE, child, NULL, + "The redefining model group definition " + "'%s' must not contain a reference to the " + "redefined definition with a " + "maxOccurs/minOccurs other than 1", + xmlSchemaFormatQName(&str, + ctxt->redef->refTargetNs, + ctxt->redef->refName), + NULL); + FREE_AND_NULL(str) + part = NULL; + } + ctxt->redef->reference = WXS_BASIC_CAST part; + ctxt->redefCounter++; + } + } + } else if (IS_SCHEMA(child, "any")) { + part = (xmlSchemaTreeItemPtr) + xmlSchemaParseAny(ctxt, schema, child); + } else if (IS_SCHEMA(child, "choice")) { + part = xmlSchemaParseModelGroup(ctxt, schema, child, + XML_SCHEMA_TYPE_CHOICE, 1); + } else if (IS_SCHEMA(child, "sequence")) { + part = xmlSchemaParseModelGroup(ctxt, schema, child, + XML_SCHEMA_TYPE_SEQUENCE, 1); + } + if (part != NULL) { + if (last == NULL) + item->children = part; + else + last->next = part; + last = part; + } + child = child->next; + } + if (child != NULL) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, NULL, + "(annotation?, (element | group | choice | sequence | any)*)"); + } + } + if ((max == 0) && (min == 0)) + return (NULL); + if (hasRefs) { + /* + * We need to resolve references. + */ + WXS_ADD_PENDING(ctxt, item); + } + if (withParticle) + return ((xmlSchemaTreeItemPtr) particle); + else + return ((xmlSchemaTreeItemPtr) item); +} + +/** + * xmlSchemaParseRestriction: + * @ctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * + * parse a XML schema Restriction definition + * *WARNING* this interface is highly subject to change + * + * Returns the type definition or NULL in case of error + */ +static xmlSchemaTypePtr +xmlSchemaParseRestriction(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, + xmlNodePtr node, xmlSchemaTypeType parentType) +{ + xmlSchemaTypePtr type; + xmlNodePtr child = NULL; + xmlAttrPtr attr; + + if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) + return (NULL); + /* Not a component, don't create it. */ + type = ctxt->ctxtType; + type->flags |= XML_SCHEMAS_TYPE_DERIVATION_METHOD_RESTRICTION; + + /* + * Check for illegal attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && + (!xmlStrEqual(attr->name, BAD_CAST "base"))) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + /* + * Extract and validate attributes. + */ + xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); + /* + * Attribute + */ + /* + * Extract the base type. The "base" attribute is mandatory if inside + * a complex type or if redefining. + * + * SPEC (1.2) "...otherwise ( has no " + * among its [children]), the simple type definition which is + * the {content type} of the type definition ·resolved· to by + * the ·actual value· of the base [attribute]" + */ + if (xmlSchemaPValAttrQName(ctxt, schema, NULL, node, "base", + &(type->baseNs), &(type->base)) == 0) + { + if ((type->base == NULL) && (type->type == XML_SCHEMA_TYPE_COMPLEX)) { + xmlSchemaPMissingAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_MISSING, + NULL, node, "base", NULL); + } else if ((ctxt->isRedefine) && + (type->flags & XML_SCHEMAS_TYPE_GLOBAL)) + { + if (type->base == NULL) { + xmlSchemaPMissingAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_MISSING, + NULL, node, "base", NULL); + } else if ((! xmlStrEqual(type->base, type->name)) || + (! xmlStrEqual(type->baseNs, type->targetNamespace))) + { + xmlChar *str1 = NULL, *str2 = NULL; + /* + * REDEFINE: SPEC src-redefine (5) + * "Within the [children], each must have a + * among its [children] ... the ·actual value· of + * whose base [attribute] must be the same as the ·actual value· + * of its own name attribute plus target namespace;" + */ + xmlSchemaPCustomErrExt(ctxt, XML_SCHEMAP_SRC_REDEFINE, + NULL, node, "This is a redefinition, but the QName " + "value '%s' of the 'base' attribute does not match the " + "type's designation '%s'", + xmlSchemaFormatQName(&str1, type->baseNs, type->base), + xmlSchemaFormatQName(&str2, type->targetNamespace, + type->name), NULL); + FREE_AND_NULL(str1); + FREE_AND_NULL(str2); + /* Avoid confusion and erase the values. */ + type->base = NULL; + type->baseNs = NULL; + } + } + } + /* + * And now for the children... + */ + child = node->children; + if (IS_SCHEMA(child, "annotation")) { + /* + * Add the annotation to the simple type ancestor. + */ + xmlSchemaAddAnnotation((xmlSchemaAnnotItemPtr) type, + xmlSchemaParseAnnotation(ctxt, child, 1)); + child = child->next; + } + if (parentType == XML_SCHEMA_TYPE_SIMPLE) { + /* + * Corresponds to . + */ + if (IS_SCHEMA(child, "simpleType")) { + if (type->base != NULL) { + /* + * src-restriction-base-or-simpleType + * Either the base [attribute] or the simpleType [child] of the + * element must be present, but not both. + */ + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_SRC_RESTRICTION_BASE_OR_SIMPLETYPE, + NULL, node, child, + "The attribute 'base' and the child are " + "mutually exclusive", NULL); + } else { + type->baseType = (xmlSchemaTypePtr) + xmlSchemaParseSimpleType(ctxt, schema, child, 0); + } + child = child->next; + } else if (type->base == NULL) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_SRC_RESTRICTION_BASE_OR_SIMPLETYPE, + NULL, node, child, + "Either the attribute 'base' or a child " + "must be present", NULL); + } + } else if (parentType == XML_SCHEMA_TYPE_COMPLEX_CONTENT) { + /* + * Corresponds to ... + * followed by: + * + * Model groups , and . + */ + if (IS_SCHEMA(child, "all")) { + type->subtypes = (xmlSchemaTypePtr) + xmlSchemaParseModelGroup(ctxt, schema, child, + XML_SCHEMA_TYPE_ALL, 1); + child = child->next; + } else if (IS_SCHEMA(child, "choice")) { + type->subtypes = (xmlSchemaTypePtr) + xmlSchemaParseModelGroup(ctxt, + schema, child, XML_SCHEMA_TYPE_CHOICE, 1); + child = child->next; + } else if (IS_SCHEMA(child, "sequence")) { + type->subtypes = (xmlSchemaTypePtr) + xmlSchemaParseModelGroup(ctxt, schema, child, + XML_SCHEMA_TYPE_SEQUENCE, 1); + child = child->next; + /* + * Model group reference . + */ + } else if (IS_SCHEMA(child, "group")) { + type->subtypes = (xmlSchemaTypePtr) + xmlSchemaParseModelGroupDefRef(ctxt, schema, child); + /* + * Note that the reference will be resolved in + * xmlSchemaResolveTypeReferences(); + */ + child = child->next; + } + } else if (parentType == XML_SCHEMA_TYPE_SIMPLE_CONTENT) { + /* + * Corresponds to ... + * + * "1.1 the simple type definition corresponding to the + * among the [children] of if there is one;" + */ + if (IS_SCHEMA(child, "simpleType")) { + /* + * We will store the to-be-restricted simple type in + * type->contentTypeDef *temporarily*. + */ + type->contentTypeDef = (xmlSchemaTypePtr) + xmlSchemaParseSimpleType(ctxt, schema, child, 0); + if ( type->contentTypeDef == NULL) + return (NULL); + child = child->next; + } + } + + if ((parentType == XML_SCHEMA_TYPE_SIMPLE) || + (parentType == XML_SCHEMA_TYPE_SIMPLE_CONTENT)) { + xmlSchemaFacetPtr facet, lastfacet = NULL; + /* + * Corresponds to ... + * ... + */ + + /* + * Add the facets to the simple type ancestor. + */ + /* + * TODO: Datatypes: 4.1.3 Constraints on XML Representation of + * Simple Type Definition Schema Representation Constraint: + * *Single Facet Value* + */ + while ((IS_SCHEMA(child, "minInclusive")) || + (IS_SCHEMA(child, "minExclusive")) || + (IS_SCHEMA(child, "maxInclusive")) || + (IS_SCHEMA(child, "maxExclusive")) || + (IS_SCHEMA(child, "totalDigits")) || + (IS_SCHEMA(child, "fractionDigits")) || + (IS_SCHEMA(child, "pattern")) || + (IS_SCHEMA(child, "enumeration")) || + (IS_SCHEMA(child, "whiteSpace")) || + (IS_SCHEMA(child, "length")) || + (IS_SCHEMA(child, "maxLength")) || + (IS_SCHEMA(child, "minLength"))) { + facet = xmlSchemaParseFacet(ctxt, schema, child); + if (facet != NULL) { + if (lastfacet == NULL) + type->facets = facet; + else + lastfacet->next = facet; + lastfacet = facet; + lastfacet->next = NULL; + } + child = child->next; + } + /* + * Create links for derivation and validation. + */ + if (type->facets != NULL) { + xmlSchemaFacetLinkPtr facetLink, lastFacetLink = NULL; + + facet = type->facets; + do { + facetLink = (xmlSchemaFacetLinkPtr) + xmlMalloc(sizeof(xmlSchemaFacetLink)); + if (facetLink == NULL) { + xmlSchemaPErrMemory(ctxt, "allocating a facet link", NULL); + xmlFree(facetLink); + return (NULL); + } + facetLink->facet = facet; + facetLink->next = NULL; + if (lastFacetLink == NULL) + type->facetSet = facetLink; + else + lastFacetLink->next = facetLink; + lastFacetLink = facetLink; + facet = facet->next; + } while (facet != NULL); + } + } + if (type->type == XML_SCHEMA_TYPE_COMPLEX) { + /* + * Attribute uses/declarations. + */ + if (xmlSchemaParseLocalAttributes(ctxt, schema, &child, + (xmlSchemaItemListPtr *) &(type->attrUses), + XML_SCHEMA_TYPE_RESTRICTION, NULL) == -1) + return(NULL); + /* + * Attribute wildcard. + */ + if (IS_SCHEMA(child, "anyAttribute")) { + type->attributeWildcard = + xmlSchemaParseAnyAttribute(ctxt, schema, child); + child = child->next; + } + } + if (child != NULL) { + if (parentType == XML_SCHEMA_TYPE_COMPLEX_CONTENT) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, NULL, + "annotation?, (group | all | choice | sequence)?, " + "((attribute | attributeGroup)*, anyAttribute?))"); + } else if (parentType == XML_SCHEMA_TYPE_SIMPLE_CONTENT) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, NULL, + "(annotation?, (simpleType?, (minExclusive | minInclusive | " + "maxExclusive | maxInclusive | totalDigits | fractionDigits | " + "length | minLength | maxLength | enumeration | whiteSpace | " + "pattern)*)?, ((attribute | attributeGroup)*, anyAttribute?))"); + } else { + /* Simple type */ + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, NULL, + "(annotation?, (simpleType?, (minExclusive | minInclusive | " + "maxExclusive | maxInclusive | totalDigits | fractionDigits | " + "length | minLength | maxLength | enumeration | whiteSpace | " + "pattern)*))"); + } + } + return (NULL); +} + +/** + * xmlSchemaParseExtension: + * @ctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * + * Parses an , which is found inside a + * or . + * *WARNING* this interface is highly subject to change. + * + * TODO: Returns the type definition or NULL in case of error + */ +static xmlSchemaTypePtr +xmlSchemaParseExtension(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, + xmlNodePtr node, xmlSchemaTypeType parentType) +{ + xmlSchemaTypePtr type; + xmlNodePtr child = NULL; + xmlAttrPtr attr; + + if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) + return (NULL); + /* Not a component, don't create it. */ + type = ctxt->ctxtType; + type->flags |= XML_SCHEMAS_TYPE_DERIVATION_METHOD_EXTENSION; + + /* + * Check for illegal attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && + (!xmlStrEqual(attr->name, BAD_CAST "base"))) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + + xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); + + /* + * Attribute "base" - mandatory. + */ + if ((xmlSchemaPValAttrQName(ctxt, schema, NULL, node, + "base", &(type->baseNs), &(type->base)) == 0) && + (type->base == NULL)) { + xmlSchemaPMissingAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_MISSING, + NULL, node, "base", NULL); + } + /* + * And now for the children... + */ + child = node->children; + if (IS_SCHEMA(child, "annotation")) { + /* + * Add the annotation to the type ancestor. + */ + xmlSchemaAddAnnotation((xmlSchemaAnnotItemPtr) type, + xmlSchemaParseAnnotation(ctxt, child, 1)); + child = child->next; + } + if (parentType == XML_SCHEMA_TYPE_COMPLEX_CONTENT) { + /* + * Corresponds to ... and: + * + * Model groups , , and . + */ + if (IS_SCHEMA(child, "all")) { + type->subtypes = (xmlSchemaTypePtr) + xmlSchemaParseModelGroup(ctxt, schema, + child, XML_SCHEMA_TYPE_ALL, 1); + child = child->next; + } else if (IS_SCHEMA(child, "choice")) { + type->subtypes = (xmlSchemaTypePtr) + xmlSchemaParseModelGroup(ctxt, schema, + child, XML_SCHEMA_TYPE_CHOICE, 1); + child = child->next; + } else if (IS_SCHEMA(child, "sequence")) { + type->subtypes = (xmlSchemaTypePtr) + xmlSchemaParseModelGroup(ctxt, schema, + child, XML_SCHEMA_TYPE_SEQUENCE, 1); + child = child->next; + } else if (IS_SCHEMA(child, "group")) { + type->subtypes = (xmlSchemaTypePtr) + xmlSchemaParseModelGroupDefRef(ctxt, schema, child); + /* + * Note that the reference will be resolved in + * xmlSchemaResolveTypeReferences(); + */ + child = child->next; + } + } + if (child != NULL) { + /* + * Attribute uses/declarations. + */ + if (xmlSchemaParseLocalAttributes(ctxt, schema, &child, + (xmlSchemaItemListPtr *) &(type->attrUses), + XML_SCHEMA_TYPE_EXTENSION, NULL) == -1) + return(NULL); + /* + * Attribute wildcard. + */ + if (IS_SCHEMA(child, "anyAttribute")) { + ctxt->ctxtType->attributeWildcard = + xmlSchemaParseAnyAttribute(ctxt, schema, child); + child = child->next; + } + } + if (child != NULL) { + if (parentType == XML_SCHEMA_TYPE_COMPLEX_CONTENT) { + /* Complex content extension. */ + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, NULL, + "(annotation?, ((group | all | choice | sequence)?, " + "((attribute | attributeGroup)*, anyAttribute?)))"); + } else { + /* Simple content extension. */ + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, NULL, + "(annotation?, ((attribute | attributeGroup)*, " + "anyAttribute?))"); + } + } + return (NULL); +} + +/** + * xmlSchemaParseSimpleContent: + * @ctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * + * parse a XML schema SimpleContent definition + * *WARNING* this interface is highly subject to change + * + * Returns the type definition or NULL in case of error + */ +static int +xmlSchemaParseSimpleContent(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaPtr schema, xmlNodePtr node, + int *hasRestrictionOrExtension) +{ + xmlSchemaTypePtr type; + xmlNodePtr child = NULL; + xmlAttrPtr attr; + + if ((ctxt == NULL) || (schema == NULL) || (node == NULL) || + (hasRestrictionOrExtension == NULL)) + return (-1); + *hasRestrictionOrExtension = 0; + /* Not a component, don't create it. */ + type = ctxt->ctxtType; + type->contentType = XML_SCHEMA_CONTENT_SIMPLE; + /* + * Check for illegal attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if ((!xmlStrEqual(attr->name, BAD_CAST "id"))) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + + xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); + + /* + * And now for the children... + */ + child = node->children; + if (IS_SCHEMA(child, "annotation")) { + /* + * Add the annotation to the complex type ancestor. + */ + xmlSchemaAddAnnotation((xmlSchemaAnnotItemPtr) type, + xmlSchemaParseAnnotation(ctxt, child, 1)); + child = child->next; + } + if (child == NULL) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_MISSING, + NULL, node, NULL, NULL, + "(annotation?, (restriction | extension))"); + } + if (child == NULL) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_MISSING, + NULL, node, NULL, NULL, + "(annotation?, (restriction | extension))"); + } + if (IS_SCHEMA(child, "restriction")) { + xmlSchemaParseRestriction(ctxt, schema, child, + XML_SCHEMA_TYPE_SIMPLE_CONTENT); + (*hasRestrictionOrExtension) = 1; + child = child->next; + } else if (IS_SCHEMA(child, "extension")) { + xmlSchemaParseExtension(ctxt, schema, child, + XML_SCHEMA_TYPE_SIMPLE_CONTENT); + (*hasRestrictionOrExtension) = 1; + child = child->next; + } + if (child != NULL) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, NULL, + "(annotation?, (restriction | extension))"); + } + return (0); +} + +/** + * xmlSchemaParseComplexContent: + * @ctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * + * parse a XML schema ComplexContent definition + * *WARNING* this interface is highly subject to change + * + * Returns the type definition or NULL in case of error + */ +static int +xmlSchemaParseComplexContent(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaPtr schema, xmlNodePtr node, + int *hasRestrictionOrExtension) +{ + xmlSchemaTypePtr type; + xmlNodePtr child = NULL; + xmlAttrPtr attr; + + if ((ctxt == NULL) || (schema == NULL) || (node == NULL) || + (hasRestrictionOrExtension == NULL)) + return (-1); + *hasRestrictionOrExtension = 0; + /* Not a component, don't create it. */ + type = ctxt->ctxtType; + /* + * Check for illegal attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if ((!xmlStrEqual(attr->name, BAD_CAST "id")) && + (!xmlStrEqual(attr->name, BAD_CAST "mixed"))) + { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + + xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); + + /* + * Set the 'mixed' on the complex type ancestor. + */ + if (xmlGetBooleanProp(ctxt, node, "mixed", 0)) { + if ((type->flags & XML_SCHEMAS_TYPE_MIXED) == 0) + type->flags |= XML_SCHEMAS_TYPE_MIXED; + } + child = node->children; + if (IS_SCHEMA(child, "annotation")) { + /* + * Add the annotation to the complex type ancestor. + */ + xmlSchemaAddAnnotation((xmlSchemaAnnotItemPtr) type, + xmlSchemaParseAnnotation(ctxt, child, 1)); + child = child->next; + } + if (child == NULL) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_MISSING, + NULL, node, NULL, + NULL, "(annotation?, (restriction | extension))"); + } + if (child == NULL) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_MISSING, + NULL, node, NULL, + NULL, "(annotation?, (restriction | extension))"); + } + if (IS_SCHEMA(child, "restriction")) { + xmlSchemaParseRestriction(ctxt, schema, child, + XML_SCHEMA_TYPE_COMPLEX_CONTENT); + (*hasRestrictionOrExtension) = 1; + child = child->next; + } else if (IS_SCHEMA(child, "extension")) { + xmlSchemaParseExtension(ctxt, schema, child, + XML_SCHEMA_TYPE_COMPLEX_CONTENT); + (*hasRestrictionOrExtension) = 1; + child = child->next; + } + if (child != NULL) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, + NULL, "(annotation?, (restriction | extension))"); + } + return (0); +} + +/** + * xmlSchemaParseComplexType: + * @ctxt: a schema validation context + * @schema: the schema being built + * @node: a subtree containing XML Schema informations + * + * parse a XML schema Complex Type definition + * *WARNING* this interface is highly subject to change + * + * Returns the type definition or NULL in case of error + */ +static xmlSchemaTypePtr +xmlSchemaParseComplexType(xmlSchemaParserCtxtPtr ctxt, xmlSchemaPtr schema, + xmlNodePtr node, int topLevel) +{ + xmlSchemaTypePtr type, ctxtType; + xmlNodePtr child = NULL; + const xmlChar *name = NULL; + xmlAttrPtr attr; + const xmlChar *attrValue; +#ifdef ENABLE_NAMED_LOCALS + char buf[40]; +#endif + int final = 0, block = 0, hasRestrictionOrExtension = 0; + + + if ((ctxt == NULL) || (schema == NULL) || (node == NULL)) + return (NULL); + + ctxtType = ctxt->ctxtType; + + if (topLevel) { + attr = xmlSchemaGetPropNode(node, "name"); + if (attr == NULL) { + xmlSchemaPMissingAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_MISSING, NULL, node, "name", NULL); + return (NULL); + } else if (xmlSchemaPValAttrNode(ctxt, NULL, attr, + xmlSchemaGetBuiltInType(XML_SCHEMAS_NCNAME), &name) != 0) { + return (NULL); + } + } + + if (topLevel == 0) { + /* + * Parse as local complex type definition. + */ +#ifdef ENABLE_NAMED_LOCALS + snprintf(buf, 39, "#CT%d", ctxt->counter++ + 1); + type = xmlSchemaAddType(ctxt, schema, + XML_SCHEMA_TYPE_COMPLEX, + xmlDictLookup(ctxt->dict, (const xmlChar *)buf, -1), + ctxt->targetNamespace, node, 0); +#else + type = xmlSchemaAddType(ctxt, schema, + XML_SCHEMA_TYPE_COMPLEX, + NULL, ctxt->targetNamespace, node, 0); +#endif + if (type == NULL) + return (NULL); + name = type->name; + type->node = node; + type->type = XML_SCHEMA_TYPE_COMPLEX; + /* + * TODO: We need the target namespace. + */ + } else { + /* + * Parse as global complex type definition. + */ + type = xmlSchemaAddType(ctxt, schema, + XML_SCHEMA_TYPE_COMPLEX, + name, ctxt->targetNamespace, node, 1); + if (type == NULL) + return (NULL); + type->node = node; + type->type = XML_SCHEMA_TYPE_COMPLEX; + type->flags |= XML_SCHEMAS_TYPE_GLOBAL; + } + type->targetNamespace = ctxt->targetNamespace; + /* + * Handle attributes. + */ + attr = node->properties; + while (attr != NULL) { + if (attr->ns == NULL) { + if (xmlStrEqual(attr->name, BAD_CAST "id")) { + /* + * Attribute "id". + */ + xmlSchemaPValAttrID(ctxt, node, BAD_CAST "id"); + } else if (xmlStrEqual(attr->name, BAD_CAST "mixed")) { + /* + * Attribute "mixed". + */ + if (xmlSchemaPGetBoolNodeValue(ctxt, + NULL, (xmlNodePtr) attr)) + type->flags |= XML_SCHEMAS_TYPE_MIXED; + } else if (topLevel) { + /* + * Attributes of global complex type definitions. + */ + if (xmlStrEqual(attr->name, BAD_CAST "name")) { + /* Pass. */ + } else if (xmlStrEqual(attr->name, BAD_CAST "abstract")) { + /* + * Attribute "abstract". + */ + if (xmlSchemaPGetBoolNodeValue(ctxt, + NULL, (xmlNodePtr) attr)) + type->flags |= XML_SCHEMAS_TYPE_ABSTRACT; + } else if (xmlStrEqual(attr->name, BAD_CAST "final")) { + /* + * Attribute "final". + */ + attrValue = xmlSchemaGetNodeContent(ctxt, + (xmlNodePtr) attr); + if (xmlSchemaPValAttrBlockFinal(attrValue, + &(type->flags), + -1, + XML_SCHEMAS_TYPE_FINAL_EXTENSION, + XML_SCHEMAS_TYPE_FINAL_RESTRICTION, + -1, -1, -1) != 0) + { + xmlSchemaPSimpleTypeErr(ctxt, + XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, + NULL, (xmlNodePtr) attr, NULL, + "(#all | List of (extension | restriction))", + attrValue, NULL, NULL, NULL); + } else + final = 1; + } else if (xmlStrEqual(attr->name, BAD_CAST "block")) { + /* + * Attribute "block". + */ + attrValue = xmlSchemaGetNodeContent(ctxt, + (xmlNodePtr) attr); + if (xmlSchemaPValAttrBlockFinal(attrValue, &(type->flags), + -1, + XML_SCHEMAS_TYPE_BLOCK_EXTENSION, + XML_SCHEMAS_TYPE_BLOCK_RESTRICTION, + -1, -1, -1) != 0) { + xmlSchemaPSimpleTypeErr(ctxt, + XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, + NULL, (xmlNodePtr) attr, NULL, + "(#all | List of (extension | restriction)) ", + attrValue, NULL, NULL, NULL); + } else + block = 1; + } else { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + } else if (xmlStrEqual(attr->ns->href, xmlSchemaNs)) { + xmlSchemaPIllegalAttrErr(ctxt, + XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, NULL, attr); + } + attr = attr->next; + } + if (! block) { + /* + * Apply default "block" values. + */ + if (schema->flags & XML_SCHEMAS_BLOCK_DEFAULT_RESTRICTION) + type->flags |= XML_SCHEMAS_TYPE_BLOCK_RESTRICTION; + if (schema->flags & XML_SCHEMAS_BLOCK_DEFAULT_EXTENSION) + type->flags |= XML_SCHEMAS_TYPE_BLOCK_EXTENSION; + } + if (! final) { + /* + * Apply default "block" values. + */ + if (schema->flags & XML_SCHEMAS_FINAL_DEFAULT_RESTRICTION) + type->flags |= XML_SCHEMAS_TYPE_FINAL_RESTRICTION; + if (schema->flags & XML_SCHEMAS_FINAL_DEFAULT_EXTENSION) + type->flags |= XML_SCHEMAS_TYPE_FINAL_EXTENSION; + } + /* + * And now for the children... + */ + child = node->children; + if (IS_SCHEMA(child, "annotation")) { + type->annot = xmlSchemaParseAnnotation(ctxt, child, 1); + child = child->next; + } + ctxt->ctxtType = type; + if (IS_SCHEMA(child, "simpleContent")) { + /* + * ... + * 3.4.3 : 2.2 + * Specifying mixed='true' when the + * alternative is chosen has no effect + */ + if (type->flags & XML_SCHEMAS_TYPE_MIXED) + type->flags ^= XML_SCHEMAS_TYPE_MIXED; + xmlSchemaParseSimpleContent(ctxt, schema, child, + &hasRestrictionOrExtension); + child = child->next; + } else if (IS_SCHEMA(child, "complexContent")) { + /* + * ... + */ + type->contentType = XML_SCHEMA_CONTENT_EMPTY; + xmlSchemaParseComplexContent(ctxt, schema, child, + &hasRestrictionOrExtension); + child = child->next; + } else { + /* + * E.g ... or ... etc. + * + * SPEC + * "...the third alternative (neither nor + * ) is chosen. This case is understood as shorthand + * for complex content restricting the ·ur-type definition·, and the + * details of the mappings should be modified as necessary. + */ + type->baseType = xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYTYPE); + type->flags |= XML_SCHEMAS_TYPE_DERIVATION_METHOD_RESTRICTION; + /* + * Parse model groups. + */ + if (IS_SCHEMA(child, "all")) { + type->subtypes = (xmlSchemaTypePtr) + xmlSchemaParseModelGroup(ctxt, schema, child, + XML_SCHEMA_TYPE_ALL, 1); + child = child->next; + } else if (IS_SCHEMA(child, "choice")) { + type->subtypes = (xmlSchemaTypePtr) + xmlSchemaParseModelGroup(ctxt, schema, child, + XML_SCHEMA_TYPE_CHOICE, 1); + child = child->next; + } else if (IS_SCHEMA(child, "sequence")) { + type->subtypes = (xmlSchemaTypePtr) + xmlSchemaParseModelGroup(ctxt, schema, child, + XML_SCHEMA_TYPE_SEQUENCE, 1); + child = child->next; + } else if (IS_SCHEMA(child, "group")) { + type->subtypes = (xmlSchemaTypePtr) + xmlSchemaParseModelGroupDefRef(ctxt, schema, child); + /* + * Note that the reference will be resolved in + * xmlSchemaResolveTypeReferences(); + */ + child = child->next; + } + /* + * Parse attribute decls/refs. + */ + if (xmlSchemaParseLocalAttributes(ctxt, schema, &child, + (xmlSchemaItemListPtr *) &(type->attrUses), + XML_SCHEMA_TYPE_RESTRICTION, NULL) == -1) + return(NULL); + /* + * Parse attribute wildcard. + */ + if (IS_SCHEMA(child, "anyAttribute")) { + type->attributeWildcard = xmlSchemaParseAnyAttribute(ctxt, schema, child); + child = child->next; + } + } + if (child != NULL) { + xmlSchemaPContentErr(ctxt, + XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, + NULL, node, child, + NULL, "(annotation?, (simpleContent | complexContent | " + "((group | all | choice | sequence)?, ((attribute | " + "attributeGroup)*, anyAttribute?))))"); + } + /* + * REDEFINE: SPEC src-redefine (5) + */ + if (topLevel && ctxt->isRedefine && (! hasRestrictionOrExtension)) { + xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_SRC_REDEFINE, + NULL, node, "This is a redefinition, thus the " + " must have a or " + "grand-child", NULL); + } + ctxt->ctxtType = ctxtType; + return (type); +} + +/************************************************************************ + * * + * Validating using Schemas * + * * + ************************************************************************/ + +/************************************************************************ + * * + * Reading/Writing Schemas * + * * + ************************************************************************/ + +#if 0 /* Will be enabled if it is clear what options are needed. */ +/** + * xmlSchemaParserCtxtSetOptions: + * @ctxt: a schema parser context + * @options: a combination of xmlSchemaParserOption + * + * Sets the options to be used during the parse. + * + * Returns 0 in case of success, -1 in case of an + * API error. + */ +static int +xmlSchemaParserCtxtSetOptions(xmlSchemaParserCtxtPtr ctxt, + int options) + +{ + int i; + + if (ctxt == NULL) + return (-1); + /* + * WARNING: Change the start value if adding to the + * xmlSchemaParseOption. + */ + for (i = 1; i < (int) sizeof(int) * 8; i++) { + if (options & 1<options = options; + return (0); +} + +/** + * xmlSchemaValidCtxtGetOptions: + * @ctxt: a schema parser context + * + * Returns the option combination of the parser context. + */ +static int +xmlSchemaParserCtxtGetOptions(xmlSchemaParserCtxtPtr ctxt) + +{ + if (ctxt == NULL) + return (-1); + else + return (ctxt->options); +} +#endif + +/** + * xmlSchemaNewParserCtxt: + * @URL: the location of the schema + * + * Create an XML Schemas parse context for that file/resource expected + * to contain an XML Schemas file. + * + * Returns the parser context or NULL in case of error + */ +xmlSchemaParserCtxtPtr +xmlSchemaNewParserCtxt(const char *URL) +{ + xmlSchemaParserCtxtPtr ret; + + if (URL == NULL) + return (NULL); + + ret = xmlSchemaParserCtxtCreate(); + if (ret == NULL) + return(NULL); + ret->dict = xmlDictCreate(); + ret->URL = xmlDictLookup(ret->dict, (const xmlChar *) URL, -1); + return (ret); +} + +/** + * xmlSchemaNewMemParserCtxt: + * @buffer: a pointer to a char array containing the schemas + * @size: the size of the array + * + * Create an XML Schemas parse context for that memory buffer expected + * to contain an XML Schemas file. + * + * Returns the parser context or NULL in case of error + */ +xmlSchemaParserCtxtPtr +xmlSchemaNewMemParserCtxt(const char *buffer, int size) +{ + xmlSchemaParserCtxtPtr ret; + + if ((buffer == NULL) || (size <= 0)) + return (NULL); + ret = xmlSchemaParserCtxtCreate(); + if (ret == NULL) + return(NULL); + ret->buffer = buffer; + ret->size = size; + ret->dict = xmlDictCreate(); + return (ret); +} + +/** + * xmlSchemaNewDocParserCtxt: + * @doc: a preparsed document tree + * + * Create an XML Schemas parse context for that document. + * NB. The document may be modified during the parsing process. + * + * Returns the parser context or NULL in case of error + */ +xmlSchemaParserCtxtPtr +xmlSchemaNewDocParserCtxt(xmlDocPtr doc) +{ + xmlSchemaParserCtxtPtr ret; + + if (doc == NULL) + return (NULL); + ret = xmlSchemaParserCtxtCreate(); + if (ret == NULL) + return(NULL); + ret->doc = doc; + ret->dict = xmlDictCreate(); + /* The application has responsibility for the document */ + ret->preserve = 1; + + return (ret); +} + +/** + * xmlSchemaFreeParserCtxt: + * @ctxt: the schema parser context + * + * Free the resources associated to the schema parser context + */ +void +xmlSchemaFreeParserCtxt(xmlSchemaParserCtxtPtr ctxt) +{ + if (ctxt == NULL) + return; + if (ctxt->doc != NULL && !ctxt->preserve) + xmlFreeDoc(ctxt->doc); + if (ctxt->vctxt != NULL) { + xmlSchemaFreeValidCtxt(ctxt->vctxt); + } + if (ctxt->ownsConstructor && (ctxt->constructor != NULL)) { + xmlSchemaConstructionCtxtFree(ctxt->constructor); + ctxt->constructor = NULL; + ctxt->ownsConstructor = 0; + } + if (ctxt->attrProhibs != NULL) + xmlSchemaItemListFree(ctxt->attrProhibs); + xmlDictFree(ctxt->dict); + xmlFree(ctxt); +} + +/************************************************************************ + * * + * Building the content models * + * * + ************************************************************************/ + +static void +xmlSchemaBuildContentModelForSubstGroup(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaParticlePtr particle, int counter, xmlAutomataStatePtr end) +{ + xmlAutomataStatePtr start, tmp; + xmlSchemaElementPtr elemDecl, member; + xmlSchemaSubstGroupPtr substGroup; + int i; + + elemDecl = (xmlSchemaElementPtr) particle->children; + /* + * Wrap the substitution group with a CHOICE. + */ + start = pctxt->state; + if (end == NULL) + end = xmlAutomataNewState(pctxt->am); + substGroup = xmlSchemaSubstGroupGet(pctxt, elemDecl); + if (substGroup == NULL) { + xmlSchemaPErr(pctxt, WXS_ITEM_NODE(particle), + XML_SCHEMAP_INTERNAL, + "Internal error: xmlSchemaBuildContentModelForSubstGroup, " + "declaration is marked having a subst. group but none " + "available.\n", elemDecl->name, NULL); + return; + } + if (counter >= 0) { + /* + * NOTE that we put the declaration in, even if it's abstract. + * However, an error will be raised during *validation* if an element + * information item shall be validated against an abstract element + * declaration. + */ + tmp = xmlAutomataNewCountedTrans(pctxt->am, start, NULL, counter); + xmlAutomataNewTransition2(pctxt->am, tmp, end, + elemDecl->name, elemDecl->targetNamespace, elemDecl); + /* + * Add subst. group members. + */ + for (i = 0; i < substGroup->members->nbItems; i++) { + member = (xmlSchemaElementPtr) substGroup->members->items[i]; + xmlAutomataNewTransition2(pctxt->am, tmp, end, + member->name, member->targetNamespace, member); + } + } else if (particle->maxOccurs == 1) { + /* + * NOTE that we put the declaration in, even if it's abstract, + */ + xmlAutomataNewEpsilon(pctxt->am, + xmlAutomataNewTransition2(pctxt->am, + start, NULL, + elemDecl->name, elemDecl->targetNamespace, elemDecl), end); + /* + * Add subst. group members. + */ + for (i = 0; i < substGroup->members->nbItems; i++) { + member = (xmlSchemaElementPtr) substGroup->members->items[i]; + /* + * NOTE: This fixes bug #341150. xmlAutomataNewOnceTrans2() + * was incorrectly used instead of xmlAutomataNewTransition2() + * (seems like a copy&paste bug from the XML_SCHEMA_TYPE_ALL + * section in xmlSchemaBuildAContentModel() ). + * TODO: Check if xmlAutomataNewOnceTrans2() was instead + * intended for the above "counter" section originally. I.e., + * check xs:all with subst-groups. + * + * tmp = xmlAutomataNewOnceTrans2(pctxt->am, start, NULL, + * member->name, member->targetNamespace, + * 1, 1, member); + */ + tmp = xmlAutomataNewTransition2(pctxt->am, start, NULL, + member->name, member->targetNamespace, member); + xmlAutomataNewEpsilon(pctxt->am, tmp, end); + } + } else { + xmlAutomataStatePtr hop; + int maxOccurs = particle->maxOccurs == UNBOUNDED ? + UNBOUNDED : particle->maxOccurs - 1; + int minOccurs = particle->minOccurs < 1 ? 0 : particle->minOccurs - 1; + + counter = + xmlAutomataNewCounter(pctxt->am, minOccurs, + maxOccurs); + hop = xmlAutomataNewState(pctxt->am); + + xmlAutomataNewEpsilon(pctxt->am, + xmlAutomataNewTransition2(pctxt->am, + start, NULL, + elemDecl->name, elemDecl->targetNamespace, elemDecl), + hop); + /* + * Add subst. group members. + */ + for (i = 0; i < substGroup->members->nbItems; i++) { + member = (xmlSchemaElementPtr) substGroup->members->items[i]; + xmlAutomataNewEpsilon(pctxt->am, + xmlAutomataNewTransition2(pctxt->am, + start, NULL, + member->name, member->targetNamespace, member), + hop); + } + xmlAutomataNewCountedTrans(pctxt->am, hop, start, counter); + xmlAutomataNewCounterTrans(pctxt->am, hop, end, counter); + } + if (particle->minOccurs == 0) + xmlAutomataNewEpsilon(pctxt->am, start, end); + pctxt->state = end; +} + +static void +xmlSchemaBuildContentModelForElement(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaParticlePtr particle) +{ + if (((xmlSchemaElementPtr) particle->children)->flags & + XML_SCHEMAS_ELEM_SUBST_GROUP_HEAD) { + /* + * Substitution groups. + */ + xmlSchemaBuildContentModelForSubstGroup(ctxt, particle, -1, NULL); + } else { + xmlSchemaElementPtr elemDecl; + xmlAutomataStatePtr start; + + elemDecl = (xmlSchemaElementPtr) particle->children; + + if (elemDecl->flags & XML_SCHEMAS_ELEM_ABSTRACT) + return; + if (particle->maxOccurs == 1) { + start = ctxt->state; + ctxt->state = xmlAutomataNewTransition2(ctxt->am, start, NULL, + elemDecl->name, elemDecl->targetNamespace, elemDecl); + } else if ((particle->maxOccurs >= UNBOUNDED) && + (particle->minOccurs < 2)) { + /* Special case. */ + start = ctxt->state; + ctxt->state = xmlAutomataNewTransition2(ctxt->am, start, NULL, + elemDecl->name, elemDecl->targetNamespace, elemDecl); + ctxt->state = xmlAutomataNewTransition2(ctxt->am, ctxt->state, ctxt->state, + elemDecl->name, elemDecl->targetNamespace, elemDecl); + } else { + int counter; + int maxOccurs = particle->maxOccurs == UNBOUNDED ? + UNBOUNDED : particle->maxOccurs - 1; + int minOccurs = particle->minOccurs < 1 ? + 0 : particle->minOccurs - 1; + + start = xmlAutomataNewEpsilon(ctxt->am, ctxt->state, NULL); + counter = xmlAutomataNewCounter(ctxt->am, minOccurs, maxOccurs); + ctxt->state = xmlAutomataNewTransition2(ctxt->am, start, NULL, + elemDecl->name, elemDecl->targetNamespace, elemDecl); + xmlAutomataNewCountedTrans(ctxt->am, ctxt->state, start, counter); + ctxt->state = xmlAutomataNewCounterTrans(ctxt->am, ctxt->state, + NULL, counter); + } + if (particle->minOccurs == 0) + xmlAutomataNewEpsilon(ctxt->am, start, ctxt->state); + } +} + +/** + * xmlSchemaBuildAContentModel: + * @ctxt: the schema parser context + * @particle: the particle component + * @name: the complex type's name whose content is being built + * + * Create the automaton for the {content type} of a complex type. + * + */ +static void +xmlSchemaBuildAContentModel(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaParticlePtr particle) +{ + if (particle == NULL) { + PERROR_INT("xmlSchemaBuildAContentModel", "particle is NULL"); + return; + } + if (particle->children == NULL) { + /* + * Just return in this case. A missing "term" of the particle + * might arise due to an invalid "term" component. + */ + return; + } + + switch (particle->children->type) { + case XML_SCHEMA_TYPE_ANY: { + xmlAutomataStatePtr start, end; + xmlSchemaWildcardPtr wild; + xmlSchemaWildcardNsPtr ns; + + wild = (xmlSchemaWildcardPtr) particle->children; + + start = pctxt->state; + end = xmlAutomataNewState(pctxt->am); + + if (particle->maxOccurs == 1) { + if (wild->any == 1) { + /* + * We need to add both transitions: + * + * 1. the {"*", "*"} for elements in a namespace. + */ + pctxt->state = + xmlAutomataNewTransition2(pctxt->am, + start, NULL, BAD_CAST "*", BAD_CAST "*", wild); + xmlAutomataNewEpsilon(pctxt->am, pctxt->state, end); + /* + * 2. the {"*"} for elements in no namespace. + */ + pctxt->state = + xmlAutomataNewTransition2(pctxt->am, + start, NULL, BAD_CAST "*", NULL, wild); + xmlAutomataNewEpsilon(pctxt->am, pctxt->state, end); + + } else if (wild->nsSet != NULL) { + ns = wild->nsSet; + do { + pctxt->state = start; + pctxt->state = xmlAutomataNewTransition2(pctxt->am, + pctxt->state, NULL, BAD_CAST "*", ns->value, wild); + xmlAutomataNewEpsilon(pctxt->am, pctxt->state, end); + ns = ns->next; + } while (ns != NULL); + + } else if (wild->negNsSet != NULL) { + pctxt->state = xmlAutomataNewNegTrans(pctxt->am, + start, end, BAD_CAST "*", wild->negNsSet->value, + wild); + } + } else { + int counter; + xmlAutomataStatePtr hop; + int maxOccurs = + particle->maxOccurs == UNBOUNDED ? UNBOUNDED : particle->maxOccurs - 1; + int minOccurs = + particle->minOccurs < 1 ? 0 : particle->minOccurs - 1; + + counter = xmlAutomataNewCounter(pctxt->am, minOccurs, maxOccurs); + hop = xmlAutomataNewState(pctxt->am); + if (wild->any == 1) { + pctxt->state = + xmlAutomataNewTransition2(pctxt->am, + start, NULL, BAD_CAST "*", BAD_CAST "*", wild); + xmlAutomataNewEpsilon(pctxt->am, pctxt->state, hop); + pctxt->state = + xmlAutomataNewTransition2(pctxt->am, + start, NULL, BAD_CAST "*", NULL, wild); + xmlAutomataNewEpsilon(pctxt->am, pctxt->state, hop); + } else if (wild->nsSet != NULL) { + ns = wild->nsSet; + do { + pctxt->state = + xmlAutomataNewTransition2(pctxt->am, + start, NULL, BAD_CAST "*", ns->value, wild); + xmlAutomataNewEpsilon(pctxt->am, pctxt->state, hop); + ns = ns->next; + } while (ns != NULL); + + } else if (wild->negNsSet != NULL) { + pctxt->state = xmlAutomataNewNegTrans(pctxt->am, + start, hop, BAD_CAST "*", wild->negNsSet->value, + wild); + } + xmlAutomataNewCountedTrans(pctxt->am, hop, start, counter); + xmlAutomataNewCounterTrans(pctxt->am, hop, end, counter); + } + if (particle->minOccurs == 0) { + xmlAutomataNewEpsilon(pctxt->am, start, end); + } + pctxt->state = end; + break; + } + case XML_SCHEMA_TYPE_ELEMENT: + xmlSchemaBuildContentModelForElement(pctxt, particle); + break; + case XML_SCHEMA_TYPE_SEQUENCE:{ + xmlSchemaTreeItemPtr sub; + + /* + * If max and min occurances are default (1) then + * simply iterate over the particles of the . + */ + if ((particle->minOccurs == 1) && (particle->maxOccurs == 1)) { + sub = particle->children->children; + while (sub != NULL) { + xmlSchemaBuildAContentModel(pctxt, + (xmlSchemaParticlePtr) sub); + sub = sub->next; + } + } else { + xmlAutomataStatePtr oldstate = pctxt->state; + + if (particle->maxOccurs >= UNBOUNDED) { + if (particle->minOccurs > 1) { + xmlAutomataStatePtr tmp; + int counter; + + pctxt->state = xmlAutomataNewEpsilon(pctxt->am, + oldstate, NULL); + oldstate = pctxt->state; + + counter = xmlAutomataNewCounter(pctxt->am, + particle->minOccurs - 1, UNBOUNDED); + + sub = particle->children->children; + while (sub != NULL) { + xmlSchemaBuildAContentModel(pctxt, + (xmlSchemaParticlePtr) sub); + sub = sub->next; + } + tmp = pctxt->state; + xmlAutomataNewCountedTrans(pctxt->am, tmp, + oldstate, counter); + pctxt->state = + xmlAutomataNewCounterTrans(pctxt->am, tmp, + NULL, counter); + + } else { + pctxt->state = xmlAutomataNewEpsilon(pctxt->am, + oldstate, NULL); + oldstate = pctxt->state; + + sub = particle->children->children; + while (sub != NULL) { + xmlSchemaBuildAContentModel(pctxt, + (xmlSchemaParticlePtr) sub); + sub = sub->next; + } + xmlAutomataNewEpsilon(pctxt->am, pctxt->state, + oldstate); + /* + * epsilon needed to block previous trans from + * being allowed to enter back from another + * construct + */ + pctxt->state = xmlAutomataNewEpsilon(pctxt->am, + pctxt->state, NULL); + if (particle->minOccurs == 0) { + xmlAutomataNewEpsilon(pctxt->am, + oldstate, pctxt->state); + } + } + } else if ((particle->maxOccurs > 1) + || (particle->minOccurs > 1)) { + xmlAutomataStatePtr tmp; + int counter; + + pctxt->state = xmlAutomataNewEpsilon(pctxt->am, + oldstate, NULL); + oldstate = pctxt->state; + + counter = xmlAutomataNewCounter(pctxt->am, + particle->minOccurs - 1, + particle->maxOccurs - 1); + + sub = particle->children->children; + while (sub != NULL) { + xmlSchemaBuildAContentModel(pctxt, + (xmlSchemaParticlePtr) sub); + sub = sub->next; + } + tmp = pctxt->state; + xmlAutomataNewCountedTrans(pctxt->am, + tmp, oldstate, counter); + pctxt->state = + xmlAutomataNewCounterTrans(pctxt->am, tmp, NULL, + counter); + if (particle->minOccurs == 0) { + xmlAutomataNewEpsilon(pctxt->am, + oldstate, pctxt->state); + } + } else { + sub = particle->children->children; + while (sub != NULL) { + xmlSchemaBuildAContentModel(pctxt, + (xmlSchemaParticlePtr) sub); + sub = sub->next; + } + if (particle->minOccurs == 0) { + xmlAutomataNewEpsilon(pctxt->am, oldstate, + pctxt->state); + } + } + } + break; + } + case XML_SCHEMA_TYPE_CHOICE:{ + xmlSchemaTreeItemPtr sub; + xmlAutomataStatePtr start, end; + + start = pctxt->state; + end = xmlAutomataNewState(pctxt->am); + + /* + * iterate over the subtypes and remerge the end with an + * epsilon transition + */ + if (particle->maxOccurs == 1) { + sub = particle->children->children; + while (sub != NULL) { + pctxt->state = start; + xmlSchemaBuildAContentModel(pctxt, + (xmlSchemaParticlePtr) sub); + xmlAutomataNewEpsilon(pctxt->am, pctxt->state, end); + sub = sub->next; + } + } else { + int counter; + xmlAutomataStatePtr hop, base; + int maxOccurs = particle->maxOccurs == UNBOUNDED ? + UNBOUNDED : particle->maxOccurs - 1; + int minOccurs = + particle->minOccurs < 1 ? 0 : particle->minOccurs - 1; + + /* + * use a counter to keep track of the number of transtions + * which went through the choice. + */ + counter = + xmlAutomataNewCounter(pctxt->am, minOccurs, maxOccurs); + hop = xmlAutomataNewState(pctxt->am); + base = xmlAutomataNewState(pctxt->am); + + sub = particle->children->children; + while (sub != NULL) { + pctxt->state = base; + xmlSchemaBuildAContentModel(pctxt, + (xmlSchemaParticlePtr) sub); + xmlAutomataNewEpsilon(pctxt->am, pctxt->state, hop); + sub = sub->next; + } + xmlAutomataNewEpsilon(pctxt->am, start, base); + xmlAutomataNewCountedTrans(pctxt->am, hop, base, counter); + xmlAutomataNewCounterTrans(pctxt->am, hop, end, counter); + } + if (particle->minOccurs == 0) { + xmlAutomataNewEpsilon(pctxt->am, start, end); + } + pctxt->state = end; + break; + } + case XML_SCHEMA_TYPE_ALL:{ + xmlAutomataStatePtr start; + xmlSchemaParticlePtr sub; + xmlSchemaElementPtr elemDecl; + int lax; + + sub = (xmlSchemaParticlePtr) particle->children->children; + if (sub == NULL) + break; + start = pctxt->state; + while (sub != NULL) { + pctxt->state = start; + + elemDecl = (xmlSchemaElementPtr) sub->children; + if (elemDecl == NULL) { + PERROR_INT("xmlSchemaBuildAContentModel", + " particle has no term"); + return; + }; + /* + * NOTE: The {max occurs} of all the particles in the + * {particles} of the group must be 0 or 1; this is + * already ensured during the parse of the content of + * . + */ + if (elemDecl->flags & XML_SCHEMAS_ELEM_SUBST_GROUP_HEAD) { + int counter; + + /* + * This is an abstract group, we need to share + * the same counter for all the element transitions + * derived from the group + */ + counter = xmlAutomataNewCounter(pctxt->am, + sub->minOccurs, sub->maxOccurs); + xmlSchemaBuildContentModelForSubstGroup(pctxt, + sub, counter, pctxt->state); + } else { + if ((sub->minOccurs == 1) && + (sub->maxOccurs == 1)) { + xmlAutomataNewOnceTrans2(pctxt->am, pctxt->state, + pctxt->state, + elemDecl->name, + elemDecl->targetNamespace, + 1, 1, elemDecl); + } else if ((sub->minOccurs == 0) && + (sub->maxOccurs == 1)) { + + xmlAutomataNewCountTrans2(pctxt->am, pctxt->state, + pctxt->state, + elemDecl->name, + elemDecl->targetNamespace, + 0, + 1, + elemDecl); + } + } + sub = (xmlSchemaParticlePtr) sub->next; + } + lax = particle->minOccurs == 0; + pctxt->state = + xmlAutomataNewAllTrans(pctxt->am, pctxt->state, NULL, lax); + break; + } + case XML_SCHEMA_TYPE_GROUP: + /* + * If we hit a model group definition, then this means that + * it was empty, thus was not substituted for the containing + * model group. Just do nothing in this case. + * TODO: But the group should be substituted and not occur at + * all in the content model at this point. Fix this. + */ + break; + default: + xmlSchemaInternalErr2(ACTXT_CAST pctxt, + "xmlSchemaBuildAContentModel", + "found unexpected term of type '%s' in content model", + WXS_ITEM_TYPE_NAME(particle->children), NULL); + return; + } +} + +/** + * xmlSchemaBuildContentModel: + * @ctxt: the schema parser context + * @type: the complex type definition + * @name: the element name + * + * Builds the content model of the complex type. + */ +static void +xmlSchemaBuildContentModel(xmlSchemaTypePtr type, + xmlSchemaParserCtxtPtr ctxt) +{ + if ((type->type != XML_SCHEMA_TYPE_COMPLEX) || + (type->contModel != NULL) || + ((type->contentType != XML_SCHEMA_CONTENT_ELEMENTS) && + (type->contentType != XML_SCHEMA_CONTENT_MIXED))) + return; + +#ifdef DEBUG_CONTENT + xmlGenericError(xmlGenericErrorContext, + "Building content model for %s\n", name); +#endif + ctxt->am = NULL; + ctxt->am = xmlNewAutomata(); + if (ctxt->am == NULL) { + xmlGenericError(xmlGenericErrorContext, + "Cannot create automata for complex type %s\n", type->name); + return; + } + ctxt->state = xmlAutomataGetInitState(ctxt->am); + /* + * Build the automaton. + */ + xmlSchemaBuildAContentModel(ctxt, WXS_TYPE_PARTICLE(type)); + xmlAutomataSetFinalState(ctxt->am, ctxt->state); + type->contModel = xmlAutomataCompile(ctxt->am); + if (type->contModel == NULL) { + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_INTERNAL, + WXS_BASIC_CAST type, type->node, + "Failed to compile the content model", NULL); + } else if (xmlRegexpIsDeterminist(type->contModel) != 1) { + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_NOT_DETERMINISTIC, + /* XML_SCHEMAS_ERR_NOTDETERMINIST, */ + WXS_BASIC_CAST type, type->node, + "The content model is not determinist", NULL); + } else { +#ifdef DEBUG_CONTENT_REGEXP + xmlGenericError(xmlGenericErrorContext, + "Content model of %s:\n", type->name); + xmlRegexpPrint(stderr, type->contModel); +#endif + } + ctxt->state = NULL; + xmlFreeAutomata(ctxt->am); + ctxt->am = NULL; +} + +/** + * xmlSchemaResolveElementReferences: + * @elem: the schema element context + * @ctxt: the schema parser context + * + * Resolves the references of an element declaration + * or particle, which has an element declaration as it's + * term. + */ +static void +xmlSchemaResolveElementReferences(xmlSchemaElementPtr elemDecl, + xmlSchemaParserCtxtPtr ctxt) +{ + if ((ctxt == NULL) || (elemDecl == NULL) || + ((elemDecl != NULL) && + (elemDecl->flags & XML_SCHEMAS_ELEM_INTERNAL_RESOLVED))) + return; + elemDecl->flags |= XML_SCHEMAS_ELEM_INTERNAL_RESOLVED; + + if ((elemDecl->subtypes == NULL) && (elemDecl->namedType != NULL)) { + xmlSchemaTypePtr type; + + /* (type definition) ... otherwise the type definition ·resolved· + * to by the ·actual value· of the type [attribute] ... + */ + type = xmlSchemaGetType(ctxt->schema, elemDecl->namedType, + elemDecl->namedTypeNs); + if (type == NULL) { + xmlSchemaPResCompAttrErr(ctxt, + XML_SCHEMAP_SRC_RESOLVE, + WXS_BASIC_CAST elemDecl, elemDecl->node, + "type", elemDecl->namedType, elemDecl->namedTypeNs, + XML_SCHEMA_TYPE_BASIC, "type definition"); + } else + elemDecl->subtypes = type; + } + if (elemDecl->substGroup != NULL) { + xmlSchemaElementPtr substHead; + + /* + * FIXME TODO: Do we need a new field in _xmlSchemaElement for + * substitutionGroup? + */ + substHead = xmlSchemaGetElem(ctxt->schema, elemDecl->substGroup, + elemDecl->substGroupNs); + if (substHead == NULL) { + xmlSchemaPResCompAttrErr(ctxt, + XML_SCHEMAP_SRC_RESOLVE, + WXS_BASIC_CAST elemDecl, NULL, + "substitutionGroup", elemDecl->substGroup, + elemDecl->substGroupNs, XML_SCHEMA_TYPE_ELEMENT, NULL); + } else { + xmlSchemaResolveElementReferences(substHead, ctxt); + /* + * Set the "substitution group affiliation". + * NOTE that now we use the "refDecl" field for this. + */ + WXS_SUBST_HEAD(elemDecl) = substHead; + /* + * The type definitions is set to: + * SPEC "...the {type definition} of the element + * declaration ·resolved· to by the ·actual value· + * of the substitutionGroup [attribute], if present" + */ + if (elemDecl->subtypes == NULL) + elemDecl->subtypes = substHead->subtypes; + } + } + /* + * SPEC "The definition of anyType serves as the default type definition + * for element declarations whose XML representation does not specify one." + */ + if ((elemDecl->subtypes == NULL) && + (elemDecl->namedType == NULL) && + (elemDecl->substGroup == NULL)) + elemDecl->subtypes = xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYTYPE); +} + +/** + * xmlSchemaResolveUnionMemberTypes: + * @ctxt: the schema parser context + * @type: the schema simple type definition + * + * Checks and builds the "member type definitions" property of the union + * simple type. This handles part (1), part (2) is done in + * xmlSchemaFinishMemberTypeDefinitionsProperty() + * + * Returns -1 in case of an internal error, 0 otherwise. + */ +static int +xmlSchemaResolveUnionMemberTypes(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaTypePtr type) +{ + + xmlSchemaTypeLinkPtr link, lastLink, newLink; + xmlSchemaTypePtr memberType; + + /* + * SPEC (1) "If the alternative is chosen, then [Definition:] + * define the explicit members as the type definitions ·resolved· + * to by the items in the ·actual value· of the memberTypes [attribute], + * if any, followed by the type definitions corresponding to the + * s among the [children] of , if any." + */ + /* + * Resolve references. + */ + link = type->memberTypes; + lastLink = NULL; + while (link != NULL) { + const xmlChar *name, *nsName; + + name = ((xmlSchemaQNameRefPtr) link->type)->name; + nsName = ((xmlSchemaQNameRefPtr) link->type)->targetNamespace; + + memberType = xmlSchemaGetType(ctxt->schema, name, nsName); + if ((memberType == NULL) || (! WXS_IS_SIMPLE(memberType))) { + xmlSchemaPResCompAttrErr(ctxt, XML_SCHEMAP_SRC_RESOLVE, + WXS_BASIC_CAST type, type->node, "memberTypes", + name, nsName, XML_SCHEMA_TYPE_SIMPLE, NULL); + /* + * Remove the member type link. + */ + if (lastLink == NULL) + type->memberTypes = link->next; + else + lastLink->next = link->next; + newLink = link; + link = link->next; + xmlFree(newLink); + } else { + link->type = memberType; + lastLink = link; + link = link->next; + } + } + /* + * Add local simple types, + */ + memberType = type->subtypes; + while (memberType != NULL) { + link = (xmlSchemaTypeLinkPtr) xmlMalloc(sizeof(xmlSchemaTypeLink)); + if (link == NULL) { + xmlSchemaPErrMemory(ctxt, "allocating a type link", NULL); + return (-1); + } + link->type = memberType; + link->next = NULL; + if (lastLink == NULL) + type->memberTypes = link; + else + lastLink->next = link; + lastLink = link; + memberType = memberType->next; + } + return (0); +} + +/** + * xmlSchemaIsDerivedFromBuiltInType: + * @ctxt: the schema parser context + * @type: the type definition + * @valType: the value type + * + * + * Returns 1 if the type has the given value type, or + * is derived from such a type. + */ +static int +xmlSchemaIsDerivedFromBuiltInType(xmlSchemaTypePtr type, int valType) +{ + if (type == NULL) + return (0); + if (WXS_IS_COMPLEX(type)) + return (0); + if (type->type == XML_SCHEMA_TYPE_BASIC) { + if (type->builtInType == valType) + return(1); + if ((type->builtInType == XML_SCHEMAS_ANYSIMPLETYPE) || + (type->builtInType == XML_SCHEMAS_ANYTYPE)) + return (0); + return(xmlSchemaIsDerivedFromBuiltInType(type->subtypes, valType)); + } + return(xmlSchemaIsDerivedFromBuiltInType(type->subtypes, valType)); +} + +#if 0 +/** + * xmlSchemaIsDerivedFromBuiltInType: + * @ctxt: the schema parser context + * @type: the type definition + * @valType: the value type + * + * + * Returns 1 if the type has the given value type, or + * is derived from such a type. + */ +static int +xmlSchemaIsUserDerivedFromBuiltInType(xmlSchemaTypePtr type, int valType) +{ + if (type == NULL) + return (0); + if (WXS_IS_COMPLEX(type)) + return (0); + if (type->type == XML_SCHEMA_TYPE_BASIC) { + if (type->builtInType == valType) + return(1); + return (0); + } else + return(xmlSchemaIsDerivedFromBuiltInType(type->subtypes, valType)); + + return (0); +} +#endif + +static xmlSchemaTypePtr +xmlSchemaQueryBuiltInType(xmlSchemaTypePtr type) +{ + if (type == NULL) + return (NULL); + if (WXS_IS_COMPLEX(type)) + return (NULL); + if (type->type == XML_SCHEMA_TYPE_BASIC) + return(type); + return(xmlSchemaQueryBuiltInType(type->subtypes)); +} + +/** + * xmlSchemaGetPrimitiveType: + * @type: the simpleType definition + * + * Returns the primitive type of the given type or + * NULL in case of error. + */ +static xmlSchemaTypePtr +xmlSchemaGetPrimitiveType(xmlSchemaTypePtr type) +{ + + while (type != NULL) { + /* + * Note that anySimpleType is actually not a primitive type + * but we need that here. + */ + if ((type->builtInType == XML_SCHEMAS_ANYSIMPLETYPE) || + (type->flags & XML_SCHEMAS_TYPE_BUILTIN_PRIMITIVE)) + return (type); + type = type->baseType; + } + + return (NULL); +} + +#if 0 +/** + * xmlSchemaGetBuiltInTypeAncestor: + * @type: the simpleType definition + * + * Returns the primitive type of the given type or + * NULL in case of error. + */ +static xmlSchemaTypePtr +xmlSchemaGetBuiltInTypeAncestor(xmlSchemaTypePtr type) +{ + if (WXS_IS_LIST(type) || WXS_IS_UNION(type)) + return (0); + while (type != NULL) { + if (type->type == XML_SCHEMA_TYPE_BASIC) + return (type); + type = type->baseType; + } + + return (NULL); +} +#endif + +/** + * xmlSchemaCloneWildcardNsConstraints: + * @ctxt: the schema parser context + * @dest: the destination wildcard + * @source: the source wildcard + * + * Clones the namespace constraints of source + * and assignes them to dest. + * Returns -1 on internal error, 0 otherwise. + */ +static int +xmlSchemaCloneWildcardNsConstraints(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaWildcardPtr dest, + xmlSchemaWildcardPtr source) +{ + xmlSchemaWildcardNsPtr cur, tmp, last; + + if ((source == NULL) || (dest == NULL)) + return(-1); + dest->any = source->any; + cur = source->nsSet; + last = NULL; + while (cur != NULL) { + tmp = xmlSchemaNewWildcardNsConstraint(ctxt); + if (tmp == NULL) + return(-1); + tmp->value = cur->value; + if (last == NULL) + dest->nsSet = tmp; + else + last->next = tmp; + last = tmp; + cur = cur->next; + } + if (dest->negNsSet != NULL) + xmlSchemaFreeWildcardNsSet(dest->negNsSet); + if (source->negNsSet != NULL) { + dest->negNsSet = xmlSchemaNewWildcardNsConstraint(ctxt); + if (dest->negNsSet == NULL) + return(-1); + dest->negNsSet->value = source->negNsSet->value; + } else + dest->negNsSet = NULL; + return(0); +} + +/** + * xmlSchemaUnionWildcards: + * @ctxt: the schema parser context + * @completeWild: the first wildcard + * @curWild: the second wildcard + * + * Unions the namespace constraints of the given wildcards. + * @completeWild will hold the resulting union. + * Returns a positive error code on failure, -1 in case of an + * internal error, 0 otherwise. + */ +static int +xmlSchemaUnionWildcards(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaWildcardPtr completeWild, + xmlSchemaWildcardPtr curWild) +{ + xmlSchemaWildcardNsPtr cur, curB, tmp; + + /* + * 1 If O1 and O2 are the same value, then that value must be the + * value. + */ + if ((completeWild->any == curWild->any) && + ((completeWild->nsSet == NULL) == (curWild->nsSet == NULL)) && + ((completeWild->negNsSet == NULL) == (curWild->negNsSet == NULL))) { + + if ((completeWild->negNsSet == NULL) || + (completeWild->negNsSet->value == curWild->negNsSet->value)) { + + if (completeWild->nsSet != NULL) { + int found = 0; + + /* + * Check equality of sets. + */ + cur = completeWild->nsSet; + while (cur != NULL) { + found = 0; + curB = curWild->nsSet; + while (curB != NULL) { + if (cur->value == curB->value) { + found = 1; + break; + } + curB = curB->next; + } + if (!found) + break; + cur = cur->next; + } + if (found) + return(0); + } else + return(0); + } + } + /* + * 2 If either O1 or O2 is any, then any must be the value + */ + if (completeWild->any != curWild->any) { + if (completeWild->any == 0) { + completeWild->any = 1; + if (completeWild->nsSet != NULL) { + xmlSchemaFreeWildcardNsSet(completeWild->nsSet); + completeWild->nsSet = NULL; + } + if (completeWild->negNsSet != NULL) { + xmlFree(completeWild->negNsSet); + completeWild->negNsSet = NULL; + } + } + return (0); + } + /* + * 3 If both O1 and O2 are sets of (namespace names or ·absent·), + * then the union of those sets must be the value. + */ + if ((completeWild->nsSet != NULL) && (curWild->nsSet != NULL)) { + int found; + xmlSchemaWildcardNsPtr start; + + cur = curWild->nsSet; + start = completeWild->nsSet; + while (cur != NULL) { + found = 0; + curB = start; + while (curB != NULL) { + if (cur->value == curB->value) { + found = 1; + break; + } + curB = curB->next; + } + if (!found) { + tmp = xmlSchemaNewWildcardNsConstraint(ctxt); + if (tmp == NULL) + return (-1); + tmp->value = cur->value; + tmp->next = completeWild->nsSet; + completeWild->nsSet = tmp; + } + cur = cur->next; + } + + return(0); + } + /* + * 4 If the two are negations of different values (namespace names + * or ·absent·), then a pair of not and ·absent· must be the value. + */ + if ((completeWild->negNsSet != NULL) && + (curWild->negNsSet != NULL) && + (completeWild->negNsSet->value != curWild->negNsSet->value)) { + completeWild->negNsSet->value = NULL; + + return(0); + } + /* + * 5. + */ + if (((completeWild->negNsSet != NULL) && + (completeWild->negNsSet->value != NULL) && + (curWild->nsSet != NULL)) || + ((curWild->negNsSet != NULL) && + (curWild->negNsSet->value != NULL) && + (completeWild->nsSet != NULL))) { + + int nsFound, absentFound = 0; + + if (completeWild->nsSet != NULL) { + cur = completeWild->nsSet; + curB = curWild->negNsSet; + } else { + cur = curWild->nsSet; + curB = completeWild->negNsSet; + } + nsFound = 0; + while (cur != NULL) { + if (cur->value == NULL) + absentFound = 1; + else if (cur->value == curB->value) + nsFound = 1; + if (nsFound && absentFound) + break; + cur = cur->next; + } + + if (nsFound && absentFound) { + /* + * 5.1 If the set S includes both the negated namespace + * name and ·absent·, then any must be the value. + */ + completeWild->any = 1; + if (completeWild->nsSet != NULL) { + xmlSchemaFreeWildcardNsSet(completeWild->nsSet); + completeWild->nsSet = NULL; + } + if (completeWild->negNsSet != NULL) { + xmlFree(completeWild->negNsSet); + completeWild->negNsSet = NULL; + } + } else if (nsFound && (!absentFound)) { + /* + * 5.2 If the set S includes the negated namespace name + * but not ·absent·, then a pair of not and ·absent· must + * be the value. + */ + if (completeWild->nsSet != NULL) { + xmlSchemaFreeWildcardNsSet(completeWild->nsSet); + completeWild->nsSet = NULL; + } + if (completeWild->negNsSet == NULL) { + completeWild->negNsSet = xmlSchemaNewWildcardNsConstraint(ctxt); + if (completeWild->negNsSet == NULL) + return (-1); + } + completeWild->negNsSet->value = NULL; + } else if ((!nsFound) && absentFound) { + /* + * 5.3 If the set S includes ·absent· but not the negated + * namespace name, then the union is not expressible. + */ + xmlSchemaPErr(ctxt, completeWild->node, + XML_SCHEMAP_UNION_NOT_EXPRESSIBLE, + "The union of the wilcard is not expressible.\n", + NULL, NULL); + return(XML_SCHEMAP_UNION_NOT_EXPRESSIBLE); + } else if ((!nsFound) && (!absentFound)) { + /* + * 5.4 If the set S does not include either the negated namespace + * name or ·absent·, then whichever of O1 or O2 is a pair of not + * and a namespace name must be the value. + */ + if (completeWild->negNsSet == NULL) { + if (completeWild->nsSet != NULL) { + xmlSchemaFreeWildcardNsSet(completeWild->nsSet); + completeWild->nsSet = NULL; + } + completeWild->negNsSet = xmlSchemaNewWildcardNsConstraint(ctxt); + if (completeWild->negNsSet == NULL) + return (-1); + completeWild->negNsSet->value = curWild->negNsSet->value; + } + } + return (0); + } + /* + * 6. + */ + if (((completeWild->negNsSet != NULL) && + (completeWild->negNsSet->value == NULL) && + (curWild->nsSet != NULL)) || + ((curWild->negNsSet != NULL) && + (curWild->negNsSet->value == NULL) && + (completeWild->nsSet != NULL))) { + + if (completeWild->nsSet != NULL) { + cur = completeWild->nsSet; + } else { + cur = curWild->nsSet; + } + while (cur != NULL) { + if (cur->value == NULL) { + /* + * 6.1 If the set S includes ·absent·, then any must be the + * value. + */ + completeWild->any = 1; + if (completeWild->nsSet != NULL) { + xmlSchemaFreeWildcardNsSet(completeWild->nsSet); + completeWild->nsSet = NULL; + } + if (completeWild->negNsSet != NULL) { + xmlFree(completeWild->negNsSet); + completeWild->negNsSet = NULL; + } + return (0); + } + cur = cur->next; + } + if (completeWild->negNsSet == NULL) { + /* + * 6.2 If the set S does not include ·absent·, then a pair of not + * and ·absent· must be the value. + */ + if (completeWild->nsSet != NULL) { + xmlSchemaFreeWildcardNsSet(completeWild->nsSet); + completeWild->nsSet = NULL; + } + completeWild->negNsSet = xmlSchemaNewWildcardNsConstraint(ctxt); + if (completeWild->negNsSet == NULL) + return (-1); + completeWild->negNsSet->value = NULL; + } + return (0); + } + return (0); + +} + +/** + * xmlSchemaIntersectWildcards: + * @ctxt: the schema parser context + * @completeWild: the first wildcard + * @curWild: the second wildcard + * + * Intersects the namespace constraints of the given wildcards. + * @completeWild will hold the resulting intersection. + * Returns a positive error code on failure, -1 in case of an + * internal error, 0 otherwise. + */ +static int +xmlSchemaIntersectWildcards(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaWildcardPtr completeWild, + xmlSchemaWildcardPtr curWild) +{ + xmlSchemaWildcardNsPtr cur, curB, prev, tmp; + + /* + * 1 If O1 and O2 are the same value, then that value must be the + * value. + */ + if ((completeWild->any == curWild->any) && + ((completeWild->nsSet == NULL) == (curWild->nsSet == NULL)) && + ((completeWild->negNsSet == NULL) == (curWild->negNsSet == NULL))) { + + if ((completeWild->negNsSet == NULL) || + (completeWild->negNsSet->value == curWild->negNsSet->value)) { + + if (completeWild->nsSet != NULL) { + int found = 0; + + /* + * Check equality of sets. + */ + cur = completeWild->nsSet; + while (cur != NULL) { + found = 0; + curB = curWild->nsSet; + while (curB != NULL) { + if (cur->value == curB->value) { + found = 1; + break; + } + curB = curB->next; + } + if (!found) + break; + cur = cur->next; + } + if (found) + return(0); + } else + return(0); + } + } + /* + * 2 If either O1 or O2 is any, then the other must be the value. + */ + if ((completeWild->any != curWild->any) && (completeWild->any)) { + if (xmlSchemaCloneWildcardNsConstraints(ctxt, completeWild, curWild) == -1) + return(-1); + return(0); + } + /* + * 3 If either O1 or O2 is a pair of not and a value (a namespace + * name or ·absent·) and the other is a set of (namespace names or + * ·absent·), then that set, minus the negated value if it was in + * the set, minus ·absent· if it was in the set, must be the value. + */ + if (((completeWild->negNsSet != NULL) && (curWild->nsSet != NULL)) || + ((curWild->negNsSet != NULL) && (completeWild->nsSet != NULL))) { + const xmlChar *neg; + + if (completeWild->nsSet == NULL) { + neg = completeWild->negNsSet->value; + if (xmlSchemaCloneWildcardNsConstraints(ctxt, completeWild, curWild) == -1) + return(-1); + } else + neg = curWild->negNsSet->value; + /* + * Remove absent and negated. + */ + prev = NULL; + cur = completeWild->nsSet; + while (cur != NULL) { + if (cur->value == NULL) { + if (prev == NULL) + completeWild->nsSet = cur->next; + else + prev->next = cur->next; + xmlFree(cur); + break; + } + prev = cur; + cur = cur->next; + } + if (neg != NULL) { + prev = NULL; + cur = completeWild->nsSet; + while (cur != NULL) { + if (cur->value == neg) { + if (prev == NULL) + completeWild->nsSet = cur->next; + else + prev->next = cur->next; + xmlFree(cur); + break; + } + prev = cur; + cur = cur->next; + } + } + + return(0); + } + /* + * 4 If both O1 and O2 are sets of (namespace names or ·absent·), + * then the intersection of those sets must be the value. + */ + if ((completeWild->nsSet != NULL) && (curWild->nsSet != NULL)) { + int found; + + cur = completeWild->nsSet; + prev = NULL; + while (cur != NULL) { + found = 0; + curB = curWild->nsSet; + while (curB != NULL) { + if (cur->value == curB->value) { + found = 1; + break; + } + curB = curB->next; + } + if (!found) { + if (prev == NULL) + completeWild->nsSet = cur->next; + else + prev->next = cur->next; + tmp = cur->next; + xmlFree(cur); + cur = tmp; + continue; + } + prev = cur; + cur = cur->next; + } + + return(0); + } + /* 5 If the two are negations of different namespace names, + * then the intersection is not expressible + */ + if ((completeWild->negNsSet != NULL) && + (curWild->negNsSet != NULL) && + (completeWild->negNsSet->value != curWild->negNsSet->value) && + (completeWild->negNsSet->value != NULL) && + (curWild->negNsSet->value != NULL)) { + + xmlSchemaPErr(ctxt, completeWild->node, XML_SCHEMAP_INTERSECTION_NOT_EXPRESSIBLE, + "The intersection of the wilcard is not expressible.\n", + NULL, NULL); + return(XML_SCHEMAP_INTERSECTION_NOT_EXPRESSIBLE); + } + /* + * 6 If the one is a negation of a namespace name and the other + * is a negation of ·absent·, then the one which is the negation + * of a namespace name must be the value. + */ + if ((completeWild->negNsSet != NULL) && (curWild->negNsSet != NULL) && + (completeWild->negNsSet->value != curWild->negNsSet->value) && + (completeWild->negNsSet->value == NULL)) { + completeWild->negNsSet->value = curWild->negNsSet->value; + } + return(0); +} + +/** + * xmlSchemaIsWildcardNsConstraintSubset: + * @ctxt: the schema parser context + * @sub: the first wildcard + * @super: the second wildcard + * + * Schema Component Constraint: Wildcard Subset (cos-ns-subset) + * + * Returns 0 if the namespace constraint of @sub is an intensional + * subset of @super, 1 otherwise. + */ +static int +xmlSchemaCheckCOSNSSubset(xmlSchemaWildcardPtr sub, + xmlSchemaWildcardPtr super) +{ + /* + * 1 super must be any. + */ + if (super->any) + return (0); + /* + * 2.1 sub must be a pair of not and a namespace name or ·absent·. + * 2.2 super must be a pair of not and the same value. + */ + if ((sub->negNsSet != NULL) && + (super->negNsSet != NULL) && + (sub->negNsSet->value == sub->negNsSet->value)) + return (0); + /* + * 3.1 sub must be a set whose members are either namespace names or ·absent·. + */ + if (sub->nsSet != NULL) { + /* + * 3.2.1 super must be the same set or a superset thereof. + */ + if (super->nsSet != NULL) { + xmlSchemaWildcardNsPtr cur, curB; + int found = 0; + + cur = sub->nsSet; + while (cur != NULL) { + found = 0; + curB = super->nsSet; + while (curB != NULL) { + if (cur->value == curB->value) { + found = 1; + break; + } + curB = curB->next; + } + if (!found) + return (1); + cur = cur->next; + } + if (found) + return (0); + } else if (super->negNsSet != NULL) { + xmlSchemaWildcardNsPtr cur; + /* + * 3.2.2 super must be a pair of not and a namespace name or + * ·absent· and that value must not be in sub's set. + */ + cur = sub->nsSet; + while (cur != NULL) { + if (cur->value == super->negNsSet->value) + return (1); + cur = cur->next; + } + return (0); + } + } + return (1); +} + +static int +xmlSchemaGetEffectiveValueConstraint(xmlSchemaAttributeUsePtr attruse, + int *fixed, + const xmlChar **value, + xmlSchemaValPtr *val) +{ + *fixed = 0; + *value = NULL; + if (val != 0) + *val = NULL; + + if (attruse->defValue != NULL) { + *value = attruse->defValue; + if (val != NULL) + *val = attruse->defVal; + if (attruse->flags & XML_SCHEMA_ATTR_USE_FIXED) + *fixed = 1; + return(1); + } else if ((attruse->attrDecl != NULL) && + (attruse->attrDecl->defValue != NULL)) { + *value = attruse->attrDecl->defValue; + if (val != NULL) + *val = attruse->attrDecl->defVal; + if (attruse->attrDecl->flags & XML_SCHEMAS_ATTR_FIXED) + *fixed = 1; + return(1); + } + return(0); +} +/** + * xmlSchemaCheckCVCWildcardNamespace: + * @wild: the wildcard + * @ns: the namespace + * + * Validation Rule: Wildcard allows Namespace Name + * (cvc-wildcard-namespace) + * + * Returns 0 if the given namespace matches the wildcard, + * 1 otherwise and -1 on API errors. + */ +static int +xmlSchemaCheckCVCWildcardNamespace(xmlSchemaWildcardPtr wild, + const xmlChar* ns) +{ + if (wild == NULL) + return(-1); + + if (wild->any) + return(0); + else if (wild->nsSet != NULL) { + xmlSchemaWildcardNsPtr cur; + + cur = wild->nsSet; + while (cur != NULL) { + if (xmlStrEqual(cur->value, ns)) + return(0); + cur = cur->next; + } + } else if ((wild->negNsSet != NULL) && (ns != NULL) && + (!xmlStrEqual(wild->negNsSet->value, ns))) + return(0); + + return(1); +} + +#define XML_SCHEMA_ACTION_DERIVE 0 +#define XML_SCHEMA_ACTION_REDEFINE 1 + +#define WXS_ACTION_STR(a) \ +((a) == XML_SCHEMA_ACTION_DERIVE) ? (const xmlChar *) "base" : (const xmlChar *) "redefined" + +/* +* Schema Component Constraint: +* Derivation Valid (Restriction, Complex) +* derivation-ok-restriction (2) - (4) +* +* ATTENTION: +* In XML Schema 1.1 this will be: +* Validation Rule: +* Checking complex type subsumption (practicalSubsumption) (1, 2 and 3) +* +*/ +static int +xmlSchemaCheckDerivationOKRestriction2to4(xmlSchemaParserCtxtPtr pctxt, + int action, + xmlSchemaBasicItemPtr item, + xmlSchemaBasicItemPtr baseItem, + xmlSchemaItemListPtr uses, + xmlSchemaItemListPtr baseUses, + xmlSchemaWildcardPtr wild, + xmlSchemaWildcardPtr baseWild) +{ + xmlSchemaAttributeUsePtr cur = NULL, bcur; + int i, j, found; /* err = 0; */ + const xmlChar *bEffValue; + int effFixed; + + if (uses != NULL) { + for (i = 0; i < uses->nbItems; i++) { + cur = uses->items[i]; + found = 0; + if (baseUses == NULL) + goto not_found; + for (j = 0; j < baseUses->nbItems; j++) { + bcur = baseUses->items[j]; + if ((WXS_ATTRUSE_DECL_NAME(cur) == + WXS_ATTRUSE_DECL_NAME(bcur)) && + (WXS_ATTRUSE_DECL_TNS(cur) == + WXS_ATTRUSE_DECL_TNS(bcur))) + { + /* + * (2.1) "If there is an attribute use in the {attribute + * uses} of the {base type definition} (call this B) whose + * {attribute declaration} has the same {name} and {target + * namespace}, then all of the following must be true:" + */ + found = 1; + + if ((cur->occurs == XML_SCHEMAS_ATTR_USE_OPTIONAL) && + (bcur->occurs == XML_SCHEMAS_ATTR_USE_REQUIRED)) + { + xmlChar *str = NULL; + /* + * (2.1.1) "one of the following must be true:" + * (2.1.1.1) "B's {required} is false." + * (2.1.1.2) "R's {required} is true." + */ + xmlSchemaPAttrUseErr4(pctxt, + XML_SCHEMAP_DERIVATION_OK_RESTRICTION_2_1_1, + WXS_ITEM_NODE(item), item, cur, + "The 'optional' attribute use is inconsistent " + "with the corresponding 'required' attribute use of " + "the %s %s", + WXS_ACTION_STR(action), + xmlSchemaGetComponentDesignation(&str, baseItem), + NULL, NULL); + FREE_AND_NULL(str); + /* err = pctxt->err; */ + } else if (xmlSchemaCheckCOSSTDerivedOK(ACTXT_CAST pctxt, + WXS_ATTRUSE_TYPEDEF(cur), + WXS_ATTRUSE_TYPEDEF(bcur), 0) != 0) + { + xmlChar *strA = NULL, *strB = NULL, *strC = NULL; + + /* + * SPEC (2.1.2) "R's {attribute declaration}'s + * {type definition} must be validly derived from + * B's {type definition} given the empty set as + * defined in Type Derivation OK (Simple) (§3.14.6)." + */ + xmlSchemaPAttrUseErr4(pctxt, + XML_SCHEMAP_DERIVATION_OK_RESTRICTION_2_1_2, + WXS_ITEM_NODE(item), item, cur, + "The attribute declaration's %s " + "is not validly derived from " + "the corresponding %s of the " + "attribute declaration in the %s %s", + xmlSchemaGetComponentDesignation(&strA, + WXS_ATTRUSE_TYPEDEF(cur)), + xmlSchemaGetComponentDesignation(&strB, + WXS_ATTRUSE_TYPEDEF(bcur)), + WXS_ACTION_STR(action), + xmlSchemaGetComponentDesignation(&strC, baseItem)); + /* xmlSchemaGetComponentDesignation(&str, baseItem), */ + FREE_AND_NULL(strA); + FREE_AND_NULL(strB); + FREE_AND_NULL(strC); + /* err = pctxt->err; */ + } else { + /* + * 2.1.3 [Definition:] Let the effective value + * constraint of an attribute use be its {value + * constraint}, if present, otherwise its {attribute + * declaration}'s {value constraint} . + */ + xmlSchemaGetEffectiveValueConstraint(bcur, + &effFixed, &bEffValue, NULL); + /* + * 2.1.3 ... one of the following must be true + * + * 2.1.3.1 B's ·effective value constraint· is + * ·absent· or default. + */ + if ((bEffValue != NULL) && + (effFixed == 1)) { + const xmlChar *rEffValue = NULL; + + xmlSchemaGetEffectiveValueConstraint(bcur, + &effFixed, &rEffValue, NULL); + /* + * 2.1.3.2 R's ·effective value constraint· is + * fixed with the same string as B's. + * MAYBE TODO: Compare the computed values. + * Hmm, it says "same string" so + * string-equality might really be sufficient. + */ + if ((effFixed == 0) || + (! WXS_ARE_DEFAULT_STR_EQUAL(rEffValue, bEffValue))) + { + xmlChar *str = NULL; + + xmlSchemaPAttrUseErr4(pctxt, + XML_SCHEMAP_DERIVATION_OK_RESTRICTION_2_1_3, + WXS_ITEM_NODE(item), item, cur, + "The effective value constraint of the " + "attribute use is inconsistent with " + "its correspondent in the %s %s", + WXS_ACTION_STR(action), + xmlSchemaGetComponentDesignation(&str, + baseItem), + NULL, NULL); + FREE_AND_NULL(str); + /* err = pctxt->err; */ + } + } + } + break; + } + } +not_found: + if (!found) { + /* + * (2.2) "otherwise the {base type definition} must have an + * {attribute wildcard} and the {target namespace} of the + * R's {attribute declaration} must be ·valid· with respect + * to that wildcard, as defined in Wildcard allows Namespace + * Name (§3.10.4)." + */ + if ((baseWild == NULL) || + (xmlSchemaCheckCVCWildcardNamespace(baseWild, + (WXS_ATTRUSE_DECL(cur))->targetNamespace) != 0)) + { + xmlChar *str = NULL; + + xmlSchemaPAttrUseErr4(pctxt, + XML_SCHEMAP_DERIVATION_OK_RESTRICTION_2_2, + WXS_ITEM_NODE(item), item, cur, + "Neither a matching attribute use, " + "nor a matching wildcard exists in the %s %s", + WXS_ACTION_STR(action), + xmlSchemaGetComponentDesignation(&str, baseItem), + NULL, NULL); + FREE_AND_NULL(str); + /* err = pctxt->err; */ + } + } + } + } + /* + * SPEC derivation-ok-restriction (3): + * (3) "For each attribute use in the {attribute uses} of the {base type + * definition} whose {required} is true, there must be an attribute + * use with an {attribute declaration} with the same {name} and + * {target namespace} as its {attribute declaration} in the {attribute + * uses} of the complex type definition itself whose {required} is true. + */ + if (baseUses != NULL) { + for (j = 0; j < baseUses->nbItems; j++) { + bcur = baseUses->items[j]; + if (bcur->occurs != XML_SCHEMAS_ATTR_USE_REQUIRED) + continue; + found = 0; + if (uses != NULL) { + for (i = 0; i < uses->nbItems; i++) { + cur = uses->items[i]; + if ((WXS_ATTRUSE_DECL_NAME(cur) == + WXS_ATTRUSE_DECL_NAME(bcur)) && + (WXS_ATTRUSE_DECL_TNS(cur) == + WXS_ATTRUSE_DECL_TNS(bcur))) { + found = 1; + break; + } + } + } + if (!found) { + xmlChar *strA = NULL, *strB = NULL; + + xmlSchemaCustomErr4(ACTXT_CAST pctxt, + XML_SCHEMAP_DERIVATION_OK_RESTRICTION_3, + NULL, item, + "A matching attribute use for the " + "'required' %s of the %s %s is missing", + xmlSchemaGetComponentDesignation(&strA, bcur), + WXS_ACTION_STR(action), + xmlSchemaGetComponentDesignation(&strB, baseItem), + NULL); + FREE_AND_NULL(strA); + FREE_AND_NULL(strB); + } + } + } + /* + * derivation-ok-restriction (4) + */ + if (wild != NULL) { + /* + * (4) "If there is an {attribute wildcard}, all of the + * following must be true:" + */ + if (baseWild == NULL) { + xmlChar *str = NULL; + + /* + * (4.1) "The {base type definition} must also have one." + */ + xmlSchemaCustomErr4(ACTXT_CAST pctxt, + XML_SCHEMAP_DERIVATION_OK_RESTRICTION_4_1, + NULL, item, + "The %s has an attribute wildcard, " + "but the %s %s '%s' does not have one", + WXS_ITEM_TYPE_NAME(item), + WXS_ACTION_STR(action), + WXS_ITEM_TYPE_NAME(baseItem), + xmlSchemaGetComponentQName(&str, baseItem)); + FREE_AND_NULL(str); + return(pctxt->err); + } else if ((baseWild->any == 0) && + xmlSchemaCheckCOSNSSubset(wild, baseWild)) + { + xmlChar *str = NULL; + /* + * (4.2) "The complex type definition's {attribute wildcard}'s + * {namespace constraint} must be a subset of the {base type + * definition}'s {attribute wildcard}'s {namespace constraint}, + * as defined by Wildcard Subset (§3.10.6)." + */ + xmlSchemaCustomErr4(ACTXT_CAST pctxt, + XML_SCHEMAP_DERIVATION_OK_RESTRICTION_4_2, + NULL, item, + "The attribute wildcard is not a valid " + "subset of the wildcard in the %s %s '%s'", + WXS_ACTION_STR(action), + WXS_ITEM_TYPE_NAME(baseItem), + xmlSchemaGetComponentQName(&str, baseItem), + NULL); + FREE_AND_NULL(str); + return(pctxt->err); + } + /* 4.3 Unless the {base type definition} is the ·ur-type + * definition·, the complex type definition's {attribute + * wildcard}'s {process contents} must be identical to or + * stronger than the {base type definition}'s {attribute + * wildcard}'s {process contents}, where strict is stronger + * than lax is stronger than skip. + */ + if ((! WXS_IS_ANYTYPE(baseItem)) && + (wild->processContents < baseWild->processContents)) { + xmlChar *str = NULL; + xmlSchemaCustomErr4(ACTXT_CAST pctxt, + XML_SCHEMAP_DERIVATION_OK_RESTRICTION_4_3, + NULL, baseItem, + "The {process contents} of the attribute wildcard is " + "weaker than the one in the %s %s '%s'", + WXS_ACTION_STR(action), + WXS_ITEM_TYPE_NAME(baseItem), + xmlSchemaGetComponentQName(&str, baseItem), + NULL); + FREE_AND_NULL(str) + return(pctxt->err); + } + } + return(0); +} + + +static int +xmlSchemaExpandAttributeGroupRefs(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaBasicItemPtr item, + xmlSchemaWildcardPtr *completeWild, + xmlSchemaItemListPtr list, + xmlSchemaItemListPtr prohibs); +/** + * xmlSchemaFixupTypeAttributeUses: + * @ctxt: the schema parser context + * @type: the complex type definition + * + * + * Builds the wildcard and the attribute uses on the given complex type. + * Returns -1 if an internal error occurs, 0 otherwise. + * + * ATTENTION TODO: Experimantally this uses pointer comparisons for + * strings, so recheck this if we start to hardcode some schemata, since + * they might not be in the same dict. + * NOTE: It is allowed to "extend" the xs:anyType type. + */ +static int +xmlSchemaFixupTypeAttributeUses(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaTypePtr type) +{ + xmlSchemaTypePtr baseType = NULL; + xmlSchemaAttributeUsePtr use; + xmlSchemaItemListPtr uses, baseUses, prohibs = NULL; + + if (type->baseType == NULL) { + PERROR_INT("xmlSchemaFixupTypeAttributeUses", + "no base type"); + return (-1); + } + baseType = type->baseType; + if (WXS_IS_TYPE_NOT_FIXED(baseType)) + if (xmlSchemaTypeFixup(baseType, ACTXT_CAST pctxt) == -1) + return(-1); + + uses = type->attrUses; + baseUses = baseType->attrUses; + /* + * Expand attribute group references. And build the 'complete' + * wildcard, i.e. intersect multiple wildcards. + * Move attribute prohibitions into a separate list. + */ + if (uses != NULL) { + if (WXS_IS_RESTRICTION(type)) { + /* + * This one will transfer all attr. prohibitions + * into pctxt->attrProhibs. + */ + if (xmlSchemaExpandAttributeGroupRefs(pctxt, + WXS_BASIC_CAST type, &(type->attributeWildcard), uses, + pctxt->attrProhibs) == -1) + { + PERROR_INT("xmlSchemaFixupTypeAttributeUses", + "failed to expand attributes"); + } + if (pctxt->attrProhibs->nbItems != 0) + prohibs = pctxt->attrProhibs; + } else { + if (xmlSchemaExpandAttributeGroupRefs(pctxt, + WXS_BASIC_CAST type, &(type->attributeWildcard), uses, + NULL) == -1) + { + PERROR_INT("xmlSchemaFixupTypeAttributeUses", + "failed to expand attributes"); + } + } + } + /* + * Inherit the attribute uses of the base type. + */ + if (baseUses != NULL) { + int i, j; + xmlSchemaAttributeUseProhibPtr pro; + + if (WXS_IS_RESTRICTION(type)) { + int usesCount; + xmlSchemaAttributeUsePtr tmp; + + if (uses != NULL) + usesCount = uses->nbItems; + else + usesCount = 0; + + /* Restriction. */ + for (i = 0; i < baseUses->nbItems; i++) { + use = baseUses->items[i]; + if (prohibs) { + /* + * Filter out prohibited uses. + */ + for (j = 0; j < prohibs->nbItems; j++) { + pro = prohibs->items[j]; + if ((WXS_ATTRUSE_DECL_NAME(use) == pro->name) && + (WXS_ATTRUSE_DECL_TNS(use) == + pro->targetNamespace)) + { + goto inherit_next; + } + } + } + if (usesCount) { + /* + * Filter out existing uses. + */ + for (j = 0; j < usesCount; j++) { + tmp = uses->items[j]; + if ((WXS_ATTRUSE_DECL_NAME(use) == + WXS_ATTRUSE_DECL_NAME(tmp)) && + (WXS_ATTRUSE_DECL_TNS(use) == + WXS_ATTRUSE_DECL_TNS(tmp))) + { + goto inherit_next; + } + } + } + if (uses == NULL) { + type->attrUses = xmlSchemaItemListCreate(); + if (type->attrUses == NULL) + goto exit_failure; + uses = type->attrUses; + } + xmlSchemaItemListAddSize(uses, 2, use); +inherit_next: {} + } + } else { + /* Extension. */ + for (i = 0; i < baseUses->nbItems; i++) { + use = baseUses->items[i]; + if (uses == NULL) { + type->attrUses = xmlSchemaItemListCreate(); + if (type->attrUses == NULL) + goto exit_failure; + uses = type->attrUses; + } + xmlSchemaItemListAddSize(uses, baseUses->nbItems, use); + } + } + } + /* + * Shrink attr. uses. + */ + if (uses) { + if (uses->nbItems == 0) { + xmlSchemaItemListFree(uses); + type->attrUses = NULL; + } + /* + * TODO: We could shrink the size of the array + * to fit the actual number of items. + */ + } + /* + * Compute the complete wildcard. + */ + if (WXS_IS_EXTENSION(type)) { + if (baseType->attributeWildcard != NULL) { + /* + * (3.2.2.1) "If the ·base wildcard· is non-·absent·, then + * the appropriate case among the following:" + */ + if (type->attributeWildcard != NULL) { + /* + * Union the complete wildcard with the base wildcard. + * SPEC {attribute wildcard} + * (3.2.2.1.2) "otherwise a wildcard whose {process contents} + * and {annotation} are those of the ·complete wildcard·, + * and whose {namespace constraint} is the intensional union + * of the {namespace constraint} of the ·complete wildcard· + * and of the ·base wildcard·, as defined in Attribute + * Wildcard Union (§3.10.6)." + */ + if (xmlSchemaUnionWildcards(pctxt, type->attributeWildcard, + baseType->attributeWildcard) == -1) + goto exit_failure; + } else { + /* + * (3.2.2.1.1) "If the ·complete wildcard· is ·absent·, + * then the ·base wildcard·." + */ + type->attributeWildcard = baseType->attributeWildcard; + } + } else { + /* + * (3.2.2.2) "otherwise (the ·base wildcard· is ·absent·) the + * ·complete wildcard" + * NOOP + */ + } + } else { + /* + * SPEC {attribute wildcard} + * (3.1) "If the alternative is chosen, then the + * ·complete wildcard·;" + * NOOP + */ + } + + return (0); + +exit_failure: + return(-1); +} + +/** + * xmlSchemaTypeFinalContains: + * @schema: the schema + * @type: the type definition + * @final: the final + * + * Evaluates if a type definition contains the given "final". + * This does take "finalDefault" into account as well. + * + * Returns 1 if the type does containt the given "final", + * 0 otherwise. + */ +static int +xmlSchemaTypeFinalContains(xmlSchemaTypePtr type, int final) +{ + if (type == NULL) + return (0); + if (type->flags & final) + return (1); + else + return (0); +} + +/** + * xmlSchemaGetUnionSimpleTypeMemberTypes: + * @type: the Union Simple Type + * + * Returns a list of member types of @type if existing, + * returns NULL otherwise. + */ +static xmlSchemaTypeLinkPtr +xmlSchemaGetUnionSimpleTypeMemberTypes(xmlSchemaTypePtr type) +{ + while ((type != NULL) && (type->type == XML_SCHEMA_TYPE_SIMPLE)) { + if (type->memberTypes != NULL) + return (type->memberTypes); + else + type = type->baseType; + } + return (NULL); +} + +/** + * xmlSchemaGetParticleTotalRangeMin: + * @particle: the particle + * + * Schema Component Constraint: Effective Total Range + * (all and sequence) + (choice) + * + * Returns the minimun Effective Total Range. + */ +static int +xmlSchemaGetParticleTotalRangeMin(xmlSchemaParticlePtr particle) +{ + if ((particle->children == NULL) || + (particle->minOccurs == 0)) + return (0); + if (particle->children->type == XML_SCHEMA_TYPE_CHOICE) { + int min = -1, cur; + xmlSchemaParticlePtr part = + (xmlSchemaParticlePtr) particle->children->children; + + if (part == NULL) + return (0); + while (part != NULL) { + if ((part->children->type == XML_SCHEMA_TYPE_ELEMENT) || + (part->children->type == XML_SCHEMA_TYPE_ANY)) + cur = part->minOccurs; + else + cur = xmlSchemaGetParticleTotalRangeMin(part); + if (cur == 0) + return (0); + if ((min > cur) || (min == -1)) + min = cur; + part = (xmlSchemaParticlePtr) part->next; + } + return (particle->minOccurs * min); + } else { + /* and */ + int sum = 0; + xmlSchemaParticlePtr part = + (xmlSchemaParticlePtr) particle->children->children; + + if (part == NULL) + return (0); + do { + if ((part->children->type == XML_SCHEMA_TYPE_ELEMENT) || + (part->children->type == XML_SCHEMA_TYPE_ANY)) + sum += part->minOccurs; + else + sum += xmlSchemaGetParticleTotalRangeMin(part); + part = (xmlSchemaParticlePtr) part->next; + } while (part != NULL); + return (particle->minOccurs * sum); + } +} + +/** + * xmlSchemaGetParticleTotalRangeMax: + * @particle: the particle + * + * Schema Component Constraint: Effective Total Range + * (all and sequence) + (choice) + * + * Returns the maximum Effective Total Range. + */ +static int +xmlSchemaGetParticleTotalRangeMax(xmlSchemaParticlePtr particle) +{ + if ((particle->children == NULL) || + (particle->children->children == NULL)) + return (0); + if (particle->children->type == XML_SCHEMA_TYPE_CHOICE) { + int max = -1, cur; + xmlSchemaParticlePtr part = + (xmlSchemaParticlePtr) particle->children->children; + + for (; part != NULL; part = (xmlSchemaParticlePtr) part->next) { + if (part->children == NULL) + continue; + if ((part->children->type == XML_SCHEMA_TYPE_ELEMENT) || + (part->children->type == XML_SCHEMA_TYPE_ANY)) + cur = part->maxOccurs; + else + cur = xmlSchemaGetParticleTotalRangeMax(part); + if (cur == UNBOUNDED) + return (UNBOUNDED); + if ((max < cur) || (max == -1)) + max = cur; + } + /* TODO: Handle overflows? */ + return (particle->maxOccurs * max); + } else { + /* and */ + int sum = 0, cur; + xmlSchemaParticlePtr part = + (xmlSchemaParticlePtr) particle->children->children; + + for (; part != NULL; part = (xmlSchemaParticlePtr) part->next) { + if (part->children == NULL) + continue; + if ((part->children->type == XML_SCHEMA_TYPE_ELEMENT) || + (part->children->type == XML_SCHEMA_TYPE_ANY)) + cur = part->maxOccurs; + else + cur = xmlSchemaGetParticleTotalRangeMax(part); + if (cur == UNBOUNDED) + return (UNBOUNDED); + if ((cur > 0) && (particle->maxOccurs == UNBOUNDED)) + return (UNBOUNDED); + sum += cur; + } + /* TODO: Handle overflows? */ + return (particle->maxOccurs * sum); + } +} + +/** + * xmlSchemaIsParticleEmptiable: + * @particle: the particle + * + * Schema Component Constraint: Particle Emptiable + * Checks whether the given particle is emptiable. + * + * Returns 1 if emptiable, 0 otherwise. + */ +static int +xmlSchemaIsParticleEmptiable(xmlSchemaParticlePtr particle) +{ + /* + * SPEC (1) "Its {min occurs} is 0." + */ + if ((particle == NULL) || (particle->minOccurs == 0) || + (particle->children == NULL)) + return (1); + /* + * SPEC (2) "Its {term} is a group and the minimum part of the + * effective total range of that group, [...] is 0." + */ + if (WXS_IS_MODEL_GROUP(particle->children)) { + if (xmlSchemaGetParticleTotalRangeMin(particle) == 0) + return (1); + } + return (0); +} + +/** + * xmlSchemaCheckCOSSTDerivedOK: + * @actxt: a context + * @type: the derived simple type definition + * @baseType: the base type definition + * @subset: the subset of ('restriction', ect.) + * + * Schema Component Constraint: + * Type Derivation OK (Simple) (cos-st-derived-OK) + * + * Checks wheter @type can be validly + * derived from @baseType. + * + * Returns 0 on success, an positive error code otherwise. + */ +static int +xmlSchemaCheckCOSSTDerivedOK(xmlSchemaAbstractCtxtPtr actxt, + xmlSchemaTypePtr type, + xmlSchemaTypePtr baseType, + int subset) +{ + /* + * 1 They are the same type definition. + * TODO: The identy check might have to be more complex than this. + */ + if (type == baseType) + return (0); + /* + * 2.1 restriction is not in the subset, or in the {final} + * of its own {base type definition}; + * + * NOTE that this will be used also via "xsi:type". + * + * TODO: Revise this, it looks strange. How can the "type" + * not be fixed or *in* fixing? + */ + if (WXS_IS_TYPE_NOT_FIXED(type)) + if (xmlSchemaTypeFixup(type, actxt) == -1) + return(-1); + if (WXS_IS_TYPE_NOT_FIXED(baseType)) + if (xmlSchemaTypeFixup(baseType, actxt) == -1) + return(-1); + if ((subset & SUBSET_RESTRICTION) || + (xmlSchemaTypeFinalContains(type->baseType, + XML_SCHEMAS_TYPE_FINAL_RESTRICTION))) { + return (XML_SCHEMAP_COS_ST_DERIVED_OK_2_1); + } + /* 2.2 */ + if (type->baseType == baseType) { + /* + * 2.2.1 D's ·base type definition· is B. + */ + return (0); + } + /* + * 2.2.2 D's ·base type definition· is not the ·ur-type definition· + * and is validly derived from B given the subset, as defined by this + * constraint. + */ + if ((! WXS_IS_ANYTYPE(type->baseType)) && + (xmlSchemaCheckCOSSTDerivedOK(actxt, type->baseType, + baseType, subset) == 0)) { + return (0); + } + /* + * 2.2.3 D's {variety} is list or union and B is the ·simple ur-type + * definition·. + */ + if (WXS_IS_ANY_SIMPLE_TYPE(baseType) && + (WXS_IS_LIST(type) || WXS_IS_UNION(type))) { + return (0); + } + /* + * 2.2.4 B's {variety} is union and D is validly derived from a type + * definition in B's {member type definitions} given the subset, as + * defined by this constraint. + * + * NOTE: This seems not to involve built-in types, since there is no + * built-in Union Simple Type. + */ + if (WXS_IS_UNION(baseType)) { + xmlSchemaTypeLinkPtr cur; + + cur = baseType->memberTypes; + while (cur != NULL) { + if (WXS_IS_TYPE_NOT_FIXED(cur->type)) + if (xmlSchemaTypeFixup(cur->type, actxt) == -1) + return(-1); + if (xmlSchemaCheckCOSSTDerivedOK(actxt, + type, cur->type, subset) == 0) + { + /* + * It just has to be validly derived from at least one + * member-type. + */ + return (0); + } + cur = cur->next; + } + } + return (XML_SCHEMAP_COS_ST_DERIVED_OK_2_2); +} + +/** + * xmlSchemaCheckTypeDefCircularInternal: + * @pctxt: the schema parser context + * @ctxtType: the type definition + * @ancestor: an ancestor of @ctxtType + * + * Checks st-props-correct (2) + ct-props-correct (3). + * Circular type definitions are not allowed. + * + * Returns XML_SCHEMAP_ST_PROPS_CORRECT_2 if the given type is + * circular, 0 otherwise. + */ +static int +xmlSchemaCheckTypeDefCircularInternal(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaTypePtr ctxtType, + xmlSchemaTypePtr ancestor) +{ + int ret; + + if ((ancestor == NULL) || (ancestor->type == XML_SCHEMA_TYPE_BASIC)) + return (0); + + if (ctxtType == ancestor) { + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_ST_PROPS_CORRECT_2, + WXS_BASIC_CAST ctxtType, WXS_ITEM_NODE(ctxtType), + "The definition is circular", NULL); + return (XML_SCHEMAP_ST_PROPS_CORRECT_2); + } + if (ancestor->flags & XML_SCHEMAS_TYPE_MARKED) { + /* + * Avoid inifinite recursion on circular types not yet checked. + */ + return (0); + } + ancestor->flags |= XML_SCHEMAS_TYPE_MARKED; + ret = xmlSchemaCheckTypeDefCircularInternal(pctxt, ctxtType, + ancestor->baseType); + ancestor->flags ^= XML_SCHEMAS_TYPE_MARKED; + return (ret); +} + +/** + * xmlSchemaCheckTypeDefCircular: + * @item: the complex/simple type definition + * @ctxt: the parser context + * @name: the name + * + * Checks for circular type definitions. + */ +static void +xmlSchemaCheckTypeDefCircular(xmlSchemaTypePtr item, + xmlSchemaParserCtxtPtr ctxt) +{ + if ((item == NULL) || + (item->type == XML_SCHEMA_TYPE_BASIC) || + (item->baseType == NULL)) + return; + xmlSchemaCheckTypeDefCircularInternal(ctxt, item, + item->baseType); +} + +/* +* Simple Type Definition Representation OK (src-simple-type) 4 +* +* "4 Circular union type definition is disallowed. That is, if the +* alternative is chosen, there must not be any entries in the +* memberTypes [attribute] at any depth which resolve to the component +* corresponding to the ." +* +* Note that this should work on the *representation* of a component, +* thus assumes any union types in the member types not being yet +* substituted. At this stage we need the variety of the types +* to be already computed. +*/ +static int +xmlSchemaCheckUnionTypeDefCircularRecur(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaTypePtr ctxType, + xmlSchemaTypeLinkPtr members) +{ + xmlSchemaTypeLinkPtr member; + xmlSchemaTypePtr memberType; + + member = members; + while (member != NULL) { + memberType = member->type; + while ((memberType != NULL) && + (memberType->type != XML_SCHEMA_TYPE_BASIC)) { + if (memberType == ctxType) { + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_SRC_SIMPLE_TYPE_4, + WXS_BASIC_CAST ctxType, NULL, + "The union type definition is circular", NULL); + return (XML_SCHEMAP_SRC_SIMPLE_TYPE_4); + } + if ((WXS_IS_UNION(memberType)) && + ((memberType->flags & XML_SCHEMAS_TYPE_MARKED) == 0)) + { + int res; + memberType->flags |= XML_SCHEMAS_TYPE_MARKED; + res = xmlSchemaCheckUnionTypeDefCircularRecur(pctxt, + ctxType, + xmlSchemaGetUnionSimpleTypeMemberTypes(memberType)); + memberType->flags ^= XML_SCHEMAS_TYPE_MARKED; + if (res != 0) + return(res); + } + memberType = memberType->baseType; + } + member = member->next; + } + return(0); +} + +static int +xmlSchemaCheckUnionTypeDefCircular(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaTypePtr type) +{ + if (! WXS_IS_UNION(type)) + return(0); + return(xmlSchemaCheckUnionTypeDefCircularRecur(pctxt, type, + type->memberTypes)); +} + +/** + * xmlSchemaResolveTypeReferences: + * @item: the complex/simple type definition + * @ctxt: the parser context + * @name: the name + * + * Resolvese type definition references + */ +static void +xmlSchemaResolveTypeReferences(xmlSchemaTypePtr typeDef, + xmlSchemaParserCtxtPtr ctxt) +{ + if (typeDef == NULL) + return; + + /* + * Resolve the base type. + */ + if (typeDef->baseType == NULL) { + typeDef->baseType = xmlSchemaGetType(ctxt->schema, + typeDef->base, typeDef->baseNs); + if (typeDef->baseType == NULL) { + xmlSchemaPResCompAttrErr(ctxt, + XML_SCHEMAP_SRC_RESOLVE, + WXS_BASIC_CAST typeDef, typeDef->node, + "base", typeDef->base, typeDef->baseNs, + XML_SCHEMA_TYPE_SIMPLE, NULL); + return; + } + } + if (WXS_IS_SIMPLE(typeDef)) { + if (WXS_IS_UNION(typeDef)) { + /* + * Resolve the memberTypes. + */ + xmlSchemaResolveUnionMemberTypes(ctxt, typeDef); + return; + } else if (WXS_IS_LIST(typeDef)) { + /* + * Resolve the itemType. + */ + if ((typeDef->subtypes == NULL) && (typeDef->base != NULL)) { + + typeDef->subtypes = xmlSchemaGetType(ctxt->schema, + typeDef->base, typeDef->baseNs); + + if ((typeDef->subtypes == NULL) || + (! WXS_IS_SIMPLE(typeDef->subtypes))) + { + typeDef->subtypes = NULL; + xmlSchemaPResCompAttrErr(ctxt, + XML_SCHEMAP_SRC_RESOLVE, + WXS_BASIC_CAST typeDef, typeDef->node, + "itemType", typeDef->base, typeDef->baseNs, + XML_SCHEMA_TYPE_SIMPLE, NULL); + } + } + return; + } + } + /* + * The ball of letters below means, that if we have a particle + * which has a QName-helper component as its {term}, we want + * to resolve it... + */ + else if ((WXS_TYPE_CONTENTTYPE(typeDef) != NULL) && + ((WXS_TYPE_CONTENTTYPE(typeDef))->type == + XML_SCHEMA_TYPE_PARTICLE) && + (WXS_TYPE_PARTICLE_TERM(typeDef) != NULL) && + ((WXS_TYPE_PARTICLE_TERM(typeDef))->type == + XML_SCHEMA_EXTRA_QNAMEREF)) + { + xmlSchemaQNameRefPtr ref = + WXS_QNAME_CAST WXS_TYPE_PARTICLE_TERM(typeDef); + xmlSchemaModelGroupDefPtr groupDef; + + /* + * URGENT TODO: Test this. + */ + WXS_TYPE_PARTICLE_TERM(typeDef) = NULL; + /* + * Resolve the MG definition reference. + */ + groupDef = + WXS_MODEL_GROUPDEF_CAST xmlSchemaGetNamedComponent(ctxt->schema, + ref->itemType, ref->name, ref->targetNamespace); + if (groupDef == NULL) { + xmlSchemaPResCompAttrErr(ctxt, XML_SCHEMAP_SRC_RESOLVE, + NULL, WXS_ITEM_NODE(WXS_TYPE_PARTICLE(typeDef)), + "ref", ref->name, ref->targetNamespace, ref->itemType, + NULL); + /* Remove the particle. */ + WXS_TYPE_CONTENTTYPE(typeDef) = NULL; + } else if (WXS_MODELGROUPDEF_MODEL(groupDef) == NULL) + /* Remove the particle. */ + WXS_TYPE_CONTENTTYPE(typeDef) = NULL; + else { + /* + * Assign the MG definition's {model group} to the + * particle's {term}. + */ + WXS_TYPE_PARTICLE_TERM(typeDef) = WXS_MODELGROUPDEF_MODEL(groupDef); + + if (WXS_MODELGROUPDEF_MODEL(groupDef)->type == XML_SCHEMA_TYPE_ALL) { + /* + * SPEC cos-all-limited (1.2) + * "1.2 the {term} property of a particle with + * {max occurs}=1 which is part of a pair which constitutes + * the {content type} of a complex type definition." + */ + if ((WXS_TYPE_PARTICLE(typeDef))->maxOccurs != 1) { + xmlSchemaCustomErr(ACTXT_CAST ctxt, + /* TODO: error code */ + XML_SCHEMAP_COS_ALL_LIMITED, + WXS_ITEM_NODE(WXS_TYPE_PARTICLE(typeDef)), NULL, + "The particle's {max occurs} must be 1, since the " + "reference resolves to an 'all' model group", + NULL, NULL); + } + } + } + } +} + + + +/** + * xmlSchemaCheckSTPropsCorrect: + * @ctxt: the schema parser context + * @type: the simple type definition + * + * Checks st-props-correct. + * + * Returns 0 if the properties are correct, + * if not, a positive error code and -1 on internal + * errors. + */ +static int +xmlSchemaCheckSTPropsCorrect(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaTypePtr type) +{ + xmlSchemaTypePtr baseType = type->baseType; + xmlChar *str = NULL; + + /* STATE: error funcs converted. */ + /* + * Schema Component Constraint: Simple Type Definition Properties Correct + * + * NOTE: This is somehow redundant, since we actually built a simple type + * to have all the needed information; this acts as an self test. + */ + /* Base type: If the datatype has been ·derived· by ·restriction· + * then the Simple Type Definition component from which it is ·derived·, + * otherwise the Simple Type Definition for anySimpleType (§4.1.6). + */ + if (baseType == NULL) { + /* + * TODO: Think about: "modulo the impact of Missing + * Sub-components (§5.3)." + */ + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_ST_PROPS_CORRECT_1, + WXS_BASIC_CAST type, NULL, + "No base type existent", NULL); + return (XML_SCHEMAP_ST_PROPS_CORRECT_1); + + } + if (! WXS_IS_SIMPLE(baseType)) { + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_ST_PROPS_CORRECT_1, + WXS_BASIC_CAST type, NULL, + "The base type '%s' is not a simple type", + xmlSchemaGetComponentQName(&str, baseType)); + FREE_AND_NULL(str) + return (XML_SCHEMAP_ST_PROPS_CORRECT_1); + } + if ( (WXS_IS_LIST(type) || WXS_IS_UNION(type)) && + (WXS_IS_RESTRICTION(type) == 0) && + (! WXS_IS_ANY_SIMPLE_TYPE(baseType))) { + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_ST_PROPS_CORRECT_1, + WXS_BASIC_CAST type, NULL, + "A type, derived by list or union, must have" + "the simple ur-type definition as base type, not '%s'", + xmlSchemaGetComponentQName(&str, baseType)); + FREE_AND_NULL(str) + return (XML_SCHEMAP_ST_PROPS_CORRECT_1); + } + /* + * Variety: One of {atomic, list, union}. + */ + if ((! WXS_IS_ATOMIC(type)) && (! WXS_IS_UNION(type)) && + (! WXS_IS_LIST(type))) { + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_ST_PROPS_CORRECT_1, + WXS_BASIC_CAST type, NULL, + "The variety is absent", NULL); + return (XML_SCHEMAP_ST_PROPS_CORRECT_1); + } + /* TODO: Finish this. Hmm, is this finished? */ + + /* + * 3 The {final} of the {base type definition} must not contain restriction. + */ + if (xmlSchemaTypeFinalContains(baseType, + XML_SCHEMAS_TYPE_FINAL_RESTRICTION)) { + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_ST_PROPS_CORRECT_3, + WXS_BASIC_CAST type, NULL, + "The 'final' of its base type '%s' must not contain " + "'restriction'", + xmlSchemaGetComponentQName(&str, baseType)); + FREE_AND_NULL(str) + return (XML_SCHEMAP_ST_PROPS_CORRECT_3); + } + + /* + * 2 All simple type definitions must be derived ultimately from the ·simple + * ur-type definition (so· circular definitions are disallowed). That is, it + * must be possible to reach a built-in primitive datatype or the ·simple + * ur-type definition· by repeatedly following the {base type definition}. + * + * NOTE: this is done in xmlSchemaCheckTypeDefCircular(). + */ + return (0); +} + +/** + * xmlSchemaCheckCOSSTRestricts: + * @ctxt: the schema parser context + * @type: the simple type definition + * + * Schema Component Constraint: + * Derivation Valid (Restriction, Simple) (cos-st-restricts) + + * Checks if the given @type (simpleType) is derived validly by restriction. + * STATUS: + * + * Returns -1 on internal errors, 0 if the type is validly derived, + * a positive error code otherwise. + */ +static int +xmlSchemaCheckCOSSTRestricts(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaTypePtr type) +{ + xmlChar *str = NULL; + + if (type->type != XML_SCHEMA_TYPE_SIMPLE) { + PERROR_INT("xmlSchemaCheckCOSSTRestricts", + "given type is not a user-derived simpleType"); + return (-1); + } + + if (WXS_IS_ATOMIC(type)) { + xmlSchemaTypePtr primitive; + /* + * 1.1 The {base type definition} must be an atomic simple + * type definition or a built-in primitive datatype. + */ + if (! WXS_IS_ATOMIC(type->baseType)) { + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_COS_ST_RESTRICTS_1_1, + WXS_BASIC_CAST type, NULL, + "The base type '%s' is not an atomic simple type", + xmlSchemaGetComponentQName(&str, type->baseType)); + FREE_AND_NULL(str) + return (XML_SCHEMAP_COS_ST_RESTRICTS_1_1); + } + /* 1.2 The {final} of the {base type definition} must not contain + * restriction. + */ + /* OPTIMIZE TODO : This is already done in xmlSchemaCheckStPropsCorrect */ + if (xmlSchemaTypeFinalContains(type->baseType, + XML_SCHEMAS_TYPE_FINAL_RESTRICTION)) { + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_COS_ST_RESTRICTS_1_2, + WXS_BASIC_CAST type, NULL, + "The final of its base type '%s' must not contain 'restriction'", + xmlSchemaGetComponentQName(&str, type->baseType)); + FREE_AND_NULL(str) + return (XML_SCHEMAP_COS_ST_RESTRICTS_1_2); + } + + /* + * 1.3.1 DF must be an allowed constraining facet for the {primitive + * type definition}, as specified in the appropriate subsection of 3.2 + * Primitive datatypes. + */ + if (type->facets != NULL) { + xmlSchemaFacetPtr facet; + int ok = 1; + + primitive = xmlSchemaGetPrimitiveType(type); + if (primitive == NULL) { + PERROR_INT("xmlSchemaCheckCOSSTRestricts", + "failed to get primitive type"); + return (-1); + } + facet = type->facets; + do { + if (xmlSchemaIsBuiltInTypeFacet(primitive, facet->type) == 0) { + ok = 0; + xmlSchemaPIllegalFacetAtomicErr(pctxt, + XML_SCHEMAP_COS_ST_RESTRICTS_1_3_1, + type, primitive, facet); + } + facet = facet->next; + } while (facet != NULL); + if (ok == 0) + return (XML_SCHEMAP_COS_ST_RESTRICTS_1_3_1); + } + /* + * SPEC (1.3.2) "If there is a facet of the same kind in the {facets} + * of the {base type definition} (call this BF),then the DF's {value} + * must be a valid restriction of BF's {value} as defined in + * [XML Schemas: Datatypes]." + * + * NOTE (1.3.2) Facet derivation constraints are currently handled in + * xmlSchemaDeriveAndValidateFacets() + */ + } else if (WXS_IS_LIST(type)) { + xmlSchemaTypePtr itemType = NULL; + + itemType = type->subtypes; + if ((itemType == NULL) || (! WXS_IS_SIMPLE(itemType))) { + PERROR_INT("xmlSchemaCheckCOSSTRestricts", + "failed to evaluate the item type"); + return (-1); + } + if (WXS_IS_TYPE_NOT_FIXED(itemType)) + xmlSchemaTypeFixup(itemType, ACTXT_CAST pctxt); + /* + * 2.1 The {item type definition} must have a {variety} of atomic or + * union (in which case all the {member type definitions} + * must be atomic). + */ + if ((! WXS_IS_ATOMIC(itemType)) && + (! WXS_IS_UNION(itemType))) { + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_COS_ST_RESTRICTS_2_1, + WXS_BASIC_CAST type, NULL, + "The item type '%s' does not have a variety of atomic or union", + xmlSchemaGetComponentQName(&str, itemType)); + FREE_AND_NULL(str) + return (XML_SCHEMAP_COS_ST_RESTRICTS_2_1); + } else if (WXS_IS_UNION(itemType)) { + xmlSchemaTypeLinkPtr member; + + member = itemType->memberTypes; + while (member != NULL) { + if (! WXS_IS_ATOMIC(member->type)) { + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_COS_ST_RESTRICTS_2_1, + WXS_BASIC_CAST type, NULL, + "The item type is a union type, but the " + "member type '%s' of this item type is not atomic", + xmlSchemaGetComponentQName(&str, member->type)); + FREE_AND_NULL(str) + return (XML_SCHEMAP_COS_ST_RESTRICTS_2_1); + } + member = member->next; + } + } + + if (WXS_IS_ANY_SIMPLE_TYPE(type->baseType)) { + xmlSchemaFacetPtr facet; + /* + * This is the case if we have: facets != NULL) { + facet = type->facets; + do { + if (facet->type != XML_SCHEMA_FACET_WHITESPACE) { + xmlSchemaPIllegalFacetListUnionErr(pctxt, + XML_SCHEMAP_COS_ST_RESTRICTS_2_3_1_2, + type, facet); + return (XML_SCHEMAP_COS_ST_RESTRICTS_2_3_1_2); + } + facet = facet->next; + } while (facet != NULL); + } + /* + * MAYBE TODO: (Hmm, not really) Datatypes states: + * A ·list· datatype can be ·derived· from an ·atomic· datatype + * whose ·lexical space· allows space (such as string or anyURI)or + * a ·union· datatype any of whose {member type definitions}'s + * ·lexical space· allows space. + */ + } else { + /* + * This is the case if we have: baseType)) { + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_1, + WXS_BASIC_CAST type, NULL, + "The base type '%s' must be a list type", + xmlSchemaGetComponentQName(&str, type->baseType)); + FREE_AND_NULL(str) + return (XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_1); + } + /* + * 2.3.2.2 The {final} of the {base type definition} must not + * contain restriction. + */ + if (xmlSchemaTypeFinalContains(type->baseType, + XML_SCHEMAS_TYPE_FINAL_RESTRICTION)) { + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_2, + WXS_BASIC_CAST type, NULL, + "The 'final' of the base type '%s' must not contain 'restriction'", + xmlSchemaGetComponentQName(&str, type->baseType)); + FREE_AND_NULL(str) + return (XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_2); + } + /* + * 2.3.2.3 The {item type definition} must be validly derived + * from the {base type definition}'s {item type definition} given + * the empty set, as defined in Type Derivation OK (Simple) (§3.14.6). + */ + { + xmlSchemaTypePtr baseItemType; + + baseItemType = type->baseType->subtypes; + if ((baseItemType == NULL) || (! WXS_IS_SIMPLE(baseItemType))) { + PERROR_INT("xmlSchemaCheckCOSSTRestricts", + "failed to eval the item type of a base type"); + return (-1); + } + if ((itemType != baseItemType) && + (xmlSchemaCheckCOSSTDerivedOK(ACTXT_CAST pctxt, itemType, + baseItemType, 0) != 0)) { + xmlChar *strBIT = NULL, *strBT = NULL; + xmlSchemaPCustomErrExt(pctxt, + XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_3, + WXS_BASIC_CAST type, NULL, + "The item type '%s' is not validly derived from " + "the item type '%s' of the base type '%s'", + xmlSchemaGetComponentQName(&str, itemType), + xmlSchemaGetComponentQName(&strBIT, baseItemType), + xmlSchemaGetComponentQName(&strBT, type->baseType)); + + FREE_AND_NULL(str) + FREE_AND_NULL(strBIT) + FREE_AND_NULL(strBT) + return (XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_3); + } + } + + if (type->facets != NULL) { + xmlSchemaFacetPtr facet; + int ok = 1; + /* + * 2.3.2.4 Only length, minLength, maxLength, whiteSpace, pattern + * and enumeration facet components are allowed among the {facets}. + */ + facet = type->facets; + do { + switch (facet->type) { + case XML_SCHEMA_FACET_LENGTH: + case XML_SCHEMA_FACET_MINLENGTH: + case XML_SCHEMA_FACET_MAXLENGTH: + case XML_SCHEMA_FACET_WHITESPACE: + /* + * TODO: 2.5.1.2 List datatypes + * The value of ·whiteSpace· is fixed to the value collapse. + */ + case XML_SCHEMA_FACET_PATTERN: + case XML_SCHEMA_FACET_ENUMERATION: + break; + default: { + xmlSchemaPIllegalFacetListUnionErr(pctxt, + XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_4, + type, facet); + /* + * We could return, but it's nicer to report all + * invalid facets. + */ + ok = 0; + } + } + facet = facet->next; + } while (facet != NULL); + if (ok == 0) + return (XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_4); + /* + * SPEC (2.3.2.5) (same as 1.3.2) + * + * NOTE (2.3.2.5) This is currently done in + * xmlSchemaDeriveAndValidateFacets() + */ + } + } + } else if (WXS_IS_UNION(type)) { + /* + * 3.1 The {member type definitions} must all have {variety} of + * atomic or list. + */ + xmlSchemaTypeLinkPtr member; + + member = type->memberTypes; + while (member != NULL) { + if (WXS_IS_TYPE_NOT_FIXED(member->type)) + xmlSchemaTypeFixup(member->type, ACTXT_CAST pctxt); + + if ((! WXS_IS_ATOMIC(member->type)) && + (! WXS_IS_LIST(member->type))) { + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_COS_ST_RESTRICTS_3_1, + WXS_BASIC_CAST type, NULL, + "The member type '%s' is neither an atomic, nor a list type", + xmlSchemaGetComponentQName(&str, member->type)); + FREE_AND_NULL(str) + return (XML_SCHEMAP_COS_ST_RESTRICTS_3_1); + } + member = member->next; + } + /* + * 3.3.1 If the {base type definition} is the ·simple ur-type + * definition· + */ + if (type->baseType->builtInType == XML_SCHEMAS_ANYSIMPLETYPE) { + /* + * 3.3.1.1 All of the {member type definitions} must have a + * {final} which does not contain union. + */ + member = type->memberTypes; + while (member != NULL) { + if (xmlSchemaTypeFinalContains(member->type, + XML_SCHEMAS_TYPE_FINAL_UNION)) { + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_COS_ST_RESTRICTS_3_3_1, + WXS_BASIC_CAST type, NULL, + "The 'final' of member type '%s' contains 'union'", + xmlSchemaGetComponentQName(&str, member->type)); + FREE_AND_NULL(str) + return (XML_SCHEMAP_COS_ST_RESTRICTS_3_3_1); + } + member = member->next; + } + /* + * 3.3.1.2 The {facets} must be empty. + */ + if (type->facetSet != NULL) { + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_COS_ST_RESTRICTS_3_3_1_2, + WXS_BASIC_CAST type, NULL, + "No facets allowed", NULL); + return (XML_SCHEMAP_COS_ST_RESTRICTS_3_3_1_2); + } + } else { + /* + * 3.3.2.1 The {base type definition} must have a {variety} of union. + * I.e. the variety of "list" is inherited. + */ + if (! WXS_IS_UNION(type->baseType)) { + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_1, + WXS_BASIC_CAST type, NULL, + "The base type '%s' is not a union type", + xmlSchemaGetComponentQName(&str, type->baseType)); + FREE_AND_NULL(str) + return (XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_1); + } + /* + * 3.3.2.2 The {final} of the {base type definition} must not contain restriction. + */ + if (xmlSchemaTypeFinalContains(type->baseType, + XML_SCHEMAS_TYPE_FINAL_RESTRICTION)) { + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_2, + WXS_BASIC_CAST type, NULL, + "The 'final' of its base type '%s' must not contain 'restriction'", + xmlSchemaGetComponentQName(&str, type->baseType)); + FREE_AND_NULL(str) + return (XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_2); + } + /* + * 3.3.2.3 The {member type definitions}, in order, must be validly + * derived from the corresponding type definitions in the {base + * type definition}'s {member type definitions} given the empty set, + * as defined in Type Derivation OK (Simple) (§3.14.6). + */ + { + xmlSchemaTypeLinkPtr baseMember; + + /* + * OPTIMIZE: if the type is restricting, it has no local defined + * member types and inherits the member types of the base type; + * thus a check for equality can be skipped. + */ + /* + * Even worse: I cannot see a scenario where a restricting + * union simple type can have other member types as the member + * types of it's base type. This check seems not necessary with + * respect to the derivation process in libxml2. + * But necessary if constructing types with an API. + */ + if (type->memberTypes != NULL) { + member = type->memberTypes; + baseMember = xmlSchemaGetUnionSimpleTypeMemberTypes(type->baseType); + if ((member == NULL) && (baseMember != NULL)) { + PERROR_INT("xmlSchemaCheckCOSSTRestricts", + "different number of member types in base"); + } + while (member != NULL) { + if (baseMember == NULL) { + PERROR_INT("xmlSchemaCheckCOSSTRestricts", + "different number of member types in base"); + } else if ((member->type != baseMember->type) && + (xmlSchemaCheckCOSSTDerivedOK(ACTXT_CAST pctxt, + member->type, baseMember->type, 0) != 0)) { + xmlChar *strBMT = NULL, *strBT = NULL; + + xmlSchemaPCustomErrExt(pctxt, + XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_3, + WXS_BASIC_CAST type, NULL, + "The member type %s is not validly " + "derived from its corresponding member " + "type %s of the base type %s", + xmlSchemaGetComponentQName(&str, member->type), + xmlSchemaGetComponentQName(&strBMT, baseMember->type), + xmlSchemaGetComponentQName(&strBT, type->baseType)); + FREE_AND_NULL(str) + FREE_AND_NULL(strBMT) + FREE_AND_NULL(strBT) + return (XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_3); + } + member = member->next; + baseMember = baseMember->next; + } + } + } + /* + * 3.3.2.4 Only pattern and enumeration facet components are + * allowed among the {facets}. + */ + if (type->facets != NULL) { + xmlSchemaFacetPtr facet; + int ok = 1; + + facet = type->facets; + do { + if ((facet->type != XML_SCHEMA_FACET_PATTERN) && + (facet->type != XML_SCHEMA_FACET_ENUMERATION)) { + xmlSchemaPIllegalFacetListUnionErr(pctxt, + XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_4, + type, facet); + ok = 0; + } + facet = facet->next; + } while (facet != NULL); + if (ok == 0) + return (XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_4); + + } + /* + * SPEC (3.3.2.5) (same as 1.3.2) + * + * NOTE (3.3.2.5) This is currently done in + * xmlSchemaDeriveAndValidateFacets() + */ + } + } + + return (0); +} + +/** + * xmlSchemaCheckSRCSimpleType: + * @ctxt: the schema parser context + * @type: the simple type definition + * + * Checks crc-simple-type constraints. + * + * Returns 0 if the constraints are satisfied, + * if not a positive error code and -1 on internal + * errors. + */ +#if 0 +static int +xmlSchemaCheckSRCSimpleType(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaTypePtr type) +{ + /* + * src-simple-type.1 The corresponding simple type definition, if any, + * must satisfy the conditions set out in Constraints on Simple Type + * Definition Schema Components (§3.14.6). + */ + if (WXS_IS_RESTRICTION(type)) { + /* + * src-simple-type.2 "If the alternative is chosen, + * either it must have a base [attribute] or a among its + * [children], but not both." + * NOTE: This is checked in the parse function of . + */ + /* + * + */ + } else if (WXS_IS_LIST(type)) { + /* src-simple-type.3 "If the alternative is chosen, either it must have + * an itemType [attribute] or a among its [children], + * but not both." + * + * NOTE: This is checked in the parse function of . + */ + } else if (WXS_IS_UNION(type)) { + /* + * src-simple-type.4 is checked in xmlSchemaCheckUnionTypeDefCircular(). + */ + } + return (0); +} +#endif + +static int +xmlSchemaCreateVCtxtOnPCtxt(xmlSchemaParserCtxtPtr ctxt) +{ + if (ctxt->vctxt == NULL) { + ctxt->vctxt = xmlSchemaNewValidCtxt(NULL); + if (ctxt->vctxt == NULL) { + xmlSchemaPErr(ctxt, NULL, + XML_SCHEMAP_INTERNAL, + "Internal error: xmlSchemaCreateVCtxtOnPCtxt, " + "failed to create a temp. validation context.\n", + NULL, NULL); + return (-1); + } + /* TODO: Pass user data. */ + xmlSchemaSetValidErrors(ctxt->vctxt, + ctxt->error, ctxt->warning, ctxt->errCtxt); + xmlSchemaSetValidStructuredErrors(ctxt->vctxt, + ctxt->serror, ctxt->errCtxt); + } + return (0); +} + +static int +xmlSchemaVCheckCVCSimpleType(xmlSchemaAbstractCtxtPtr actxt, + xmlNodePtr node, + xmlSchemaTypePtr type, + const xmlChar *value, + xmlSchemaValPtr *retVal, + int fireErrors, + int normalize, + int isNormalized); + +/** + * xmlSchemaParseCheckCOSValidDefault: + * @pctxt: the schema parser context + * @type: the simple type definition + * @value: the default value + * @node: an optional node (the holder of the value) + * + * Schema Component Constraint: Element Default Valid (Immediate) + * (cos-valid-default) + * This will be used by the parser only. For the validator there's + * an other version. + * + * Returns 0 if the constraints are satisfied, + * if not, a positive error code and -1 on internal + * errors. + */ +static int +xmlSchemaParseCheckCOSValidDefault(xmlSchemaParserCtxtPtr pctxt, + xmlNodePtr node, + xmlSchemaTypePtr type, + const xmlChar *value, + xmlSchemaValPtr *val) +{ + int ret = 0; + + /* + * cos-valid-default: + * Schema Component Constraint: Element Default Valid (Immediate) + * For a string to be a valid default with respect to a type + * definition the appropriate case among the following must be true: + */ + if WXS_IS_COMPLEX(type) { + /* + * Complex type. + * + * SPEC (2.1) "its {content type} must be a simple type definition + * or mixed." + * SPEC (2.2.2) "If the {content type} is mixed, then the {content + * type}'s particle must be ·emptiable· as defined by + * Particle Emptiable (§3.9.6)." + */ + if ((! WXS_HAS_SIMPLE_CONTENT(type)) && + ((! WXS_HAS_MIXED_CONTENT(type)) || (! WXS_EMPTIABLE(type)))) { + /* NOTE that this covers (2.2.2) as well. */ + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_COS_VALID_DEFAULT_2_1, + WXS_BASIC_CAST type, type->node, + "For a string to be a valid default, the type definition " + "must be a simple type or a complex type with mixed content " + "and a particle emptiable", NULL); + return(XML_SCHEMAP_COS_VALID_DEFAULT_2_1); + } + } + /* + * 1 If the type definition is a simple type definition, then the string + * must be ·valid· with respect to that definition as defined by String + * Valid (§3.14.4). + * + * AND + * + * 2.2.1 If the {content type} is a simple type definition, then the + * string must be ·valid· with respect to that simple type definition + * as defined by String Valid (§3.14.4). + */ + if (WXS_IS_SIMPLE(type)) + ret = xmlSchemaVCheckCVCSimpleType(ACTXT_CAST pctxt, node, + type, value, val, 1, 1, 0); + else if (WXS_HAS_SIMPLE_CONTENT(type)) + ret = xmlSchemaVCheckCVCSimpleType(ACTXT_CAST pctxt, node, + type->contentTypeDef, value, val, 1, 1, 0); + else + return (ret); + + if (ret < 0) { + PERROR_INT("xmlSchemaParseCheckCOSValidDefault", + "calling xmlSchemaVCheckCVCSimpleType()"); + } + + return (ret); +} + +/** + * xmlSchemaCheckCTPropsCorrect: + * @ctxt: the schema parser context + * @type: the complex type definition + * + *.(4.6) Constraints on Complex Type Definition Schema Components + * Schema Component Constraint: + * Complex Type Definition Properties Correct (ct-props-correct) + * STATUS: (seems) complete + * + * Returns 0 if the constraints are satisfied, a positive + * error code if not and -1 if an internal error occured. + */ +static int +xmlSchemaCheckCTPropsCorrect(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaTypePtr type) +{ + /* + * TODO: Correct the error code; XML_SCHEMAP_SRC_CT_1 is used temporarily. + * + * SPEC (1) "The values of the properties of a complex type definition must + * be as described in the property tableau in The Complex Type Definition + * Schema Component (§3.4.1), modulo the impact of Missing + * Sub-components (§5.3)." + */ + if ((type->baseType != NULL) && + (WXS_IS_SIMPLE(type->baseType)) && + (WXS_IS_EXTENSION(type) == 0)) { + /* + * SPEC (2) "If the {base type definition} is a simple type definition, + * the {derivation method} must be extension." + */ + xmlSchemaCustomErr(ACTXT_CAST pctxt, + XML_SCHEMAP_SRC_CT_1, + NULL, WXS_BASIC_CAST type, + "If the base type is a simple type, the derivation method must be " + "'extension'", NULL, NULL); + return (XML_SCHEMAP_SRC_CT_1); + } + /* + * SPEC (3) "Circular definitions are disallowed, except for the ·ur-type + * definition·. That is, it must be possible to reach the ·ur-type + * definition by repeatedly following the {base type definition}." + * + * NOTE (3) is done in xmlSchemaCheckTypeDefCircular(). + */ + /* + * NOTE that (4) and (5) need the following: + * - attribute uses need to be already inherited (apply attr. prohibitions) + * - attribute group references need to be expanded already + * - simple types need to be typefixed already + */ + if (type->attrUses && + (((xmlSchemaItemListPtr) type->attrUses)->nbItems > 1)) + { + xmlSchemaItemListPtr uses = (xmlSchemaItemListPtr) type->attrUses; + xmlSchemaAttributeUsePtr use, tmp; + int i, j, hasId = 0; + + for (i = uses->nbItems -1; i >= 0; i--) { + use = uses->items[i]; + + /* + * SPEC ct-props-correct + * (4) "Two distinct attribute declarations in the + * {attribute uses} must not have identical {name}s and + * {target namespace}s." + */ + if (i > 0) { + for (j = i -1; j >= 0; j--) { + tmp = uses->items[j]; + if ((WXS_ATTRUSE_DECL_NAME(use) == + WXS_ATTRUSE_DECL_NAME(tmp)) && + (WXS_ATTRUSE_DECL_TNS(use) == + WXS_ATTRUSE_DECL_TNS(tmp))) + { + xmlChar *str = NULL; + + xmlSchemaCustomErr(ACTXT_CAST pctxt, + XML_SCHEMAP_AG_PROPS_CORRECT, + NULL, WXS_BASIC_CAST type, + "Duplicate %s", + xmlSchemaGetComponentDesignation(&str, use), + NULL); + FREE_AND_NULL(str); + /* + * Remove the duplicate. + */ + if (xmlSchemaItemListRemove(uses, i) == -1) + goto exit_failure; + goto next_use; + } + } + } + /* + * SPEC ct-props-correct + * (5) "Two distinct attribute declarations in the + * {attribute uses} must not have {type definition}s which + * are or are derived from ID." + */ + if (WXS_ATTRUSE_TYPEDEF(use) != NULL) { + if (xmlSchemaIsDerivedFromBuiltInType( + WXS_ATTRUSE_TYPEDEF(use), XML_SCHEMAS_ID)) + { + if (hasId) { + xmlChar *str = NULL; + + xmlSchemaCustomErr(ACTXT_CAST pctxt, + XML_SCHEMAP_AG_PROPS_CORRECT, + NULL, WXS_BASIC_CAST type, + "There must not exist more than one attribute " + "declaration of type 'xs:ID' " + "(or derived from 'xs:ID'). The %s violates this " + "constraint", + xmlSchemaGetComponentDesignation(&str, use), + NULL); + FREE_AND_NULL(str); + if (xmlSchemaItemListRemove(uses, i) == -1) + goto exit_failure; + } + + hasId = 1; + } + } +next_use: {} + } + } + return (0); +exit_failure: + return(-1); +} + +static int +xmlSchemaAreEqualTypes(xmlSchemaTypePtr typeA, + xmlSchemaTypePtr typeB) +{ + /* + * TODO: This should implement component-identity + * in the future. + */ + if ((typeA == NULL) || (typeB == NULL)) + return (0); + return (typeA == typeB); +} + +/** + * xmlSchemaCheckCOSCTDerivedOK: + * @ctxt: the schema parser context + * @type: the to-be derived complex type definition + * @baseType: the base complex type definition + * @set: the given set + * + * Schema Component Constraint: + * Type Derivation OK (Complex) (cos-ct-derived-ok) + * + * STATUS: completed + * + * Returns 0 if the constraints are satisfied, or 1 + * if not. + */ +static int +xmlSchemaCheckCOSCTDerivedOK(xmlSchemaAbstractCtxtPtr actxt, + xmlSchemaTypePtr type, + xmlSchemaTypePtr baseType, + int set) +{ + int equal = xmlSchemaAreEqualTypes(type, baseType); + /* TODO: Error codes. */ + /* + * SPEC "For a complex type definition (call it D, for derived) + * to be validly derived from a type definition (call this + * B, for base) given a subset of {extension, restriction} + * all of the following must be true:" + */ + if (! equal) { + /* + * SPEC (1) "If B and D are not the same type definition, then the + * {derivation method} of D must not be in the subset." + */ + if (((set & SUBSET_EXTENSION) && (WXS_IS_EXTENSION(type))) || + ((set & SUBSET_RESTRICTION) && (WXS_IS_RESTRICTION(type)))) + return (1); + } else { + /* + * SPEC (2.1) "B and D must be the same type definition." + */ + return (0); + } + /* + * SPEC (2.2) "B must be D's {base type definition}." + */ + if (type->baseType == baseType) + return (0); + /* + * SPEC (2.3.1) "D's {base type definition} must not be the ·ur-type + * definition·." + */ + if (WXS_IS_ANYTYPE(type->baseType)) + return (1); + + if (WXS_IS_COMPLEX(type->baseType)) { + /* + * SPEC (2.3.2.1) "If D's {base type definition} is complex, then it + * must be validly derived from B given the subset as defined by this + * constraint." + */ + return (xmlSchemaCheckCOSCTDerivedOK(actxt, type->baseType, + baseType, set)); + } else { + /* + * SPEC (2.3.2.2) "If D's {base type definition} is simple, then it + * must be validly derived from B given the subset as defined in Type + * Derivation OK (Simple) (§3.14.6). + */ + return (xmlSchemaCheckCOSSTDerivedOK(actxt, type->baseType, + baseType, set)); + } +} + +/** + * xmlSchemaCheckCOSDerivedOK: + * @type: the derived simple type definition + * @baseType: the base type definition + * + * Calls: + * Type Derivation OK (Simple) AND Type Derivation OK (Complex) + * + * Checks wheter @type can be validly derived from @baseType. + * + * Returns 0 on success, an positive error code otherwise. + */ +static int +xmlSchemaCheckCOSDerivedOK(xmlSchemaAbstractCtxtPtr actxt, + xmlSchemaTypePtr type, + xmlSchemaTypePtr baseType, + int set) +{ + if (WXS_IS_SIMPLE(type)) + return (xmlSchemaCheckCOSSTDerivedOK(actxt, type, baseType, set)); + else + return (xmlSchemaCheckCOSCTDerivedOK(actxt, type, baseType, set)); +} + +/** + * xmlSchemaCheckCOSCTExtends: + * @ctxt: the schema parser context + * @type: the complex type definition + * + * (3.4.6) Constraints on Complex Type Definition Schema Components + * Schema Component Constraint: + * Derivation Valid (Extension) (cos-ct-extends) + * + * STATUS: + * missing: + * (1.5) + * (1.4.3.2.2.2) "Particle Valid (Extension)" + * + * Returns 0 if the constraints are satisfied, a positive + * error code if not and -1 if an internal error occured. + */ +static int +xmlSchemaCheckCOSCTExtends(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaTypePtr type) +{ + xmlSchemaTypePtr base = type->baseType; + /* + * TODO: Correct the error code; XML_SCHEMAP_COS_CT_EXTENDS_1_1 is used + * temporarily only. + */ + /* + * SPEC (1) "If the {base type definition} is a complex type definition, + * then all of the following must be true:" + */ + if (WXS_IS_COMPLEX(base)) { + /* + * SPEC (1.1) "The {final} of the {base type definition} must not + * contain extension." + */ + if (base->flags & XML_SCHEMAS_TYPE_FINAL_EXTENSION) { + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_COS_CT_EXTENDS_1_1, + WXS_BASIC_CAST type, NULL, + "The 'final' of the base type definition " + "contains 'extension'", NULL); + return (XML_SCHEMAP_COS_CT_EXTENDS_1_1); + } + + /* + * ATTENTION: The constrains (1.2) and (1.3) are not applied, + * since they are automatically satisfied through the + * inheriting mechanism. + * Note that even if redefining components, the inheriting mechanism + * is used. + */ +#if 0 + /* + * SPEC (1.2) "Its {attribute uses} must be a subset of the {attribute + * uses} + * of the complex type definition itself, that is, for every attribute + * use in the {attribute uses} of the {base type definition}, there + * must be an attribute use in the {attribute uses} of the complex + * type definition itself whose {attribute declaration} has the same + * {name}, {target namespace} and {type definition} as its attribute + * declaration" + */ + if (base->attrUses != NULL) { + int i, j, found; + xmlSchemaAttributeUsePtr use, buse; + + for (i = 0; i < (WXS_LIST_CAST base->attrUses)->nbItems; i ++) { + buse = (WXS_LIST_CAST base->attrUses)->items[i]; + found = 0; + if (type->attrUses != NULL) { + use = (WXS_LIST_CAST type->attrUses)->items[j]; + for (j = 0; j < (WXS_LIST_CAST type->attrUses)->nbItems; j ++) + { + if ((WXS_ATTRUSE_DECL_NAME(use) == + WXS_ATTRUSE_DECL_NAME(buse)) && + (WXS_ATTRUSE_DECL_TNS(use) == + WXS_ATTRUSE_DECL_TNS(buse)) && + (WXS_ATTRUSE_TYPEDEF(use) == + WXS_ATTRUSE_TYPEDEF(buse)) + { + found = 1; + break; + } + } + } + if (! found) { + xmlChar *str = NULL; + + xmlSchemaCustomErr(ACTXT_CAST ctxt, + XML_SCHEMAP_COS_CT_EXTENDS_1_2, + NULL, WXS_BASIC_CAST type, + /* + * TODO: The report does not indicate that also the + * type needs to be the same. + */ + "This type is missing a matching correspondent " + "for its {base type}'s %s in its {attribute uses}", + xmlSchemaGetComponentDesignation(&str, + buse->children), + NULL); + FREE_AND_NULL(str) + } + } + } + /* + * SPEC (1.3) "If it has an {attribute wildcard}, the complex type + * definition must also have one, and the base type definition's + * {attribute wildcard}'s {namespace constraint} must be a subset + * of the complex type definition's {attribute wildcard}'s {namespace + * constraint}, as defined by Wildcard Subset (§3.10.6)." + */ + + /* + * MAYBE TODO: Enable if ever needed. But this will be needed only + * if created the type via a schema construction API. + */ + if (base->attributeWildcard != NULL) { + if (type->attributeWilcard == NULL) { + xmlChar *str = NULL; + + xmlSchemaCustomErr(ACTXT_CAST pctxt, + XML_SCHEMAP_COS_CT_EXTENDS_1_3, + NULL, type, + "The base %s has an attribute wildcard, " + "but this type is missing an attribute wildcard", + xmlSchemaGetComponentDesignation(&str, base)); + FREE_AND_NULL(str) + + } else if (xmlSchemaCheckCOSNSSubset( + base->attributeWildcard, type->attributeWildcard)) + { + xmlChar *str = NULL; + + xmlSchemaCustomErr(ACTXT_CAST pctxt, + XML_SCHEMAP_COS_CT_EXTENDS_1_3, + NULL, type, + "The attribute wildcard is not a valid " + "superset of the one in the base %s", + xmlSchemaGetComponentDesignation(&str, base)); + FREE_AND_NULL(str) + } + } +#endif + /* + * SPEC (1.4) "One of the following must be true:" + */ + if ((type->contentTypeDef != NULL) && + (type->contentTypeDef == base->contentTypeDef)) { + /* + * SPEC (1.4.1) "The {content type} of the {base type definition} + * and the {content type} of the complex type definition itself + * must be the same simple type definition" + * PASS + */ + } else if ((type->contentType == XML_SCHEMA_CONTENT_EMPTY) && + (base->contentType == XML_SCHEMA_CONTENT_EMPTY) ) { + /* + * SPEC (1.4.2) "The {content type} of both the {base type + * definition} and the complex type definition itself must + * be empty." + * PASS + */ + } else { + /* + * SPEC (1.4.3) "All of the following must be true:" + */ + if (type->subtypes == NULL) { + /* + * SPEC 1.4.3.1 The {content type} of the complex type + * definition itself must specify a particle. + */ + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_COS_CT_EXTENDS_1_1, + WXS_BASIC_CAST type, NULL, + "The content type must specify a particle", NULL); + return (XML_SCHEMAP_COS_CT_EXTENDS_1_1); + } + /* + * SPEC (1.4.3.2) "One of the following must be true:" + */ + if (base->contentType == XML_SCHEMA_CONTENT_EMPTY) { + /* + * SPEC (1.4.3.2.1) "The {content type} of the {base type + * definition} must be empty. + * PASS + */ + } else { + /* + * SPEC (1.4.3.2.2) "All of the following must be true:" + */ + if ((type->contentType != base->contentType) || + ((type->contentType != XML_SCHEMA_CONTENT_MIXED) && + (type->contentType != XML_SCHEMA_CONTENT_ELEMENTS))) { + /* + * SPEC (1.4.3.2.2.1) "Both {content type}s must be mixed + * or both must be element-only." + */ + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_COS_CT_EXTENDS_1_1, + WXS_BASIC_CAST type, NULL, + "The content type of both, the type and its base " + "type, must either 'mixed' or 'element-only'", NULL); + return (XML_SCHEMAP_COS_CT_EXTENDS_1_1); + } + /* + * URGENT TODO SPEC (1.4.3.2.2.2) "The particle of the + * complex type definition must be a ·valid extension· + * of the {base type definition}'s particle, as defined + * in Particle Valid (Extension) (§3.9.6)." + * + * NOTE that we won't check "Particle Valid (Extension)", + * since it is ensured by the derivation process in + * xmlSchemaTypeFixup(). We need to implement this when heading + * for a construction API + * TODO: !! This is needed to be checked if redefining a type !! + */ + } + /* + * URGENT TODO (1.5) + */ + } + } else { + /* + * SPEC (2) "If the {base type definition} is a simple type definition, + * then all of the following must be true:" + */ + if (type->contentTypeDef != base) { + /* + * SPEC (2.1) "The {content type} must be the same simple type + * definition." + */ + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_COS_CT_EXTENDS_1_1, + WXS_BASIC_CAST type, NULL, + "The content type must be the simple base type", NULL); + return (XML_SCHEMAP_COS_CT_EXTENDS_1_1); + } + if (base->flags & XML_SCHEMAS_TYPE_FINAL_EXTENSION) { + /* + * SPEC (2.2) "The {final} of the {base type definition} must not + * contain extension" + * NOTE that this is the same as (1.1). + */ + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_COS_CT_EXTENDS_1_1, + WXS_BASIC_CAST type, NULL, + "The 'final' of the base type definition " + "contains 'extension'", NULL); + return (XML_SCHEMAP_COS_CT_EXTENDS_1_1); + } + } + return (0); +} + +/** + * xmlSchemaCheckDerivationOKRestriction: + * @ctxt: the schema parser context + * @type: the complex type definition + * + * (3.4.6) Constraints on Complex Type Definition Schema Components + * Schema Component Constraint: + * Derivation Valid (Restriction, Complex) (derivation-ok-restriction) + * + * STATUS: + * missing: + * (5.4.2) ??? + * + * ATTENTION: + * In XML Schema 1.1 this will be: + * Validation Rule: Checking complex type subsumption + * + * Returns 0 if the constraints are satisfied, a positive + * error code if not and -1 if an internal error occured. + */ +static int +xmlSchemaCheckDerivationOKRestriction(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaTypePtr type) +{ + xmlSchemaTypePtr base; + + /* + * TODO: Correct the error code; XML_SCHEMAP_DERIVATION_OK_RESTRICTION_1 is used + * temporarily only. + */ + base = type->baseType; + if (! WXS_IS_COMPLEX(base)) { + xmlSchemaCustomErr(ACTXT_CAST ctxt, + XML_SCHEMAP_DERIVATION_OK_RESTRICTION_1, + type->node, WXS_BASIC_CAST type, + "The base type must be a complex type", NULL, NULL); + return(ctxt->err); + } + if (base->flags & XML_SCHEMAS_TYPE_FINAL_RESTRICTION) { + /* + * SPEC (1) "The {base type definition} must be a complex type + * definition whose {final} does not contain restriction." + */ + xmlSchemaCustomErr(ACTXT_CAST ctxt, + XML_SCHEMAP_DERIVATION_OK_RESTRICTION_1, + type->node, WXS_BASIC_CAST type, + "The 'final' of the base type definition " + "contains 'restriction'", NULL, NULL); + return (ctxt->err); + } + /* + * SPEC (2), (3) and (4) + * Those are handled in a separate function, since the + * same constraints are needed for redefinition of + * attribute groups as well. + */ + if (xmlSchemaCheckDerivationOKRestriction2to4(ctxt, + XML_SCHEMA_ACTION_DERIVE, + WXS_BASIC_CAST type, WXS_BASIC_CAST base, + type->attrUses, base->attrUses, + type->attributeWildcard, + base->attributeWildcard) == -1) + { + return(-1); + } + /* + * SPEC (5) "One of the following must be true:" + */ + if (base->builtInType == XML_SCHEMAS_ANYTYPE) { + /* + * SPEC (5.1) "The {base type definition} must be the + * ·ur-type definition·." + * PASS + */ + } else if ((type->contentType == XML_SCHEMA_CONTENT_SIMPLE) || + (type->contentType == XML_SCHEMA_CONTENT_BASIC)) { + /* + * SPEC (5.2.1) "The {content type} of the complex type definition + * must be a simple type definition" + * + * SPEC (5.2.2) "One of the following must be true:" + */ + if ((base->contentType == XML_SCHEMA_CONTENT_SIMPLE) || + (base->contentType == XML_SCHEMA_CONTENT_BASIC)) + { + int err; + /* + * SPEC (5.2.2.1) "The {content type} of the {base type + * definition} must be a simple type definition from which + * the {content type} is validly derived given the empty + * set as defined in Type Derivation OK (Simple) (§3.14.6)." + * + * ATTENTION TODO: This seems not needed if the type implicitely + * derived from the base type. + * + */ + err = xmlSchemaCheckCOSSTDerivedOK(ACTXT_CAST ctxt, + type->contentTypeDef, base->contentTypeDef, 0); + if (err != 0) { + xmlChar *strA = NULL, *strB = NULL; + + if (err == -1) + return(-1); + xmlSchemaCustomErr(ACTXT_CAST ctxt, + XML_SCHEMAP_DERIVATION_OK_RESTRICTION_1, + NULL, WXS_BASIC_CAST type, + "The {content type} %s is not validly derived from the " + "base type's {content type} %s", + xmlSchemaGetComponentDesignation(&strA, + type->contentTypeDef), + xmlSchemaGetComponentDesignation(&strB, + base->contentTypeDef)); + FREE_AND_NULL(strA); + FREE_AND_NULL(strB); + return(ctxt->err); + } + } else if ((base->contentType == XML_SCHEMA_CONTENT_MIXED) && + (xmlSchemaIsParticleEmptiable( + (xmlSchemaParticlePtr) base->subtypes))) { + /* + * SPEC (5.2.2.2) "The {base type definition} must be mixed + * and have a particle which is ·emptiable· as defined in + * Particle Emptiable (§3.9.6)." + * PASS + */ + } else { + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_DERIVATION_OK_RESTRICTION_1, + WXS_BASIC_CAST type, NULL, + "The content type of the base type must be either " + "a simple type or 'mixed' and an emptiable particle", NULL); + return (ctxt->err); + } + } else if (type->contentType == XML_SCHEMA_CONTENT_EMPTY) { + /* + * SPEC (5.3.1) "The {content type} of the complex type itself must + * be empty" + */ + if (base->contentType == XML_SCHEMA_CONTENT_EMPTY) { + /* + * SPEC (5.3.2.1) "The {content type} of the {base type + * definition} must also be empty." + * PASS + */ + } else if (((base->contentType == XML_SCHEMA_CONTENT_ELEMENTS) || + (base->contentType == XML_SCHEMA_CONTENT_MIXED)) && + xmlSchemaIsParticleEmptiable( + (xmlSchemaParticlePtr) base->subtypes)) { + /* + * SPEC (5.3.2.2) "The {content type} of the {base type + * definition} must be elementOnly or mixed and have a particle + * which is ·emptiable· as defined in Particle Emptiable (§3.9.6)." + * PASS + */ + } else { + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_DERIVATION_OK_RESTRICTION_1, + WXS_BASIC_CAST type, NULL, + "The content type of the base type must be either " + "empty or 'mixed' (or 'elements-only') and an emptiable " + "particle", NULL); + return (ctxt->err); + } + } else if ((type->contentType == XML_SCHEMA_CONTENT_ELEMENTS) || + WXS_HAS_MIXED_CONTENT(type)) { + /* + * SPEC (5.4.1.1) "The {content type} of the complex type definition + * itself must be element-only" + */ + if (WXS_HAS_MIXED_CONTENT(type) && (! WXS_HAS_MIXED_CONTENT(base))) { + /* + * SPEC (5.4.1.2) "The {content type} of the complex type + * definition itself and of the {base type definition} must be + * mixed" + */ + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_DERIVATION_OK_RESTRICTION_1, + WXS_BASIC_CAST type, NULL, + "If the content type is 'mixed', then the content type of the " + "base type must also be 'mixed'", NULL); + return (ctxt->err); + } + /* + * SPEC (5.4.2) "The particle of the complex type definition itself + * must be a ·valid restriction· of the particle of the {content + * type} of the {base type definition} as defined in Particle Valid + * (Restriction) (§3.9.6). + * + * URGENT TODO: (5.4.2) + */ + } else { + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_DERIVATION_OK_RESTRICTION_1, + WXS_BASIC_CAST type, NULL, + "The type is not a valid restriction of its base type", NULL); + return (ctxt->err); + } + return (0); +} + +/** + * xmlSchemaCheckCTComponent: + * @ctxt: the schema parser context + * @type: the complex type definition + * + * (3.4.6) Constraints on Complex Type Definition Schema Components + * + * Returns 0 if the constraints are satisfied, a positive + * error code if not and -1 if an internal error occured. + */ +static int +xmlSchemaCheckCTComponent(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaTypePtr type) +{ + int ret; + /* + * Complex Type Definition Properties Correct + */ + ret = xmlSchemaCheckCTPropsCorrect(ctxt, type); + if (ret != 0) + return (ret); + if (WXS_IS_EXTENSION(type)) + ret = xmlSchemaCheckCOSCTExtends(ctxt, type); + else + ret = xmlSchemaCheckDerivationOKRestriction(ctxt, type); + return (ret); +} + +/** + * xmlSchemaCheckSRCCT: + * @ctxt: the schema parser context + * @type: the complex type definition + * + * (3.4.3) Constraints on XML Representations of Complex Type Definitions: + * Schema Representation Constraint: + * Complex Type Definition Representation OK (src-ct) + * + * Returns 0 if the constraints are satisfied, a positive + * error code if not and -1 if an internal error occured. + */ +static int +xmlSchemaCheckSRCCT(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaTypePtr type) +{ + xmlSchemaTypePtr base; + int ret = 0; + + /* + * TODO: Adjust the error codes here, as I used + * XML_SCHEMAP_SRC_CT_1 only yet. + */ + base = type->baseType; + if (! WXS_HAS_SIMPLE_CONTENT(type)) { + /* + * 1 If the alternative is chosen, the type definition + * ·resolved· to by the ·actual value· of the base [attribute] + * must be a complex type definition; + */ + if (! WXS_IS_COMPLEX(base)) { + xmlChar *str = NULL; + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_SRC_CT_1, + WXS_BASIC_CAST type, type->node, + "If using , the base type is expected to be " + "a complex type. The base type '%s' is a simple type", + xmlSchemaFormatQName(&str, base->targetNamespace, + base->name)); + FREE_AND_NULL(str) + return (XML_SCHEMAP_SRC_CT_1); + } + } else { + /* + * SPEC + * 2 If the alternative is chosen, all of the + * following must be true: + * 2.1 The type definition ·resolved· to by the ·actual value· of the + * base [attribute] must be one of the following: + */ + if (WXS_IS_SIMPLE(base)) { + if (WXS_IS_EXTENSION(type) == 0) { + xmlChar *str = NULL; + /* + * 2.1.3 only if the alternative is also + * chosen, a simple type definition. + */ + /* TODO: Change error code to ..._SRC_CT_2_1_3. */ + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_SRC_CT_1, + WXS_BASIC_CAST type, NULL, + "If using and , the base " + "type must be a complex type. The base type '%s' is " + "a simple type", + xmlSchemaFormatQName(&str, base->targetNamespace, + base->name)); + FREE_AND_NULL(str) + return (XML_SCHEMAP_SRC_CT_1); + } + } else { + /* Base type is a complex type. */ + if ((base->contentType == XML_SCHEMA_CONTENT_SIMPLE) || + (base->contentType == XML_SCHEMA_CONTENT_BASIC)) { + /* + * 2.1.1 a complex type definition whose {content type} is a + * simple type definition; + * PASS + */ + if (base->contentTypeDef == NULL) { + xmlSchemaPCustomErr(ctxt, XML_SCHEMAP_INTERNAL, + WXS_BASIC_CAST type, NULL, + "Internal error: xmlSchemaCheckSRCCT, " + "'%s', base type has no content type", + type->name); + return (-1); + } + } else if ((base->contentType == XML_SCHEMA_CONTENT_MIXED) && + (WXS_IS_RESTRICTION(type))) { + + /* + * 2.1.2 only if the alternative is also + * chosen, a complex type definition whose {content type} + * is mixed and a particle emptiable. + */ + if (! xmlSchemaIsParticleEmptiable( + (xmlSchemaParticlePtr) base->subtypes)) { + ret = XML_SCHEMAP_SRC_CT_1; + } else + /* + * Attention: at this point the child is in + * ->contentTypeDef (put there during parsing). + */ + if (type->contentTypeDef == NULL) { + xmlChar *str = NULL; + /* + * 2.2 If clause 2.1.2 above is satisfied, then there + * must be a among the [children] of + * . + */ + /* TODO: Change error code to ..._SRC_CT_2_2. */ + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_SRC_CT_1, + WXS_BASIC_CAST type, NULL, + "A is expected among the children " + "of , if is used and " + "the base type '%s' is a complex type", + xmlSchemaFormatQName(&str, base->targetNamespace, + base->name)); + FREE_AND_NULL(str) + return (XML_SCHEMAP_SRC_CT_1); + } + } else { + ret = XML_SCHEMAP_SRC_CT_1; + } + } + if (ret > 0) { + xmlChar *str = NULL; + if (WXS_IS_RESTRICTION(type)) { + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_SRC_CT_1, + WXS_BASIC_CAST type, NULL, + "If and is used, the " + "base type must be a simple type or a complex type with " + "mixed content and particle emptiable. The base type " + "'%s' is none of those", + xmlSchemaFormatQName(&str, base->targetNamespace, + base->name)); + } else { + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_SRC_CT_1, + WXS_BASIC_CAST type, NULL, + "If and is used, the " + "base type must be a simple type. The base type '%s' " + "is a complex type", + xmlSchemaFormatQName(&str, base->targetNamespace, + base->name)); + } + FREE_AND_NULL(str) + } + } + /* + * SPEC (3) "The corresponding complex type definition component must + * satisfy the conditions set out in Constraints on Complex Type + * Definition Schema Components (§3.4.6);" + * NOTE (3) will be done in xmlSchemaTypeFixup(). + */ + /* + * SPEC (4) If clause 2.2.1 or clause 2.2.2 in the correspondence specification + * above for {attribute wildcard} is satisfied, the intensional + * intersection must be expressible, as defined in Attribute Wildcard + * Intersection (§3.10.6). + * NOTE (4) is done in xmlSchemaFixupTypeAttributeUses(). + */ + return (ret); +} + +#ifdef ENABLE_PARTICLE_RESTRICTION +/** + * xmlSchemaCheckParticleRangeOK: + * @ctxt: the schema parser context + * @type: the complex type definition + * + * (3.9.6) Constraints on Particle Schema Components + * Schema Component Constraint: + * Occurrence Range OK (range-ok) + * + * STATUS: complete + * + * Returns 0 if the constraints are satisfied, a positive + * error code if not and -1 if an internal error occured. + */ +static int +xmlSchemaCheckParticleRangeOK(int rmin, int rmax, + int bmin, int bmax) +{ + if (rmin < bmin) + return (1); + if ((bmax != UNBOUNDED) && + (rmax > bmax)) + return (1); + return (0); +} + +/** + * xmlSchemaCheckRCaseNameAndTypeOK: + * @ctxt: the schema parser context + * @r: the restricting element declaration particle + * @b: the base element declaration particle + * + * (3.9.6) Constraints on Particle Schema Components + * Schema Component Constraint: + * Particle Restriction OK (Elt:Elt -- NameAndTypeOK) + * (rcase-NameAndTypeOK) + * + * STATUS: + * MISSING (3.2.3) + * CLARIFY: (3.2.2) + * + * Returns 0 if the constraints are satisfied, a positive + * error code if not and -1 if an internal error occured. + */ +static int +xmlSchemaCheckRCaseNameAndTypeOK(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaParticlePtr r, + xmlSchemaParticlePtr b) +{ + xmlSchemaElementPtr elemR, elemB; + + /* TODO: Error codes (rcase-NameAndTypeOK). */ + elemR = (xmlSchemaElementPtr) r->children; + elemB = (xmlSchemaElementPtr) b->children; + /* + * SPEC (1) "The declarations' {name}s and {target namespace}s are + * the same." + */ + if ((elemR != elemB) && + ((! xmlStrEqual(elemR->name, elemB->name)) || + (! xmlStrEqual(elemR->targetNamespace, elemB->targetNamespace)))) + return (1); + /* + * SPEC (2) "R's occurrence range is a valid restriction of B's + * occurrence range as defined by Occurrence Range OK (§3.9.6)." + */ + if (xmlSchemaCheckParticleRangeOK(r->minOccurs, r->maxOccurs, + b->minOccurs, b->maxOccurs) != 0) + return (1); + /* + * SPEC (3.1) "Both B's declaration's {scope} and R's declaration's + * {scope} are global." + */ + if (elemR == elemB) + return (0); + /* + * SPEC (3.2.1) "Either B's {nillable} is true or R's {nillable} is false." + */ + if (((elemB->flags & XML_SCHEMAS_ELEM_NILLABLE) == 0) && + (elemR->flags & XML_SCHEMAS_ELEM_NILLABLE)) + return (1); + /* + * SPEC (3.2.2) "either B's declaration's {value constraint} is absent, + * or is not fixed, or R's declaration's {value constraint} is fixed + * with the same value." + */ + if ((elemB->value != NULL) && (elemB->flags & XML_SCHEMAS_ELEM_FIXED) && + ((elemR->value == NULL) || + ((elemR->flags & XML_SCHEMAS_ELEM_FIXED) == 0) || + /* TODO: Equality of the initial value or normalized or canonical? */ + (! xmlStrEqual(elemR->value, elemB->value)))) + return (1); + /* + * TODO: SPEC (3.2.3) "R's declaration's {identity-constraint + * definitions} is a subset of B's declaration's {identity-constraint + * definitions}, if any." + */ + if (elemB->idcs != NULL) { + /* TODO */ + } + /* + * SPEC (3.2.4) "R's declaration's {disallowed substitutions} is a + * superset of B's declaration's {disallowed substitutions}." + */ + if (((elemB->flags & XML_SCHEMAS_ELEM_BLOCK_EXTENSION) && + ((elemR->flags & XML_SCHEMAS_ELEM_BLOCK_EXTENSION) == 0)) || + ((elemB->flags & XML_SCHEMAS_ELEM_BLOCK_RESTRICTION) && + ((elemR->flags & XML_SCHEMAS_ELEM_BLOCK_RESTRICTION) == 0)) || + ((elemB->flags & XML_SCHEMAS_ELEM_BLOCK_SUBSTITUTION) && + ((elemR->flags & XML_SCHEMAS_ELEM_BLOCK_SUBSTITUTION) == 0))) + return (1); + /* + * SPEC (3.2.5) "R's {type definition} is validly derived given + * {extension, list, union} from B's {type definition}" + * + * BADSPEC TODO: What's the point of adding "list" and "union" to the + * set, if the corresponding constraints handle "restriction" and + * "extension" only? + * + */ + { + int set = 0; + + set |= SUBSET_EXTENSION; + set |= SUBSET_LIST; + set |= SUBSET_UNION; + if (xmlSchemaCheckCOSDerivedOK(ACTXT_CAST ctxt, elemR->subtypes, + elemB->subtypes, set) != 0) + return (1); + } + return (0); +} + +/** + * xmlSchemaCheckRCaseNSCompat: + * @ctxt: the schema parser context + * @r: the restricting element declaration particle + * @b: the base wildcard particle + * + * (3.9.6) Constraints on Particle Schema Components + * Schema Component Constraint: + * Particle Derivation OK (Elt:Any -- NSCompat) + * (rcase-NSCompat) + * + * STATUS: complete + * + * Returns 0 if the constraints are satisfied, a positive + * error code if not and -1 if an internal error occured. + */ +static int +xmlSchemaCheckRCaseNSCompat(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaParticlePtr r, + xmlSchemaParticlePtr b) +{ + /* TODO:Error codes (rcase-NSCompat). */ + /* + * SPEC "For an element declaration particle to be a ·valid restriction· + * of a wildcard particle all of the following must be true:" + * + * SPEC (1) "The element declaration's {target namespace} is ·valid· + * with respect to the wildcard's {namespace constraint} as defined by + * Wildcard allows Namespace Name (§3.10.4)." + */ + if (xmlSchemaCheckCVCWildcardNamespace((xmlSchemaWildcardPtr) b->children, + ((xmlSchemaElementPtr) r->children)->targetNamespace) != 0) + return (1); + /* + * SPEC (2) "R's occurrence range is a valid restriction of B's + * occurrence range as defined by Occurrence Range OK (§3.9.6)." + */ + if (xmlSchemaCheckParticleRangeOK(r->minOccurs, r->maxOccurs, + b->minOccurs, b->maxOccurs) != 0) + return (1); + + return (0); +} + +/** + * xmlSchemaCheckRCaseRecurseAsIfGroup: + * @ctxt: the schema parser context + * @r: the restricting element declaration particle + * @b: the base model group particle + * + * (3.9.6) Constraints on Particle Schema Components + * Schema Component Constraint: + * Particle Derivation OK (Elt:All/Choice/Sequence -- RecurseAsIfGroup) + * (rcase-RecurseAsIfGroup) + * + * STATUS: TODO + * + * Returns 0 if the constraints are satisfied, a positive + * error code if not and -1 if an internal error occured. + */ +static int +xmlSchemaCheckRCaseRecurseAsIfGroup(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaParticlePtr r, + xmlSchemaParticlePtr b) +{ + /* TODO: Error codes (rcase-RecurseAsIfGroup). */ + TODO + return (0); +} + +/** + * xmlSchemaCheckRCaseNSSubset: + * @ctxt: the schema parser context + * @r: the restricting wildcard particle + * @b: the base wildcard particle + * + * (3.9.6) Constraints on Particle Schema Components + * Schema Component Constraint: + * Particle Derivation OK (Any:Any -- NSSubset) + * (rcase-NSSubset) + * + * STATUS: complete + * + * Returns 0 if the constraints are satisfied, a positive + * error code if not and -1 if an internal error occured. + */ +static int +xmlSchemaCheckRCaseNSSubset(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaParticlePtr r, + xmlSchemaParticlePtr b, + int isAnyTypeBase) +{ + /* TODO: Error codes (rcase-NSSubset). */ + /* + * SPEC (1) "R's occurrence range is a valid restriction of B's + * occurrence range as defined by Occurrence Range OK (§3.9.6)." + */ + if (xmlSchemaCheckParticleRangeOK(r->minOccurs, r->maxOccurs, + b->minOccurs, b->maxOccurs)) + return (1); + /* + * SPEC (2) "R's {namespace constraint} must be an intensional subset + * of B's {namespace constraint} as defined by Wildcard Subset (§3.10.6)." + */ + if (xmlSchemaCheckCOSNSSubset((xmlSchemaWildcardPtr) r->children, + (xmlSchemaWildcardPtr) b->children)) + return (1); + /* + * SPEC (3) "Unless B is the content model wildcard of the ·ur-type + * definition·, R's {process contents} must be identical to or stronger + * than B's {process contents}, where strict is stronger than lax is + * stronger than skip." + */ + if (! isAnyTypeBase) { + if ( ((xmlSchemaWildcardPtr) r->children)->processContents < + ((xmlSchemaWildcardPtr) b->children)->processContents) + return (1); + } + + return (0); +} + +/** + * xmlSchemaCheckCOSParticleRestrict: + * @ctxt: the schema parser context + * @type: the complex type definition + * + * (3.9.6) Constraints on Particle Schema Components + * Schema Component Constraint: + * Particle Valid (Restriction) (cos-particle-restrict) + * + * STATUS: TODO + * + * Returns 0 if the constraints are satisfied, a positive + * error code if not and -1 if an internal error occured. + */ +static int +xmlSchemaCheckCOSParticleRestrict(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaParticlePtr r, + xmlSchemaParticlePtr b) +{ + int ret = 0; + + /*part = WXS_TYPE_PARTICLE(type); + basePart = WXS_TYPE_PARTICLE(base); + */ + + TODO + + /* + * SPEC (1) "They are the same particle." + */ + if (r == b) + return (0); + + + return (0); +} + +/** + * xmlSchemaCheckRCaseNSRecurseCheckCardinality: + * @ctxt: the schema parser context + * @r: the model group particle + * @b: the base wildcard particle + * + * (3.9.6) Constraints on Particle Schema Components + * Schema Component Constraint: + * Particle Derivation OK (All/Choice/Sequence:Any -- + * NSRecurseCheckCardinality) + * (rcase-NSRecurseCheckCardinality) + * + * STATUS: TODO: subst-groups + * + * Returns 0 if the constraints are satisfied, a positive + * error code if not and -1 if an internal error occured. + */ +static int +xmlSchemaCheckRCaseNSRecurseCheckCardinality(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaParticlePtr r, + xmlSchemaParticlePtr b) +{ + xmlSchemaParticlePtr part; + /* TODO: Error codes (rcase-NSRecurseCheckCardinality). */ + if ((r->children == NULL) || (r->children->children == NULL)) + return (-1); + /* + * SPEC "For a group particle to be a ·valid restriction· of a + * wildcard particle..." + * + * SPEC (1) "Every member of the {particles} of the group is a ·valid + * restriction· of the wildcard as defined by + * Particle Valid (Restriction) (§3.9.6)." + */ + part = (xmlSchemaParticlePtr) r->children->children; + do { + if (xmlSchemaCheckCOSParticleRestrict(ctxt, part, b)) + return (1); + part = (xmlSchemaParticlePtr) part->next; + } while (part != NULL); + /* + * SPEC (2) "The effective total range of the group [...] is a + * valid restriction of B's occurrence range as defined by + * Occurrence Range OK (§3.9.6)." + */ + if (xmlSchemaCheckParticleRangeOK( + xmlSchemaGetParticleTotalRangeMin(r), + xmlSchemaGetParticleTotalRangeMax(r), + b->minOccurs, b->maxOccurs) != 0) + return (1); + return (0); +} + +/** + * xmlSchemaCheckRCaseRecurse: + * @ctxt: the schema parser context + * @r: the or model group particle + * @b: the base or model group particle + * + * (3.9.6) Constraints on Particle Schema Components + * Schema Component Constraint: + * Particle Derivation OK (All:All,Sequence:Sequence -- + Recurse) + * (rcase-Recurse) + * + * STATUS: ? + * TODO: subst-groups + * + * Returns 0 if the constraints are satisfied, a positive + * error code if not and -1 if an internal error occured. + */ +static int +xmlSchemaCheckRCaseRecurse(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaParticlePtr r, + xmlSchemaParticlePtr b) +{ + /* xmlSchemaParticlePtr part; */ + /* TODO: Error codes (rcase-Recurse). */ + if ((r->children == NULL) || (b->children == NULL) || + (r->children->type != b->children->type)) + return (-1); + /* + * SPEC "For an all or sequence group particle to be a ·valid + * restriction· of another group particle with the same {compositor}..." + * + * SPEC (1) "R's occurrence range is a valid restriction of B's + * occurrence range as defined by Occurrence Range OK (§3.9.6)." + */ + if (xmlSchemaCheckParticleRangeOK(r->minOccurs, r->maxOccurs, + b->minOccurs, b->maxOccurs)) + return (1); + + + return (0); +} + +#endif + +#define FACET_RESTR_MUTUAL_ERR(fac1, fac2) \ + xmlSchemaPCustomErrExt(pctxt, \ + XML_SCHEMAP_INVALID_FACET_VALUE, \ + WXS_BASIC_CAST fac1, fac1->node, \ + "It is an error for both '%s' and '%s' to be specified on the "\ + "same type definition", \ + BAD_CAST xmlSchemaFacetTypeToString(fac1->type), \ + BAD_CAST xmlSchemaFacetTypeToString(fac2->type), NULL); + +#define FACET_RESTR_ERR(fac1, msg) \ + xmlSchemaPCustomErr(pctxt, \ + XML_SCHEMAP_INVALID_FACET_VALUE, \ + WXS_BASIC_CAST fac1, fac1->node, \ + msg, NULL); + +#define FACET_RESTR_FIXED_ERR(fac) \ + xmlSchemaPCustomErr(pctxt, \ + XML_SCHEMAP_INVALID_FACET_VALUE, \ + WXS_BASIC_CAST fac, fac->node, \ + "The base type's facet is 'fixed', thus the value must not " \ + "differ", NULL); + +static void +xmlSchemaDeriveFacetErr(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaFacetPtr facet1, + xmlSchemaFacetPtr facet2, + int lessGreater, + int orEqual, + int ofBase) +{ + xmlChar *msg = NULL; + + msg = xmlStrdup(BAD_CAST "'"); + msg = xmlStrcat(msg, xmlSchemaFacetTypeToString(facet1->type)); + msg = xmlStrcat(msg, BAD_CAST "' has to be"); + if (lessGreater == 0) + msg = xmlStrcat(msg, BAD_CAST " equal to"); + if (lessGreater == 1) + msg = xmlStrcat(msg, BAD_CAST " greater than"); + else + msg = xmlStrcat(msg, BAD_CAST " less than"); + + if (orEqual) + msg = xmlStrcat(msg, BAD_CAST " or equal to"); + msg = xmlStrcat(msg, BAD_CAST " '"); + msg = xmlStrcat(msg, xmlSchemaFacetTypeToString(facet2->type)); + if (ofBase) + msg = xmlStrcat(msg, BAD_CAST "' of the base type"); + else + msg = xmlStrcat(msg, BAD_CAST "'"); + + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_INVALID_FACET_VALUE, + WXS_BASIC_CAST facet1, NULL, + (const char *) msg, NULL); + + if (msg != NULL) + xmlFree(msg); +} + +/* +* xmlSchemaDeriveAndValidateFacets: +* +* Schema Component Constraint: Simple Type Restriction (Facets) +* (st-restrict-facets) +*/ +static int +xmlSchemaDeriveAndValidateFacets(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaTypePtr type) +{ + xmlSchemaTypePtr base = type->baseType; + xmlSchemaFacetLinkPtr link, cur, last = NULL; + xmlSchemaFacetPtr facet, bfacet, + flength = NULL, ftotdig = NULL, ffracdig = NULL, + fmaxlen = NULL, fminlen = NULL, /* facets of the current type */ + fmininc = NULL, fmaxinc = NULL, + fminexc = NULL, fmaxexc = NULL, + bflength = NULL, bftotdig = NULL, bffracdig = NULL, + bfmaxlen = NULL, bfminlen = NULL, /* facets of the base type */ + bfmininc = NULL, bfmaxinc = NULL, + bfminexc = NULL, bfmaxexc = NULL; + int res; /* err = 0, fixedErr; */ + + /* + * SPEC st-restrict-facets 1: + * "The {variety} of R is the same as that of B." + */ + /* + * SPEC st-restrict-facets 2: + * "If {variety} is atomic, the {primitive type definition} + * of R is the same as that of B." + * + * NOTE: we leave 1 & 2 out for now, since this will be + * satisfied by the derivation process. + * CONSTRUCTION TODO: Maybe needed if using a construction API. + */ + /* + * SPEC st-restrict-facets 3: + * "The {facets} of R are the union of S and the {facets} + * of B, eliminating duplicates. To eliminate duplicates, + * when a facet of the same kind occurs in both S and the + * {facets} of B, the one in the {facets} of B is not + * included, with the exception of enumeration and pattern + * facets, for which multiple occurrences with distinct values + * are allowed." + */ + + if ((type->facetSet == NULL) && (base->facetSet == NULL)) + return (0); + + last = type->facetSet; + if (last != NULL) + while (last->next != NULL) + last = last->next; + + for (cur = type->facetSet; cur != NULL; cur = cur->next) { + facet = cur->facet; + switch (facet->type) { + case XML_SCHEMA_FACET_LENGTH: + flength = facet; break; + case XML_SCHEMA_FACET_MINLENGTH: + fminlen = facet; break; + case XML_SCHEMA_FACET_MININCLUSIVE: + fmininc = facet; break; + case XML_SCHEMA_FACET_MINEXCLUSIVE: + fminexc = facet; break; + case XML_SCHEMA_FACET_MAXLENGTH: + fmaxlen = facet; break; + case XML_SCHEMA_FACET_MAXINCLUSIVE: + fmaxinc = facet; break; + case XML_SCHEMA_FACET_MAXEXCLUSIVE: + fmaxexc = facet; break; + case XML_SCHEMA_FACET_TOTALDIGITS: + ftotdig = facet; break; + case XML_SCHEMA_FACET_FRACTIONDIGITS: + ffracdig = facet; break; + default: + break; + } + } + for (cur = base->facetSet; cur != NULL; cur = cur->next) { + facet = cur->facet; + switch (facet->type) { + case XML_SCHEMA_FACET_LENGTH: + bflength = facet; break; + case XML_SCHEMA_FACET_MINLENGTH: + bfminlen = facet; break; + case XML_SCHEMA_FACET_MININCLUSIVE: + bfmininc = facet; break; + case XML_SCHEMA_FACET_MINEXCLUSIVE: + bfminexc = facet; break; + case XML_SCHEMA_FACET_MAXLENGTH: + bfmaxlen = facet; break; + case XML_SCHEMA_FACET_MAXINCLUSIVE: + bfmaxinc = facet; break; + case XML_SCHEMA_FACET_MAXEXCLUSIVE: + bfmaxexc = facet; break; + case XML_SCHEMA_FACET_TOTALDIGITS: + bftotdig = facet; break; + case XML_SCHEMA_FACET_FRACTIONDIGITS: + bffracdig = facet; break; + default: + break; + } + } + /* + * length and minLength or maxLength (2.2) + (3.2) + */ + if (flength && (fminlen || fmaxlen)) { + FACET_RESTR_ERR(flength, "It is an error for both 'length' and " + "either of 'minLength' or 'maxLength' to be specified on " + "the same type definition") + } + /* + * Mutual exclusions in the same derivation step. + */ + if ((fmaxinc) && (fmaxexc)) { + /* + * SCC "maxInclusive and maxExclusive" + */ + FACET_RESTR_MUTUAL_ERR(fmaxinc, fmaxexc) + } + if ((fmininc) && (fminexc)) { + /* + * SCC "minInclusive and minExclusive" + */ + FACET_RESTR_MUTUAL_ERR(fmininc, fminexc) + } + + if (flength && bflength) { + /* + * SCC "length valid restriction" + * The values have to be equal. + */ + res = xmlSchemaCompareValues(flength->val, bflength->val); + if (res == -2) + goto internal_error; + if (res != 0) + xmlSchemaDeriveFacetErr(pctxt, flength, bflength, 0, 0, 1); + if ((res != 0) && (bflength->fixed)) { + FACET_RESTR_FIXED_ERR(flength) + } + + } + if (fminlen && bfminlen) { + /* + * SCC "minLength valid restriction" + * minLength >= BASE minLength + */ + res = xmlSchemaCompareValues(fminlen->val, bfminlen->val); + if (res == -2) + goto internal_error; + if (res == -1) + xmlSchemaDeriveFacetErr(pctxt, fminlen, bfminlen, 1, 1, 1); + if ((res != 0) && (bfminlen->fixed)) { + FACET_RESTR_FIXED_ERR(fminlen) + } + } + if (fmaxlen && bfmaxlen) { + /* + * SCC "maxLength valid restriction" + * maxLength <= BASE minLength + */ + res = xmlSchemaCompareValues(fmaxlen->val, bfmaxlen->val); + if (res == -2) + goto internal_error; + if (res == 1) + xmlSchemaDeriveFacetErr(pctxt, fmaxlen, bfmaxlen, -1, 1, 1); + if ((res != 0) && (bfmaxlen->fixed)) { + FACET_RESTR_FIXED_ERR(fmaxlen) + } + } + /* + * SCC "length and minLength or maxLength" + */ + if (! flength) + flength = bflength; + if (flength) { + if (! fminlen) + flength = bflength; + if (fminlen) { + /* (1.1) length >= minLength */ + res = xmlSchemaCompareValues(flength->val, fminlen->val); + if (res == -2) + goto internal_error; + if (res == -1) + xmlSchemaDeriveFacetErr(pctxt, flength, fminlen, 1, 1, 0); + } + if (! fmaxlen) + fmaxlen = bfmaxlen; + if (fmaxlen) { + /* (2.1) length <= maxLength */ + res = xmlSchemaCompareValues(flength->val, fmaxlen->val); + if (res == -2) + goto internal_error; + if (res == 1) + xmlSchemaDeriveFacetErr(pctxt, flength, fmaxlen, -1, 1, 0); + } + } + if (fmaxinc) { + /* + * "maxInclusive" + */ + if (fmininc) { + /* SCC "maxInclusive >= minInclusive" */ + res = xmlSchemaCompareValues(fmaxinc->val, fmininc->val); + if (res == -2) + goto internal_error; + if (res == -1) { + xmlSchemaDeriveFacetErr(pctxt, fmaxinc, fmininc, 1, 1, 0); + } + } + /* + * SCC "maxInclusive valid restriction" + */ + if (bfmaxinc) { + /* maxInclusive <= BASE maxInclusive */ + res = xmlSchemaCompareValues(fmaxinc->val, bfmaxinc->val); + if (res == -2) + goto internal_error; + if (res == 1) + xmlSchemaDeriveFacetErr(pctxt, fmaxinc, bfmaxinc, -1, 1, 1); + if ((res != 0) && (bfmaxinc->fixed)) { + FACET_RESTR_FIXED_ERR(fmaxinc) + } + } + if (bfmaxexc) { + /* maxInclusive < BASE maxExclusive */ + res = xmlSchemaCompareValues(fmaxinc->val, bfmaxexc->val); + if (res == -2) + goto internal_error; + if (res != -1) { + xmlSchemaDeriveFacetErr(pctxt, fmaxinc, bfmaxexc, -1, 0, 1); + } + } + if (bfmininc) { + /* maxInclusive >= BASE minInclusive */ + res = xmlSchemaCompareValues(fmaxinc->val, bfmininc->val); + if (res == -2) + goto internal_error; + if (res == -1) { + xmlSchemaDeriveFacetErr(pctxt, fmaxinc, bfmininc, 1, 1, 1); + } + } + if (bfminexc) { + /* maxInclusive > BASE minExclusive */ + res = xmlSchemaCompareValues(fmaxinc->val, bfminexc->val); + if (res == -2) + goto internal_error; + if (res != 1) { + xmlSchemaDeriveFacetErr(pctxt, fmaxinc, bfminexc, 1, 0, 1); + } + } + } + if (fmaxexc) { + /* + * "maxExclusive >= minExclusive" + */ + if (fminexc) { + res = xmlSchemaCompareValues(fmaxexc->val, fminexc->val); + if (res == -2) + goto internal_error; + if (res == -1) { + xmlSchemaDeriveFacetErr(pctxt, fmaxexc, fminexc, 1, 1, 0); + } + } + /* + * "maxExclusive valid restriction" + */ + if (bfmaxexc) { + /* maxExclusive <= BASE maxExclusive */ + res = xmlSchemaCompareValues(fmaxexc->val, bfmaxexc->val); + if (res == -2) + goto internal_error; + if (res == 1) { + xmlSchemaDeriveFacetErr(pctxt, fmaxexc, bfmaxexc, -1, 1, 1); + } + if ((res != 0) && (bfmaxexc->fixed)) { + FACET_RESTR_FIXED_ERR(fmaxexc) + } + } + if (bfmaxinc) { + /* maxExclusive <= BASE maxInclusive */ + res = xmlSchemaCompareValues(fmaxexc->val, bfmaxinc->val); + if (res == -2) + goto internal_error; + if (res == 1) { + xmlSchemaDeriveFacetErr(pctxt, fmaxexc, bfmaxinc, -1, 1, 1); + } + } + if (bfmininc) { + /* maxExclusive > BASE minInclusive */ + res = xmlSchemaCompareValues(fmaxexc->val, bfmininc->val); + if (res == -2) + goto internal_error; + if (res != 1) { + xmlSchemaDeriveFacetErr(pctxt, fmaxexc, bfmininc, 1, 0, 1); + } + } + if (bfminexc) { + /* maxExclusive > BASE minExclusive */ + res = xmlSchemaCompareValues(fmaxexc->val, bfminexc->val); + if (res == -2) + goto internal_error; + if (res != 1) { + xmlSchemaDeriveFacetErr(pctxt, fmaxexc, bfminexc, 1, 0, 1); + } + } + } + if (fminexc) { + /* + * "minExclusive < maxInclusive" + */ + if (fmaxinc) { + res = xmlSchemaCompareValues(fminexc->val, fmaxinc->val); + if (res == -2) + goto internal_error; + if (res != -1) { + xmlSchemaDeriveFacetErr(pctxt, fminexc, fmaxinc, -1, 0, 0); + } + } + /* + * "minExclusive valid restriction" + */ + if (bfminexc) { + /* minExclusive >= BASE minExclusive */ + res = xmlSchemaCompareValues(fminexc->val, bfminexc->val); + if (res == -2) + goto internal_error; + if (res == -1) { + xmlSchemaDeriveFacetErr(pctxt, fminexc, bfminexc, 1, 1, 1); + } + if ((res != 0) && (bfminexc->fixed)) { + FACET_RESTR_FIXED_ERR(fminexc) + } + } + if (bfmaxinc) { + /* minExclusive <= BASE maxInclusive */ + res = xmlSchemaCompareValues(fminexc->val, bfmaxinc->val); + if (res == -2) + goto internal_error; + if (res == 1) { + xmlSchemaDeriveFacetErr(pctxt, fminexc, bfmaxinc, -1, 1, 1); + } + } + if (bfmininc) { + /* minExclusive >= BASE minInclusive */ + res = xmlSchemaCompareValues(fminexc->val, bfmininc->val); + if (res == -2) + goto internal_error; + if (res == -1) { + xmlSchemaDeriveFacetErr(pctxt, fminexc, bfmininc, 1, 1, 1); + } + } + if (bfmaxexc) { + /* minExclusive < BASE maxExclusive */ + res = xmlSchemaCompareValues(fminexc->val, bfmaxexc->val); + if (res == -2) + goto internal_error; + if (res != -1) { + xmlSchemaDeriveFacetErr(pctxt, fminexc, bfmaxexc, -1, 0, 1); + } + } + } + if (fmininc) { + /* + * "minInclusive < maxExclusive" + */ + if (fmaxexc) { + res = xmlSchemaCompareValues(fmininc->val, fmaxexc->val); + if (res == -2) + goto internal_error; + if (res != -1) { + xmlSchemaDeriveFacetErr(pctxt, fmininc, fmaxexc, -1, 0, 0); + } + } + /* + * "minExclusive valid restriction" + */ + if (bfmininc) { + /* minInclusive >= BASE minInclusive */ + res = xmlSchemaCompareValues(fmininc->val, bfmininc->val); + if (res == -2) + goto internal_error; + if (res == -1) { + xmlSchemaDeriveFacetErr(pctxt, fmininc, bfmininc, 1, 1, 1); + } + if ((res != 0) && (bfmininc->fixed)) { + FACET_RESTR_FIXED_ERR(fmininc) + } + } + if (bfmaxinc) { + /* minInclusive <= BASE maxInclusive */ + res = xmlSchemaCompareValues(fmininc->val, bfmaxinc->val); + if (res == -2) + goto internal_error; + if (res == 1) { + xmlSchemaDeriveFacetErr(pctxt, fmininc, bfmaxinc, -1, 1, 1); + } + } + if (bfminexc) { + /* minInclusive > BASE minExclusive */ + res = xmlSchemaCompareValues(fmininc->val, bfminexc->val); + if (res == -2) + goto internal_error; + if (res != 1) + xmlSchemaDeriveFacetErr(pctxt, fmininc, bfminexc, 1, 0, 1); + } + if (bfmaxexc) { + /* minInclusive < BASE maxExclusive */ + res = xmlSchemaCompareValues(fmininc->val, bfmaxexc->val); + if (res == -2) + goto internal_error; + if (res != -1) + xmlSchemaDeriveFacetErr(pctxt, fmininc, bfmaxexc, -1, 0, 1); + } + } + if (ftotdig && bftotdig) { + /* + * SCC " totalDigits valid restriction" + * totalDigits <= BASE totalDigits + */ + res = xmlSchemaCompareValues(ftotdig->val, bftotdig->val); + if (res == -2) + goto internal_error; + if (res == 1) + xmlSchemaDeriveFacetErr(pctxt, ftotdig, bftotdig, + -1, 1, 1); + if ((res != 0) && (bftotdig->fixed)) { + FACET_RESTR_FIXED_ERR(ftotdig) + } + } + if (ffracdig && bffracdig) { + /* + * SCC "fractionDigits valid restriction" + * fractionDigits <= BASE fractionDigits + */ + res = xmlSchemaCompareValues(ffracdig->val, bffracdig->val); + if (res == -2) + goto internal_error; + if (res == 1) + xmlSchemaDeriveFacetErr(pctxt, ffracdig, bffracdig, + -1, 1, 1); + if ((res != 0) && (bffracdig->fixed)) { + FACET_RESTR_FIXED_ERR(ffracdig) + } + } + /* + * SCC "fractionDigits less than or equal to totalDigits" + */ + if (! ftotdig) + ftotdig = bftotdig; + if (! ffracdig) + ffracdig = bffracdig; + if (ftotdig && ffracdig) { + res = xmlSchemaCompareValues(ffracdig->val, ftotdig->val); + if (res == -2) + goto internal_error; + if (res == 1) + xmlSchemaDeriveFacetErr(pctxt, ffracdig, ftotdig, + -1, 1, 0); + } + /* + * *Enumerations* won' be added here, since only the first set + * of enumerations in the ancestor-or-self axis is used + * for validation, plus we need to use the base type of those + * enumerations for whitespace. + * + * *Patterns*: won't be add here, since they are ORed at + * type level and ANDed at ancestor level. This will + * happed during validation by walking the base axis + * of the type. + */ + for (cur = base->facetSet; cur != NULL; cur = cur->next) { + bfacet = cur->facet; + /* + * Special handling of enumerations and patterns. + * TODO: hmm, they should not appear in the set, so remove this. + */ + if ((bfacet->type == XML_SCHEMA_FACET_PATTERN) || + (bfacet->type == XML_SCHEMA_FACET_ENUMERATION)) + continue; + /* + * Search for a duplicate facet in the current type. + */ + link = type->facetSet; + /* err = 0; */ + /* fixedErr = 0; */ + while (link != NULL) { + facet = link->facet; + if (facet->type == bfacet->type) { + switch (facet->type) { + case XML_SCHEMA_FACET_WHITESPACE: + /* + * The whitespace must be stronger. + */ + if (facet->whitespace < bfacet->whitespace) { + FACET_RESTR_ERR(flength, + "The 'whitespace' value has to be equal to " + "or stronger than the 'whitespace' value of " + "the base type") + } + if ((bfacet->fixed) && + (facet->whitespace != bfacet->whitespace)) { + FACET_RESTR_FIXED_ERR(facet) + } + break; + default: + break; + } + /* Duplicate found. */ + break; + } + link = link->next; + } + /* + * If no duplicate was found: add the base types's facet + * to the set. + */ + if (link == NULL) { + link = (xmlSchemaFacetLinkPtr) + xmlMalloc(sizeof(xmlSchemaFacetLink)); + if (link == NULL) { + xmlSchemaPErrMemory(pctxt, + "deriving facets, creating a facet link", NULL); + return (-1); + } + link->facet = cur->facet; + link->next = NULL; + if (last == NULL) + type->facetSet = link; + else + last->next = link; + last = link; + } + + } + + return (0); +internal_error: + PERROR_INT("xmlSchemaDeriveAndValidateFacets", + "an error occured"); + return (-1); +} + +static int +xmlSchemaFinishMemberTypeDefinitionsProperty(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaTypePtr type) +{ + xmlSchemaTypeLinkPtr link, lastLink, prevLink, subLink, newLink; + /* + * The actual value is then formed by replacing any union type + * definition in the ·explicit members· with the members of their + * {member type definitions}, in order. + * + * TODO: There's a bug entry at + * "http://lists.w3.org/Archives/Public/www-xml-schema-comments/2005JulSep/0287.html" + * which indicates that we'll keep the union types the future. + */ + link = type->memberTypes; + while (link != NULL) { + + if (WXS_IS_TYPE_NOT_FIXED(link->type)) + xmlSchemaTypeFixup(link->type, ACTXT_CAST pctxt); + + if (WXS_IS_UNION(link->type)) { + subLink = xmlSchemaGetUnionSimpleTypeMemberTypes(link->type); + if (subLink != NULL) { + link->type = subLink->type; + if (subLink->next != NULL) { + lastLink = link->next; + subLink = subLink->next; + prevLink = link; + while (subLink != NULL) { + newLink = (xmlSchemaTypeLinkPtr) + xmlMalloc(sizeof(xmlSchemaTypeLink)); + if (newLink == NULL) { + xmlSchemaPErrMemory(pctxt, "allocating a type link", + NULL); + return (-1); + } + newLink->type = subLink->type; + prevLink->next = newLink; + prevLink = newLink; + newLink->next = lastLink; + + subLink = subLink->next; + } + } + } + } + link = link->next; + } + return (0); +} + +static void +xmlSchemaTypeFixupOptimFacets(xmlSchemaTypePtr type) +{ + int has = 0, needVal = 0, normVal = 0; + + has = (type->baseType->flags & XML_SCHEMAS_TYPE_HAS_FACETS) ? 1 : 0; + if (has) { + needVal = (type->baseType->flags & + XML_SCHEMAS_TYPE_FACETSNEEDVALUE) ? 1 : 0; + normVal = (type->baseType->flags & + XML_SCHEMAS_TYPE_NORMVALUENEEDED) ? 1 : 0; + } + if (type->facets != NULL) { + xmlSchemaFacetPtr fac; + + for (fac = type->facets; fac != NULL; fac = fac->next) { + switch (fac->type) { + case XML_SCHEMA_FACET_WHITESPACE: + break; + case XML_SCHEMA_FACET_PATTERN: + normVal = 1; + has = 1; + break; + case XML_SCHEMA_FACET_ENUMERATION: + needVal = 1; + normVal = 1; + has = 1; + break; + default: + has = 1; + break; + } + } + } + if (normVal) + type->flags |= XML_SCHEMAS_TYPE_NORMVALUENEEDED; + if (needVal) + type->flags |= XML_SCHEMAS_TYPE_FACETSNEEDVALUE; + if (has) + type->flags |= XML_SCHEMAS_TYPE_HAS_FACETS; + + if (has && (! needVal) && WXS_IS_ATOMIC(type)) { + xmlSchemaTypePtr prim = xmlSchemaGetPrimitiveType(type); + /* + * OPTIMIZE VAL TODO: Some facets need a computed value. + */ + if ((prim->builtInType != XML_SCHEMAS_ANYSIMPLETYPE) && + (prim->builtInType != XML_SCHEMAS_STRING)) { + type->flags |= XML_SCHEMAS_TYPE_FACETSNEEDVALUE; + } + } +} + +static int +xmlSchemaTypeFixupWhitespace(xmlSchemaTypePtr type) +{ + + + /* + * Evaluate the whitespace-facet value. + */ + if (WXS_IS_LIST(type)) { + type->flags |= XML_SCHEMAS_TYPE_WHITESPACE_COLLAPSE; + return (0); + } else if (WXS_IS_UNION(type)) + return (0); + + if (type->facetSet != NULL) { + xmlSchemaFacetLinkPtr lin; + + for (lin = type->facetSet; lin != NULL; lin = lin->next) { + if (lin->facet->type == XML_SCHEMA_FACET_WHITESPACE) { + switch (lin->facet->whitespace) { + case XML_SCHEMAS_FACET_PRESERVE: + type->flags |= XML_SCHEMAS_TYPE_WHITESPACE_PRESERVE; + break; + case XML_SCHEMAS_FACET_REPLACE: + type->flags |= XML_SCHEMAS_TYPE_WHITESPACE_REPLACE; + break; + case XML_SCHEMAS_FACET_COLLAPSE: + type->flags |= XML_SCHEMAS_TYPE_WHITESPACE_COLLAPSE; + break; + default: + return (-1); + } + return (0); + } + } + } + /* + * For all ·atomic· datatypes other than string (and types ·derived· + * by ·restriction· from it) the value of whiteSpace is fixed to + * collapse + */ + { + xmlSchemaTypePtr anc; + + for (anc = type->baseType; anc != NULL && + anc->builtInType != XML_SCHEMAS_ANYTYPE; + anc = anc->baseType) { + + if (anc->type == XML_SCHEMA_TYPE_BASIC) { + if (anc->builtInType == XML_SCHEMAS_NORMSTRING) { + type->flags |= XML_SCHEMAS_TYPE_WHITESPACE_REPLACE; + + } else if ((anc->builtInType == XML_SCHEMAS_STRING) || + (anc->builtInType == XML_SCHEMAS_ANYSIMPLETYPE)) { + type->flags |= XML_SCHEMAS_TYPE_WHITESPACE_PRESERVE; + + } else + type->flags |= XML_SCHEMAS_TYPE_WHITESPACE_COLLAPSE; + break; + } + } + } + return (0); +} + +static int +xmlSchemaFixupSimpleTypeStageOne(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaTypePtr type) +{ + if (type->type != XML_SCHEMA_TYPE_SIMPLE) + return(0); + if (! WXS_IS_TYPE_NOT_FIXED_1(type)) + return(0); + type->flags |= XML_SCHEMAS_TYPE_FIXUP_1; + + if (WXS_IS_LIST(type)) { + /* + * Corresponds to ... + */ + if (type->subtypes == NULL) { + /* + * This one is really needed, so get out. + */ + PERROR_INT("xmlSchemaFixupSimpleTypeStageOne", + "list type has no item-type assigned"); + return(-1); + } + } else if (WXS_IS_UNION(type)) { + /* + * Corresponds to ... + */ + if (type->memberTypes == NULL) { + /* + * This one is really needed, so get out. + */ + PERROR_INT("xmlSchemaFixupSimpleTypeStageOne", + "union type has no member-types assigned"); + return(-1); + } + } else { + /* + * Corresponds to ... + */ + if (type->baseType == NULL) { + PERROR_INT("xmlSchemaFixupSimpleTypeStageOne", + "type has no base-type assigned"); + return(-1); + } + if (WXS_IS_TYPE_NOT_FIXED_1(type->baseType)) + if (xmlSchemaFixupSimpleTypeStageOne(pctxt, type->baseType) == -1) + return(-1); + /* + * Variety + * If the alternative is chosen, then the + * {variety} of the {base type definition}. + */ + if (WXS_IS_ATOMIC(type->baseType)) + type->flags |= XML_SCHEMAS_TYPE_VARIETY_ATOMIC; + else if (WXS_IS_LIST(type->baseType)) { + type->flags |= XML_SCHEMAS_TYPE_VARIETY_LIST; + /* + * Inherit the itemType. + */ + type->subtypes = type->baseType->subtypes; + } else if (WXS_IS_UNION(type->baseType)) { + type->flags |= XML_SCHEMAS_TYPE_VARIETY_UNION; + /* + * NOTE that we won't assign the memberTypes of the base, + * since this will make trouble when freeing them; we will + * use a lookup function to access them instead. + */ + } + } + return(0); +} + +#ifdef DEBUG_TYPE +static void +xmlSchemaDebugFixedType(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaTypePtr type) +{ + if (type->node != NULL) { + xmlGenericError(xmlGenericErrorContext, + "Type of %s : %s:%d :", name, + type->node->doc->URL, + xmlGetLineNo(type->node)); + } else { + xmlGenericError(xmlGenericErrorContext, "Type of %s :", name); + } + if ((WXS_IS_SIMPLE(type)) || (WXS_IS_COMPLEX(type))) { + switch (type->contentType) { + case XML_SCHEMA_CONTENT_SIMPLE: + xmlGenericError(xmlGenericErrorContext, "simple\n"); + break; + case XML_SCHEMA_CONTENT_ELEMENTS: + xmlGenericError(xmlGenericErrorContext, "elements\n"); + break; + case XML_SCHEMA_CONTENT_UNKNOWN: + xmlGenericError(xmlGenericErrorContext, "unknown !!!\n"); + break; + case XML_SCHEMA_CONTENT_EMPTY: + xmlGenericError(xmlGenericErrorContext, "empty\n"); + break; + case XML_SCHEMA_CONTENT_MIXED: + if (xmlSchemaIsParticleEmptiable((xmlSchemaParticlePtr) + type->subtypes)) + xmlGenericError(xmlGenericErrorContext, + "mixed as emptiable particle\n"); + else + xmlGenericError(xmlGenericErrorContext, "mixed\n"); + break; + /* Removed, since not used. */ + /* + case XML_SCHEMA_CONTENT_MIXED_OR_ELEMENTS: + xmlGenericError(xmlGenericErrorContext, "mixed or elems\n"); + break; + */ + case XML_SCHEMA_CONTENT_BASIC: + xmlGenericError(xmlGenericErrorContext, "basic\n"); + break; + default: + xmlGenericError(xmlGenericErrorContext, + "not registered !!!\n"); + break; + } + } +} +#endif + +/* +* 3.14.6 Constraints on Simple Type Definition Schema Components +*/ +static int +xmlSchemaFixupSimpleTypeStageTwo(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaTypePtr type) +{ + int res, olderrs = pctxt->nberrors; + + if (type->type != XML_SCHEMA_TYPE_SIMPLE) + return(-1); + + if (! WXS_IS_TYPE_NOT_FIXED(type)) + return(0); + + type->flags |= XML_SCHEMAS_TYPE_INTERNAL_RESOLVED; + type->contentType = XML_SCHEMA_CONTENT_SIMPLE; + + if (type->baseType == NULL) { + PERROR_INT("xmlSchemaFixupSimpleTypeStageTwo", + "missing baseType"); + goto exit_failure; + } + if (WXS_IS_TYPE_NOT_FIXED(type->baseType)) + xmlSchemaTypeFixup(type->baseType, ACTXT_CAST pctxt); + /* + * If a member type of a union is a union itself, we need to substitute + * that member type for its member types. + * NOTE that this might change in WXS 1.1; i.e. we will keep the union + * types in WXS 1.1. + */ + if ((type->memberTypes != NULL) && + (xmlSchemaFinishMemberTypeDefinitionsProperty(pctxt, type) == -1)) + return(-1); + /* + * SPEC src-simple-type 1 + * "The corresponding simple type definition, if any, must satisfy + * the conditions set out in Constraints on Simple Type Definition + * Schema Components (§3.14.6)." + */ + /* + * Schema Component Constraint: Simple Type Definition Properties Correct + * (st-props-correct) + */ + res = xmlSchemaCheckSTPropsCorrect(pctxt, type); + HFAILURE HERROR + /* + * Schema Component Constraint: Derivation Valid (Restriction, Simple) + * (cos-st-restricts) + */ + res = xmlSchemaCheckCOSSTRestricts(pctxt, type); + HFAILURE HERROR + /* + * TODO: Removed the error report, since it got annoying to get an + * extra error report, if anything failed until now. + * Enable this if needed. + * + * xmlSchemaPErr(ctxt, type->node, + * XML_SCHEMAP_SRC_SIMPLE_TYPE_1, + * "Simple type '%s' does not satisfy the constraints " + * "on simple type definitions.\n", + * type->name, NULL); + */ + /* + * Schema Component Constraint: Simple Type Restriction (Facets) + * (st-restrict-facets) + */ + res = xmlSchemaCheckFacetValues(type, pctxt); + HFAILURE HERROR + if ((type->facetSet != NULL) || + (type->baseType->facetSet != NULL)) { + res = xmlSchemaDeriveAndValidateFacets(pctxt, type); + HFAILURE HERROR + } + /* + * Whitespace value. + */ + res = xmlSchemaTypeFixupWhitespace(type); + HFAILURE HERROR + xmlSchemaTypeFixupOptimFacets(type); + +exit_error: +#ifdef DEBUG_TYPE + xmlSchemaDebugFixedType(pctxt, type); +#endif + if (olderrs != pctxt->nberrors) + return(pctxt->err); + return(0); + +exit_failure: +#ifdef DEBUG_TYPE + xmlSchemaDebugFixedType(pctxt, type); +#endif + return(-1); +} + +static int +xmlSchemaFixupComplexType(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaTypePtr type) +{ + int res = 0, olderrs = pctxt->nberrors; + xmlSchemaTypePtr baseType = type->baseType; + + if (! WXS_IS_TYPE_NOT_FIXED(type)) + return(0); + type->flags |= XML_SCHEMAS_TYPE_INTERNAL_RESOLVED; + if (baseType == NULL) { + PERROR_INT("xmlSchemaFixupComplexType", + "missing baseType"); + goto exit_failure; + } + /* + * Fixup the base type. + */ + if (WXS_IS_TYPE_NOT_FIXED(baseType)) + xmlSchemaTypeFixup(baseType, ACTXT_CAST pctxt); + if (baseType->flags & XML_SCHEMAS_TYPE_INTERNAL_INVALID) { + /* + * Skip fixup if the base type is invalid. + * TODO: Generate a warning! + */ + return(0); + } + /* + * This basically checks if the base type can be derived. + */ + res = xmlSchemaCheckSRCCT(pctxt, type); + HFAILURE HERROR + /* + * Fixup the content type. + */ + if (type->contentType == XML_SCHEMA_CONTENT_SIMPLE) { + /* + * Corresponds to ... + */ + if ((WXS_IS_COMPLEX(baseType)) && + (baseType->contentTypeDef != NULL) && + (WXS_IS_RESTRICTION(type))) { + xmlSchemaTypePtr contentBase, content; +#ifdef ENABLE_NAMED_LOCALS + char buf[30]; + const xmlChar *tmpname; +#endif + /* + * SPEC (1) If + base type is , + * "whose own {content type} is a simple type..." + */ + if (type->contentTypeDef != NULL) { + /* + * SPEC (1.1) "the simple type definition corresponding to the + * among the [children] of if there + * is one;" + * Note that this " among the [children]" was put + * into ->contentTypeDef during parsing. + */ + contentBase = type->contentTypeDef; + type->contentTypeDef = NULL; + } else { + /* + * (1.2) "...otherwise ( has no + * among its [children]), the simple type definition which + * is the {content type} of the ... base type." + */ + contentBase = baseType->contentTypeDef; + } + /* + * SPEC + * "... a simple type definition which restricts the simple + * type definition identified in clause 1.1 or clause 1.2 + * with a set of facet components" + * + * Create the anonymous simple type, which will be the content + * type of the complex type. + */ +#ifdef ENABLE_NAMED_LOCALS + snprintf(buf, 29, "#scST%d", ++(pctxt->counter)); + tmpname = xmlDictLookup(pctxt->dict, BAD_CAST buf, -1); + content = xmlSchemaAddType(pctxt, pctxt->schema, + XML_SCHEMA_TYPE_SIMPLE, tmpname, type->targetNamespace, + type->node, 0); +#else + content = xmlSchemaAddType(pctxt, pctxt->schema, + XML_SCHEMA_TYPE_SIMPLE, NULL, type->targetNamespace, + type->node, 0); +#endif + if (content == NULL) + goto exit_failure; + /* + * We will use the same node as for the + * to have it somehow anchored in the schema doc. + */ + content->type = XML_SCHEMA_TYPE_SIMPLE; + content->baseType = contentBase; + /* + * Move the facets, previously anchored on the + * complexType during parsing. + */ + content->facets = type->facets; + type->facets = NULL; + content->facetSet = type->facetSet; + type->facetSet = NULL; + + type->contentTypeDef = content; + if (WXS_IS_TYPE_NOT_FIXED(contentBase)) + xmlSchemaTypeFixup(contentBase, ACTXT_CAST pctxt); + /* + * Fixup the newly created type. We don't need to check + * for circularity here. + */ + res = xmlSchemaFixupSimpleTypeStageOne(pctxt, content); + HFAILURE HERROR + res = xmlSchemaFixupSimpleTypeStageTwo(pctxt, content); + HFAILURE HERROR + + } else if ((WXS_IS_COMPLEX(baseType)) && + (baseType->contentType == XML_SCHEMA_CONTENT_MIXED) && + (WXS_IS_RESTRICTION(type))) { + /* + * SPEC (2) If + base is a mixed with + * an emptiable particle, then a simple type definition which + * restricts the 's child. + */ + if ((type->contentTypeDef == NULL) || + (type->contentTypeDef->baseType == NULL)) { + /* + * TODO: Check if this ever happens. + */ + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_INTERNAL, + WXS_BASIC_CAST type, NULL, + "Internal error: xmlSchemaTypeFixup, " + "complex type '%s': the " + "is missing a child, but was not catched " + "by xmlSchemaCheckSRCCT()", type->name); + goto exit_failure; + } + } else if ((WXS_IS_COMPLEX(baseType)) && WXS_IS_EXTENSION(type)) { + /* + * SPEC (3) If + base is with + * content, "...then the {content type} of that + * complex type definition" + */ + if (baseType->contentTypeDef == NULL) { + /* + * TODO: Check if this ever happens. xmlSchemaCheckSRCCT + * should have catched this already. + */ + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_INTERNAL, + WXS_BASIC_CAST type, NULL, + "Internal error: xmlSchemaTypeFixup, " + "complex type '%s': the ed base type is " + "a complex type with no simple content type", + type->name); + goto exit_failure; + } + type->contentTypeDef = baseType->contentTypeDef; + } else if ((WXS_IS_SIMPLE(baseType)) && WXS_IS_EXTENSION(type)) { + /* + * SPEC (4) + base is + * "... then that simple type definition" + */ + type->contentTypeDef = baseType; + } else { + /* + * TODO: Check if this ever happens. + */ + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_INTERNAL, + WXS_BASIC_CAST type, NULL, + "Internal error: xmlSchemaTypeFixup, " + "complex type '%s' with : unhandled " + "derivation case", type->name); + goto exit_failure; + } + } else { + int dummySequence = 0; + xmlSchemaParticlePtr particle = + (xmlSchemaParticlePtr) type->subtypes; + /* + * Corresponds to ... + * + * NOTE that the effective mixed was already set during parsing of + * and ; its flag value is + * XML_SCHEMAS_TYPE_MIXED. + * + * Compute the "effective content": + * (2.1.1) + (2.1.2) + (2.1.3) + */ + if ((particle == NULL) || + ((particle->type == XML_SCHEMA_TYPE_PARTICLE) && + ((particle->children->type == XML_SCHEMA_TYPE_ALL) || + (particle->children->type == XML_SCHEMA_TYPE_SEQUENCE) || + ((particle->children->type == XML_SCHEMA_TYPE_CHOICE) && + (particle->minOccurs == 0))) && + ( ((xmlSchemaTreeItemPtr) particle->children)->children == NULL))) { + if (type->flags & XML_SCHEMAS_TYPE_MIXED) { + /* + * SPEC (2.1.4) "If the ·effective mixed· is true, then + * a particle whose properties are as follows:..." + * + * Empty sequence model group with + * minOccurs/maxOccurs = 1 (i.e. a "particle emptiable"). + * NOTE that we sill assign it the node to + * somehow anchor it in the doc. + */ + if ((particle == NULL) || + (particle->children->type != XML_SCHEMA_TYPE_SEQUENCE)) { + /* + * Create the particle. + */ + particle = xmlSchemaAddParticle(pctxt, + type->node, 1, 1); + if (particle == NULL) + goto exit_failure; + /* + * Create the model group. + */ /* URGENT TODO: avoid adding to pending items. */ + particle->children = (xmlSchemaTreeItemPtr) + xmlSchemaAddModelGroup(pctxt, pctxt->schema, + XML_SCHEMA_TYPE_SEQUENCE, type->node); + if (particle->children == NULL) + goto exit_failure; + + type->subtypes = (xmlSchemaTypePtr) particle; + } + dummySequence = 1; + type->contentType = XML_SCHEMA_CONTENT_ELEMENTS; + } else { + /* + * SPEC (2.1.5) "otherwise empty" + */ + type->contentType = XML_SCHEMA_CONTENT_EMPTY; + } + } else { + /* + * SPEC (2.2) "otherwise the particle corresponding to the + * , , or among the + * [children]." + */ + type->contentType = XML_SCHEMA_CONTENT_ELEMENTS; + } + /* + * Compute the "content type". + */ + if (WXS_IS_RESTRICTION(type)) { + /* + * SPEC (3.1) "If ..." + * (3.1.1) + (3.1.2) */ + if (type->contentType != XML_SCHEMA_CONTENT_EMPTY) { + if (type->flags & XML_SCHEMAS_TYPE_MIXED) + type->contentType = XML_SCHEMA_CONTENT_MIXED; + } + } else { + /* + * SPEC (3.2) "If ..." + */ + if (type->contentType == XML_SCHEMA_CONTENT_EMPTY) { + /* + * SPEC (3.2.1) + */ + type->contentType = baseType->contentType; + type->subtypes = baseType->subtypes; + /* + * NOTE that the effective mixed is ignored here. + */ + } else if (baseType->contentType == XML_SCHEMA_CONTENT_EMPTY) { + /* + * SPEC (3.2.2) + */ + if (type->flags & XML_SCHEMAS_TYPE_MIXED) + type->contentType = XML_SCHEMA_CONTENT_MIXED; + } else { + /* + * SPEC (3.2.3) + */ + if (type->flags & XML_SCHEMAS_TYPE_MIXED) + type->contentType = XML_SCHEMA_CONTENT_MIXED; + /* + * "A model group whose {compositor} is sequence and whose + * {particles} are..." + */ + if ((WXS_TYPE_PARTICLE(type) != NULL) && + (WXS_TYPE_PARTICLE_TERM(type) != NULL) && + ((WXS_TYPE_PARTICLE_TERM(type))->type == + XML_SCHEMA_TYPE_ALL)) + { + /* + * SPEC cos-all-limited (1) + */ + xmlSchemaCustomErr(ACTXT_CAST pctxt, + /* TODO: error code */ + XML_SCHEMAP_COS_ALL_LIMITED, + WXS_ITEM_NODE(type), NULL, + "The type has an 'all' model group in its " + "{content type} and thus cannot be derived from " + "a non-empty type, since this would produce a " + "'sequence' model group containing the 'all' " + "model group; 'all' model groups are not " + "allowed to appear inside other model groups", + NULL, NULL); + + } else if ((WXS_TYPE_PARTICLE(baseType) != NULL) && + (WXS_TYPE_PARTICLE_TERM(baseType) != NULL) && + ((WXS_TYPE_PARTICLE_TERM(baseType))->type == + XML_SCHEMA_TYPE_ALL)) + { + /* + * SPEC cos-all-limited (1) + */ + xmlSchemaCustomErr(ACTXT_CAST pctxt, + /* TODO: error code */ + XML_SCHEMAP_COS_ALL_LIMITED, + WXS_ITEM_NODE(type), NULL, + "A type cannot be derived by extension from a type " + "which has an 'all' model group in its " + "{content type}, since this would produce a " + "'sequence' model group containing the 'all' " + "model group; 'all' model groups are not " + "allowed to appear inside other model groups", + NULL, NULL); + + } else if (! dummySequence) { + xmlSchemaTreeItemPtr effectiveContent = + (xmlSchemaTreeItemPtr) type->subtypes; + /* + * Create the particle. + */ + particle = xmlSchemaAddParticle(pctxt, + type->node, 1, 1); + if (particle == NULL) + goto exit_failure; + /* + * Create the "sequence" model group. + */ + particle->children = (xmlSchemaTreeItemPtr) + xmlSchemaAddModelGroup(pctxt, pctxt->schema, + XML_SCHEMA_TYPE_SEQUENCE, type->node); + if (particle->children == NULL) + goto exit_failure; + WXS_TYPE_CONTENTTYPE(type) = (xmlSchemaTypePtr) particle; + /* + * SPEC "the particle of the {content type} of + * the ... base ..." + * Create a duplicate of the base type's particle + * and assign its "term" to it. + */ + particle->children->children = + (xmlSchemaTreeItemPtr) xmlSchemaAddParticle(pctxt, + type->node, + ((xmlSchemaParticlePtr) type->subtypes)->minOccurs, + ((xmlSchemaParticlePtr) type->subtypes)->maxOccurs); + if (particle->children->children == NULL) + goto exit_failure; + particle = (xmlSchemaParticlePtr) + particle->children->children; + particle->children = + ((xmlSchemaParticlePtr) baseType->subtypes)->children; + /* + * SPEC "followed by the ·effective content·." + */ + particle->next = effectiveContent; + /* + * This all will result in: + * new-particle + * --> new-sequence( + * new-particle + * --> base-model, + * this-particle + * --> this-model + * ) + */ + } else { + /* + * This is the case when there is already an empty + * with minOccurs==maxOccurs==1. + * Just add the base types's content type. + * NOTE that, although we miss to add an intermediate + * , this should produce no difference to + * neither the regex compilation of the content model, + * nor to the complex type contraints. + */ + particle->children->children = + (xmlSchemaTreeItemPtr) baseType->subtypes; + } + } + } + } + /* + * Now fixup attribute uses: + * - expand attr. group references + * - intersect attribute wildcards + * - inherit attribute uses of the base type + * - inherit or union attr. wildcards if extending + * - apply attr. use prohibitions if restricting + */ + res = xmlSchemaFixupTypeAttributeUses(pctxt, type); + HFAILURE HERROR + /* + * Apply the complex type component constraints; this will not + * check attributes, since this is done in + * xmlSchemaFixupTypeAttributeUses(). + */ + res = xmlSchemaCheckCTComponent(pctxt, type); + HFAILURE HERROR + +#ifdef DEBUG_TYPE + xmlSchemaDebugFixedType(pctxt, type); +#endif + if (olderrs != pctxt->nberrors) + return(pctxt->err); + else + return(0); + +exit_error: + type->flags |= XML_SCHEMAS_TYPE_INTERNAL_INVALID; +#ifdef DEBUG_TYPE + xmlSchemaDebugFixedType(pctxt, type); +#endif + return(pctxt->err); + +exit_failure: + type->flags |= XML_SCHEMAS_TYPE_INTERNAL_INVALID; +#ifdef DEBUG_TYPE + xmlSchemaDebugFixedType(pctxt, type); +#endif + return(-1); +} + + +/** + * xmlSchemaTypeFixup: + * @typeDecl: the schema type definition + * @ctxt: the schema parser context + * + * Fixes the content model of the type. + * URGENT TODO: We need an int result! + */ +static int +xmlSchemaTypeFixup(xmlSchemaTypePtr type, + xmlSchemaAbstractCtxtPtr actxt) +{ + if (type == NULL) + return(0); + if (actxt->type != XML_SCHEMA_CTXT_PARSER) { + AERROR_INT("xmlSchemaTypeFixup", + "this function needs a parser context"); + return(-1); + } + if (! WXS_IS_TYPE_NOT_FIXED(type)) + return(0); + if (type->type == XML_SCHEMA_TYPE_COMPLEX) + return(xmlSchemaFixupComplexType(PCTXT_CAST actxt, type)); + else if (type->type == XML_SCHEMA_TYPE_SIMPLE) + return(xmlSchemaFixupSimpleTypeStageTwo(PCTXT_CAST actxt, type)); + return(0); +} + +/** + * xmlSchemaCheckFacet: + * @facet: the facet + * @typeDecl: the schema type definition + * @pctxt: the schema parser context or NULL + * @name: the optional name of the type + * + * Checks and computes the values of facets. + * + * Returns 0 if valid, a positive error code if not valid and + * -1 in case of an internal or API error. + */ +int +xmlSchemaCheckFacet(xmlSchemaFacetPtr facet, + xmlSchemaTypePtr typeDecl, + xmlSchemaParserCtxtPtr pctxt, + const xmlChar * name ATTRIBUTE_UNUSED) +{ + int ret = 0, ctxtGiven; + + if ((facet == NULL) || (typeDecl == NULL)) + return(-1); + /* + * TODO: will the parser context be given if used from + * the relaxNG module? + */ + if (pctxt == NULL) + ctxtGiven = 0; + else + ctxtGiven = 1; + + switch (facet->type) { + case XML_SCHEMA_FACET_MININCLUSIVE: + case XML_SCHEMA_FACET_MINEXCLUSIVE: + case XML_SCHEMA_FACET_MAXINCLUSIVE: + case XML_SCHEMA_FACET_MAXEXCLUSIVE: + case XML_SCHEMA_FACET_ENUMERATION: { + /* + * Okay we need to validate the value + * at that point. + */ + xmlSchemaTypePtr base; + + /* 4.3.5.5 Constraints on enumeration Schema Components + * Schema Component Constraint: enumeration valid restriction + * It is an ·error· if any member of {value} is not in the + * ·value space· of {base type definition}. + * + * minInclusive, maxInclusive, minExclusive, maxExclusive: + * The value ·must· be in the + * ·value space· of the ·base type·. + */ + /* + * This function is intended to deliver a compiled value + * on the facet. In this implementation of XML Schemata the + * type holding a facet, won't be a built-in type. + * Thus to ensure that other API + * calls (relaxng) do work, if the given type is a built-in + * type, we will assume that the given built-in type *is + * already* the base type. + */ + if (typeDecl->type != XML_SCHEMA_TYPE_BASIC) { + base = typeDecl->baseType; + if (base == NULL) { + PERROR_INT("xmlSchemaCheckFacet", + "a type user derived type has no base type"); + return (-1); + } + } else + base = typeDecl; + + if (! ctxtGiven) { + /* + * A context is needed if called from RelaxNG. + */ + pctxt = xmlSchemaNewParserCtxt("*"); + if (pctxt == NULL) + return (-1); + } + /* + * NOTE: This call does not check the content nodes, + * since they are not available: + * facet->node is just the node holding the facet + * definition, *not* the attribute holding the *value* + * of the facet. + */ + ret = xmlSchemaVCheckCVCSimpleType( + ACTXT_CAST pctxt, facet->node, base, + facet->value, &(facet->val), 1, 1, 0); + if (ret != 0) { + if (ret < 0) { + /* No error message for RelaxNG. */ + if (ctxtGiven) { + xmlSchemaCustomErr(ACTXT_CAST pctxt, + XML_SCHEMAP_INTERNAL, facet->node, NULL, + "Internal error: xmlSchemaCheckFacet, " + "failed to validate the value '%s' of the " + "facet '%s' against the base type", + facet->value, xmlSchemaFacetTypeToString(facet->type)); + } + goto internal_error; + } + ret = XML_SCHEMAP_INVALID_FACET_VALUE; + /* No error message for RelaxNG. */ + if (ctxtGiven) { + xmlChar *str = NULL; + + xmlSchemaCustomErr(ACTXT_CAST pctxt, + ret, facet->node, WXS_BASIC_CAST facet, + "The value '%s' of the facet does not validate " + "against the base type '%s'", + facet->value, + xmlSchemaFormatQName(&str, + base->targetNamespace, base->name)); + FREE_AND_NULL(str); + } + goto exit; + } else if (facet->val == NULL) { + if (ctxtGiven) { + PERROR_INT("xmlSchemaCheckFacet", + "value was not computed"); + } + TODO + } + break; + } + case XML_SCHEMA_FACET_PATTERN: + facet->regexp = xmlRegexpCompile(facet->value); + if (facet->regexp == NULL) { + ret = XML_SCHEMAP_REGEXP_INVALID; + /* No error message for RelaxNG. */ + if (ctxtGiven) { + xmlSchemaCustomErr(ACTXT_CAST pctxt, + ret, facet->node, WXS_BASIC_CAST typeDecl, + "The value '%s' of the facet 'pattern' is not a " + "valid regular expression", + facet->value, NULL); + } + } + break; + case XML_SCHEMA_FACET_TOTALDIGITS: + case XML_SCHEMA_FACET_FRACTIONDIGITS: + case XML_SCHEMA_FACET_LENGTH: + case XML_SCHEMA_FACET_MAXLENGTH: + case XML_SCHEMA_FACET_MINLENGTH: + + if (facet->type == XML_SCHEMA_FACET_TOTALDIGITS) { + ret = xmlSchemaValidatePredefinedType( + xmlSchemaGetBuiltInType(XML_SCHEMAS_PINTEGER), + facet->value, &(facet->val)); + } else { + ret = xmlSchemaValidatePredefinedType( + xmlSchemaGetBuiltInType(XML_SCHEMAS_NNINTEGER), + facet->value, &(facet->val)); + } + if (ret != 0) { + if (ret < 0) { + /* No error message for RelaxNG. */ + if (ctxtGiven) { + PERROR_INT("xmlSchemaCheckFacet", + "validating facet value"); + } + goto internal_error; + } + ret = XML_SCHEMAP_INVALID_FACET_VALUE; + /* No error message for RelaxNG. */ + if (ctxtGiven) { + /* error code */ + xmlSchemaCustomErr4(ACTXT_CAST pctxt, + ret, facet->node, WXS_BASIC_CAST typeDecl, + "The value '%s' of the facet '%s' is not a valid '%s'", + facet->value, + xmlSchemaFacetTypeToString(facet->type), + (facet->type != XML_SCHEMA_FACET_TOTALDIGITS) ? + BAD_CAST "nonNegativeInteger" : + BAD_CAST "positiveInteger", + NULL); + } + } + break; + + case XML_SCHEMA_FACET_WHITESPACE:{ + if (xmlStrEqual(facet->value, BAD_CAST "preserve")) { + facet->whitespace = XML_SCHEMAS_FACET_PRESERVE; + } else if (xmlStrEqual(facet->value, BAD_CAST "replace")) { + facet->whitespace = XML_SCHEMAS_FACET_REPLACE; + } else if (xmlStrEqual(facet->value, BAD_CAST "collapse")) { + facet->whitespace = XML_SCHEMAS_FACET_COLLAPSE; + } else { + ret = XML_SCHEMAP_INVALID_FACET_VALUE; + /* No error message for RelaxNG. */ + if (ctxtGiven) { + /* error was previously: XML_SCHEMAP_INVALID_WHITE_SPACE */ + xmlSchemaCustomErr(ACTXT_CAST pctxt, + ret, facet->node, WXS_BASIC_CAST typeDecl, + "The value '%s' of the facet 'whitespace' is not " + "valid", facet->value, NULL); + } + } + } + default: + break; + } +exit: + if ((! ctxtGiven) && (pctxt != NULL)) + xmlSchemaFreeParserCtxt(pctxt); + return (ret); +internal_error: + if ((! ctxtGiven) && (pctxt != NULL)) + xmlSchemaFreeParserCtxt(pctxt); + return (-1); +} + +/** + * xmlSchemaCheckFacetValues: + * @typeDecl: the schema type definition + * @ctxt: the schema parser context + * + * Checks the default values types, especially for facets + */ +static int +xmlSchemaCheckFacetValues(xmlSchemaTypePtr typeDecl, + xmlSchemaParserCtxtPtr pctxt) +{ + int res, olderrs = pctxt->nberrors; + const xmlChar *name = typeDecl->name; + /* + * NOTE: It is intended to use the facets list, instead + * of facetSet. + */ + if (typeDecl->facets != NULL) { + xmlSchemaFacetPtr facet = typeDecl->facets; + + /* + * Temporarily assign the "schema" to the validation context + * of the parser context. This is needed for NOTATION validation. + */ + if (pctxt->vctxt == NULL) { + if (xmlSchemaCreateVCtxtOnPCtxt(pctxt) == -1) + return(-1); + } + pctxt->vctxt->schema = pctxt->schema; + while (facet != NULL) { + res = xmlSchemaCheckFacet(facet, typeDecl, pctxt, name); + HFAILURE + facet = facet->next; + } + pctxt->vctxt->schema = NULL; + } + if (olderrs != pctxt->nberrors) + return(pctxt->err); + return(0); +exit_failure: + return(-1); +} + +/** + * xmlSchemaGetCircModelGrDefRef: + * @ctxtMGroup: the searched model group + * @selfMGroup: the second searched model group + * @particle: the first particle + * + * This one is intended to be used by + * xmlSchemaCheckGroupDefCircular only. + * + * Returns the particle with the circular model group definition reference, + * otherwise NULL. + */ +static xmlSchemaTreeItemPtr +xmlSchemaGetCircModelGrDefRef(xmlSchemaModelGroupDefPtr groupDef, + xmlSchemaTreeItemPtr particle) +{ + xmlSchemaTreeItemPtr circ = NULL; + xmlSchemaTreeItemPtr term; + xmlSchemaModelGroupDefPtr gdef; + + for (; particle != NULL; particle = particle->next) { + term = particle->children; + if (term == NULL) + continue; + switch (term->type) { + case XML_SCHEMA_TYPE_GROUP: + gdef = (xmlSchemaModelGroupDefPtr) term; + if (gdef == groupDef) + return (particle); + /* + * Mark this model group definition to avoid infinite + * recursion on circular references not yet examined. + */ + if (gdef->flags & XML_SCHEMA_MODEL_GROUP_DEF_MARKED) + continue; + if (gdef->children != NULL) { + gdef->flags |= XML_SCHEMA_MODEL_GROUP_DEF_MARKED; + circ = xmlSchemaGetCircModelGrDefRef(groupDef, + gdef->children->children); + gdef->flags ^= XML_SCHEMA_MODEL_GROUP_DEF_MARKED; + if (circ != NULL) + return (circ); + } + break; + case XML_SCHEMA_TYPE_SEQUENCE: + case XML_SCHEMA_TYPE_CHOICE: + case XML_SCHEMA_TYPE_ALL: + circ = xmlSchemaGetCircModelGrDefRef(groupDef, term->children); + if (circ != NULL) + return (circ); + break; + default: + break; + } + } + return (NULL); +} + +/** + * xmlSchemaCheckGroupDefCircular: + * @item: the model group definition + * @ctxt: the parser context + * @name: the name + * + * Checks for circular references to model group definitions. + */ +static void +xmlSchemaCheckGroupDefCircular(xmlSchemaModelGroupDefPtr item, + xmlSchemaParserCtxtPtr ctxt) +{ + /* + * Schema Component Constraint: Model Group Correct + * 2 Circular groups are disallowed. That is, within the {particles} + * of a group there must not be at any depth a particle whose {term} + * is the group itself. + */ + if ((item == NULL) || + (item->type != XML_SCHEMA_TYPE_GROUP) || + (item->children == NULL)) + return; + { + xmlSchemaTreeItemPtr circ; + + circ = xmlSchemaGetCircModelGrDefRef(item, item->children->children); + if (circ != NULL) { + xmlChar *str = NULL; + /* + * TODO: The error report is not adequate: this constraint + * is defined for model groups but not definitions, but since + * there cannot be any circular model groups without a model group + * definition (if not using a construction API), we check those + * defintions only. + */ + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_MG_PROPS_CORRECT_2, + NULL, WXS_ITEM_NODE(circ), + "Circular reference to the model group definition '%s' " + "defined", xmlSchemaFormatQName(&str, + item->targetNamespace, item->name)); + FREE_AND_NULL(str) + /* + * NOTE: We will cut the reference to avoid further + * confusion of the processor. This is a fatal error. + */ + circ->children = NULL; + } + } +} + +/** + * xmlSchemaModelGroupToModelGroupDefFixup: + * @ctxt: the parser context + * @mg: the model group + * + * Assigns the model group of model group definitions to the "term" + * of the referencing particle. + * In xmlSchemaResolveModelGroupParticleReferences the model group + * definitions were assigned to the "term", since needed for the + * circularity check. + * + * Schema Component Constraint: + * All Group Limited (cos-all-limited) (1.2) + */ +static void +xmlSchemaModelGroupToModelGroupDefFixup( + xmlSchemaParserCtxtPtr ctxt ATTRIBUTE_UNUSED, + xmlSchemaModelGroupPtr mg) +{ + xmlSchemaParticlePtr particle = WXS_MODELGROUP_PARTICLE(mg); + + while (particle != NULL) { + if ((WXS_PARTICLE_TERM(particle) == NULL) || + ((WXS_PARTICLE_TERM(particle))->type != + XML_SCHEMA_TYPE_GROUP)) + { + particle = WXS_PTC_CAST particle->next; + continue; + } + if (WXS_MODELGROUPDEF_MODEL(WXS_PARTICLE_TERM(particle)) == NULL) { + /* + * TODO: Remove the particle. + */ + WXS_PARTICLE_TERM(particle) = NULL; + particle = WXS_PTC_CAST particle->next; + continue; + } + /* + * Assign the model group to the {term} of the particle. + */ + WXS_PARTICLE_TERM(particle) = + WXS_TREE_CAST WXS_MODELGROUPDEF_MODEL(WXS_PARTICLE_TERM(particle)); + + particle = WXS_PTC_CAST particle->next; + } +} + +/** + * xmlSchemaCheckAttrGroupCircularRecur: + * @ctxtGr: the searched attribute group + * @attr: the current attribute list to be processed + * + * This one is intended to be used by + * xmlSchemaCheckAttrGroupCircular only. + * + * Returns the circular attribute grou reference, otherwise NULL. + */ +static xmlSchemaQNameRefPtr +xmlSchemaCheckAttrGroupCircularRecur(xmlSchemaAttributeGroupPtr ctxtGr, + xmlSchemaItemListPtr list) +{ + xmlSchemaAttributeGroupPtr gr; + xmlSchemaQNameRefPtr ref, circ; + int i; + /* + * We will search for an attribute group reference which + * references the context attribute group. + */ + for (i = 0; i < list->nbItems; i++) { + ref = list->items[i]; + if ((ref->type == XML_SCHEMA_EXTRA_QNAMEREF) && + (ref->itemType == XML_SCHEMA_TYPE_ATTRIBUTEGROUP) && + (ref->item != NULL)) + { + gr = WXS_ATTR_GROUP_CAST ref->item; + if (gr == ctxtGr) + return(ref); + if (gr->flags & XML_SCHEMAS_ATTRGROUP_MARKED) + continue; + /* + * Mark as visited to avoid infinite recursion on + * circular references not yet examined. + */ + if ((gr->attrUses) && + (gr->flags & XML_SCHEMAS_ATTRGROUP_HAS_REFS)) + { + gr->flags |= XML_SCHEMAS_ATTRGROUP_MARKED; + circ = xmlSchemaCheckAttrGroupCircularRecur(ctxtGr, + (xmlSchemaItemListPtr) gr->attrUses); + gr->flags ^= XML_SCHEMAS_ATTRGROUP_MARKED; + if (circ != NULL) + return (circ); + } + + } + } + return (NULL); +} + +/** + * xmlSchemaCheckAttrGroupCircular: + * attrGr: the attribute group definition + * @ctxt: the parser context + * @name: the name + * + * Checks for circular references of attribute groups. + */ +static int +xmlSchemaCheckAttrGroupCircular(xmlSchemaAttributeGroupPtr attrGr, + xmlSchemaParserCtxtPtr ctxt) +{ + /* + * Schema Representation Constraint: + * Attribute Group Definition Representation OK + * 3 Circular group reference is disallowed outside . + * That is, unless this element information item's parent is + * , then among the [children], if any, there must + * not be an with ref [attribute] which resolves + * to the component corresponding to this . Indirect + * circularity is also ruled out. That is, when QName resolution + * (Schema Document) (§3.15.3) is applied to a ·QName· arising from + * any s with a ref [attribute] among the [children], + * it must not be the case that a ·QName· is encountered at any depth + * which resolves to the component corresponding to this . + */ + if (attrGr->attrUses == NULL) + return(0); + else if ((attrGr->flags & XML_SCHEMAS_ATTRGROUP_HAS_REFS) == 0) + return(0); + else { + xmlSchemaQNameRefPtr circ; + + circ = xmlSchemaCheckAttrGroupCircularRecur(attrGr, + (xmlSchemaItemListPtr) attrGr->attrUses); + if (circ != NULL) { + xmlChar *str = NULL; + /* + * TODO: Report the referenced attr group as QName. + */ + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_SRC_ATTRIBUTE_GROUP_3, + NULL, WXS_ITEM_NODE(WXS_BASIC_CAST circ), + "Circular reference to the attribute group '%s' " + "defined", xmlSchemaGetComponentQName(&str, attrGr)); + FREE_AND_NULL(str); + /* + * NOTE: We will cut the reference to avoid further + * confusion of the processor. + * BADSPEC TODO: The spec should define how to process in this case. + */ + circ->item = NULL; + return(ctxt->err); + } + } + return(0); +} + +static int +xmlSchemaAttributeGroupExpandRefs(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaAttributeGroupPtr attrGr); + +/** + * xmlSchemaExpandAttributeGroupRefs: + * @pctxt: the parser context + * @node: the node of the component holding the attribute uses + * @completeWild: the intersected wildcard to be returned + * @list: the attribute uses + * + * Substitutes contained attribute group references + * for their attribute uses. Wilcards are intersected. + * Attribute use prohibitions are removed from the list + * and returned via the @prohibs list. + * Pointlessness of attr. prohibs, if a matching attr. decl + * is existent a well, are checked. + */ +static int +xmlSchemaExpandAttributeGroupRefs(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaBasicItemPtr item, + xmlSchemaWildcardPtr *completeWild, + xmlSchemaItemListPtr list, + xmlSchemaItemListPtr prohibs) +{ + xmlSchemaAttributeGroupPtr gr; + xmlSchemaAttributeUsePtr use; + xmlSchemaItemListPtr sublist; + int i, j; + int created = (*completeWild == NULL) ? 0 : 1; + + if (prohibs) + prohibs->nbItems = 0; + + for (i = 0; i < list->nbItems; i++) { + use = list->items[i]; + + if (use->type == XML_SCHEMA_EXTRA_ATTR_USE_PROHIB) { + if (prohibs == NULL) { + PERROR_INT("xmlSchemaExpandAttributeGroupRefs", + "unexpected attr prohibition found"); + return(-1); + } + /* + * Remove from attribute uses. + */ + if (xmlSchemaItemListRemove(list, i) == -1) + return(-1); + i--; + /* + * Note that duplicate prohibitions were already + * handled at parsing time. + */ + /* + * Add to list of prohibitions. + */ + xmlSchemaItemListAddSize(prohibs, 2, use); + continue; + } + if ((use->type == XML_SCHEMA_EXTRA_QNAMEREF) && + ((WXS_QNAME_CAST use)->itemType == XML_SCHEMA_TYPE_ATTRIBUTEGROUP)) + { + if ((WXS_QNAME_CAST use)->item == NULL) + return(-1); + gr = WXS_ATTR_GROUP_CAST (WXS_QNAME_CAST use)->item; + /* + * Expand the referenced attr. group. + * TODO: remove this, this is done in a previous step, so + * already done here. + */ + if ((gr->flags & XML_SCHEMAS_ATTRGROUP_WILDCARD_BUILDED) == 0) { + if (xmlSchemaAttributeGroupExpandRefs(pctxt, gr) == -1) + return(-1); + } + /* + * Build the 'complete' wildcard; i.e. intersect multiple + * wildcards. + */ + if (gr->attributeWildcard != NULL) { + if (*completeWild == NULL) { + *completeWild = gr->attributeWildcard; + } else { + if (! created) { + xmlSchemaWildcardPtr tmpWild; + + /* + * Copy the first encountered wildcard as context, + * except for the annotation. + * + * Although the complete wildcard might not correspond + * to any node in the schema, we will anchor it on + * the node of the owner component. + */ + tmpWild = xmlSchemaAddWildcard(pctxt, pctxt->schema, + XML_SCHEMA_TYPE_ANY_ATTRIBUTE, + WXS_ITEM_NODE(item)); + if (tmpWild == NULL) + return(-1); + if (xmlSchemaCloneWildcardNsConstraints(pctxt, + tmpWild, *completeWild) == -1) + return (-1); + tmpWild->processContents = (*completeWild)->processContents; + *completeWild = tmpWild; + created = 1; + } + + if (xmlSchemaIntersectWildcards(pctxt, *completeWild, + gr->attributeWildcard) == -1) + return(-1); + } + } + /* + * Just remove the reference if the referenced group does not + * contain any attribute uses. + */ + if (gr->attrUses == NULL) { + if (xmlSchemaItemListRemove(list, i) == -1) + return(-1); + i--; + continue; + } + /* + * Add the attribute uses. + */ + sublist = ((xmlSchemaItemListPtr) gr->attrUses); + if (sublist->nbItems != 0) { + list->items[i] = sublist->items[0]; + if (sublist->nbItems != 1) { + for (j = 1; j < sublist->nbItems; j++) { + i++; + if (xmlSchemaItemListInsert(list, + sublist->items[j], i) == -1) + return(-1); + } + } + } + } + + } + /* + * Handle pointless prohibitions of declared attributes. + */ + if (prohibs && (prohibs->nbItems != 0) && (list->nbItems != 0)) { + xmlSchemaAttributeUseProhibPtr prohib; + + for (i = prohibs->nbItems -1; i >= 0; i--) { + prohib = prohibs->items[i]; + for (j = 0; j < list->nbItems; j++) { + use = list->items[j]; + + if ((prohib->name == WXS_ATTRUSE_DECL_NAME(use)) && + (prohib->targetNamespace == WXS_ATTRUSE_DECL_TNS(use))) + { + xmlChar *str = NULL; + + xmlSchemaCustomWarning(ACTXT_CAST pctxt, + XML_SCHEMAP_WARN_ATTR_POINTLESS_PROH, + prohib->node, NULL, + "Skipping pointless attribute use prohibition " + "'%s', since a corresponding attribute use " + "exists already in the type definition", + xmlSchemaFormatQName(&str, + prohib->targetNamespace, prohib->name), + NULL, NULL); + FREE_AND_NULL(str); + /* + * Remove the prohibition. + */ + if (xmlSchemaItemListRemove(prohibs, i) == -1) + return(-1); + break; + } + } + } + } + return(0); +} + +/** + * xmlSchemaAttributeGroupExpandRefs: + * @pctxt: the parser context + * @attrGr: the attribute group definition + * + * Computation of: + * {attribute uses} property + * {attribute wildcard} property + * + * Substitutes contained attribute group references + * for their attribute uses. Wilcards are intersected. + */ +static int +xmlSchemaAttributeGroupExpandRefs(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaAttributeGroupPtr attrGr) +{ + if ((attrGr->attrUses == NULL) || + (attrGr->flags & XML_SCHEMAS_ATTRGROUP_WILDCARD_BUILDED)) + return(0); + + attrGr->flags |= XML_SCHEMAS_ATTRGROUP_WILDCARD_BUILDED; + if (xmlSchemaExpandAttributeGroupRefs(pctxt, WXS_BASIC_CAST attrGr, + &(attrGr->attributeWildcard), attrGr->attrUses, NULL) == -1) + return(-1); + return(0); +} + +/** + * xmlSchemaAttributeGroupExpandRefs: + * @pctxt: the parser context + * @attrGr: the attribute group definition + * + * Substitutes contained attribute group references + * for their attribute uses. Wilcards are intersected. + * + * Schema Component Constraint: + * Attribute Group Definition Properties Correct (ag-props-correct) + */ +static int +xmlSchemaCheckAGPropsCorrect(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaAttributeGroupPtr attrGr) +{ + /* + * SPEC ag-props-correct + * (1) "The values of the properties of an attribute group definition + * must be as described in the property tableau in The Attribute + * Group Definition Schema Component (§3.6.1), modulo the impact of + * Missing Sub-components (§5.3);" + */ + + if ((attrGr->attrUses != NULL) && + (WXS_LIST_CAST attrGr->attrUses)->nbItems > 1) + { + xmlSchemaItemListPtr uses = WXS_LIST_CAST attrGr->attrUses; + xmlSchemaAttributeUsePtr use, tmp; + int i, j, hasId = 0; + + for (i = uses->nbItems -1; i >= 0; i--) { + use = uses->items[i]; + /* + * SPEC ag-props-correct + * (2) "Two distinct members of the {attribute uses} must not have + * {attribute declaration}s both of whose {name}s match and whose + * {target namespace}s are identical." + */ + if (i > 0) { + for (j = i -1; j >= 0; j--) { + tmp = uses->items[j]; + if ((WXS_ATTRUSE_DECL_NAME(use) == + WXS_ATTRUSE_DECL_NAME(tmp)) && + (WXS_ATTRUSE_DECL_TNS(use) == + WXS_ATTRUSE_DECL_TNS(tmp))) + { + xmlChar *str = NULL; + + xmlSchemaCustomErr(ACTXT_CAST pctxt, + XML_SCHEMAP_AG_PROPS_CORRECT, + attrGr->node, WXS_BASIC_CAST attrGr, + "Duplicate %s", + xmlSchemaGetComponentDesignation(&str, use), + NULL); + FREE_AND_NULL(str); + /* + * Remove the duplicate. + */ + if (xmlSchemaItemListRemove(uses, i) == -1) + return(-1); + goto next_use; + } + } + } + /* + * SPEC ag-props-correct + * (3) "Two distinct members of the {attribute uses} must not have + * {attribute declaration}s both of whose {type definition}s are or + * are derived from ID." + * TODO: Does 'derived' include member-types of unions? + */ + if (WXS_ATTRUSE_TYPEDEF(use) != NULL) { + if (xmlSchemaIsDerivedFromBuiltInType( + WXS_ATTRUSE_TYPEDEF(use), XML_SCHEMAS_ID)) + { + if (hasId) { + xmlChar *str = NULL; + + xmlSchemaCustomErr(ACTXT_CAST pctxt, + XML_SCHEMAP_AG_PROPS_CORRECT, + attrGr->node, WXS_BASIC_CAST attrGr, + "There must not exist more than one attribute " + "declaration of type 'xs:ID' " + "(or derived from 'xs:ID'). The %s violates this " + "constraint", + xmlSchemaGetComponentDesignation(&str, use), + NULL); + FREE_AND_NULL(str); + if (xmlSchemaItemListRemove(uses, i) == -1) + return(-1); + } + hasId = 1; + } + } +next_use: {} + } + } + return(0); +} + +/** + * xmlSchemaResolveAttrGroupReferences: + * @attrgrpDecl: the schema attribute definition + * @ctxt: the schema parser context + * @name: the attribute name + * + * Resolves references to attribute group definitions. + */ +static int +xmlSchemaResolveAttrGroupReferences(xmlSchemaQNameRefPtr ref, + xmlSchemaParserCtxtPtr ctxt) +{ + xmlSchemaAttributeGroupPtr group; + + if (ref->item != NULL) + return(0); + group = xmlSchemaGetAttributeGroup(ctxt->schema, + ref->name, + ref->targetNamespace); + if (group == NULL) { + xmlSchemaPResCompAttrErr(ctxt, + XML_SCHEMAP_SRC_RESOLVE, + NULL, ref->node, + "ref", ref->name, ref->targetNamespace, + ref->itemType, NULL); + return(ctxt->err); + } + ref->item = WXS_BASIC_CAST group; + return(0); +} + +/** + * xmlSchemaCheckAttrPropsCorrect: + * @item: an schema attribute declaration/use + * @ctxt: a schema parser context + * @name: the name of the attribute + * + * + * Schema Component Constraint: + * Attribute Declaration Properties Correct (a-props-correct) + * + * Validates the value constraints of an attribute declaration/use. + * NOTE that this needs the simle type definitions to be already + * builded and checked. + */ +static int +xmlSchemaCheckAttrPropsCorrect(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaAttributePtr attr) +{ + + /* + * SPEC a-props-correct (1) + * "The values of the properties of an attribute declaration must + * be as described in the property tableau in The Attribute + * Declaration Schema Component (§3.2.1), modulo the impact of + * Missing Sub-components (§5.3)." + */ + + if (WXS_ATTR_TYPEDEF(attr) == NULL) + return(0); + + if (attr->defValue != NULL) { + int ret; + + /* + * SPEC a-props-correct (3) + * "If the {type definition} is or is derived from ID then there + * must not be a {value constraint}." + */ + if (xmlSchemaIsDerivedFromBuiltInType( + WXS_ATTR_TYPEDEF(attr), XML_SCHEMAS_ID)) + { + xmlSchemaCustomErr(ACTXT_CAST pctxt, + XML_SCHEMAP_A_PROPS_CORRECT_3, + NULL, WXS_BASIC_CAST attr, + "Value constraints are not allowed if the type definition " + "is or is derived from xs:ID", + NULL, NULL); + return(pctxt->err); + } + /* + * SPEC a-props-correct (2) + * "if there is a {value constraint}, the canonical lexical + * representation of its value must be ·valid· with respect + * to the {type definition} as defined in String Valid (§3.14.4)." + * TODO: Don't care about the *cononical* stuff here, this requirement + * will be removed in WXS 1.1 anyway. + */ + ret = xmlSchemaVCheckCVCSimpleType(ACTXT_CAST pctxt, + attr->node, WXS_ATTR_TYPEDEF(attr), + attr->defValue, &(attr->defVal), + 1, 1, 0); + if (ret != 0) { + if (ret < 0) { + PERROR_INT("xmlSchemaCheckAttrPropsCorrect", + "calling xmlSchemaVCheckCVCSimpleType()"); + return(-1); + } + xmlSchemaCustomErr(ACTXT_CAST pctxt, + XML_SCHEMAP_A_PROPS_CORRECT_2, + NULL, WXS_BASIC_CAST attr, + "The value of the value constraint is not valid", + NULL, NULL); + return(pctxt->err); + } + } + + return(0); +} + +static xmlSchemaElementPtr +xmlSchemaCheckSubstGroupCircular(xmlSchemaElementPtr elemDecl, + xmlSchemaElementPtr ancestor) +{ + xmlSchemaElementPtr ret; + + if (WXS_SUBST_HEAD(ancestor) == NULL) + return (NULL); + if (WXS_SUBST_HEAD(ancestor) == elemDecl) + return (ancestor); + + if (WXS_SUBST_HEAD(ancestor)->flags & XML_SCHEMAS_ELEM_CIRCULAR) + return (NULL); + WXS_SUBST_HEAD(ancestor)->flags |= XML_SCHEMAS_ELEM_CIRCULAR; + ret = xmlSchemaCheckSubstGroupCircular(elemDecl, + WXS_SUBST_HEAD(ancestor)); + WXS_SUBST_HEAD(ancestor)->flags ^= XML_SCHEMAS_ELEM_CIRCULAR; + + return (ret); +} + +/** + * xmlSchemaCheckElemPropsCorrect: + * @ctxt: a schema parser context + * @decl: the element declaration + * @name: the name of the attribute + * + * Schema Component Constraint: + * Element Declaration Properties Correct (e-props-correct) + * + * STATUS: + * missing: (6) + */ +static int +xmlSchemaCheckElemPropsCorrect(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaElementPtr elemDecl) +{ + int ret = 0; + xmlSchemaTypePtr typeDef = WXS_ELEM_TYPEDEF(elemDecl); + /* + * SPEC (1) "The values of the properties of an element declaration + * must be as described in the property tableau in The Element + * Declaration Schema Component (§3.3.1), modulo the impact of Missing + * Sub-components (§5.3)." + */ + if (WXS_SUBST_HEAD(elemDecl) != NULL) { + xmlSchemaElementPtr head = WXS_SUBST_HEAD(elemDecl), circ; + + xmlSchemaCheckElementDeclComponent(head, pctxt); + /* + * SPEC (3) "If there is a non-·absent· {substitution group + * affiliation}, then {scope} must be global." + */ + if ((elemDecl->flags & XML_SCHEMAS_ELEM_GLOBAL) == 0) { + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_E_PROPS_CORRECT_3, + WXS_BASIC_CAST elemDecl, NULL, + "Only global element declarations can have a " + "substitution group affiliation", NULL); + ret = XML_SCHEMAP_E_PROPS_CORRECT_3; + } + /* + * TODO: SPEC (6) "Circular substitution groups are disallowed. + * That is, it must not be possible to return to an element declaration + * by repeatedly following the {substitution group affiliation} + * property." + */ + if (head == elemDecl) + circ = head; + else if (WXS_SUBST_HEAD(head) != NULL) + circ = xmlSchemaCheckSubstGroupCircular(head, head); + else + circ = NULL; + if (circ != NULL) { + xmlChar *strA = NULL, *strB = NULL; + + xmlSchemaPCustomErrExt(pctxt, + XML_SCHEMAP_E_PROPS_CORRECT_6, + WXS_BASIC_CAST circ, NULL, + "The element declaration '%s' defines a circular " + "substitution group to element declaration '%s'", + xmlSchemaGetComponentQName(&strA, circ), + xmlSchemaGetComponentQName(&strB, head), + NULL); + FREE_AND_NULL(strA) + FREE_AND_NULL(strB) + ret = XML_SCHEMAP_E_PROPS_CORRECT_6; + } + /* + * SPEC (4) "If there is a {substitution group affiliation}, + * the {type definition} + * of the element declaration must be validly derived from the {type + * definition} of the {substitution group affiliation}, given the value + * of the {substitution group exclusions} of the {substitution group + * affiliation}, as defined in Type Derivation OK (Complex) (§3.4.6) + * (if the {type definition} is complex) or as defined in + * Type Derivation OK (Simple) (§3.14.6) (if the {type definition} is + * simple)." + * + * NOTE: {substitution group exclusions} means the values of the + * attribute "final". + */ + + if (typeDef != WXS_ELEM_TYPEDEF(WXS_SUBST_HEAD(elemDecl))) { + int set = 0; + + if (head->flags & XML_SCHEMAS_ELEM_FINAL_EXTENSION) + set |= SUBSET_EXTENSION; + if (head->flags & XML_SCHEMAS_ELEM_FINAL_RESTRICTION) + set |= SUBSET_RESTRICTION; + + if (xmlSchemaCheckCOSDerivedOK(ACTXT_CAST pctxt, typeDef, + WXS_ELEM_TYPEDEF(head), set) != 0) { + xmlChar *strA = NULL, *strB = NULL, *strC = NULL; + + ret = XML_SCHEMAP_E_PROPS_CORRECT_4; + xmlSchemaPCustomErrExt(pctxt, + XML_SCHEMAP_E_PROPS_CORRECT_4, + WXS_BASIC_CAST elemDecl, NULL, + "The type definition '%s' was " + "either rejected by the substitution group " + "affiliation '%s', or not validly derived from its type " + "definition '%s'", + xmlSchemaGetComponentQName(&strA, typeDef), + xmlSchemaGetComponentQName(&strB, head), + xmlSchemaGetComponentQName(&strC, WXS_ELEM_TYPEDEF(head))); + FREE_AND_NULL(strA) + FREE_AND_NULL(strB) + FREE_AND_NULL(strC) + } + } + } + /* + * SPEC (5) "If the {type definition} or {type definition}'s + * {content type} + * is or is derived from ID then there must not be a {value constraint}. + * Note: The use of ID as a type definition for elements goes beyond + * XML 1.0, and should be avoided if backwards compatibility is desired" + */ + if ((elemDecl->value != NULL) && + ((WXS_IS_SIMPLE(typeDef) && + xmlSchemaIsDerivedFromBuiltInType(typeDef, XML_SCHEMAS_ID)) || + (WXS_IS_COMPLEX(typeDef) && + WXS_HAS_SIMPLE_CONTENT(typeDef) && + xmlSchemaIsDerivedFromBuiltInType(typeDef->contentTypeDef, + XML_SCHEMAS_ID)))) { + + ret = XML_SCHEMAP_E_PROPS_CORRECT_5; + xmlSchemaPCustomErr(pctxt, + XML_SCHEMAP_E_PROPS_CORRECT_5, + WXS_BASIC_CAST elemDecl, NULL, + "The type definition (or type definition's content type) is or " + "is derived from ID; value constraints are not allowed in " + "conjunction with such a type definition", NULL); + } else if (elemDecl->value != NULL) { + int vcret; + xmlNodePtr node = NULL; + + /* + * SPEC (2) "If there is a {value constraint}, the canonical lexical + * representation of its value must be ·valid· with respect to the + * {type definition} as defined in Element Default Valid (Immediate) + * (§3.3.6)." + */ + if (typeDef == NULL) { + xmlSchemaPErr(pctxt, elemDecl->node, + XML_SCHEMAP_INTERNAL, + "Internal error: xmlSchemaCheckElemPropsCorrect, " + "type is missing... skipping validation of " + "the value constraint", NULL, NULL); + return (-1); + } + if (elemDecl->node != NULL) { + if (elemDecl->flags & XML_SCHEMAS_ELEM_FIXED) + node = (xmlNodePtr) xmlHasProp(elemDecl->node, + BAD_CAST "fixed"); + else + node = (xmlNodePtr) xmlHasProp(elemDecl->node, + BAD_CAST "default"); + } + vcret = xmlSchemaParseCheckCOSValidDefault(pctxt, node, + typeDef, elemDecl->value, &(elemDecl->defVal)); + if (vcret != 0) { + if (vcret < 0) { + PERROR_INT("xmlSchemaElemCheckValConstr", + "failed to validate the value constraint of an " + "element declaration"); + return (-1); + } + return (vcret); + } + } + + return (ret); +} + +/** + * xmlSchemaCheckElemSubstGroup: + * @ctxt: a schema parser context + * @decl: the element declaration + * @name: the name of the attribute + * + * Schema Component Constraint: + * Substitution Group (cos-equiv-class) + * + * In Libxml2 the subst. groups will be precomputed, in terms of that + * a list will be built for each subst. group head, holding all direct + * referents to this head. + * NOTE that this function needs: + * 1. circular subst. groups to be checked beforehand + * 2. the declaration's type to be derived from the head's type + * + * STATUS: + * + */ +static void +xmlSchemaCheckElemSubstGroup(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaElementPtr elemDecl) +{ + if ((WXS_SUBST_HEAD(elemDecl) == NULL) || + /* SPEC (1) "Its {abstract} is false." */ + (elemDecl->flags & XML_SCHEMAS_ELEM_ABSTRACT)) + return; + { + xmlSchemaElementPtr head; + xmlSchemaTypePtr headType, type; + int set, methSet; + /* + * SPEC (2) "It is validly substitutable for HEAD subject to HEAD's + * {disallowed substitutions} as the blocking constraint, as defined in + * Substitution Group OK (Transitive) (§3.3.6)." + */ + for (head = WXS_SUBST_HEAD(elemDecl); head != NULL; + head = WXS_SUBST_HEAD(head)) { + set = 0; + methSet = 0; + /* + * The blocking constraints. + */ + if (head->flags & XML_SCHEMAS_ELEM_BLOCK_SUBSTITUTION) + continue; + headType = head->subtypes; + type = elemDecl->subtypes; + if (headType == type) + goto add_member; + if (head->flags & XML_SCHEMAS_ELEM_BLOCK_RESTRICTION) + set |= XML_SCHEMAS_TYPE_BLOCK_RESTRICTION; + if (head->flags & XML_SCHEMAS_ELEM_BLOCK_EXTENSION) + set |= XML_SCHEMAS_TYPE_BLOCK_EXTENSION; + /* + * SPEC: Substitution Group OK (Transitive) (2.3) + * "The set of all {derivation method}s involved in the + * derivation of D's {type definition} from C's {type definition} + * does not intersect with the union of the blocking constraint, + * C's {prohibited substitutions} (if C is complex, otherwise the + * empty set) and the {prohibited substitutions} (respectively the + * empty set) of any intermediate {type definition}s in the + * derivation of D's {type definition} from C's {type definition}." + */ + /* + * OPTIMIZE TODO: Optimize this a bit, since, if traversing the + * subst.head axis, the methSet does not need to be computed for + * the full depth over and over. + */ + /* + * The set of all {derivation method}s involved in the derivation + */ + while ((type != NULL) && (type != headType)) { + if ((WXS_IS_EXTENSION(type)) && + ((methSet & XML_SCHEMAS_TYPE_BLOCK_RESTRICTION) == 0)) + methSet |= XML_SCHEMAS_TYPE_BLOCK_EXTENSION; + + if (WXS_IS_RESTRICTION(type) && + ((methSet & XML_SCHEMAS_TYPE_BLOCK_RESTRICTION) == 0)) + methSet |= XML_SCHEMAS_TYPE_BLOCK_RESTRICTION; + + type = type->baseType; + } + /* + * The {prohibited substitutions} of all intermediate types + + * the head's type. + */ + type = elemDecl->subtypes->baseType; + while (type != NULL) { + if (WXS_IS_COMPLEX(type)) { + if ((type->flags & + XML_SCHEMAS_TYPE_BLOCK_EXTENSION) && + ((set & XML_SCHEMAS_TYPE_BLOCK_EXTENSION) == 0)) + set |= XML_SCHEMAS_TYPE_BLOCK_EXTENSION; + if ((type->flags & + XML_SCHEMAS_TYPE_BLOCK_RESTRICTION) && + ((set & XML_SCHEMAS_TYPE_BLOCK_RESTRICTION) == 0)) + set |= XML_SCHEMAS_TYPE_BLOCK_RESTRICTION; + } else + break; + if (type == headType) + break; + type = type->baseType; + } + if ((set != 0) && + (((set & XML_SCHEMAS_TYPE_BLOCK_EXTENSION) && + (methSet & XML_SCHEMAS_TYPE_BLOCK_EXTENSION)) || + ((set & XML_SCHEMAS_TYPE_BLOCK_RESTRICTION) && + (methSet & XML_SCHEMAS_TYPE_BLOCK_RESTRICTION)))) { + continue; + } +add_member: + xmlSchemaAddElementSubstitutionMember(ctxt, head, elemDecl); + if ((head->flags & XML_SCHEMAS_ELEM_SUBST_GROUP_HEAD) == 0) + head->flags |= XML_SCHEMAS_ELEM_SUBST_GROUP_HEAD; + } + } +} + +/** + * xmlSchemaCheckElementDeclComponent + * @item: an schema element declaration/particle + * @ctxt: a schema parser context + * @name: the name of the attribute + * + * Validates the value constraints of an element declaration. + * + * Fixes finish doing the computations on the element declarations. + */ +static void +xmlSchemaCheckElementDeclComponent(xmlSchemaElementPtr elemDecl, + xmlSchemaParserCtxtPtr ctxt) +{ + if (elemDecl == NULL) + return; + if (elemDecl->flags & XML_SCHEMAS_ELEM_INTERNAL_CHECKED) + return; + elemDecl->flags |= XML_SCHEMAS_ELEM_INTERNAL_CHECKED; + if (xmlSchemaCheckElemPropsCorrect(ctxt, elemDecl) == 0) + xmlSchemaCheckElemSubstGroup(ctxt, elemDecl); +} + +/** + * xmlSchemaResolveModelGroupParticleReferences: + * @particle: a particle component + * @ctxt: a parser context + * + * Resolves references of a model group's {particles} to + * model group definitions and to element declarations. + */ +static void +xmlSchemaResolveModelGroupParticleReferences( + xmlSchemaParserCtxtPtr ctxt, + xmlSchemaModelGroupPtr mg) +{ + xmlSchemaParticlePtr particle = WXS_MODELGROUP_PARTICLE(mg); + xmlSchemaQNameRefPtr ref; + xmlSchemaBasicItemPtr refItem; + + /* + * URGENT TODO: Test this. + */ + while (particle != NULL) { + if ((WXS_PARTICLE_TERM(particle) == NULL) || + ((WXS_PARTICLE_TERM(particle))->type != + XML_SCHEMA_EXTRA_QNAMEREF)) + { + goto next_particle; + } + ref = WXS_QNAME_CAST WXS_PARTICLE_TERM(particle); + /* + * Resolve the reference. + * NULL the {term} by default. + */ + particle->children = NULL; + + refItem = xmlSchemaGetNamedComponent(ctxt->schema, + ref->itemType, ref->name, ref->targetNamespace); + if (refItem == NULL) { + xmlSchemaPResCompAttrErr(ctxt, XML_SCHEMAP_SRC_RESOLVE, + NULL, WXS_ITEM_NODE(particle), "ref", ref->name, + ref->targetNamespace, ref->itemType, NULL); + /* TODO: remove the particle. */ + goto next_particle; + } + if (refItem->type == XML_SCHEMA_TYPE_GROUP) { + if (WXS_MODELGROUPDEF_MODEL(refItem) == NULL) + /* TODO: remove the particle. */ + goto next_particle; + /* + * NOTE that we will assign the model group definition + * itself to the "term" of the particle. This will ease + * the check for circular model group definitions. After + * that the "term" will be assigned the model group of the + * model group definition. + */ + if ((WXS_MODELGROUPDEF_MODEL(refItem))->type == + XML_SCHEMA_TYPE_ALL) { + /* + * SPEC cos-all-limited (1) + * SPEC cos-all-limited (1.2) + * "It appears only as the value of one or both of the + * following properties:" + * (1.1) "the {model group} property of a model group + * definition." + * (1.2) "the {term} property of a particle [... of] the " + * {content type} of a complex type definition." + */ + xmlSchemaCustomErr(ACTXT_CAST ctxt, + /* TODO: error code */ + XML_SCHEMAP_COS_ALL_LIMITED, + WXS_ITEM_NODE(particle), NULL, + "A model group definition is referenced, but " + "it contains an 'all' model group, which " + "cannot be contained by model groups", + NULL, NULL); + /* TODO: remove the particle. */ + goto next_particle; + } + particle->children = (xmlSchemaTreeItemPtr) refItem; + } else { + /* + * TODO: Are referenced element declarations the only + * other components we expect here? + */ + particle->children = (xmlSchemaTreeItemPtr) refItem; + } +next_particle: + particle = WXS_PTC_CAST particle->next; + } +} + +static int +xmlSchemaAreValuesEqual(xmlSchemaValPtr x, + xmlSchemaValPtr y) +{ + xmlSchemaTypePtr tx, ty, ptx, pty; + int ret; + + while (x != NULL) { + /* Same types. */ + tx = xmlSchemaGetBuiltInType(xmlSchemaGetValType(x)); + ty = xmlSchemaGetBuiltInType(xmlSchemaGetValType(y)); + ptx = xmlSchemaGetPrimitiveType(tx); + pty = xmlSchemaGetPrimitiveType(ty); + /* + * (1) if a datatype T' is ·derived· by ·restriction· from an + * atomic datatype T then the ·value space· of T' is a subset of + * the ·value space· of T. */ + /* + * (2) if datatypes T' and T'' are ·derived· by ·restriction· + * from a common atomic ancestor T then the ·value space·s of T' + * and T'' may overlap. + */ + if (ptx != pty) + return(0); + /* + * We assume computed values to be normalized, so do a fast + * string comparison for string based types. + */ + if ((ptx->builtInType == XML_SCHEMAS_STRING) || + WXS_IS_ANY_SIMPLE_TYPE(ptx)) { + if (! xmlStrEqual( + xmlSchemaValueGetAsString(x), + xmlSchemaValueGetAsString(y))) + return (0); + } else { + ret = xmlSchemaCompareValuesWhtsp( + x, XML_SCHEMA_WHITESPACE_PRESERVE, + y, XML_SCHEMA_WHITESPACE_PRESERVE); + if (ret == -2) + return(-1); + if (ret != 0) + return(0); + } + /* + * Lists. + */ + x = xmlSchemaValueGetNext(x); + if (x != NULL) { + y = xmlSchemaValueGetNext(y); + if (y == NULL) + return (0); + } else if (xmlSchemaValueGetNext(y) != NULL) + return (0); + else + return (1); + } + return (0); +} + +/** + * xmlSchemaResolveAttrUseReferences: + * @item: an attribute use + * @ctxt: a parser context + * + * Resolves the referenced attribute declaration. + */ +static int +xmlSchemaResolveAttrUseReferences(xmlSchemaAttributeUsePtr ause, + xmlSchemaParserCtxtPtr ctxt) +{ + if ((ctxt == NULL) || (ause == NULL)) + return(-1); + if ((ause->attrDecl == NULL) || + (ause->attrDecl->type != XML_SCHEMA_EXTRA_QNAMEREF)) + return(0); + + { + xmlSchemaQNameRefPtr ref = WXS_QNAME_CAST ause->attrDecl; + + /* + * TODO: Evaluate, what errors could occur if the declaration is not + * found. + */ + ause->attrDecl = xmlSchemaGetAttributeDecl(ctxt->schema, + ref->name, ref->targetNamespace); + if (ause->attrDecl == NULL) { + xmlSchemaPResCompAttrErr(ctxt, + XML_SCHEMAP_SRC_RESOLVE, + WXS_BASIC_CAST ause, ause->node, + "ref", ref->name, ref->targetNamespace, + XML_SCHEMA_TYPE_ATTRIBUTE, NULL); + return(ctxt->err);; + } + } + return(0); +} + +/** + * xmlSchemaCheckAttrUsePropsCorrect: + * @ctxt: a parser context + * @use: an attribute use + * + * Schema Component Constraint: + * Attribute Use Correct (au-props-correct) + * + */ +static int +xmlSchemaCheckAttrUsePropsCorrect(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaAttributeUsePtr use) +{ + if ((ctxt == NULL) || (use == NULL)) + return(-1); + if ((use->defValue == NULL) || (WXS_ATTRUSE_DECL(use) == NULL) || + ((WXS_ATTRUSE_DECL(use))->type != XML_SCHEMA_TYPE_ATTRIBUTE)) + return(0); + + /* + * SPEC au-props-correct (1) + * "The values of the properties of an attribute use must be as + * described in the property tableau in The Attribute Use Schema + * Component (§3.5.1), modulo the impact of Missing + * Sub-components (§5.3)." + */ + + if (((WXS_ATTRUSE_DECL(use))->defValue != NULL) && + ((WXS_ATTRUSE_DECL(use))->flags & XML_SCHEMAS_ATTR_FIXED) && + ((use->flags & XML_SCHEMA_ATTR_USE_FIXED) == 0)) + { + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_AU_PROPS_CORRECT_2, + WXS_BASIC_CAST use, NULL, + "The attribute declaration has a 'fixed' value constraint " + ", thus the attribute use must also have a 'fixed' value " + "constraint", + NULL); + return(ctxt->err); + } + /* + * Compute and check the value constraint's value. + */ + if ((use->defVal != NULL) && (WXS_ATTRUSE_TYPEDEF(use) != NULL)) { + int ret; + /* + * TODO: The spec seems to be missing a check of the + * value constraint of the attribute use. We will do it here. + */ + /* + * SPEC a-props-correct (3) + */ + if (xmlSchemaIsDerivedFromBuiltInType( + WXS_ATTRUSE_TYPEDEF(use), XML_SCHEMAS_ID)) + { + xmlSchemaCustomErr(ACTXT_CAST ctxt, + XML_SCHEMAP_AU_PROPS_CORRECT, + NULL, WXS_BASIC_CAST use, + "Value constraints are not allowed if the type definition " + "is or is derived from xs:ID", + NULL, NULL); + return(ctxt->err); + } + + ret = xmlSchemaVCheckCVCSimpleType(ACTXT_CAST ctxt, + use->node, WXS_ATTRUSE_TYPEDEF(use), + use->defValue, &(use->defVal), + 1, 1, 0); + if (ret != 0) { + if (ret < 0) { + PERROR_INT2("xmlSchemaCheckAttrUsePropsCorrect", + "calling xmlSchemaVCheckCVCSimpleType()"); + return(-1); + } + xmlSchemaCustomErr(ACTXT_CAST ctxt, + XML_SCHEMAP_AU_PROPS_CORRECT, + NULL, WXS_BASIC_CAST use, + "The value of the value constraint is not valid", + NULL, NULL); + return(ctxt->err); + } + } + /* + * SPEC au-props-correct (2) + * "If the {attribute declaration} has a fixed + * {value constraint}, then if the attribute use itself has a + * {value constraint}, it must also be fixed and its value must match + * that of the {attribute declaration}'s {value constraint}." + */ + if (((WXS_ATTRUSE_DECL(use))->defVal != NULL) && + (((WXS_ATTRUSE_DECL(use))->flags & XML_SCHEMA_ATTR_USE_FIXED) == 0)) + { + if (! xmlSchemaAreValuesEqual(use->defVal, + (WXS_ATTRUSE_DECL(use))->defVal)) + { + xmlSchemaPCustomErr(ctxt, + XML_SCHEMAP_AU_PROPS_CORRECT_2, + WXS_BASIC_CAST use, NULL, + "The 'fixed' value constraint of the attribute use " + "must match the attribute declaration's value " + "constraint '%s'", + (WXS_ATTRUSE_DECL(use))->defValue); + } + return(ctxt->err); + } + return(0); +} + + + + +/** + * xmlSchemaResolveAttrTypeReferences: + * @item: an attribute declaration + * @ctxt: a parser context + * + * Resolves the referenced type definition component. + */ +static int +xmlSchemaResolveAttrTypeReferences(xmlSchemaAttributePtr item, + xmlSchemaParserCtxtPtr ctxt) +{ + /* + * The simple type definition corresponding to the element + * information item in the [children], if present, otherwise the simple + * type definition ·resolved· to by the ·actual value· of the type + * [attribute], if present, otherwise the ·simple ur-type definition·. + */ + if (item->flags & XML_SCHEMAS_ATTR_INTERNAL_RESOLVED) + return(0); + item->flags |= XML_SCHEMAS_ATTR_INTERNAL_RESOLVED; + if (item->subtypes != NULL) + return(0); + if (item->typeName != NULL) { + xmlSchemaTypePtr type; + + type = xmlSchemaGetType(ctxt->schema, item->typeName, + item->typeNs); + if ((type == NULL) || (! WXS_IS_SIMPLE(type))) { + xmlSchemaPResCompAttrErr(ctxt, + XML_SCHEMAP_SRC_RESOLVE, + WXS_BASIC_CAST item, item->node, + "type", item->typeName, item->typeNs, + XML_SCHEMA_TYPE_SIMPLE, NULL); + return(ctxt->err); + } else + item->subtypes = type; + + } else { + /* + * The type defaults to the xs:anySimpleType. + */ + item->subtypes = xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYSIMPLETYPE); + } + return(0); +} + +/** + * xmlSchemaResolveIDCKeyReferences: + * @idc: the identity-constraint definition + * @ctxt: the schema parser context + * @name: the attribute name + * + * Resolve keyRef references to key/unique IDCs. + * Schema Component Constraint: + * Identity-constraint Definition Properties Correct (c-props-correct) + */ +static int +xmlSchemaResolveIDCKeyReferences(xmlSchemaIDCPtr idc, + xmlSchemaParserCtxtPtr pctxt) +{ + if (idc->type != XML_SCHEMA_TYPE_IDC_KEYREF) + return(0); + if (idc->ref->name != NULL) { + idc->ref->item = (xmlSchemaBasicItemPtr) + xmlSchemaGetIDC(pctxt->schema, idc->ref->name, + idc->ref->targetNamespace); + if (idc->ref->item == NULL) { + /* + * TODO: It is actually not an error to fail to resolve + * at this stage. BUT we need to be that strict! + */ + xmlSchemaPResCompAttrErr(pctxt, + XML_SCHEMAP_SRC_RESOLVE, + WXS_BASIC_CAST idc, idc->node, + "refer", idc->ref->name, + idc->ref->targetNamespace, + XML_SCHEMA_TYPE_IDC_KEY, NULL); + return(pctxt->err); + } else if (idc->ref->item->type == XML_SCHEMA_TYPE_IDC_KEYREF) { + /* + * SPEC c-props-correct (1) + */ + xmlSchemaCustomErr(ACTXT_CAST pctxt, + XML_SCHEMAP_C_PROPS_CORRECT, + NULL, WXS_BASIC_CAST idc, + "The keyref references a keyref", + NULL, NULL); + idc->ref->item = NULL; + return(pctxt->err); + } else { + if (idc->nbFields != + ((xmlSchemaIDCPtr) idc->ref->item)->nbFields) { + xmlChar *str = NULL; + xmlSchemaIDCPtr refer; + + refer = (xmlSchemaIDCPtr) idc->ref->item; + /* + * SPEC c-props-correct(2) + * "If the {identity-constraint category} is keyref, + * the cardinality of the {fields} must equal that of + * the {fields} of the {referenced key}. + */ + xmlSchemaCustomErr(ACTXT_CAST pctxt, + XML_SCHEMAP_C_PROPS_CORRECT, + NULL, WXS_BASIC_CAST idc, + "The cardinality of the keyref differs from the " + "cardinality of the referenced key/unique '%s'", + xmlSchemaFormatQName(&str, refer->targetNamespace, + refer->name), + NULL); + FREE_AND_NULL(str) + return(pctxt->err); + } + } + } + return(0); +} + +static int +xmlSchemaResolveAttrUseProhibReferences(xmlSchemaAttributeUseProhibPtr prohib, + xmlSchemaParserCtxtPtr pctxt) +{ + if (xmlSchemaGetAttributeDecl(pctxt->schema, prohib->name, + prohib->targetNamespace) == NULL) { + + xmlSchemaPResCompAttrErr(pctxt, + XML_SCHEMAP_SRC_RESOLVE, + NULL, prohib->node, + "ref", prohib->name, prohib->targetNamespace, + XML_SCHEMA_TYPE_ATTRIBUTE, NULL); + return(XML_SCHEMAP_SRC_RESOLVE); + } + return(0); +} + +#define WXS_REDEFINED_TYPE(c) \ +(((xmlSchemaTypePtr) item)->flags & XML_SCHEMAS_TYPE_REDEFINED) + +#define WXS_REDEFINED_MODEL_GROUP_DEF(c) \ +(((xmlSchemaModelGroupDefPtr) item)->flags & XML_SCHEMA_MODEL_GROUP_DEF_REDEFINED) + +#define WXS_REDEFINED_ATTR_GROUP(c) \ +(((xmlSchemaAttributeGroupPtr) item)->flags & XML_SCHEMAS_ATTRGROUP_REDEFINED) + +static int +xmlSchemaCheckSRCRedefineFirst(xmlSchemaParserCtxtPtr pctxt) +{ + int err = 0; + xmlSchemaRedefPtr redef = WXS_CONSTRUCTOR(pctxt)->redefs; + xmlSchemaBasicItemPtr prev, item; + int wasRedefined; + + if (redef == NULL) + return(0); + + do { + item = redef->item; + /* + * First try to locate the redefined component in the + * schema graph starting with the redefined schema. + * NOTE: According to this schema bug entry: + * http://lists.w3.org/Archives/Public/www-xml-schema-comments/2005OctDec/0019.html + * it's not clear if the referenced component needs to originate + * from the d schema _document_ or the schema; the latter + * would include all imported and included sub-schemas of the + * d schema. Currenlty we latter approach is used. + * SUPPLEMENT: It seems that the WG moves towards the latter + * approach, so we are doing it right. + * + */ + prev = xmlSchemaFindRedefCompInGraph( + redef->targetBucket, item->type, + redef->refName, redef->refTargetNs); + if (prev == NULL) { + xmlChar *str = NULL; + xmlNodePtr node; + + /* + * SPEC src-redefine: + * (6.2.1) "The ·actual value· of its own name attribute plus + * target namespace must successfully ·resolve· to a model + * group definition in I." + * (7.2.1) "The ·actual value· of its own name attribute plus + * target namespace must successfully ·resolve· to an attribute + * group definition in I." + + * + * Note that, if we are redefining with the use of references + * to components, the spec assumes the src-resolve to be used; + * but this won't assure that we search only *inside* the + * redefined schema. + */ + if (redef->reference) + node = WXS_ITEM_NODE(redef->reference); + else + node = WXS_ITEM_NODE(item); + xmlSchemaCustomErr(ACTXT_CAST pctxt, + /* + * TODO: error code. + * Probably XML_SCHEMAP_SRC_RESOLVE, if this is using the + * reference kind. + */ + XML_SCHEMAP_SRC_REDEFINE, node, NULL, + "The %s '%s' to be redefined could not be found in " + "the redefined schema", + WXS_ITEM_TYPE_NAME(item), + xmlSchemaFormatQName(&str, redef->refTargetNs, + redef->refName)); + FREE_AND_NULL(str); + err = pctxt->err; + redef = redef->next; + continue; + } + /* + * TODO: Obtaining and setting the redefinition state is really + * clumsy. + */ + wasRedefined = 0; + switch (item->type) { + case XML_SCHEMA_TYPE_COMPLEX: + case XML_SCHEMA_TYPE_SIMPLE: + if ((WXS_TYPE_CAST prev)->flags & + XML_SCHEMAS_TYPE_REDEFINED) + { + wasRedefined = 1; + break; + } + /* Mark it as redefined. */ + (WXS_TYPE_CAST prev)->flags |= XML_SCHEMAS_TYPE_REDEFINED; + /* + * Assign the redefined type to the + * base type of the redefining type. + * TODO: How + */ + ((xmlSchemaTypePtr) item)->baseType = + (xmlSchemaTypePtr) prev; + break; + case XML_SCHEMA_TYPE_GROUP: + if ((WXS_MODEL_GROUPDEF_CAST prev)->flags & + XML_SCHEMA_MODEL_GROUP_DEF_REDEFINED) + { + wasRedefined = 1; + break; + } + /* Mark it as redefined. */ + (WXS_MODEL_GROUPDEF_CAST prev)->flags |= + XML_SCHEMA_MODEL_GROUP_DEF_REDEFINED; + if (redef->reference != NULL) { + /* + * Overwrite the QName-reference with the + * referenced model group def. + */ + (WXS_PTC_CAST redef->reference)->children = + WXS_TREE_CAST prev; + } + redef->target = prev; + break; + case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: + if ((WXS_ATTR_GROUP_CAST prev)->flags & + XML_SCHEMAS_ATTRGROUP_REDEFINED) + { + wasRedefined = 1; + break; + } + (WXS_ATTR_GROUP_CAST prev)->flags |= + XML_SCHEMAS_ATTRGROUP_REDEFINED; + if (redef->reference != NULL) { + /* + * Assign the redefined attribute group to the + * QName-reference component. + * This is the easy case, since we will just + * expand the redefined group. + */ + (WXS_QNAME_CAST redef->reference)->item = prev; + redef->target = NULL; + } else { + /* + * This is the complicated case: we need + * to apply src-redefine (7.2.2) at a later + * stage, i.e. when attribute group references + * have beed expanded and simple types have + * beed fixed. + */ + redef->target = prev; + } + break; + default: + PERROR_INT("xmlSchemaResolveRedefReferences", + "Unexpected redefined component type"); + return(-1); + } + if (wasRedefined) { + xmlChar *str = NULL; + xmlNodePtr node; + + if (redef->reference) + node = WXS_ITEM_NODE(redef->reference); + else + node = WXS_ITEM_NODE(redef->item); + + xmlSchemaCustomErr(ACTXT_CAST pctxt, + /* TODO: error code. */ + XML_SCHEMAP_SRC_REDEFINE, + node, NULL, + "The referenced %s was already redefined. Multiple " + "redefinition of the same component is not supported", + xmlSchemaGetComponentDesignation(&str, prev), + NULL); + FREE_AND_NULL(str) + err = pctxt->err; + redef = redef->next; + continue; + } + redef = redef->next; + } while (redef != NULL); + + return(err); +} + +static int +xmlSchemaCheckSRCRedefineSecond(xmlSchemaParserCtxtPtr pctxt) +{ + int err = 0; + xmlSchemaRedefPtr redef = WXS_CONSTRUCTOR(pctxt)->redefs; + xmlSchemaBasicItemPtr item; + + if (redef == NULL) + return(0); + + do { + if (redef->target == NULL) { + redef = redef->next; + continue; + } + item = redef->item; + + switch (item->type) { + case XML_SCHEMA_TYPE_SIMPLE: + case XML_SCHEMA_TYPE_COMPLEX: + /* + * Since the spec wants the {name} of the redefined + * type to be 'absent', we'll NULL it. + */ + (WXS_TYPE_CAST redef->target)->name = NULL; + + /* + * TODO: Seems like there's nothing more to do. The normal + * inheritance mechanism is used. But not 100% sure. + */ + break; + case XML_SCHEMA_TYPE_GROUP: + /* + * URGENT TODO: + * SPEC src-redefine: + * (6.2.2) "The {model group} of the model group definition + * which corresponds to it per XML Representation of Model + * Group Definition Schema Components (§3.7.2) must be a + * ·valid restriction· of the {model group} of that model + * group definition in I, as defined in Particle Valid + * (Restriction) (§3.9.6)." + */ + break; + case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: + /* + * SPEC src-redefine: + * (7.2.2) "The {attribute uses} and {attribute wildcard} of + * the attribute group definition which corresponds to it + * per XML Representation of Attribute Group Definition Schema + * Components (§3.6.2) must be ·valid restrictions· of the + * {attribute uses} and {attribute wildcard} of that attribute + * group definition in I, as defined in clause 2, clause 3 and + * clause 4 of Derivation Valid (Restriction, Complex) + * (§3.4.6) (where references to the base type definition are + * understood as references to the attribute group definition + * in I)." + */ + err = xmlSchemaCheckDerivationOKRestriction2to4(pctxt, + XML_SCHEMA_ACTION_REDEFINE, + item, redef->target, + (WXS_ATTR_GROUP_CAST item)->attrUses, + (WXS_ATTR_GROUP_CAST redef->target)->attrUses, + (WXS_ATTR_GROUP_CAST item)->attributeWildcard, + (WXS_ATTR_GROUP_CAST redef->target)->attributeWildcard); + if (err == -1) + return(-1); + break; + default: + break; + } + redef = redef->next; + } while (redef != NULL); + return(0); +} + + +static int +xmlSchemaAddComponents(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaBucketPtr bucket) +{ + xmlSchemaBasicItemPtr item; + int err; + xmlHashTablePtr *table; + const xmlChar *name; + int i; + +#define WXS_GET_GLOBAL_HASH(c, slot) { \ + if (WXS_IS_BUCKET_IMPMAIN((c)->type)) \ + table = &(WXS_IMPBUCKET((c))->schema->slot); \ + else \ + table = &(WXS_INCBUCKET((c))->ownerImport->schema->slot); } + + /* + * Add global components to the schema's hash tables. + * This is the place where duplicate components will be + * detected. + * TODO: I think normally we should support imports of the + * same namespace from multiple locations. We don't do currently, + * but if we do then according to: + * http://www.w3.org/Bugs/Public/show_bug.cgi?id=2224 + * we would need, if imported directly, to import redefined + * components as well to be able to catch clashing components. + * (I hope I'll still know what this means after some months :-() + */ + if (bucket == NULL) + return(-1); + if (bucket->flags & XML_SCHEMA_BUCKET_COMPS_ADDED) + return(0); + bucket->flags |= XML_SCHEMA_BUCKET_COMPS_ADDED; + + for (i = 0; i < bucket->globals->nbItems; i++) { + item = bucket->globals->items[i]; + table = NULL; + switch (item->type) { + case XML_SCHEMA_TYPE_COMPLEX: + case XML_SCHEMA_TYPE_SIMPLE: + if (WXS_REDEFINED_TYPE(item)) + continue; + name = (WXS_TYPE_CAST item)->name; + WXS_GET_GLOBAL_HASH(bucket, typeDecl) + break; + case XML_SCHEMA_TYPE_ELEMENT: + name = (WXS_ELEM_CAST item)->name; + WXS_GET_GLOBAL_HASH(bucket, elemDecl) + break; + case XML_SCHEMA_TYPE_ATTRIBUTE: + name = (WXS_ATTR_CAST item)->name; + WXS_GET_GLOBAL_HASH(bucket, attrDecl) + break; + case XML_SCHEMA_TYPE_GROUP: + if (WXS_REDEFINED_MODEL_GROUP_DEF(item)) + continue; + name = (WXS_MODEL_GROUPDEF_CAST item)->name; + WXS_GET_GLOBAL_HASH(bucket, groupDecl) + break; + case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: + if (WXS_REDEFINED_ATTR_GROUP(item)) + continue; + name = (WXS_ATTR_GROUP_CAST item)->name; + WXS_GET_GLOBAL_HASH(bucket, attrgrpDecl) + break; + case XML_SCHEMA_TYPE_IDC_KEY: + case XML_SCHEMA_TYPE_IDC_UNIQUE: + case XML_SCHEMA_TYPE_IDC_KEYREF: + name = (WXS_IDC_CAST item)->name; + WXS_GET_GLOBAL_HASH(bucket, idcDef) + break; + case XML_SCHEMA_TYPE_NOTATION: + name = ((xmlSchemaNotationPtr) item)->name; + WXS_GET_GLOBAL_HASH(bucket, notaDecl) + break; + default: + PERROR_INT("xmlSchemaAddComponents", + "Unexpected global component type"); + continue; + } + if (*table == NULL) { + *table = xmlHashCreateDict(10, pctxt->dict); + if (*table == NULL) { + PERROR_INT("xmlSchemaAddComponents", + "failed to create a component hash table"); + return(-1); + } + } + err = xmlHashAddEntry(*table, name, item); + if (err != 0) { + xmlChar *str = NULL; + + xmlSchemaCustomErr(ACTXT_CAST pctxt, + XML_SCHEMAP_REDEFINED_TYPE, + WXS_ITEM_NODE(item), + WXS_BASIC_CAST item, + "A global %s '%s' does already exist", + WXS_ITEM_TYPE_NAME(item), + xmlSchemaGetComponentQName(&str, item)); + FREE_AND_NULL(str); + } + } + /* + * Process imported/included schemas. + */ + if (bucket->relations != NULL) { + xmlSchemaSchemaRelationPtr rel = bucket->relations; + do { + if ((rel->bucket != NULL) && + ((rel->bucket->flags & XML_SCHEMA_BUCKET_COMPS_ADDED) == 0)) { + if (xmlSchemaAddComponents(pctxt, rel->bucket) == -1) + return(-1); + } + rel = rel->next; + } while (rel != NULL); + } + return(0); +} + +static int +xmlSchemaFixupComponents(xmlSchemaParserCtxtPtr pctxt, + xmlSchemaBucketPtr rootBucket) +{ + xmlSchemaConstructionCtxtPtr con = pctxt->constructor; + xmlSchemaTreeItemPtr item, *items; + int nbItems, i, ret = 0; + xmlSchemaBucketPtr oldbucket = con->bucket; + +#define FIXHFAILURE if (pctxt->err == XML_SCHEMAP_INTERNAL) goto exit_failure; + + if ((con->pending == NULL) || + (con->pending->nbItems == 0)) + return(0); + + /* + * Since xmlSchemaFixupComplexType() will create new particles + * (local components), and those particle components need a bucket + * on the constructor, we'll assure here that the constructor has + * a bucket. + * TODO: Think about storing locals _only_ on the main bucket. + */ + if (con->bucket == NULL) + con->bucket = rootBucket; + + /* TODO: + * SPEC (src-redefine): + * (6.2) "If it has no such self-reference, then all of the + * following must be true:" + + * (6.2.2) The {model group} of the model group definition which + * corresponds to it per XML Representation of Model Group + * Definition Schema Components (§3.7.2) must be a ·valid + * restriction· of the {model group} of that model group definition + * in I, as defined in Particle Valid (Restriction) (§3.9.6)." + */ + xmlSchemaCheckSRCRedefineFirst(pctxt); + + /* + * Add global components to the schemata's hash tables. + */ + xmlSchemaAddComponents(pctxt, rootBucket); + + pctxt->ctxtType = NULL; + items = (xmlSchemaTreeItemPtr *) con->pending->items; + nbItems = con->pending->nbItems; + /* + * Now that we have parsed *all* the schema document(s) and converted + * them to schema components, we can resolve references, apply component + * constraints, create the FSA from the content model, etc. + */ + /* + * Resolve references of.. + * + * 1. element declarations: + * - the type definition + * - the substitution group affiliation + * 2. simple/complex types: + * - the base type definition + * - the memberTypes of union types + * - the itemType of list types + * 3. attributes declarations and attribute uses: + * - the type definition + * - if an attribute use, then the attribute declaration + * 4. attribute group references: + * - the attribute group definition + * 5. particles: + * - the term of the particle (e.g. a model group) + * 6. IDC key-references: + * - the referenced IDC 'key' or 'unique' definition + * 7. Attribute prohibitions which had a "ref" attribute. + */ + for (i = 0; i < nbItems; i++) { + item = items[i]; + switch (item->type) { + case XML_SCHEMA_TYPE_ELEMENT: + xmlSchemaResolveElementReferences( + (xmlSchemaElementPtr) item, pctxt); + FIXHFAILURE; + break; + case XML_SCHEMA_TYPE_COMPLEX: + case XML_SCHEMA_TYPE_SIMPLE: + xmlSchemaResolveTypeReferences( + (xmlSchemaTypePtr) item, pctxt); + FIXHFAILURE; + break; + case XML_SCHEMA_TYPE_ATTRIBUTE: + xmlSchemaResolveAttrTypeReferences( + (xmlSchemaAttributePtr) item, pctxt); + FIXHFAILURE; + break; + case XML_SCHEMA_TYPE_ATTRIBUTE_USE: + xmlSchemaResolveAttrUseReferences( + (xmlSchemaAttributeUsePtr) item, pctxt); + FIXHFAILURE; + break; + case XML_SCHEMA_EXTRA_QNAMEREF: + if ((WXS_QNAME_CAST item)->itemType == + XML_SCHEMA_TYPE_ATTRIBUTEGROUP) + { + xmlSchemaResolveAttrGroupReferences( + WXS_QNAME_CAST item, pctxt); + } + FIXHFAILURE; + break; + case XML_SCHEMA_TYPE_SEQUENCE: + case XML_SCHEMA_TYPE_CHOICE: + case XML_SCHEMA_TYPE_ALL: + xmlSchemaResolveModelGroupParticleReferences(pctxt, + WXS_MODEL_GROUP_CAST item); + FIXHFAILURE; + break; + case XML_SCHEMA_TYPE_IDC_KEY: + case XML_SCHEMA_TYPE_IDC_UNIQUE: + case XML_SCHEMA_TYPE_IDC_KEYREF: + xmlSchemaResolveIDCKeyReferences( + (xmlSchemaIDCPtr) item, pctxt); + FIXHFAILURE; + break; + case XML_SCHEMA_EXTRA_ATTR_USE_PROHIB: + /* + * Handle attribue prohibition which had a + * "ref" attribute. + */ + xmlSchemaResolveAttrUseProhibReferences( + WXS_ATTR_PROHIB_CAST item, pctxt); + FIXHFAILURE; + break; + default: + break; + } + } + if (pctxt->nberrors != 0) + goto exit_error; + + /* + * Now that all references are resolved we + * can check for circularity of... + * 1. the base axis of type definitions + * 2. nested model group definitions + * 3. nested attribute group definitions + * TODO: check for circual substitution groups. + */ + for (i = 0; i < nbItems; i++) { + item = items[i]; + /* + * Let's better stop on the first error here. + */ + switch (item->type) { + case XML_SCHEMA_TYPE_COMPLEX: + case XML_SCHEMA_TYPE_SIMPLE: + xmlSchemaCheckTypeDefCircular( + (xmlSchemaTypePtr) item, pctxt); + FIXHFAILURE; + if (pctxt->nberrors != 0) + goto exit_error; + break; + case XML_SCHEMA_TYPE_GROUP: + xmlSchemaCheckGroupDefCircular( + (xmlSchemaModelGroupDefPtr) item, pctxt); + FIXHFAILURE; + if (pctxt->nberrors != 0) + goto exit_error; + break; + case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: + xmlSchemaCheckAttrGroupCircular( + (xmlSchemaAttributeGroupPtr) item, pctxt); + FIXHFAILURE; + if (pctxt->nberrors != 0) + goto exit_error; + break; + default: + break; + } + } + if (pctxt->nberrors != 0) + goto exit_error; + /* + * Model group definition references: + * Such a reference is reflected by a particle at the component + * level. Until now the 'term' of such particles pointed + * to the model group definition; this was done, in order to + * ease circularity checks. Now we need to set the 'term' of + * such particles to the model group of the model group definition. + */ + for (i = 0; i < nbItems; i++) { + item = items[i]; + switch (item->type) { + case XML_SCHEMA_TYPE_SEQUENCE: + case XML_SCHEMA_TYPE_CHOICE: + xmlSchemaModelGroupToModelGroupDefFixup(pctxt, + WXS_MODEL_GROUP_CAST item); + break; + default: + break; + } + } + if (pctxt->nberrors != 0) + goto exit_error; + /* + * Expand attribute group references of attribute group definitions. + */ + for (i = 0; i < nbItems; i++) { + item = items[i]; + switch (item->type) { + case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: + if ((! WXS_ATTR_GROUP_EXPANDED(item)) && + WXS_ATTR_GROUP_HAS_REFS(item)) + { + xmlSchemaAttributeGroupExpandRefs(pctxt, + WXS_ATTR_GROUP_CAST item); + FIXHFAILURE; + } + break; + default: + break; + } + } + if (pctxt->nberrors != 0) + goto exit_error; + /* + * First compute the variety of simple types. This is needed as + * a seperate step, since otherwise we won't be able to detect + * circular union types in all cases. + */ + for (i = 0; i < nbItems; i++) { + item = items[i]; + switch (item->type) { + case XML_SCHEMA_TYPE_SIMPLE: + if (WXS_IS_TYPE_NOT_FIXED_1((xmlSchemaTypePtr) item)) { + xmlSchemaFixupSimpleTypeStageOne(pctxt, + (xmlSchemaTypePtr) item); + FIXHFAILURE; + } + break; + default: + break; + } + } + if (pctxt->nberrors != 0) + goto exit_error; + /* + * Detect circular union types. Note that this needs the variety to + * be already computed. + */ + for (i = 0; i < nbItems; i++) { + item = items[i]; + switch (item->type) { + case XML_SCHEMA_TYPE_SIMPLE: + if (((xmlSchemaTypePtr) item)->memberTypes != NULL) { + xmlSchemaCheckUnionTypeDefCircular(pctxt, + (xmlSchemaTypePtr) item); + FIXHFAILURE; + } + break; + default: + break; + } + } + if (pctxt->nberrors != 0) + goto exit_error; + + /* + * Do the complete type fixup for simple types. + */ + for (i = 0; i < nbItems; i++) { + item = items[i]; + switch (item->type) { + case XML_SCHEMA_TYPE_SIMPLE: + if (WXS_IS_TYPE_NOT_FIXED(WXS_TYPE_CAST item)) { + xmlSchemaFixupSimpleTypeStageTwo(pctxt, WXS_TYPE_CAST item); + FIXHFAILURE; + } + break; + default: + break; + } + } + if (pctxt->nberrors != 0) + goto exit_error; + /* + * At this point we need all simple types to be builded and checked. + */ + /* + * Apply contraints for attribute declarations. + */ + for (i = 0; i < nbItems; i++) { + item = items[i]; + switch (item->type) { + case XML_SCHEMA_TYPE_ATTRIBUTE: + xmlSchemaCheckAttrPropsCorrect(pctxt, WXS_ATTR_CAST item); + FIXHFAILURE; + break; + default: + break; + } + } + if (pctxt->nberrors != 0) + goto exit_error; + /* + * Apply constraints for attribute uses. + */ + for (i = 0; i < nbItems; i++) { + item = items[i]; + switch (item->type) { + case XML_SCHEMA_TYPE_ATTRIBUTE_USE: + if (((xmlSchemaAttributeUsePtr)item)->defValue != NULL) { + xmlSchemaCheckAttrUsePropsCorrect(pctxt, + WXS_ATTR_USE_CAST item); + FIXHFAILURE; + } + break; + default: + break; + } + } + if (pctxt->nberrors != 0) + goto exit_error; + + /* + * Apply constraints for attribute group definitions. + */ + for (i = 0; i < nbItems; i++) { + item = items[i]; + switch (item->type) { + case XML_SCHEMA_TYPE_ATTRIBUTEGROUP: + if (( (WXS_ATTR_GROUP_CAST item)->attrUses != NULL) && + ( (WXS_LIST_CAST (WXS_ATTR_GROUP_CAST item)->attrUses)->nbItems > 1)) + { + xmlSchemaCheckAGPropsCorrect(pctxt, WXS_ATTR_GROUP_CAST item); + FIXHFAILURE; + } + break; + default: + break; + } + } + if (pctxt->nberrors != 0) + goto exit_error; + + /* + * Apply constraints for redefinitions. + */ + if (WXS_CONSTRUCTOR(pctxt)->redefs != NULL) + xmlSchemaCheckSRCRedefineSecond(pctxt); + if (pctxt->nberrors != 0) + goto exit_error; + + /* + * Fixup complex types. + */ + for (i = 0; i < nbItems; i++) { + item = con->pending->items[i]; + switch (item->type) { + case XML_SCHEMA_TYPE_COMPLEX: + if (WXS_IS_TYPE_NOT_FIXED(WXS_TYPE_CAST item)) { + xmlSchemaFixupComplexType(pctxt, WXS_TYPE_CAST item); + FIXHFAILURE; + } + break; + default: + break; + } + } + if (pctxt->nberrors != 0) + goto exit_error; + + /* + * The list could have changed, since xmlSchemaFixupComplexType() + * will create particles and model groups in some cases. + */ + items = (xmlSchemaTreeItemPtr *) con->pending->items; + nbItems = con->pending->nbItems; + + /* + * At this point all complex types need to be builded and checked. + */ + /* + * Apply some constraints for element declarations. + */ + for (i = 0; i < nbItems; i++) { + item = items[i]; + switch (item->type) { + case XML_SCHEMA_TYPE_ELEMENT: + + if ((((xmlSchemaElementPtr) item)->flags & + XML_SCHEMAS_ELEM_INTERNAL_CHECKED) == 0) { + xmlSchemaCheckElementDeclComponent( + (xmlSchemaElementPtr) item, pctxt); + FIXHFAILURE; + } + break; + default: + break; + } + } + if (pctxt->nberrors != 0) + goto exit_error; + /* + * Finally we can build the automaton from the content model of + * complex types. + */ + + for (i = 0; i < nbItems; i++) { + item = items[i]; + switch (item->type) { + case XML_SCHEMA_TYPE_COMPLEX: + xmlSchemaBuildContentModel((xmlSchemaTypePtr) item, pctxt); + /* FIXHFAILURE; */ + break; + default: + break; + } + } + if (pctxt->nberrors != 0) + goto exit_error; + /* + * URGENT TODO: cos-element-consistent + */ + goto exit; + +exit_error: + ret = pctxt->err; + goto exit; + +exit_failure: + ret = -1; + +exit: + /* + * Reset the constructor. This is needed for XSI acquisition, since + * those items will be processed over and over again for every XSI + * if not cleared here. + */ + con->bucket = oldbucket; + con->pending->nbItems = 0; + if (con->substGroups != NULL) { + xmlHashFree(con->substGroups, + (xmlHashDeallocator) xmlSchemaSubstGroupFree); + con->substGroups = NULL; + } + if (con->redefs != NULL) { + xmlSchemaRedefListFree(con->redefs); + con->redefs = NULL; + } + return(ret); +} +/** + * xmlSchemaParse: + * @ctxt: a schema validation context + * + * parse a schema definition resource and build an internal + * XML Shema struture which can be used to validate instances. + * + * Returns the internal XML Schema structure built from the resource or + * NULL in case of error + */ +xmlSchemaPtr +xmlSchemaParse(xmlSchemaParserCtxtPtr ctxt) +{ + xmlSchemaPtr mainSchema = NULL; + xmlSchemaBucketPtr bucket = NULL; + int res; + + /* + * This one is used if the schema to be parsed was specified via + * the API; i.e. not automatically by the validated instance document. + */ + + xmlSchemaInitTypes(); + + if (ctxt == NULL) + return (NULL); + + /* TODO: Init the context. Is this all we need?*/ + ctxt->nberrors = 0; + ctxt->err = 0; + ctxt->counter = 0; + + /* Create the *main* schema. */ + mainSchema = xmlSchemaNewSchema(ctxt); + if (mainSchema == NULL) + goto exit_failure; + /* + * Create the schema constructor. + */ + if (ctxt->constructor == NULL) { + ctxt->constructor = xmlSchemaConstructionCtxtCreate(ctxt->dict); + if (ctxt->constructor == NULL) + return(NULL); + /* Take ownership of the constructor to be able to free it. */ + ctxt->ownsConstructor = 1; + } + ctxt->constructor->mainSchema = mainSchema; + /* + * Locate and add the schema document. + */ + res = xmlSchemaAddSchemaDoc(ctxt, XML_SCHEMA_SCHEMA_MAIN, + ctxt->URL, ctxt->doc, ctxt->buffer, ctxt->size, NULL, + NULL, NULL, &bucket); + if (res == -1) + goto exit_failure; + if (res != 0) + goto exit; + + if (bucket == NULL) { + /* TODO: Error code, actually we failed to *locate* the schema. */ + if (ctxt->URL) + xmlSchemaCustomErr(ACTXT_CAST ctxt, XML_SCHEMAP_FAILED_LOAD, + NULL, NULL, + "Failed to locate the main schema resource at '%s'", + ctxt->URL, NULL); + else + xmlSchemaCustomErr(ACTXT_CAST ctxt, XML_SCHEMAP_FAILED_LOAD, + NULL, NULL, + "Failed to locate the main schema resource", + NULL, NULL); + goto exit; + } + /* Then do the parsing for good. */ + if (xmlSchemaParseNewDocWithContext(ctxt, mainSchema, bucket) == -1) + goto exit_failure; + if (ctxt->nberrors != 0) + goto exit; + + mainSchema->doc = bucket->doc; + mainSchema->preserve = ctxt->preserve; + + ctxt->schema = mainSchema; + + if (xmlSchemaFixupComponents(ctxt, WXS_CONSTRUCTOR(ctxt)->mainBucket) == -1) + goto exit_failure; + + /* + * TODO: This is not nice, since we cannot distinguish from the + * result if there was an internal error or not. + */ +exit: + if (ctxt->nberrors != 0) { + if (mainSchema) { + xmlSchemaFree(mainSchema); + mainSchema = NULL; + } + if (ctxt->constructor) { + xmlSchemaConstructionCtxtFree(ctxt->constructor); + ctxt->constructor = NULL; + ctxt->ownsConstructor = 0; + } + } + ctxt->schema = NULL; + return(mainSchema); +exit_failure: + /* + * Quite verbose, but should catch internal errors, which were + * not communitated. + */ + if (mainSchema) { + xmlSchemaFree(mainSchema); + mainSchema = NULL; + } + if (ctxt->constructor) { + xmlSchemaConstructionCtxtFree(ctxt->constructor); + ctxt->constructor = NULL; + ctxt->ownsConstructor = 0; + } + PERROR_INT2("xmlSchemaParse", + "An internal error occured"); + ctxt->schema = NULL; + return(NULL); +} + +/** + * xmlSchemaSetParserErrors: + * @ctxt: a schema validation context + * @err: the error callback + * @warn: the warning callback + * @ctx: contextual data for the callbacks + * + * Set the callback functions used to handle errors for a validation context + */ +void +xmlSchemaSetParserErrors(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaValidityErrorFunc err, + xmlSchemaValidityWarningFunc warn, void *ctx) +{ + if (ctxt == NULL) + return; + ctxt->error = err; + ctxt->warning = warn; + ctxt->errCtxt = ctx; + if (ctxt->vctxt != NULL) + xmlSchemaSetValidErrors(ctxt->vctxt, err, warn, ctx); +} + +/** + * xmlSchemaSetParserStructuredErrors: + * @ctxt: a schema parser context + * @serror: the structured error function + * @ctx: the functions context + * + * Set the structured error callback + */ +void +xmlSchemaSetParserStructuredErrors(xmlSchemaParserCtxtPtr ctxt, + xmlStructuredErrorFunc serror, + void *ctx) +{ + if (ctxt == NULL) + return; + ctxt->serror = serror; + ctxt->errCtxt = ctx; + if (ctxt->vctxt != NULL) + xmlSchemaSetValidStructuredErrors(ctxt->vctxt, serror, ctx); +} + +/** + * xmlSchemaGetParserErrors: + * @ctxt: a XMl-Schema parser context + * @err: the error callback result + * @warn: the warning callback result + * @ctx: contextual data for the callbacks result + * + * Get the callback information used to handle errors for a parser context + * + * Returns -1 in case of failure, 0 otherwise + */ +int +xmlSchemaGetParserErrors(xmlSchemaParserCtxtPtr ctxt, + xmlSchemaValidityErrorFunc * err, + xmlSchemaValidityWarningFunc * warn, void **ctx) +{ + if (ctxt == NULL) + return(-1); + if (err != NULL) + *err = ctxt->error; + if (warn != NULL) + *warn = ctxt->warning; + if (ctx != NULL) + *ctx = ctxt->errCtxt; + return(0); +} + +/** + * xmlSchemaFacetTypeToString: + * @type: the facet type + * + * Convert the xmlSchemaTypeType to a char string. + * + * Returns the char string representation of the facet type if the + * type is a facet and an "Internal Error" string otherwise. + */ +static const xmlChar * +xmlSchemaFacetTypeToString(xmlSchemaTypeType type) +{ + switch (type) { + case XML_SCHEMA_FACET_PATTERN: + return (BAD_CAST "pattern"); + case XML_SCHEMA_FACET_MAXEXCLUSIVE: + return (BAD_CAST "maxExclusive"); + case XML_SCHEMA_FACET_MAXINCLUSIVE: + return (BAD_CAST "maxInclusive"); + case XML_SCHEMA_FACET_MINEXCLUSIVE: + return (BAD_CAST "minExclusive"); + case XML_SCHEMA_FACET_MININCLUSIVE: + return (BAD_CAST "minInclusive"); + case XML_SCHEMA_FACET_WHITESPACE: + return (BAD_CAST "whiteSpace"); + case XML_SCHEMA_FACET_ENUMERATION: + return (BAD_CAST "enumeration"); + case XML_SCHEMA_FACET_LENGTH: + return (BAD_CAST "length"); + case XML_SCHEMA_FACET_MAXLENGTH: + return (BAD_CAST "maxLength"); + case XML_SCHEMA_FACET_MINLENGTH: + return (BAD_CAST "minLength"); + case XML_SCHEMA_FACET_TOTALDIGITS: + return (BAD_CAST "totalDigits"); + case XML_SCHEMA_FACET_FRACTIONDIGITS: + return (BAD_CAST "fractionDigits"); + default: + break; + } + return (BAD_CAST "Internal Error"); +} + +static xmlSchemaWhitespaceValueType +xmlSchemaGetWhiteSpaceFacetValue(xmlSchemaTypePtr type) +{ + /* + * The normalization type can be changed only for types which are derived + * from xsd:string. + */ + if (type->type == XML_SCHEMA_TYPE_BASIC) { + /* + * Note that we assume a whitespace of preserve for anySimpleType. + */ + if ((type->builtInType == XML_SCHEMAS_STRING) || + (type->builtInType == XML_SCHEMAS_ANYSIMPLETYPE)) + return(XML_SCHEMA_WHITESPACE_PRESERVE); + else if (type->builtInType == XML_SCHEMAS_NORMSTRING) + return(XML_SCHEMA_WHITESPACE_REPLACE); + else { + /* + * For all ·atomic· datatypes other than string (and types ·derived· + * by ·restriction· from it) the value of whiteSpace is fixed to + * collapse + * Note that this includes built-in list datatypes. + */ + return(XML_SCHEMA_WHITESPACE_COLLAPSE); + } + } else if (WXS_IS_LIST(type)) { + /* + * For list types the facet "whiteSpace" is fixed to "collapse". + */ + return (XML_SCHEMA_WHITESPACE_COLLAPSE); + } else if (WXS_IS_UNION(type)) { + return (XML_SCHEMA_WHITESPACE_UNKNOWN); + } else if (WXS_IS_ATOMIC(type)) { + if (type->flags & XML_SCHEMAS_TYPE_WHITESPACE_PRESERVE) + return (XML_SCHEMA_WHITESPACE_PRESERVE); + else if (type->flags & XML_SCHEMAS_TYPE_WHITESPACE_REPLACE) + return (XML_SCHEMA_WHITESPACE_REPLACE); + else + return (XML_SCHEMA_WHITESPACE_COLLAPSE); + } + return (-1); +} + +/************************************************************************ + * * + * Simple type validation * + * * + ************************************************************************/ + + +/************************************************************************ + * * + * DOM Validation code * + * * + ************************************************************************/ + +/** + * xmlSchemaAssembleByLocation: + * @pctxt: a schema parser context + * @vctxt: a schema validation context + * @schema: the existing schema + * @node: the node that fired the assembling + * @nsName: the namespace name of the new schema + * @location: the location of the schema + * + * Expands an existing schema by an additional schema. + * + * Returns 0 if the new schema is correct, a positive error code + * number otherwise and -1 in case of an internal or API error. + */ +static int +xmlSchemaAssembleByLocation(xmlSchemaValidCtxtPtr vctxt, + xmlSchemaPtr schema, + xmlNodePtr node, + const xmlChar *nsName, + const xmlChar *location) +{ + int ret = 0; + xmlSchemaParserCtxtPtr pctxt; + xmlSchemaBucketPtr bucket = NULL; + + if ((vctxt == NULL) || (schema == NULL)) + return (-1); + + if (vctxt->pctxt == NULL) { + VERROR_INT("xmlSchemaAssembleByLocation", + "no parser context available"); + return(-1); + } + pctxt = vctxt->pctxt; + if (pctxt->constructor == NULL) { + PERROR_INT("xmlSchemaAssembleByLocation", + "no constructor"); + return(-1); + } + /* + * Acquire the schema document. + */ + location = xmlSchemaBuildAbsoluteURI(pctxt->dict, + location, node); + /* + * Note that we pass XML_SCHEMA_SCHEMA_IMPORT here; + * the process will automatically change this to + * XML_SCHEMA_SCHEMA_MAIN if it is the first schema document. + */ + ret = xmlSchemaAddSchemaDoc(pctxt, XML_SCHEMA_SCHEMA_IMPORT, + location, NULL, NULL, 0, node, NULL, nsName, + &bucket); + if (ret != 0) + return(ret); + if (bucket == NULL) { + /* + * Generate a warning that the document could not be located. + */ + xmlSchemaCustomWarning(ACTXT_CAST vctxt, XML_SCHEMAV_MISC, + node, NULL, + "The document at location '%s' could not be acquired", + location, NULL, NULL); + return(ret); + } + /* + * The first located schema will be handled as if all other + * schemas imported by XSI were imported by this first schema. + */ + if ((bucket != NULL) && + (WXS_CONSTRUCTOR(pctxt)->bucket == NULL)) + WXS_CONSTRUCTOR(pctxt)->bucket = bucket; + /* + * TODO: Is this handled like an import? I.e. is it not an error + * if the schema cannot be located? + */ + if ((bucket == NULL) || (! CAN_PARSE_SCHEMA(bucket))) + return(0); + /* + * We will reuse the parser context for every schema imported + * directly via XSI. So reset the context. + */ + pctxt->nberrors = 0; + pctxt->err = 0; + pctxt->doc = bucket->doc; + + ret = xmlSchemaParseNewDocWithContext(pctxt, schema, bucket); + if (ret == -1) { + pctxt->doc = NULL; + goto exit_failure; + } + /* Paranoid error channelling. */ + if ((ret == 0) && (pctxt->nberrors != 0)) + ret = pctxt->err; + if (pctxt->nberrors == 0) { + /* + * Only bother to fixup pending components, if there was + * no error yet. + * For every XSI acquired schema (and its sub-schemata) we will + * fixup the components. + */ + xmlSchemaFixupComponents(pctxt, bucket); + ret = pctxt->err; + /* + * Not nice, but we need somehow to channel the schema parser + * error to the validation context. + */ + if ((ret != 0) && (vctxt->err == 0)) + vctxt->err = ret; + vctxt->nberrors += pctxt->nberrors; + } else { + /* Add to validation error sum. */ + vctxt->nberrors += pctxt->nberrors; + } + pctxt->doc = NULL; + return(ret); +exit_failure: + pctxt->doc = NULL; + return (-1); +} + +static xmlSchemaAttrInfoPtr +xmlSchemaGetMetaAttrInfo(xmlSchemaValidCtxtPtr vctxt, + int metaType) +{ + if (vctxt->nbAttrInfos == 0) + return (NULL); + { + int i; + xmlSchemaAttrInfoPtr iattr; + + for (i = 0; i < vctxt->nbAttrInfos; i++) { + iattr = vctxt->attrInfos[i]; + if (iattr->metaType == metaType) + return (iattr); + } + + } + return (NULL); +} + +/** + * xmlSchemaAssembleByXSI: + * @vctxt: a schema validation context + * + * Expands an existing schema by an additional schema using + * the xsi:schemaLocation or xsi:noNamespaceSchemaLocation attribute + * of an instance. If xsi:noNamespaceSchemaLocation is used, @noNamespace + * must be set to 1. + * + * Returns 0 if the new schema is correct, a positive error code + * number otherwise and -1 in case of an internal or API error. + */ +static int +xmlSchemaAssembleByXSI(xmlSchemaValidCtxtPtr vctxt) +{ + const xmlChar *cur, *end; + const xmlChar *nsname = NULL, *location; + int count = 0; + int ret = 0; + xmlSchemaAttrInfoPtr iattr; + + /* + * Parse the value; we will assume an even number of values + * to be given (this is how Xerces and XSV work). + * + * URGENT TODO: !! This needs to work for both + * @noNamespaceSchemaLocation AND @schemaLocation on the same + * element !! + */ + iattr = xmlSchemaGetMetaAttrInfo(vctxt, + XML_SCHEMA_ATTR_INFO_META_XSI_SCHEMA_LOC); + if (iattr == NULL) + iattr = xmlSchemaGetMetaAttrInfo(vctxt, + XML_SCHEMA_ATTR_INFO_META_XSI_NO_NS_SCHEMA_LOC); + if (iattr == NULL) + return (0); + cur = iattr->value; + do { + /* + * TODO: Move the string parsing mechanism away from here. + */ + if (iattr->metaType == XML_SCHEMA_ATTR_INFO_META_XSI_SCHEMA_LOC) { + /* + * Get the namespace name. + */ + while (IS_BLANK_CH(*cur)) + cur++; + end = cur; + while ((*end != 0) && (!(IS_BLANK_CH(*end)))) + end++; + if (end == cur) + break; + count++; /* TODO: Don't use the schema's dict. */ + nsname = xmlDictLookup(vctxt->schema->dict, cur, end - cur); + cur = end; + } + /* + * Get the URI. + */ + while (IS_BLANK_CH(*cur)) + cur++; + end = cur; + while ((*end != 0) && (!(IS_BLANK_CH(*end)))) + end++; + if (end == cur) { + if (iattr->metaType == + XML_SCHEMA_ATTR_INFO_META_XSI_SCHEMA_LOC) + { + /* + * If using @schemaLocation then tuples are expected. + * I.e. the namespace name *and* the document's URI. + */ + xmlSchemaCustomWarning(ACTXT_CAST vctxt, XML_SCHEMAV_MISC, + iattr->node, NULL, + "The value must consist of tuples: the target namespace " + "name and the document's URI", NULL, NULL, NULL); + } + break; + } + count++; /* TODO: Don't use the schema's dict. */ + location = xmlDictLookup(vctxt->schema->dict, cur, end - cur); + cur = end; + ret = xmlSchemaAssembleByLocation(vctxt, vctxt->schema, + iattr->node, nsname, location); + if (ret == -1) { + VERROR_INT("xmlSchemaAssembleByXSI", + "assembling schemata"); + return (-1); + } + } while (*cur != 0); + return (ret); +} + +static const xmlChar * +xmlSchemaLookupNamespace(xmlSchemaValidCtxtPtr vctxt, + const xmlChar *prefix) +{ + if (vctxt->sax != NULL) { + int i, j; + xmlSchemaNodeInfoPtr inode; + + for (i = vctxt->depth; i >= 0; i--) { + if (vctxt->elemInfos[i]->nbNsBindings != 0) { + inode = vctxt->elemInfos[i]; + for (j = 0; j < inode->nbNsBindings * 2; j += 2) { + if (((prefix == NULL) && + (inode->nsBindings[j] == NULL)) || + ((prefix != NULL) && xmlStrEqual(prefix, + inode->nsBindings[j]))) { + + /* + * Note that the namespace bindings are already + * in a string dict. + */ + return (inode->nsBindings[j+1]); + } + } + } + } + return (NULL); +#ifdef LIBXML_WRITER_ENABLED + } else if (vctxt->reader != NULL) { + xmlChar *nsName; + + nsName = xmlTextReaderLookupNamespace(vctxt->reader, prefix); + if (nsName != NULL) { + const xmlChar *ret; + + ret = xmlDictLookup(vctxt->dict, nsName, -1); + xmlFree(nsName); + return (ret); + } else + return (NULL); +#endif + } else { + xmlNsPtr ns; + + if ((vctxt->inode->node == NULL) || + (vctxt->inode->node->doc == NULL)) { + VERROR_INT("xmlSchemaLookupNamespace", + "no node or node's doc avaliable"); + return (NULL); + } + ns = xmlSearchNs(vctxt->inode->node->doc, + vctxt->inode->node, prefix); + if (ns != NULL) + return (ns->href); + return (NULL); + } +} + +/* +* This one works on the schema of the validation context. +*/ +static int +xmlSchemaValidateNotation(xmlSchemaValidCtxtPtr vctxt, + xmlSchemaPtr schema, + xmlNodePtr node, + const xmlChar *value, + xmlSchemaValPtr *val, + int valNeeded) +{ + int ret; + + if (vctxt && (vctxt->schema == NULL)) { + VERROR_INT("xmlSchemaValidateNotation", + "a schema is needed on the validation context"); + return (-1); + } + ret = xmlValidateQName(value, 1); + if (ret != 0) + return (ret); + { + xmlChar *localName = NULL; + xmlChar *prefix = NULL; + + localName = xmlSplitQName2(value, &prefix); + if (prefix != NULL) { + const xmlChar *nsName = NULL; + + if (vctxt != NULL) + nsName = xmlSchemaLookupNamespace(vctxt, BAD_CAST prefix); + else if (node != NULL) { + xmlNsPtr ns = xmlSearchNs(node->doc, node, prefix); + if (ns != NULL) + nsName = ns->href; + } else { + xmlFree(prefix); + xmlFree(localName); + return (1); + } + if (nsName == NULL) { + xmlFree(prefix); + xmlFree(localName); + return (1); + } + if (xmlSchemaGetNotation(schema, localName, nsName) != NULL) { + if (valNeeded && (val != NULL)) { + (*val) = xmlSchemaNewNOTATIONValue(BAD_CAST localName, + BAD_CAST xmlStrdup(nsName)); + if (*val == NULL) + ret = -1; + } + } else + ret = 1; + xmlFree(prefix); + xmlFree(localName); + } else { + if (xmlSchemaGetNotation(schema, value, NULL) != NULL) { + if (valNeeded && (val != NULL)) { + (*val) = xmlSchemaNewNOTATIONValue( + BAD_CAST xmlStrdup(value), NULL); + if (*val == NULL) + ret = -1; + } + } else + return (1); + } + } + return (ret); +} + +static int +xmlSchemaVAddNodeQName(xmlSchemaValidCtxtPtr vctxt, + const xmlChar* lname, + const xmlChar* nsname) +{ + int i; + + lname = xmlDictLookup(vctxt->dict, lname, -1); + if (lname == NULL) + return(-1); + if (nsname != NULL) { + nsname = xmlDictLookup(vctxt->dict, nsname, -1); + if (nsname == NULL) + return(-1); + } + for (i = 0; i < vctxt->nodeQNames->nbItems; i += 2) { + if ((vctxt->nodeQNames->items [i] == lname) && + (vctxt->nodeQNames->items[i +1] == nsname)) + /* Already there */ + return(i); + } + /* Add new entry. */ + i = vctxt->nodeQNames->nbItems; + xmlSchemaItemListAdd(vctxt->nodeQNames, (void *) lname); + xmlSchemaItemListAdd(vctxt->nodeQNames, (void *) nsname); + return(i); +} + +/************************************************************************ + * * + * Validation of identity-constraints (IDC) * + * * + ************************************************************************/ + +/** + * xmlSchemaAugmentIDC: + * @idcDef: the IDC definition + * + * Creates an augmented IDC definition item. + * + * Returns the item, or NULL on internal errors. + */ +static void +xmlSchemaAugmentIDC(xmlSchemaIDCPtr idcDef, + xmlSchemaValidCtxtPtr vctxt) +{ + xmlSchemaIDCAugPtr aidc; + + aidc = (xmlSchemaIDCAugPtr) xmlMalloc(sizeof(xmlSchemaIDCAug)); + if (aidc == NULL) { + xmlSchemaVErrMemory(vctxt, + "xmlSchemaAugmentIDC: allocating an augmented IDC definition", + NULL); + return; + } + aidc->keyrefDepth = -1; + aidc->def = idcDef; + aidc->next = NULL; + if (vctxt->aidcs == NULL) + vctxt->aidcs = aidc; + else { + aidc->next = vctxt->aidcs; + vctxt->aidcs = aidc; + } + /* + * Save if we have keyrefs at all. + */ + if ((vctxt->hasKeyrefs == 0) && + (idcDef->type == XML_SCHEMA_TYPE_IDC_KEYREF)) + vctxt->hasKeyrefs = 1; +} + +/** + * xmlSchemaIDCNewBinding: + * @idcDef: the IDC definition of this binding + * + * Creates a new IDC binding. + * + * Returns the new IDC binding, NULL on internal errors. + */ +static xmlSchemaPSVIIDCBindingPtr +xmlSchemaIDCNewBinding(xmlSchemaIDCPtr idcDef) +{ + xmlSchemaPSVIIDCBindingPtr ret; + + ret = (xmlSchemaPSVIIDCBindingPtr) xmlMalloc( + sizeof(xmlSchemaPSVIIDCBinding)); + if (ret == NULL) { + xmlSchemaVErrMemory(NULL, + "allocating a PSVI IDC binding item", NULL); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchemaPSVIIDCBinding)); + ret->definition = idcDef; + return (ret); +} + +/** + * xmlSchemaIDCStoreNodeTableItem: + * @vctxt: the WXS validation context + * @item: the IDC node table item + * + * The validation context is used to store IDC node table items. + * They are stored to avoid copying them if IDC node-tables are merged + * with corresponding parent IDC node-tables (bubbling). + * + * Returns 0 if succeeded, -1 on internal errors. + */ +static int +xmlSchemaIDCStoreNodeTableItem(xmlSchemaValidCtxtPtr vctxt, + xmlSchemaPSVIIDCNodePtr item) +{ + /* + * Add to gobal list. + */ + if (vctxt->idcNodes == NULL) { + vctxt->idcNodes = (xmlSchemaPSVIIDCNodePtr *) + xmlMalloc(20 * sizeof(xmlSchemaPSVIIDCNodePtr)); + if (vctxt->idcNodes == NULL) { + xmlSchemaVErrMemory(vctxt, + "allocating the IDC node table item list", NULL); + return (-1); + } + vctxt->sizeIdcNodes = 20; + } else if (vctxt->sizeIdcNodes <= vctxt->nbIdcNodes) { + vctxt->sizeIdcNodes *= 2; + vctxt->idcNodes = (xmlSchemaPSVIIDCNodePtr *) + xmlRealloc(vctxt->idcNodes, vctxt->sizeIdcNodes * + sizeof(xmlSchemaPSVIIDCNodePtr)); + if (vctxt->idcNodes == NULL) { + xmlSchemaVErrMemory(vctxt, + "re-allocating the IDC node table item list", NULL); + return (-1); + } + } + vctxt->idcNodes[vctxt->nbIdcNodes++] = item; + + return (0); +} + +/** + * xmlSchemaIDCStoreKey: + * @vctxt: the WXS validation context + * @item: the IDC key + * + * The validation context is used to store an IDC key. + * + * Returns 0 if succeeded, -1 on internal errors. + */ +static int +xmlSchemaIDCStoreKey(xmlSchemaValidCtxtPtr vctxt, + xmlSchemaPSVIIDCKeyPtr key) +{ + /* + * Add to gobal list. + */ + if (vctxt->idcKeys == NULL) { + vctxt->idcKeys = (xmlSchemaPSVIIDCKeyPtr *) + xmlMalloc(40 * sizeof(xmlSchemaPSVIIDCKeyPtr)); + if (vctxt->idcKeys == NULL) { + xmlSchemaVErrMemory(vctxt, + "allocating the IDC key storage list", NULL); + return (-1); + } + vctxt->sizeIdcKeys = 40; + } else if (vctxt->sizeIdcKeys <= vctxt->nbIdcKeys) { + vctxt->sizeIdcKeys *= 2; + vctxt->idcKeys = (xmlSchemaPSVIIDCKeyPtr *) + xmlRealloc(vctxt->idcKeys, vctxt->sizeIdcKeys * + sizeof(xmlSchemaPSVIIDCKeyPtr)); + if (vctxt->idcKeys == NULL) { + xmlSchemaVErrMemory(vctxt, + "re-allocating the IDC key storage list", NULL); + return (-1); + } + } + vctxt->idcKeys[vctxt->nbIdcKeys++] = key; + + return (0); +} + +/** + * xmlSchemaIDCAppendNodeTableItem: + * @bind: the IDC binding + * @ntItem: the node-table item + * + * Appends the IDC node-table item to the binding. + * + * Returns 0 on success and -1 on internal errors. + */ +static int +xmlSchemaIDCAppendNodeTableItem(xmlSchemaPSVIIDCBindingPtr bind, + xmlSchemaPSVIIDCNodePtr ntItem) +{ + if (bind->nodeTable == NULL) { + bind->sizeNodes = 10; + bind->nodeTable = (xmlSchemaPSVIIDCNodePtr *) + xmlMalloc(10 * sizeof(xmlSchemaPSVIIDCNodePtr)); + if (bind->nodeTable == NULL) { + xmlSchemaVErrMemory(NULL, + "allocating an array of IDC node-table items", NULL); + return(-1); + } + } else if (bind->sizeNodes <= bind->nbNodes) { + bind->sizeNodes *= 2; + bind->nodeTable = (xmlSchemaPSVIIDCNodePtr *) + xmlRealloc(bind->nodeTable, bind->sizeNodes * + sizeof(xmlSchemaPSVIIDCNodePtr)); + if (bind->nodeTable == NULL) { + xmlSchemaVErrMemory(NULL, + "re-allocating an array of IDC node-table items", NULL); + return(-1); + } + } + bind->nodeTable[bind->nbNodes++] = ntItem; + return(0); +} + +/** + * xmlSchemaIDCAcquireBinding: + * @vctxt: the WXS validation context + * @matcher: the IDC matcher + * + * Looks up an PSVI IDC binding, for the IDC definition and + * of the given matcher. If none found, a new one is created + * and added to the IDC table. + * + * Returns an IDC binding or NULL on internal errors. + */ +static xmlSchemaPSVIIDCBindingPtr +xmlSchemaIDCAcquireBinding(xmlSchemaValidCtxtPtr vctxt, + xmlSchemaIDCMatcherPtr matcher) +{ + xmlSchemaNodeInfoPtr ielem; + + ielem = vctxt->elemInfos[matcher->depth]; + + if (ielem->idcTable == NULL) { + ielem->idcTable = xmlSchemaIDCNewBinding(matcher->aidc->def); + if (ielem->idcTable == NULL) + return (NULL); + return(ielem->idcTable); + } else { + xmlSchemaPSVIIDCBindingPtr bind = NULL; + + bind = ielem->idcTable; + do { + if (bind->definition == matcher->aidc->def) + return(bind); + if (bind->next == NULL) { + bind->next = xmlSchemaIDCNewBinding(matcher->aidc->def); + if (bind->next == NULL) + return (NULL); + return(bind->next); + } + bind = bind->next; + } while (bind != NULL); + } + return (NULL); +} + +static xmlSchemaItemListPtr +xmlSchemaIDCAcquireTargetList(xmlSchemaValidCtxtPtr vctxt ATTRIBUTE_UNUSED, + xmlSchemaIDCMatcherPtr matcher) +{ + if (matcher->targets == NULL) + matcher->targets = xmlSchemaItemListCreate(); + return(matcher->targets); +} + +/** + * xmlSchemaIDCFreeKey: + * @key: the IDC key + * + * Frees an IDC key together with its compiled value. + */ +static void +xmlSchemaIDCFreeKey(xmlSchemaPSVIIDCKeyPtr key) +{ + if (key->val != NULL) + xmlSchemaFreeValue(key->val); + xmlFree(key); +} + +/** + * xmlSchemaIDCFreeBinding: + * + * Frees an IDC binding. Note that the node table-items + * are not freed. + */ +static void +xmlSchemaIDCFreeBinding(xmlSchemaPSVIIDCBindingPtr bind) +{ + if (bind->nodeTable != NULL) + xmlFree(bind->nodeTable); + if (bind->dupls != NULL) + xmlSchemaItemListFree(bind->dupls); + xmlFree(bind); +} + +/** + * xmlSchemaIDCFreeIDCTable: + * @bind: the first IDC binding in the list + * + * Frees an IDC table, i.e. all the IDC bindings in the list. + */ +static void +xmlSchemaIDCFreeIDCTable(xmlSchemaPSVIIDCBindingPtr bind) +{ + xmlSchemaPSVIIDCBindingPtr prev; + + while (bind != NULL) { + prev = bind; + bind = bind->next; + xmlSchemaIDCFreeBinding(prev); + } +} + +/** + * xmlSchemaIDCFreeMatcherList: + * @matcher: the first IDC matcher in the list + * + * Frees a list of IDC matchers. + */ +static void +xmlSchemaIDCFreeMatcherList(xmlSchemaIDCMatcherPtr matcher) +{ + xmlSchemaIDCMatcherPtr next; + + while (matcher != NULL) { + next = matcher->next; + if (matcher->keySeqs != NULL) { + int i; + for (i = 0; i < matcher->sizeKeySeqs; i++) + if (matcher->keySeqs[i] != NULL) + xmlFree(matcher->keySeqs[i]); + xmlFree(matcher->keySeqs); + } + if (matcher->targets != NULL) { + if (matcher->idcType == XML_SCHEMA_TYPE_IDC_KEYREF) { + int i; + xmlSchemaPSVIIDCNodePtr idcNode; + /* + * Node-table items for keyrefs are not stored globally + * to the validation context, since they are not bubbled. + * We need to free them here. + */ + for (i = 0; i < matcher->targets->nbItems; i++) { + idcNode = + (xmlSchemaPSVIIDCNodePtr) matcher->targets->items[i]; + xmlFree(idcNode->keys); + xmlFree(idcNode); + } + } + xmlSchemaItemListFree(matcher->targets); + } + xmlFree(matcher); + matcher = next; + } +} + +/** + * xmlSchemaIDCAddStateObject: + * @vctxt: the WXS validation context + * @matcher: the IDC matcher + * @sel: the XPath information + * @parent: the parent "selector" state object if any + * @type: "selector" or "field" + * + * Creates/reuses and activates state objects for the given + * XPath information; if the XPath expression consists of unions, + * multiple state objects are created for every unioned expression. + * + * Returns 0 on success and -1 on internal errors. + */ +static int +xmlSchemaIDCAddStateObject(xmlSchemaValidCtxtPtr vctxt, + xmlSchemaIDCMatcherPtr matcher, + xmlSchemaIDCSelectPtr sel, + int type) +{ + xmlSchemaIDCStateObjPtr sto; + + /* + * Reuse the state objects from the pool. + */ + if (vctxt->xpathStatePool != NULL) { + sto = vctxt->xpathStatePool; + vctxt->xpathStatePool = sto->next; + sto->next = NULL; + } else { + /* + * Create a new state object. + */ + sto = (xmlSchemaIDCStateObjPtr) xmlMalloc(sizeof(xmlSchemaIDCStateObj)); + if (sto == NULL) { + xmlSchemaVErrMemory(NULL, + "allocating an IDC state object", NULL); + return (-1); + } + memset(sto, 0, sizeof(xmlSchemaIDCStateObj)); + } + /* + * Add to global list. + */ + if (vctxt->xpathStates != NULL) + sto->next = vctxt->xpathStates; + vctxt->xpathStates = sto; + + /* + * Free the old xpath validation context. + */ + if (sto->xpathCtxt != NULL) + xmlFreeStreamCtxt((xmlStreamCtxtPtr) sto->xpathCtxt); + + /* + * Create a new XPath (pattern) validation context. + */ + sto->xpathCtxt = (void *) xmlPatternGetStreamCtxt( + (xmlPatternPtr) sel->xpathComp); + if (sto->xpathCtxt == NULL) { + VERROR_INT("xmlSchemaIDCAddStateObject", + "failed to create an XPath validation context"); + return (-1); + } + sto->type = type; + sto->depth = vctxt->depth; + sto->matcher = matcher; + sto->sel = sel; + sto->nbHistory = 0; + +#ifdef DEBUG_IDC + xmlGenericError(xmlGenericErrorContext, "IDC: STO push '%s'\n", + sto->sel->xpath); +#endif + return (0); +} + +/** + * xmlSchemaXPathEvaluate: + * @vctxt: the WXS validation context + * @nodeType: the nodeType of the current node + * + * Evaluates all active XPath state objects. + * + * Returns the number of IC "field" state objects which resolved to + * this node, 0 if none resolved and -1 on internal errors. + */ +static int +xmlSchemaXPathEvaluate(xmlSchemaValidCtxtPtr vctxt, + xmlElementType nodeType) +{ + xmlSchemaIDCStateObjPtr sto, head = NULL, first; + int res, resolved = 0, depth = vctxt->depth; + + if (vctxt->xpathStates == NULL) + return (0); + + if (nodeType == XML_ATTRIBUTE_NODE) + depth++; +#ifdef DEBUG_IDC + { + xmlChar *str = NULL; + xmlGenericError(xmlGenericErrorContext, + "IDC: EVAL on %s, depth %d, type %d\n", + xmlSchemaFormatQName(&str, vctxt->inode->nsName, + vctxt->inode->localName), depth, nodeType); + FREE_AND_NULL(str) + } +#endif + /* + * Process all active XPath state objects. + */ + first = vctxt->xpathStates; + sto = first; + while (sto != head) { +#ifdef DEBUG_IDC + if (sto->type == XPATH_STATE_OBJ_TYPE_IDC_SELECTOR) + xmlGenericError(xmlGenericErrorContext, "IDC: ['%s'] selector '%s'\n", + sto->matcher->aidc->def->name, sto->sel->xpath); + else + xmlGenericError(xmlGenericErrorContext, "IDC: ['%s'] field '%s'\n", + sto->matcher->aidc->def->name, sto->sel->xpath); +#endif + if (nodeType == XML_ELEMENT_NODE) + res = xmlStreamPush((xmlStreamCtxtPtr) sto->xpathCtxt, + vctxt->inode->localName, vctxt->inode->nsName); + else + res = xmlStreamPushAttr((xmlStreamCtxtPtr) sto->xpathCtxt, + vctxt->inode->localName, vctxt->inode->nsName); + + if (res == -1) { + VERROR_INT("xmlSchemaXPathEvaluate", + "calling xmlStreamPush()"); + return (-1); + } + if (res == 0) + goto next_sto; + /* + * Full match. + */ +#ifdef DEBUG_IDC + xmlGenericError(xmlGenericErrorContext, "IDC: " + "MATCH\n"); +#endif + /* + * Register a match in the state object history. + */ + if (sto->history == NULL) { + sto->history = (int *) xmlMalloc(5 * sizeof(int)); + if (sto->history == NULL) { + xmlSchemaVErrMemory(NULL, + "allocating the state object history", NULL); + return(-1); + } + sto->sizeHistory = 10; + } else if (sto->sizeHistory <= sto->nbHistory) { + sto->sizeHistory *= 2; + sto->history = (int *) xmlRealloc(sto->history, + sto->sizeHistory * sizeof(int)); + if (sto->history == NULL) { + xmlSchemaVErrMemory(NULL, + "re-allocating the state object history", NULL); + return(-1); + } + } + sto->history[sto->nbHistory++] = depth; + +#ifdef DEBUG_IDC + xmlGenericError(xmlGenericErrorContext, "IDC: push match '%d'\n", + vctxt->depth); +#endif + + if (sto->type == XPATH_STATE_OBJ_TYPE_IDC_SELECTOR) { + xmlSchemaIDCSelectPtr sel; + /* + * Activate state objects for the IDC fields of + * the IDC selector. + */ +#ifdef DEBUG_IDC + xmlGenericError(xmlGenericErrorContext, "IDC: " + "activating field states\n"); +#endif + sel = sto->matcher->aidc->def->fields; + while (sel != NULL) { + if (xmlSchemaIDCAddStateObject(vctxt, sto->matcher, + sel, XPATH_STATE_OBJ_TYPE_IDC_FIELD) == -1) + return (-1); + sel = sel->next; + } + } else if (sto->type == XPATH_STATE_OBJ_TYPE_IDC_FIELD) { + /* + * An IDC key node was found by the IDC field. + */ +#ifdef DEBUG_IDC + xmlGenericError(xmlGenericErrorContext, + "IDC: key found\n"); +#endif + /* + * Notify that the character value of this node is + * needed. + */ + if (resolved == 0) { + if ((vctxt->inode->flags & + XML_SCHEMA_NODE_INFO_VALUE_NEEDED) == 0) + vctxt->inode->flags |= XML_SCHEMA_NODE_INFO_VALUE_NEEDED; + } + resolved++; + } +next_sto: + if (sto->next == NULL) { + /* + * Evaluate field state objects created on this node as well. + */ + head = first; + sto = vctxt->xpathStates; + } else + sto = sto->next; + } + return (resolved); +} + +static const xmlChar * +xmlSchemaFormatIDCKeySequence(xmlSchemaValidCtxtPtr vctxt, + xmlChar **buf, + xmlSchemaPSVIIDCKeyPtr *seq, + int count) +{ + int i, res; + xmlChar *value = NULL; + + *buf = xmlStrdup(BAD_CAST "["); + for (i = 0; i < count; i++) { + *buf = xmlStrcat(*buf, BAD_CAST "'"); + res = xmlSchemaGetCanonValueWhtspExt(seq[i]->val, + xmlSchemaGetWhiteSpaceFacetValue(seq[i]->type), + &value); + if (res == 0) + *buf = xmlStrcat(*buf, BAD_CAST value); + else { + VERROR_INT("xmlSchemaFormatIDCKeySequence", + "failed to compute a canonical value"); + *buf = xmlStrcat(*buf, BAD_CAST "???"); + } + if (i < count -1) + *buf = xmlStrcat(*buf, BAD_CAST "', "); + else + *buf = xmlStrcat(*buf, BAD_CAST "'"); + if (value != NULL) { + xmlFree(value); + value = NULL; + } + } + *buf = xmlStrcat(*buf, BAD_CAST "]"); + + return (BAD_CAST *buf); +} + +/** + * xmlSchemaXPathPop: + * @vctxt: the WXS validation context + * + * Pops all XPath states. + * + * Returns 0 on success and -1 on internal errors. + */ +static int +xmlSchemaXPathPop(xmlSchemaValidCtxtPtr vctxt) +{ + xmlSchemaIDCStateObjPtr sto; + int res; + + if (vctxt->xpathStates == NULL) + return(0); + sto = vctxt->xpathStates; + do { + res = xmlStreamPop((xmlStreamCtxtPtr) sto->xpathCtxt); + if (res == -1) + return (-1); + sto = sto->next; + } while (sto != NULL); + return(0); +} + +/** + * xmlSchemaXPathProcessHistory: + * @vctxt: the WXS validation context + * @type: the simple/complex type of the current node if any at all + * @val: the precompiled value + * + * Processes and pops the history items of the IDC state objects. + * IDC key-sequences are validated/created on IDC bindings. + * + * Returns 0 on success and -1 on internal errors. + */ +static int +xmlSchemaXPathProcessHistory(xmlSchemaValidCtxtPtr vctxt, + int depth) +{ + xmlSchemaIDCStateObjPtr sto, nextsto; + int res, matchDepth; + xmlSchemaPSVIIDCKeyPtr key = NULL; + xmlSchemaTypePtr type = vctxt->inode->typeDef, simpleType = NULL; + + if (vctxt->xpathStates == NULL) + return (0); + sto = vctxt->xpathStates; + +#ifdef DEBUG_IDC + { + xmlChar *str = NULL; + xmlGenericError(xmlGenericErrorContext, + "IDC: BACK on %s, depth %d\n", + xmlSchemaFormatQName(&str, vctxt->inode->nsName, + vctxt->inode->localName), vctxt->depth); + FREE_AND_NULL(str) + } +#endif + /* + * Evaluate the state objects. + */ + while (sto != NULL) { + res = xmlStreamPop((xmlStreamCtxtPtr) sto->xpathCtxt); + if (res == -1) { + VERROR_INT("xmlSchemaXPathProcessHistory", + "calling xmlStreamPop()"); + return (-1); + } +#ifdef DEBUG_IDC + xmlGenericError(xmlGenericErrorContext, "IDC: stream pop '%s'\n", + sto->sel->xpath); +#endif + if (sto->nbHistory == 0) + goto deregister_check; + + matchDepth = sto->history[sto->nbHistory -1]; + + /* + * Only matches at the current depth are of interest. + */ + if (matchDepth != depth) { + sto = sto->next; + continue; + } + if (sto->type == XPATH_STATE_OBJ_TYPE_IDC_FIELD) { + /* + * NOTE: According to + * http://www.w3.org/Bugs/Public/show_bug.cgi?id=2198 + * ... the simple-content of complex types is also allowed. + */ + + if (WXS_IS_COMPLEX(type)) { + if (WXS_HAS_SIMPLE_CONTENT(type)) { + /* + * Sanity check for complex types with simple content. + */ + simpleType = type->contentTypeDef; + if (simpleType == NULL) { + VERROR_INT("xmlSchemaXPathProcessHistory", + "field resolves to a CT with simple content " + "but the CT is missing the ST definition"); + return (-1); + } + } else + simpleType = NULL; + } else + simpleType = type; + if (simpleType == NULL) { + xmlChar *str = NULL; + + /* + * Not qualified if the field resolves to a node of non + * simple type. + */ + xmlSchemaCustomErr(ACTXT_CAST vctxt, + XML_SCHEMAV_CVC_IDC, NULL, + WXS_BASIC_CAST sto->matcher->aidc->def, + "The XPath '%s' of a field of %s does evaluate to a node of " + "non-simple type", + sto->sel->xpath, + xmlSchemaGetIDCDesignation(&str, sto->matcher->aidc->def)); + FREE_AND_NULL(str); + sto->nbHistory--; + goto deregister_check; + } + + if ((key == NULL) && (vctxt->inode->val == NULL)) { + /* + * Failed to provide the normalized value; maybe + * the value was invalid. + */ + VERROR(XML_SCHEMAV_CVC_IDC, + WXS_BASIC_CAST sto->matcher->aidc->def, + "Warning: No precomputed value available, the value " + "was either invalid or something strange happend"); + sto->nbHistory--; + goto deregister_check; + } else { + xmlSchemaIDCMatcherPtr matcher = sto->matcher; + xmlSchemaPSVIIDCKeyPtr *keySeq; + int pos, idx; + + /* + * The key will be anchored on the matcher's list of + * key-sequences. The position in this list is determined + * by the target node's depth relative to the matcher's + * depth of creation (i.e. the depth of the scope element). + * + * Element Depth Pos List-entries + * 0 NULL + * 1 NULL + * 2 2 target + * + * + * + * The size of the list is only dependant on the depth of + * the tree. + * An entry will be NULLed in selector_leave, i.e. when + * we hit the target's + */ + pos = sto->depth - matcher->depth; + idx = sto->sel->index; + + /* + * Create/grow the array of key-sequences. + */ + if (matcher->keySeqs == NULL) { + if (pos > 9) + matcher->sizeKeySeqs = pos * 2; + else + matcher->sizeKeySeqs = 10; + matcher->keySeqs = (xmlSchemaPSVIIDCKeyPtr **) + xmlMalloc(matcher->sizeKeySeqs * + sizeof(xmlSchemaPSVIIDCKeyPtr *)); + if (matcher->keySeqs == NULL) { + xmlSchemaVErrMemory(NULL, + "allocating an array of key-sequences", + NULL); + return(-1); + } + memset(matcher->keySeqs, 0, + matcher->sizeKeySeqs * + sizeof(xmlSchemaPSVIIDCKeyPtr *)); + } else if (pos >= matcher->sizeKeySeqs) { + int i = matcher->sizeKeySeqs; + + matcher->sizeKeySeqs *= 2; + matcher->keySeqs = (xmlSchemaPSVIIDCKeyPtr **) + xmlRealloc(matcher->keySeqs, + matcher->sizeKeySeqs * + sizeof(xmlSchemaPSVIIDCKeyPtr *)); + if (matcher->keySeqs == NULL) { + xmlSchemaVErrMemory(NULL, + "reallocating an array of key-sequences", + NULL); + return (-1); + } + /* + * The array needs to be NULLed. + * TODO: Use memset? + */ + for (; i < matcher->sizeKeySeqs; i++) + matcher->keySeqs[i] = NULL; + } + + /* + * Get/create the key-sequence. + */ + keySeq = matcher->keySeqs[pos]; + if (keySeq == NULL) { + goto create_sequence; + } else if (keySeq[idx] != NULL) { + xmlChar *str = NULL; + /* + * cvc-identity-constraint: + * 3 For each node in the ·target node set· all + * of the {fields}, with that node as the context + * node, evaluate to either an empty node-set or + * a node-set with exactly one member, which must + * have a simple type. + * + * The key was already set; report an error. + */ + xmlSchemaCustomErr(ACTXT_CAST vctxt, + XML_SCHEMAV_CVC_IDC, NULL, + WXS_BASIC_CAST matcher->aidc->def, + "The XPath '%s' of a field of %s evaluates to a " + "node-set with more than one member", + sto->sel->xpath, + xmlSchemaGetIDCDesignation(&str, matcher->aidc->def)); + FREE_AND_NULL(str); + sto->nbHistory--; + goto deregister_check; + } else + goto create_key; + +create_sequence: + /* + * Create a key-sequence. + */ + keySeq = (xmlSchemaPSVIIDCKeyPtr *) xmlMalloc( + matcher->aidc->def->nbFields * + sizeof(xmlSchemaPSVIIDCKeyPtr)); + if (keySeq == NULL) { + xmlSchemaVErrMemory(NULL, + "allocating an IDC key-sequence", NULL); + return(-1); + } + memset(keySeq, 0, matcher->aidc->def->nbFields * + sizeof(xmlSchemaPSVIIDCKeyPtr)); + matcher->keySeqs[pos] = keySeq; +create_key: + /* + * Create a key once per node only. + */ + if (key == NULL) { + key = (xmlSchemaPSVIIDCKeyPtr) xmlMalloc( + sizeof(xmlSchemaPSVIIDCKey)); + if (key == NULL) { + xmlSchemaVErrMemory(NULL, + "allocating a IDC key", NULL); + xmlFree(keySeq); + matcher->keySeqs[pos] = NULL; + return(-1); + } + /* + * Consume the compiled value. + */ + key->type = simpleType; + key->val = vctxt->inode->val; + vctxt->inode->val = NULL; + /* + * Store the key in a global list. + */ + if (xmlSchemaIDCStoreKey(vctxt, key) == -1) { + xmlSchemaIDCFreeKey(key); + return (-1); + } + } + keySeq[idx] = key; + } + } else if (sto->type == XPATH_STATE_OBJ_TYPE_IDC_SELECTOR) { + + xmlSchemaPSVIIDCKeyPtr **keySeq = NULL; + /* xmlSchemaPSVIIDCBindingPtr bind; */ + xmlSchemaPSVIIDCNodePtr ntItem; + xmlSchemaIDCMatcherPtr matcher; + xmlSchemaIDCPtr idc; + xmlSchemaItemListPtr targets; + int pos, i, j, nbKeys; + /* + * Here we have the following scenario: + * An IDC 'selector' state object resolved to a target node, + * during the time this target node was in the + * ancestor-or-self axis, the 'field' state object(s) looked + * out for matching nodes to create a key-sequence for this + * target node. Now we are back to this target node and need + * to put the key-sequence, together with the target node + * itself, into the node-table of the corresponding IDC + * binding. + */ + matcher = sto->matcher; + idc = matcher->aidc->def; + nbKeys = idc->nbFields; + pos = depth - matcher->depth; + /* + * Check if the matcher has any key-sequences at all, plus + * if it has a key-sequence for the current target node. + */ + if ((matcher->keySeqs == NULL) || + (matcher->sizeKeySeqs <= pos)) { + if (idc->type == XML_SCHEMA_TYPE_IDC_KEY) + goto selector_key_error; + else + goto selector_leave; + } + + keySeq = &(matcher->keySeqs[pos]); + if (*keySeq == NULL) { + if (idc->type == XML_SCHEMA_TYPE_IDC_KEY) + goto selector_key_error; + else + goto selector_leave; + } + + for (i = 0; i < nbKeys; i++) { + if ((*keySeq)[i] == NULL) { + /* + * Not qualified, if not all fields did resolve. + */ + if (idc->type == XML_SCHEMA_TYPE_IDC_KEY) { + /* + * All fields of a "key" IDC must resolve. + */ + goto selector_key_error; + } + goto selector_leave; + } + } + /* + * All fields did resolve. + */ + + /* + * 4.1 If the {identity-constraint category} is unique(/key), + * then no two members of the ·qualified node set· have + * ·key-sequences· whose members are pairwise equal, as + * defined by Equal in [XML Schemas: Datatypes]. + * + * Get the IDC binding from the matcher and check for + * duplicate key-sequences. + */ +#if 0 + bind = xmlSchemaIDCAcquireBinding(vctxt, matcher); +#endif + targets = xmlSchemaIDCAcquireTargetList(vctxt, matcher); + if ((idc->type != XML_SCHEMA_TYPE_IDC_KEYREF) && + (targets->nbItems != 0)) { + xmlSchemaPSVIIDCKeyPtr ckey, bkey, *bkeySeq; + + i = 0; + res = 0; + /* + * Compare the key-sequences, key by key. + */ + do { + bkeySeq = + ((xmlSchemaPSVIIDCNodePtr) targets->items[i])->keys; + for (j = 0; j < nbKeys; j++) { + ckey = (*keySeq)[j]; + bkey = bkeySeq[j]; + res = xmlSchemaAreValuesEqual(ckey->val, bkey->val); + if (res == -1) { + return (-1); + } else if (res == 0) { + /* + * One of the keys differs, so the key-sequence + * won't be equal; get out. + */ + break; + } + } + if (res == 1) { + /* + * Duplicate key-sequence found. + */ + break; + } + i++; + } while (i < targets->nbItems); + if (i != targets->nbItems) { + xmlChar *str = NULL, *strB = NULL; + /* + * TODO: Try to report the key-sequence. + */ + xmlSchemaCustomErr(ACTXT_CAST vctxt, + XML_SCHEMAV_CVC_IDC, NULL, + WXS_BASIC_CAST idc, + "Duplicate key-sequence %s in %s", + xmlSchemaFormatIDCKeySequence(vctxt, &str, + (*keySeq), nbKeys), + xmlSchemaGetIDCDesignation(&strB, idc)); + FREE_AND_NULL(str); + FREE_AND_NULL(strB); + goto selector_leave; + } + } + /* + * Add a node-table item to the IDC binding. + */ + ntItem = (xmlSchemaPSVIIDCNodePtr) xmlMalloc( + sizeof(xmlSchemaPSVIIDCNode)); + if (ntItem == NULL) { + xmlSchemaVErrMemory(NULL, + "allocating an IDC node-table item", NULL); + xmlFree(*keySeq); + *keySeq = NULL; + return(-1); + } + memset(ntItem, 0, sizeof(xmlSchemaPSVIIDCNode)); + + /* + * Store the node-table item in a global list. + */ + if (idc->type != XML_SCHEMA_TYPE_IDC_KEYREF) { + if (xmlSchemaIDCStoreNodeTableItem(vctxt, ntItem) == -1) { + xmlFree(ntItem); + xmlFree(*keySeq); + *keySeq = NULL; + return (-1); + } + ntItem->nodeQNameID = -1; + } else { + /* + * Save a cached QName for this node on the IDC node, to be + * able to report it, even if the node is not saved. + */ + ntItem->nodeQNameID = xmlSchemaVAddNodeQName(vctxt, + vctxt->inode->localName, vctxt->inode->nsName); + if (ntItem->nodeQNameID == -1) { + xmlFree(ntItem); + xmlFree(*keySeq); + *keySeq = NULL; + return (-1); + } + } + /* + * Init the node-table item: Save the node, position and + * consume the key-sequence. + */ + ntItem->node = vctxt->node; + ntItem->nodeLine = vctxt->inode->nodeLine; + ntItem->keys = *keySeq; + *keySeq = NULL; +#if 0 + if (xmlSchemaIDCAppendNodeTableItem(bind, ntItem) == -1) { +#endif + if (xmlSchemaItemListAdd(targets, ntItem) == -1) { + if (idc->type == XML_SCHEMA_TYPE_IDC_KEYREF) { + /* + * Free the item, since keyref items won't be + * put on a global list. + */ + xmlFree(ntItem->keys); + xmlFree(ntItem); + } + return (-1); + } + + goto selector_leave; +selector_key_error: + { + xmlChar *str = NULL; + /* + * 4.2.1 (KEY) The ·target node set· and the + * ·qualified node set· are equal, that is, every + * member of the ·target node set· is also a member + * of the ·qualified node set· and vice versa. + */ + xmlSchemaCustomErr(ACTXT_CAST vctxt, + XML_SCHEMAV_CVC_IDC, NULL, + WXS_BASIC_CAST idc, + "Not all fields of %s evaluate to a node", + xmlSchemaGetIDCDesignation(&str, idc), NULL); + FREE_AND_NULL(str); + } +selector_leave: + /* + * Free the key-sequence if not added to the IDC table. + */ + if ((keySeq != NULL) && (*keySeq != NULL)) { + xmlFree(*keySeq); + *keySeq = NULL; + } + } /* if selector */ + + sto->nbHistory--; + +deregister_check: + /* + * Deregister state objects if they reach the depth of creation. + */ + if ((sto->nbHistory == 0) && (sto->depth == depth)) { +#ifdef DEBUG_IDC + xmlGenericError(xmlGenericErrorContext, "IDC: STO pop '%s'\n", + sto->sel->xpath); +#endif + if (vctxt->xpathStates != sto) { + VERROR_INT("xmlSchemaXPathProcessHistory", + "The state object to be removed is not the first " + "in the list"); + } + nextsto = sto->next; + /* + * Unlink from the list of active XPath state objects. + */ + vctxt->xpathStates = sto->next; + sto->next = vctxt->xpathStatePool; + /* + * Link it to the pool of reusable state objects. + */ + vctxt->xpathStatePool = sto; + sto = nextsto; + } else + sto = sto->next; + } /* while (sto != NULL) */ + return (0); +} + +/** + * xmlSchemaIDCRegisterMatchers: + * @vctxt: the WXS validation context + * @elemDecl: the element declaration + * + * Creates helper objects to evaluate IDC selectors/fields + * successively. + * + * Returns 0 if OK and -1 on internal errors. + */ +static int +xmlSchemaIDCRegisterMatchers(xmlSchemaValidCtxtPtr vctxt, + xmlSchemaElementPtr elemDecl) +{ + xmlSchemaIDCMatcherPtr matcher, last = NULL; + xmlSchemaIDCPtr idc, refIdc; + xmlSchemaIDCAugPtr aidc; + + idc = (xmlSchemaIDCPtr) elemDecl->idcs; + if (idc == NULL) + return (0); + +#ifdef DEBUG_IDC + { + xmlChar *str = NULL; + xmlGenericError(xmlGenericErrorContext, + "IDC: REGISTER on %s, depth %d\n", + (char *) xmlSchemaFormatQName(&str, vctxt->inode->nsName, + vctxt->inode->localName), vctxt->depth); + FREE_AND_NULL(str) + } +#endif + if (vctxt->inode->idcMatchers != NULL) { + VERROR_INT("xmlSchemaIDCRegisterMatchers", + "The chain of IDC matchers is expected to be empty"); + return (-1); + } + do { + if (idc->type == XML_SCHEMA_TYPE_IDC_KEYREF) { + /* + * Since IDCs bubbles are expensive we need to know the + * depth at which the bubbles should stop; this will be + * the depth of the top-most keyref IDC. If no keyref + * references a key/unique IDC, the keyrefDepth will + * be -1, indicating that no bubbles are needed. + */ + refIdc = (xmlSchemaIDCPtr) idc->ref->item; + if (refIdc != NULL) { + /* + * Remember that we have keyrefs on this node. + */ + vctxt->inode->hasKeyrefs = 1; + /* + * Lookup the referenced augmented IDC info. + */ + aidc = vctxt->aidcs; + while (aidc != NULL) { + if (aidc->def == refIdc) + break; + aidc = aidc->next; + } + if (aidc == NULL) { + VERROR_INT("xmlSchemaIDCRegisterMatchers", + "Could not find an augmented IDC item for an IDC " + "definition"); + return (-1); + } + if ((aidc->keyrefDepth == -1) || + (vctxt->depth < aidc->keyrefDepth)) + aidc->keyrefDepth = vctxt->depth; + } + } + /* + * Lookup the augmented IDC item for the IDC definition. + */ + aidc = vctxt->aidcs; + while (aidc != NULL) { + if (aidc->def == idc) + break; + aidc = aidc->next; + } + if (aidc == NULL) { + VERROR_INT("xmlSchemaIDCRegisterMatchers", + "Could not find an augmented IDC item for an IDC definition"); + return (-1); + } + /* + * Create an IDC matcher for every IDC definition. + */ + matcher = (xmlSchemaIDCMatcherPtr) + xmlMalloc(sizeof(xmlSchemaIDCMatcher)); + if (matcher == NULL) { + xmlSchemaVErrMemory(vctxt, + "allocating an IDC matcher", NULL); + return (-1); + } + memset(matcher, 0, sizeof(xmlSchemaIDCMatcher)); + if (last == NULL) + vctxt->inode->idcMatchers = matcher; + else + last->next = matcher; + last = matcher; + + matcher->type = IDC_MATCHER; + matcher->depth = vctxt->depth; + matcher->aidc = aidc; + matcher->idcType = aidc->def->type; +#ifdef DEBUG_IDC + xmlGenericError(xmlGenericErrorContext, "IDC: register matcher\n"); +#endif + /* + * Init the automaton state object. + */ + if (xmlSchemaIDCAddStateObject(vctxt, matcher, + idc->selector, XPATH_STATE_OBJ_TYPE_IDC_SELECTOR) == -1) + return (-1); + + idc = idc->next; + } while (idc != NULL); + return (0); +} + +static int +xmlSchemaIDCFillNodeTables(xmlSchemaValidCtxtPtr vctxt, + xmlSchemaNodeInfoPtr ielem) +{ + xmlSchemaPSVIIDCBindingPtr bind; + int res, i, j, k, nbTargets, nbFields, nbDupls, nbNodeTable; + xmlSchemaPSVIIDCKeyPtr *keys, *ntkeys; + xmlSchemaPSVIIDCNodePtr *targets, *dupls; + + xmlSchemaIDCMatcherPtr matcher = ielem->idcMatchers; + /* vctxt->createIDCNodeTables */ + while (matcher != NULL) { + /* + * Skip keyref IDCs and empty IDC target-lists. + */ + if ((matcher->aidc->def->type == XML_SCHEMA_TYPE_IDC_KEYREF) || + WXS_ILIST_IS_EMPTY(matcher->targets)) + { + matcher = matcher->next; + continue; + } + /* + * If we _want_ the IDC node-table to be created in any case + * then do so. Otherwise create them only if keyrefs need them. + */ + if ((! vctxt->createIDCNodeTables) && + ((matcher->aidc->keyrefDepth == -1) || + (matcher->aidc->keyrefDepth > vctxt->depth))) + { + matcher = matcher->next; + continue; + } + /* + * Get/create the IDC binding on this element for the IDC definition. + */ + bind = xmlSchemaIDCAcquireBinding(vctxt, matcher); + + if (! WXS_ILIST_IS_EMPTY(bind->dupls)) { + dupls = (xmlSchemaPSVIIDCNodePtr *) bind->dupls->items; + nbDupls = bind->dupls->nbItems; + } else { + dupls = NULL; + nbDupls = 0; + } + if (bind->nodeTable != NULL) { + nbNodeTable = bind->nbNodes; + } else { + nbNodeTable = 0; + } + + if ((nbNodeTable == 0) && (nbDupls == 0)) { + /* + * Transfer all IDC target-nodes to the IDC node-table. + */ + bind->nodeTable = + (xmlSchemaPSVIIDCNodePtr *) matcher->targets->items; + bind->sizeNodes = matcher->targets->sizeItems; + bind->nbNodes = matcher->targets->nbItems; + + matcher->targets->items = NULL; + matcher->targets->sizeItems = 0; + matcher->targets->nbItems = 0; + } else { + /* + * Compare the key-sequences and add to the IDC node-table. + */ + nbTargets = matcher->targets->nbItems; + targets = (xmlSchemaPSVIIDCNodePtr *) matcher->targets->items; + nbFields = matcher->aidc->def->nbFields; + i = 0; + do { + keys = targets[i]->keys; + if (nbDupls) { + /* + * Search in already found duplicates first. + */ + j = 0; + do { + if (nbFields == 1) { + res = xmlSchemaAreValuesEqual(keys[0]->val, + dupls[j]->keys[0]->val); + if (res == -1) + goto internal_error; + if (res == 1) { + /* + * Equal key-sequence. + */ + goto next_target; + } + } else { + res = 0; + ntkeys = dupls[j]->keys; + for (k = 0; k < nbFields; k++) { + res = xmlSchemaAreValuesEqual(keys[k]->val, + ntkeys[k]->val); + if (res == -1) + goto internal_error; + if (res == 0) { + /* + * One of the keys differs. + */ + break; + } + } + if (res == 1) { + /* + * Equal key-sequence found. + */ + goto next_target; + } + } + j++; + } while (j < nbDupls); + } + if (nbNodeTable) { + j = 0; + do { + if (nbFields == 1) { + res = xmlSchemaAreValuesEqual(keys[0]->val, + bind->nodeTable[j]->keys[0]->val); + if (res == -1) + goto internal_error; + if (res == 0) { + /* + * The key-sequence differs. + */ + goto next_node_table_entry; + } + } else { + res = 0; + ntkeys = bind->nodeTable[j]->keys; + for (k = 0; k < nbFields; k++) { + res = xmlSchemaAreValuesEqual(keys[k]->val, + ntkeys[k]->val); + if (res == -1) + goto internal_error; + if (res == 0) { + /* + * One of the keys differs. + */ + goto next_node_table_entry; + } + } + } + /* + * Add the duplicate to the list of duplicates. + */ + if (bind->dupls == NULL) { + bind->dupls = xmlSchemaItemListCreate(); + if (bind->dupls == NULL) + goto internal_error; + } + if (xmlSchemaItemListAdd(bind->dupls, bind->nodeTable[j]) == -1) + goto internal_error; + /* + * Remove the duplicate entry from the IDC node-table. + */ + bind->nodeTable[j] = bind->nodeTable[bind->nbNodes -1]; + bind->nbNodes--; + + goto next_target; + +next_node_table_entry: + j++; + } while (j < nbNodeTable); + } + /* + * If everything is fine, then add the IDC target-node to + * the IDC node-table. + */ + if (xmlSchemaIDCAppendNodeTableItem(bind, targets[i]) == -1) + goto internal_error; + +next_target: + i++; + } while (i < nbTargets); + } + matcher = matcher->next; + } + return(0); + +internal_error: + return(-1); +} + +/** + * xmlSchemaBubbleIDCNodeTables: + * @depth: the current tree depth + * + * Merges IDC bindings of an element at @depth into the corresponding IDC + * bindings of its parent element. If a duplicate note-table entry is found, + * both, the parent node-table entry and child entry are discarded from the + * node-table of the parent. + * + * Returns 0 if OK and -1 on internal errors. + */ +static int +xmlSchemaBubbleIDCNodeTables(xmlSchemaValidCtxtPtr vctxt) +{ + xmlSchemaPSVIIDCBindingPtr bind; /* IDC bindings of the current node. */ + xmlSchemaPSVIIDCBindingPtr *parTable, parBind = NULL; /* parent IDC bindings. */ + xmlSchemaPSVIIDCNodePtr node, parNode = NULL, *dupls, *parNodes; /* node-table entries. */ + xmlSchemaIDCAugPtr aidc; + int i, j, k, ret = 0, nbFields, oldNum, oldDupls; + + bind = vctxt->inode->idcTable; + if (bind == NULL) { + /* Fine, no table, no bubbles. */ + return (0); + } + + parTable = &(vctxt->elemInfos[vctxt->depth -1]->idcTable); + /* + * Walk all bindings; create new or add to existing bindings. + * Remove duplicate key-sequences. + */ + while (bind != NULL) { + + if ((bind->nbNodes == 0) && WXS_ILIST_IS_EMPTY(bind->dupls)) + goto next_binding; + /* + * Check if the key/unique IDC table needs to be bubbled. + */ + if (! vctxt->createIDCNodeTables) { + aidc = vctxt->aidcs; + do { + if (aidc->def == bind->definition) { + if ((aidc->keyrefDepth == -1) || + (aidc->keyrefDepth >= vctxt->depth)) { + goto next_binding; + } + break; + } + aidc = aidc->next; + } while (aidc != NULL); + } + + if (parTable != NULL) + parBind = *parTable; + /* + * Search a matching parent binding for the + * IDC definition. + */ + while (parBind != NULL) { + if (parBind->definition == bind->definition) + break; + parBind = parBind->next; + } + + if (parBind != NULL) { + /* + * Compare every node-table entry of the child node, + * i.e. the key-sequence within, ... + */ + oldNum = parBind->nbNodes; /* Skip newly added items. */ + + if (! WXS_ILIST_IS_EMPTY(parBind->dupls)) { + oldDupls = parBind->dupls->nbItems; + dupls = (xmlSchemaPSVIIDCNodePtr *) parBind->dupls->items; + } else { + dupls = NULL; + oldDupls = 0; + } + + parNodes = parBind->nodeTable; + nbFields = bind->definition->nbFields; + + for (i = 0; i < bind->nbNodes; i++) { + node = bind->nodeTable[i]; + if (node == NULL) + continue; + /* + * ...with every key-sequence of the parent node, already + * evaluated to be a duplicate key-sequence. + */ + if (oldDupls) { + j = 0; + while (j < oldDupls) { + if (nbFields == 1) { + ret = xmlSchemaAreValuesEqual( + node->keys[0]->val, + dupls[j]->keys[0]->val); + if (ret == -1) + goto internal_error; + if (ret == 0) { + j++; + continue; + } + } else { + parNode = dupls[j]; + for (k = 0; k < nbFields; k++) { + ret = xmlSchemaAreValuesEqual( + node->keys[k]->val, + parNode->keys[k]->val); + if (ret == -1) + goto internal_error; + if (ret == 0) + break; + } + } + if (ret == 1) + /* Duplicate found. */ + break; + j++; + } + if (j != oldDupls) { + /* Duplicate found. Skip this entry. */ + continue; + } + } + /* + * ... and with every key-sequence of the parent node. + */ + if (oldNum) { + j = 0; + while (j < oldNum) { + parNode = parNodes[j]; + if (nbFields == 1) { + ret = xmlSchemaAreValuesEqual( + node->keys[0]->val, + parNode->keys[0]->val); + if (ret == -1) + goto internal_error; + if (ret == 0) { + j++; + continue; + } + } else { + for (k = 0; k < nbFields; k++) { + ret = xmlSchemaAreValuesEqual( + node->keys[k]->val, + parNode->keys[k]->val); + if (ret == -1) + goto internal_error; + if (ret == 0) + break; + } + } + if (ret == 1) + /* Duplicate found. */ + break; + j++; + } + if (j != oldNum) { + /* + * Handle duplicates. Move the duplicate in + * the parent's node-table to the list of + * duplicates. + */ + oldNum--; + parBind->nbNodes--; + /* + * Move last old item to pos of duplicate. + */ + parNodes[j] = parNodes[oldNum]; + + if (parBind->nbNodes != oldNum) { + /* + * If new items exist, move last new item to + * last of old items. + */ + parNodes[oldNum] = + parNodes[parBind->nbNodes]; + } + if (parBind->dupls == NULL) { + parBind->dupls = xmlSchemaItemListCreate(); + if (parBind->dupls == NULL) + goto internal_error; + } + xmlSchemaItemListAdd(parBind->dupls, parNode); + } else { + /* + * Add the node-table entry (node and key-sequence) of + * the child node to the node table of the parent node. + */ + if (parBind->nodeTable == NULL) { + parBind->nodeTable = (xmlSchemaPSVIIDCNodePtr *) + xmlMalloc(10 * sizeof(xmlSchemaPSVIIDCNodePtr)); + if (parBind->nodeTable == NULL) { + xmlSchemaVErrMemory(NULL, + "allocating IDC list of node-table items", NULL); + goto internal_error; + } + parBind->sizeNodes = 1; + } else if (parBind->nbNodes >= parBind->sizeNodes) { + parBind->sizeNodes *= 2; + parBind->nodeTable = (xmlSchemaPSVIIDCNodePtr *) + xmlRealloc(parBind->nodeTable, parBind->sizeNodes * + sizeof(xmlSchemaPSVIIDCNodePtr)); + if (parBind->nodeTable == NULL) { + xmlSchemaVErrMemory(NULL, + "re-allocating IDC list of node-table items", NULL); + goto internal_error; + } + } + parNodes = parBind->nodeTable; + /* + * Append the new node-table entry to the 'new node-table + * entries' section. + */ + parNodes[parBind->nbNodes++] = node; + } + + } + + } + } else { + /* + * No binding for the IDC was found: create a new one and + * copy all node-tables. + */ + parBind = xmlSchemaIDCNewBinding(bind->definition); + if (parBind == NULL) + goto internal_error; + + /* + * TODO: Hmm, how to optimize the initial number of + * allocated entries? + */ + if (bind->nbNodes != 0) { + /* + * Add all IDC node-table entries. + */ + if (! vctxt->psviExposeIDCNodeTables) { + /* + * Just move the entries. + * NOTE: this is quite save here, since + * all the keyref lookups have already been + * performed. + */ + parBind->nodeTable = bind->nodeTable; + bind->nodeTable = NULL; + parBind->sizeNodes = bind->sizeNodes; + bind->sizeNodes = 0; + parBind->nbNodes = bind->nbNodes; + bind->nbNodes = 0; + } else { + /* + * Copy the entries. + */ + parBind->nodeTable = (xmlSchemaPSVIIDCNodePtr *) + xmlMalloc(bind->nbNodes * + sizeof(xmlSchemaPSVIIDCNodePtr)); + if (parBind->nodeTable == NULL) { + xmlSchemaVErrMemory(NULL, + "allocating an array of IDC node-table " + "items", NULL); + xmlSchemaIDCFreeBinding(parBind); + goto internal_error; + } + parBind->sizeNodes = bind->nbNodes; + parBind->nbNodes = bind->nbNodes; + memcpy(parBind->nodeTable, bind->nodeTable, + bind->nbNodes * sizeof(xmlSchemaPSVIIDCNodePtr)); + } + } + if (bind->dupls) { + /* + * Move the duplicates. + */ + if (parBind->dupls != NULL) + xmlSchemaItemListFree(parBind->dupls); + parBind->dupls = bind->dupls; + bind->dupls = NULL; + } + if (*parTable == NULL) + *parTable = parBind; + else { + parBind->next = *parTable; + *parTable = parBind; + } + } + +next_binding: + bind = bind->next; + } + return (0); + +internal_error: + return(-1); +} + +/** + * xmlSchemaCheckCVCIDCKeyRef: + * @vctxt: the WXS validation context + * @elemDecl: the element declaration + * + * Check the cvc-idc-keyref constraints. + */ +static int +xmlSchemaCheckCVCIDCKeyRef(xmlSchemaValidCtxtPtr vctxt) +{ + xmlSchemaIDCMatcherPtr matcher; + xmlSchemaPSVIIDCBindingPtr bind; + + matcher = vctxt->inode->idcMatchers; + /* + * Find a keyref. + */ + while (matcher != NULL) { + if ((matcher->idcType == XML_SCHEMA_TYPE_IDC_KEYREF) && + matcher->targets && + matcher->targets->nbItems) + { + int i, j, k, res, nbFields, hasDupls; + xmlSchemaPSVIIDCKeyPtr *refKeys, *keys; + xmlSchemaPSVIIDCNodePtr refNode = NULL; + + nbFields = matcher->aidc->def->nbFields; + + /* + * Find the IDC node-table for the referenced IDC key/unique. + */ + bind = vctxt->inode->idcTable; + while (bind != NULL) { + if ((xmlSchemaIDCPtr) matcher->aidc->def->ref->item == + bind->definition) + break; + bind = bind->next; + } + hasDupls = (bind && bind->dupls && bind->dupls->nbItems) ? 1 : 0; + /* + * Search for a matching key-sequences. + */ + for (i = 0; i < matcher->targets->nbItems; i++) { + res = 0; + refNode = matcher->targets->items[i]; + if (bind != NULL) { + refKeys = refNode->keys; + for (j = 0; j < bind->nbNodes; j++) { + keys = bind->nodeTable[j]->keys; + for (k = 0; k < nbFields; k++) { + res = xmlSchemaAreValuesEqual(keys[k]->val, + refKeys[k]->val); + if (res == 0) + break; + else if (res == -1) { + return (-1); + } + } + if (res == 1) { + /* + * Match found. + */ + break; + } + } + if ((res == 0) && hasDupls) { + /* + * Search in duplicates + */ + for (j = 0; j < bind->dupls->nbItems; j++) { + keys = ((xmlSchemaPSVIIDCNodePtr) + bind->dupls->items[j])->keys; + for (k = 0; k < nbFields; k++) { + res = xmlSchemaAreValuesEqual(keys[k]->val, + refKeys[k]->val); + if (res == 0) + break; + else if (res == -1) { + return (-1); + } + } + if (res == 1) { + /* + * Match in duplicates found. + */ + xmlChar *str = NULL, *strB = NULL; + xmlSchemaKeyrefErr(vctxt, + XML_SCHEMAV_CVC_IDC, refNode, + (xmlSchemaTypePtr) matcher->aidc->def, + "More than one match found for " + "key-sequence %s of keyref '%s'", + xmlSchemaFormatIDCKeySequence(vctxt, &str, + refNode->keys, nbFields), + xmlSchemaGetComponentQName(&strB, + matcher->aidc->def)); + FREE_AND_NULL(str); + FREE_AND_NULL(strB); + break; + } + } + } + } + + if (res == 0) { + xmlChar *str = NULL, *strB = NULL; + xmlSchemaKeyrefErr(vctxt, + XML_SCHEMAV_CVC_IDC, refNode, + (xmlSchemaTypePtr) matcher->aidc->def, + "No match found for key-sequence %s of keyref '%s'", + xmlSchemaFormatIDCKeySequence(vctxt, &str, + refNode->keys, nbFields), + xmlSchemaGetComponentQName(&strB, matcher->aidc->def)); + FREE_AND_NULL(str); + FREE_AND_NULL(strB); + } + } + } + matcher = matcher->next; + } + /* TODO: Return an error if any error encountered. */ + return (0); +} + +/************************************************************************ + * * + * XML Reader validation code * + * * + ************************************************************************/ + +static xmlSchemaAttrInfoPtr +xmlSchemaGetFreshAttrInfo(xmlSchemaValidCtxtPtr vctxt) +{ + xmlSchemaAttrInfoPtr iattr; + /* + * Grow/create list of attribute infos. + */ + if (vctxt->attrInfos == NULL) { + vctxt->attrInfos = (xmlSchemaAttrInfoPtr *) + xmlMalloc(sizeof(xmlSchemaAttrInfoPtr)); + vctxt->sizeAttrInfos = 1; + if (vctxt->attrInfos == NULL) { + xmlSchemaVErrMemory(vctxt, + "allocating attribute info list", NULL); + return (NULL); + } + } else if (vctxt->sizeAttrInfos <= vctxt->nbAttrInfos) { + vctxt->sizeAttrInfos++; + vctxt->attrInfos = (xmlSchemaAttrInfoPtr *) + xmlRealloc(vctxt->attrInfos, + vctxt->sizeAttrInfos * sizeof(xmlSchemaAttrInfoPtr)); + if (vctxt->attrInfos == NULL) { + xmlSchemaVErrMemory(vctxt, + "re-allocating attribute info list", NULL); + return (NULL); + } + } else { + iattr = vctxt->attrInfos[vctxt->nbAttrInfos++]; + if (iattr->localName != NULL) { + VERROR_INT("xmlSchemaGetFreshAttrInfo", + "attr info not cleared"); + return (NULL); + } + iattr->nodeType = XML_ATTRIBUTE_NODE; + return (iattr); + } + /* + * Create an attribute info. + */ + iattr = (xmlSchemaAttrInfoPtr) + xmlMalloc(sizeof(xmlSchemaAttrInfo)); + if (iattr == NULL) { + xmlSchemaVErrMemory(vctxt, "creating new attribute info", NULL); + return (NULL); + } + memset(iattr, 0, sizeof(xmlSchemaAttrInfo)); + iattr->nodeType = XML_ATTRIBUTE_NODE; + vctxt->attrInfos[vctxt->nbAttrInfos++] = iattr; + + return (iattr); +} + +static int +xmlSchemaValidatorPushAttribute(xmlSchemaValidCtxtPtr vctxt, + xmlNodePtr attrNode, + int nodeLine, + const xmlChar *localName, + const xmlChar *nsName, + int ownedNames, + xmlChar *value, + int ownedValue) +{ + xmlSchemaAttrInfoPtr attr; + + attr = xmlSchemaGetFreshAttrInfo(vctxt); + if (attr == NULL) { + VERROR_INT("xmlSchemaPushAttribute", + "calling xmlSchemaGetFreshAttrInfo()"); + return (-1); + } + attr->node = attrNode; + attr->nodeLine = nodeLine; + attr->state = XML_SCHEMAS_ATTR_UNKNOWN; + attr->localName = localName; + attr->nsName = nsName; + if (ownedNames) + attr->flags |= XML_SCHEMA_NODE_INFO_FLAG_OWNED_NAMES; + /* + * Evaluate if it's an XSI attribute. + */ + if (nsName != NULL) { + if (xmlStrEqual(localName, BAD_CAST "nil")) { + if (xmlStrEqual(attr->nsName, xmlSchemaInstanceNs)) { + attr->metaType = XML_SCHEMA_ATTR_INFO_META_XSI_NIL; + } + } else if (xmlStrEqual(localName, BAD_CAST "type")) { + if (xmlStrEqual(attr->nsName, xmlSchemaInstanceNs)) { + attr->metaType = XML_SCHEMA_ATTR_INFO_META_XSI_TYPE; + } + } else if (xmlStrEqual(localName, BAD_CAST "schemaLocation")) { + if (xmlStrEqual(attr->nsName, xmlSchemaInstanceNs)) { + attr->metaType = XML_SCHEMA_ATTR_INFO_META_XSI_SCHEMA_LOC; + } + } else if (xmlStrEqual(localName, BAD_CAST "noNamespaceSchemaLocation")) { + if (xmlStrEqual(attr->nsName, xmlSchemaInstanceNs)) { + attr->metaType = XML_SCHEMA_ATTR_INFO_META_XSI_NO_NS_SCHEMA_LOC; + } + } else if (xmlStrEqual(attr->nsName, xmlNamespaceNs)) { + attr->metaType = XML_SCHEMA_ATTR_INFO_META_XMLNS; + } + } + attr->value = value; + if (ownedValue) + attr->flags |= XML_SCHEMA_NODE_INFO_FLAG_OWNED_VALUES; + if (attr->metaType != 0) + attr->state = XML_SCHEMAS_ATTR_META; + return (0); +} + +static void +xmlSchemaClearElemInfo(xmlSchemaNodeInfoPtr ielem) +{ + ielem->hasKeyrefs = 0; + ielem->appliedXPath = 0; + if (ielem->flags & XML_SCHEMA_NODE_INFO_FLAG_OWNED_NAMES) { + FREE_AND_NULL(ielem->localName); + FREE_AND_NULL(ielem->nsName); + } else { + ielem->localName = NULL; + ielem->nsName = NULL; + } + if (ielem->flags & XML_SCHEMA_NODE_INFO_FLAG_OWNED_VALUES) { + FREE_AND_NULL(ielem->value); + } else { + ielem->value = NULL; + } + if (ielem->val != NULL) { + /* + * PSVI TODO: Be careful not to free it when the value is + * exposed via PSVI. + */ + xmlSchemaFreeValue(ielem->val); + ielem->val = NULL; + } + if (ielem->idcMatchers != NULL) { + /* + * URGENT OPTIMIZE TODO: Use a pool of IDC matchers. + */ + xmlSchemaIDCFreeMatcherList(ielem->idcMatchers); + ielem->idcMatchers = NULL; + } + if (ielem->idcTable != NULL) { + /* + * OPTIMIZE TODO: Use a pool of IDC tables??. + */ + xmlSchemaIDCFreeIDCTable(ielem->idcTable); + ielem->idcTable = NULL; + } + if (ielem->regexCtxt != NULL) { + xmlRegFreeExecCtxt(ielem->regexCtxt); + ielem->regexCtxt = NULL; + } + if (ielem->nsBindings != NULL) { + xmlFree((xmlChar **)ielem->nsBindings); + ielem->nsBindings = NULL; + ielem->nbNsBindings = 0; + ielem->sizeNsBindings = 0; + } +} + +/** + * xmlSchemaGetFreshElemInfo: + * @vctxt: the schema validation context + * + * Creates/reuses and initializes the element info item for + * the currect tree depth. + * + * Returns the element info item or NULL on API or internal errors. + */ +static xmlSchemaNodeInfoPtr +xmlSchemaGetFreshElemInfo(xmlSchemaValidCtxtPtr vctxt) +{ + xmlSchemaNodeInfoPtr info = NULL; + + if (vctxt->depth > vctxt->sizeElemInfos) { + VERROR_INT("xmlSchemaGetFreshElemInfo", + "inconsistent depth encountered"); + return (NULL); + } + if (vctxt->elemInfos == NULL) { + vctxt->elemInfos = (xmlSchemaNodeInfoPtr *) + xmlMalloc(10 * sizeof(xmlSchemaNodeInfoPtr)); + if (vctxt->elemInfos == NULL) { + xmlSchemaVErrMemory(vctxt, + "allocating the element info array", NULL); + return (NULL); + } + memset(vctxt->elemInfos, 0, 10 * sizeof(xmlSchemaNodeInfoPtr)); + vctxt->sizeElemInfos = 10; + } else if (vctxt->sizeElemInfos <= vctxt->depth) { + int i = vctxt->sizeElemInfos; + + vctxt->sizeElemInfos *= 2; + vctxt->elemInfos = (xmlSchemaNodeInfoPtr *) + xmlRealloc(vctxt->elemInfos, vctxt->sizeElemInfos * + sizeof(xmlSchemaNodeInfoPtr)); + if (vctxt->elemInfos == NULL) { + xmlSchemaVErrMemory(vctxt, + "re-allocating the element info array", NULL); + return (NULL); + } + /* + * We need the new memory to be NULLed. + * TODO: Use memset instead? + */ + for (; i < vctxt->sizeElemInfos; i++) + vctxt->elemInfos[i] = NULL; + } else + info = vctxt->elemInfos[vctxt->depth]; + + if (info == NULL) { + info = (xmlSchemaNodeInfoPtr) + xmlMalloc(sizeof(xmlSchemaNodeInfo)); + if (info == NULL) { + xmlSchemaVErrMemory(vctxt, + "allocating an element info", NULL); + return (NULL); + } + vctxt->elemInfos[vctxt->depth] = info; + } else { + if (info->localName != NULL) { + VERROR_INT("xmlSchemaGetFreshElemInfo", + "elem info has not been cleared"); + return (NULL); + } + } + memset(info, 0, sizeof(xmlSchemaNodeInfo)); + info->nodeType = XML_ELEMENT_NODE; + info->depth = vctxt->depth; + + return (info); +} + +#define ACTIVATE_ATTRIBUTE(item) vctxt->inode = (xmlSchemaNodeInfoPtr) item; +#define ACTIVATE_ELEM vctxt->inode = vctxt->elemInfos[vctxt->depth]; +#define ACTIVATE_PARENT_ELEM vctxt->inode = vctxt->elemInfos[vctxt->depth -1]; + +static int +xmlSchemaValidateFacets(xmlSchemaAbstractCtxtPtr actxt, + xmlNodePtr node, + xmlSchemaTypePtr type, + xmlSchemaValType valType, + const xmlChar * value, + xmlSchemaValPtr val, + unsigned long length, + int fireErrors) +{ + int ret, error = 0; + + xmlSchemaTypePtr tmpType; + xmlSchemaFacetLinkPtr facetLink; + xmlSchemaFacetPtr facet; + unsigned long len = 0; + xmlSchemaWhitespaceValueType ws; + + /* + * In Libxml2, derived built-in types have currently no explicit facets. + */ + if (type->type == XML_SCHEMA_TYPE_BASIC) + return (0); + + /* + * NOTE: Do not jump away, if the facetSet of the given type is + * empty: until now, "pattern" and "enumeration" facets of the + * *base types* need to be checked as well. + */ + if (type->facetSet == NULL) + goto pattern_and_enum; + + if (! WXS_IS_ATOMIC(type)) { + if (WXS_IS_LIST(type)) + goto WXS_IS_LIST; + else + goto pattern_and_enum; + } + /* + * Whitespace handling is only of importance for string-based + * types. + */ + tmpType = xmlSchemaGetPrimitiveType(type); + if ((tmpType->builtInType == XML_SCHEMAS_STRING) || + WXS_IS_ANY_SIMPLE_TYPE(tmpType)) { + ws = xmlSchemaGetWhiteSpaceFacetValue(type); + } else + ws = XML_SCHEMA_WHITESPACE_COLLAPSE; + /* + * If the value was not computed (for string or + * anySimpleType based types), then use the provided + * type. + */ + if (val == NULL) + valType = valType; + else + valType = xmlSchemaGetValType(val); + + ret = 0; + for (facetLink = type->facetSet; facetLink != NULL; + facetLink = facetLink->next) { + /* + * Skip the pattern "whiteSpace": it is used to + * format the character content beforehand. + */ + switch (facetLink->facet->type) { + case XML_SCHEMA_FACET_WHITESPACE: + case XML_SCHEMA_FACET_PATTERN: + case XML_SCHEMA_FACET_ENUMERATION: + continue; + case XML_SCHEMA_FACET_LENGTH: + case XML_SCHEMA_FACET_MINLENGTH: + case XML_SCHEMA_FACET_MAXLENGTH: + ret = xmlSchemaValidateLengthFacetWhtsp(facetLink->facet, + valType, value, val, &len, ws); + break; + default: + ret = xmlSchemaValidateFacetWhtsp(facetLink->facet, ws, + valType, value, val, ws); + break; + } + if (ret < 0) { + AERROR_INT("xmlSchemaValidateFacets", + "validating against a atomic type facet"); + return (-1); + } else if (ret > 0) { + if (fireErrors) + xmlSchemaFacetErr(actxt, ret, node, + value, len, type, facetLink->facet, NULL, NULL, NULL); + else + return (ret); + if (error == 0) + error = ret; + } + ret = 0; + } + +WXS_IS_LIST: + if (! WXS_IS_LIST(type)) + goto pattern_and_enum; + /* + * "length", "minLength" and "maxLength" of list types. + */ + ret = 0; + for (facetLink = type->facetSet; facetLink != NULL; + facetLink = facetLink->next) { + + switch (facetLink->facet->type) { + case XML_SCHEMA_FACET_LENGTH: + case XML_SCHEMA_FACET_MINLENGTH: + case XML_SCHEMA_FACET_MAXLENGTH: + ret = xmlSchemaValidateListSimpleTypeFacet(facetLink->facet, + value, length, NULL); + break; + default: + continue; + } + if (ret < 0) { + AERROR_INT("xmlSchemaValidateFacets", + "validating against a list type facet"); + return (-1); + } else if (ret > 0) { + if (fireErrors) + xmlSchemaFacetErr(actxt, ret, node, + value, length, type, facetLink->facet, NULL, NULL, NULL); + else + return (ret); + if (error == 0) + error = ret; + } + ret = 0; + } + +pattern_and_enum: + if (error >= 0) { + int found = 0; + /* + * Process enumerations. Facet values are in the value space + * of the defining type's base type. This seems to be a bug in the + * XML Schema 1.0 spec. Use the whitespace type of the base type. + * Only the first set of enumerations in the ancestor-or-self axis + * is used for validation. + */ + ret = 0; + tmpType = type; + do { + for (facet = tmpType->facets; facet != NULL; facet = facet->next) { + if (facet->type != XML_SCHEMA_FACET_ENUMERATION) + continue; + found = 1; + ret = xmlSchemaAreValuesEqual(facet->val, val); + if (ret == 1) + break; + else if (ret < 0) { + AERROR_INT("xmlSchemaValidateFacets", + "validating against an enumeration facet"); + return (-1); + } + } + if (ret != 0) + break; + /* + * Break on the first set of enumerations. Any additional + * enumerations which might be existent on the ancestors + * of the current type are restricted by this set; thus + * *must* *not* be taken into account. + */ + if (found) + break; + tmpType = tmpType->baseType; + } while ((tmpType != NULL) && + (tmpType->type != XML_SCHEMA_TYPE_BASIC)); + if (found && (ret == 0)) { + ret = XML_SCHEMAV_CVC_ENUMERATION_VALID; + if (fireErrors) { + xmlSchemaFacetErr(actxt, ret, node, + value, 0, type, NULL, NULL, NULL, NULL); + } else + return (ret); + if (error == 0) + error = ret; + } + } + + if (error >= 0) { + int found; + /* + * Process patters. Pattern facets are ORed at type level + * and ANDed if derived. Walk the base type axis. + */ + tmpType = type; + facet = NULL; + do { + found = 0; + for (facetLink = tmpType->facetSet; facetLink != NULL; + facetLink = facetLink->next) { + if (facetLink->facet->type != XML_SCHEMA_FACET_PATTERN) + continue; + found = 1; + /* + * NOTE that for patterns, @value needs to be the + * normalized vaule. + */ + ret = xmlRegexpExec(facetLink->facet->regexp, value); + if (ret == 1) + break; + else if (ret < 0) { + AERROR_INT("xmlSchemaValidateFacets", + "validating against a pattern facet"); + return (-1); + } else { + /* + * Save the last non-validating facet. + */ + facet = facetLink->facet; + } + } + if (found && (ret != 1)) { + ret = XML_SCHEMAV_CVC_PATTERN_VALID; + if (fireErrors) { + xmlSchemaFacetErr(actxt, ret, node, + value, 0, type, facet, NULL, NULL, NULL); + } else + return (ret); + if (error == 0) + error = ret; + break; + } + tmpType = tmpType->baseType; + } while ((tmpType != NULL) && (tmpType->type != XML_SCHEMA_TYPE_BASIC)); + } + + return (error); +} + +static xmlChar * +xmlSchemaNormalizeValue(xmlSchemaTypePtr type, + const xmlChar *value) +{ + switch (xmlSchemaGetWhiteSpaceFacetValue(type)) { + case XML_SCHEMA_WHITESPACE_COLLAPSE: + return (xmlSchemaCollapseString(value)); + case XML_SCHEMA_WHITESPACE_REPLACE: + return (xmlSchemaWhiteSpaceReplace(value)); + default: + return (NULL); + } +} + +static int +xmlSchemaValidateQName(xmlSchemaValidCtxtPtr vctxt, + const xmlChar *value, + xmlSchemaValPtr *val, + int valNeeded) +{ + int ret; + const xmlChar *nsName; + xmlChar *local, *prefix = NULL; + + ret = xmlValidateQName(value, 1); + if (ret != 0) { + if (ret == -1) { + VERROR_INT("xmlSchemaValidateQName", + "calling xmlValidateQName()"); + return (-1); + } + return( XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_1); + } + /* + * NOTE: xmlSplitQName2 will always return a duplicated + * strings. + */ + local = xmlSplitQName2(value, &prefix); + if (local == NULL) + local = xmlStrdup(value); + /* + * OPTIMIZE TODO: Use flags for: + * - is there any namespace binding? + * - is there a default namespace? + */ + nsName = xmlSchemaLookupNamespace(vctxt, prefix); + + if (prefix != NULL) { + xmlFree(prefix); + /* + * A namespace must be found if the prefix is + * NOT NULL. + */ + if (nsName == NULL) { + ret = XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_1; + xmlSchemaCustomErr(ACTXT_CAST vctxt, ret, NULL, + WXS_BASIC_CAST xmlSchemaGetBuiltInType(XML_SCHEMAS_QNAME), + "The QName value '%s' has no " + "corresponding namespace declaration in " + "scope", value, NULL); + if (local != NULL) + xmlFree(local); + return (ret); + } + } + if (valNeeded && val) { + if (nsName != NULL) + *val = xmlSchemaNewQNameValue( + BAD_CAST xmlStrdup(nsName), BAD_CAST local); + else + *val = xmlSchemaNewQNameValue(NULL, + BAD_CAST local); + } else + xmlFree(local); + return (0); +} + +/* +* cvc-simple-type +*/ +static int +xmlSchemaVCheckCVCSimpleType(xmlSchemaAbstractCtxtPtr actxt, + xmlNodePtr node, + xmlSchemaTypePtr type, + const xmlChar *value, + xmlSchemaValPtr *retVal, + int fireErrors, + int normalize, + int isNormalized) +{ + int ret = 0, valNeeded = (retVal) ? 1 : 0; + xmlSchemaValPtr val = NULL; + /* xmlSchemaWhitespaceValueType ws; */ + xmlChar *normValue = NULL; + +#define NORMALIZE(atype) \ + if ((! isNormalized) && \ + (normalize || (type->flags & XML_SCHEMAS_TYPE_NORMVALUENEEDED))) { \ + normValue = xmlSchemaNormalizeValue(atype, value); \ + if (normValue != NULL) \ + value = normValue; \ + isNormalized = 1; \ + } + + if ((retVal != NULL) && (*retVal != NULL)) { + xmlSchemaFreeValue(*retVal); + *retVal = NULL; + } + /* + * 3.14.4 Simple Type Definition Validation Rules + * Validation Rule: String Valid + */ + /* + * 1 It is schema-valid with respect to that definition as defined + * by Datatype Valid in [XML Schemas: Datatypes]. + */ + /* + * 2.1 If The definition is ENTITY or is validly derived from ENTITY given + * the empty set, as defined in Type Derivation OK (Simple) (§3.14.6), then + * the string must be a ·declared entity name·. + */ + /* + * 2.2 If The definition is ENTITIES or is validly derived from ENTITIES + * given the empty set, as defined in Type Derivation OK (Simple) (§3.14.6), + * then every whitespace-delimited substring of the string must be a ·declared + * entity name·. + */ + /* + * 2.3 otherwise no further condition applies. + */ + if ((! valNeeded) && (type->flags & XML_SCHEMAS_TYPE_FACETSNEEDVALUE)) + valNeeded = 1; + if (value == NULL) + value = BAD_CAST ""; + if (WXS_IS_ANY_SIMPLE_TYPE(type) || WXS_IS_ATOMIC(type)) { + xmlSchemaTypePtr biType; /* The built-in type. */ + /* + * SPEC (1.2.1) "if {variety} is ·atomic· then the string must ·match· + * a literal in the ·lexical space· of {base type definition}" + */ + /* + * Whitespace-normalize. + */ + NORMALIZE(type); + if (type->type != XML_SCHEMA_TYPE_BASIC) { + /* + * Get the built-in type. + */ + biType = type->baseType; + while ((biType != NULL) && + (biType->type != XML_SCHEMA_TYPE_BASIC)) + biType = biType->baseType; + + if (biType == NULL) { + AERROR_INT("xmlSchemaVCheckCVCSimpleType", + "could not get the built-in type"); + goto internal_error; + } + } else + biType = type; + /* + * NOTATIONs need to be processed here, since they need + * to lookup in the hashtable of NOTATION declarations of the schema. + */ + if (actxt->type == XML_SCHEMA_CTXT_VALIDATOR) { + switch (biType->builtInType) { + case XML_SCHEMAS_NOTATION: + ret = xmlSchemaValidateNotation( + (xmlSchemaValidCtxtPtr) actxt, + ((xmlSchemaValidCtxtPtr) actxt)->schema, + NULL, value, &val, valNeeded); + break; + case XML_SCHEMAS_QNAME: + ret = xmlSchemaValidateQName((xmlSchemaValidCtxtPtr) actxt, + value, &val, valNeeded); + break; + default: + /* ws = xmlSchemaGetWhiteSpaceFacetValue(type); */ + if (valNeeded) + ret = xmlSchemaValPredefTypeNodeNoNorm(biType, + value, &val, NULL); + else + ret = xmlSchemaValPredefTypeNodeNoNorm(biType, + value, NULL, NULL); + break; + } + } else if (actxt->type == XML_SCHEMA_CTXT_PARSER) { + switch (biType->builtInType) { + case XML_SCHEMAS_NOTATION: + ret = xmlSchemaValidateNotation(NULL, + ((xmlSchemaParserCtxtPtr) actxt)->schema, node, + value, &val, valNeeded); + break; + default: + /* ws = xmlSchemaGetWhiteSpaceFacetValue(type); */ + if (valNeeded) + ret = xmlSchemaValPredefTypeNodeNoNorm(biType, + value, &val, node); + else + ret = xmlSchemaValPredefTypeNodeNoNorm(biType, + value, NULL, node); + break; + } + } else { + /* + * Validation via a public API is not implemented yet. + */ + TODO + goto internal_error; + } + if (ret != 0) { + if (ret < 0) { + AERROR_INT("xmlSchemaVCheckCVCSimpleType", + "validating against a built-in type"); + goto internal_error; + } + if (WXS_IS_LIST(type)) + ret = XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_2; + else + ret = XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_1; + } + if ((ret == 0) && (type->flags & XML_SCHEMAS_TYPE_HAS_FACETS)) { + /* + * Check facets. + */ + ret = xmlSchemaValidateFacets(actxt, node, type, + (xmlSchemaValType) biType->builtInType, value, val, + 0, fireErrors); + if (ret != 0) { + if (ret < 0) { + AERROR_INT("xmlSchemaVCheckCVCSimpleType", + "validating facets of atomic simple type"); + goto internal_error; + } + if (WXS_IS_LIST(type)) + ret = XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_2; + else + ret = XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_1; + } + } + if (fireErrors && (ret > 0)) + xmlSchemaSimpleTypeErr(actxt, ret, node, value, type, 1); + } else if (WXS_IS_LIST(type)) { + + xmlSchemaTypePtr itemType; + const xmlChar *cur, *end; + xmlChar *tmpValue = NULL; + unsigned long len = 0; + xmlSchemaValPtr prevVal = NULL, curVal = NULL; + /* 1.2.2 if {variety} is ·list· then the string must be a sequence + * of white space separated tokens, each of which ·match·es a literal + * in the ·lexical space· of {item type definition} + */ + /* + * Note that XML_SCHEMAS_TYPE_NORMVALUENEEDED will be set if + * the list type has an enum or pattern facet. + */ + NORMALIZE(type); + /* + * VAL TODO: Optimize validation of empty values. + * VAL TODO: We do not have computed values for lists. + */ + itemType = WXS_LIST_ITEMTYPE(type); + cur = value; + do { + while (IS_BLANK_CH(*cur)) + cur++; + end = cur; + while ((*end != 0) && (!(IS_BLANK_CH(*end)))) + end++; + if (end == cur) + break; + tmpValue = xmlStrndup(cur, end - cur); + len++; + + if (valNeeded) + ret = xmlSchemaVCheckCVCSimpleType(actxt, node, itemType, + tmpValue, &curVal, fireErrors, 0, 1); + else + ret = xmlSchemaVCheckCVCSimpleType(actxt, node, itemType, + tmpValue, NULL, fireErrors, 0, 1); + FREE_AND_NULL(tmpValue); + if (curVal != NULL) { + /* + * Add to list of computed values. + */ + if (val == NULL) + val = curVal; + else + xmlSchemaValueAppend(prevVal, curVal); + prevVal = curVal; + curVal = NULL; + } + if (ret != 0) { + if (ret < 0) { + AERROR_INT("xmlSchemaVCheckCVCSimpleType", + "validating an item of list simple type"); + goto internal_error; + } + ret = XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_2; + break; + } + cur = end; + } while (*cur != 0); + FREE_AND_NULL(tmpValue); + if ((ret == 0) && (type->flags & XML_SCHEMAS_TYPE_HAS_FACETS)) { + /* + * Apply facets (pattern, enumeration). + */ + ret = xmlSchemaValidateFacets(actxt, node, type, + XML_SCHEMAS_UNKNOWN, value, val, + len, fireErrors); + if (ret != 0) { + if (ret < 0) { + AERROR_INT("xmlSchemaVCheckCVCSimpleType", + "validating facets of list simple type"); + goto internal_error; + } + ret = XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_2; + } + } + if (fireErrors && (ret > 0)) { + /* + * Report the normalized value. + */ + normalize = 1; + NORMALIZE(type); + xmlSchemaSimpleTypeErr(actxt, ret, node, value, type, 1); + } + } else if (WXS_IS_UNION(type)) { + xmlSchemaTypeLinkPtr memberLink; + /* + * TODO: For all datatypes ·derived· by ·union· whiteSpace does + * not apply directly; however, the normalization behavior of ·union· + * types is controlled by the value of whiteSpace on that one of the + * ·memberTypes· against which the ·union· is successfully validated. + * + * This means that the value is normalized by the first validating + * member type, then the facets of the union type are applied. This + * needs changing of the value! + */ + + /* + * 1.2.3 if {variety} is ·union· then the string must ·match· a + * literal in the ·lexical space· of at least one member of + * {member type definitions} + */ + memberLink = xmlSchemaGetUnionSimpleTypeMemberTypes(type); + if (memberLink == NULL) { + AERROR_INT("xmlSchemaVCheckCVCSimpleType", + "union simple type has no member types"); + goto internal_error; + } + /* + * Always normalize union type values, since we currently + * cannot store the whitespace information with the value + * itself; otherwise a later value-comparison would be + * not possible. + */ + while (memberLink != NULL) { + if (valNeeded) + ret = xmlSchemaVCheckCVCSimpleType(actxt, node, + memberLink->type, value, &val, 0, 1, 0); + else + ret = xmlSchemaVCheckCVCSimpleType(actxt, node, + memberLink->type, value, NULL, 0, 1, 0); + if (ret <= 0) + break; + memberLink = memberLink->next; + } + if (ret != 0) { + if (ret < 0) { + AERROR_INT("xmlSchemaVCheckCVCSimpleType", + "validating members of union simple type"); + goto internal_error; + } + ret = XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_3; + } + /* + * Apply facets (pattern, enumeration). + */ + if ((ret == 0) && (type->flags & XML_SCHEMAS_TYPE_HAS_FACETS)) { + /* + * The normalization behavior of ·union· types is controlled by + * the value of whiteSpace on that one of the ·memberTypes· + * against which the ·union· is successfully validated. + */ + NORMALIZE(memberLink->type); + ret = xmlSchemaValidateFacets(actxt, node, type, + XML_SCHEMAS_UNKNOWN, value, val, + 0, fireErrors); + if (ret != 0) { + if (ret < 0) { + AERROR_INT("xmlSchemaVCheckCVCSimpleType", + "validating facets of union simple type"); + goto internal_error; + } + ret = XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_3; + } + } + if (fireErrors && (ret > 0)) + xmlSchemaSimpleTypeErr(actxt, ret, node, value, type, 1); + } + + if (normValue != NULL) + xmlFree(normValue); + if (ret == 0) { + if (retVal != NULL) + *retVal = val; + else if (val != NULL) + xmlSchemaFreeValue(val); + } else if (val != NULL) + xmlSchemaFreeValue(val); + return (ret); +internal_error: + if (normValue != NULL) + xmlFree(normValue); + if (val != NULL) + xmlSchemaFreeValue(val); + return (-1); +} + +static int +xmlSchemaVExpandQName(xmlSchemaValidCtxtPtr vctxt, + const xmlChar *value, + const xmlChar **nsName, + const xmlChar **localName) +{ + int ret = 0; + + if ((nsName == NULL) || (localName == NULL)) + return (-1); + *nsName = NULL; + *localName = NULL; + + ret = xmlValidateQName(value, 1); + if (ret == -1) + return (-1); + if (ret > 0) { + xmlSchemaSimpleTypeErr(ACTXT_CAST vctxt, + XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_1, NULL, + value, xmlSchemaGetBuiltInType(XML_SCHEMAS_QNAME), 1); + return (1); + } + { + xmlChar *local = NULL; + xmlChar *prefix; + + /* + * NOTE: xmlSplitQName2 will return a duplicated + * string. + */ + local = xmlSplitQName2(value, &prefix); + if (local == NULL) + *localName = xmlDictLookup(vctxt->dict, value, -1); + else { + *localName = xmlDictLookup(vctxt->dict, local, -1); + xmlFree(local); + } + + *nsName = xmlSchemaLookupNamespace(vctxt, prefix); + + if (prefix != NULL) { + xmlFree(prefix); + /* + * A namespace must be found if the prefix is NOT NULL. + */ + if (*nsName == NULL) { + xmlSchemaCustomErr(ACTXT_CAST vctxt, + XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_1, NULL, + WXS_BASIC_CAST xmlSchemaGetBuiltInType(XML_SCHEMAS_QNAME), + "The QName value '%s' has no " + "corresponding namespace declaration in scope", + value, NULL); + return (2); + } + } + } + return (0); +} + +static int +xmlSchemaProcessXSIType(xmlSchemaValidCtxtPtr vctxt, + xmlSchemaAttrInfoPtr iattr, + xmlSchemaTypePtr *localType, + xmlSchemaElementPtr elemDecl) +{ + int ret = 0; + /* + * cvc-elt (3.3.4) : (4) + * AND + * Schema-Validity Assessment (Element) (cvc-assess-elt) + * (1.2.1.2.1) - (1.2.1.2.4) + * Handle 'xsi:type'. + */ + if (localType == NULL) + return (-1); + *localType = NULL; + if (iattr == NULL) + return (0); + else { + const xmlChar *nsName = NULL, *local = NULL; + /* + * TODO: We should report a *warning* that the type was overriden + * by the instance. + */ + ACTIVATE_ATTRIBUTE(iattr); + /* + * (cvc-elt) (3.3.4) : (4.1) + * (cvc-assess-elt) (1.2.1.2.2) + */ + ret = xmlSchemaVExpandQName(vctxt, iattr->value, + &nsName, &local); + if (ret != 0) { + if (ret < 0) { + VERROR_INT("xmlSchemaValidateElementByDeclaration", + "calling xmlSchemaQNameExpand() to validate the " + "attribute 'xsi:type'"); + goto internal_error; + } + goto exit; + } + /* + * (cvc-elt) (3.3.4) : (4.2) + * (cvc-assess-elt) (1.2.1.2.3) + */ + *localType = xmlSchemaGetType(vctxt->schema, local, nsName); + if (*localType == NULL) { + xmlChar *str = NULL; + + xmlSchemaCustomErr(ACTXT_CAST vctxt, + XML_SCHEMAV_CVC_ELT_4_2, NULL, + WXS_BASIC_CAST xmlSchemaGetBuiltInType(XML_SCHEMAS_QNAME), + "The QName value '%s' of the xsi:type attribute does not " + "resolve to a type definition", + xmlSchemaFormatQName(&str, nsName, local), NULL); + FREE_AND_NULL(str); + ret = vctxt->err; + goto exit; + } + if (elemDecl != NULL) { + int set = 0; + + /* + * SPEC cvc-elt (3.3.4) : (4.3) (Type Derivation OK) + * "The ·local type definition· must be validly + * derived from the {type definition} given the union of + * the {disallowed substitutions} and the {type definition}'s + * {prohibited substitutions}, as defined in + * Type Derivation OK (Complex) (§3.4.6) + * (if it is a complex type definition), + * or given {disallowed substitutions} as defined in Type + * Derivation OK (Simple) (§3.14.6) (if it is a simple type + * definition)." + * + * {disallowed substitutions}: the "block" on the element decl. + * {prohibited substitutions}: the "block" on the type def. + */ + /* + * OPTIMIZE TODO: We could map types already evaluated + * to be validly derived from other types to avoid checking + * this over and over for the same types. + */ + if ((elemDecl->flags & XML_SCHEMAS_ELEM_BLOCK_EXTENSION) || + (elemDecl->subtypes->flags & + XML_SCHEMAS_TYPE_BLOCK_EXTENSION)) + set |= SUBSET_EXTENSION; + + if ((elemDecl->flags & XML_SCHEMAS_ELEM_BLOCK_RESTRICTION) || + (elemDecl->subtypes->flags & + XML_SCHEMAS_TYPE_BLOCK_RESTRICTION)) + set |= SUBSET_RESTRICTION; + + /* + * REMOVED and CHANGED since this produced a parser context + * which adds to the string dict of the schema. So this would + * change the schema and we don't want this. We don't need + * the parser context anymore. + * + * if ((vctxt->pctxt == NULL) && + * (xmlSchemaCreatePCtxtOnVCtxt(vctxt) == -1)) + * return (-1); + */ + + if (xmlSchemaCheckCOSDerivedOK(ACTXT_CAST vctxt, *localType, + elemDecl->subtypes, set) != 0) { + xmlChar *str = NULL; + + xmlSchemaCustomErr(ACTXT_CAST vctxt, + XML_SCHEMAV_CVC_ELT_4_3, NULL, NULL, + "The type definition '%s', specified by xsi:type, is " + "blocked or not validly derived from the type definition " + "of the element declaration", + xmlSchemaFormatQName(&str, + (*localType)->targetNamespace, + (*localType)->name), + NULL); + FREE_AND_NULL(str); + ret = vctxt->err; + *localType = NULL; + } + } + } +exit: + ACTIVATE_ELEM; + return (ret); +internal_error: + ACTIVATE_ELEM; + return (-1); +} + +static int +xmlSchemaValidateElemDecl(xmlSchemaValidCtxtPtr vctxt) +{ + xmlSchemaElementPtr elemDecl = vctxt->inode->decl; + xmlSchemaTypePtr actualType; + + /* + * cvc-elt (3.3.4) : 1 + */ + if (elemDecl == NULL) { + VERROR(XML_SCHEMAV_CVC_ELT_1, NULL, + "No matching declaration available"); + return (vctxt->err); + } + actualType = WXS_ELEM_TYPEDEF(elemDecl); + /* + * cvc-elt (3.3.4) : 2 + */ + if (elemDecl->flags & XML_SCHEMAS_ELEM_ABSTRACT) { + VERROR(XML_SCHEMAV_CVC_ELT_2, NULL, + "The element declaration is abstract"); + return (vctxt->err); + } + if (actualType == NULL) { + VERROR(XML_SCHEMAV_CVC_TYPE_1, NULL, + "The type definition is absent"); + return (XML_SCHEMAV_CVC_TYPE_1); + } + if (vctxt->nbAttrInfos != 0) { + int ret; + xmlSchemaAttrInfoPtr iattr; + /* + * cvc-elt (3.3.4) : 3 + * Handle 'xsi:nil'. + */ + iattr = xmlSchemaGetMetaAttrInfo(vctxt, + XML_SCHEMA_ATTR_INFO_META_XSI_NIL); + if (iattr) { + ACTIVATE_ATTRIBUTE(iattr); + /* + * Validate the value. + */ + ret = xmlSchemaVCheckCVCSimpleType( + ACTXT_CAST vctxt, NULL, + xmlSchemaGetBuiltInType(XML_SCHEMAS_BOOLEAN), + iattr->value, &(iattr->val), 1, 0, 0); + ACTIVATE_ELEM; + if (ret < 0) { + VERROR_INT("xmlSchemaValidateElemDecl", + "calling xmlSchemaVCheckCVCSimpleType() to " + "validate the attribute 'xsi:nil'"); + return (-1); + } + if (ret == 0) { + if ((elemDecl->flags & XML_SCHEMAS_ELEM_NILLABLE) == 0) { + /* + * cvc-elt (3.3.4) : 3.1 + */ + VERROR(XML_SCHEMAV_CVC_ELT_3_1, NULL, + "The element is not 'nillable'"); + /* Does not return an error on purpose. */ + } else { + if (xmlSchemaValueGetAsBoolean(iattr->val)) { + /* + * cvc-elt (3.3.4) : 3.2.2 + */ + if ((elemDecl->flags & XML_SCHEMAS_ELEM_FIXED) && + (elemDecl->value != NULL)) { + VERROR(XML_SCHEMAV_CVC_ELT_3_2_2, NULL, + "The element cannot be 'nilled' because " + "there is a fixed value constraint defined " + "for it"); + /* Does not return an error on purpose. */ + } else + vctxt->inode->flags |= + XML_SCHEMA_ELEM_INFO_NILLED; + } + } + } + } + /* + * cvc-elt (3.3.4) : 4 + * Handle 'xsi:type'. + */ + iattr = xmlSchemaGetMetaAttrInfo(vctxt, + XML_SCHEMA_ATTR_INFO_META_XSI_TYPE); + if (iattr) { + xmlSchemaTypePtr localType = NULL; + + ret = xmlSchemaProcessXSIType(vctxt, iattr, &localType, + elemDecl); + if (ret != 0) { + if (ret == -1) { + VERROR_INT("xmlSchemaValidateElemDecl", + "calling xmlSchemaProcessXSIType() to " + "process the attribute 'xsi:type'"); + return (-1); + } + /* Does not return an error on purpose. */ + } + if (localType != NULL) { + vctxt->inode->flags |= XML_SCHEMA_ELEM_INFO_LOCAL_TYPE; + actualType = localType; + } + } + } + /* + * IDC: Register identity-constraint XPath matchers. + */ + if ((elemDecl->idcs != NULL) && + (xmlSchemaIDCRegisterMatchers(vctxt, elemDecl) == -1)) + return (-1); + /* + * No actual type definition. + */ + if (actualType == NULL) { + VERROR(XML_SCHEMAV_CVC_TYPE_1, NULL, + "The type definition is absent"); + return (XML_SCHEMAV_CVC_TYPE_1); + } + /* + * Remember the actual type definition. + */ + vctxt->inode->typeDef = actualType; + + return (0); +} + +static int +xmlSchemaVAttributesSimple(xmlSchemaValidCtxtPtr vctxt) +{ + xmlSchemaAttrInfoPtr iattr; + int ret = 0, i; + + /* + * SPEC cvc-type (3.1.1) + * "The attributes of must be empty, excepting those whose namespace + * name is identical to http://www.w3.org/2001/XMLSchema-instance and + * whose local name is one of type, nil, schemaLocation or + * noNamespaceSchemaLocation." + */ + if (vctxt->nbAttrInfos == 0) + return (0); + for (i = 0; i < vctxt->nbAttrInfos; i++) { + iattr = vctxt->attrInfos[i]; + if (! iattr->metaType) { + ACTIVATE_ATTRIBUTE(iattr) + xmlSchemaIllegalAttrErr(ACTXT_CAST vctxt, + XML_SCHEMAV_CVC_TYPE_3_1_1, iattr, NULL); + ret = XML_SCHEMAV_CVC_TYPE_3_1_1; + } + } + ACTIVATE_ELEM + return (ret); +} + +/* +* Cleanup currently used attribute infos. +*/ +static void +xmlSchemaClearAttrInfos(xmlSchemaValidCtxtPtr vctxt) +{ + int i; + xmlSchemaAttrInfoPtr attr; + + if (vctxt->nbAttrInfos == 0) + return; + for (i = 0; i < vctxt->nbAttrInfos; i++) { + attr = vctxt->attrInfos[i]; + if (attr->flags & XML_SCHEMA_NODE_INFO_FLAG_OWNED_NAMES) { + if (attr->localName != NULL) + xmlFree((xmlChar *) attr->localName); + if (attr->nsName != NULL) + xmlFree((xmlChar *) attr->nsName); + } + if (attr->flags & XML_SCHEMA_NODE_INFO_FLAG_OWNED_VALUES) { + if (attr->value != NULL) + xmlFree((xmlChar *) attr->value); + } + if (attr->val != NULL) { + xmlSchemaFreeValue(attr->val); + attr->val = NULL; + } + memset(attr, 0, sizeof(xmlSchemaAttrInfo)); + } + vctxt->nbAttrInfos = 0; +} + +/* +* 3.4.4 Complex Type Definition Validation Rules +* Element Locally Valid (Complex Type) (cvc-complex-type) +* 3.2.4 Attribute Declaration Validation Rules +* Validation Rule: Attribute Locally Valid (cvc-attribute) +* Attribute Locally Valid (Use) (cvc-au) +* +* Only "assessed" attribute information items will be visible to +* IDCs. I.e. not "lax" (without declaration) and "skip" wild attributes. +*/ +static int +xmlSchemaVAttributesComplex(xmlSchemaValidCtxtPtr vctxt) +{ + xmlSchemaTypePtr type = vctxt->inode->typeDef; + xmlSchemaItemListPtr attrUseList; + xmlSchemaAttributeUsePtr attrUse = NULL; + xmlSchemaAttributePtr attrDecl = NULL; + xmlSchemaAttrInfoPtr iattr, tmpiattr; + int i, j, found, nbAttrs, nbUses; + int xpathRes = 0, res, wildIDs = 0, fixed; + xmlNodePtr defAttrOwnerElem = NULL; + + /* + * SPEC (cvc-attribute) + * (1) "The declaration must not be ·absent· (see Missing + * Sub-components (§5.3) for how this can fail to be + * the case)." + * (2) "Its {type definition} must not be absent." + * + * NOTE (1) + (2): This is not handled here, since we currently do not + * allow validation against schemas which have missing sub-components. + * + * SPEC (cvc-complex-type) + * (3) "For each attribute information item in the element information + * item's [attributes] excepting those whose [namespace name] is + * identical to http://www.w3.org/2001/XMLSchema-instance and whose + * [local name] is one of type, nil, schemaLocation or + * noNamespaceSchemaLocation, the appropriate case among the following + * must be true: + * + */ + attrUseList = (xmlSchemaItemListPtr) type->attrUses; + /* + * @nbAttrs is the number of attributes present in the instance. + */ + nbAttrs = vctxt->nbAttrInfos; + if (attrUseList != NULL) + nbUses = attrUseList->nbItems; + else + nbUses = 0; + for (i = 0; i < nbUses; i++) { + found = 0; + attrUse = attrUseList->items[i]; + attrDecl = WXS_ATTRUSE_DECL(attrUse); + for (j = 0; j < nbAttrs; j++) { + iattr = vctxt->attrInfos[j]; + /* + * SPEC (cvc-complex-type) (3) + * Skip meta attributes. + */ + if (iattr->metaType) + continue; + if (iattr->localName[0] != attrDecl->name[0]) + continue; + if (!xmlStrEqual(iattr->localName, attrDecl->name)) + continue; + if (!xmlStrEqual(iattr->nsName, attrDecl->targetNamespace)) + continue; + found = 1; + /* + * SPEC (cvc-complex-type) + * (3.1) "If there is among the {attribute uses} an attribute + * use with an {attribute declaration} whose {name} matches + * the attribute information item's [local name] and whose + * {target namespace} is identical to the attribute information + * item's [namespace name] (where an ·absent· {target namespace} + * is taken to be identical to a [namespace name] with no value), + * then the attribute information must be ·valid· with respect + * to that attribute use as per Attribute Locally Valid (Use) + * (§3.5.4). In this case the {attribute declaration} of that + * attribute use is the ·context-determined declaration· for the + * attribute information item with respect to Schema-Validity + * Assessment (Attribute) (§3.2.4) and + * Assessment Outcome (Attribute) (§3.2.5). + */ + iattr->state = XML_SCHEMAS_ATTR_ASSESSED; + iattr->use = attrUse; + /* + * Context-determined declaration. + */ + iattr->decl = attrDecl; + iattr->typeDef = attrDecl->subtypes; + break; + } + + if (found) + continue; + + if (attrUse->occurs == XML_SCHEMAS_ATTR_USE_REQUIRED) { + /* + * Handle non-existent, required attributes. + * + * SPEC (cvc-complex-type) + * (4) "The {attribute declaration} of each attribute use in + * the {attribute uses} whose {required} is true matches one + * of the attribute information items in the element information + * item's [attributes] as per clause 3.1 above." + */ + tmpiattr = xmlSchemaGetFreshAttrInfo(vctxt); + if (tmpiattr == NULL) { + VERROR_INT( + "xmlSchemaVAttributesComplex", + "calling xmlSchemaGetFreshAttrInfo()"); + return (-1); + } + tmpiattr->state = XML_SCHEMAS_ATTR_ERR_MISSING; + tmpiattr->use = attrUse; + tmpiattr->decl = attrDecl; + } else if ((attrUse->occurs == XML_SCHEMAS_ATTR_USE_OPTIONAL) && + ((attrUse->defValue != NULL) || + (attrDecl->defValue != NULL))) { + /* + * Handle non-existent, optional, default/fixed attributes. + */ + tmpiattr = xmlSchemaGetFreshAttrInfo(vctxt); + if (tmpiattr == NULL) { + VERROR_INT( + "xmlSchemaVAttributesComplex", + "calling xmlSchemaGetFreshAttrInfo()"); + return (-1); + } + tmpiattr->state = XML_SCHEMAS_ATTR_DEFAULT; + tmpiattr->use = attrUse; + tmpiattr->decl = attrDecl; + tmpiattr->typeDef = attrDecl->subtypes; + tmpiattr->localName = attrDecl->name; + tmpiattr->nsName = attrDecl->targetNamespace; + } + } + + if (vctxt->nbAttrInfos == 0) + return (0); + nbUses = vctxt->nbAttrInfos; + /* + * Validate against the wildcard. + */ + if (type->attributeWildcard != NULL) { + /* + * SPEC (cvc-complex-type) + * (3.2.1) "There must be an {attribute wildcard}." + */ + for (i = 0; i < nbAttrs; i++) { + iattr = vctxt->attrInfos[i]; + /* + * SPEC (cvc-complex-type) (3) + * Skip meta attributes. + */ + if (iattr->state != XML_SCHEMAS_ATTR_UNKNOWN) + continue; + /* + * SPEC (cvc-complex-type) + * (3.2.2) "The attribute information item must be ·valid· with + * respect to it as defined in Item Valid (Wildcard) (§3.10.4)." + * + * SPEC Item Valid (Wildcard) (cvc-wildcard) + * "... its [namespace name] must be ·valid· with respect to + * the wildcard constraint, as defined in Wildcard allows + * Namespace Name (§3.10.4)." + */ + if (xmlSchemaCheckCVCWildcardNamespace(type->attributeWildcard, + iattr->nsName) == 0) { + /* + * Handle processContents. + * + * SPEC (cvc-wildcard): + * processContents | context-determined declaration: + * "strict" "mustFind" + * "lax" "none" + * "skip" "skip" + */ + if (type->attributeWildcard->processContents == + XML_SCHEMAS_ANY_SKIP) { + /* + * context-determined declaration = "skip" + * + * SPEC PSVI Assessment Outcome (Attribute) + * [validity] = "notKnown" + * [validation attempted] = "none" + */ + iattr->state = XML_SCHEMAS_ATTR_WILD_SKIP; + continue; + } + /* + * Find an attribute declaration. + */ + iattr->decl = xmlSchemaGetAttributeDecl(vctxt->schema, + iattr->localName, iattr->nsName); + if (iattr->decl != NULL) { + iattr->state = XML_SCHEMAS_ATTR_ASSESSED; + /* + * SPEC (cvc-complex-type) + * (5) "Let [Definition:] the wild IDs be the set of + * all attribute information item to which clause 3.2 + * applied and whose ·validation· resulted in a + * ·context-determined declaration· of mustFind or no + * ·context-determined declaration· at all, and whose + * [local name] and [namespace name] resolve (as + * defined by QName resolution (Instance) (§3.15.4)) to + * an attribute declaration whose {type definition} is + * or is derived from ID. Then all of the following + * must be true:" + */ + iattr->typeDef = WXS_ATTR_TYPEDEF(iattr->decl); + if (xmlSchemaIsDerivedFromBuiltInType( + iattr->typeDef, XML_SCHEMAS_ID)) { + /* + * SPEC (5.1) "There must be no more than one + * item in ·wild IDs·." + */ + if (wildIDs != 0) { + /* VAL TODO */ + iattr->state = XML_SCHEMAS_ATTR_ERR_WILD_DUPLICATE_ID; + TODO + continue; + } + wildIDs++; + /* + * SPEC (cvc-complex-type) + * (5.2) "If ·wild IDs· is non-empty, there must not + * be any attribute uses among the {attribute uses} + * whose {attribute declaration}'s {type definition} + * is or is derived from ID." + */ + for (j = 0; j < attrUseList->nbItems; j++) { + if (xmlSchemaIsDerivedFromBuiltInType( + WXS_ATTRUSE_TYPEDEF(attrUseList->items[j]), + XML_SCHEMAS_ID)) { + /* URGENT VAL TODO: implement */ + iattr->state = XML_SCHEMAS_ATTR_ERR_WILD_AND_USE_ID; + TODO + break; + } + } + } + } else if (type->attributeWildcard->processContents == + XML_SCHEMAS_ANY_LAX) { + iattr->state = XML_SCHEMAS_ATTR_WILD_LAX_NO_DECL; + /* + * SPEC PSVI Assessment Outcome (Attribute) + * [validity] = "notKnown" + * [validation attempted] = "none" + */ + } else { + iattr->state = XML_SCHEMAS_ATTR_ERR_WILD_STRICT_NO_DECL; + } + } + } + } + + if (vctxt->nbAttrInfos == 0) + return (0); + + /* + * Get the owner element; needed for creation of default attributes. + * This fixes bug #341337, reported by David Grohmann. + */ + if (vctxt->options & XML_SCHEMA_VAL_VC_I_CREATE) { + xmlSchemaNodeInfoPtr ielem = vctxt->elemInfos[vctxt->depth]; + if (ielem && ielem->node && ielem->node->doc) + defAttrOwnerElem = ielem->node; + } + /* + * Validate values, create default attributes, evaluate IDCs. + */ + for (i = 0; i < vctxt->nbAttrInfos; i++) { + iattr = vctxt->attrInfos[i]; + /* + * VAL TODO: Note that we won't try to resolve IDCs to + * "lax" and "skip" validated attributes. Check what to + * do in this case. + */ + if ((iattr->state != XML_SCHEMAS_ATTR_ASSESSED) && + (iattr->state != XML_SCHEMAS_ATTR_DEFAULT)) + continue; + /* + * VAL TODO: What to do if the type definition is missing? + */ + if (iattr->typeDef == NULL) { + iattr->state = XML_SCHEMAS_ATTR_ERR_NO_TYPE; + continue; + } + + ACTIVATE_ATTRIBUTE(iattr); + fixed = 0; + xpathRes = 0; + + if (vctxt->xpathStates != NULL) { + /* + * Evaluate IDCs. + */ + xpathRes = xmlSchemaXPathEvaluate(vctxt, + XML_ATTRIBUTE_NODE); + if (xpathRes == -1) { + VERROR_INT("xmlSchemaVAttributesComplex", + "calling xmlSchemaXPathEvaluate()"); + goto internal_error; + } + } + + if (iattr->state == XML_SCHEMAS_ATTR_DEFAULT) { + /* + * Default/fixed attributes. + * We need the value only if we need to resolve IDCs or + * will create default attributes. + */ + if ((xpathRes) || (defAttrOwnerElem)) { + if (iattr->use->defValue != NULL) { + iattr->value = (xmlChar *) iattr->use->defValue; + iattr->val = iattr->use->defVal; + } else { + iattr->value = (xmlChar *) iattr->decl->defValue; + iattr->val = iattr->decl->defVal; + } + /* + * IDCs will consume the precomputed default value, + * so we need to clone it. + */ + if (iattr->val == NULL) { + VERROR_INT("xmlSchemaVAttributesComplex", + "default/fixed value on an attribute use was " + "not precomputed"); + goto internal_error; + } + iattr->val = xmlSchemaCopyValue(iattr->val); + if (iattr->val == NULL) { + VERROR_INT("xmlSchemaVAttributesComplex", + "calling xmlSchemaCopyValue()"); + goto internal_error; + } + } + /* + * PSVI: Add the default attribute to the current element. + * VAL TODO: Should we use the *normalized* value? This currently + * uses the *initial* value. + */ + + if (defAttrOwnerElem) { + xmlChar *normValue; + const xmlChar *value; + + value = iattr->value; + /* + * Normalize the value. + */ + normValue = xmlSchemaNormalizeValue(iattr->typeDef, + iattr->value); + if (normValue != NULL) + value = BAD_CAST normValue; + + if (iattr->nsName == NULL) { + if (xmlNewProp(defAttrOwnerElem, + iattr->localName, value) == NULL) { + VERROR_INT("xmlSchemaVAttributesComplex", + "callling xmlNewProp()"); + if (normValue != NULL) + xmlFree(normValue); + goto internal_error; + } + } else { + xmlNsPtr ns; + + ns = xmlSearchNsByHref(defAttrOwnerElem->doc, + defAttrOwnerElem, iattr->nsName); + if (ns == NULL) { + xmlChar prefix[12]; + int counter = 0; + + /* + * Create a namespace declaration on the validation + * root node if no namespace declaration is in scope. + */ + do { + snprintf((char *) prefix, 12, "p%d", counter++); + ns = xmlSearchNs(defAttrOwnerElem->doc, + defAttrOwnerElem, BAD_CAST prefix); + if (counter > 1000) { + VERROR_INT( + "xmlSchemaVAttributesComplex", + "could not compute a ns prefix for a " + "default/fixed attribute"); + if (normValue != NULL) + xmlFree(normValue); + goto internal_error; + } + } while (ns != NULL); + ns = xmlNewNs(vctxt->validationRoot, + iattr->nsName, BAD_CAST prefix); + } + /* + * TODO: + * http://lists.w3.org/Archives/Public/www-xml-schema-comments/2005JulSep/0406.html + * If we have QNames: do we need to ensure there's a + * prefix defined for the QName? + */ + xmlNewNsProp(defAttrOwnerElem, ns, iattr->localName, value); + } + if (normValue != NULL) + xmlFree(normValue); + } + /* + * Go directly to IDC evaluation. + */ + goto eval_idcs; + } + /* + * Validate the value. + */ + if (vctxt->value != NULL) { + /* + * Free last computed value; just for safety reasons. + */ + xmlSchemaFreeValue(vctxt->value); + vctxt->value = NULL; + } + /* + * Note that the attribute *use* can be unavailable, if + * the attribute was a wild attribute. + */ + if ((iattr->decl->flags & XML_SCHEMAS_ATTR_FIXED) || + ((iattr->use != NULL) && + (iattr->use->flags & XML_SCHEMAS_ATTR_FIXED))) + fixed = 1; + else + fixed = 0; + /* + * SPEC (cvc-attribute) + * (3) "The item's ·normalized value· must be locally ·valid· + * with respect to that {type definition} as per + * String Valid (§3.14.4)." + * + * VAL TODO: Do we already have the + * "normalized attribute value" here? + */ + if (xpathRes || fixed) { + iattr->flags |= XML_SCHEMA_NODE_INFO_VALUE_NEEDED; + /* + * Request a computed value. + */ + res = xmlSchemaVCheckCVCSimpleType( + ACTXT_CAST vctxt, + iattr->node, iattr->typeDef, iattr->value, &(iattr->val), + 1, 1, 0); + } else { + res = xmlSchemaVCheckCVCSimpleType( + ACTXT_CAST vctxt, + iattr->node, iattr->typeDef, iattr->value, NULL, + 1, 0, 0); + } + + if (res != 0) { + if (res == -1) { + VERROR_INT("xmlSchemaVAttributesComplex", + "calling xmlSchemaStreamValidateSimpleTypeValue()"); + goto internal_error; + } + iattr->state = XML_SCHEMAS_ATTR_INVALID_VALUE; + /* + * SPEC PSVI Assessment Outcome (Attribute) + * [validity] = "invalid" + */ + goto eval_idcs; + } + + if (fixed) { + /* + * SPEC Attribute Locally Valid (Use) (cvc-au) + * "For an attribute information item to be·valid· + * with respect to an attribute use its *normalized* + * value· must match the *canonical* lexical + * representation of the attribute use's {value + * constraint}value, if it is present and fixed." + * + * VAL TODO: The requirement for the *canonical* value + * will be removed in XML Schema 1.1. + */ + /* + * SPEC Attribute Locally Valid (cvc-attribute) + * (4) "The item's *actual* value· must match the *value* of + * the {value constraint}, if it is present and fixed." + */ + if (iattr->val == NULL) { + /* VAL TODO: A value was not precomputed. */ + TODO + goto eval_idcs; + } + if ((iattr->use != NULL) && + (iattr->use->defValue != NULL)) { + if (iattr->use->defVal == NULL) { + /* VAL TODO: A default value was not precomputed. */ + TODO + goto eval_idcs; + } + iattr->vcValue = iattr->use->defValue; + /* + if (xmlSchemaCompareValuesWhtsp(attr->val, + (xmlSchemaWhitespaceValueType) ws, + attr->use->defVal, + (xmlSchemaWhitespaceValueType) ws) != 0) { + */ + if (! xmlSchemaAreValuesEqual(iattr->val, iattr->use->defVal)) + iattr->state = XML_SCHEMAS_ATTR_ERR_FIXED_VALUE; + } else { + if (iattr->decl->defVal == NULL) { + /* VAL TODO: A default value was not precomputed. */ + TODO + goto eval_idcs; + } + iattr->vcValue = iattr->decl->defValue; + /* + if (xmlSchemaCompareValuesWhtsp(attr->val, + (xmlSchemaWhitespaceValueType) ws, + attrDecl->defVal, + (xmlSchemaWhitespaceValueType) ws) != 0) { + */ + if (! xmlSchemaAreValuesEqual(iattr->val, iattr->decl->defVal)) + iattr->state = XML_SCHEMAS_ATTR_ERR_FIXED_VALUE; + } + /* + * [validity] = "valid" + */ + } +eval_idcs: + /* + * Evaluate IDCs. + */ + if (xpathRes) { + if (xmlSchemaXPathProcessHistory(vctxt, + vctxt->depth +1) == -1) { + VERROR_INT("xmlSchemaVAttributesComplex", + "calling xmlSchemaXPathEvaluate()"); + goto internal_error; + } + } else if (vctxt->xpathStates != NULL) + xmlSchemaXPathPop(vctxt); + } + + /* + * Report errors. + */ + for (i = 0; i < vctxt->nbAttrInfos; i++) { + iattr = vctxt->attrInfos[i]; + if ((iattr->state == XML_SCHEMAS_ATTR_META) || + (iattr->state == XML_SCHEMAS_ATTR_ASSESSED) || + (iattr->state == XML_SCHEMAS_ATTR_WILD_SKIP) || + (iattr->state == XML_SCHEMAS_ATTR_WILD_LAX_NO_DECL)) + continue; + ACTIVATE_ATTRIBUTE(iattr); + switch (iattr->state) { + case XML_SCHEMAS_ATTR_ERR_MISSING: { + xmlChar *str = NULL; + ACTIVATE_ELEM; + xmlSchemaCustomErr(ACTXT_CAST vctxt, + XML_SCHEMAV_CVC_COMPLEX_TYPE_4, NULL, NULL, + "The attribute '%s' is required but missing", + xmlSchemaFormatQName(&str, + iattr->decl->targetNamespace, + iattr->decl->name), + NULL); + FREE_AND_NULL(str) + break; + } + case XML_SCHEMAS_ATTR_ERR_NO_TYPE: + VERROR(XML_SCHEMAV_CVC_ATTRIBUTE_2, NULL, + "The type definition is absent"); + break; + case XML_SCHEMAS_ATTR_ERR_FIXED_VALUE: + xmlSchemaCustomErr(ACTXT_CAST vctxt, + XML_SCHEMAV_CVC_AU, NULL, NULL, + "The value '%s' does not match the fixed " + "value constraint '%s'", + iattr->value, iattr->vcValue); + break; + case XML_SCHEMAS_ATTR_ERR_WILD_STRICT_NO_DECL: + VERROR(XML_SCHEMAV_CVC_WILDCARD, NULL, + "No matching global attribute declaration available, but " + "demanded by the strict wildcard"); + break; + case XML_SCHEMAS_ATTR_UNKNOWN: + if (iattr->metaType) + break; + /* + * MAYBE VAL TODO: One might report different error messages + * for the following errors. + */ + if (type->attributeWildcard == NULL) { + xmlSchemaIllegalAttrErr(ACTXT_CAST vctxt, + XML_SCHEMAV_CVC_COMPLEX_TYPE_3_2_1, iattr, NULL); + } else { + xmlSchemaIllegalAttrErr(ACTXT_CAST vctxt, + XML_SCHEMAV_CVC_COMPLEX_TYPE_3_2_2, iattr, NULL); + } + break; + default: + break; + } + } + + ACTIVATE_ELEM; + return (0); +internal_error: + ACTIVATE_ELEM; + return (-1); +} + +static int +xmlSchemaValidateElemWildcard(xmlSchemaValidCtxtPtr vctxt, + int *skip) +{ + xmlSchemaWildcardPtr wild = (xmlSchemaWildcardPtr) vctxt->inode->decl; + /* + * The namespace of the element was already identified to be + * matching the wildcard. + */ + if ((skip == NULL) || (wild == NULL) || + (wild->type != XML_SCHEMA_TYPE_ANY)) { + VERROR_INT("xmlSchemaValidateElemWildcard", + "bad arguments"); + return (-1); + } + *skip = 0; + if (wild->processContents == XML_SCHEMAS_ANY_SKIP) { + /* + * URGENT VAL TODO: Either we need to position the stream to the + * next sibling, or walk the whole subtree. + */ + *skip = 1; + return (0); + } + { + xmlSchemaElementPtr decl = NULL; + + decl = xmlSchemaGetElem(vctxt->schema, + vctxt->inode->localName, vctxt->inode->nsName); + if (decl != NULL) { + vctxt->inode->decl = decl; + return (0); + } + } + if (wild->processContents == XML_SCHEMAS_ANY_STRICT) { + /* VAL TODO: Change to proper error code. */ + VERROR(XML_SCHEMAV_CVC_ELT_1, NULL, /* WXS_BASIC_CAST wild */ + "No matching global element declaration available, but " + "demanded by the strict wildcard"); + return (vctxt->err); + } + if (vctxt->nbAttrInfos != 0) { + xmlSchemaAttrInfoPtr iattr; + /* + * SPEC Validation Rule: Schema-Validity Assessment (Element) + * (1.2.1.2.1) - (1.2.1.2.3 ) + * + * Use the xsi:type attribute for the type definition. + */ + iattr = xmlSchemaGetMetaAttrInfo(vctxt, + XML_SCHEMA_ATTR_INFO_META_XSI_TYPE); + if (iattr != NULL) { + if (xmlSchemaProcessXSIType(vctxt, iattr, + &(vctxt->inode->typeDef), NULL) == -1) { + VERROR_INT("xmlSchemaValidateElemWildcard", + "calling xmlSchemaProcessXSIType() to " + "process the attribute 'xsi:nil'"); + return (-1); + } + /* + * Don't return an error on purpose. + */ + return (0); + } + } + /* + * SPEC Validation Rule: Schema-Validity Assessment (Element) + * + * Fallback to "anyType". + */ + vctxt->inode->typeDef = + xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYTYPE); + return (0); +} + +/* +* xmlSchemaCheckCOSValidDefault: +* +* This will be called if: not nilled, no content and a default/fixed +* value is provided. +*/ + +static int +xmlSchemaCheckCOSValidDefault(xmlSchemaValidCtxtPtr vctxt, + const xmlChar *value, + xmlSchemaValPtr *val) +{ + int ret = 0; + xmlSchemaNodeInfoPtr inode = vctxt->inode; + + /* + * cos-valid-default: + * Schema Component Constraint: Element Default Valid (Immediate) + * For a string to be a valid default with respect to a type + * definition the appropriate case among the following must be true: + */ + if WXS_IS_COMPLEX(inode->typeDef) { + /* + * Complex type. + * + * SPEC (2.1) "its {content type} must be a simple type definition + * or mixed." + * SPEC (2.2.2) "If the {content type} is mixed, then the {content + * type}'s particle must be ·emptiable· as defined by + * Particle Emptiable (§3.9.6)." + */ + if ((! WXS_HAS_SIMPLE_CONTENT(inode->typeDef)) && + ((! WXS_HAS_MIXED_CONTENT(inode->typeDef)) || + (! WXS_EMPTIABLE(inode->typeDef)))) { + ret = XML_SCHEMAP_COS_VALID_DEFAULT_2_1; + /* NOTE that this covers (2.2.2) as well. */ + VERROR(ret, NULL, + "For a string to be a valid default, the type definition " + "must be a simple type or a complex type with simple content " + "or mixed content and a particle emptiable"); + return(ret); + } + } + /* + * 1 If the type definition is a simple type definition, then the string + * must be ·valid· with respect to that definition as defined by String + * Valid (§3.14.4). + * + * AND + * + * 2.2.1 If the {content type} is a simple type definition, then the + * string must be ·valid· with respect to that simple type definition + * as defined by String Valid (§3.14.4). + */ + if (WXS_IS_SIMPLE(inode->typeDef)) { + + ret = xmlSchemaVCheckCVCSimpleType(ACTXT_CAST vctxt, + NULL, inode->typeDef, value, val, 1, 1, 0); + + } else if (WXS_HAS_SIMPLE_CONTENT(inode->typeDef)) { + + ret = xmlSchemaVCheckCVCSimpleType(ACTXT_CAST vctxt, + NULL, inode->typeDef->contentTypeDef, value, val, 1, 1, 0); + } + if (ret < 0) { + VERROR_INT("xmlSchemaCheckCOSValidDefault", + "calling xmlSchemaVCheckCVCSimpleType()"); + } + return (ret); +} + +static void +xmlSchemaVContentModelCallback(xmlSchemaValidCtxtPtr vctxt ATTRIBUTE_UNUSED, + const xmlChar * name ATTRIBUTE_UNUSED, + xmlSchemaElementPtr item, + xmlSchemaNodeInfoPtr inode) +{ + inode->decl = item; +#ifdef DEBUG_CONTENT + { + xmlChar *str = NULL; + + if (item->type == XML_SCHEMA_TYPE_ELEMENT) { + xmlGenericError(xmlGenericErrorContext, + "AUTOMATON callback for '%s' [declaration]\n", + xmlSchemaFormatQName(&str, + inode->localName, inode->nsName)); + } else { + xmlGenericError(xmlGenericErrorContext, + "AUTOMATON callback for '%s' [wildcard]\n", + xmlSchemaFormatQName(&str, + inode->localName, inode->nsName)); + + } + FREE_AND_NULL(str) + } +#endif +} + +static int +xmlSchemaValidatorPushElem(xmlSchemaValidCtxtPtr vctxt) +{ + vctxt->inode = xmlSchemaGetFreshElemInfo(vctxt); + if (vctxt->inode == NULL) { + VERROR_INT("xmlSchemaValidatorPushElem", + "calling xmlSchemaGetFreshElemInfo()"); + return (-1); + } + vctxt->nbAttrInfos = 0; + return (0); +} + +static int +xmlSchemaVCheckINodeDataType(xmlSchemaValidCtxtPtr vctxt, + xmlSchemaNodeInfoPtr inode, + xmlSchemaTypePtr type, + const xmlChar *value) +{ + if (inode->flags & XML_SCHEMA_NODE_INFO_VALUE_NEEDED) + return (xmlSchemaVCheckCVCSimpleType( + ACTXT_CAST vctxt, NULL, + type, value, &(inode->val), 1, 1, 0)); + else + return (xmlSchemaVCheckCVCSimpleType( + ACTXT_CAST vctxt, NULL, + type, value, NULL, 1, 0, 0)); +} + + + +/* +* Process END of element. +*/ +static int +xmlSchemaValidatorPopElem(xmlSchemaValidCtxtPtr vctxt) +{ + int ret = 0; + xmlSchemaNodeInfoPtr inode = vctxt->inode; + + if (vctxt->nbAttrInfos != 0) + xmlSchemaClearAttrInfos(vctxt); + if (inode->flags & XML_SCHEMA_NODE_INFO_ERR_NOT_EXPECTED) { + /* + * This element was not expected; + * we will not validate child elements of broken parents. + * Skip validation of all content of the parent. + */ + vctxt->skipDepth = vctxt->depth -1; + goto end_elem; + } + if ((inode->typeDef == NULL) || + (inode->flags & XML_SCHEMA_NODE_INFO_ERR_BAD_TYPE)) { + /* + * 1. the type definition might be missing if the element was + * error prone + * 2. it might be abstract. + */ + goto end_elem; + } + /* + * Check the content model. + */ + if ((inode->typeDef->contentType == XML_SCHEMA_CONTENT_MIXED) || + (inode->typeDef->contentType == XML_SCHEMA_CONTENT_ELEMENTS)) { + + /* + * Workaround for "anyType". + */ + if (inode->typeDef->builtInType == XML_SCHEMAS_ANYTYPE) + goto character_content; + + if ((inode->flags & XML_SCHEMA_ELEM_INFO_ERR_BAD_CONTENT) == 0) { + xmlChar *values[10]; + int terminal, nbval = 10, nbneg; + + if (inode->regexCtxt == NULL) { + /* + * Create the regex context. + */ + inode->regexCtxt = + xmlRegNewExecCtxt(inode->typeDef->contModel, + (xmlRegExecCallbacks) xmlSchemaVContentModelCallback, + vctxt); + if (inode->regexCtxt == NULL) { + VERROR_INT("xmlSchemaValidatorPopElem", + "failed to create a regex context"); + goto internal_error; + } +#ifdef DEBUG_AUTOMATA + xmlGenericError(xmlGenericErrorContext, + "AUTOMATON create on '%s'\n", inode->localName); +#endif + } + /* + * Get hold of the still expected content, since a further + * call to xmlRegExecPushString() will loose this information. + */ + xmlRegExecNextValues(inode->regexCtxt, + &nbval, &nbneg, &values[0], &terminal); + ret = xmlRegExecPushString(inode->regexCtxt, NULL, NULL); + if (ret <= 0) { + /* + * Still missing something. + */ + ret = 1; + inode->flags |= + XML_SCHEMA_ELEM_INFO_ERR_BAD_CONTENT; + xmlSchemaComplexTypeErr(ACTXT_CAST vctxt, + XML_SCHEMAV_ELEMENT_CONTENT, NULL, NULL, + "Missing child element(s)", + nbval, nbneg, values); +#ifdef DEBUG_AUTOMATA + xmlGenericError(xmlGenericErrorContext, + "AUTOMATON missing ERROR on '%s'\n", + inode->localName); +#endif + } else { + /* + * Content model is satisfied. + */ + ret = 0; +#ifdef DEBUG_AUTOMATA + xmlGenericError(xmlGenericErrorContext, + "AUTOMATON succeeded on '%s'\n", + inode->localName); +#endif + } + + } + } + if (inode->typeDef->contentType == XML_SCHEMA_CONTENT_ELEMENTS) + goto end_elem; + +character_content: + + if (vctxt->value != NULL) { + xmlSchemaFreeValue(vctxt->value); + vctxt->value = NULL; + } + /* + * Check character content. + */ + if (inode->decl == NULL) { + /* + * Speedup if no declaration exists. + */ + if (WXS_IS_SIMPLE(inode->typeDef)) { + ret = xmlSchemaVCheckINodeDataType(vctxt, + inode, inode->typeDef, inode->value); + } else if (WXS_HAS_SIMPLE_CONTENT(inode->typeDef)) { + ret = xmlSchemaVCheckINodeDataType(vctxt, + inode, inode->typeDef->contentTypeDef, + inode->value); + } + if (ret < 0) { + VERROR_INT("xmlSchemaValidatorPopElem", + "calling xmlSchemaVCheckCVCSimpleType()"); + goto internal_error; + } + goto end_elem; + } + /* + * cvc-elt (3.3.4) : 5 + * The appropriate case among the following must be true: + */ + /* + * cvc-elt (3.3.4) : 5.1 + * If the declaration has a {value constraint}, + * the item has neither element nor character [children] and + * clause 3.2 has not applied, then all of the following must be true: + */ + if ((inode->decl->value != NULL) && + (inode->flags & XML_SCHEMA_ELEM_INFO_EMPTY) && + (! INODE_NILLED(inode))) { + /* + * cvc-elt (3.3.4) : 5.1.1 + * If the ·actual type definition· is a ·local type definition· + * then the canonical lexical representation of the {value constraint} + * value must be a valid default for the ·actual type definition· as + * defined in Element Default Valid (Immediate) (§3.3.6). + */ + /* + * NOTE: 'local' above means types acquired by xsi:type. + * NOTE: Although the *canonical* value is stated, it is not + * relevant if canonical or not. Additionally XML Schema 1.1 + * will removed this requirement as well. + */ + if (inode->flags & XML_SCHEMA_ELEM_INFO_LOCAL_TYPE) { + + ret = xmlSchemaCheckCOSValidDefault(vctxt, + inode->decl->value, &(inode->val)); + if (ret != 0) { + if (ret < 0) { + VERROR_INT("xmlSchemaValidatorPopElem", + "calling xmlSchemaCheckCOSValidDefault()"); + goto internal_error; + } + goto end_elem; + } + /* + * Stop here, to avoid redundant validation of the value + * (see following). + */ + goto default_psvi; + } + /* + * cvc-elt (3.3.4) : 5.1.2 + * The element information item with the canonical lexical + * representation of the {value constraint} value used as its + * ·normalized value· must be ·valid· with respect to the + * ·actual type definition· as defined by Element Locally Valid (Type) + * (§3.3.4). + */ + if (WXS_IS_SIMPLE(inode->typeDef)) { + ret = xmlSchemaVCheckINodeDataType(vctxt, + inode, inode->typeDef, inode->decl->value); + } else if (WXS_HAS_SIMPLE_CONTENT(inode->typeDef)) { + ret = xmlSchemaVCheckINodeDataType(vctxt, + inode, inode->typeDef->contentTypeDef, + inode->decl->value); + } + if (ret != 0) { + if (ret < 0) { + VERROR_INT("xmlSchemaValidatorPopElem", + "calling xmlSchemaVCheckCVCSimpleType()"); + goto internal_error; + } + goto end_elem; + } + +default_psvi: + /* + * PSVI: Create a text node on the instance element. + */ + if ((vctxt->options & XML_SCHEMA_VAL_VC_I_CREATE) && + (inode->node != NULL)) { + xmlNodePtr textChild; + xmlChar *normValue; + /* + * VAL TODO: Normalize the value. + */ + normValue = xmlSchemaNormalizeValue(inode->typeDef, + inode->decl->value); + if (normValue != NULL) { + textChild = xmlNewText(BAD_CAST normValue); + xmlFree(normValue); + } else + textChild = xmlNewText(inode->decl->value); + if (textChild == NULL) { + VERROR_INT("xmlSchemaValidatorPopElem", + "calling xmlNewText()"); + goto internal_error; + } else + xmlAddChild(inode->node, textChild); + } + + } else if (! INODE_NILLED(inode)) { + /* + * 5.2.1 The element information item must be ·valid· with respect + * to the ·actual type definition· as defined by Element Locally + * Valid (Type) (§3.3.4). + */ + if (WXS_IS_SIMPLE(inode->typeDef)) { + /* + * SPEC (cvc-type) (3.1) + * "If the type definition is a simple type definition, ..." + * (3.1.3) "If clause 3.2 of Element Locally Valid + * (Element) (§3.3.4) did not apply, then the ·normalized value· + * must be ·valid· with respect to the type definition as defined + * by String Valid (§3.14.4). + */ + ret = xmlSchemaVCheckINodeDataType(vctxt, + inode, inode->typeDef, inode->value); + } else if (WXS_HAS_SIMPLE_CONTENT(inode->typeDef)) { + /* + * SPEC (cvc-type) (3.2) "If the type definition is a complex type + * definition, then the element information item must be + * ·valid· with respect to the type definition as per + * Element Locally Valid (Complex Type) (§3.4.4);" + * + * SPEC (cvc-complex-type) (2.2) + * "If the {content type} is a simple type definition, ... + * the ·normalized value· of the element information item is + * ·valid· with respect to that simple type definition as + * defined by String Valid (§3.14.4)." + */ + ret = xmlSchemaVCheckINodeDataType(vctxt, + inode, inode->typeDef->contentTypeDef, inode->value); + } + if (ret != 0) { + if (ret < 0) { + VERROR_INT("xmlSchemaValidatorPopElem", + "calling xmlSchemaVCheckCVCSimpleType()"); + goto internal_error; + } + goto end_elem; + } + /* + * 5.2.2 If there is a fixed {value constraint} and clause 3.2 has + * not applied, all of the following must be true: + */ + if ((inode->decl->value != NULL) && + (inode->decl->flags & XML_SCHEMAS_ELEM_FIXED)) { + + /* + * TODO: We will need a computed value, when comparison is + * done on computed values. + */ + /* + * 5.2.2.1 The element information item must have no element + * information item [children]. + */ + if (inode->flags & + XML_SCHEMA_ELEM_INFO_HAS_ELEM_CONTENT) { + ret = XML_SCHEMAV_CVC_ELT_5_2_2_1; + VERROR(ret, NULL, + "The content must not containt element nodes since " + "there is a fixed value constraint"); + goto end_elem; + } else { + /* + * 5.2.2.2 The appropriate case among the following must + * be true: + */ + if (WXS_HAS_MIXED_CONTENT(inode->typeDef)) { + /* + * 5.2.2.2.1 If the {content type} of the ·actual type + * definition· is mixed, then the *initial value* of the + * item must match the canonical lexical representation + * of the {value constraint} value. + * + * ... the *initial value* of an element information + * item is the string composed of, in order, the + * [character code] of each character information item in + * the [children] of that element information item. + */ + if (! xmlStrEqual(inode->value, inode->decl->value)){ + /* + * VAL TODO: Report invalid & expected values as well. + * VAL TODO: Implement the canonical stuff. + */ + ret = XML_SCHEMAV_CVC_ELT_5_2_2_2_1; + xmlSchemaCustomErr(ACTXT_CAST vctxt, + ret, NULL, NULL, + "The initial value '%s' does not match the fixed " + "value constraint '%s'", + inode->value, inode->decl->value); + goto end_elem; + } + } else if (WXS_HAS_SIMPLE_CONTENT(inode->typeDef)) { + /* + * 5.2.2.2.2 If the {content type} of the ·actual type + * definition· is a simple type definition, then the + * *actual value* of the item must match the canonical + * lexical representation of the {value constraint} value. + */ + /* + * VAL TODO: *actual value* is the normalized value, impl. + * this. + * VAL TODO: Report invalid & expected values as well. + * VAL TODO: Implement a comparison with the computed values. + */ + if (! xmlStrEqual(inode->value, + inode->decl->value)) { + ret = XML_SCHEMAV_CVC_ELT_5_2_2_2_2; + xmlSchemaCustomErr(ACTXT_CAST vctxt, + ret, NULL, NULL, + "The actual value '%s' does not match the fixed " + "value constraint '%s'", + inode->value, + inode->decl->value); + goto end_elem; + } + } + } + } + } + +end_elem: + if (vctxt->depth < 0) { + /* TODO: raise error? */ + return (0); + } + if (vctxt->depth == vctxt->skipDepth) + vctxt->skipDepth = -1; + /* + * Evaluate the history of XPath state objects. + */ + if (inode->appliedXPath && + (xmlSchemaXPathProcessHistory(vctxt, vctxt->depth) == -1)) + goto internal_error; + /* + * MAYBE TODO: + * SPEC (6) "The element information item must be ·valid· with + * respect to each of the {identity-constraint definitions} as per + * Identity-constraint Satisfied (§3.11.4)." + */ + /* + * PSVI TODO: If we expose IDC node-tables via PSVI then the tables + * need to be built in any case. + * We will currently build IDC node-tables and bubble them only if + * keyrefs do exist. + */ + + /* + * Add the current IDC target-nodes to the IDC node-tables. + */ + if ((inode->idcMatchers != NULL) && + (vctxt->hasKeyrefs || vctxt->createIDCNodeTables)) + { + if (xmlSchemaIDCFillNodeTables(vctxt, inode) == -1) + goto internal_error; + } + /* + * Validate IDC keyrefs. + */ + if (vctxt->inode->hasKeyrefs) + if (xmlSchemaCheckCVCIDCKeyRef(vctxt) == -1) + goto internal_error; + /* + * Merge/free the IDC table. + */ + if (inode->idcTable != NULL) { +#ifdef DEBUG_IDC_NODE_TABLE + xmlSchemaDebugDumpIDCTable(stdout, + inode->nsName, + inode->localName, + inode->idcTable); +#endif + if ((vctxt->depth > 0) && + (vctxt->hasKeyrefs || vctxt->createIDCNodeTables)) + { + /* + * Merge the IDC node table with the table of the parent node. + */ + if (xmlSchemaBubbleIDCNodeTables(vctxt) == -1) + goto internal_error; + } + } + /* + * Clear the current ielem. + * VAL TODO: Don't free the PSVI IDC tables if they are + * requested for the PSVI. + */ + xmlSchemaClearElemInfo(inode); + /* + * Skip further processing if we are on the validation root. + */ + if (vctxt->depth == 0) { + vctxt->depth--; + vctxt->inode = NULL; + return (0); + } + /* + * Reset the keyrefDepth if needed. + */ + if (vctxt->aidcs != NULL) { + xmlSchemaIDCAugPtr aidc = vctxt->aidcs; + do { + if (aidc->keyrefDepth == vctxt->depth) { + /* + * A 'keyrefDepth' of a key/unique IDC matches the current + * depth, this means that we are leaving the scope of the + * top-most keyref IDC which refers to this IDC. + */ + aidc->keyrefDepth = -1; + } + aidc = aidc->next; + } while (aidc != NULL); + } + vctxt->depth--; + vctxt->inode = vctxt->elemInfos[vctxt->depth]; + /* + * VAL TODO: 7 If the element information item is the ·validation root·, it must be + * ·valid· per Validation Root Valid (ID/IDREF) (§3.3.4). + */ + return (ret); + +internal_error: + vctxt->err = -1; + return (-1); +} + +/* +* 3.4.4 Complex Type Definition Validation Rules +* Validation Rule: Element Locally Valid (Complex Type) (cvc-complex-type) +*/ +static int +xmlSchemaValidateChildElem(xmlSchemaValidCtxtPtr vctxt) +{ + xmlSchemaNodeInfoPtr pielem; + xmlSchemaTypePtr ptype; + int ret = 0; + + if (vctxt->depth <= 0) { + VERROR_INT("xmlSchemaValidateChildElem", + "not intended for the validation root"); + return (-1); + } + pielem = vctxt->elemInfos[vctxt->depth -1]; + if (pielem->flags & XML_SCHEMA_ELEM_INFO_EMPTY) + pielem->flags ^= XML_SCHEMA_ELEM_INFO_EMPTY; + /* + * Handle 'nilled' elements. + */ + if (INODE_NILLED(pielem)) { + /* + * SPEC (cvc-elt) (3.3.4) : (3.2.1) + */ + ACTIVATE_PARENT_ELEM; + ret = XML_SCHEMAV_CVC_ELT_3_2_1; + VERROR(ret, NULL, + "Neither character nor element content is allowed, " + "because the element was 'nilled'"); + ACTIVATE_ELEM; + goto unexpected_elem; + } + + ptype = pielem->typeDef; + + if (ptype->builtInType == XML_SCHEMAS_ANYTYPE) { + /* + * Workaround for "anyType": we have currently no content model + * assigned for "anyType", so handle it explicitely. + * "anyType" has an unbounded, lax "any" wildcard. + */ + vctxt->inode->decl = xmlSchemaGetElem(vctxt->schema, + vctxt->inode->localName, + vctxt->inode->nsName); + + if (vctxt->inode->decl == NULL) { + xmlSchemaAttrInfoPtr iattr; + /* + * Process "xsi:type". + * SPEC (cvc-assess-elt) (1.2.1.2.1) - (1.2.1.2.3) + */ + iattr = xmlSchemaGetMetaAttrInfo(vctxt, + XML_SCHEMA_ATTR_INFO_META_XSI_TYPE); + if (iattr != NULL) { + ret = xmlSchemaProcessXSIType(vctxt, iattr, + &(vctxt->inode->typeDef), NULL); + if (ret != 0) { + if (ret == -1) { + VERROR_INT("xmlSchemaValidateChildElem", + "calling xmlSchemaProcessXSIType() to " + "process the attribute 'xsi:nil'"); + return (-1); + } + return (ret); + } + } else { + /* + * Fallback to "anyType". + * + * SPEC (cvc-assess-elt) + * "If the item cannot be ·strictly assessed·, [...] + * an element information item's schema validity may be laxly + * assessed if its ·context-determined declaration· is not + * skip by ·validating· with respect to the ·ur-type + * definition· as per Element Locally Valid (Type) (§3.3.4)." + */ + vctxt->inode->typeDef = + xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYTYPE); + } + } + return (0); + } + + switch (ptype->contentType) { + case XML_SCHEMA_CONTENT_EMPTY: + /* + * SPEC (2.1) "If the {content type} is empty, then the + * element information item has no character or element + * information item [children]." + */ + ACTIVATE_PARENT_ELEM + ret = XML_SCHEMAV_CVC_COMPLEX_TYPE_2_1; + VERROR(ret, NULL, + "Element content is not allowed, " + "because the content type is empty"); + ACTIVATE_ELEM + goto unexpected_elem; + break; + + case XML_SCHEMA_CONTENT_MIXED: + case XML_SCHEMA_CONTENT_ELEMENTS: { + xmlRegExecCtxtPtr regexCtxt; + xmlChar *values[10]; + int terminal, nbval = 10, nbneg; + + /* VAL TODO: Optimized "anyType" validation.*/ + + if (ptype->contModel == NULL) { + VERROR_INT("xmlSchemaValidateChildElem", + "type has elem content but no content model"); + return (-1); + } + /* + * Safety belf for evaluation if the cont. model was already + * examined to be invalid. + */ + if (pielem->flags & XML_SCHEMA_ELEM_INFO_ERR_BAD_CONTENT) { + VERROR_INT("xmlSchemaValidateChildElem", + "validating elem, but elem content is already invalid"); + return (-1); + } + + regexCtxt = pielem->regexCtxt; + if (regexCtxt == NULL) { + /* + * Create the regex context. + */ + regexCtxt = xmlRegNewExecCtxt(ptype->contModel, + (xmlRegExecCallbacks) xmlSchemaVContentModelCallback, + vctxt); + if (regexCtxt == NULL) { + VERROR_INT("xmlSchemaValidateChildElem", + "failed to create a regex context"); + return (-1); + } + pielem->regexCtxt = regexCtxt; +#ifdef DEBUG_AUTOMATA + xmlGenericError(xmlGenericErrorContext, "AUTOMATA create on '%s'\n", + pielem->localName); +#endif + } + + /* + * SPEC (2.4) "If the {content type} is element-only or mixed, + * then the sequence of the element information item's + * element information item [children], if any, taken in + * order, is ·valid· with respect to the {content type}'s + * particle, as defined in Element Sequence Locally Valid + * (Particle) (§3.9.4)." + */ + ret = xmlRegExecPushString2(regexCtxt, + vctxt->inode->localName, + vctxt->inode->nsName, + vctxt->inode); +#ifdef DEBUG_AUTOMATA + if (ret < 0) + xmlGenericError(xmlGenericErrorContext, + "AUTOMATON push ERROR for '%s' on '%s'\n", + vctxt->inode->localName, pielem->localName); + else + xmlGenericError(xmlGenericErrorContext, + "AUTOMATON push OK for '%s' on '%s'\n", + vctxt->inode->localName, pielem->localName); +#endif + if (vctxt->err == XML_SCHEMAV_INTERNAL) { + VERROR_INT("xmlSchemaValidateChildElem", + "calling xmlRegExecPushString2()"); + return (-1); + } + if (ret < 0) { + xmlRegExecErrInfo(regexCtxt, NULL, &nbval, &nbneg, + &values[0], &terminal); + xmlSchemaComplexTypeErr(ACTXT_CAST vctxt, + XML_SCHEMAV_ELEMENT_CONTENT, NULL,NULL, + "This element is not expected", + nbval, nbneg, values); + ret = vctxt->err; + goto unexpected_elem; + } else + ret = 0; + } + break; + case XML_SCHEMA_CONTENT_SIMPLE: + case XML_SCHEMA_CONTENT_BASIC: + ACTIVATE_PARENT_ELEM + if (WXS_IS_COMPLEX(ptype)) { + /* + * SPEC (cvc-complex-type) (2.2) + * "If the {content type} is a simple type definition, then + * the element information item has no element information + * item [children], ..." + */ + ret = XML_SCHEMAV_CVC_COMPLEX_TYPE_2_2; + VERROR(ret, NULL, "Element content is not allowed, " + "because the content type is a simple type definition"); + } else { + /* + * SPEC (cvc-type) (3.1.2) "The element information item must + * have no element information item [children]." + */ + ret = XML_SCHEMAV_CVC_TYPE_3_1_2; + VERROR(ret, NULL, "Element content is not allowed, " + "because the type definition is simple"); + } + ACTIVATE_ELEM + ret = vctxt->err; + goto unexpected_elem; + break; + + default: + break; + } + return (ret); +unexpected_elem: + /* + * Pop this element and set the skipDepth to skip + * all further content of the parent element. + */ + vctxt->skipDepth = vctxt->depth; + vctxt->inode->flags |= XML_SCHEMA_NODE_INFO_ERR_NOT_EXPECTED; + pielem->flags |= XML_SCHEMA_ELEM_INFO_ERR_BAD_CONTENT; + return (ret); +} + +#define XML_SCHEMA_PUSH_TEXT_PERSIST 1 +#define XML_SCHEMA_PUSH_TEXT_CREATED 2 +#define XML_SCHEMA_PUSH_TEXT_VOLATILE 3 + +static int +xmlSchemaVPushText(xmlSchemaValidCtxtPtr vctxt, + int nodeType, const xmlChar *value, int len, + int mode, int *consumed) +{ + /* + * Unfortunately we have to duplicate the text sometimes. + * OPTIMIZE: Maybe we could skip it, if: + * 1. content type is simple + * 2. whitespace is "collapse" + * 3. it consists of whitespace only + * + * Process character content. + */ + if (consumed != NULL) + *consumed = 0; + if (INODE_NILLED(vctxt->inode)) { + /* + * SPEC cvc-elt (3.3.4 - 3.2.1) + * "The element information item must have no character or + * element information item [children]." + */ + VERROR(XML_SCHEMAV_CVC_ELT_3_2_1, NULL, + "Neither character nor element content is allowed " + "because the element is 'nilled'"); + return (vctxt->err); + } + /* + * SPEC (2.1) "If the {content type} is empty, then the + * element information item has no character or element + * information item [children]." + */ + if (vctxt->inode->typeDef->contentType == + XML_SCHEMA_CONTENT_EMPTY) { + VERROR(XML_SCHEMAV_CVC_COMPLEX_TYPE_2_1, NULL, + "Character content is not allowed, " + "because the content type is empty"); + return (vctxt->err); + } + + if (vctxt->inode->typeDef->contentType == + XML_SCHEMA_CONTENT_ELEMENTS) { + if ((nodeType != XML_TEXT_NODE) || + (! xmlSchemaIsBlank((xmlChar *) value, len))) { + /* + * SPEC cvc-complex-type (2.3) + * "If the {content type} is element-only, then the + * element information item has no character information + * item [children] other than those whose [character + * code] is defined as a white space in [XML 1.0 (Second + * Edition)]." + */ + VERROR(XML_SCHEMAV_CVC_COMPLEX_TYPE_2_3, NULL, + "Character content other than whitespace is not allowed " + "because the content type is 'element-only'"); + return (vctxt->err); + } + return (0); + } + + if ((value == NULL) || (value[0] == 0)) + return (0); + /* + * Save the value. + * NOTE that even if the content type is *mixed*, we need the + * *initial value* for default/fixed value constraints. + */ + if ((vctxt->inode->typeDef->contentType == XML_SCHEMA_CONTENT_MIXED) && + ((vctxt->inode->decl == NULL) || + (vctxt->inode->decl->value == NULL))) + return (0); + + if (vctxt->inode->value == NULL) { + /* + * Set the value. + */ + switch (mode) { + case XML_SCHEMA_PUSH_TEXT_PERSIST: + /* + * When working on a tree. + */ + vctxt->inode->value = value; + break; + case XML_SCHEMA_PUSH_TEXT_CREATED: + /* + * When working with the reader. + * The value will be freed by the element info. + */ + vctxt->inode->value = value; + if (consumed != NULL) + *consumed = 1; + vctxt->inode->flags |= + XML_SCHEMA_NODE_INFO_FLAG_OWNED_VALUES; + break; + case XML_SCHEMA_PUSH_TEXT_VOLATILE: + /* + * When working with SAX. + * The value will be freed by the element info. + */ + if (len != -1) + vctxt->inode->value = BAD_CAST xmlStrndup(value, len); + else + vctxt->inode->value = BAD_CAST xmlStrdup(value); + vctxt->inode->flags |= + XML_SCHEMA_NODE_INFO_FLAG_OWNED_VALUES; + break; + default: + break; + } + } else { + if (len < 0) + len = xmlStrlen(value); + /* + * Concat the value. + */ + if (vctxt->inode->flags & XML_SCHEMA_NODE_INFO_FLAG_OWNED_VALUES) { + vctxt->inode->value = BAD_CAST xmlStrncat( + (xmlChar *) vctxt->inode->value, value, len); + } else { + vctxt->inode->value = + BAD_CAST xmlStrncatNew(vctxt->inode->value, value, len); + vctxt->inode->flags |= XML_SCHEMA_NODE_INFO_FLAG_OWNED_VALUES; + } + } + + return (0); +} + +static int +xmlSchemaValidateElem(xmlSchemaValidCtxtPtr vctxt) +{ + int ret = 0; + + if ((vctxt->skipDepth != -1) && + (vctxt->depth >= vctxt->skipDepth)) { + VERROR_INT("xmlSchemaValidateElem", + "in skip-state"); + goto internal_error; + } + if (vctxt->xsiAssemble) { + /* + * We will stop validation if there was an error during + * dynamic schema construction. + * Note that we simply set @skipDepth to 0, this could + * mean that a streaming document via SAX would be + * still read to the end but it won't be validated any more. + * TODO: If we are sure how to stop the validation at once + * for all input scenarios, then this should be changed to + * instantly stop the validation. + */ + ret = xmlSchemaAssembleByXSI(vctxt); + if (ret != 0) { + if (ret == -1) + goto internal_error; + vctxt->skipDepth = 0; + return(ret); + } + } + if (vctxt->depth > 0) { + /* + * Validate this element against the content model + * of the parent. + */ + ret = xmlSchemaValidateChildElem(vctxt); + if (ret != 0) { + if (ret < 0) { + VERROR_INT("xmlSchemaValidateElem", + "calling xmlSchemaStreamValidateChildElement()"); + goto internal_error; + } + goto exit; + } + if (vctxt->depth == vctxt->skipDepth) + goto exit; + if ((vctxt->inode->decl == NULL) && + (vctxt->inode->typeDef == NULL)) { + VERROR_INT("xmlSchemaValidateElem", + "the child element was valid but neither the " + "declaration nor the type was set"); + goto internal_error; + } + } else { + /* + * Get the declaration of the validation root. + */ + vctxt->inode->decl = xmlSchemaGetElem(vctxt->schema, + vctxt->inode->localName, + vctxt->inode->nsName); + if (vctxt->inode->decl == NULL) { + ret = XML_SCHEMAV_CVC_ELT_1; + VERROR(ret, NULL, + "No matching global declaration available " + "for the validation root"); + goto exit; + } + } + + if (vctxt->inode->decl == NULL) + goto type_validation; + + if (vctxt->inode->decl->type == XML_SCHEMA_TYPE_ANY) { + int skip; + /* + * Wildcards. + */ + ret = xmlSchemaValidateElemWildcard(vctxt, &skip); + if (ret != 0) { + if (ret < 0) { + VERROR_INT("xmlSchemaValidateElem", + "calling xmlSchemaValidateElemWildcard()"); + goto internal_error; + } + goto exit; + } + if (skip) { + vctxt->skipDepth = vctxt->depth; + goto exit; + } + /* + * The declaration might be set by the wildcard validation, + * when the processContents is "lax" or "strict". + */ + if (vctxt->inode->decl->type != XML_SCHEMA_TYPE_ELEMENT) { + /* + * Clear the "decl" field to not confuse further processing. + */ + vctxt->inode->decl = NULL; + goto type_validation; + } + } + /* + * Validate against the declaration. + */ + ret = xmlSchemaValidateElemDecl(vctxt); + if (ret != 0) { + if (ret < 0) { + VERROR_INT("xmlSchemaValidateElem", + "calling xmlSchemaValidateElemDecl()"); + goto internal_error; + } + goto exit; + } + /* + * Validate against the type definition. + */ +type_validation: + + if (vctxt->inode->typeDef == NULL) { + vctxt->inode->flags |= XML_SCHEMA_NODE_INFO_ERR_BAD_TYPE; + ret = XML_SCHEMAV_CVC_TYPE_1; + VERROR(ret, NULL, + "The type definition is absent"); + goto exit; + } + if (vctxt->inode->typeDef->flags & XML_SCHEMAS_TYPE_ABSTRACT) { + vctxt->inode->flags |= XML_SCHEMA_NODE_INFO_ERR_BAD_TYPE; + ret = XML_SCHEMAV_CVC_TYPE_2; + VERROR(ret, NULL, + "The type definition is abstract"); + goto exit; + } + /* + * Evaluate IDCs. Do it here, since new IDC matchers are registered + * during validation against the declaration. This must be done + * _before_ attribute validation. + */ + if (vctxt->xpathStates != NULL) { + ret = xmlSchemaXPathEvaluate(vctxt, XML_ELEMENT_NODE); + vctxt->inode->appliedXPath = 1; + if (ret == -1) { + VERROR_INT("xmlSchemaValidateElem", + "calling xmlSchemaXPathEvaluate()"); + goto internal_error; + } + } + /* + * Validate attributes. + */ + if (WXS_IS_COMPLEX(vctxt->inode->typeDef)) { + if ((vctxt->nbAttrInfos != 0) || + (vctxt->inode->typeDef->attrUses != NULL)) { + + ret = xmlSchemaVAttributesComplex(vctxt); + } + } else if (vctxt->nbAttrInfos != 0) { + + ret = xmlSchemaVAttributesSimple(vctxt); + } + /* + * Clear registered attributes. + */ + if (vctxt->nbAttrInfos != 0) + xmlSchemaClearAttrInfos(vctxt); + if (ret == -1) { + VERROR_INT("xmlSchemaValidateElem", + "calling attributes validation"); + goto internal_error; + } + /* + * Don't return an error if attributes are invalid on purpose. + */ + ret = 0; + +exit: + if (ret != 0) + vctxt->skipDepth = vctxt->depth; + return (ret); +internal_error: + return (-1); +} + +#ifdef XML_SCHEMA_READER_ENABLED +static int +xmlSchemaVReaderWalk(xmlSchemaValidCtxtPtr vctxt) +{ + const int WHTSP = 13, SIGN_WHTSP = 14, END_ELEM = 15; + int depth, nodeType, ret = 0, consumed; + xmlSchemaNodeInfoPtr ielem; + + vctxt->depth = -1; + ret = xmlTextReaderRead(vctxt->reader); + /* + * Move to the document element. + */ + while (ret == 1) { + nodeType = xmlTextReaderNodeType(vctxt->reader); + if (nodeType == XML_ELEMENT_NODE) + goto root_found; + ret = xmlTextReaderRead(vctxt->reader); + } + goto exit; + +root_found: + + do { + depth = xmlTextReaderDepth(vctxt->reader); + nodeType = xmlTextReaderNodeType(vctxt->reader); + + if (nodeType == XML_ELEMENT_NODE) { + + vctxt->depth++; + if (xmlSchemaValidatorPushElem(vctxt) == -1) { + VERROR_INT("xmlSchemaVReaderWalk", + "calling xmlSchemaValidatorPushElem()"); + goto internal_error; + } + ielem = vctxt->inode; + ielem->localName = xmlTextReaderLocalName(vctxt->reader); + ielem->nsName = xmlTextReaderNamespaceUri(vctxt->reader); + ielem->flags |= XML_SCHEMA_NODE_INFO_FLAG_OWNED_NAMES; + /* + * Is the element empty? + */ + ret = xmlTextReaderIsEmptyElement(vctxt->reader); + if (ret == -1) { + VERROR_INT("xmlSchemaVReaderWalk", + "calling xmlTextReaderIsEmptyElement()"); + goto internal_error; + } + if (ret) { + ielem->flags |= XML_SCHEMA_ELEM_INFO_EMPTY; + } + /* + * Register attributes. + */ + vctxt->nbAttrInfos = 0; + ret = xmlTextReaderMoveToFirstAttribute(vctxt->reader); + if (ret == -1) { + VERROR_INT("xmlSchemaVReaderWalk", + "calling xmlTextReaderMoveToFirstAttribute()"); + goto internal_error; + } + if (ret == 1) { + do { + /* + * VAL TODO: How do we know that the reader works on a + * node tree, to be able to pass a node here? + */ + if (xmlSchemaValidatorPushAttribute(vctxt, NULL, + (const xmlChar *) xmlTextReaderLocalName(vctxt->reader), + xmlTextReaderNamespaceUri(vctxt->reader), 1, + xmlTextReaderValue(vctxt->reader), 1) == -1) { + + VERROR_INT("xmlSchemaVReaderWalk", + "calling xmlSchemaValidatorPushAttribute()"); + goto internal_error; + } + ret = xmlTextReaderMoveToNextAttribute(vctxt->reader); + if (ret == -1) { + VERROR_INT("xmlSchemaVReaderWalk", + "calling xmlTextReaderMoveToFirstAttribute()"); + goto internal_error; + } + } while (ret == 1); + /* + * Back to element position. + */ + ret = xmlTextReaderMoveToElement(vctxt->reader); + if (ret == -1) { + VERROR_INT("xmlSchemaVReaderWalk", + "calling xmlTextReaderMoveToElement()"); + goto internal_error; + } + } + /* + * Validate the element. + */ + ret= xmlSchemaValidateElem(vctxt); + if (ret != 0) { + if (ret == -1) { + VERROR_INT("xmlSchemaVReaderWalk", + "calling xmlSchemaValidateElem()"); + goto internal_error; + } + goto exit; + } + if (vctxt->depth == vctxt->skipDepth) { + int curDepth; + /* + * Skip all content. + */ + if ((ielem->flags & XML_SCHEMA_ELEM_INFO_EMPTY) == 0) { + ret = xmlTextReaderRead(vctxt->reader); + curDepth = xmlTextReaderDepth(vctxt->reader); + while ((ret == 1) && (curDepth != depth)) { + ret = xmlTextReaderRead(vctxt->reader); + curDepth = xmlTextReaderDepth(vctxt->reader); + } + if (ret < 0) { + /* + * VAL TODO: A reader error occured; what to do here? + */ + ret = 1; + goto exit; + } + } + goto leave_elem; + } + /* + * READER VAL TODO: Is an END_ELEM really never called + * if the elem is empty? + */ + if (ielem->flags & XML_SCHEMA_ELEM_INFO_EMPTY) + goto leave_elem; + } else if (nodeType == END_ELEM) { + /* + * Process END of element. + */ +leave_elem: + ret = xmlSchemaValidatorPopElem(vctxt); + if (ret != 0) { + if (ret < 0) { + VERROR_INT("xmlSchemaVReaderWalk", + "calling xmlSchemaValidatorPopElem()"); + goto internal_error; + } + goto exit; + } + if (vctxt->depth >= 0) + ielem = vctxt->inode; + else + ielem = NULL; + } else if ((nodeType == XML_TEXT_NODE) || + (nodeType == XML_CDATA_SECTION_NODE) || + (nodeType == WHTSP) || + (nodeType == SIGN_WHTSP)) { + /* + * Process character content. + */ + xmlChar *value; + + if ((nodeType == WHTSP) || (nodeType == SIGN_WHTSP)) + nodeType = XML_TEXT_NODE; + + value = xmlTextReaderValue(vctxt->reader); + ret = xmlSchemaVPushText(vctxt, nodeType, BAD_CAST value, + -1, XML_SCHEMA_PUSH_TEXT_CREATED, &consumed); + if (! consumed) + xmlFree(value); + if (ret == -1) { + VERROR_INT("xmlSchemaVReaderWalk", + "calling xmlSchemaVPushText()"); + goto internal_error; + } + } else if ((nodeType == XML_ENTITY_NODE) || + (nodeType == XML_ENTITY_REF_NODE)) { + /* + * VAL TODO: What to do with entities? + */ + TODO + } + /* + * Read next node. + */ + ret = xmlTextReaderRead(vctxt->reader); + } while (ret == 1); + +exit: + return (ret); +internal_error: + return (-1); +} +#endif + +/************************************************************************ + * * + * SAX validation handlers * + * * + ************************************************************************/ + +/* +* Process text content. +*/ +static void +xmlSchemaSAXHandleText(void *ctx, + const xmlChar * ch, + int len) +{ + xmlSchemaValidCtxtPtr vctxt = (xmlSchemaValidCtxtPtr) ctx; + + if (vctxt->depth < 0) + return; + if ((vctxt->skipDepth != -1) && (vctxt->depth >= vctxt->skipDepth)) + return; + if (vctxt->inode->flags & XML_SCHEMA_ELEM_INFO_EMPTY) + vctxt->inode->flags ^= XML_SCHEMA_ELEM_INFO_EMPTY; + if (xmlSchemaVPushText(vctxt, XML_TEXT_NODE, ch, len, + XML_SCHEMA_PUSH_TEXT_VOLATILE, NULL) == -1) { + VERROR_INT("xmlSchemaSAXHandleCDataSection", + "calling xmlSchemaVPushText()"); + vctxt->err = -1; + xmlStopParser(vctxt->parserCtxt); + } +} + +/* +* Process CDATA content. +*/ +static void +xmlSchemaSAXHandleCDataSection(void *ctx, + const xmlChar * ch, + int len) +{ + xmlSchemaValidCtxtPtr vctxt = (xmlSchemaValidCtxtPtr) ctx; + + if (vctxt->depth < 0) + return; + if ((vctxt->skipDepth != -1) && (vctxt->depth >= vctxt->skipDepth)) + return; + if (vctxt->inode->flags & XML_SCHEMA_ELEM_INFO_EMPTY) + vctxt->inode->flags ^= XML_SCHEMA_ELEM_INFO_EMPTY; + if (xmlSchemaVPushText(vctxt, XML_CDATA_SECTION_NODE, ch, len, + XML_SCHEMA_PUSH_TEXT_VOLATILE, NULL) == -1) { + VERROR_INT("xmlSchemaSAXHandleCDataSection", + "calling xmlSchemaVPushText()"); + vctxt->err = -1; + xmlStopParser(vctxt->parserCtxt); + } +} + +static void +xmlSchemaSAXHandleReference(void *ctx ATTRIBUTE_UNUSED, + const xmlChar * name ATTRIBUTE_UNUSED) +{ + xmlSchemaValidCtxtPtr vctxt = (xmlSchemaValidCtxtPtr) ctx; + + if (vctxt->depth < 0) + return; + if ((vctxt->skipDepth != -1) && (vctxt->depth >= vctxt->skipDepth)) + return; + /* SAX VAL TODO: What to do here? */ + TODO +} + +static void +xmlSchemaSAXHandleStartElementNs(void *ctx, + const xmlChar * localname, + const xmlChar * prefix ATTRIBUTE_UNUSED, + const xmlChar * URI, + int nb_namespaces, + const xmlChar ** namespaces, + int nb_attributes, + int nb_defaulted ATTRIBUTE_UNUSED, + const xmlChar ** attributes) +{ + xmlSchemaValidCtxtPtr vctxt = (xmlSchemaValidCtxtPtr) ctx; + int ret; + xmlSchemaNodeInfoPtr ielem; + int i, j; + + /* + * SAX VAL TODO: What to do with nb_defaulted? + */ + /* + * Skip elements if inside a "skip" wildcard or invalid. + */ + vctxt->depth++; + if ((vctxt->skipDepth != -1) && (vctxt->depth >= vctxt->skipDepth)) + return; + /* + * Push the element. + */ + if (xmlSchemaValidatorPushElem(vctxt) == -1) { + VERROR_INT("xmlSchemaSAXHandleStartElementNs", + "calling xmlSchemaValidatorPushElem()"); + goto internal_error; + } + ielem = vctxt->inode; + /* + * TODO: Is this OK? + */ + ielem->nodeLine = xmlSAX2GetLineNumber(vctxt->parserCtxt); + ielem->localName = localname; + ielem->nsName = URI; + ielem->flags |= XML_SCHEMA_ELEM_INFO_EMPTY; + /* + * Register namespaces on the elem info. + */ + if (nb_namespaces != 0) { + /* + * Although the parser builds its own namespace list, + * we have no access to it, so we'll use an own one. + */ + for (i = 0, j = 0; i < nb_namespaces; i++, j += 2) { + /* + * Store prefix and namespace name. + */ + if (ielem->nsBindings == NULL) { + ielem->nsBindings = + (const xmlChar **) xmlMalloc(10 * + sizeof(const xmlChar *)); + if (ielem->nsBindings == NULL) { + xmlSchemaVErrMemory(vctxt, + "allocating namespace bindings for SAX validation", + NULL); + goto internal_error; + } + ielem->nbNsBindings = 0; + ielem->sizeNsBindings = 5; + } else if (ielem->sizeNsBindings <= ielem->nbNsBindings) { + ielem->sizeNsBindings *= 2; + ielem->nsBindings = + (const xmlChar **) xmlRealloc( + (void *) ielem->nsBindings, + ielem->sizeNsBindings * 2 * sizeof(const xmlChar *)); + if (ielem->nsBindings == NULL) { + xmlSchemaVErrMemory(vctxt, + "re-allocating namespace bindings for SAX validation", + NULL); + goto internal_error; + } + } + + ielem->nsBindings[ielem->nbNsBindings * 2] = namespaces[j]; + if (namespaces[j+1][0] == 0) { + /* + * Handle xmlns="". + */ + ielem->nsBindings[ielem->nbNsBindings * 2 + 1] = NULL; + } else + ielem->nsBindings[ielem->nbNsBindings * 2 + 1] = + namespaces[j+1]; + ielem->nbNsBindings++; + } + } + /* + * Register attributes. + * SAX VAL TODO: We are not adding namespace declaration + * attributes yet. + */ + if (nb_attributes != 0) { + xmlChar *value; + + for (j = 0, i = 0; i < nb_attributes; i++, j += 5) { + /* + * Duplicate the value. + */ + value = xmlStrndup(attributes[j+3], + attributes[j+4] - attributes[j+3]); + /* + * TODO: Set the node line. + */ + ret = xmlSchemaValidatorPushAttribute(vctxt, + NULL, ielem->nodeLine, attributes[j], attributes[j+2], 0, + value, 1); + if (ret == -1) { + VERROR_INT("xmlSchemaSAXHandleStartElementNs", + "calling xmlSchemaValidatorPushAttribute()"); + goto internal_error; + } + } + } + /* + * Validate the element. + */ + ret = xmlSchemaValidateElem(vctxt); + if (ret != 0) { + if (ret == -1) { + VERROR_INT("xmlSchemaSAXHandleStartElementNs", + "calling xmlSchemaValidateElem()"); + goto internal_error; + } + goto exit; + } + +exit: + return; +internal_error: + vctxt->err = -1; + xmlStopParser(vctxt->parserCtxt); + return; +} + +static void +xmlSchemaSAXHandleEndElementNs(void *ctx, + const xmlChar * localname ATTRIBUTE_UNUSED, + const xmlChar * prefix ATTRIBUTE_UNUSED, + const xmlChar * URI ATTRIBUTE_UNUSED) +{ + xmlSchemaValidCtxtPtr vctxt = (xmlSchemaValidCtxtPtr) ctx; + int res; + + /* + * Skip elements if inside a "skip" wildcard or if invalid. + */ + if (vctxt->skipDepth != -1) { + if (vctxt->depth > vctxt->skipDepth) { + vctxt->depth--; + return; + } else + vctxt->skipDepth = -1; + } + /* + * SAX VAL TODO: Just a temporary check. + */ + if ((!xmlStrEqual(vctxt->inode->localName, localname)) || + (!xmlStrEqual(vctxt->inode->nsName, URI))) { + VERROR_INT("xmlSchemaSAXHandleEndElementNs", + "elem pop mismatch"); + } + res = xmlSchemaValidatorPopElem(vctxt); + if (res != 0) { + if (res < 0) { + VERROR_INT("xmlSchemaSAXHandleEndElementNs", + "calling xmlSchemaValidatorPopElem()"); + goto internal_error; + } + goto exit; + } +exit: + return; +internal_error: + vctxt->err = -1; + xmlStopParser(vctxt->parserCtxt); + return; +} + +/************************************************************************ + * * + * Validation interfaces * + * * + ************************************************************************/ + +/** + * xmlSchemaNewValidCtxt: + * @schema: a precompiled XML Schemas + * + * Create an XML Schemas validation context based on the given schema. + * + * Returns the validation context or NULL in case of error + */ +xmlSchemaValidCtxtPtr +xmlSchemaNewValidCtxt(xmlSchemaPtr schema) +{ + xmlSchemaValidCtxtPtr ret; + + ret = (xmlSchemaValidCtxtPtr) xmlMalloc(sizeof(xmlSchemaValidCtxt)); + if (ret == NULL) { + xmlSchemaVErrMemory(NULL, "allocating validation context", NULL); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchemaValidCtxt)); + ret->type = XML_SCHEMA_CTXT_VALIDATOR; + ret->dict = xmlDictCreate(); + ret->nodeQNames = xmlSchemaItemListCreate(); + ret->schema = schema; + return (ret); +} + +/** + * xmlSchemaClearValidCtxt: + * @ctxt: the schema validation context + * + * Free the resources associated to the schema validation context; + * leaves some fields alive intended for reuse of the context. + */ +static void +xmlSchemaClearValidCtxt(xmlSchemaValidCtxtPtr vctxt) +{ + if (vctxt == NULL) + return; + + /* + * TODO: Should we clear the flags? + * Might be problematic if one reuses the context + * and assumes that the options remain the same. + */ + vctxt->flags = 0; + vctxt->validationRoot = NULL; + vctxt->doc = NULL; +#ifdef LIBXML_READER_ENABLED + vctxt->reader = NULL; +#endif + vctxt->hasKeyrefs = 0; + + if (vctxt->value != NULL) { + xmlSchemaFreeValue(vctxt->value); + vctxt->value = NULL; + } + /* + * Augmented IDC information. + */ + if (vctxt->aidcs != NULL) { + xmlSchemaIDCAugPtr cur = vctxt->aidcs, next; + do { + next = cur->next; + xmlFree(cur); + cur = next; + } while (cur != NULL); + vctxt->aidcs = NULL; + } + if (vctxt->idcNodes != NULL) { + int i; + xmlSchemaPSVIIDCNodePtr item; + + for (i = 0; i < vctxt->nbIdcNodes; i++) { + item = vctxt->idcNodes[i]; + xmlFree(item->keys); + xmlFree(item); + } + xmlFree(vctxt->idcNodes); + vctxt->idcNodes = NULL; + } + /* + * Note that we won't delete the XPath state pool here. + */ + if (vctxt->xpathStates != NULL) { + xmlSchemaFreeIDCStateObjList(vctxt->xpathStates); + vctxt->xpathStates = NULL; + } + /* + * Attribute info. + */ + if (vctxt->nbAttrInfos != 0) { + xmlSchemaClearAttrInfos(vctxt); + } + /* + * Element info. + */ + if (vctxt->elemInfos != NULL) { + int i; + xmlSchemaNodeInfoPtr ei; + + for (i = 0; i < vctxt->sizeElemInfos; i++) { + ei = vctxt->elemInfos[i]; + if (ei == NULL) + break; + xmlSchemaClearElemInfo(ei); + } + } + xmlSchemaItemListClear(vctxt->nodeQNames); + /* Recreate the dict. */ + xmlDictFree(vctxt->dict); + /* + * TODO: Is is save to recreate it? Do we have a scenario + * where the user provides the dict? + */ + vctxt->dict = xmlDictCreate(); +} + +/** + * xmlSchemaFreeValidCtxt: + * @ctxt: the schema validation context + * + * Free the resources associated to the schema validation context + */ +void +xmlSchemaFreeValidCtxt(xmlSchemaValidCtxtPtr ctxt) +{ + if (ctxt == NULL) + return; + if (ctxt->value != NULL) + xmlSchemaFreeValue(ctxt->value); + if (ctxt->pctxt != NULL) + xmlSchemaFreeParserCtxt(ctxt->pctxt); + if (ctxt->idcNodes != NULL) { + int i; + xmlSchemaPSVIIDCNodePtr item; + + for (i = 0; i < ctxt->nbIdcNodes; i++) { + item = ctxt->idcNodes[i]; + xmlFree(item->keys); + xmlFree(item); + } + xmlFree(ctxt->idcNodes); + } + if (ctxt->idcKeys != NULL) { + int i; + for (i = 0; i < ctxt->nbIdcKeys; i++) + xmlSchemaIDCFreeKey(ctxt->idcKeys[i]); + xmlFree(ctxt->idcKeys); + } + + if (ctxt->xpathStates != NULL) + xmlSchemaFreeIDCStateObjList(ctxt->xpathStates); + if (ctxt->xpathStatePool != NULL) + xmlSchemaFreeIDCStateObjList(ctxt->xpathStatePool); + + /* + * Augmented IDC information. + */ + if (ctxt->aidcs != NULL) { + xmlSchemaIDCAugPtr cur = ctxt->aidcs, next; + do { + next = cur->next; + xmlFree(cur); + cur = next; + } while (cur != NULL); + } + if (ctxt->attrInfos != NULL) { + int i; + xmlSchemaAttrInfoPtr attr; + + /* Just a paranoid call to the cleanup. */ + if (ctxt->nbAttrInfos != 0) + xmlSchemaClearAttrInfos(ctxt); + for (i = 0; i < ctxt->sizeAttrInfos; i++) { + attr = ctxt->attrInfos[i]; + xmlFree(attr); + } + xmlFree(ctxt->attrInfos); + } + if (ctxt->elemInfos != NULL) { + int i; + xmlSchemaNodeInfoPtr ei; + + for (i = 0; i < ctxt->sizeElemInfos; i++) { + ei = ctxt->elemInfos[i]; + if (ei == NULL) + break; + xmlSchemaClearElemInfo(ei); + xmlFree(ei); + } + xmlFree(ctxt->elemInfos); + } + if (ctxt->nodeQNames != NULL) + xmlSchemaItemListFree(ctxt->nodeQNames); + if (ctxt->dict != NULL) + xmlDictFree(ctxt->dict); + xmlFree(ctxt); +} + +/** + * xmlSchemaIsValid: + * @ctxt: the schema validation context + * + * Check if any error was detected during validation. + * + * Returns 1 if valid so far, 0 if errors were detected, and -1 in case + * of internal error. + */ +int +xmlSchemaIsValid(xmlSchemaValidCtxtPtr ctxt) +{ + if (ctxt == NULL) + return(-1); + return(ctxt->err == 0); +} + +/** + * xmlSchemaSetValidErrors: + * @ctxt: a schema validation context + * @err: the error function + * @warn: the warning function + * @ctx: the functions context + * + * Set the error and warning callback informations + */ +void +xmlSchemaSetValidErrors(xmlSchemaValidCtxtPtr ctxt, + xmlSchemaValidityErrorFunc err, + xmlSchemaValidityWarningFunc warn, void *ctx) +{ + if (ctxt == NULL) + return; + ctxt->error = err; + ctxt->warning = warn; + ctxt->errCtxt = ctx; + if (ctxt->pctxt != NULL) + xmlSchemaSetParserErrors(ctxt->pctxt, err, warn, ctx); +} + +/** + * xmlSchemaSetValidStructuredErrors: + * @ctxt: a schema validation context + * @serror: the structured error function + * @ctx: the functions context + * + * Set the structured error callback + */ +void +xmlSchemaSetValidStructuredErrors(xmlSchemaValidCtxtPtr ctxt, + xmlStructuredErrorFunc serror, void *ctx) +{ + if (ctxt == NULL) + return; + ctxt->serror = serror; + ctxt->error = NULL; + ctxt->warning = NULL; + ctxt->errCtxt = ctx; + if (ctxt->pctxt != NULL) + xmlSchemaSetParserStructuredErrors(ctxt->pctxt, serror, ctx); +} + +/** + * xmlSchemaGetValidErrors: + * @ctxt: a XML-Schema validation context + * @err: the error function result + * @warn: the warning function result + * @ctx: the functions context result + * + * Get the error and warning callback informations + * + * Returns -1 in case of error and 0 otherwise + */ +int +xmlSchemaGetValidErrors(xmlSchemaValidCtxtPtr ctxt, + xmlSchemaValidityErrorFunc * err, + xmlSchemaValidityWarningFunc * warn, void **ctx) +{ + if (ctxt == NULL) + return (-1); + if (err != NULL) + *err = ctxt->error; + if (warn != NULL) + *warn = ctxt->warning; + if (ctx != NULL) + *ctx = ctxt->errCtxt; + return (0); +} + + +/** + * xmlSchemaSetValidOptions: + * @ctxt: a schema validation context + * @options: a combination of xmlSchemaValidOption + * + * Sets the options to be used during the validation. + * + * Returns 0 in case of success, -1 in case of an + * API error. + */ +int +xmlSchemaSetValidOptions(xmlSchemaValidCtxtPtr ctxt, + int options) + +{ + int i; + + if (ctxt == NULL) + return (-1); + /* + * WARNING: Change the start value if adding to the + * xmlSchemaValidOption. + * TODO: Is there an other, more easy to maintain, + * way? + */ + for (i = 1; i < (int) sizeof(int) * 8; i++) { + if (options & 1<options = options; + return (0); +} + +/** + * xmlSchemaValidCtxtGetOptions: + * @ctxt: a schema validation context + * + * Get the validation context options. + * + * Returns the option combination or -1 on error. + */ +int +xmlSchemaValidCtxtGetOptions(xmlSchemaValidCtxtPtr ctxt) + +{ + if (ctxt == NULL) + return (-1); + else + return (ctxt->options); +} + +static int +xmlSchemaVDocWalk(xmlSchemaValidCtxtPtr vctxt) +{ + xmlAttrPtr attr; + int ret = 0; + xmlSchemaNodeInfoPtr ielem = NULL; + xmlNodePtr node, valRoot; + const xmlChar *nsName; + + /* DOC VAL TODO: Move this to the start function. */ + valRoot = xmlDocGetRootElement(vctxt->doc); + if (valRoot == NULL) { + /* VAL TODO: Error code? */ + VERROR(1, NULL, "The document has no document element"); + return (1); + } + vctxt->depth = -1; + vctxt->validationRoot = valRoot; + node = valRoot; + while (node != NULL) { + if ((vctxt->skipDepth != -1) && (vctxt->depth >= vctxt->skipDepth)) + goto next_sibling; + if (node->type == XML_ELEMENT_NODE) { + + /* + * Init the node-info. + */ + vctxt->depth++; + if (xmlSchemaValidatorPushElem(vctxt) == -1) + goto internal_error; + ielem = vctxt->inode; + ielem->node = node; + ielem->nodeLine = node->line; + ielem->localName = node->name; + if (node->ns != NULL) + ielem->nsName = node->ns->href; + ielem->flags |= XML_SCHEMA_ELEM_INFO_EMPTY; + /* + * Register attributes. + * DOC VAL TODO: We do not register namespace declaration + * attributes yet. + */ + vctxt->nbAttrInfos = 0; + if (node->properties != NULL) { + attr = node->properties; + do { + if (attr->ns != NULL) + nsName = attr->ns->href; + else + nsName = NULL; + ret = xmlSchemaValidatorPushAttribute(vctxt, + (xmlNodePtr) attr, + /* + * Note that we give it the line number of the + * parent element. + */ + ielem->nodeLine, + attr->name, nsName, 0, + xmlNodeListGetString(attr->doc, attr->children, 1), 1); + if (ret == -1) { + VERROR_INT("xmlSchemaDocWalk", + "calling xmlSchemaValidatorPushAttribute()"); + goto internal_error; + } + attr = attr->next; + } while (attr); + } + /* + * Validate the element. + */ + ret = xmlSchemaValidateElem(vctxt); + if (ret != 0) { + if (ret == -1) { + VERROR_INT("xmlSchemaDocWalk", + "calling xmlSchemaValidateElem()"); + goto internal_error; + } + /* + * Don't stop validation; just skip the content + * of this element. + */ + goto leave_node; + } + if ((vctxt->skipDepth != -1) && + (vctxt->depth >= vctxt->skipDepth)) + goto leave_node; + } else if ((node->type == XML_TEXT_NODE) || + (node->type == XML_CDATA_SECTION_NODE)) { + /* + * Process character content. + */ + if ((ielem != NULL) && (ielem->flags & XML_SCHEMA_ELEM_INFO_EMPTY)) + ielem->flags ^= XML_SCHEMA_ELEM_INFO_EMPTY; + ret = xmlSchemaVPushText(vctxt, node->type, node->content, + -1, XML_SCHEMA_PUSH_TEXT_PERSIST, NULL); + if (ret < 0) { + VERROR_INT("xmlSchemaVDocWalk", + "calling xmlSchemaVPushText()"); + goto internal_error; + } + /* + * DOC VAL TODO: Should we skip further validation of the + * element content here? + */ + } else if ((node->type == XML_ENTITY_NODE) || + (node->type == XML_ENTITY_REF_NODE)) { + /* + * DOC VAL TODO: What to do with entities? + */ + VERROR_INT("xmlSchemaVDocWalk", + "there is at least one entity reference in the node-tree " + "currently being validated. Processing of entities with " + "this XML Schema processor is not supported (yet). Please " + "substitute entities before validation."); + goto internal_error; + } else { + goto leave_node; + /* + * DOC VAL TODO: XInclude nodes, etc. + */ + } + /* + * Walk the doc. + */ + if (node->children != NULL) { + node = node->children; + continue; + } +leave_node: + if (node->type == XML_ELEMENT_NODE) { + /* + * Leaving the scope of an element. + */ + if (node != vctxt->inode->node) { + VERROR_INT("xmlSchemaVDocWalk", + "element position mismatch"); + goto internal_error; + } + ret = xmlSchemaValidatorPopElem(vctxt); + if (ret != 0) { + if (ret < 0) { + VERROR_INT("xmlSchemaVDocWalk", + "calling xmlSchemaValidatorPopElem()"); + goto internal_error; + } + } + if (node == valRoot) + goto exit; + } +next_sibling: + if (node->next != NULL) + node = node->next; + else { + node = node->parent; + goto leave_node; + } + } + +exit: + return (ret); +internal_error: + return (-1); +} + +static int +xmlSchemaPreRun(xmlSchemaValidCtxtPtr vctxt) { + /* + * Some initialization. + */ + vctxt->err = 0; + vctxt->nberrors = 0; + vctxt->depth = -1; + vctxt->skipDepth = -1; + vctxt->xsiAssemble = 0; + vctxt->hasKeyrefs = 0; +#ifdef ENABLE_IDC_NODE_TABLES_TEST + vctxt->createIDCNodeTables = 1; +#else + vctxt->createIDCNodeTables = 0; +#endif + /* + * Create a schema + parser if necessary. + */ + if (vctxt->schema == NULL) { + xmlSchemaParserCtxtPtr pctxt; + + vctxt->xsiAssemble = 1; + /* + * If not schema was given then we will create a schema + * dynamically using XSI schema locations. + * + * Create the schema parser context. + */ + if ((vctxt->pctxt == NULL) && + (xmlSchemaCreatePCtxtOnVCtxt(vctxt) == -1)) + return (-1); + pctxt = vctxt->pctxt; + pctxt->xsiAssemble = 1; + /* + * Create the schema. + */ + vctxt->schema = xmlSchemaNewSchema(pctxt); + if (vctxt->schema == NULL) + return (-1); + /* + * Create the schema construction context. + */ + pctxt->constructor = xmlSchemaConstructionCtxtCreate(pctxt->dict); + if (pctxt->constructor == NULL) + return(-1); + pctxt->constructor->mainSchema = vctxt->schema; + /* + * Take ownership of the constructor to be able to free it. + */ + pctxt->ownsConstructor = 1; + } + /* + * Augment the IDC definitions. + */ + if (vctxt->schema->idcDef != NULL) { + xmlHashScan(vctxt->schema->idcDef, + (xmlHashScanner) xmlSchemaAugmentIDC, vctxt); + } + return(0); +} + +static void +xmlSchemaPostRun(xmlSchemaValidCtxtPtr vctxt) { + if (vctxt->xsiAssemble) { + if (vctxt->schema != NULL) { + xmlSchemaFree(vctxt->schema); + vctxt->schema = NULL; + } + } + xmlSchemaClearValidCtxt(vctxt); +} + +static int +xmlSchemaVStart(xmlSchemaValidCtxtPtr vctxt) +{ + int ret = 0; + + if (xmlSchemaPreRun(vctxt) < 0) + return(-1); + + if (vctxt->doc != NULL) { + /* + * Tree validation. + */ + ret = xmlSchemaVDocWalk(vctxt); +#ifdef LIBXML_READER_ENABLED + } else if (vctxt->reader != NULL) { + /* + * XML Reader validation. + */ +#ifdef XML_SCHEMA_READER_ENABLED + ret = xmlSchemaVReaderWalk(vctxt); +#endif +#endif + } else if ((vctxt->sax != NULL) && (vctxt->parserCtxt != NULL)) { + /* + * SAX validation. + */ + ret = xmlParseDocument(vctxt->parserCtxt); + } else { + VERROR_INT("xmlSchemaVStart", + "no instance to validate"); + ret = -1; + } + + xmlSchemaPostRun(vctxt); + if (ret == 0) + ret = vctxt->err; + return (ret); +} + +/** + * xmlSchemaValidateOneElement: + * @ctxt: a schema validation context + * @elem: an element node + * + * Validate a branch of a tree, starting with the given @elem. + * + * Returns 0 if the element and its subtree is valid, a positive error + * code number otherwise and -1 in case of an internal or API error. + */ +int +xmlSchemaValidateOneElement(xmlSchemaValidCtxtPtr ctxt, xmlNodePtr elem) +{ + if ((ctxt == NULL) || (elem == NULL) || (elem->type != XML_ELEMENT_NODE)) + return (-1); + + if (ctxt->schema == NULL) + return (-1); + + ctxt->doc = elem->doc; + ctxt->node = elem; + ctxt->validationRoot = elem; + return(xmlSchemaVStart(ctxt)); +} + +/** + * xmlSchemaValidateDoc: + * @ctxt: a schema validation context + * @doc: a parsed document tree + * + * Validate a document tree in memory. + * + * Returns 0 if the document is schemas valid, a positive error code + * number otherwise and -1 in case of internal or API error. + */ +int +xmlSchemaValidateDoc(xmlSchemaValidCtxtPtr ctxt, xmlDocPtr doc) +{ + if ((ctxt == NULL) || (doc == NULL)) + return (-1); + + ctxt->doc = doc; + ctxt->node = xmlDocGetRootElement(doc); + if (ctxt->node == NULL) { + xmlSchemaCustomErr(ACTXT_CAST ctxt, + XML_SCHEMAV_DOCUMENT_ELEMENT_MISSING, + (xmlNodePtr) doc, NULL, + "The document has no document element", NULL, NULL); + return (ctxt->err); + } + ctxt->validationRoot = ctxt->node; + return (xmlSchemaVStart(ctxt)); +} + + +/************************************************************************ + * * + * Function and data for SAX streaming API * + * * + ************************************************************************/ +typedef struct _xmlSchemaSplitSAXData xmlSchemaSplitSAXData; +typedef xmlSchemaSplitSAXData *xmlSchemaSplitSAXDataPtr; + +struct _xmlSchemaSplitSAXData { + xmlSAXHandlerPtr user_sax; + void *user_data; + xmlSchemaValidCtxtPtr ctxt; + xmlSAXHandlerPtr schemas_sax; +}; + +#define XML_SAX_PLUG_MAGIC 0xdc43ba21 + +struct _xmlSchemaSAXPlug { + unsigned int magic; + + /* the original callbacks informations */ + xmlSAXHandlerPtr *user_sax_ptr; + xmlSAXHandlerPtr user_sax; + void **user_data_ptr; + void *user_data; + + /* the block plugged back and validation informations */ + xmlSAXHandler schemas_sax; + xmlSchemaValidCtxtPtr ctxt; +}; + +/* All those functions just bounces to the user provided SAX handlers */ +static void +internalSubsetSplit(void *ctx, const xmlChar *name, + const xmlChar *ExternalID, const xmlChar *SystemID) +{ + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if ((ctxt != NULL) && (ctxt->user_sax != NULL) && + (ctxt->user_sax->internalSubset != NULL)) + ctxt->user_sax->internalSubset(ctxt->user_data, name, ExternalID, + SystemID); +} + +static int +isStandaloneSplit(void *ctx) +{ + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if ((ctxt != NULL) && (ctxt->user_sax != NULL) && + (ctxt->user_sax->isStandalone != NULL)) + return(ctxt->user_sax->isStandalone(ctxt->user_data)); + return(0); +} + +static int +hasInternalSubsetSplit(void *ctx) +{ + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if ((ctxt != NULL) && (ctxt->user_sax != NULL) && + (ctxt->user_sax->hasInternalSubset != NULL)) + return(ctxt->user_sax->hasInternalSubset(ctxt->user_data)); + return(0); +} + +static int +hasExternalSubsetSplit(void *ctx) +{ + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if ((ctxt != NULL) && (ctxt->user_sax != NULL) && + (ctxt->user_sax->hasExternalSubset != NULL)) + return(ctxt->user_sax->hasExternalSubset(ctxt->user_data)); + return(0); +} + +static void +externalSubsetSplit(void *ctx, const xmlChar *name, + const xmlChar *ExternalID, const xmlChar *SystemID) +{ + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if ((ctxt != NULL) && (ctxt->user_sax != NULL) && + (ctxt->user_sax->internalSubset != NULL)) + ctxt->user_sax->internalSubset(ctxt->user_data, name, ExternalID, + SystemID); +} + +static xmlParserInputPtr +resolveEntitySplit(void *ctx, const xmlChar *publicId, const xmlChar *systemId) +{ + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if ((ctxt != NULL) && (ctxt->user_sax != NULL) && + (ctxt->user_sax->resolveEntity != NULL)) + return(ctxt->user_sax->resolveEntity(ctxt->user_data, publicId, + systemId)); + return(NULL); +} + +static xmlEntityPtr +getEntitySplit(void *ctx, const xmlChar *name) +{ + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if ((ctxt != NULL) && (ctxt->user_sax != NULL) && + (ctxt->user_sax->getEntity != NULL)) + return(ctxt->user_sax->getEntity(ctxt->user_data, name)); + return(NULL); +} + +static xmlEntityPtr +getParameterEntitySplit(void *ctx, const xmlChar *name) +{ + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if ((ctxt != NULL) && (ctxt->user_sax != NULL) && + (ctxt->user_sax->getParameterEntity != NULL)) + return(ctxt->user_sax->getParameterEntity(ctxt->user_data, name)); + return(NULL); +} + + +static void +entityDeclSplit(void *ctx, const xmlChar *name, int type, + const xmlChar *publicId, const xmlChar *systemId, xmlChar *content) +{ + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if ((ctxt != NULL) && (ctxt->user_sax != NULL) && + (ctxt->user_sax->entityDecl != NULL)) + ctxt->user_sax->entityDecl(ctxt->user_data, name, type, publicId, + systemId, content); +} + +static void +attributeDeclSplit(void *ctx, const xmlChar * elem, + const xmlChar * name, int type, int def, + const xmlChar * defaultValue, xmlEnumerationPtr tree) +{ + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if ((ctxt != NULL) && (ctxt->user_sax != NULL) && + (ctxt->user_sax->attributeDecl != NULL)) { + ctxt->user_sax->attributeDecl(ctxt->user_data, elem, name, type, + def, defaultValue, tree); + } else { + xmlFreeEnumeration(tree); + } +} + +static void +elementDeclSplit(void *ctx, const xmlChar *name, int type, + xmlElementContentPtr content) +{ + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if ((ctxt != NULL) && (ctxt->user_sax != NULL) && + (ctxt->user_sax->elementDecl != NULL)) + ctxt->user_sax->elementDecl(ctxt->user_data, name, type, content); +} + +static void +notationDeclSplit(void *ctx, const xmlChar *name, + const xmlChar *publicId, const xmlChar *systemId) +{ + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if ((ctxt != NULL) && (ctxt->user_sax != NULL) && + (ctxt->user_sax->notationDecl != NULL)) + ctxt->user_sax->notationDecl(ctxt->user_data, name, publicId, + systemId); +} + +static void +unparsedEntityDeclSplit(void *ctx, const xmlChar *name, + const xmlChar *publicId, const xmlChar *systemId, + const xmlChar *notationName) +{ + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if ((ctxt != NULL) && (ctxt->user_sax != NULL) && + (ctxt->user_sax->unparsedEntityDecl != NULL)) + ctxt->user_sax->unparsedEntityDecl(ctxt->user_data, name, publicId, + systemId, notationName); +} + +static void +setDocumentLocatorSplit(void *ctx, xmlSAXLocatorPtr loc) +{ + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if ((ctxt != NULL) && (ctxt->user_sax != NULL) && + (ctxt->user_sax->setDocumentLocator != NULL)) + ctxt->user_sax->setDocumentLocator(ctxt->user_data, loc); +} + +static void +startDocumentSplit(void *ctx) +{ + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if ((ctxt != NULL) && (ctxt->user_sax != NULL) && + (ctxt->user_sax->startDocument != NULL)) + ctxt->user_sax->startDocument(ctxt->user_data); +} + +static void +endDocumentSplit(void *ctx) +{ + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if ((ctxt != NULL) && (ctxt->user_sax != NULL) && + (ctxt->user_sax->endDocument != NULL)) + ctxt->user_sax->endDocument(ctxt->user_data); +} + +static void +processingInstructionSplit(void *ctx, const xmlChar *target, + const xmlChar *data) +{ + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if ((ctxt != NULL) && (ctxt->user_sax != NULL) && + (ctxt->user_sax->processingInstruction != NULL)) + ctxt->user_sax->processingInstruction(ctxt->user_data, target, data); +} + +static void +commentSplit(void *ctx, const xmlChar *value) +{ + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if ((ctxt != NULL) && (ctxt->user_sax != NULL) && + (ctxt->user_sax->comment != NULL)) + ctxt->user_sax->comment(ctxt->user_data, value); +} + +/* + * Varargs error callbacks to the user application, harder ... + */ + +static void XMLCDECL +warningSplit(void *ctx, const char *msg ATTRIBUTE_UNUSED, ...) { + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if ((ctxt != NULL) && (ctxt->user_sax != NULL) && + (ctxt->user_sax->warning != NULL)) { + TODO + } +} +static void XMLCDECL +errorSplit(void *ctx, const char *msg ATTRIBUTE_UNUSED, ...) { + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if ((ctxt != NULL) && (ctxt->user_sax != NULL) && + (ctxt->user_sax->error != NULL)) { + TODO + } +} +static void XMLCDECL +fatalErrorSplit(void *ctx, const char *msg ATTRIBUTE_UNUSED, ...) { + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if ((ctxt != NULL) && (ctxt->user_sax != NULL) && + (ctxt->user_sax->fatalError != NULL)) { + TODO + } +} + +/* + * Those are function where both the user handler and the schemas handler + * need to be called. + */ +static void +charactersSplit(void *ctx, const xmlChar *ch, int len) +{ + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if (ctxt == NULL) + return; + if ((ctxt->user_sax != NULL) && (ctxt->user_sax->characters != NULL)) + ctxt->user_sax->characters(ctxt->user_data, ch, len); + if (ctxt->ctxt != NULL) + xmlSchemaSAXHandleText(ctxt->ctxt, ch, len); +} + +static void +ignorableWhitespaceSplit(void *ctx, const xmlChar *ch, int len) +{ + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if (ctxt == NULL) + return; + if ((ctxt->user_sax != NULL) && + (ctxt->user_sax->ignorableWhitespace != NULL)) + ctxt->user_sax->ignorableWhitespace(ctxt->user_data, ch, len); + if (ctxt->ctxt != NULL) + xmlSchemaSAXHandleText(ctxt->ctxt, ch, len); +} + +static void +cdataBlockSplit(void *ctx, const xmlChar *value, int len) +{ + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if (ctxt == NULL) + return; + if ((ctxt->user_sax != NULL) && + (ctxt->user_sax->ignorableWhitespace != NULL)) + ctxt->user_sax->ignorableWhitespace(ctxt->user_data, value, len); + if (ctxt->ctxt != NULL) + xmlSchemaSAXHandleCDataSection(ctxt->ctxt, value, len); +} + +static void +referenceSplit(void *ctx, const xmlChar *name) +{ + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if (ctxt == NULL) + return; + if ((ctxt != NULL) && (ctxt->user_sax != NULL) && + (ctxt->user_sax->reference != NULL)) + ctxt->user_sax->reference(ctxt->user_data, name); + if (ctxt->ctxt != NULL) + xmlSchemaSAXHandleReference(ctxt->user_data, name); +} + +static void +startElementNsSplit(void *ctx, const xmlChar * localname, + const xmlChar * prefix, const xmlChar * URI, + int nb_namespaces, const xmlChar ** namespaces, + int nb_attributes, int nb_defaulted, + const xmlChar ** attributes) { + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if (ctxt == NULL) + return; + if ((ctxt->user_sax != NULL) && + (ctxt->user_sax->startElementNs != NULL)) + ctxt->user_sax->startElementNs(ctxt->user_data, localname, prefix, + URI, nb_namespaces, namespaces, + nb_attributes, nb_defaulted, + attributes); + if (ctxt->ctxt != NULL) + xmlSchemaSAXHandleStartElementNs(ctxt->ctxt, localname, prefix, + URI, nb_namespaces, namespaces, + nb_attributes, nb_defaulted, + attributes); +} + +static void +endElementNsSplit(void *ctx, const xmlChar * localname, + const xmlChar * prefix, const xmlChar * URI) { + xmlSchemaSAXPlugPtr ctxt = (xmlSchemaSAXPlugPtr) ctx; + if (ctxt == NULL) + return; + if ((ctxt->user_sax != NULL) && + (ctxt->user_sax->endElementNs != NULL)) + ctxt->user_sax->endElementNs(ctxt->user_data, localname, prefix, URI); + if (ctxt->ctxt != NULL) + xmlSchemaSAXHandleEndElementNs(ctxt->ctxt, localname, prefix, URI); +} + +/** + * xmlSchemaSAXPlug: + * @ctxt: a schema validation context + * @sax: a pointer to the original xmlSAXHandlerPtr + * @user_data: a pointer to the original SAX user data pointer + * + * Plug a SAX based validation layer in a SAX parsing event flow. + * The original @saxptr and @dataptr data are replaced by new pointers + * but the calls to the original will be maintained. + * + * Returns a pointer to a data structure needed to unplug the validation layer + * or NULL in case of errors. + */ +xmlSchemaSAXPlugPtr +xmlSchemaSAXPlug(xmlSchemaValidCtxtPtr ctxt, + xmlSAXHandlerPtr *sax, void **user_data) +{ + xmlSchemaSAXPlugPtr ret; + xmlSAXHandlerPtr old_sax; + + if ((ctxt == NULL) || (sax == NULL) || (user_data == NULL)) + return(NULL); + + /* + * We only allow to plug into SAX2 event streams + */ + old_sax = *sax; + if ((old_sax != NULL) && (old_sax->initialized != XML_SAX2_MAGIC)) + return(NULL); + if ((old_sax != NULL) && + (old_sax->startElementNs == NULL) && (old_sax->endElementNs == NULL) && + ((old_sax->startElement != NULL) || (old_sax->endElement != NULL))) + return(NULL); + + /* + * everything seems right allocate the local data needed for that layer + */ + ret = (xmlSchemaSAXPlugPtr) xmlMalloc(sizeof(xmlSchemaSAXPlugStruct)); + if (ret == NULL) { + return(NULL); + } + memset(ret, 0, sizeof(xmlSchemaSAXPlugStruct)); + ret->magic = XML_SAX_PLUG_MAGIC; + ret->schemas_sax.initialized = XML_SAX2_MAGIC; + ret->ctxt = ctxt; + ret->user_sax_ptr = sax; + ret->user_sax = old_sax; + if (old_sax == NULL) { + /* + * go direct, no need for the split block and functions. + */ + ret->schemas_sax.startElementNs = xmlSchemaSAXHandleStartElementNs; + ret->schemas_sax.endElementNs = xmlSchemaSAXHandleEndElementNs; + /* + * Note that we use the same text-function for both, to prevent + * the parser from testing for ignorable whitespace. + */ + ret->schemas_sax.ignorableWhitespace = xmlSchemaSAXHandleText; + ret->schemas_sax.characters = xmlSchemaSAXHandleText; + + ret->schemas_sax.cdataBlock = xmlSchemaSAXHandleCDataSection; + ret->schemas_sax.reference = xmlSchemaSAXHandleReference; + + ret->user_data = ctxt; + *user_data = ctxt; + } else { + /* + * for each callback unused by Schemas initialize it to the Split + * routine only if non NULL in the user block, this can speed up + * things at the SAX level. + */ + if (old_sax->internalSubset != NULL) + ret->schemas_sax.internalSubset = internalSubsetSplit; + if (old_sax->isStandalone != NULL) + ret->schemas_sax.isStandalone = isStandaloneSplit; + if (old_sax->hasInternalSubset != NULL) + ret->schemas_sax.hasInternalSubset = hasInternalSubsetSplit; + if (old_sax->hasExternalSubset != NULL) + ret->schemas_sax.hasExternalSubset = hasExternalSubsetSplit; + if (old_sax->resolveEntity != NULL) + ret->schemas_sax.resolveEntity = resolveEntitySplit; + if (old_sax->getEntity != NULL) + ret->schemas_sax.getEntity = getEntitySplit; + if (old_sax->entityDecl != NULL) + ret->schemas_sax.entityDecl = entityDeclSplit; + if (old_sax->notationDecl != NULL) + ret->schemas_sax.notationDecl = notationDeclSplit; + if (old_sax->attributeDecl != NULL) + ret->schemas_sax.attributeDecl = attributeDeclSplit; + if (old_sax->elementDecl != NULL) + ret->schemas_sax.elementDecl = elementDeclSplit; + if (old_sax->unparsedEntityDecl != NULL) + ret->schemas_sax.unparsedEntityDecl = unparsedEntityDeclSplit; + if (old_sax->setDocumentLocator != NULL) + ret->schemas_sax.setDocumentLocator = setDocumentLocatorSplit; + if (old_sax->startDocument != NULL) + ret->schemas_sax.startDocument = startDocumentSplit; + if (old_sax->endDocument != NULL) + ret->schemas_sax.endDocument = endDocumentSplit; + if (old_sax->processingInstruction != NULL) + ret->schemas_sax.processingInstruction = processingInstructionSplit; + if (old_sax->comment != NULL) + ret->schemas_sax.comment = commentSplit; + if (old_sax->warning != NULL) + ret->schemas_sax.warning = warningSplit; + if (old_sax->error != NULL) + ret->schemas_sax.error = errorSplit; + if (old_sax->fatalError != NULL) + ret->schemas_sax.fatalError = fatalErrorSplit; + if (old_sax->getParameterEntity != NULL) + ret->schemas_sax.getParameterEntity = getParameterEntitySplit; + if (old_sax->externalSubset != NULL) + ret->schemas_sax.externalSubset = externalSubsetSplit; + + /* + * the 6 schemas callback have to go to the splitter functions + * Note that we use the same text-function for ignorableWhitespace + * if possible, to prevent the parser from testing for ignorable + * whitespace. + */ + ret->schemas_sax.characters = charactersSplit; + if ((old_sax->ignorableWhitespace != NULL) && + (old_sax->ignorableWhitespace != old_sax->characters)) + ret->schemas_sax.ignorableWhitespace = ignorableWhitespaceSplit; + else + ret->schemas_sax.ignorableWhitespace = charactersSplit; + ret->schemas_sax.cdataBlock = cdataBlockSplit; + ret->schemas_sax.reference = referenceSplit; + ret->schemas_sax.startElementNs = startElementNsSplit; + ret->schemas_sax.endElementNs = endElementNsSplit; + + ret->user_data_ptr = user_data; + ret->user_data = *user_data; + *user_data = ret; + } + + /* + * plug the pointers back. + */ + *sax = &(ret->schemas_sax); + ctxt->sax = *sax; + ctxt->flags |= XML_SCHEMA_VALID_CTXT_FLAG_STREAM; + xmlSchemaPreRun(ctxt); + return(ret); +} + +/** + * xmlSchemaSAXUnplug: + * @plug: a data structure returned by xmlSchemaSAXPlug + * + * Unplug a SAX based validation layer in a SAX parsing event flow. + * The original pointers used in the call are restored. + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +xmlSchemaSAXUnplug(xmlSchemaSAXPlugPtr plug) +{ + xmlSAXHandlerPtr *sax; + void **user_data; + + if ((plug == NULL) || (plug->magic != XML_SAX_PLUG_MAGIC)) + return(-1); + plug->magic = 0; + + xmlSchemaPostRun(plug->ctxt); + /* restore the data */ + sax = plug->user_sax_ptr; + *sax = plug->user_sax; + if (plug->user_sax != NULL) { + user_data = plug->user_data_ptr; + *user_data = plug->user_data; + } + + /* free and return */ + xmlFree(plug); + return(0); +} + +/** + * xmlSchemaValidateStream: + * @ctxt: a schema validation context + * @input: the input to use for reading the data + * @enc: an optional encoding information + * @sax: a SAX handler for the resulting events + * @user_data: the context to provide to the SAX handler. + * + * Validate an input based on a flow of SAX event from the parser + * and forward the events to the @sax handler with the provided @user_data + * the user provided @sax handler must be a SAX2 one. + * + * Returns 0 if the document is schemas valid, a positive error code + * number otherwise and -1 in case of internal or API error. + */ +int +xmlSchemaValidateStream(xmlSchemaValidCtxtPtr ctxt, + xmlParserInputBufferPtr input, xmlCharEncoding enc, + xmlSAXHandlerPtr sax, void *user_data) +{ + xmlSchemaSAXPlugPtr plug = NULL; + xmlSAXHandlerPtr old_sax = NULL; + xmlParserCtxtPtr pctxt = NULL; + xmlParserInputPtr inputStream = NULL; + int ret; + + if ((ctxt == NULL) || (input == NULL)) + return (-1); + + /* + * prepare the parser + */ + pctxt = xmlNewParserCtxt(); + if (pctxt == NULL) + return (-1); + old_sax = pctxt->sax; + pctxt->sax = sax; + pctxt->userData = user_data; +#if 0 + if (options) + xmlCtxtUseOptions(pctxt, options); +#endif + pctxt->linenumbers = 1; + + inputStream = xmlNewIOInputStream(pctxt, input, enc);; + if (inputStream == NULL) { + ret = -1; + goto done; + } + inputPush(pctxt, inputStream); + ctxt->parserCtxt = pctxt; + ctxt->input = input; + + /* + * Plug the validation and launch the parsing + */ + plug = xmlSchemaSAXPlug(ctxt, &(pctxt->sax), &(pctxt->userData)); + if (plug == NULL) { + ret = -1; + goto done; + } + ctxt->input = input; + ctxt->enc = enc; + ctxt->sax = pctxt->sax; + ctxt->flags |= XML_SCHEMA_VALID_CTXT_FLAG_STREAM; + ret = xmlSchemaVStart(ctxt); + + if ((ret == 0) && (! ctxt->parserCtxt->wellFormed)) { + ret = ctxt->parserCtxt->errNo; + if (ret == 0) + ret = 1; + } + +done: + ctxt->parserCtxt = NULL; + ctxt->sax = NULL; + ctxt->input = NULL; + if (plug != NULL) { + xmlSchemaSAXUnplug(plug); + } + /* cleanup */ + if (pctxt != NULL) { + pctxt->sax = old_sax; + xmlFreeParserCtxt(pctxt); + } + return (ret); +} + +/** + * xmlSchemaValidateFile: + * @ctxt: a schema validation context + * @filename: the URI of the instance + * @options: a future set of options, currently unused + * + * Do a schemas validation of the given resource, it will use the + * SAX streamable validation internally. + * + * Returns 0 if the document is valid, a positive error code + * number otherwise and -1 in case of an internal or API error. + */ +int +xmlSchemaValidateFile(xmlSchemaValidCtxtPtr ctxt, + const char * filename, + int options ATTRIBUTE_UNUSED) +{ + int ret; + xmlParserInputBufferPtr input; + + if ((ctxt == NULL) || (filename == NULL)) + return (-1); + + input = xmlParserInputBufferCreateFilename(filename, + XML_CHAR_ENCODING_NONE); + if (input == NULL) + return (-1); + ret = xmlSchemaValidateStream(ctxt, input, XML_CHAR_ENCODING_NONE, + NULL, NULL); + return (ret); +} + +#define bottom_xmlschemas +#include "elfgcchack.h" +#endif /* LIBXML_SCHEMAS_ENABLED */ diff --git a/Extras/LibXML/xmlschemastypes.c b/Extras/LibXML/xmlschemastypes.c new file mode 100644 index 000000000..987f85ff6 --- /dev/null +++ b/Extras/LibXML/xmlschemastypes.c @@ -0,0 +1,6101 @@ +/* + * schemastypes.c : implementation of the XML Schema Datatypes + * definition and validity checking + * + * See Copyright for the status of this software. + * + * Daniel Veillard + */ + +#define IN_LIBXML +#include "libxml.h" + +#ifdef LIBXML_SCHEMAS_ENABLED + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef HAVE_MATH_H +#include +#endif +#ifdef HAVE_FLOAT_H +#include +#endif + +#define DEBUG + +#ifndef LIBXML_XPATH_ENABLED +extern double xmlXPathNAN; +extern double xmlXPathPINF; +extern double xmlXPathNINF; +#endif + +#define TODO \ + xmlGenericError(xmlGenericErrorContext, \ + "Unimplemented block at %s:%d\n", \ + __FILE__, __LINE__); + +#define XML_SCHEMAS_NAMESPACE_NAME \ + (const xmlChar *)"http://www.w3.org/2001/XMLSchema" + +#define IS_WSP_REPLACE_CH(c) ((((c) == 0x9) || ((c) == 0xa)) || \ + ((c) == 0xd)) + +#define IS_WSP_SPACE_CH(c) ((c) == 0x20) + +#define IS_WSP_BLANK_CH(c) IS_BLANK_CH(c) + +/* Date value */ +typedef struct _xmlSchemaValDate xmlSchemaValDate; +typedef xmlSchemaValDate *xmlSchemaValDatePtr; +struct _xmlSchemaValDate { + long year; + unsigned int mon :4; /* 1 <= mon <= 12 */ + unsigned int day :5; /* 1 <= day <= 31 */ + unsigned int hour :5; /* 0 <= hour <= 23 */ + unsigned int min :6; /* 0 <= min <= 59 */ + double sec; + unsigned int tz_flag :1; /* is tzo explicitely set? */ + signed int tzo :12; /* -1440 <= tzo <= 1440; + currently only -840 to +840 are needed */ +}; + +/* Duration value */ +typedef struct _xmlSchemaValDuration xmlSchemaValDuration; +typedef xmlSchemaValDuration *xmlSchemaValDurationPtr; +struct _xmlSchemaValDuration { + long mon; /* mon stores years also */ + long day; + double sec; /* sec stores min and hour also */ +}; + +typedef struct _xmlSchemaValDecimal xmlSchemaValDecimal; +typedef xmlSchemaValDecimal *xmlSchemaValDecimalPtr; +struct _xmlSchemaValDecimal { + /* would use long long but not portable */ + unsigned long lo; + unsigned long mi; + unsigned long hi; + unsigned int extra; + unsigned int sign:1; + unsigned int frac:7; + unsigned int total:8; +}; + +typedef struct _xmlSchemaValQName xmlSchemaValQName; +typedef xmlSchemaValQName *xmlSchemaValQNamePtr; +struct _xmlSchemaValQName { + xmlChar *name; + xmlChar *uri; +}; + +typedef struct _xmlSchemaValHex xmlSchemaValHex; +typedef xmlSchemaValHex *xmlSchemaValHexPtr; +struct _xmlSchemaValHex { + xmlChar *str; + unsigned int total; +}; + +typedef struct _xmlSchemaValBase64 xmlSchemaValBase64; +typedef xmlSchemaValBase64 *xmlSchemaValBase64Ptr; +struct _xmlSchemaValBase64 { + xmlChar *str; + unsigned int total; +}; + +struct _xmlSchemaVal { + xmlSchemaValType type; + struct _xmlSchemaVal *next; + union { + xmlSchemaValDecimal decimal; + xmlSchemaValDate date; + xmlSchemaValDuration dur; + xmlSchemaValQName qname; + xmlSchemaValHex hex; + xmlSchemaValBase64 base64; + float f; + double d; + int b; + xmlChar *str; + } value; +}; + +static int xmlSchemaTypesInitialized = 0; +static xmlHashTablePtr xmlSchemaTypesBank = NULL; + +/* + * Basic types + */ +static xmlSchemaTypePtr xmlSchemaTypeStringDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeAnyTypeDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeAnySimpleTypeDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeDecimalDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeDatetimeDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeDateDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeTimeDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeGYearDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeGYearMonthDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeGDayDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeGMonthDayDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeGMonthDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeDurationDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeFloatDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeBooleanDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeDoubleDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeHexBinaryDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeBase64BinaryDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeAnyURIDef = NULL; + +/* + * Derived types + */ +static xmlSchemaTypePtr xmlSchemaTypePositiveIntegerDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeNonPositiveIntegerDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeNegativeIntegerDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeNonNegativeIntegerDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeIntegerDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeLongDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeIntDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeShortDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeByteDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeUnsignedLongDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeUnsignedIntDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeUnsignedShortDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeUnsignedByteDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeNormStringDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeTokenDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeLanguageDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeNameDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeQNameDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeNCNameDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeIdDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeIdrefDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeIdrefsDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeEntityDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeEntitiesDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeNotationDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeNmtokenDef = NULL; +static xmlSchemaTypePtr xmlSchemaTypeNmtokensDef = NULL; + +/************************************************************************ + * * + * 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 * + * * + ************************************************************************/ + +/** + * xmlSchemaNewValue: + * @type: the value type + * + * Allocate a new simple type value + * + * Returns a pointer to the new value or NULL in case of error + */ +static xmlSchemaValPtr +xmlSchemaNewValue(xmlSchemaValType type) { + xmlSchemaValPtr value; + + value = (xmlSchemaValPtr) xmlMalloc(sizeof(xmlSchemaVal)); + if (value == NULL) { + return(NULL); + } + memset(value, 0, sizeof(xmlSchemaVal)); + value->type = type; + return(value); +} + +static xmlSchemaFacetPtr +xmlSchemaNewMinLengthFacet(int value) +{ + xmlSchemaFacetPtr ret; + + ret = xmlSchemaNewFacet(); + if (ret == NULL) { + return(NULL); + } + ret->type = XML_SCHEMA_FACET_MINLENGTH; + ret->val = xmlSchemaNewValue(XML_SCHEMAS_NNINTEGER); + ret->val->value.decimal.lo = value; + return (ret); +} + +/* + * xmlSchemaInitBasicType: + * @name: the type name + * @type: the value type associated + * + * Initialize one primitive built-in type + */ +static xmlSchemaTypePtr +xmlSchemaInitBasicType(const char *name, xmlSchemaValType type, + xmlSchemaTypePtr baseType) { + 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->targetNamespace = XML_SCHEMAS_NAMESPACE_NAME; + ret->type = XML_SCHEMA_TYPE_BASIC; + ret->baseType = baseType; + ret->contentType = XML_SCHEMA_CONTENT_BASIC; + /* + * Primitive types. + */ + switch (type) { + case XML_SCHEMAS_STRING: + case XML_SCHEMAS_DECIMAL: + case XML_SCHEMAS_DATE: + case XML_SCHEMAS_DATETIME: + case XML_SCHEMAS_TIME: + case XML_SCHEMAS_GYEAR: + case XML_SCHEMAS_GYEARMONTH: + case XML_SCHEMAS_GMONTH: + case XML_SCHEMAS_GMONTHDAY: + case XML_SCHEMAS_GDAY: + case XML_SCHEMAS_DURATION: + case XML_SCHEMAS_FLOAT: + case XML_SCHEMAS_DOUBLE: + case XML_SCHEMAS_BOOLEAN: + case XML_SCHEMAS_ANYURI: + case XML_SCHEMAS_HEXBINARY: + case XML_SCHEMAS_BASE64BINARY: + case XML_SCHEMAS_QNAME: + case XML_SCHEMAS_NOTATION: + ret->flags |= XML_SCHEMAS_TYPE_BUILTIN_PRIMITIVE; + break; + default: + break; + } + /* + * Set variety. + */ + switch (type) { + case XML_SCHEMAS_ANYTYPE: + case XML_SCHEMAS_ANYSIMPLETYPE: + break; + case XML_SCHEMAS_IDREFS: + case XML_SCHEMAS_NMTOKENS: + case XML_SCHEMAS_ENTITIES: + ret->flags |= XML_SCHEMAS_TYPE_VARIETY_LIST; + ret->facets = xmlSchemaNewMinLengthFacet(1); + ret->flags |= XML_SCHEMAS_TYPE_HAS_FACETS; + break; + default: + ret->flags |= XML_SCHEMAS_TYPE_VARIETY_ATOMIC; + break; + } + xmlHashAddEntry2(xmlSchemaTypesBank, ret->name, + XML_SCHEMAS_NAMESPACE_NAME, ret); + ret->builtInType = type; + return(ret); +} + +/* +* WARNING: Those type reside normally in xmlschemas.c but are +* redefined here locally in oder of being able to use them for xs:anyType- +* TODO: Remove those definition if we move the types to a header file. +* TODO: Always keep those structs up-to-date with the originals. +*/ +#define UNBOUNDED (1 << 30) + +typedef struct _xmlSchemaTreeItem xmlSchemaTreeItem; +typedef xmlSchemaTreeItem *xmlSchemaTreeItemPtr; +struct _xmlSchemaTreeItem { + xmlSchemaTypeType type; + xmlSchemaAnnotPtr annot; + xmlSchemaTreeItemPtr next; + xmlSchemaTreeItemPtr children; +}; + +typedef struct _xmlSchemaParticle xmlSchemaParticle; +typedef xmlSchemaParticle *xmlSchemaParticlePtr; +struct _xmlSchemaParticle { + xmlSchemaTypeType type; + xmlSchemaAnnotPtr annot; + xmlSchemaTreeItemPtr next; + xmlSchemaTreeItemPtr children; + int minOccurs; + int maxOccurs; + xmlNodePtr node; +}; + +typedef struct _xmlSchemaModelGroup xmlSchemaModelGroup; +typedef xmlSchemaModelGroup *xmlSchemaModelGroupPtr; +struct _xmlSchemaModelGroup { + xmlSchemaTypeType type; + xmlSchemaAnnotPtr annot; + xmlSchemaTreeItemPtr next; + xmlSchemaTreeItemPtr children; + xmlNodePtr node; +}; + +static xmlSchemaParticlePtr +xmlSchemaAddParticle(void) +{ + xmlSchemaParticlePtr ret = NULL; + + ret = (xmlSchemaParticlePtr) + xmlMalloc(sizeof(xmlSchemaParticle)); + if (ret == NULL) { + xmlSchemaTypeErrMemory(NULL, "allocating particle component"); + return (NULL); + } + memset(ret, 0, sizeof(xmlSchemaParticle)); + ret->type = XML_SCHEMA_TYPE_PARTICLE; + ret->minOccurs = 1; + ret->maxOccurs = 1; + return (ret); +} + +/* + * xmlSchemaInitTypes: + * + * Initialize the default XML Schemas type library + */ +void +xmlSchemaInitTypes(void) +{ + if (xmlSchemaTypesInitialized != 0) + return; + xmlSchemaTypesBank = xmlHashCreate(40); + + + /* + * 3.4.7 Built-in Complex Type Definition + */ + xmlSchemaTypeAnyTypeDef = xmlSchemaInitBasicType("anyType", + XML_SCHEMAS_ANYTYPE, + NULL); + xmlSchemaTypeAnyTypeDef->baseType = xmlSchemaTypeAnyTypeDef; + xmlSchemaTypeAnyTypeDef->contentType = XML_SCHEMA_CONTENT_MIXED; + /* + * Init the content type. + */ + xmlSchemaTypeAnyTypeDef->contentType = XML_SCHEMA_CONTENT_MIXED; + { + xmlSchemaParticlePtr particle; + xmlSchemaModelGroupPtr sequence; + xmlSchemaWildcardPtr wild; + /* First particle. */ + particle = xmlSchemaAddParticle(); + if (particle == NULL) + return; + xmlSchemaTypeAnyTypeDef->subtypes = (xmlSchemaTypePtr) particle; + /* Sequence model group. */ + sequence = (xmlSchemaModelGroupPtr) + xmlMalloc(sizeof(xmlSchemaModelGroup)); + if (sequence == NULL) { + xmlSchemaTypeErrMemory(NULL, "allocating model group component"); + return; + } + memset(sequence, 0, sizeof(xmlSchemaModelGroup)); + sequence->type = XML_SCHEMA_TYPE_SEQUENCE; + particle->children = (xmlSchemaTreeItemPtr) sequence; + /* Second particle. */ + particle = xmlSchemaAddParticle(); + if (particle == NULL) + return; + particle->minOccurs = 0; + particle->maxOccurs = UNBOUNDED; + sequence->children = (xmlSchemaTreeItemPtr) particle; + /* The wildcard */ + wild = (xmlSchemaWildcardPtr) xmlMalloc(sizeof(xmlSchemaWildcard)); + if (wild == NULL) { + xmlSchemaTypeErrMemory(NULL, "allocating wildcard component"); + return; + } + memset(wild, 0, sizeof(xmlSchemaWildcard)); + wild->type = XML_SCHEMA_TYPE_ANY; + wild->any = 1; + wild->processContents = XML_SCHEMAS_ANY_LAX; + particle->children = (xmlSchemaTreeItemPtr) wild; + /* + * Create the attribute wildcard. + */ + wild = (xmlSchemaWildcardPtr) xmlMalloc(sizeof(xmlSchemaWildcard)); + if (wild == NULL) { + xmlSchemaTypeErrMemory(NULL, "could not create an attribute " + "wildcard on anyType"); + return; + } + memset(wild, 0, sizeof(xmlSchemaWildcard)); + wild->any = 1; + wild->processContents = XML_SCHEMAS_ANY_LAX; + xmlSchemaTypeAnyTypeDef->attributeWildcard = wild; + } + xmlSchemaTypeAnySimpleTypeDef = xmlSchemaInitBasicType("anySimpleType", + XML_SCHEMAS_ANYSIMPLETYPE, + xmlSchemaTypeAnyTypeDef); + /* + * primitive datatypes + */ + xmlSchemaTypeStringDef = xmlSchemaInitBasicType("string", + XML_SCHEMAS_STRING, + xmlSchemaTypeAnySimpleTypeDef); + xmlSchemaTypeDecimalDef = xmlSchemaInitBasicType("decimal", + XML_SCHEMAS_DECIMAL, + xmlSchemaTypeAnySimpleTypeDef); + xmlSchemaTypeDateDef = xmlSchemaInitBasicType("date", + XML_SCHEMAS_DATE, + xmlSchemaTypeAnySimpleTypeDef); + xmlSchemaTypeDatetimeDef = xmlSchemaInitBasicType("dateTime", + XML_SCHEMAS_DATETIME, + xmlSchemaTypeAnySimpleTypeDef); + xmlSchemaTypeTimeDef = xmlSchemaInitBasicType("time", + XML_SCHEMAS_TIME, + xmlSchemaTypeAnySimpleTypeDef); + xmlSchemaTypeGYearDef = xmlSchemaInitBasicType("gYear", + XML_SCHEMAS_GYEAR, + xmlSchemaTypeAnySimpleTypeDef); + xmlSchemaTypeGYearMonthDef = xmlSchemaInitBasicType("gYearMonth", + XML_SCHEMAS_GYEARMONTH, + xmlSchemaTypeAnySimpleTypeDef); + xmlSchemaTypeGMonthDef = xmlSchemaInitBasicType("gMonth", + XML_SCHEMAS_GMONTH, + xmlSchemaTypeAnySimpleTypeDef); + xmlSchemaTypeGMonthDayDef = xmlSchemaInitBasicType("gMonthDay", + XML_SCHEMAS_GMONTHDAY, + xmlSchemaTypeAnySimpleTypeDef); + xmlSchemaTypeGDayDef = xmlSchemaInitBasicType("gDay", + XML_SCHEMAS_GDAY, + xmlSchemaTypeAnySimpleTypeDef); + xmlSchemaTypeDurationDef = xmlSchemaInitBasicType("duration", + XML_SCHEMAS_DURATION, + xmlSchemaTypeAnySimpleTypeDef); + xmlSchemaTypeFloatDef = xmlSchemaInitBasicType("float", + XML_SCHEMAS_FLOAT, + xmlSchemaTypeAnySimpleTypeDef); + xmlSchemaTypeDoubleDef = xmlSchemaInitBasicType("double", + XML_SCHEMAS_DOUBLE, + xmlSchemaTypeAnySimpleTypeDef); + xmlSchemaTypeBooleanDef = xmlSchemaInitBasicType("boolean", + XML_SCHEMAS_BOOLEAN, + xmlSchemaTypeAnySimpleTypeDef); + xmlSchemaTypeAnyURIDef = xmlSchemaInitBasicType("anyURI", + XML_SCHEMAS_ANYURI, + xmlSchemaTypeAnySimpleTypeDef); + xmlSchemaTypeHexBinaryDef = xmlSchemaInitBasicType("hexBinary", + XML_SCHEMAS_HEXBINARY, + xmlSchemaTypeAnySimpleTypeDef); + xmlSchemaTypeBase64BinaryDef + = xmlSchemaInitBasicType("base64Binary", XML_SCHEMAS_BASE64BINARY, + xmlSchemaTypeAnySimpleTypeDef); + xmlSchemaTypeNotationDef = xmlSchemaInitBasicType("NOTATION", + XML_SCHEMAS_NOTATION, + xmlSchemaTypeAnySimpleTypeDef); + xmlSchemaTypeQNameDef = xmlSchemaInitBasicType("QName", + XML_SCHEMAS_QNAME, + xmlSchemaTypeAnySimpleTypeDef); + + /* + * derived datatypes + */ + xmlSchemaTypeIntegerDef = xmlSchemaInitBasicType("integer", + XML_SCHEMAS_INTEGER, + xmlSchemaTypeDecimalDef); + xmlSchemaTypeNonPositiveIntegerDef = + xmlSchemaInitBasicType("nonPositiveInteger", + XML_SCHEMAS_NPINTEGER, + xmlSchemaTypeIntegerDef); + xmlSchemaTypeNegativeIntegerDef = + xmlSchemaInitBasicType("negativeInteger", XML_SCHEMAS_NINTEGER, + xmlSchemaTypeNonPositiveIntegerDef); + xmlSchemaTypeLongDef = + xmlSchemaInitBasicType("long", XML_SCHEMAS_LONG, + xmlSchemaTypeIntegerDef); + xmlSchemaTypeIntDef = xmlSchemaInitBasicType("int", XML_SCHEMAS_INT, + xmlSchemaTypeLongDef); + xmlSchemaTypeShortDef = xmlSchemaInitBasicType("short", + XML_SCHEMAS_SHORT, + xmlSchemaTypeIntDef); + xmlSchemaTypeByteDef = xmlSchemaInitBasicType("byte", + XML_SCHEMAS_BYTE, + xmlSchemaTypeShortDef); + xmlSchemaTypeNonNegativeIntegerDef = + xmlSchemaInitBasicType("nonNegativeInteger", + XML_SCHEMAS_NNINTEGER, + xmlSchemaTypeIntegerDef); + xmlSchemaTypeUnsignedLongDef = + xmlSchemaInitBasicType("unsignedLong", XML_SCHEMAS_ULONG, + xmlSchemaTypeNonNegativeIntegerDef); + xmlSchemaTypeUnsignedIntDef = + xmlSchemaInitBasicType("unsignedInt", XML_SCHEMAS_UINT, + xmlSchemaTypeUnsignedLongDef); + xmlSchemaTypeUnsignedShortDef = + xmlSchemaInitBasicType("unsignedShort", XML_SCHEMAS_USHORT, + xmlSchemaTypeUnsignedIntDef); + xmlSchemaTypeUnsignedByteDef = + xmlSchemaInitBasicType("unsignedByte", XML_SCHEMAS_UBYTE, + xmlSchemaTypeUnsignedShortDef); + xmlSchemaTypePositiveIntegerDef = + xmlSchemaInitBasicType("positiveInteger", XML_SCHEMAS_PINTEGER, + xmlSchemaTypeNonNegativeIntegerDef); + xmlSchemaTypeNormStringDef = xmlSchemaInitBasicType("normalizedString", + XML_SCHEMAS_NORMSTRING, + xmlSchemaTypeStringDef); + xmlSchemaTypeTokenDef = xmlSchemaInitBasicType("token", + XML_SCHEMAS_TOKEN, + xmlSchemaTypeNormStringDef); + xmlSchemaTypeLanguageDef = xmlSchemaInitBasicType("language", + XML_SCHEMAS_LANGUAGE, + xmlSchemaTypeTokenDef); + xmlSchemaTypeNameDef = xmlSchemaInitBasicType("Name", + XML_SCHEMAS_NAME, + xmlSchemaTypeTokenDef); + xmlSchemaTypeNmtokenDef = xmlSchemaInitBasicType("NMTOKEN", + XML_SCHEMAS_NMTOKEN, + xmlSchemaTypeTokenDef); + xmlSchemaTypeNCNameDef = xmlSchemaInitBasicType("NCName", + XML_SCHEMAS_NCNAME, + xmlSchemaTypeNameDef); + xmlSchemaTypeIdDef = xmlSchemaInitBasicType("ID", XML_SCHEMAS_ID, + xmlSchemaTypeNCNameDef); + xmlSchemaTypeIdrefDef = xmlSchemaInitBasicType("IDREF", + XML_SCHEMAS_IDREF, + xmlSchemaTypeNCNameDef); + xmlSchemaTypeEntityDef = xmlSchemaInitBasicType("ENTITY", + XML_SCHEMAS_ENTITY, + xmlSchemaTypeNCNameDef); + /* + * Derived list types. + */ + /* ENTITIES */ + xmlSchemaTypeEntitiesDef = xmlSchemaInitBasicType("ENTITIES", + XML_SCHEMAS_ENTITIES, + xmlSchemaTypeAnySimpleTypeDef); + xmlSchemaTypeEntitiesDef->subtypes = xmlSchemaTypeEntityDef; + /* IDREFS */ + xmlSchemaTypeIdrefsDef = xmlSchemaInitBasicType("IDREFS", + XML_SCHEMAS_IDREFS, + xmlSchemaTypeAnySimpleTypeDef); + xmlSchemaTypeIdrefsDef->subtypes = xmlSchemaTypeIdrefDef; + + /* NMTOKENS */ + xmlSchemaTypeNmtokensDef = xmlSchemaInitBasicType("NMTOKENS", + XML_SCHEMAS_NMTOKENS, + xmlSchemaTypeAnySimpleTypeDef); + xmlSchemaTypeNmtokensDef->subtypes = xmlSchemaTypeNmtokenDef; + + xmlSchemaTypesInitialized = 1; +} + +/** + * xmlSchemaCleanupTypes: + * + * Cleanup the default XML Schemas type library + */ +void +xmlSchemaCleanupTypes(void) { + if (xmlSchemaTypesInitialized == 0) + return; + /* + * Free xs:anyType. + */ + { + xmlSchemaParticlePtr particle; + /* Attribute wildcard. */ + xmlSchemaFreeWildcard(xmlSchemaTypeAnyTypeDef->attributeWildcard); + /* Content type. */ + particle = (xmlSchemaParticlePtr) xmlSchemaTypeAnyTypeDef->subtypes; + /* Wildcard. */ + xmlSchemaFreeWildcard((xmlSchemaWildcardPtr) + particle->children->children->children); + xmlFree((xmlSchemaParticlePtr) particle->children->children); + /* Sequence model group. */ + xmlFree((xmlSchemaModelGroupPtr) particle->children); + xmlFree((xmlSchemaParticlePtr) particle); + xmlSchemaTypeAnyTypeDef->subtypes = NULL; + } + xmlHashFree(xmlSchemaTypesBank, (xmlHashDeallocator) xmlSchemaFreeType); + xmlSchemaTypesInitialized = 0; +} + +/** + * xmlSchemaIsBuiltInTypeFacet: + * @type: the built-in type + * @facetType: the facet type + * + * Evaluates if a specific facet can be + * used in conjunction with a type. + * + * Returns 1 if the facet can be used with the given built-in type, + * 0 otherwise and -1 in case the type is not a built-in type. + */ +int +xmlSchemaIsBuiltInTypeFacet(xmlSchemaTypePtr type, int facetType) +{ + if (type == NULL) + return (-1); + if (type->type != XML_SCHEMA_TYPE_BASIC) + return (-1); + switch (type->builtInType) { + case XML_SCHEMAS_BOOLEAN: + if ((facetType == XML_SCHEMA_FACET_PATTERN) || + (facetType == XML_SCHEMA_FACET_WHITESPACE)) + return (1); + else + return (0); + case XML_SCHEMAS_STRING: + case XML_SCHEMAS_NOTATION: + case XML_SCHEMAS_QNAME: + case XML_SCHEMAS_ANYURI: + case XML_SCHEMAS_BASE64BINARY: + case XML_SCHEMAS_HEXBINARY: + if ((facetType == XML_SCHEMA_FACET_LENGTH) || + (facetType == XML_SCHEMA_FACET_MINLENGTH) || + (facetType == XML_SCHEMA_FACET_MAXLENGTH) || + (facetType == XML_SCHEMA_FACET_PATTERN) || + (facetType == XML_SCHEMA_FACET_ENUMERATION) || + (facetType == XML_SCHEMA_FACET_WHITESPACE)) + return (1); + else + return (0); + case XML_SCHEMAS_DECIMAL: + if ((facetType == XML_SCHEMA_FACET_TOTALDIGITS) || + (facetType == XML_SCHEMA_FACET_FRACTIONDIGITS) || + (facetType == XML_SCHEMA_FACET_PATTERN) || + (facetType == XML_SCHEMA_FACET_WHITESPACE) || + (facetType == XML_SCHEMA_FACET_ENUMERATION) || + (facetType == XML_SCHEMA_FACET_MAXINCLUSIVE) || + (facetType == XML_SCHEMA_FACET_MAXEXCLUSIVE) || + (facetType == XML_SCHEMA_FACET_MININCLUSIVE) || + (facetType == XML_SCHEMA_FACET_MINEXCLUSIVE)) + return (1); + else + return (0); + 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: + case XML_SCHEMAS_DURATION: + case XML_SCHEMAS_FLOAT: + case XML_SCHEMAS_DOUBLE: + if ((facetType == XML_SCHEMA_FACET_PATTERN) || + (facetType == XML_SCHEMA_FACET_ENUMERATION) || + (facetType == XML_SCHEMA_FACET_WHITESPACE) || + (facetType == XML_SCHEMA_FACET_MAXINCLUSIVE) || + (facetType == XML_SCHEMA_FACET_MAXEXCLUSIVE) || + (facetType == XML_SCHEMA_FACET_MININCLUSIVE) || + (facetType == XML_SCHEMA_FACET_MINEXCLUSIVE)) + return (1); + else + return (0); + default: + break; + } + return (0); +} + +/** + * xmlSchemaGetBuiltInType: + * @type: the type of the built in type + * + * Gives you the type struct for a built-in + * type by its type id. + * + * Returns the type if found, NULL otherwise. + */ +xmlSchemaTypePtr +xmlSchemaGetBuiltInType(xmlSchemaValType type) +{ + if (xmlSchemaTypesInitialized == 0) + xmlSchemaInitTypes(); + switch (type) { + + case XML_SCHEMAS_ANYSIMPLETYPE: + return (xmlSchemaTypeAnySimpleTypeDef); + case XML_SCHEMAS_STRING: + return (xmlSchemaTypeStringDef); + case XML_SCHEMAS_NORMSTRING: + return (xmlSchemaTypeNormStringDef); + case XML_SCHEMAS_DECIMAL: + return (xmlSchemaTypeDecimalDef); + case XML_SCHEMAS_TIME: + return (xmlSchemaTypeTimeDef); + case XML_SCHEMAS_GDAY: + return (xmlSchemaTypeGDayDef); + case XML_SCHEMAS_GMONTH: + return (xmlSchemaTypeGMonthDef); + case XML_SCHEMAS_GMONTHDAY: + return (xmlSchemaTypeGMonthDayDef); + case XML_SCHEMAS_GYEAR: + return (xmlSchemaTypeGYearDef); + case XML_SCHEMAS_GYEARMONTH: + return (xmlSchemaTypeGYearMonthDef); + case XML_SCHEMAS_DATE: + return (xmlSchemaTypeDateDef); + case XML_SCHEMAS_DATETIME: + return (xmlSchemaTypeDatetimeDef); + case XML_SCHEMAS_DURATION: + return (xmlSchemaTypeDurationDef); + case XML_SCHEMAS_FLOAT: + return (xmlSchemaTypeFloatDef); + case XML_SCHEMAS_DOUBLE: + return (xmlSchemaTypeDoubleDef); + case XML_SCHEMAS_BOOLEAN: + return (xmlSchemaTypeBooleanDef); + case XML_SCHEMAS_TOKEN: + return (xmlSchemaTypeTokenDef); + case XML_SCHEMAS_LANGUAGE: + return (xmlSchemaTypeLanguageDef); + case XML_SCHEMAS_NMTOKEN: + return (xmlSchemaTypeNmtokenDef); + case XML_SCHEMAS_NMTOKENS: + return (xmlSchemaTypeNmtokensDef); + case XML_SCHEMAS_NAME: + return (xmlSchemaTypeNameDef); + case XML_SCHEMAS_QNAME: + return (xmlSchemaTypeQNameDef); + case XML_SCHEMAS_NCNAME: + return (xmlSchemaTypeNCNameDef); + case XML_SCHEMAS_ID: + return (xmlSchemaTypeIdDef); + case XML_SCHEMAS_IDREF: + return (xmlSchemaTypeIdrefDef); + case XML_SCHEMAS_IDREFS: + return (xmlSchemaTypeIdrefsDef); + case XML_SCHEMAS_ENTITY: + return (xmlSchemaTypeEntityDef); + case XML_SCHEMAS_ENTITIES: + return (xmlSchemaTypeEntitiesDef); + case XML_SCHEMAS_NOTATION: + return (xmlSchemaTypeNotationDef); + case XML_SCHEMAS_ANYURI: + return (xmlSchemaTypeAnyURIDef); + case XML_SCHEMAS_INTEGER: + return (xmlSchemaTypeIntegerDef); + case XML_SCHEMAS_NPINTEGER: + return (xmlSchemaTypeNonPositiveIntegerDef); + case XML_SCHEMAS_NINTEGER: + return (xmlSchemaTypeNegativeIntegerDef); + case XML_SCHEMAS_NNINTEGER: + return (xmlSchemaTypeNonNegativeIntegerDef); + case XML_SCHEMAS_PINTEGER: + return (xmlSchemaTypePositiveIntegerDef); + case XML_SCHEMAS_INT: + return (xmlSchemaTypeIntDef); + case XML_SCHEMAS_UINT: + return (xmlSchemaTypeUnsignedIntDef); + case XML_SCHEMAS_LONG: + return (xmlSchemaTypeLongDef); + case XML_SCHEMAS_ULONG: + return (xmlSchemaTypeUnsignedLongDef); + case XML_SCHEMAS_SHORT: + return (xmlSchemaTypeShortDef); + case XML_SCHEMAS_USHORT: + return (xmlSchemaTypeUnsignedShortDef); + case XML_SCHEMAS_BYTE: + return (xmlSchemaTypeByteDef); + case XML_SCHEMAS_UBYTE: + return (xmlSchemaTypeUnsignedByteDef); + case XML_SCHEMAS_HEXBINARY: + return (xmlSchemaTypeHexBinaryDef); + case XML_SCHEMAS_BASE64BINARY: + return (xmlSchemaTypeBase64BinaryDef); + case XML_SCHEMAS_ANYTYPE: + return (xmlSchemaTypeAnyTypeDef); + default: + return (NULL); + } +} + +/** + * xmlSchemaValueAppend: + * @prev: the value + * @cur: the value to be appended + * + * Appends a next sibling to a list of computed values. + * + * Returns 0 if succeeded and -1 on API errors. + */ +int +xmlSchemaValueAppend(xmlSchemaValPtr prev, xmlSchemaValPtr cur) { + + if ((prev == NULL) || (cur == NULL)) + return (-1); + prev->next = cur; + return (0); +} + +/** + * xmlSchemaValueGetNext: + * @cur: the value + * + * Accessor for the next sibling of a list of computed values. + * + * Returns the next value or NULL if there was none, or on + * API errors. + */ +xmlSchemaValPtr +xmlSchemaValueGetNext(xmlSchemaValPtr cur) { + + if (cur == NULL) + return (NULL); + return (cur->next); +} + +/** + * xmlSchemaValueGetAsString: + * @val: the value + * + * Accessor for the string value of a computed value. + * + * Returns the string value or NULL if there was none, or on + * API errors. + */ +const xmlChar * +xmlSchemaValueGetAsString(xmlSchemaValPtr val) +{ + if (val == NULL) + return (NULL); + switch (val->type) { + case XML_SCHEMAS_STRING: + case XML_SCHEMAS_NORMSTRING: + case XML_SCHEMAS_ANYSIMPLETYPE: + 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_ANYURI: + return (BAD_CAST val->value.str); + default: + break; + } + return (NULL); +} + +/** + * xmlSchemaValueGetAsBoolean: + * @val: the value + * + * Accessor for the boolean value of a computed value. + * + * Returns 1 if true and 0 if false, or in case of an error. Hmm. + */ +int +xmlSchemaValueGetAsBoolean(xmlSchemaValPtr val) +{ + if ((val == NULL) || (val->type != XML_SCHEMAS_BOOLEAN)) + return (0); + return (val->value.b); +} + +/** + * xmlSchemaNewStringValue: + * @type: the value type + * @value: the value + * + * Allocate a new simple type value. The type can be + * of XML_SCHEMAS_STRING. + * WARNING: This one is intended to be expanded for other + * string based types. We need this for anySimpleType as well. + * The given value is consumed and freed with the struct. + * + * Returns a pointer to the new value or NULL in case of error + */ +xmlSchemaValPtr +xmlSchemaNewStringValue(xmlSchemaValType type, + const xmlChar *value) +{ + xmlSchemaValPtr val; + + if (type != XML_SCHEMAS_STRING) + return(NULL); + val = (xmlSchemaValPtr) xmlMalloc(sizeof(xmlSchemaVal)); + if (val == NULL) { + return(NULL); + } + memset(val, 0, sizeof(xmlSchemaVal)); + val->type = type; + val->value.str = (xmlChar *) value; + return(val); +} + +/** + * xmlSchemaNewNOTATIONValue: + * @name: the notation name + * @ns: the notation namespace name or NULL + * + * Allocate a new NOTATION value. + * The given values are consumed and freed with the struct. + * + * Returns a pointer to the new value or NULL in case of error + */ +xmlSchemaValPtr +xmlSchemaNewNOTATIONValue(const xmlChar *name, + const xmlChar *ns) +{ + xmlSchemaValPtr val; + + val = xmlSchemaNewValue(XML_SCHEMAS_NOTATION); + if (val == NULL) + return (NULL); + + val->value.qname.name = (xmlChar *)name; + if (ns != NULL) + val->value.qname.uri = (xmlChar *)ns; + return(val); +} + +/** + * xmlSchemaNewQNameValue: + * @namespaceName: the namespace name + * @localName: the local name + * + * Allocate a new QName value. + * The given values are consumed and freed with the struct. + * + * Returns a pointer to the new value or NULL in case of an error. + */ +xmlSchemaValPtr +xmlSchemaNewQNameValue(const xmlChar *namespaceName, + const xmlChar *localName) +{ + xmlSchemaValPtr val; + + val = xmlSchemaNewValue(XML_SCHEMAS_QNAME); + if (val == NULL) + return (NULL); + + val->value.qname.name = (xmlChar *) localName; + val->value.qname.uri = (xmlChar *) namespaceName; + return(val); +} + +/** + * xmlSchemaFreeValue: + * @value: the value to free + * + * Cleanup the default XML Schemas type library + */ +void +xmlSchemaFreeValue(xmlSchemaValPtr value) { + xmlSchemaValPtr prev; + + while (value != NULL) { + 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_ANYURI: + case XML_SCHEMAS_ANYSIMPLETYPE: + if (value->value.str != NULL) + xmlFree(value->value.str); + break; + case XML_SCHEMAS_NOTATION: + 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; + } + prev = value; + value = value->next; + xmlFree(prev); + } +} + +/** + * 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)); +} + +/** + * xmlSchemaGetBuiltInListSimpleTypeItemType: + * @type: the built-in simple type. + * + * Lookup function + * + * Returns the item type of @type as defined by the built-in datatype + * hierarchy of XML Schema Part 2: Datatypes, or NULL in case of an error. + */ +xmlSchemaTypePtr +xmlSchemaGetBuiltInListSimpleTypeItemType(xmlSchemaTypePtr type) +{ + if ((type == NULL) || (type->type != XML_SCHEMA_TYPE_BASIC)) + return (NULL); + switch (type->builtInType) { + case XML_SCHEMAS_NMTOKENS: + return (xmlSchemaTypeNmtokenDef ); + case XML_SCHEMAS_IDREFS: + return (xmlSchemaTypeIdrefDef); + case XML_SCHEMAS_ENTITIES: + return (xmlSchemaTypeEntityDef); + default: + return (NULL); + } +} + +/**************************************************************** + * * + * 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 > -840) && (tzo < 840)) +#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 + */ +static int +_xmlSchemaParseGYear (xmlSchemaValDatePtr dt, const xmlChar **str) { + const xmlChar *cur = *str, *firstChar; + int isneg = 0, digcnt = 0; + + if (((*cur < '0') || (*cur > '9')) && + (*cur != '-') && (*cur != '+')) + return -1; + + if (*cur == '-') { + isneg = 1; + cur++; + } + + firstChar = cur; + + while ((*cur >= '0') && (*cur <= '9')) { + dt->year = dt->year * 10 + (*cur - '0'); + cur++; + digcnt++; + } + + /* year must be at least 4 digits (CCYY); over 4 + * digits cannot have a leading zero. */ + if ((digcnt < 4) || ((digcnt > 4) && (*firstChar == '0'))) + return 1; + + if (isneg) + dt->year = - dt->year; + + if (!VALID_YEAR(dt->year)) + return 2; + + *str = cur; + return 0; +} + +/** + * PARSE_2_DIGITS: + * @num: the integer to fill in + * @cur: an #xmlChar * + * @invalid: an integer + * + * Parses a 2-digits integer and updates @num with the value. @cur is + * updated to point just after the integer. + * In case of error, @invalid is set to %TRUE, values of @num and + * @cur are undefined. + */ +#define PARSE_2_DIGITS(num, cur, invalid) \ + if ((cur[0] < '0') || (cur[0] > '9') || \ + (cur[1] < '0') || (cur[1] > '9')) \ + invalid = 1; \ + else \ + num = (cur[0] - '0') * 10 + (cur[1] - '0'); \ + cur += 2; + +/** + * PARSE_FLOAT: + * @num: the double to fill in + * @cur: an #xmlChar * + * @invalid: an integer + * + * Parses a float and updates @num with the value. @cur is + * updated to point just after the float. The float must have a + * 2-digits integer part and may or may not have a decimal part. + * In case of error, @invalid is set to %TRUE, values of @num and + * @cur are undefined. + */ +#define PARSE_FLOAT(num, cur, invalid) \ + PARSE_2_DIGITS(num, cur, invalid); \ + if (!invalid && (*cur == '.')) { \ + double mult = 1; \ + cur++; \ + if ((*cur < '0') || (*cur > '9')) \ + invalid = 1; \ + while ((*cur >= '0') && (*cur <= '9')) { \ + mult /= 10; \ + num += (*cur - '0') * mult; \ + cur++; \ + } \ + } + +/** + * _xmlSchemaParseGMonth: + * @dt: pointer to a date structure + * @str: pointer to the string to analyze + * + * Parses a xs:gMonth without time zone and fills in the appropriate + * field of the @dt structure. @str is updated to point just after the + * xs:gMonth. + * + * Returns 0 or the error code + */ +static int +_xmlSchemaParseGMonth (xmlSchemaValDatePtr dt, const xmlChar **str) { + const xmlChar *cur = *str; + int ret = 0; + unsigned int value = 0; + + PARSE_2_DIGITS(value, cur, ret); + if (ret != 0) + return ret; + + if (!VALID_MONTH(value)) + return 2; + + dt->mon = value; + + *str = cur; + return 0; +} + +/** + * _xmlSchemaParseGDay: + * @dt: pointer to a date structure + * @str: pointer to the string to analyze + * + * Parses a xs:gDay without time zone and fills in the appropriate + * field of the @dt structure. @str is updated to point just after the + * xs:gDay. + * + * Returns 0 or the error code + */ +static int +_xmlSchemaParseGDay (xmlSchemaValDatePtr dt, const xmlChar **str) { + const xmlChar *cur = *str; + int ret = 0; + unsigned int value = 0; + + PARSE_2_DIGITS(value, cur, ret); + if (ret != 0) + return ret; + + if (!VALID_DAY(value)) + return 2; + + dt->day = value; + *str = cur; + return 0; +} + +/** + * _xmlSchemaParseTime: + * @dt: pointer to a date structure + * @str: pointer to the string to analyze + * + * Parses a xs:time without time zone and fills in the appropriate + * fields of the @dt structure. @str is updated to point just after the + * xs:time. + * In case of error, values of @dt fields are undefined. + * + * Returns 0 or the error code + */ +static int +_xmlSchemaParseTime (xmlSchemaValDatePtr dt, const xmlChar **str) { + const xmlChar *cur = *str; + int ret = 0; + int value = 0; + + PARSE_2_DIGITS(value, cur, ret); + if (ret != 0) + return ret; + if (*cur != ':') + return 1; + if (!VALID_HOUR(value)) + return 2; + cur++; + + /* the ':' insures this string is xs:time */ + dt->hour = value; + + PARSE_2_DIGITS(value, cur, ret); + if (ret != 0) + return ret; + if (!VALID_MIN(value)) + return 2; + dt->min = value; + + if (*cur != ':') + return 1; + cur++; + + PARSE_FLOAT(dt->sec, cur, ret); + if (ret != 0) + return ret; + + if ((!VALID_SEC(dt->sec)) || (!VALID_TZO(dt->tzo))) + return 2; + + *str = cur; + return 0; +} + +/** + * _xmlSchemaParseTimeZone: + * @dt: pointer to a date structure + * @str: pointer to the string to analyze + * + * Parses a time zone without time zone and fills in the appropriate + * field of the @dt structure. @str is updated to point just after the + * time zone. + * + * Returns 0 or the error code + */ +static int +_xmlSchemaParseTimeZone (xmlSchemaValDatePtr dt, const xmlChar **str) { + const xmlChar *cur; + int ret = 0; + + if (str == NULL) + return -1; + cur = *str; + + 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) + */ +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. + */ +static int +xmlSchemaValidateDates (xmlSchemaValType type, + const xmlChar *dateTime, xmlSchemaValPtr *val, + int collapse) { + 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 (collapse) + while IS_WSP_BLANK_CH(*cur) cur++; + + if ((*cur != '-') && (*cur < '0') && (*cur > '9')) + return 1; + + dt = xmlSchemaNewValue(XML_SCHEMAS_UNKNOWN); + if (dt == NULL) + return -1; + + if ((cur[0] == '-') && (cur[1] == '-')) { + /* + * It's an incomplete date (xs:gMonthDay, xs:gMonth or + * xs:gDay) + */ + cur += 2; + + /* is it an xs:gDay? */ + if (*cur == '-') { + 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). + * FUTURE TODO: Zero will become valid in XML Schema 1.1 + * probably. + */ + 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 (collapse) + while IS_WSP_BLANK_CH(*cur) 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. + */ +static int +xmlSchemaValidateDuration (xmlSchemaTypePtr type ATTRIBUTE_UNUSED, + const xmlChar *duration, xmlSchemaValPtr *val, + int collapse) { + 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 (collapse) + while IS_WSP_BLANK_CH(*cur) cur++; + + 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) + 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) || (seq == 6)) + goto error; + } + cur++; + if (collapse) + while IS_WSP_BLANK_CH(*cur) 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. + */ +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); + return(xmlStrndup(start, end - start)); +} + +/** + * xmlSchemaWhiteSpaceReplace: + * @value: a value + * + * Replaces 0xd, 0x9 and 0xa with a space. + * + * Returns the new string or NULL if no change was required. + */ +xmlChar * +xmlSchemaWhiteSpaceReplace(const xmlChar *value) { + const xmlChar *cur = value; + xmlChar *ret = NULL, *mcur; + + if (value == NULL) + return(NULL); + + while ((*cur != 0) && + (((*cur) != 0xd) && ((*cur) != 0x9) && ((*cur) != 0xa))) { + cur++; + } + if (*cur == 0) + return (NULL); + ret = xmlStrdup(value); + /* TODO FIXME: I guess gcc will bark at this. */ + mcur = (xmlChar *) (ret + (cur - value)); + do { + if ( ((*mcur) == 0xd) || ((*mcur) == 0x9) || ((*mcur) == 0xa) ) + *mcur = ' '; + mcur++; + } while (*mcur != 0); + return(ret); +} + +/** + * xmlSchemaCollapseString: + * @value: a value + * + * Removes and normalize white spaces in the string + * + * Returns the new string or NULL if no change was required. + */ +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); + 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 + */ +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); + } + if (ret != NULL) { + *ret = NULL; + } + 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) { + 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++; + } + /* TODO what return value ? c.f. bug #158628 + if (ret != NULL) { + TODO + } */ + xmlFree(val); + 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 significant digits in the number 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') { /* ignore leading zeroes */ + 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 vlidation + * + * Check that a value conforms to the lexical space of the atomic type. + * if true a value is computed and returned in @val. + * This checks the value space for list types as well (IDREFS, NMTOKENS). + * + * 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, + xmlSchemaWhitespaceValueType ws, + int normOnTheFly, int applyNorm, int createStringValue) +{ + xmlSchemaValPtr v; + xmlChar *norm = NULL; + int ret = 0; + + if (xmlSchemaTypesInitialized == 0) + xmlSchemaInitTypes(); + if (type == NULL) + return (-1); + + /* + * validating a non existant text node is similar to validating + * an empty one. + */ + if (value == NULL) + value = BAD_CAST ""; + + if (val != NULL) + *val = NULL; + if ((flags == 0) && (value != NULL)) { + + if ((type->builtInType != XML_SCHEMAS_STRING) && + (type->builtInType != XML_SCHEMAS_ANYTYPE) && + (type->builtInType != XML_SCHEMAS_ANYSIMPLETYPE)) { + if (type->builtInType == XML_SCHEMAS_NORMSTRING) + norm = xmlSchemaWhiteSpaceReplace(value); + else + norm = xmlSchemaCollapseString(value); + if (norm != NULL) + value = norm; + } + } + + switch (type->builtInType) { + case XML_SCHEMAS_UNKNOWN: + goto error; + case XML_SCHEMAS_ANYTYPE: + case XML_SCHEMAS_ANYSIMPLETYPE: + if ((createStringValue) && (val != NULL)) { + v = xmlSchemaNewValue(XML_SCHEMAS_ANYSIMPLETYPE); + if (v != NULL) { + v->value.str = xmlStrdup(value); + *val = v; + } else { + goto error; + } + } + goto return0; + case XML_SCHEMAS_STRING: + if (! normOnTheFly) { + const xmlChar *cur = value; + + if (ws == XML_SCHEMA_WHITESPACE_REPLACE) { + while (*cur != 0) { + if ((*cur == 0xd) || (*cur == 0xa) || (*cur == 0x9)) { + goto return1; + } else { + cur++; + } + } + } else if (ws == XML_SCHEMA_WHITESPACE_COLLAPSE) { + while (*cur != 0) { + if ((*cur == 0xd) || (*cur == 0xa) || (*cur == 0x9)) { + goto return1; + } else if IS_WSP_SPACE_CH(*cur) { + cur++; + if IS_WSP_SPACE_CH(*cur) + goto return1; + } else { + cur++; + } + } + } + } + if (createStringValue && (val != NULL)) { + if (applyNorm) { + if (ws == XML_SCHEMA_WHITESPACE_COLLAPSE) + norm = xmlSchemaCollapseString(value); + else if (ws == XML_SCHEMA_WHITESPACE_REPLACE) + norm = xmlSchemaWhiteSpaceReplace(value); + if (norm != NULL) + value = norm; + } + v = xmlSchemaNewValue(XML_SCHEMAS_STRING); + if (v != NULL) { + v->value.str = xmlStrdup(value); + *val = v; + } else { + goto error; + } + } + goto return0; + case XML_SCHEMAS_NORMSTRING:{ + if (normOnTheFly) { + if (applyNorm) { + if (ws == XML_SCHEMA_WHITESPACE_COLLAPSE) + norm = xmlSchemaCollapseString(value); + else + norm = xmlSchemaWhiteSpaceReplace(value); + if (norm != NULL) + value = norm; + } + } else { + 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; + unsigned int len, neg, integ, hasLeadingZeroes; + xmlChar cval[25]; + xmlChar *cptr = cval; + + if ((cur == NULL) || (*cur == 0)) + goto return1; + + /* + * xs:decimal has a whitespace-facet value of 'collapse'. + */ + if (normOnTheFly) + while IS_WSP_BLANK_CH(*cur) cur++; + + /* + * First we handle an optional sign. + */ + neg = 0; + if (*cur == '-') { + neg = 1; + cur++; + } else if (*cur == '+') + cur++; + /* + * Disallow: "", "-", "- " + */ + if (*cur == 0) + goto return1; + /* + * Next we "pre-parse" the number, in preparation for calling + * the common routine xmlSchemaParseUInt. We get rid of any + * leading zeroes (because we have reserved only 25 chars), + * and note the position of a decimal point. + */ + len = 0; + integ = ~0u; + hasLeadingZeroes = 0; + /* + * Skip leading zeroes. + */ + while (*cur == '0') { + cur++; + hasLeadingZeroes = 1; + } + if (*cur != 0) { + do { + if ((*cur >= '0') && (*cur <= '9')) { + *cptr++ = *cur++; + len++; + } else if (*cur == '.') { + cur++; + integ = len; + do { + if ((*cur >= '0') && (*cur <= '9')) { + *cptr++ = *cur++; + len++; + } else + break; + } while (len < 24); + /* + * Disallow "." but allow "00." + */ + if ((len == 0) && (!hasLeadingZeroes)) + goto return1; + break; + } else + break; + } while (len < 24); + } + if (normOnTheFly) + while IS_WSP_BLANK_CH(*cur) cur++; + if (*cur != 0) + goto return1; /* error if any extraneous chars */ + if (val != NULL) { + v = xmlSchemaNewValue(XML_SCHEMAS_DECIMAL); + if (v != NULL) { + /* + * Now evaluate the significant digits of the number + */ + if (len != 0) { + + if (integ != ~0u) { + /* + * Get rid of trailing zeroes in the + * fractional part. + */ + while ((len != integ) && (*(cptr-1) == '0')) { + cptr--; + len--; + } + } + /* + * Terminate the (preparsed) string. + */ + if (len != 0) { + *cptr = 0; + cptr = cval; + + xmlSchemaParseUInt((const xmlChar **)&cptr, + &v->value.decimal.lo, + &v->value.decimal.mi, + &v->value.decimal.hi); + } + } + /* + * Set the total digits to 1 if a zero value. + */ + v->value.decimal.sign = neg; + if (len == 0) { + /* Speedup for zero values. */ + v->value.decimal.total = 1; + } else { + v->value.decimal.total = len; + if (integ == ~0u) + v->value.decimal.frac = 0; + else + v->value.decimal.frac = len - integ; + } + *val = v; + } + } + 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(type->builtInType, value, val, + normOnTheFly); + break; + case XML_SCHEMAS_DURATION: + ret = xmlSchemaValidateDuration(type, value, val, + normOnTheFly); + break; + case XML_SCHEMAS_FLOAT: + case XML_SCHEMAS_DOUBLE:{ + const xmlChar *cur = value; + int neg = 0; + + if (normOnTheFly) + while IS_WSP_BLANK_CH(*cur) cur++; + + 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); + goto error; + } + } else { + v = xmlSchemaNewValue(XML_SCHEMAS_DOUBLE); + if (v != NULL) { + v->value.d = xmlXPathNAN; + } else { + xmlSchemaFreeValue(v); + 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); + 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); + 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 (normOnTheFly) + while IS_WSP_BLANK_CH(*cur) cur++; + + if (*cur != 0) + goto return1; + if (val != NULL) { + if (type == xmlSchemaTypeFloatDef) { + v = xmlSchemaNewValue(XML_SCHEMAS_FLOAT); + if (v != NULL) { + /* + * TODO: sscanf seems not to give the correct + * value for extremely high/low values. + * E.g. "1E-149" results in zero. + */ + 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) { + /* + * TODO: sscanf seems not to give the correct + * value for extremely high/low values. + */ + 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 (normOnTheFly) { + while IS_WSP_BLANK_CH(*cur) cur++; + if (*cur == '0') { + ret = 0; + cur++; + } else if (*cur == '1') { + ret = 1; + cur++; + } else if (*cur == 't') { + cur++; + if ((*cur++ == 'r') && (*cur++ == 'u') && + (*cur++ == 'e')) { + ret = 1; + } else + goto return1; + } else if (*cur == 'f') { + cur++; + if ((*cur++ == 'a') && (*cur++ == 'l') && + (*cur++ == 's') && (*cur++ == 'e')) { + ret = 0; + } else + goto return1; + } else + goto return1; + if (*cur != 0) { + while IS_WSP_BLANK_CH(*cur) cur++; + if (*cur != 0) + goto return1; + } + } else { + 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 (! normOnTheFly) { + 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 (normOnTheFly) { + norm = xmlSchemaCollapseString(value); + if (norm != NULL) + value = norm; + } + 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) && (value != NULL)) { + v = xmlSchemaNewValue(XML_SCHEMAS_NAME); + if (v != NULL) { + const xmlChar *start = value, *end; + while (IS_BLANK_CH(*start)) start++; + end = start; + while ((*end != 0) && (!IS_BLANK_CH(*end))) end++; + v->value.str = xmlStrndup(start, end - start); + *val = v; + } else { + goto error; + } + } + goto done; + case XML_SCHEMAS_QNAME:{ + const xmlChar *uri = NULL; + xmlChar *local = NULL; + + ret = xmlValidateQName(value, 1); + if (ret != 0) + goto done; + if (node != NULL) { + xmlChar *prefix; + xmlNsPtr ns; + + local = xmlSplitQName2(value, &prefix); + ns = xmlSearchNs(node->doc, node, prefix); + if ((ns == NULL) && (prefix != NULL)) { + xmlFree(prefix); + if (local != NULL) + xmlFree(local); + goto return1; + } + if (ns != NULL) + uri = ns->href; + if (prefix != NULL) + xmlFree(prefix); + } + if (val != NULL) { + v = xmlSchemaNewValue(XML_SCHEMAS_QNAME); + if (v == NULL) { + if (local != NULL) + xmlFree(local); + goto error; + } + if (local != NULL) + v->value.qname.name = local; + else + v->value.qname.name = xmlStrdup(value); + if (uri != NULL) + v->value.qname.uri = xmlStrdup(uri); + *val = v; + } else + if (local != NULL) + xmlFree(local); + 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)) { + v = xmlSchemaNewValue(XML_SCHEMAS_IDREF); + if (v == NULL) + goto error; + v->value.str = xmlStrdup(value); + *val = v; + } + 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; + if (normOnTheFly) { + norm = xmlSchemaCollapseString(value); + if (norm != NULL) + value = norm; + } + 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, *start; + xmlChar *base; + int total, i = 0; + + if (cur == NULL) + goto return1; + + if (normOnTheFly) + while IS_WSP_BLANK_CH(*cur) cur++; + + start = cur; + while (((*cur >= '0') && (*cur <= '9')) || + ((*cur >= 'A') && (*cur <= 'F')) || + ((*cur >= 'a') && (*cur <= 'f'))) { + i++; + cur++; + } + if (normOnTheFly) + while IS_WSP_BLANK_CH(*cur) 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; + /* + * Copy only the normalized piece. + * CRITICAL TODO: Check this. + */ + cur = xmlStrndup(start, i); + 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 (normOnTheFly) + while IS_WSP_BLANK_CH(*cur) cur++; + if (*cur == '-') { + sign = 1; + cur++; + } else if (*cur == '+') + cur++; + ret = xmlSchemaParseUInt(&cur, &lo, &mi, &hi); + if (ret == -1) + goto return1; + if (normOnTheFly) + while IS_WSP_BLANK_CH(*cur) cur++; + if (*cur != 0) + goto return1; + if (type->builtInType == XML_SCHEMAS_NPINTEGER) { + if ((sign == 0) && + ((hi != 0) || (mi != 0) || (lo != 0))) + goto return1; + } else if (type->builtInType == XML_SCHEMAS_PINTEGER) { + if (sign == 1) + goto return1; + if ((hi == 0) && (mi == 0) && (lo == 0)) + goto return1; + } else if (type->builtInType == XML_SCHEMAS_NINTEGER) { + if (sign == 0) + goto return1; + if ((hi == 0) && (mi == 0) && (lo == 0)) + goto return1; + } else if (type->builtInType == XML_SCHEMAS_NNINTEGER) { + if ((sign == 1) && + ((hi != 0) || (mi != 0) || (lo != 0))) + goto return1; + } + if (val != NULL) { + v = xmlSchemaNewValue(type->builtInType); + if (v != NULL) { + if (ret == 0) + ret++; + v->value.decimal.lo = lo; + v->value.decimal.mi = mi; + v->value.decimal.hi = hi; + v->value.decimal.sign = sign; + v->value.decimal.frac = 0; + v->value.decimal.total = ret; + *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 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->builtInType == 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->builtInType == 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->builtInType == 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->builtInType == 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(type->builtInType); + if (v != NULL) { + v->value.decimal.lo = lo; + v->value.decimal.mi = mi; + v->value.decimal.hi = hi; + v->value.decimal.sign = sign; + v->value.decimal.frac = 0; + v->value.decimal.total = ret; + *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; + + if (cur == NULL) + goto return1; + ret = xmlSchemaParseUInt(&cur, &lo, &mi, &hi); + if (ret < 0) + goto return1; + if (*cur != 0) + goto return1; + if (type->builtInType == 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->builtInType == XML_SCHEMAS_UINT) { + if (hi != 0) + goto return1; + if (mi >= 42) { + if (mi > 42) + goto return1; + if (lo > 94967295) + goto return1; + } + } else if (type->builtInType == XML_SCHEMAS_USHORT) { + if ((mi != 0) || (hi != 0)) + goto return1; + if (lo > 65535) + goto return1; + } else if (type->builtInType == XML_SCHEMAS_UBYTE) { + if ((mi != 0) || (hi != 0)) + goto return1; + if (lo > 255) + goto return1; + } + if (val != NULL) { + v = xmlSchemaNewValue(type->builtInType); + 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 = ret; + *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, + XML_SCHEMA_WHITESPACE_UNKNOWN, 1, 1, 0)); +} + +/** + * xmlSchemaValPredefTypeNodeNoNorm: + * @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. + * This one does apply any normalization to the value. + * + * Returns 0 if this validates, a positive error code number otherwise + * and -1 in case of internal or API error. + */ +int +xmlSchemaValPredefTypeNodeNoNorm(xmlSchemaTypePtr type, const xmlChar *value, + xmlSchemaValPtr *val, xmlNodePtr node) { + return(xmlSchemaValAtomicType(type, value, val, node, 1, + XML_SCHEMA_WHITESPACE_UNKNOWN, 1, 0, 1)); +} + +/** + * 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 + */ +static int +xmlSchemaCompareDecimals(xmlSchemaValPtr x, xmlSchemaValPtr y) +{ + xmlSchemaValPtr swp; + int order = 1, integx, integy, dlen; + unsigned long hi, mi, lo; + + /* + * First test: If x is -ve and not zero + */ + if ((x->value.decimal.sign) && + ((x->value.decimal.lo != 0) || + (x->value.decimal.mi != 0) || + (x->value.decimal.hi != 0))) { + /* + * Then if y is -ve and not zero reverse the compare + */ + if ((y->value.decimal.sign) && + ((y->value.decimal.lo != 0) || + (y->value.decimal.mi != 0) || + (y->value.decimal.hi != 0))) + order = -1; + /* + * Otherwise (y >= 0) we have the answer + */ + else + return (-1); + /* + * If x is not -ve and y is -ve we have the answer + */ + } else if ((y->value.decimal.sign) && + ((y->value.decimal.lo != 0) || + (y->value.decimal.mi != 0) || + (y->value.decimal.hi != 0))) { + return (1); + } + /* + * If it's not simply determined by a difference in sign, + * then we need to compare the actual values of the two nums. + * To do this, we start by looking at the integral parts. + * If the number of integral digits differ, then we have our + * answer. + */ + integx = x->value.decimal.total - x->value.decimal.frac; + integy = y->value.decimal.total - y->value.decimal.frac; + /* + * NOTE: We changed the "total" for values like "0.1" + * (or "-0.1" or ".1") to be 1, which was 2 previously. + * Therefore the special case, when such values are + * compared with 0, needs to be handled separately; + * otherwise a zero would be recognized incorrectly as + * greater than those values. This has the nice side effect + * that we gain an overall optimized comparison with zeroes. + * Note that a "0" has a "total" of 1 already. + */ + if (integx == 1) { + if (x->value.decimal.lo == 0) { + if (integy != 1) + return -order; + else if (y->value.decimal.lo != 0) + return -order; + else + return(0); + } + } + if (integy == 1) { + if (y->value.decimal.lo == 0) { + if (integx != 1) + return order; + else if (x->value.decimal.lo != 0) + return order; + else + return(0); + } + } + + if (integx > integy) + return order; + else if (integy > integx) + return -order; + + /* + * If the number of integral digits is the same for both numbers, + * then things get a little more complicated. We need to "normalize" + * the numbers in order to properly compare them. To do this, we + * look at the total length of each number (length => number of + * significant digits), and divide the "shorter" by 10 (decreasing + * the length) until they are of equal length. + */ + dlen = x->value.decimal.total - y->value.decimal.total; + if (dlen < 0) { /* y has more digits than x */ + swp = x; + hi = y->value.decimal.hi; + mi = y->value.decimal.mi; + lo = y->value.decimal.lo; + dlen = -dlen; + order = -order; + } else { /* x has more digits than y */ + swp = y; + hi = x->value.decimal.hi; + mi = x->value.decimal.mi; + lo = x->value.decimal.lo; + } + while (dlen > 8) { /* in effect, right shift by 10**8 */ + lo = mi; + mi = hi; + hi = 0; + dlen -= 8; + } + while (dlen > 0) { + unsigned long rem1, rem2; + rem1 = (hi % 10) * 100000000L; + hi = hi / 10; + rem2 = (mi % 10) * 100000000L; + mi = (mi + rem1) / 10; + lo = (lo + rem2) / 10; + dlen--; + } + if (hi > swp->value.decimal.hi) { + return order; + } else if (hi == swp->value.decimal.hi) { + if (mi > swp->value.decimal.mi) { + return order; + } else if (mi == swp->value.decimal.mi) { + if (lo > swp->value.decimal.lo) { + return order; + } else if (lo == swp->value.decimal.lo) { + if (x->value.decimal.total == y->value.decimal.total) { + return 0; + } else { + return order; + } + } + } + } + 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) + +/** + * xmlSchemaDupVal: + * @v: the #xmlSchemaValPtr value to duplicate + * + * Makes a copy of @v. The calling program is responsible for freeing + * the returned value. + * + * returns a pointer to a duplicated #xmlSchemaValPtr or NULL if error. + */ +static xmlSchemaValPtr +xmlSchemaDupVal (xmlSchemaValPtr v) +{ + xmlSchemaValPtr ret = xmlSchemaNewValue(v->type); + if (ret == NULL) + return NULL; + + memcpy(ret, v, sizeof(xmlSchemaVal)); + ret->next = NULL; + return ret; +} + +/** + * xmlSchemaCopyValue: + * @val: the precomputed value to be copied + * + * Copies the precomputed value. This duplicates any string within. + * + * Returns the copy or NULL if a copy for a data-type is not implemented. + */ +xmlSchemaValPtr +xmlSchemaCopyValue(xmlSchemaValPtr val) +{ + xmlSchemaValPtr ret = NULL, prev = NULL, cur; + + /* + * Copy the string values. + */ + while (val != NULL) { + switch (val->type) { + case XML_SCHEMAS_ANYTYPE: + case XML_SCHEMAS_IDREFS: + case XML_SCHEMAS_ENTITIES: + case XML_SCHEMAS_NMTOKENS: + xmlSchemaFreeValue(ret); + return (NULL); + case XML_SCHEMAS_ANYSIMPLETYPE: + case XML_SCHEMAS_STRING: + case XML_SCHEMAS_NORMSTRING: + case XML_SCHEMAS_TOKEN: + case XML_SCHEMAS_LANGUAGE: + case XML_SCHEMAS_NAME: + case XML_SCHEMAS_NCNAME: + case XML_SCHEMAS_ID: + case XML_SCHEMAS_IDREF: + case XML_SCHEMAS_ENTITY: + case XML_SCHEMAS_NMTOKEN: + case XML_SCHEMAS_ANYURI: + cur = xmlSchemaDupVal(val); + if (val->value.str != NULL) + cur->value.str = xmlStrdup(BAD_CAST val->value.str); + break; + case XML_SCHEMAS_QNAME: + case XML_SCHEMAS_NOTATION: + cur = xmlSchemaDupVal(val); + if (val->value.qname.name != NULL) + cur->value.qname.name = + xmlStrdup(BAD_CAST val->value.qname.name); + if (val->value.qname.uri != NULL) + cur->value.qname.uri = + xmlStrdup(BAD_CAST val->value.qname.uri); + break; + case XML_SCHEMAS_HEXBINARY: + cur = xmlSchemaDupVal(val); + if (val->value.hex.str != NULL) + cur->value.hex.str = xmlStrdup(BAD_CAST val->value.hex.str); + break; + case XML_SCHEMAS_BASE64BINARY: + cur = xmlSchemaDupVal(val); + if (val->value.base64.str != NULL) + cur->value.base64.str = + xmlStrdup(BAD_CAST val->value.base64.str); + break; + default: + cur = xmlSchemaDupVal(val); + break; + } + if (ret == NULL) + ret = cur; + else + prev->next = cur; + prev = cur; + val = val->next; + } + return (ret); +} + +/** + * _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. The returned #xmlSchemaVal is the same type as + * @dt. The calling program is responsible for freeing the returned value. + * + * Returns a pointer to a new #xmlSchemaVal or NULL if error. + */ +static xmlSchemaValPtr +_xmlSchemaDateAdd (xmlSchemaValPtr dt, xmlSchemaValPtr dur) +{ + xmlSchemaValPtr ret, tmp; + long carry, tempdays, temp; + xmlSchemaValDatePtr r, d; + xmlSchemaValDurationPtr u; + + if ((dt == NULL) || (dur == NULL)) + return NULL; + + ret = xmlSchemaNewValue(dt->type); + if (ret == NULL) + return NULL; + + /* make a copy so we don't alter the original value */ + tmp = xmlSchemaDupVal(dt); + if (tmp == NULL) { + xmlSchemaFreeValue(ret); + return NULL; + } + + r = &(ret->value.date); + d = &(tmp->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((int)r->mon-1, 1, 13); + long tyr = r->year + (long)FQUOTIENT_RANGE((int)r->mon-1, 1, 13); + if (tyr == 0) + tyr--; + /* + * Coverity detected an overrun in daysInMonth + * of size 12 at position 12 with index variable "((r)->mon - 1)" + */ + if (tmon < 0) + tmon = 0; + if (tmon > 12) + tmon = 12; + 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; + } + } + + xmlSchemaFreeValue(tmp); + + return ret; +} + +/** + * xmlSchemaDateNormalize: + * @dt: an #xmlSchemaValPtr of a date/time type value. + * @offset: number of seconds to adjust @dt by. + * + * Normalize @dt to GMT time. The @offset parameter is subtracted from + * the return value is a time-zone offset is present on @dt. + * + * Returns a normalized copy of @dt or NULL if error. + */ +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->type != XML_SCHEMAS_DATE)) || (dt->value.date.tzo == 0)) + return xmlSchemaDupVal(dt); + + dur = xmlSchemaNewValue(XML_SCHEMAS_DURATION); + if (dur == NULL) + return NULL; + + dur->value.date.sec -= offset; + + ret = _xmlSchemaDateAdd(dt, dur); + if (ret == NULL) + return NULL; + + 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. + */ +static long +_xmlSchemaDateCastYMToDays (const xmlSchemaValPtr dt) +{ + long ret; + int mon; + + mon = dt->value.date.mon; + if (mon <= 0) mon = 1; /* normalization */ + + 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, 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, 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 + */ +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); + 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; +} + +/** + * xmlSchemaComparePreserveReplaceStrings: + * @x: a first string value + * @y: a second string value + * @invert: inverts the result if x < y or x > y. + * + * Compare 2 string for their normalized values. + * @x is a string with whitespace of "preserve", @y is + * a string with a whitespace of "replace". I.e. @x could + * be an "xsd:string" and @y an "xsd:normalizedString". + * + * Returns -1 if x < y, 0 if x == y, 1 if x > y, and -2 in + * case of error + */ +static int +xmlSchemaComparePreserveReplaceStrings(const xmlChar *x, + const xmlChar *y, + int invert) +{ + int tmp; + + while ((*x != 0) && (*y != 0)) { + if (IS_WSP_REPLACE_CH(*y)) { + if (! IS_WSP_SPACE_CH(*x)) { + if ((*x - 0x20) < 0) { + if (invert) + return(1); + else + return(-1); + } else { + if (invert) + return(-1); + else + return(1); + } + } + } else { + tmp = *x - *y; + if (tmp < 0) { + if (invert) + return(1); + else + return(-1); + } + if (tmp > 0) { + if (invert) + return(-1); + else + return(1); + } + } + x++; + y++; + } + if (*x != 0) { + if (invert) + return(-1); + else + return(1); + } + if (*y != 0) { + if (invert) + return(1); + else + return(-1); + } + return(0); +} + +/** + * xmlSchemaComparePreserveCollapseStrings: + * @x: a first string value + * @y: a second string value + * + * Compare 2 string for their normalized values. + * @x is a string with whitespace of "preserve", @y is + * a string with a whitespace of "collapse". I.e. @x could + * be an "xsd:string" and @y an "xsd:normalizedString". + * + * Returns -1 if x < y, 0 if x == y, 1 if x > y, and -2 in + * case of error + */ +static int +xmlSchemaComparePreserveCollapseStrings(const xmlChar *x, + const xmlChar *y, + int invert) +{ + int tmp; + + /* + * Skip leading blank chars of the collapsed string. + */ + while IS_WSP_BLANK_CH(*y) + y++; + + while ((*x != 0) && (*y != 0)) { + if IS_WSP_BLANK_CH(*y) { + if (! IS_WSP_SPACE_CH(*x)) { + /* + * The yv character would have been replaced to 0x20. + */ + if ((*x - 0x20) < 0) { + if (invert) + return(1); + else + return(-1); + } else { + if (invert) + return(-1); + else + return(1); + } + } + x++; + y++; + /* + * Skip contiguous blank chars of the collapsed string. + */ + while IS_WSP_BLANK_CH(*y) + y++; + } else { + tmp = *x++ - *y++; + if (tmp < 0) { + if (invert) + return(1); + else + return(-1); + } + if (tmp > 0) { + if (invert) + return(-1); + else + return(1); + } + } + } + if (*x != 0) { + if (invert) + return(-1); + else + return(1); + } + if (*y != 0) { + /* + * Skip trailing blank chars of the collapsed string. + */ + while IS_WSP_BLANK_CH(*y) + y++; + if (*y != 0) { + if (invert) + return(1); + else + return(-1); + } + } + return(0); +} + +/** + * xmlSchemaComparePreserveCollapseStrings: + * @x: a first string value + * @y: a second string value + * + * Compare 2 string for their normalized values. + * @x is a string with whitespace of "preserve", @y is + * a string with a whitespace of "collapse". I.e. @x could + * be an "xsd:string" and @y an "xsd:normalizedString". + * + * Returns -1 if x < y, 0 if x == y, 1 if x > y, and -2 in + * case of error + */ +static int +xmlSchemaCompareReplaceCollapseStrings(const xmlChar *x, + const xmlChar *y, + int invert) +{ + int tmp; + + /* + * Skip leading blank chars of the collapsed string. + */ + while IS_WSP_BLANK_CH(*y) + y++; + + while ((*x != 0) && (*y != 0)) { + if IS_WSP_BLANK_CH(*y) { + if (! IS_WSP_BLANK_CH(*x)) { + /* + * The yv character would have been replaced to 0x20. + */ + if ((*x - 0x20) < 0) { + if (invert) + return(1); + else + return(-1); + } else { + if (invert) + return(-1); + else + return(1); + } + } + x++; + y++; + /* + * Skip contiguous blank chars of the collapsed string. + */ + while IS_WSP_BLANK_CH(*y) + y++; + } else { + if IS_WSP_BLANK_CH(*x) { + /* + * The xv character would have been replaced to 0x20. + */ + if ((0x20 - *y) < 0) { + if (invert) + return(1); + else + return(-1); + } else { + if (invert) + return(-1); + else + return(1); + } + } + tmp = *x++ - *y++; + if (tmp < 0) + return(-1); + if (tmp > 0) + return(1); + } + } + if (*x != 0) { + if (invert) + return(-1); + else + return(1); + } + if (*y != 0) { + /* + * Skip trailing blank chars of the collapsed string. + */ + while IS_WSP_BLANK_CH(*y) + y++; + if (*y != 0) { + if (invert) + return(1); + else + return(-1); + } + } + return(0); +} + + +/** + * xmlSchemaCompareReplacedStrings: + * @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 + */ +static int +xmlSchemaCompareReplacedStrings(const xmlChar *x, + const xmlChar *y) +{ + int tmp; + + while ((*x != 0) && (*y != 0)) { + if IS_WSP_BLANK_CH(*y) { + if (! IS_WSP_BLANK_CH(*x)) { + if ((*x - 0x20) < 0) + return(-1); + else + return(1); + } + } else { + if IS_WSP_BLANK_CH(*x) { + if ((0x20 - *y) < 0) + return(-1); + else + return(1); + } + tmp = *x - *y; + if (tmp < 0) + return(-1); + if (tmp > 0) + return(1); + } + x++; + y++; + } + if (*x != 0) + return(1); + if (*y != 0) + 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 + */ +static int +xmlSchemaCompareNormStrings(const xmlChar *x, + const xmlChar *y) { + int tmp; + + while (IS_BLANK_CH(*x)) x++; + while (IS_BLANK_CH(*y)) y++; + while ((*x != 0) && (*y != 0)) { + if (IS_BLANK_CH(*x)) { + if (!IS_BLANK_CH(*y)) { + tmp = *x - *y; + return(tmp); + } + while (IS_BLANK_CH(*x)) x++; + while (IS_BLANK_CH(*y)) y++; + } else { + tmp = *x++ - *y++; + if (tmp < 0) + return(-1); + if (tmp > 0) + return(1); + } + } + if (*x != 0) { + while (IS_BLANK_CH(*x)) x++; + if (*x != 0) + return(1); + } + if (*y != 0) { + while (IS_BLANK_CH(*y)) y++; + if (*y != 0) + return(-1); + } + return(0); +} + +/** + * 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 + */ +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. + */ + 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 + * @xvalue: the first value as a string (optional) + * @xwtsp: the whitespace type + * @y: a second value + * @xvalue: the second value as a string (optional) + * @ywtsp: the whitespace type + * + * Compare 2 values + * + * Returns -1 if x < y, 0 if x == y, 1 if x > y, 2 if x <> y, 3 if not + * comparable and -2 in case of error + */ +static int +xmlSchemaCompareValuesInternal(xmlSchemaValType xtype, + xmlSchemaValPtr x, + const xmlChar *xvalue, + xmlSchemaWhitespaceValueType xws, + xmlSchemaValType ytype, + xmlSchemaValPtr y, + const xmlChar *yvalue, + xmlSchemaWhitespaceValueType yws) +{ + switch (xtype) { + case XML_SCHEMAS_UNKNOWN: + case XML_SCHEMAS_ANYTYPE: + 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 ((x == NULL) || (y == NULL)) + return(-2); + if (ytype == xtype) + return(xmlSchemaCompareDecimals(x, y)); + if ((ytype == XML_SCHEMAS_DECIMAL) || + (ytype == XML_SCHEMAS_INTEGER) || + (ytype == XML_SCHEMAS_NPINTEGER) || + (ytype == XML_SCHEMAS_NINTEGER) || + (ytype == XML_SCHEMAS_NNINTEGER) || + (ytype == XML_SCHEMAS_PINTEGER) || + (ytype == XML_SCHEMAS_INT) || + (ytype == XML_SCHEMAS_UINT) || + (ytype == XML_SCHEMAS_LONG) || + (ytype == XML_SCHEMAS_ULONG) || + (ytype == XML_SCHEMAS_SHORT) || + (ytype == XML_SCHEMAS_USHORT) || + (ytype == XML_SCHEMAS_BYTE) || + (ytype == XML_SCHEMAS_UBYTE)) + return(xmlSchemaCompareDecimals(x, y)); + return(-2); + case XML_SCHEMAS_DURATION: + if ((x == NULL) || (y == NULL)) + return(-2); + if (ytype == 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 ((x == NULL) || (y == NULL)) + return(-2); + if ((ytype == XML_SCHEMAS_DATETIME) || + (ytype == XML_SCHEMAS_TIME) || + (ytype == XML_SCHEMAS_GDAY) || + (ytype == XML_SCHEMAS_GMONTH) || + (ytype == XML_SCHEMAS_GMONTHDAY) || + (ytype == XML_SCHEMAS_GYEAR) || + (ytype == XML_SCHEMAS_DATE) || + (ytype == XML_SCHEMAS_GYEARMONTH)) + return (xmlSchemaCompareDates(x, y)); + return (-2); + /* + * Note that we will support comparison of string types against + * anySimpleType as well. + */ + case XML_SCHEMAS_ANYSIMPLETYPE: + case XML_SCHEMAS_STRING: + 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_ANYURI: + { + const xmlChar *xv, *yv; + + if (x == NULL) + xv = xvalue; + else + xv = x->value.str; + if (y == NULL) + yv = yvalue; + else + yv = y->value.str; + /* + * TODO: Compare those against QName. + */ + if (ytype == XML_SCHEMAS_QNAME) { + TODO + if (y == NULL) + return(-2); + return (-2); + } + if ((ytype == XML_SCHEMAS_ANYSIMPLETYPE) || + (ytype == XML_SCHEMAS_STRING) || + (ytype == XML_SCHEMAS_NORMSTRING) || + (ytype == XML_SCHEMAS_TOKEN) || + (ytype == XML_SCHEMAS_LANGUAGE) || + (ytype == XML_SCHEMAS_NMTOKEN) || + (ytype == XML_SCHEMAS_NAME) || + (ytype == XML_SCHEMAS_NCNAME) || + (ytype == XML_SCHEMAS_ID) || + (ytype == XML_SCHEMAS_IDREF) || + (ytype == XML_SCHEMAS_ENTITY) || + (ytype == XML_SCHEMAS_ANYURI)) { + + if (xws == XML_SCHEMA_WHITESPACE_PRESERVE) { + + if (yws == XML_SCHEMA_WHITESPACE_PRESERVE) { + /* TODO: What about x < y or x > y. */ + if (xmlStrEqual(xv, yv)) + return (0); + else + return (2); + } else if (yws == XML_SCHEMA_WHITESPACE_REPLACE) + return (xmlSchemaComparePreserveReplaceStrings(xv, yv, 0)); + else if (yws == XML_SCHEMA_WHITESPACE_COLLAPSE) + return (xmlSchemaComparePreserveCollapseStrings(xv, yv, 0)); + + } else if (xws == XML_SCHEMA_WHITESPACE_REPLACE) { + + if (yws == XML_SCHEMA_WHITESPACE_PRESERVE) + return (xmlSchemaComparePreserveReplaceStrings(yv, xv, 1)); + if (yws == XML_SCHEMA_WHITESPACE_REPLACE) + return (xmlSchemaCompareReplacedStrings(xv, yv)); + if (yws == XML_SCHEMA_WHITESPACE_COLLAPSE) + return (xmlSchemaCompareReplaceCollapseStrings(xv, yv, 0)); + + } else if (xws == XML_SCHEMA_WHITESPACE_COLLAPSE) { + + if (yws == XML_SCHEMA_WHITESPACE_PRESERVE) + return (xmlSchemaComparePreserveCollapseStrings(yv, xv, 1)); + if (yws == XML_SCHEMA_WHITESPACE_REPLACE) + return (xmlSchemaCompareReplaceCollapseStrings(yv, xv, 1)); + if (yws == XML_SCHEMA_WHITESPACE_COLLAPSE) + return (xmlSchemaCompareNormStrings(xv, yv)); + } else + return (-2); + + } + return (-2); + } + case XML_SCHEMAS_QNAME: + case XML_SCHEMAS_NOTATION: + if ((x == NULL) || (y == NULL)) + return(-2); + if ((ytype == XML_SCHEMAS_QNAME) || + (ytype == XML_SCHEMAS_NOTATION)) { + 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 ((x == NULL) || (y == NULL)) + return(-2); + if ((ytype == XML_SCHEMAS_FLOAT) || + (ytype == XML_SCHEMAS_DOUBLE)) + return (xmlSchemaCompareFloats(x, y)); + return (-2); + case XML_SCHEMAS_BOOLEAN: + if ((x == NULL) || (y == NULL)) + return(-2); + if (ytype == 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 ((x == NULL) || (y == NULL)) + return(-2); + if (ytype == 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 ((x == NULL) || (y == NULL)) + return(-2); + if (ytype == 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 + return(-1); + } + else if (x->value.base64.total > y->value.base64.total) + return(1); + else + return(-1); + } + return (-2); + case XML_SCHEMAS_IDREFS: + case XML_SCHEMAS_ENTITIES: + case XML_SCHEMAS_NMTOKENS: + TODO + break; + } + 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) { + xmlSchemaWhitespaceValueType xws, yws; + + if ((x == NULL) || (y == NULL)) + return(-2); + if (x->type == XML_SCHEMAS_STRING) + xws = XML_SCHEMA_WHITESPACE_PRESERVE; + else if (x->type == XML_SCHEMAS_NORMSTRING) + xws = XML_SCHEMA_WHITESPACE_REPLACE; + else + xws = XML_SCHEMA_WHITESPACE_COLLAPSE; + + if (y->type == XML_SCHEMAS_STRING) + yws = XML_SCHEMA_WHITESPACE_PRESERVE; + else if (x->type == XML_SCHEMAS_NORMSTRING) + yws = XML_SCHEMA_WHITESPACE_REPLACE; + else + yws = XML_SCHEMA_WHITESPACE_COLLAPSE; + + return(xmlSchemaCompareValuesInternal(x->type, x, NULL, xws, y->type, + y, NULL, yws)); +} + +/** + * xmlSchemaCompareValuesWhtsp: + * @x: a first value + * @xws: the whitespace value of x + * @y: a second value + * @yws: the whitespace value of y + * + * 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 +xmlSchemaCompareValuesWhtsp(xmlSchemaValPtr x, + xmlSchemaWhitespaceValueType xws, + xmlSchemaValPtr y, + xmlSchemaWhitespaceValueType yws) +{ + if ((x == NULL) || (y == NULL)) + return(-2); + return(xmlSchemaCompareValuesInternal(x->type, x, NULL, xws, y->type, + y, NULL, yws)); +} + +/** + * xmlSchemaCompareValuesWhtspExt: + * @x: a first value + * @xws: the whitespace value of x + * @y: a second value + * @yws: the whitespace value of y + * + * 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 + */ +static int +xmlSchemaCompareValuesWhtspExt(xmlSchemaValType xtype, + xmlSchemaValPtr x, + const xmlChar *xvalue, + xmlSchemaWhitespaceValueType xws, + xmlSchemaValType ytype, + xmlSchemaValPtr y, + const xmlChar *yvalue, + xmlSchemaWhitespaceValueType yws) +{ + return(xmlSchemaCompareValuesInternal(xtype, x, xvalue, xws, ytype, y, + yvalue, yws)); +} + +/** + * 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); +} + +/** + * xmlSchemaGetFacetValueAsULong: + * @facet: an schemas type facet + * + * Extract the value of a facet + * + * Returns the value as a long + */ +unsigned long +xmlSchemaGetFacetValueAsULong(xmlSchemaFacetPtr facet) +{ + /* + * TODO: Check if this is a decimal. + */ + if (facet == NULL) + return 0; + return ((unsigned long) facet->val->value.decimal.lo); +} + +/** + * xmlSchemaValidateListSimpleTypeFacet: + * @facet: the facet to check + * @value: the lexical repr of the value to validate + * @actualLen: the number of list items + * @expectedLen: the resulting expected number of list items + * + * Checks the value of a list simple type against a facet. + * + * Returns 0 if the value is valid, a positive error code + * number otherwise and -1 in case of an internal error. + */ +int +xmlSchemaValidateListSimpleTypeFacet(xmlSchemaFacetPtr facet, + const xmlChar *value, + unsigned long actualLen, + unsigned long *expectedLen) +{ + if (facet == NULL) + return(-1); + /* + * TODO: Check if this will work with large numbers. + * (compare value.decimal.mi and value.decimal.hi as well?). + */ + if (facet->type == XML_SCHEMA_FACET_LENGTH) { + if (actualLen != facet->val->value.decimal.lo) { + if (expectedLen != NULL) + *expectedLen = facet->val->value.decimal.lo; + return (XML_SCHEMAV_CVC_LENGTH_VALID); + } + } else if (facet->type == XML_SCHEMA_FACET_MINLENGTH) { + if (actualLen < facet->val->value.decimal.lo) { + if (expectedLen != NULL) + *expectedLen = facet->val->value.decimal.lo; + return (XML_SCHEMAV_CVC_MINLENGTH_VALID); + } + } else if (facet->type == XML_SCHEMA_FACET_MAXLENGTH) { + if (actualLen > facet->val->value.decimal.lo) { + if (expectedLen != NULL) + *expectedLen = facet->val->value.decimal.lo; + return (XML_SCHEMAV_CVC_MAXLENGTH_VALID); + } + } else + /* + * NOTE: That we can pass NULL as xmlSchemaValPtr to + * xmlSchemaValidateFacet, since the remaining facet types + * are: XML_SCHEMA_FACET_PATTERN, XML_SCHEMA_FACET_ENUMERATION. + */ + return(xmlSchemaValidateFacet(NULL, facet, value, NULL)); + return (0); +} + +/** + * xmlSchemaValidateLengthFacet: + * @type: the built-in type + * @facet: the facet to check + * @value: the lexical repr. of the value to be validated + * @val: the precomputed value + * @ws: the whitespace type of the value + * @length: the actual length of the value + * + * Checka a value against a "length", "minLength" and "maxLength" + * facet; sets @length to the computed length of @value. + * + * Returns 0 if the value is valid, a positive error code + * otherwise and -1 in case of an internal or API error. + */ +static int +xmlSchemaValidateLengthFacetInternal(xmlSchemaFacetPtr facet, + xmlSchemaTypeType valType, + const xmlChar *value, + xmlSchemaValPtr val, + unsigned long *length, + xmlSchemaWhitespaceValueType ws) +{ + unsigned int len = 0; + + if ((length == NULL) || (facet == NULL)) + return (-1); + *length = 0; + if ((facet->type != XML_SCHEMA_FACET_LENGTH) && + (facet->type != XML_SCHEMA_FACET_MAXLENGTH) && + (facet->type != XML_SCHEMA_FACET_MINLENGTH)) + return (-1); + + /* + * TODO: length, maxLength and minLength must be of type + * nonNegativeInteger only. Check if decimal is used somehow. + */ + 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 (valType) { + case XML_SCHEMAS_STRING: + case XML_SCHEMAS_NORMSTRING: + if (ws == XML_SCHEMA_WHITESPACE_UNKNOWN) { + /* + * This is to ensure API compatibility with the old + * xmlSchemaValidateLengthFacet(). Anyway, this was and + * is not the correct handling. + * TODO: Get rid of this case somehow. + */ + if (valType == XML_SCHEMAS_STRING) + len = xmlUTF8Strlen(value); + else + len = xmlSchemaNormLen(value); + } else if (value != NULL) { + if (ws == XML_SCHEMA_WHITESPACE_COLLAPSE) + len = xmlSchemaNormLen(value); + else + /* + * Should be OK for "preserve" as well. + */ + len = xmlUTF8Strlen(value); + } + break; + case XML_SCHEMAS_IDREF: + case XML_SCHEMAS_TOKEN: + case XML_SCHEMAS_LANGUAGE: + case XML_SCHEMAS_NMTOKEN: + case XML_SCHEMAS_NAME: + case XML_SCHEMAS_NCNAME: + case XML_SCHEMAS_ID: + /* + * FIXME: What exactly to do with anyURI? + */ + case XML_SCHEMAS_ANYURI: + if (value != NULL) + len = xmlSchemaNormLen(value); + break; + case XML_SCHEMAS_QNAME: + case XML_SCHEMAS_NOTATION: + /* + * For QName and NOTATION, those facets are + * deprecated and should be ignored. + */ + return (0); + default: + TODO + } + } + *length = (unsigned long) len; + /* + * TODO: Return the whole expected value, i.e. "lo", "mi" and "hi". + */ + if (facet->type == XML_SCHEMA_FACET_LENGTH) { + if (len != facet->val->value.decimal.lo) + return(XML_SCHEMAV_CVC_LENGTH_VALID); + } else if (facet->type == XML_SCHEMA_FACET_MINLENGTH) { + if (len < facet->val->value.decimal.lo) + return(XML_SCHEMAV_CVC_MINLENGTH_VALID); + } else { + if (len > facet->val->value.decimal.lo) + return(XML_SCHEMAV_CVC_MAXLENGTH_VALID); + } + + return (0); +} + +/** + * xmlSchemaValidateLengthFacet: + * @type: the built-in type + * @facet: the facet to check + * @value: the lexical repr. of the value to be validated + * @val: the precomputed value + * @length: the actual length of the value + * + * Checka a value against a "length", "minLength" and "maxLength" + * facet; sets @length to the computed length of @value. + * + * Returns 0 if the value is valid, a positive error code + * otherwise and -1 in case of an internal or API error. + */ +int +xmlSchemaValidateLengthFacet(xmlSchemaTypePtr type, + xmlSchemaFacetPtr facet, + const xmlChar *value, + xmlSchemaValPtr val, + unsigned long *length) +{ + if (type == NULL) + return(-1); + return (xmlSchemaValidateLengthFacetInternal(facet, + type->builtInType, value, val, length, + XML_SCHEMA_WHITESPACE_UNKNOWN)); +} + +/** + * xmlSchemaValidateLengthFacetWhtsp: + * @facet: the facet to check + * @valType: the built-in type + * @value: the lexical repr. of the value to be validated + * @val: the precomputed value + * @ws: the whitespace type of the value + * @length: the actual length of the value + * + * Checka a value against a "length", "minLength" and "maxLength" + * facet; sets @length to the computed length of @value. + * + * Returns 0 if the value is valid, a positive error code + * otherwise and -1 in case of an internal or API error. + */ +int +xmlSchemaValidateLengthFacetWhtsp(xmlSchemaFacetPtr facet, + xmlSchemaValType valType, + const xmlChar *value, + xmlSchemaValPtr val, + unsigned long *length, + xmlSchemaWhitespaceValueType ws) +{ + return (xmlSchemaValidateLengthFacetInternal(facet, valType, value, val, + length, ws)); +} + +/** + * xmlSchemaValidateFacetInternal: + * @facet: the facet to check + * @fws: the whitespace type of the facet's value + * @valType: the built-in type of the value + * @value: the lexical repr of the value to validate + * @val: the precomputed value + * @ws: the whitespace type of the 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. + */ +static int +xmlSchemaValidateFacetInternal(xmlSchemaFacetPtr facet, + xmlSchemaWhitespaceValueType fws, + xmlSchemaValType valType, + const xmlChar *value, + xmlSchemaValPtr val, + xmlSchemaWhitespaceValueType ws) +{ + int ret; + + if (facet == NULL) + return(-1); + + switch (facet->type) { + case XML_SCHEMA_FACET_PATTERN: + /* + * NOTE that for patterns, the @value needs to be the normalized + * value, *not* the lexical initial value or the canonical value. + */ + if (value == NULL) + return(-1); + ret = xmlRegexpExec(facet->regexp, value); + if (ret == 1) + return(0); + if (ret == 0) + return(XML_SCHEMAV_CVC_PATTERN_VALID); + return(ret); + case XML_SCHEMA_FACET_MAXEXCLUSIVE: + ret = xmlSchemaCompareValues(val, facet->val); + if (ret == -2) + return(-1); + if (ret == -1) + return(0); + return(XML_SCHEMAV_CVC_MAXEXCLUSIVE_VALID); + case XML_SCHEMA_FACET_MAXINCLUSIVE: + ret = xmlSchemaCompareValues(val, facet->val); + if (ret == -2) + return(-1); + if ((ret == -1) || (ret == 0)) + return(0); + return(XML_SCHEMAV_CVC_MAXINCLUSIVE_VALID); + case XML_SCHEMA_FACET_MINEXCLUSIVE: + ret = xmlSchemaCompareValues(val, facet->val); + if (ret == -2) + return(-1); + if (ret == 1) + return(0); + return(XML_SCHEMAV_CVC_MINEXCLUSIVE_VALID); + case XML_SCHEMA_FACET_MININCLUSIVE: + ret = xmlSchemaCompareValues(val, facet->val); + if (ret == -2) + return(-1); + if ((ret == 1) || (ret == 0)) + return(0); + return(XML_SCHEMAV_CVC_MININCLUSIVE_VALID); + case XML_SCHEMA_FACET_WHITESPACE: + /* TODO whitespaces */ + /* + * NOTE: Whitespace should be handled to normalize + * the value to be validated against a the facets; + * not to normalize the value in-between. + */ + return(0); + case XML_SCHEMA_FACET_ENUMERATION: + if (ws == XML_SCHEMA_WHITESPACE_UNKNOWN) { + /* + * This is to ensure API compatibility with the old + * xmlSchemaValidateFacet(). + * TODO: Get rid of this case. + */ + if ((facet->value != NULL) && + (xmlStrEqual(facet->value, value))) + return(0); + } else { + ret = xmlSchemaCompareValuesWhtspExt(facet->val->type, + facet->val, facet->value, fws, valType, val, + value, ws); + if (ret == -2) + return(-1); + if (ret == 0) + return(0); + } + return(XML_SCHEMAV_CVC_ENUMERATION_VALID); + case XML_SCHEMA_FACET_LENGTH: + /* + * SPEC (1.3) "if {primitive type definition} is QName or NOTATION, + * then any {value} is facet-valid." + */ + if ((valType == XML_SCHEMAS_QNAME) || + (valType == XML_SCHEMAS_NOTATION)) + return (0); + /* No break on purpose. */ + case XML_SCHEMA_FACET_MAXLENGTH: + case XML_SCHEMA_FACET_MINLENGTH: { + unsigned int len = 0; + + if ((valType == XML_SCHEMAS_QNAME) || + (valType == XML_SCHEMAS_NOTATION)) + return (0); + /* + * TODO: length, maxLength and minLength must be of type + * nonNegativeInteger only. Check if decimal is used somehow. + */ + 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 (valType) { + case XML_SCHEMAS_STRING: + case XML_SCHEMAS_NORMSTRING: + if (ws == XML_SCHEMA_WHITESPACE_UNKNOWN) { + /* + * This is to ensure API compatibility with the old + * xmlSchemaValidateFacet(). Anyway, this was and + * is not the correct handling. + * TODO: Get rid of this case somehow. + */ + if (valType == XML_SCHEMAS_STRING) + len = xmlUTF8Strlen(value); + else + len = xmlSchemaNormLen(value); + } else if (value != NULL) { + if (ws == XML_SCHEMA_WHITESPACE_COLLAPSE) + len = xmlSchemaNormLen(value); + else + /* + * Should be OK for "preserve" as well. + */ + len = xmlUTF8Strlen(value); + } + break; + case XML_SCHEMAS_IDREF: + 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_ANYURI: + if (value != NULL) + len = xmlSchemaNormLen(value); + break; + default: + TODO + } + } + if (facet->type == XML_SCHEMA_FACET_LENGTH) { + if (len != facet->val->value.decimal.lo) + return(XML_SCHEMAV_CVC_LENGTH_VALID); + } else if (facet->type == XML_SCHEMA_FACET_MINLENGTH) { + if (len < facet->val->value.decimal.lo) + return(XML_SCHEMAV_CVC_MINLENGTH_VALID); + } else { + if (len > facet->val->value.decimal.lo) + return(XML_SCHEMAV_CVC_MAXLENGTH_VALID); + } + break; + } + case XML_SCHEMA_FACET_TOTALDIGITS: + case XML_SCHEMA_FACET_FRACTIONDIGITS: + + if ((facet->val == NULL) || + ((facet->val->type != XML_SCHEMAS_PINTEGER) && + (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(XML_SCHEMAV_CVC_TOTALDIGITS_VALID); + + } else if (facet->type == XML_SCHEMA_FACET_FRACTIONDIGITS) { + if (val->value.decimal.frac > facet->val->value.decimal.lo) + return(XML_SCHEMAV_CVC_FRACTIONDIGITS_VALID); + } + break; + default: + TODO + } + return(0); + +} + +/** + * 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, + xmlSchemaFacetPtr facet, + const xmlChar *value, + xmlSchemaValPtr val) +{ + /* + * This tries to ensure API compatibility regarding the old + * xmlSchemaValidateFacet() and the new xmlSchemaValidateFacetInternal() and + * xmlSchemaValidateFacetWhtsp(). + */ + if (val != NULL) + return(xmlSchemaValidateFacetInternal(facet, + XML_SCHEMA_WHITESPACE_UNKNOWN, val->type, value, val, + XML_SCHEMA_WHITESPACE_UNKNOWN)); + else if (base != NULL) + return(xmlSchemaValidateFacetInternal(facet, + XML_SCHEMA_WHITESPACE_UNKNOWN, base->builtInType, value, val, + XML_SCHEMA_WHITESPACE_UNKNOWN)); + return(-1); +} + +/** + * xmlSchemaValidateFacetWhtsp: + * @facet: the facet to check + * @fws: the whitespace type of the facet's value + * @valType: the built-in type of the value + * @value: the lexical (or normalized for pattern) repr of the value to validate + * @val: the precomputed value + * @ws: the whitespace type of the value + * + * Check a value against a facet condition. This takes value normalization + * according to the specified whitespace types into account. + * Note that @value needs to be the *normalized* value if the facet + * is of type "pattern". + * + * Returns 0 if the element is schemas valid, a positive error code + * number otherwise and -1 in case of internal or API error. + */ +int +xmlSchemaValidateFacetWhtsp(xmlSchemaFacetPtr facet, + xmlSchemaWhitespaceValueType fws, + xmlSchemaValType valType, + const xmlChar *value, + xmlSchemaValPtr val, + xmlSchemaWhitespaceValueType ws) +{ + return(xmlSchemaValidateFacetInternal(facet, fws, valType, + value, val, ws)); +} + +#if 0 +#ifndef DBL_DIG +#define DBL_DIG 16 +#endif +#ifndef DBL_EPSILON +#define DBL_EPSILON 1E-9 +#endif + +#define INTEGER_DIGITS DBL_DIG +#define FRACTION_DIGITS (DBL_DIG + 1) +#define EXPONENT_DIGITS (3 + 2) + +/** + * xmlXPathFormatNumber: + * @number: number to format + * @buffer: output buffer + * @buffersize: size of output buffer + * + * Convert the number into a string representation. + */ +static void +xmlSchemaFormatFloat(double number, char buffer[], int buffersize) +{ + switch (xmlXPathIsInf(number)) { + case 1: + if (buffersize > (int)sizeof("INF")) + snprintf(buffer, buffersize, "INF"); + break; + case -1: + if (buffersize > (int)sizeof("-INF")) + snprintf(buffer, buffersize, "-INF"); + break; + default: + if (xmlXPathIsNaN(number)) { + if (buffersize > (int)sizeof("NaN")) + snprintf(buffer, buffersize, "NaN"); + } else if (number == 0) { + snprintf(buffer, buffersize, "0.0E0"); + } else { + /* 3 is sign, decimal point, and terminating zero */ + char work[DBL_DIG + EXPONENT_DIGITS + 3]; + int integer_place, fraction_place; + char *ptr; + char *after_fraction; + double absolute_value; + int size; + + absolute_value = fabs(number); + + /* + * Result is in work, and after_fraction points + * just past the fractional part. + * Use scientific notation + */ + integer_place = DBL_DIG + EXPONENT_DIGITS + 1; + fraction_place = DBL_DIG - 1; + snprintf(work, sizeof(work),"%*.*e", + integer_place, fraction_place, number); + after_fraction = strchr(work + DBL_DIG, 'e'); + /* Remove fractional trailing zeroes */ + ptr = after_fraction; + while (*(--ptr) == '0') + ; + if (*ptr != '.') + ptr++; + while ((*ptr++ = *after_fraction++) != 0); + + /* Finally copy result back to caller */ + size = strlen(work) + 1; + if (size > buffersize) { + work[buffersize - 1] = 0; + size = buffersize; + } + memmove(buffer, work, size); + } + break; + } +} +#endif + +/** + * xmlSchemaGetCanonValue: + * @val: the precomputed value + * @retValue: the returned value + * + * Get a the cononical lexical representation of the value. + * The caller has to FREE the returned retValue. + * + * WARNING: Some value types are not supported yet, resulting + * in a @retValue of "???". + * + * TODO: XML Schema 1.0 does not define canonical representations + * for: duration, gYearMonth, gYear, gMonthDay, gMonth, gDay, + * anyURI, QName, NOTATION. This will be fixed in XML Schema 1.1. + * + * + * Returns 0 if the value could be built, 1 if the value type is + * not supported yet and -1 in case of API errors. + */ +int +xmlSchemaGetCanonValue(xmlSchemaValPtr val, const xmlChar **retValue) +{ + if ((retValue == NULL) || (val == NULL)) + return (-1); + *retValue = NULL; + switch (val->type) { + case XML_SCHEMAS_STRING: + if (val->value.str == NULL) + *retValue = BAD_CAST xmlStrdup(BAD_CAST ""); + else + *retValue = + BAD_CAST xmlStrdup((const xmlChar *) val->value.str); + break; + case XML_SCHEMAS_NORMSTRING: + if (val->value.str == NULL) + *retValue = BAD_CAST xmlStrdup(BAD_CAST ""); + else { + *retValue = xmlSchemaWhiteSpaceReplace( + (const xmlChar *) val->value.str); + if ((*retValue) == NULL) + *retValue = BAD_CAST xmlStrdup( + (const xmlChar *) val->value.str); + } + break; + 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: /* Unclear */ + case XML_SCHEMAS_ANYURI: /* Unclear */ + if (val->value.str == NULL) + return (-1); + *retValue = + BAD_CAST xmlSchemaCollapseString(BAD_CAST val->value.str); + if (*retValue == NULL) + *retValue = + BAD_CAST xmlStrdup((const xmlChar *) val->value.str); + break; + case XML_SCHEMAS_QNAME: + /* TODO: Unclear in XML Schema 1.0. */ + if (val->value.qname.uri == NULL) { + *retValue = BAD_CAST xmlStrdup(BAD_CAST val->value.qname.name); + return (0); + } else { + *retValue = BAD_CAST xmlStrdup(BAD_CAST "{"); + *retValue = BAD_CAST xmlStrcat((xmlChar *) (*retValue), + BAD_CAST val->value.qname.uri); + *retValue = BAD_CAST xmlStrcat((xmlChar *) (*retValue), + BAD_CAST "}"); + *retValue = BAD_CAST xmlStrcat((xmlChar *) (*retValue), + BAD_CAST val->value.qname.uri); + } + break; + case XML_SCHEMAS_DECIMAL: + /* + * TODO: Lookout for a more simple implementation. + */ + if ((val->value.decimal.total == 1) && + (val->value.decimal.lo == 0)) { + *retValue = xmlStrdup(BAD_CAST "0.0"); + } else { + xmlSchemaValDecimal dec = val->value.decimal; + int bufsize; + char *buf = NULL, *offs; + + /* Add room for the decimal point as well. */ + bufsize = dec.total + 2; + if (dec.sign) + bufsize++; + /* Add room for leading/trailing zero. */ + if ((dec.frac == 0) || (dec.frac == dec.total)) + bufsize++; + buf = xmlMalloc(bufsize); + offs = buf; + if (dec.sign) + *offs++ = '-'; + if (dec.frac == dec.total) { + *offs++ = '0'; + *offs++ = '.'; + } + if (dec.hi != 0) + snprintf(offs, bufsize - (offs - buf), + "%lu%lu%lu", dec.hi, dec.mi, dec.lo); + else if (dec.mi != 0) + snprintf(offs, bufsize - (offs - buf), + "%lu%lu", dec.mi, dec.lo); + else + snprintf(offs, bufsize - (offs - buf), + "%lu", dec.lo); + + if (dec.frac != 0) { + if (dec.frac != dec.total) { + int diff = dec.total - dec.frac; + /* + * Insert the decimal point. + */ + memmove(offs + diff + 1, offs + diff, dec.frac +1); + offs[diff] = '.'; + } else { + unsigned int i = 0; + /* + * Insert missing zeroes behind the decimal point. + */ + while (*(offs + i) != 0) + i++; + if (i < dec.total) { + memmove(offs + (dec.total - i), offs, i +1); + memset(offs, '0', dec.total - i); + } + } + } else { + /* + * Append decimal point and zero. + */ + offs = buf + bufsize - 1; + *offs-- = 0; + *offs-- = '0'; + *offs-- = '.'; + } + *retValue = BAD_CAST buf; + } + break; + case XML_SCHEMAS_INTEGER: + case XML_SCHEMAS_PINTEGER: + case XML_SCHEMAS_NPINTEGER: + case XML_SCHEMAS_NINTEGER: + case XML_SCHEMAS_NNINTEGER: + case XML_SCHEMAS_LONG: + case XML_SCHEMAS_BYTE: + case XML_SCHEMAS_SHORT: + case XML_SCHEMAS_INT: + case XML_SCHEMAS_UINT: + case XML_SCHEMAS_ULONG: + case XML_SCHEMAS_USHORT: + case XML_SCHEMAS_UBYTE: + if ((val->value.decimal.total == 1) && + (val->value.decimal.lo == 0)) + *retValue = xmlStrdup(BAD_CAST "0"); + else { + xmlSchemaValDecimal dec = val->value.decimal; + int bufsize = dec.total + 1; + + /* Add room for the decimal point as well. */ + if (dec.sign) + bufsize++; + *retValue = xmlMalloc(bufsize); + if (dec.hi != 0) { + if (dec.sign) + snprintf((char *) *retValue, bufsize, + "-%lu%lu%lu", dec.hi, dec.mi, dec.lo); + else + snprintf((char *) *retValue, bufsize, + "%lu%lu%lu", dec.hi, dec.mi, dec.lo); + } else if (dec.mi != 0) { + if (dec.sign) + snprintf((char *) *retValue, bufsize, + "-%lu%lu", dec.mi, dec.lo); + else + snprintf((char *) *retValue, bufsize, + "%lu%lu", dec.mi, dec.lo); + } else { + if (dec.sign) + snprintf((char *) *retValue, bufsize, "-%lu", dec.lo); + else + snprintf((char *) *retValue, bufsize, "%lu", dec.lo); + } + } + break; + case XML_SCHEMAS_BOOLEAN: + if (val->value.b) + *retValue = BAD_CAST xmlStrdup(BAD_CAST "true"); + else + *retValue = BAD_CAST xmlStrdup(BAD_CAST "false"); + break; + case XML_SCHEMAS_DURATION: { + char buf[100]; + unsigned long year; + unsigned long mon, day, hour = 0, min = 0; + double sec = 0, left; + + /* TODO: Unclear in XML Schema 1.0 */ + /* + * TODO: This results in a normalized output of the value + * - which is NOT conformant to the spec - + * since the exact values of each property are not + * recoverable. Think about extending the structure to + * provide a field for every property. + */ + year = (unsigned long) FQUOTIENT(labs(val->value.dur.mon), 12); + mon = labs(val->value.dur.mon) - 12 * year; + + day = (unsigned long) FQUOTIENT(fabs(val->value.dur.sec), 86400); + left = fabs(val->value.dur.sec) - day * 86400; + if (left > 0) { + hour = (unsigned long) FQUOTIENT(left, 3600); + left = left - (hour * 3600); + if (left > 0) { + min = (unsigned long) FQUOTIENT(left, 60); + sec = left - (min * 60); + } + } + if ((val->value.dur.mon < 0) || (val->value.dur.sec < 0)) + snprintf(buf, 100, "P%luY%luM%luDT%luH%luM%.14gS", + year, mon, day, hour, min, sec); + else + snprintf(buf, 100, "-P%luY%luM%luDT%luH%luM%.14gS", + year, mon, day, hour, min, sec); + *retValue = BAD_CAST xmlStrdup(BAD_CAST buf); + } + break; + case XML_SCHEMAS_GYEAR: { + char buf[30]; + /* TODO: Unclear in XML Schema 1.0 */ + /* TODO: What to do with the timezone? */ + snprintf(buf, 30, "%04ld", val->value.date.year); + *retValue = BAD_CAST xmlStrdup(BAD_CAST buf); + } + break; + case XML_SCHEMAS_GMONTH: { + /* TODO: Unclear in XML Schema 1.0 */ + /* TODO: What to do with the timezone? */ + *retValue = xmlMalloc(5); + snprintf((char *) *retValue, 6, "--%02u", + val->value.date.mon); + } + break; + case XML_SCHEMAS_GDAY: { + /* TODO: Unclear in XML Schema 1.0 */ + /* TODO: What to do with the timezone? */ + *retValue = xmlMalloc(6); + snprintf((char *) *retValue, 6, "---%02u", + val->value.date.day); + } + break; + case XML_SCHEMAS_GMONTHDAY: { + /* TODO: Unclear in XML Schema 1.0 */ + /* TODO: What to do with the timezone? */ + *retValue = xmlMalloc(8); + snprintf((char *) *retValue, 8, "--%02u-%02u", + val->value.date.mon, val->value.date.day); + } + break; + case XML_SCHEMAS_GYEARMONTH: { + char buf[35]; + /* TODO: Unclear in XML Schema 1.0 */ + /* TODO: What to do with the timezone? */ + if (val->value.date.year < 0) + snprintf(buf, 35, "-%04ld-%02u", + labs(val->value.date.year), + val->value.date.mon); + else + snprintf(buf, 35, "%04ld-%02u", + val->value.date.year, val->value.date.mon); + *retValue = BAD_CAST xmlStrdup(BAD_CAST buf); + } + break; + case XML_SCHEMAS_TIME: + { + char buf[30]; + + if (val->value.date.tz_flag) { + xmlSchemaValPtr norm; + + norm = xmlSchemaDateNormalize(val, 0); + if (norm == NULL) + return (-1); + /* + * TODO: Check if "%.14g" is portable. + */ + snprintf(buf, 30, + "%02u:%02u:%02.14gZ", + norm->value.date.hour, + norm->value.date.min, + norm->value.date.sec); + xmlSchemaFreeValue(norm); + } else { + snprintf(buf, 30, + "%02u:%02u:%02.14g", + val->value.date.hour, + val->value.date.min, + val->value.date.sec); + } + *retValue = BAD_CAST xmlStrdup(BAD_CAST buf); + } + break; + case XML_SCHEMAS_DATE: + { + char buf[30]; + + if (val->value.date.tz_flag) { + xmlSchemaValPtr norm; + + norm = xmlSchemaDateNormalize(val, 0); + if (norm == NULL) + return (-1); + /* + * TODO: Append the canonical value of the + * recoverable timezone and not "Z". + */ + snprintf(buf, 30, + "%04ld:%02u:%02uZ", + norm->value.date.year, norm->value.date.mon, + norm->value.date.day); + xmlSchemaFreeValue(norm); + } else { + snprintf(buf, 30, + "%04ld:%02u:%02u", + val->value.date.year, val->value.date.mon, + val->value.date.day); + } + *retValue = BAD_CAST xmlStrdup(BAD_CAST buf); + } + break; + case XML_SCHEMAS_DATETIME: + { + char buf[50]; + + if (val->value.date.tz_flag) { + xmlSchemaValPtr norm; + + norm = xmlSchemaDateNormalize(val, 0); + if (norm == NULL) + return (-1); + /* + * TODO: Check if "%.14g" is portable. + */ + snprintf(buf, 50, + "%04ld:%02u:%02uT%02u:%02u:%02.14gZ", + norm->value.date.year, norm->value.date.mon, + norm->value.date.day, norm->value.date.hour, + norm->value.date.min, norm->value.date.sec); + xmlSchemaFreeValue(norm); + } else { + snprintf(buf, 50, + "%04ld:%02u:%02uT%02u:%02u:%02.14g", + val->value.date.year, val->value.date.mon, + val->value.date.day, val->value.date.hour, + val->value.date.min, val->value.date.sec); + } + *retValue = BAD_CAST xmlStrdup(BAD_CAST buf); + } + break; + case XML_SCHEMAS_HEXBINARY: + *retValue = BAD_CAST xmlStrdup(BAD_CAST val->value.hex.str); + break; + case XML_SCHEMAS_BASE64BINARY: + /* + * TODO: Is the following spec piece implemented?: + * SPEC: "Note: For some values the canonical form defined + * above does not conform to [RFC 2045], which requires breaking + * with linefeeds at appropriate intervals." + */ + *retValue = BAD_CAST xmlStrdup(BAD_CAST val->value.base64.str); + break; + case XML_SCHEMAS_FLOAT: { + char buf[30]; + /* + * |m| < 16777216, -149 <= e <= 104. + * TODO: Handle, NaN, INF, -INF. The format is not + * yet conformant. The c type float does not cover + * the whole range. + */ + snprintf(buf, 30, "%01.14e", val->value.f); + *retValue = BAD_CAST xmlStrdup(BAD_CAST buf); + } + break; + case XML_SCHEMAS_DOUBLE: { + char buf[40]; + /* |m| < 9007199254740992, -1075 <= e <= 970 */ + /* + * TODO: Handle, NaN, INF, -INF. The format is not + * yet conformant. The c type float does not cover + * the whole range. + */ + snprintf(buf, 40, "%01.14e", val->value.d); + *retValue = BAD_CAST xmlStrdup(BAD_CAST buf); + } + break; + default: + *retValue = BAD_CAST xmlStrdup(BAD_CAST "???"); + return (1); + } + return (0); +} + +/** + * xmlSchemaGetCanonValueWhtsp: + * @val: the precomputed value + * @retValue: the returned value + * @ws: the whitespace type of the value + * + * Get a the cononical representation of the value. + * The caller has to free the returned @retValue. + * + * Returns 0 if the value could be built, 1 if the value type is + * not supported yet and -1 in case of API errors. + */ +int +xmlSchemaGetCanonValueWhtsp(xmlSchemaValPtr val, + const xmlChar **retValue, + xmlSchemaWhitespaceValueType ws) +{ + if ((retValue == NULL) || (val == NULL)) + return (-1); + if ((ws == XML_SCHEMA_WHITESPACE_UNKNOWN) || + (ws > XML_SCHEMA_WHITESPACE_COLLAPSE)) + return (-1); + + *retValue = NULL; + switch (val->type) { + case XML_SCHEMAS_STRING: + if (val->value.str == NULL) + *retValue = BAD_CAST xmlStrdup(BAD_CAST ""); + else if (ws == XML_SCHEMA_WHITESPACE_COLLAPSE) + *retValue = xmlSchemaCollapseString(val->value.str); + else if (ws == XML_SCHEMA_WHITESPACE_REPLACE) + *retValue = xmlSchemaWhiteSpaceReplace(val->value.str); + if ((*retValue) == NULL) + *retValue = BAD_CAST xmlStrdup(val->value.str); + break; + case XML_SCHEMAS_NORMSTRING: + if (val->value.str == NULL) + *retValue = BAD_CAST xmlStrdup(BAD_CAST ""); + else { + if (ws == XML_SCHEMA_WHITESPACE_COLLAPSE) + *retValue = xmlSchemaCollapseString(val->value.str); + else + *retValue = xmlSchemaWhiteSpaceReplace(val->value.str); + if ((*retValue) == NULL) + *retValue = BAD_CAST xmlStrdup(val->value.str); + } + break; + default: + return (xmlSchemaGetCanonValue(val, retValue)); + } + return (0); +} + +/** + * xmlSchemaGetValType: + * @val: a schemas value + * + * Accessor for the type of a value + * + * Returns the xmlSchemaValType of the value + */ +xmlSchemaValType +xmlSchemaGetValType(xmlSchemaValPtr val) +{ + if (val == NULL) + return(XML_SCHEMAS_UNKNOWN); + return (val->type); +} + +#define bottom_xmlschemastypes +#include "elfgcchack.h" +#endif /* LIBXML_SCHEMAS_ENABLED */ diff --git a/Extras/LibXML/xpath.c b/Extras/LibXML/xpath.c new file mode 100644 index 000000000..c6c227448 --- /dev/null +++ b/Extras/LibXML/xpath.c @@ -0,0 +1,14568 @@ +/* + * xpath.c: XML Path Language implementation + * XPath is a language for addressing parts of an XML document, + * designed to be used by both XSLT and XPointer + *f + * Reference: W3C Recommendation 16 November 1999 + * http://www.w3.org/TR/1999/REC-xpath-19991116 + * Public reference: + * http://www.w3.org/TR/xpath + * + * See Copyright for the status of this software + * + * Author: daniel@veillard.com + * + */ + +#define IN_LIBXML +#include "libxml.h" + +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_MATH_H +#include +#endif +#ifdef HAVE_FLOAT_H +#include +#endif +#ifdef HAVE_CTYPE_H +#include +#endif +#ifdef HAVE_SIGNAL_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#ifdef LIBXML_XPTR_ENABLED +#include +#endif +#ifdef LIBXML_DEBUG_ENABLED +#include +#endif +#include +#include +#include +#ifdef LIBXML_PATTERN_ENABLED +#include +#endif + +#ifdef LIBXML_PATTERN_ENABLED +#define XPATH_STREAMING +#endif + +#define TODO \ + xmlGenericError(xmlGenericErrorContext, \ + "Unimplemented block at %s:%d\n", \ + __FILE__, __LINE__); + +/* +* XP_PATTERN_TO_ANY_NODE_ENABLED: when an XPath expression can be +* evaluated using the streaming mode (pattern.c) then this is used to +* enable resolution to nodes of type text-node, cdata-section-node, +* comment-node and pi-node. The only known scenario where this is +* needed is an expression like "foo//.", "//.", etc.; i.e. an expression +* where the final node to be selected can be of any type. +* Disabling this #define will result in an incorrect evaluation to +* only element-nodes and the document node. +*/ +#define XP_PATTERN_TO_ANY_NODE_ENABLED + +/* +* XP_OPTIMIZED_NON_ELEM_COMPARISON: +* If defined, this will use xmlXPathCmpNodesExt() instead of +* xmlXPathCmpNodes(). The new function is optimized comparison of +* non-element nodes; actually it will speed up comparison only if +* xmlXPathOrderDocElems() was called in order to index the elements of +* a tree in document order; Libxslt does such an indexing, thus it will +* benefit from this optimization. +*/ +#define XP_OPTIMIZED_NON_ELEM_COMPARISON + +/* +* XP_OPTIMIZED_FILTER_FIRST: +* If defined, this will optimize expressions like "key('foo', 'val')[b][1]" +* in a way, that it stop evaluation at the first node. +*/ +#define XP_OPTIMIZED_FILTER_FIRST + +/* +* XP_DEBUG_OBJ_USAGE: +* Internal flag to enable tracking of how much XPath objects have been +* created. +*/ +/* #define XP_DEBUG_OBJ_USAGE */ + +/* + * TODO: + * There are a few spots where some tests are done which depend upon ascii + * data. These should be enhanced for full UTF8 support (see particularly + * any use of the macros IS_ASCII_CHARACTER and IS_ASCII_DIGIT) + */ + +#if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED) +/************************************************************************ + * * + * Forward declarations * + * * + ************************************************************************/ +static void +xmlXPathFreeValueTree(xmlNodeSetPtr obj); +static void +xmlXPathReleaseObject(xmlXPathContextPtr ctxt, xmlXPathObjectPtr obj); + +/************************************************************************ + * * + * Floating point stuff * + * * + ************************************************************************/ + +#ifndef TRIO_REPLACE_STDIO +#define TRIO_PUBLIC static +#endif +#include "trionan.c" + +/* + * The lack of portability of this section of the libc is annoying ! + */ +double xmlXPathNAN = 0; +double xmlXPathPINF = 1; +double xmlXPathNINF = -1; +static double xmlXPathNZERO = 0; /* not exported from headers */ +static int xmlXPathInitialized = 0; + +/** + * xmlXPathInit: + * + * Initialize the XPath environment + */ +void +xmlXPathInit(void) { + if (xmlXPathInitialized) return; + + xmlXPathPINF = trio_pinf(); + xmlXPathNINF = trio_ninf(); + xmlXPathNAN = trio_nan(); + xmlXPathNZERO = trio_nzero(); + + xmlXPathInitialized = 1; +} + +/** + * xmlXPathIsNaN: + * @val: a double value + * + * Provides a portable isnan() function to detect whether a double + * is a NotaNumber. Based on trio code + * http://sourceforge.net/projects/ctrio/ + * + * Returns 1 if the value is a NaN, 0 otherwise + */ +int +xmlXPathIsNaN(double val) { + return(trio_isnan(val)); +} + +/** + * xmlXPathIsInf: + * @val: a double value + * + * Provides a portable isinf() function to detect whether a double + * is a +Infinite or -Infinite. Based on trio code + * http://sourceforge.net/projects/ctrio/ + * + * Returns 1 vi the value is +Infinite, -1 if -Infinite, 0 otherwise + */ +int +xmlXPathIsInf(double val) { + return(trio_isinf(val)); +} + +#endif /* SCHEMAS or XPATH */ +#ifdef LIBXML_XPATH_ENABLED +/** + * xmlXPathGetSign: + * @val: a double value + * + * Provides a portable function to detect the sign of a double + * Modified from trio code + * http://sourceforge.net/projects/ctrio/ + * + * Returns 1 if the value is Negative, 0 if positive + */ +static int +xmlXPathGetSign(double val) { + return(trio_signbit(val)); +} + + +/* + * TODO: when compatibility allows remove all "fake node libxslt" strings + * the test should just be name[0] = ' ' + */ +/* #define DEBUG */ +/* #define DEBUG_STEP */ +/* #define DEBUG_STEP_NTH */ +/* #define DEBUG_EXPR */ +/* #define DEBUG_EVAL_COUNTS */ + +static xmlNs xmlXPathXMLNamespaceStruct = { + NULL, + XML_NAMESPACE_DECL, + XML_XML_NAMESPACE, + BAD_CAST "xml", + NULL +}; +static xmlNsPtr xmlXPathXMLNamespace = &xmlXPathXMLNamespaceStruct; +#ifndef LIBXML_THREAD_ENABLED +/* + * Optimizer is disabled only when threaded apps are detected while + * the library ain't compiled for thread safety. + */ +static int xmlXPathDisableOptimizer = 0; +#endif + +/************************************************************************ + * * + * Error handling routines * + * * + ************************************************************************/ + +/** + * XP_ERRORNULL: + * @X: the error code + * + * Macro to raise an XPath error and return NULL. + */ +#define XP_ERRORNULL(X) \ + { xmlXPathErr(ctxt, X); return(NULL); } + +/* + * The array xmlXPathErrorMessages corresponds to the enum xmlXPathError + */ +static const char *xmlXPathErrorMessages[] = { + "Ok\n", + "Number encoding\n", + "Unfinished literal\n", + "Start of literal\n", + "Expected $ for variable reference\n", + "Undefined variable\n", + "Invalid predicate\n", + "Invalid expression\n", + "Missing closing curly brace\n", + "Unregistered function\n", + "Invalid operand\n", + "Invalid type\n", + "Invalid number of arguments\n", + "Invalid context size\n", + "Invalid context position\n", + "Memory allocation error\n", + "Syntax error\n", + "Resource error\n", + "Sub resource error\n", + "Undefined namespace prefix\n", + "Encoding error\n", + "Char out of XML range\n", + "Invalid or incomplete context\n", + "?? Unknown error ??\n" /* Must be last in the list! */ +}; +#define MAXERRNO ((int)(sizeof(xmlXPathErrorMessages) / \ + sizeof(xmlXPathErrorMessages[0])) - 1) +/** + * xmlXPathErrMemory: + * @ctxt: an XPath context + * @extra: extra informations + * + * Handle a redefinition of attribute error + */ +static void +xmlXPathErrMemory(xmlXPathContextPtr ctxt, const char *extra) +{ + if (ctxt != NULL) { + if (extra) { + xmlChar buf[200]; + + xmlStrPrintf(buf, 200, + BAD_CAST "Memory allocation failed : %s\n", + extra); + ctxt->lastError.message = (char *) xmlStrdup(buf); + } else { + ctxt->lastError.message = (char *) + xmlStrdup(BAD_CAST "Memory allocation failed\n"); + } + ctxt->lastError.domain = XML_FROM_XPATH; + ctxt->lastError.code = XML_ERR_NO_MEMORY; + if (ctxt->error != NULL) + ctxt->error(ctxt->userData, &ctxt->lastError); + } else { + if (extra) + __xmlRaiseError(NULL, NULL, NULL, + NULL, NULL, XML_FROM_XPATH, + XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, + extra, NULL, NULL, 0, 0, + "Memory allocation failed : %s\n", extra); + else + __xmlRaiseError(NULL, NULL, NULL, + NULL, NULL, XML_FROM_XPATH, + XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, + NULL, NULL, NULL, 0, 0, + "Memory allocation failed\n"); + } +} + +/** + * xmlXPathPErrMemory: + * @ctxt: an XPath parser context + * @extra: extra informations + * + * Handle a redefinition of attribute error + */ +static void +xmlXPathPErrMemory(xmlXPathParserContextPtr ctxt, const char *extra) +{ + if (ctxt == NULL) + xmlXPathErrMemory(NULL, extra); + else { + ctxt->error = XPATH_MEMORY_ERROR; + xmlXPathErrMemory(ctxt->context, extra); + } +} + +/** + * xmlXPathErr: + * @ctxt: a XPath parser context + * @error: the error code + * + * Handle an XPath error + */ +void +xmlXPathErr(xmlXPathParserContextPtr ctxt, int error) +{ + if ((error < 0) || (error > MAXERRNO)) + error = MAXERRNO; + if (ctxt == NULL) { + __xmlRaiseError(NULL, NULL, NULL, + NULL, NULL, XML_FROM_XPATH, + error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK, + XML_ERR_ERROR, NULL, 0, + NULL, NULL, NULL, 0, 0, + xmlXPathErrorMessages[error]); + return; + } + ctxt->error = error; + if (ctxt->context == NULL) { + __xmlRaiseError(NULL, NULL, NULL, + NULL, NULL, XML_FROM_XPATH, + error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK, + XML_ERR_ERROR, NULL, 0, + (const char *) ctxt->base, NULL, NULL, + ctxt->cur - ctxt->base, 0, + xmlXPathErrorMessages[error]); + return; + } + + /* cleanup current last error */ + xmlResetError(&ctxt->context->lastError); + + ctxt->context->lastError.domain = XML_FROM_XPATH; + ctxt->context->lastError.code = error + XML_XPATH_EXPRESSION_OK - + XPATH_EXPRESSION_OK; + ctxt->context->lastError.level = XML_ERR_ERROR; + ctxt->context->lastError.str1 = (char *) xmlStrdup(ctxt->base); + ctxt->context->lastError.int1 = ctxt->cur - ctxt->base; + ctxt->context->lastError.node = ctxt->context->debugNode; + if (ctxt->context->error != NULL) { + ctxt->context->error(ctxt->context->userData, + &ctxt->context->lastError); + } else { + __xmlRaiseError(NULL, NULL, NULL, + NULL, ctxt->context->debugNode, XML_FROM_XPATH, + error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK, + XML_ERR_ERROR, NULL, 0, + (const char *) ctxt->base, NULL, NULL, + ctxt->cur - ctxt->base, 0, + xmlXPathErrorMessages[error]); + } + +} + +/** + * xmlXPatherror: + * @ctxt: the XPath Parser context + * @file: the file name + * @line: the line number + * @no: the error number + * + * Formats an error message. + */ +void +xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file ATTRIBUTE_UNUSED, + int line ATTRIBUTE_UNUSED, int no) { + xmlXPathErr(ctxt, no); +} + +/************************************************************************ + * * + * Utilities * + * * + ************************************************************************/ + +/** + * xsltPointerList: + * + * Pointer-list for various purposes. + */ +typedef struct _xmlPointerList xmlPointerList; +typedef xmlPointerList *xmlPointerListPtr; +struct _xmlPointerList { + void **items; + int number; + int size; +}; +/* +* TODO: Since such a list-handling is used in xmlschemas.c and libxslt +* and here, we should make the functions public. +*/ +static int +xmlPointerListAddSize(xmlPointerListPtr list, + void *item, + int initialSize) +{ + if (list->items == NULL) { + if (initialSize <= 0) + initialSize = 1; + list->items = (void **) xmlMalloc( + initialSize * sizeof(void *)); + if (list->items == NULL) { + xmlXPathErrMemory(NULL, + "xmlPointerListCreate: allocating item\n"); + return(-1); + } + list->number = 0; + list->size = initialSize; + } else if (list->size <= list->number) { + list->size *= 2; + list->items = (void **) xmlRealloc(list->items, + list->size * sizeof(void *)); + if (list->items == NULL) { + xmlXPathErrMemory(NULL, + "xmlPointerListCreate: re-allocating item\n"); + list->size = 0; + return(-1); + } + } + list->items[list->number++] = item; + return(0); +} + +/** + * xsltPointerListCreate: + * + * Creates an xsltPointerList structure. + * + * Returns a xsltPointerList structure or NULL in case of an error. + */ +static xmlPointerListPtr +xmlPointerListCreate(int initialSize) +{ + xmlPointerListPtr ret; + + ret = xmlMalloc(sizeof(xmlPointerList)); + if (ret == NULL) { + xmlXPathErrMemory(NULL, + "xmlPointerListCreate: allocating item\n"); + return (NULL); + } + memset(ret, 0, sizeof(xmlPointerList)); + if (initialSize > 0) { + xmlPointerListAddSize(ret, NULL, initialSize); + ret->number = 0; + } + return (ret); +} + +/** + * xsltPointerListFree: + * + * Frees the xsltPointerList structure. This does not free + * the content of the list. + */ +static void +xmlPointerListFree(xmlPointerListPtr list) +{ + if (list == NULL) + return; + if (list->items != NULL) + xmlFree(list->items); + xmlFree(list); +} + +/************************************************************************ + * * + * Parser Types * + * * + ************************************************************************/ + +/* + * Types are private: + */ + +typedef enum { + XPATH_OP_END=0, + XPATH_OP_AND, + XPATH_OP_OR, + XPATH_OP_EQUAL, + XPATH_OP_CMP, + XPATH_OP_PLUS, + XPATH_OP_MULT, + XPATH_OP_UNION, + XPATH_OP_ROOT, + XPATH_OP_NODE, + XPATH_OP_RESET, /* 10 */ + XPATH_OP_COLLECT, + XPATH_OP_VALUE, /* 12 */ + XPATH_OP_VARIABLE, + XPATH_OP_FUNCTION, + XPATH_OP_ARG, + XPATH_OP_PREDICATE, + XPATH_OP_FILTER, /* 17 */ + XPATH_OP_SORT /* 18 */ +#ifdef LIBXML_XPTR_ENABLED + ,XPATH_OP_RANGETO +#endif +} xmlXPathOp; + +typedef enum { + AXIS_ANCESTOR = 1, + AXIS_ANCESTOR_OR_SELF, + AXIS_ATTRIBUTE, + AXIS_CHILD, + AXIS_DESCENDANT, + AXIS_DESCENDANT_OR_SELF, + AXIS_FOLLOWING, + AXIS_FOLLOWING_SIBLING, + AXIS_NAMESPACE, + AXIS_PARENT, + AXIS_PRECEDING, + AXIS_PRECEDING_SIBLING, + AXIS_SELF +} xmlXPathAxisVal; + +typedef enum { + NODE_TEST_NONE = 0, + NODE_TEST_TYPE = 1, + NODE_TEST_PI = 2, + NODE_TEST_ALL = 3, + NODE_TEST_NS = 4, + NODE_TEST_NAME = 5 +} xmlXPathTestVal; + +typedef enum { + NODE_TYPE_NODE = 0, + NODE_TYPE_COMMENT = XML_COMMENT_NODE, + NODE_TYPE_TEXT = XML_TEXT_NODE, + NODE_TYPE_PI = XML_PI_NODE +} xmlXPathTypeVal; + +#define XP_REWRITE_DOS_CHILD_ELEM 1 + +typedef struct _xmlXPathStepOp xmlXPathStepOp; +typedef xmlXPathStepOp *xmlXPathStepOpPtr; +struct _xmlXPathStepOp { + xmlXPathOp op; /* The identifier of the operation */ + int ch1; /* First child */ + int ch2; /* Second child */ + int value; + int value2; + int value3; + void *value4; + void *value5; + void *cache; + void *cacheURI; + int rewriteType; +}; + +struct _xmlXPathCompExpr { + int nbStep; /* Number of steps in this expression */ + int maxStep; /* Maximum number of steps allocated */ + xmlXPathStepOp *steps; /* ops for computation of this expression */ + int last; /* index of last step in expression */ + xmlChar *expr; /* the expression being computed */ + xmlDictPtr dict; /* the dictionnary to use if any */ +#ifdef DEBUG_EVAL_COUNTS + int nb; + xmlChar *string; +#endif +#ifdef XPATH_STREAMING + xmlPatternPtr stream; +#endif +}; + +/************************************************************************ + * * + * Parser Type functions * + * * + ************************************************************************/ + +/** + * xmlXPathNewCompExpr: + * + * Create a new Xpath component + * + * Returns the newly allocated xmlXPathCompExprPtr or NULL in case of error + */ +static xmlXPathCompExprPtr +xmlXPathNewCompExpr(void) { + xmlXPathCompExprPtr cur; + + cur = (xmlXPathCompExprPtr) xmlMalloc(sizeof(xmlXPathCompExpr)); + if (cur == NULL) { + xmlXPathErrMemory(NULL, "allocating component\n"); + return(NULL); + } + memset(cur, 0, sizeof(xmlXPathCompExpr)); + cur->maxStep = 10; + cur->nbStep = 0; + cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep * + sizeof(xmlXPathStepOp)); + if (cur->steps == NULL) { + xmlXPathErrMemory(NULL, "allocating steps\n"); + xmlFree(cur); + return(NULL); + } + memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp)); + cur->last = -1; +#ifdef DEBUG_EVAL_COUNTS + cur->nb = 0; +#endif + return(cur); +} + +/** + * xmlXPathFreeCompExpr: + * @comp: an XPATH comp + * + * Free up the memory allocated by @comp + */ +void +xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp) +{ + xmlXPathStepOpPtr op; + int i; + + if (comp == NULL) + return; + if (comp->dict == NULL) { + for (i = 0; i < comp->nbStep; i++) { + op = &comp->steps[i]; + if (op->value4 != NULL) { + if (op->op == XPATH_OP_VALUE) + xmlXPathFreeObject(op->value4); + else + xmlFree(op->value4); + } + if (op->value5 != NULL) + xmlFree(op->value5); + } + } else { + for (i = 0; i < comp->nbStep; i++) { + op = &comp->steps[i]; + if (op->value4 != NULL) { + if (op->op == XPATH_OP_VALUE) + xmlXPathFreeObject(op->value4); + } + } + xmlDictFree(comp->dict); + } + if (comp->steps != NULL) { + xmlFree(comp->steps); + } +#ifdef DEBUG_EVAL_COUNTS + if (comp->string != NULL) { + xmlFree(comp->string); + } +#endif +#ifdef XPATH_STREAMING + if (comp->stream != NULL) { + xmlFreePatternList(comp->stream); + } +#endif + if (comp->expr != NULL) { + xmlFree(comp->expr); + } + + xmlFree(comp); +} + +/** + * xmlXPathCompExprAdd: + * @comp: the compiled expression + * @ch1: first child index + * @ch2: second child index + * @op: an op + * @value: the first int value + * @value2: the second int value + * @value3: the third int value + * @value4: the first string value + * @value5: the second string value + * + * Add a step to an XPath Compiled Expression + * + * Returns -1 in case of failure, the index otherwise + */ +static int +xmlXPathCompExprAdd(xmlXPathCompExprPtr comp, int ch1, int ch2, + xmlXPathOp op, int value, + int value2, int value3, void *value4, void *value5) { + if (comp->nbStep >= comp->maxStep) { + xmlXPathStepOp *real; + + comp->maxStep *= 2; + real = (xmlXPathStepOp *) xmlRealloc(comp->steps, + comp->maxStep * sizeof(xmlXPathStepOp)); + if (real == NULL) { + comp->maxStep /= 2; + xmlXPathErrMemory(NULL, "adding step\n"); + return(-1); + } + comp->steps = real; + } + comp->last = comp->nbStep; + comp->steps[comp->nbStep].ch1 = ch1; + comp->steps[comp->nbStep].ch2 = ch2; + comp->steps[comp->nbStep].op = op; + comp->steps[comp->nbStep].value = value; + comp->steps[comp->nbStep].value2 = value2; + comp->steps[comp->nbStep].value3 = value3; + if ((comp->dict != NULL) && + ((op == XPATH_OP_FUNCTION) || (op == XPATH_OP_VARIABLE) || + (op == XPATH_OP_COLLECT))) { + if (value4 != NULL) { + comp->steps[comp->nbStep].value4 = (xmlChar *) + (void *)xmlDictLookup(comp->dict, value4, -1); + xmlFree(value4); + } else + comp->steps[comp->nbStep].value4 = NULL; + if (value5 != NULL) { + comp->steps[comp->nbStep].value5 = (xmlChar *) + (void *)xmlDictLookup(comp->dict, value5, -1); + xmlFree(value5); + } else + comp->steps[comp->nbStep].value5 = NULL; + } else { + comp->steps[comp->nbStep].value4 = value4; + comp->steps[comp->nbStep].value5 = value5; + } + comp->steps[comp->nbStep].cache = NULL; + return(comp->nbStep++); +} + +/** + * xmlXPathCompSwap: + * @comp: the compiled expression + * @op: operation index + * + * Swaps 2 operations in the compiled expression + */ +static void +xmlXPathCompSwap(xmlXPathStepOpPtr op) { + int tmp; + +#ifndef LIBXML_THREAD_ENABLED + /* + * Since this manipulates possibly shared variables, this is + * disabled if one detects that the library is used in a multithreaded + * application + */ + if (xmlXPathDisableOptimizer) + return; +#endif + + tmp = op->ch1; + op->ch1 = op->ch2; + op->ch2 = tmp; +} + +#define PUSH_FULL_EXPR(op, op1, op2, val, val2, val3, val4, val5) \ + xmlXPathCompExprAdd(ctxt->comp, (op1), (op2), \ + (op), (val), (val2), (val3), (val4), (val5)) +#define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \ + xmlXPathCompExprAdd(ctxt->comp, ctxt->comp->last, -1, \ + (op), (val), (val2), (val3), (val4), (val5)) + +#define PUSH_LEAVE_EXPR(op, val, val2) \ +xmlXPathCompExprAdd(ctxt->comp, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL) + +#define PUSH_UNARY_EXPR(op, ch, val, val2) \ +xmlXPathCompExprAdd(ctxt->comp, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL) + +#define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \ +xmlXPathCompExprAdd(ctxt->comp, (ch1), (ch2), (op), \ + (val), (val2), 0 ,NULL ,NULL) + +/************************************************************************ + * * + * XPath object cache structures * + * * + ************************************************************************/ + +/* #define XP_DEFAULT_CACHE_ON */ + +#define XP_HAS_CACHE(c) ((c != NULL) && ((c)->cache != NULL)) + +typedef struct _xmlXPathContextCache xmlXPathContextCache; +typedef xmlXPathContextCache *xmlXPathContextCachePtr; +struct _xmlXPathContextCache { + xmlPointerListPtr nodesetObjs; /* contains xmlXPathObjectPtr */ + xmlPointerListPtr stringObjs; /* contains xmlXPathObjectPtr */ + xmlPointerListPtr booleanObjs; /* contains xmlXPathObjectPtr */ + xmlPointerListPtr numberObjs; /* contains xmlXPathObjectPtr */ + xmlPointerListPtr miscObjs; /* contains xmlXPathObjectPtr */ + int maxNodeset; + int maxString; + int maxBoolean; + int maxNumber; + int maxMisc; +#ifdef XP_DEBUG_OBJ_USAGE + int dbgCachedAll; + int dbgCachedNodeset; + int dbgCachedString; + int dbgCachedBool; + int dbgCachedNumber; + int dbgCachedPoint; + int dbgCachedRange; + int dbgCachedLocset; + int dbgCachedUsers; + int dbgCachedXSLTTree; + int dbgCachedUndefined; + + + int dbgReusedAll; + int dbgReusedNodeset; + int dbgReusedString; + int dbgReusedBool; + int dbgReusedNumber; + int dbgReusedPoint; + int dbgReusedRange; + int dbgReusedLocset; + int dbgReusedUsers; + int dbgReusedXSLTTree; + int dbgReusedUndefined; + +#endif +}; + +/************************************************************************ + * * + * Debugging related functions * + * * + ************************************************************************/ + +#define STRANGE \ + xmlGenericError(xmlGenericErrorContext, \ + "Internal error at %s:%d\n", \ + __FILE__, __LINE__); + +#ifdef LIBXML_DEBUG_ENABLED +static void +xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) { + int i; + char shift[100]; + + for (i = 0;((i < depth) && (i < 25));i++) + shift[2 * i] = shift[2 * i + 1] = ' '; + shift[2 * i] = shift[2 * i + 1] = 0; + if (cur == NULL) { + fprintf(output, shift); + fprintf(output, "Node is NULL !\n"); + return; + + } + + if ((cur->type == XML_DOCUMENT_NODE) || + (cur->type == XML_HTML_DOCUMENT_NODE)) { + fprintf(output, shift); + fprintf(output, " /\n"); + } else if (cur->type == XML_ATTRIBUTE_NODE) + xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth); + else + xmlDebugDumpOneNode(output, cur, depth); +} +static void +xmlXPathDebugDumpNodeList(FILE *output, xmlNodePtr cur, int depth) { + xmlNodePtr tmp; + int i; + char shift[100]; + + for (i = 0;((i < depth) && (i < 25));i++) + shift[2 * i] = shift[2 * i + 1] = ' '; + shift[2 * i] = shift[2 * i + 1] = 0; + if (cur == NULL) { + fprintf(output, shift); + fprintf(output, "Node is NULL !\n"); + return; + + } + + while (cur != NULL) { + tmp = cur; + cur = cur->next; + xmlDebugDumpOneNode(output, tmp, depth); + } +} + +static void +xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) { + int i; + char shift[100]; + + for (i = 0;((i < depth) && (i < 25));i++) + shift[2 * i] = shift[2 * i + 1] = ' '; + shift[2 * i] = shift[2 * i + 1] = 0; + + if (cur == NULL) { + fprintf(output, shift); + fprintf(output, "NodeSet is NULL !\n"); + return; + + } + + if (cur != NULL) { + fprintf(output, "Set contains %d nodes:\n", cur->nodeNr); + for (i = 0;i < cur->nodeNr;i++) { + fprintf(output, shift); + fprintf(output, "%d", i + 1); + xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1); + } + } +} + +static void +xmlXPathDebugDumpValueTree(FILE *output, xmlNodeSetPtr cur, int depth) { + int i; + char shift[100]; + + for (i = 0;((i < depth) && (i < 25));i++) + shift[2 * i] = shift[2 * i + 1] = ' '; + shift[2 * i] = shift[2 * i + 1] = 0; + + if ((cur == NULL) || (cur->nodeNr == 0) || (cur->nodeTab[0] == NULL)) { + fprintf(output, shift); + fprintf(output, "Value Tree is NULL !\n"); + return; + + } + + fprintf(output, shift); + fprintf(output, "%d", i + 1); + xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1); +} +#if defined(LIBXML_XPTR_ENABLED) +static void +xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) { + int i; + char shift[100]; + + for (i = 0;((i < depth) && (i < 25));i++) + shift[2 * i] = shift[2 * i + 1] = ' '; + shift[2 * i] = shift[2 * i + 1] = 0; + + if (cur == NULL) { + fprintf(output, shift); + fprintf(output, "LocationSet is NULL !\n"); + return; + + } + + for (i = 0;i < cur->locNr;i++) { + fprintf(output, shift); + fprintf(output, "%d : ", i + 1); + xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1); + } +} +#endif /* LIBXML_XPTR_ENABLED */ + +/** + * xmlXPathDebugDumpObject: + * @output: the FILE * to dump the output + * @cur: the object to inspect + * @depth: indentation level + * + * Dump the content of the object for debugging purposes + */ +void +xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) { + int i; + char shift[100]; + + if (output == NULL) return; + + for (i = 0;((i < depth) && (i < 25));i++) + shift[2 * i] = shift[2 * i + 1] = ' '; + shift[2 * i] = shift[2 * i + 1] = 0; + + + fprintf(output, shift); + + if (cur == NULL) { + fprintf(output, "Object is empty (NULL)\n"); + return; + } + switch(cur->type) { + case XPATH_UNDEFINED: + fprintf(output, "Object is uninitialized\n"); + break; + case XPATH_NODESET: + fprintf(output, "Object is a Node Set :\n"); + xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth); + break; + case XPATH_XSLT_TREE: + fprintf(output, "Object is an XSLT value tree :\n"); + xmlXPathDebugDumpValueTree(output, cur->nodesetval, depth); + break; + case XPATH_BOOLEAN: + fprintf(output, "Object is a Boolean : "); + if (cur->boolval) fprintf(output, "true\n"); + else fprintf(output, "false\n"); + break; + case XPATH_NUMBER: + switch (xmlXPathIsInf(cur->floatval)) { + case 1: + fprintf(output, "Object is a number : Infinity\n"); + break; + case -1: + fprintf(output, "Object is a number : -Infinity\n"); + break; + default: + if (xmlXPathIsNaN(cur->floatval)) { + fprintf(output, "Object is a number : NaN\n"); + } else if (cur->floatval == 0 && xmlXPathGetSign(cur->floatval) != 0) { + fprintf(output, "Object is a number : 0\n"); + } else { + fprintf(output, "Object is a number : %0g\n", cur->floatval); + } + } + break; + case XPATH_STRING: + fprintf(output, "Object is a string : "); + xmlDebugDumpString(output, cur->stringval); + fprintf(output, "\n"); + break; + case XPATH_POINT: + fprintf(output, "Object is a point : index %d in node", cur->index); + xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1); + fprintf(output, "\n"); + break; + case XPATH_RANGE: + if ((cur->user2 == NULL) || + ((cur->user2 == cur->user) && (cur->index == cur->index2))) { + fprintf(output, "Object is a collapsed range :\n"); + fprintf(output, shift); + if (cur->index >= 0) + fprintf(output, "index %d in ", cur->index); + fprintf(output, "node\n"); + xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, + depth + 1); + } else { + fprintf(output, "Object is a range :\n"); + fprintf(output, shift); + fprintf(output, "From "); + if (cur->index >= 0) + fprintf(output, "index %d in ", cur->index); + fprintf(output, "node\n"); + xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, + depth + 1); + fprintf(output, shift); + fprintf(output, "To "); + if (cur->index2 >= 0) + fprintf(output, "index %d in ", cur->index2); + fprintf(output, "node\n"); + xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2, + depth + 1); + fprintf(output, "\n"); + } + break; + case XPATH_LOCATIONSET: +#if defined(LIBXML_XPTR_ENABLED) + fprintf(output, "Object is a Location Set:\n"); + xmlXPathDebugDumpLocationSet(output, + (xmlLocationSetPtr) cur->user, depth); +#endif + break; + case XPATH_USERS: + fprintf(output, "Object is user defined\n"); + break; + } +} + +static void +xmlXPathDebugDumpStepOp(FILE *output, xmlXPathCompExprPtr comp, + xmlXPathStepOpPtr op, int depth) { + int i; + char shift[100]; + + for (i = 0;((i < depth) && (i < 25));i++) + shift[2 * i] = shift[2 * i + 1] = ' '; + shift[2 * i] = shift[2 * i + 1] = 0; + + fprintf(output, shift); + if (op == NULL) { + fprintf(output, "Step is NULL\n"); + return; + } + switch (op->op) { + case XPATH_OP_END: + fprintf(output, "END"); break; + case XPATH_OP_AND: + fprintf(output, "AND"); break; + case XPATH_OP_OR: + fprintf(output, "OR"); break; + case XPATH_OP_EQUAL: + if (op->value) + fprintf(output, "EQUAL ="); + else + fprintf(output, "EQUAL !="); + break; + case XPATH_OP_CMP: + if (op->value) + fprintf(output, "CMP <"); + else + fprintf(output, "CMP >"); + if (!op->value2) + fprintf(output, "="); + break; + case XPATH_OP_PLUS: + if (op->value == 0) + fprintf(output, "PLUS -"); + else if (op->value == 1) + fprintf(output, "PLUS +"); + else if (op->value == 2) + fprintf(output, "PLUS unary -"); + else if (op->value == 3) + fprintf(output, "PLUS unary - -"); + break; + case XPATH_OP_MULT: + if (op->value == 0) + fprintf(output, "MULT *"); + else if (op->value == 1) + fprintf(output, "MULT div"); + else + fprintf(output, "MULT mod"); + break; + case XPATH_OP_UNION: + fprintf(output, "UNION"); break; + case XPATH_OP_ROOT: + fprintf(output, "ROOT"); break; + case XPATH_OP_NODE: + fprintf(output, "NODE"); break; + case XPATH_OP_RESET: + fprintf(output, "RESET"); break; + case XPATH_OP_SORT: + fprintf(output, "SORT"); break; + case XPATH_OP_COLLECT: { + xmlXPathAxisVal axis = (xmlXPathAxisVal)op->value; + xmlXPathTestVal test = (xmlXPathTestVal)op->value2; + xmlXPathTypeVal type = (xmlXPathTypeVal)op->value3; + const xmlChar *prefix = op->value4; + const xmlChar *name = op->value5; + + fprintf(output, "COLLECT "); + switch (axis) { + case AXIS_ANCESTOR: + fprintf(output, " 'ancestors' "); break; + case AXIS_ANCESTOR_OR_SELF: + fprintf(output, " 'ancestors-or-self' "); break; + case AXIS_ATTRIBUTE: + fprintf(output, " 'attributes' "); break; + case AXIS_CHILD: + fprintf(output, " 'child' "); break; + case AXIS_DESCENDANT: + fprintf(output, " 'descendant' "); break; + case AXIS_DESCENDANT_OR_SELF: + fprintf(output, " 'descendant-or-self' "); break; + case AXIS_FOLLOWING: + fprintf(output, " 'following' "); break; + case AXIS_FOLLOWING_SIBLING: + fprintf(output, " 'following-siblings' "); break; + case AXIS_NAMESPACE: + fprintf(output, " 'namespace' "); break; + case AXIS_PARENT: + fprintf(output, " 'parent' "); break; + case AXIS_PRECEDING: + fprintf(output, " 'preceding' "); break; + case AXIS_PRECEDING_SIBLING: + fprintf(output, " 'preceding-sibling' "); break; + case AXIS_SELF: + fprintf(output, " 'self' "); break; + } + switch (test) { + case NODE_TEST_NONE: + fprintf(output, "'none' "); break; + case NODE_TEST_TYPE: + fprintf(output, "'type' "); break; + case NODE_TEST_PI: + fprintf(output, "'PI' "); break; + case NODE_TEST_ALL: + fprintf(output, "'all' "); break; + case NODE_TEST_NS: + fprintf(output, "'namespace' "); break; + case NODE_TEST_NAME: + fprintf(output, "'name' "); break; + } + switch (type) { + case NODE_TYPE_NODE: + fprintf(output, "'node' "); break; + case NODE_TYPE_COMMENT: + fprintf(output, "'comment' "); break; + case NODE_TYPE_TEXT: + fprintf(output, "'text' "); break; + case NODE_TYPE_PI: + fprintf(output, "'PI' "); break; + } + if (prefix != NULL) + fprintf(output, "%s:", prefix); + if (name != NULL) + fprintf(output, "%s", (const char *) name); + break; + + } + case XPATH_OP_VALUE: { + xmlXPathObjectPtr object = (xmlXPathObjectPtr) op->value4; + + fprintf(output, "ELEM "); + xmlXPathDebugDumpObject(output, object, 0); + goto finish; + } + case XPATH_OP_VARIABLE: { + const xmlChar *prefix = op->value5; + const xmlChar *name = op->value4; + + if (prefix != NULL) + fprintf(output, "VARIABLE %s:%s", prefix, name); + else + fprintf(output, "VARIABLE %s", name); + break; + } + case XPATH_OP_FUNCTION: { + int nbargs = op->value; + const xmlChar *prefix = op->value5; + const xmlChar *name = op->value4; + + if (prefix != NULL) + fprintf(output, "FUNCTION %s:%s(%d args)", + prefix, name, nbargs); + else + fprintf(output, "FUNCTION %s(%d args)", name, nbargs); + break; + } + case XPATH_OP_ARG: fprintf(output, "ARG"); break; + case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE"); break; + case XPATH_OP_FILTER: fprintf(output, "FILTER"); break; +#ifdef LIBXML_XPTR_ENABLED + case XPATH_OP_RANGETO: fprintf(output, "RANGETO"); break; +#endif + default: + fprintf(output, "UNKNOWN %d\n", op->op); return; + } + fprintf(output, "\n"); +finish: + if (op->ch1 >= 0) + xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch1], depth + 1); + if (op->ch2 >= 0) + xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch2], depth + 1); +} + +/** + * xmlXPathDebugDumpCompExpr: + * @output: the FILE * for the output + * @comp: the precompiled XPath expression + * @depth: the indentation level. + * + * Dumps the tree of the compiled XPath expression. + */ +void +xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp, + int depth) { + int i; + char shift[100]; + + if ((output == NULL) || (comp == NULL)) return; + + for (i = 0;((i < depth) && (i < 25));i++) + shift[2 * i] = shift[2 * i + 1] = ' '; + shift[2 * i] = shift[2 * i + 1] = 0; + + fprintf(output, shift); + + fprintf(output, "Compiled Expression : %d elements\n", + comp->nbStep); + i = comp->last; + xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1); +} + +#ifdef XP_DEBUG_OBJ_USAGE + +/* +* XPath object usage related debugging variables. +*/ +static int xmlXPathDebugObjCounterUndefined = 0; +static int xmlXPathDebugObjCounterNodeset = 0; +static int xmlXPathDebugObjCounterBool = 0; +static int xmlXPathDebugObjCounterNumber = 0; +static int xmlXPathDebugObjCounterString = 0; +static int xmlXPathDebugObjCounterPoint = 0; +static int xmlXPathDebugObjCounterRange = 0; +static int xmlXPathDebugObjCounterLocset = 0; +static int xmlXPathDebugObjCounterUsers = 0; +static int xmlXPathDebugObjCounterXSLTTree = 0; +static int xmlXPathDebugObjCounterAll = 0; + +static int xmlXPathDebugObjTotalUndefined = 0; +static int xmlXPathDebugObjTotalNodeset = 0; +static int xmlXPathDebugObjTotalBool = 0; +static int xmlXPathDebugObjTotalNumber = 0; +static int xmlXPathDebugObjTotalString = 0; +static int xmlXPathDebugObjTotalPoint = 0; +static int xmlXPathDebugObjTotalRange = 0; +static int xmlXPathDebugObjTotalLocset = 0; +static int xmlXPathDebugObjTotalUsers = 0; +static int xmlXPathDebugObjTotalXSLTTree = 0; +static int xmlXPathDebugObjTotalAll = 0; + +static int xmlXPathDebugObjMaxUndefined = 0; +static int xmlXPathDebugObjMaxNodeset = 0; +static int xmlXPathDebugObjMaxBool = 0; +static int xmlXPathDebugObjMaxNumber = 0; +static int xmlXPathDebugObjMaxString = 0; +static int xmlXPathDebugObjMaxPoint = 0; +static int xmlXPathDebugObjMaxRange = 0; +static int xmlXPathDebugObjMaxLocset = 0; +static int xmlXPathDebugObjMaxUsers = 0; +static int xmlXPathDebugObjMaxXSLTTree = 0; +static int xmlXPathDebugObjMaxAll = 0; + +/* REVISIT TODO: Make this static when committing */ +static void +xmlXPathDebugObjUsageReset(xmlXPathContextPtr ctxt) +{ + if (ctxt != NULL) { + if (ctxt->cache != NULL) { + xmlXPathContextCachePtr cache = + (xmlXPathContextCachePtr) ctxt->cache; + + cache->dbgCachedAll = 0; + cache->dbgCachedNodeset = 0; + cache->dbgCachedString = 0; + cache->dbgCachedBool = 0; + cache->dbgCachedNumber = 0; + cache->dbgCachedPoint = 0; + cache->dbgCachedRange = 0; + cache->dbgCachedLocset = 0; + cache->dbgCachedUsers = 0; + cache->dbgCachedXSLTTree = 0; + cache->dbgCachedUndefined = 0; + + cache->dbgReusedAll = 0; + cache->dbgReusedNodeset = 0; + cache->dbgReusedString = 0; + cache->dbgReusedBool = 0; + cache->dbgReusedNumber = 0; + cache->dbgReusedPoint = 0; + cache->dbgReusedRange = 0; + cache->dbgReusedLocset = 0; + cache->dbgReusedUsers = 0; + cache->dbgReusedXSLTTree = 0; + cache->dbgReusedUndefined = 0; + } + } + + xmlXPathDebugObjCounterUndefined = 0; + xmlXPathDebugObjCounterNodeset = 0; + xmlXPathDebugObjCounterBool = 0; + xmlXPathDebugObjCounterNumber = 0; + xmlXPathDebugObjCounterString = 0; + xmlXPathDebugObjCounterPoint = 0; + xmlXPathDebugObjCounterRange = 0; + xmlXPathDebugObjCounterLocset = 0; + xmlXPathDebugObjCounterUsers = 0; + xmlXPathDebugObjCounterXSLTTree = 0; + xmlXPathDebugObjCounterAll = 0; + + xmlXPathDebugObjTotalUndefined = 0; + xmlXPathDebugObjTotalNodeset = 0; + xmlXPathDebugObjTotalBool = 0; + xmlXPathDebugObjTotalNumber = 0; + xmlXPathDebugObjTotalString = 0; + xmlXPathDebugObjTotalPoint = 0; + xmlXPathDebugObjTotalRange = 0; + xmlXPathDebugObjTotalLocset = 0; + xmlXPathDebugObjTotalUsers = 0; + xmlXPathDebugObjTotalXSLTTree = 0; + xmlXPathDebugObjTotalAll = 0; + + xmlXPathDebugObjMaxUndefined = 0; + xmlXPathDebugObjMaxNodeset = 0; + xmlXPathDebugObjMaxBool = 0; + xmlXPathDebugObjMaxNumber = 0; + xmlXPathDebugObjMaxString = 0; + xmlXPathDebugObjMaxPoint = 0; + xmlXPathDebugObjMaxRange = 0; + xmlXPathDebugObjMaxLocset = 0; + xmlXPathDebugObjMaxUsers = 0; + xmlXPathDebugObjMaxXSLTTree = 0; + xmlXPathDebugObjMaxAll = 0; + +} + +static void +xmlXPathDebugObjUsageRequested(xmlXPathContextPtr ctxt, + xmlXPathObjectType objType) +{ + int isCached = 0; + + if (ctxt != NULL) { + if (ctxt->cache != NULL) { + xmlXPathContextCachePtr cache = + (xmlXPathContextCachePtr) ctxt->cache; + + isCached = 1; + + cache->dbgReusedAll++; + switch (objType) { + case XPATH_UNDEFINED: + cache->dbgReusedUndefined++; + break; + case XPATH_NODESET: + cache->dbgReusedNodeset++; + break; + case XPATH_BOOLEAN: + cache->dbgReusedBool++; + break; + case XPATH_NUMBER: + cache->dbgReusedNumber++; + break; + case XPATH_STRING: + cache->dbgReusedString++; + break; + case XPATH_POINT: + cache->dbgReusedPoint++; + break; + case XPATH_RANGE: + cache->dbgReusedRange++; + break; + case XPATH_LOCATIONSET: + cache->dbgReusedLocset++; + break; + case XPATH_USERS: + cache->dbgReusedUsers++; + break; + case XPATH_XSLT_TREE: + cache->dbgReusedXSLTTree++; + break; + default: + break; + } + } + } + + switch (objType) { + case XPATH_UNDEFINED: + if (! isCached) + xmlXPathDebugObjTotalUndefined++; + xmlXPathDebugObjCounterUndefined++; + if (xmlXPathDebugObjCounterUndefined > + xmlXPathDebugObjMaxUndefined) + xmlXPathDebugObjMaxUndefined = + xmlXPathDebugObjCounterUndefined; + break; + case XPATH_NODESET: + if (! isCached) + xmlXPathDebugObjTotalNodeset++; + xmlXPathDebugObjCounterNodeset++; + if (xmlXPathDebugObjCounterNodeset > + xmlXPathDebugObjMaxNodeset) + xmlXPathDebugObjMaxNodeset = + xmlXPathDebugObjCounterNodeset; + break; + case XPATH_BOOLEAN: + if (! isCached) + xmlXPathDebugObjTotalBool++; + xmlXPathDebugObjCounterBool++; + if (xmlXPathDebugObjCounterBool > + xmlXPathDebugObjMaxBool) + xmlXPathDebugObjMaxBool = + xmlXPathDebugObjCounterBool; + break; + case XPATH_NUMBER: + if (! isCached) + xmlXPathDebugObjTotalNumber++; + xmlXPathDebugObjCounterNumber++; + if (xmlXPathDebugObjCounterNumber > + xmlXPathDebugObjMaxNumber) + xmlXPathDebugObjMaxNumber = + xmlXPathDebugObjCounterNumber; + break; + case XPATH_STRING: + if (! isCached) + xmlXPathDebugObjTotalString++; + xmlXPathDebugObjCounterString++; + if (xmlXPathDebugObjCounterString > + xmlXPathDebugObjMaxString) + xmlXPathDebugObjMaxString = + xmlXPathDebugObjCounterString; + break; + case XPATH_POINT: + if (! isCached) + xmlXPathDebugObjTotalPoint++; + xmlXPathDebugObjCounterPoint++; + if (xmlXPathDebugObjCounterPoint > + xmlXPathDebugObjMaxPoint) + xmlXPathDebugObjMaxPoint = + xmlXPathDebugObjCounterPoint; + break; + case XPATH_RANGE: + if (! isCached) + xmlXPathDebugObjTotalRange++; + xmlXPathDebugObjCounterRange++; + if (xmlXPathDebugObjCounterRange > + xmlXPathDebugObjMaxRange) + xmlXPathDebugObjMaxRange = + xmlXPathDebugObjCounterRange; + break; + case XPATH_LOCATIONSET: + if (! isCached) + xmlXPathDebugObjTotalLocset++; + xmlXPathDebugObjCounterLocset++; + if (xmlXPathDebugObjCounterLocset > + xmlXPathDebugObjMaxLocset) + xmlXPathDebugObjMaxLocset = + xmlXPathDebugObjCounterLocset; + break; + case XPATH_USERS: + if (! isCached) + xmlXPathDebugObjTotalUsers++; + xmlXPathDebugObjCounterUsers++; + if (xmlXPathDebugObjCounterUsers > + xmlXPathDebugObjMaxUsers) + xmlXPathDebugObjMaxUsers = + xmlXPathDebugObjCounterUsers; + break; + case XPATH_XSLT_TREE: + if (! isCached) + xmlXPathDebugObjTotalXSLTTree++; + xmlXPathDebugObjCounterXSLTTree++; + if (xmlXPathDebugObjCounterXSLTTree > + xmlXPathDebugObjMaxXSLTTree) + xmlXPathDebugObjMaxXSLTTree = + xmlXPathDebugObjCounterXSLTTree; + break; + default: + break; + } + if (! isCached) + xmlXPathDebugObjTotalAll++; + xmlXPathDebugObjCounterAll++; + if (xmlXPathDebugObjCounterAll > + xmlXPathDebugObjMaxAll) + xmlXPathDebugObjMaxAll = + xmlXPathDebugObjCounterAll; +} + +static void +xmlXPathDebugObjUsageReleased(xmlXPathContextPtr ctxt, + xmlXPathObjectType objType) +{ + int isCached = 0; + + if (ctxt != NULL) { + if (ctxt->cache != NULL) { + xmlXPathContextCachePtr cache = + (xmlXPathContextCachePtr) ctxt->cache; + + isCached = 1; + + cache->dbgCachedAll++; + switch (objType) { + case XPATH_UNDEFINED: + cache->dbgCachedUndefined++; + break; + case XPATH_NODESET: + cache->dbgCachedNodeset++; + break; + case XPATH_BOOLEAN: + cache->dbgCachedBool++; + break; + case XPATH_NUMBER: + cache->dbgCachedNumber++; + break; + case XPATH_STRING: + cache->dbgCachedString++; + break; + case XPATH_POINT: + cache->dbgCachedPoint++; + break; + case XPATH_RANGE: + cache->dbgCachedRange++; + break; + case XPATH_LOCATIONSET: + cache->dbgCachedLocset++; + break; + case XPATH_USERS: + cache->dbgCachedUsers++; + break; + case XPATH_XSLT_TREE: + cache->dbgCachedXSLTTree++; + break; + default: + break; + } + + } + } + switch (objType) { + case XPATH_UNDEFINED: + xmlXPathDebugObjCounterUndefined--; + break; + case XPATH_NODESET: + xmlXPathDebugObjCounterNodeset--; + break; + case XPATH_BOOLEAN: + xmlXPathDebugObjCounterBool--; + break; + case XPATH_NUMBER: + xmlXPathDebugObjCounterNumber--; + break; + case XPATH_STRING: + xmlXPathDebugObjCounterString--; + break; + case XPATH_POINT: + xmlXPathDebugObjCounterPoint--; + break; + case XPATH_RANGE: + xmlXPathDebugObjCounterRange--; + break; + case XPATH_LOCATIONSET: + xmlXPathDebugObjCounterLocset--; + break; + case XPATH_USERS: + xmlXPathDebugObjCounterUsers--; + break; + case XPATH_XSLT_TREE: + xmlXPathDebugObjCounterXSLTTree--; + break; + default: + break; + } + xmlXPathDebugObjCounterAll--; +} + +/* REVISIT TODO: Make this static when committing */ +static void +xmlXPathDebugObjUsageDisplay(xmlXPathContextPtr ctxt) +{ + int reqAll, reqNodeset, reqString, reqBool, reqNumber, + reqXSLTTree, reqUndefined; + int caAll = 0, caNodeset = 0, caString = 0, caBool = 0, + caNumber = 0, caXSLTTree = 0, caUndefined = 0; + int reAll = 0, reNodeset = 0, reString = 0, reBool = 0, + reNumber = 0, reXSLTTree = 0, reUndefined = 0; + int leftObjs = xmlXPathDebugObjCounterAll; + + reqAll = xmlXPathDebugObjTotalAll; + reqNodeset = xmlXPathDebugObjTotalNodeset; + reqString = xmlXPathDebugObjTotalString; + reqBool = xmlXPathDebugObjTotalBool; + reqNumber = xmlXPathDebugObjTotalNumber; + reqXSLTTree = xmlXPathDebugObjTotalXSLTTree; + reqUndefined = xmlXPathDebugObjTotalUndefined; + + printf("# XPath object usage:\n"); + + if (ctxt != NULL) { + if (ctxt->cache != NULL) { + xmlXPathContextCachePtr cache = + (xmlXPathContextCachePtr) ctxt->cache; + + reAll = cache->dbgReusedAll; + reqAll += reAll; + reNodeset = cache->dbgReusedNodeset; + reqNodeset += reNodeset; + reString = cache->dbgReusedString; + reqString += reString; + reBool = cache->dbgReusedBool; + reqBool += reBool; + reNumber = cache->dbgReusedNumber; + reqNumber += reNumber; + reXSLTTree = cache->dbgReusedXSLTTree; + reqXSLTTree += reXSLTTree; + reUndefined = cache->dbgReusedUndefined; + reqUndefined += reUndefined; + + caAll = cache->dbgCachedAll; + caBool = cache->dbgCachedBool; + caNodeset = cache->dbgCachedNodeset; + caString = cache->dbgCachedString; + caNumber = cache->dbgCachedNumber; + caXSLTTree = cache->dbgCachedXSLTTree; + caUndefined = cache->dbgCachedUndefined; + + if (cache->nodesetObjs) + leftObjs -= cache->nodesetObjs->number; + if (cache->stringObjs) + leftObjs -= cache->stringObjs->number; + if (cache->booleanObjs) + leftObjs -= cache->booleanObjs->number; + if (cache->numberObjs) + leftObjs -= cache->numberObjs->number; + if (cache->miscObjs) + leftObjs -= cache->miscObjs->number; + } + } + + printf("# all\n"); + printf("# total : %d\n", reqAll); + printf("# left : %d\n", leftObjs); + printf("# created: %d\n", xmlXPathDebugObjTotalAll); + printf("# reused : %d\n", reAll); + printf("# max : %d\n", xmlXPathDebugObjMaxAll); + + printf("# node-sets\n"); + printf("# total : %d\n", reqNodeset); + printf("# created: %d\n", xmlXPathDebugObjTotalNodeset); + printf("# reused : %d\n", reNodeset); + printf("# max : %d\n", xmlXPathDebugObjMaxNodeset); + + printf("# strings\n"); + printf("# total : %d\n", reqString); + printf("# created: %d\n", xmlXPathDebugObjTotalString); + printf("# reused : %d\n", reString); + printf("# max : %d\n", xmlXPathDebugObjMaxString); + + printf("# booleans\n"); + printf("# total : %d\n", reqBool); + printf("# created: %d\n", xmlXPathDebugObjTotalBool); + printf("# reused : %d\n", reBool); + printf("# max : %d\n", xmlXPathDebugObjMaxBool); + + printf("# numbers\n"); + printf("# total : %d\n", reqNumber); + printf("# created: %d\n", xmlXPathDebugObjTotalNumber); + printf("# reused : %d\n", reNumber); + printf("# max : %d\n", xmlXPathDebugObjMaxNumber); + + printf("# XSLT result tree fragments\n"); + printf("# total : %d\n", reqXSLTTree); + printf("# created: %d\n", xmlXPathDebugObjTotalXSLTTree); + printf("# reused : %d\n", reXSLTTree); + printf("# max : %d\n", xmlXPathDebugObjMaxXSLTTree); + + printf("# undefined\n"); + printf("# total : %d\n", reqUndefined); + printf("# created: %d\n", xmlXPathDebugObjTotalUndefined); + printf("# reused : %d\n", reUndefined); + printf("# max : %d\n", xmlXPathDebugObjMaxUndefined); + +} + +#endif /* XP_DEBUG_OBJ_USAGE */ + +#endif /* LIBXML_DEBUG_ENABLED */ + +/************************************************************************ + * * + * XPath object caching * + * * + ************************************************************************/ + +/** + * xmlXPathNewCache: + * + * Create a new object cache + * + * Returns the xmlXPathCache just allocated. + */ +static xmlXPathContextCachePtr +xmlXPathNewCache(void) +{ + xmlXPathContextCachePtr ret; + + ret = (xmlXPathContextCachePtr) xmlMalloc(sizeof(xmlXPathContextCache)); + if (ret == NULL) { + xmlXPathErrMemory(NULL, "creating object cache\n"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathContextCache)); + ret->maxNodeset = 100; + ret->maxString = 100; + ret->maxBoolean = 100; + ret->maxNumber = 100; + ret->maxMisc = 100; + return(ret); +} + +static void +xmlXPathCacheFreeObjectList(xmlPointerListPtr list) +{ + int i; + xmlXPathObjectPtr obj; + + if (list == NULL) + return; + + for (i = 0; i < list->number; i++) { + obj = list->items[i]; + /* + * Note that it is already assured that we don't need to + * look out for namespace nodes in the node-set. + */ + if (obj->nodesetval != NULL) { + if (obj->nodesetval->nodeTab != NULL) + xmlFree(obj->nodesetval->nodeTab); + xmlFree(obj->nodesetval); + } + xmlFree(obj); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjCounterAll--; +#endif + } + xmlPointerListFree(list); +} + +static void +xmlXPathFreeCache(xmlXPathContextCachePtr cache) +{ + if (cache == NULL) + return; + if (cache->nodesetObjs) + xmlXPathCacheFreeObjectList(cache->nodesetObjs); + if (cache->stringObjs) + xmlXPathCacheFreeObjectList(cache->stringObjs); + if (cache->booleanObjs) + xmlXPathCacheFreeObjectList(cache->booleanObjs); + if (cache->numberObjs) + xmlXPathCacheFreeObjectList(cache->numberObjs); + if (cache->miscObjs) + xmlXPathCacheFreeObjectList(cache->miscObjs); + xmlFree(cache); +} + +/** + * xmlXPathContextSetCache: + * + * @ctxt: the XPath context + * @active: enables/disables (creates/frees) the cache + * @value: a value with semantics dependant on @options + * @options: options (currently only the value 0 is used) + * + * Creates/frees an object cache on the XPath context. + * If activates XPath objects (xmlXPathObject) will be cached internally + * to be reused. + * @options: + * 0: This will set the XPath object caching: + * @value: + * This will set the maximum number of XPath objects + * to be cached per slot + * There are 5 slots for: node-set, string, number, boolean, and + * misc objects. Use <0 for the default number (100). + * Other values for @options have currently no effect. + * + * Returns 0 if the setting succeeded, and -1 on API or internal errors. + */ +int +xmlXPathContextSetCache(xmlXPathContextPtr ctxt, + int active, + int value, + int options) +{ + if (ctxt == NULL) + return(-1); + if (active) { + xmlXPathContextCachePtr cache; + + if (ctxt->cache == NULL) { + ctxt->cache = xmlXPathNewCache(); + if (ctxt->cache == NULL) + return(-1); + } + cache = (xmlXPathContextCachePtr) ctxt->cache; + if (options == 0) { + if (value < 0) + value = 100; + cache->maxNodeset = value; + cache->maxString = value; + cache->maxNumber = value; + cache->maxBoolean = value; + cache->maxMisc = value; + } + } else if (ctxt->cache != NULL) { + xmlXPathFreeCache((xmlXPathContextCachePtr) ctxt->cache); + ctxt->cache = NULL; + } + return(0); +} + +/** + * xmlXPathCacheWrapNodeSet: + * @ctxt: the XPath context + * @val: the NodePtr value + * + * This is the cached version of xmlXPathWrapNodeSet(). + * Wrap the Nodeset @val in a new xmlXPathObjectPtr + * + * Returns the created or reused object. + */ +static xmlXPathObjectPtr +xmlXPathCacheWrapNodeSet(xmlXPathContextPtr ctxt, xmlNodeSetPtr val) +{ + if ((ctxt != NULL) && (ctxt->cache != NULL)) { + xmlXPathContextCachePtr cache = + (xmlXPathContextCachePtr) ctxt->cache; + + if ((cache->miscObjs != NULL) && + (cache->miscObjs->number != 0)) + { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) + cache->miscObjs->items[--cache->miscObjs->number]; + ret->type = XPATH_NODESET; + ret->nodesetval = val; +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET); +#endif + return(ret); + } + } + + return(xmlXPathWrapNodeSet(val)); + +} + +/** + * xmlXPathCacheWrapString: + * @ctxt: the XPath context + * @val: the xmlChar * value + * + * This is the cached version of xmlXPathWrapString(). + * Wraps the @val string into an XPath object. + * + * Returns the created or reused object. + */ +static xmlXPathObjectPtr +xmlXPathCacheWrapString(xmlXPathContextPtr ctxt, xmlChar *val) +{ + if ((ctxt != NULL) && (ctxt->cache != NULL)) { + xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache; + + if ((cache->stringObjs != NULL) && + (cache->stringObjs->number != 0)) + { + + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) + cache->stringObjs->items[--cache->stringObjs->number]; + ret->type = XPATH_STRING; + ret->stringval = val; +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING); +#endif + return(ret); + } else if ((cache->miscObjs != NULL) && + (cache->miscObjs->number != 0)) + { + xmlXPathObjectPtr ret; + /* + * Fallback to misc-cache. + */ + ret = (xmlXPathObjectPtr) + cache->miscObjs->items[--cache->miscObjs->number]; + + ret->type = XPATH_STRING; + ret->stringval = val; +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING); +#endif + return(ret); + } + } + return(xmlXPathWrapString(val)); +} + +/** + * xmlXPathCacheNewNodeSet: + * @ctxt: the XPath context + * @val: the NodePtr value + * + * This is the cached version of xmlXPathNewNodeSet(). + * Acquire an xmlXPathObjectPtr of type NodeSet and initialize + * it with the single Node @val + * + * Returns the created or reused object. + */ +static xmlXPathObjectPtr +xmlXPathCacheNewNodeSet(xmlXPathContextPtr ctxt, xmlNodePtr val) +{ + if ((ctxt != NULL) && (ctxt->cache)) { + xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache; + + if ((cache->nodesetObjs != NULL) && + (cache->nodesetObjs->number != 0)) + { + xmlXPathObjectPtr ret; + /* + * Use the nodset-cache. + */ + ret = (xmlXPathObjectPtr) + cache->nodesetObjs->items[--cache->nodesetObjs->number]; + ret->type = XPATH_NODESET; + ret->boolval = 0; + if (val) { + if ((ret->nodesetval->nodeMax == 0) || + (val->type == XML_NAMESPACE_DECL)) + { + xmlXPathNodeSetAddUnique(ret->nodesetval, val); + } else { + ret->nodesetval->nodeTab[0] = val; + ret->nodesetval->nodeNr = 1; + } + } +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET); +#endif + return(ret); + } else if ((cache->miscObjs != NULL) && + (cache->miscObjs->number != 0)) + { + xmlXPathObjectPtr ret; + /* + * Fallback to misc-cache. + */ + + ret = (xmlXPathObjectPtr) + cache->miscObjs->items[--cache->miscObjs->number]; + + ret->type = XPATH_NODESET; + ret->boolval = 0; + ret->nodesetval = xmlXPathNodeSetCreate(val); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET); +#endif + return(ret); + } + } + return(xmlXPathNewNodeSet(val)); +} + +/** + * xmlXPathCacheNewCString: + * @ctxt: the XPath context + * @val: the char * value + * + * This is the cached version of xmlXPathNewCString(). + * Acquire an xmlXPathObjectPtr of type string and of value @val + * + * Returns the created or reused object. + */ +static xmlXPathObjectPtr +xmlXPathCacheNewCString(xmlXPathContextPtr ctxt, const char *val) +{ + if ((ctxt != NULL) && (ctxt->cache)) { + xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache; + + if ((cache->stringObjs != NULL) && + (cache->stringObjs->number != 0)) + { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) + cache->stringObjs->items[--cache->stringObjs->number]; + + ret->type = XPATH_STRING; + ret->stringval = xmlStrdup(BAD_CAST val); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING); +#endif + return(ret); + } else if ((cache->miscObjs != NULL) && + (cache->miscObjs->number != 0)) + { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) + cache->miscObjs->items[--cache->miscObjs->number]; + + ret->type = XPATH_STRING; + ret->stringval = xmlStrdup(BAD_CAST val); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING); +#endif + return(ret); + } + } + return(xmlXPathNewCString(val)); +} + +/** + * xmlXPathCacheNewString: + * @ctxt: the XPath context + * @val: the xmlChar * value + * + * This is the cached version of xmlXPathNewString(). + * Acquire an xmlXPathObjectPtr of type string and of value @val + * + * Returns the created or reused object. + */ +static xmlXPathObjectPtr +xmlXPathCacheNewString(xmlXPathContextPtr ctxt, const xmlChar *val) +{ + if ((ctxt != NULL) && (ctxt->cache)) { + xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache; + + if ((cache->stringObjs != NULL) && + (cache->stringObjs->number != 0)) + { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) + cache->stringObjs->items[--cache->stringObjs->number]; + ret->type = XPATH_STRING; + if (val != NULL) + ret->stringval = xmlStrdup(val); + else + ret->stringval = xmlStrdup((const xmlChar *)""); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING); +#endif + return(ret); + } else if ((cache->miscObjs != NULL) && + (cache->miscObjs->number != 0)) + { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) + cache->miscObjs->items[--cache->miscObjs->number]; + + ret->type = XPATH_STRING; + if (val != NULL) + ret->stringval = xmlStrdup(val); + else + ret->stringval = xmlStrdup((const xmlChar *)""); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING); +#endif + return(ret); + } + } + return(xmlXPathNewString(val)); +} + +/** + * xmlXPathCacheNewBoolean: + * @ctxt: the XPath context + * @val: the boolean value + * + * This is the cached version of xmlXPathNewBoolean(). + * Acquires an xmlXPathObjectPtr of type boolean and of value @val + * + * Returns the created or reused object. + */ +static xmlXPathObjectPtr +xmlXPathCacheNewBoolean(xmlXPathContextPtr ctxt, int val) +{ + if ((ctxt != NULL) && (ctxt->cache)) { + xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache; + + if ((cache->booleanObjs != NULL) && + (cache->booleanObjs->number != 0)) + { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) + cache->booleanObjs->items[--cache->booleanObjs->number]; + ret->type = XPATH_BOOLEAN; + ret->boolval = (val != 0); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_BOOLEAN); +#endif + return(ret); + } else if ((cache->miscObjs != NULL) && + (cache->miscObjs->number != 0)) + { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) + cache->miscObjs->items[--cache->miscObjs->number]; + + ret->type = XPATH_BOOLEAN; + ret->boolval = (val != 0); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_BOOLEAN); +#endif + return(ret); + } + } + return(xmlXPathNewBoolean(val)); +} + +/** + * xmlXPathCacheNewFloat: + * @ctxt: the XPath context + * @val: the double value + * + * This is the cached version of xmlXPathNewFloat(). + * Acquires an xmlXPathObjectPtr of type double and of value @val + * + * Returns the created or reused object. + */ +static xmlXPathObjectPtr +xmlXPathCacheNewFloat(xmlXPathContextPtr ctxt, double val) +{ + if ((ctxt != NULL) && (ctxt->cache)) { + xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache; + + if ((cache->numberObjs != NULL) && + (cache->numberObjs->number != 0)) + { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) + cache->numberObjs->items[--cache->numberObjs->number]; + ret->type = XPATH_NUMBER; + ret->floatval = val; +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_NUMBER); +#endif + return(ret); + } else if ((cache->miscObjs != NULL) && + (cache->miscObjs->number != 0)) + { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) + cache->miscObjs->items[--cache->miscObjs->number]; + + ret->type = XPATH_NUMBER; + ret->floatval = val; +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(ctxt, XPATH_NUMBER); +#endif + return(ret); + } + } + return(xmlXPathNewFloat(val)); +} + +/** + * xmlXPathCacheConvertString: + * @ctxt: the XPath context + * @val: an XPath object + * + * This is the cached version of xmlXPathConvertString(). + * Converts an existing object to its string() equivalent + * + * Returns a created or reused object, the old one is freed (cached) + * (or the operation is done directly on @val) + */ + +static xmlXPathObjectPtr +xmlXPathCacheConvertString(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) { + xmlChar *res = NULL; + + if (val == NULL) + return(xmlXPathCacheNewCString(ctxt, "")); + + switch (val->type) { + case XPATH_UNDEFINED: +#ifdef DEBUG_EXPR + xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n"); +#endif + break; + case XPATH_NODESET: + case XPATH_XSLT_TREE: + res = xmlXPathCastNodeSetToString(val->nodesetval); + break; + case XPATH_STRING: + return(val); + case XPATH_BOOLEAN: + res = xmlXPathCastBooleanToString(val->boolval); + break; + case XPATH_NUMBER: + res = xmlXPathCastNumberToString(val->floatval); + break; + case XPATH_USERS: + case XPATH_POINT: + case XPATH_RANGE: + case XPATH_LOCATIONSET: + TODO; + break; + } + xmlXPathReleaseObject(ctxt, val); + if (res == NULL) + return(xmlXPathCacheNewCString(ctxt, "")); + return(xmlXPathCacheWrapString(ctxt, res)); +} + +/** + * xmlXPathCacheObjectCopy: + * @ctxt: the XPath context + * @val: the original object + * + * This is the cached version of xmlXPathObjectCopy(). + * Acquire a copy of a given object + * + * Returns a created or reused created object. + */ +static xmlXPathObjectPtr +xmlXPathCacheObjectCopy(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) +{ + if (val == NULL) + return(NULL); + + switch (val->type) { + case XPATH_NODESET: + if (XP_HAS_CACHE(ctxt)) + return(xmlXPathCacheWrapNodeSet(ctxt, + xmlXPathNodeSetMerge(NULL, val->nodesetval))); + case XPATH_STRING: + if (XP_HAS_CACHE(ctxt)) + return(xmlXPathCacheNewString(ctxt, val->stringval)); + case XPATH_BOOLEAN: + if (XP_HAS_CACHE(ctxt)) + return(xmlXPathCacheNewBoolean(ctxt, val->boolval)); + case XPATH_NUMBER: + if (XP_HAS_CACHE(ctxt)) + return(xmlXPathCacheNewFloat(ctxt, val->floatval)); + default: + break; + } + return(xmlXPathObjectCopy(val)); +} + +/** + * xmlXPathCacheConvertBoolean: + * @ctxt: the XPath context + * @val: an XPath object + * + * This is the cached version of xmlXPathConvertBoolean(). + * Converts an existing object to its boolean() equivalent + * + * Returns a created or reused object, the old one is freed (or the operation + * is done directly on @val) + */ +static xmlXPathObjectPtr +xmlXPathCacheConvertBoolean(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) { + xmlXPathObjectPtr ret; + + if (val == NULL) + return(xmlXPathCacheNewBoolean(ctxt, 0)); + if (val->type == XPATH_BOOLEAN) + return(val); + ret = xmlXPathCacheNewBoolean(ctxt, xmlXPathCastToBoolean(val)); + xmlXPathReleaseObject(ctxt, val); + return(ret); +} + +/** + * xmlXPathCacheConvertNumber: + * @ctxt: the XPath context + * @val: an XPath object + * + * This is the cached version of xmlXPathConvertNumber(). + * Converts an existing object to its number() equivalent + * + * Returns a created or reused object, the old one is freed (or the operation + * is done directly on @val) + */ +static xmlXPathObjectPtr +xmlXPathCacheConvertNumber(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) { + xmlXPathObjectPtr ret; + + if (val == NULL) + return(xmlXPathCacheNewFloat(ctxt, 0.0)); + if (val->type == XPATH_NUMBER) + return(val); + ret = xmlXPathCacheNewFloat(ctxt, xmlXPathCastToNumber(val)); + xmlXPathReleaseObject(ctxt, val); + return(ret); +} + +/************************************************************************ + * * + * Parser stacks related functions and macros * + * * + ************************************************************************/ + +/** + * valuePop: + * @ctxt: an XPath evaluation context + * + * Pops the top XPath object from the value stack + * + * Returns the XPath object just removed + */ +xmlXPathObjectPtr +valuePop(xmlXPathParserContextPtr ctxt) +{ + xmlXPathObjectPtr ret; + + if ((ctxt == NULL) || (ctxt->valueNr <= 0)) + return (NULL); + ctxt->valueNr--; + if (ctxt->valueNr > 0) + ctxt->value = ctxt->valueTab[ctxt->valueNr - 1]; + else + ctxt->value = NULL; + ret = ctxt->valueTab[ctxt->valueNr]; + ctxt->valueTab[ctxt->valueNr] = NULL; + return (ret); +} +/** + * valuePush: + * @ctxt: an XPath evaluation context + * @value: the XPath object + * + * Pushes a new XPath object on top of the value stack + * + * returns the number of items on the value stack + */ +int +valuePush(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr value) +{ + if ((ctxt == NULL) || (value == NULL)) return(-1); + if (ctxt->valueNr >= ctxt->valueMax) { + xmlXPathObjectPtr *tmp; + + tmp = (xmlXPathObjectPtr *) xmlRealloc(ctxt->valueTab, + 2 * ctxt->valueMax * + sizeof(ctxt->valueTab[0])); + if (tmp == NULL) { + xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); + return (0); + } + ctxt->valueMax *= 2; + ctxt->valueTab = tmp; + } + ctxt->valueTab[ctxt->valueNr] = value; + ctxt->value = value; + return (ctxt->valueNr++); +} + +/** + * xmlXPathPopBoolean: + * @ctxt: an XPath parser context + * + * Pops a boolean from the stack, handling conversion if needed. + * Check error with #xmlXPathCheckError. + * + * Returns the boolean + */ +int +xmlXPathPopBoolean (xmlXPathParserContextPtr ctxt) { + xmlXPathObjectPtr obj; + int ret; + + obj = valuePop(ctxt); + if (obj == NULL) { + xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND); + return(0); + } + if (obj->type != XPATH_BOOLEAN) + ret = xmlXPathCastToBoolean(obj); + else + ret = obj->boolval; + xmlXPathReleaseObject(ctxt->context, obj); + return(ret); +} + +/** + * xmlXPathPopNumber: + * @ctxt: an XPath parser context + * + * Pops a number from the stack, handling conversion if needed. + * Check error with #xmlXPathCheckError. + * + * Returns the number + */ +double +xmlXPathPopNumber (xmlXPathParserContextPtr ctxt) { + xmlXPathObjectPtr obj; + double ret; + + obj = valuePop(ctxt); + if (obj == NULL) { + xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND); + return(0); + } + if (obj->type != XPATH_NUMBER) + ret = xmlXPathCastToNumber(obj); + else + ret = obj->floatval; + xmlXPathReleaseObject(ctxt->context, obj); + return(ret); +} + +/** + * xmlXPathPopString: + * @ctxt: an XPath parser context + * + * Pops a string from the stack, handling conversion if needed. + * Check error with #xmlXPathCheckError. + * + * Returns the string + */ +xmlChar * +xmlXPathPopString (xmlXPathParserContextPtr ctxt) { + xmlXPathObjectPtr obj; + xmlChar * ret; + + obj = valuePop(ctxt); + if (obj == NULL) { + xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND); + return(NULL); + } + ret = xmlXPathCastToString(obj); /* this does required strdup */ + /* TODO: needs refactoring somewhere else */ + if (obj->stringval == ret) + obj->stringval = NULL; + xmlXPathReleaseObject(ctxt->context, obj); + return(ret); +} + +/** + * xmlXPathPopNodeSet: + * @ctxt: an XPath parser context + * + * Pops a node-set from the stack, handling conversion if needed. + * Check error with #xmlXPathCheckError. + * + * Returns the node-set + */ +xmlNodeSetPtr +xmlXPathPopNodeSet (xmlXPathParserContextPtr ctxt) { + xmlXPathObjectPtr obj; + xmlNodeSetPtr ret; + + if (ctxt == NULL) return(NULL); + if (ctxt->value == NULL) { + xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND); + return(NULL); + } + if (!xmlXPathStackIsNodeSet(ctxt)) { + xmlXPathSetTypeError(ctxt); + return(NULL); + } + obj = valuePop(ctxt); + ret = obj->nodesetval; +#if 0 + /* to fix memory leak of not clearing obj->user */ + if (obj->boolval && obj->user != NULL) + xmlFreeNodeList((xmlNodePtr) obj->user); +#endif + obj->nodesetval = NULL; + xmlXPathReleaseObject(ctxt->context, obj); + return(ret); +} + +/** + * xmlXPathPopExternal: + * @ctxt: an XPath parser context + * + * Pops an external object from the stack, handling conversion if needed. + * Check error with #xmlXPathCheckError. + * + * Returns the object + */ +void * +xmlXPathPopExternal (xmlXPathParserContextPtr ctxt) { + xmlXPathObjectPtr obj; + void * ret; + + if ((ctxt == NULL) || (ctxt->value == NULL)) { + xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND); + return(NULL); + } + if (ctxt->value->type != XPATH_USERS) { + xmlXPathSetTypeError(ctxt); + return(NULL); + } + obj = valuePop(ctxt); + ret = obj->user; + obj->user = NULL; + xmlXPathReleaseObject(ctxt->context, obj); + return(ret); +} + +/* + * Macros for accessing the content. Those should be used only by the parser, + * and not exported. + * + * Dirty macros, i.e. one need to make assumption on the context to use them + * + * CUR_PTR return the current pointer to the xmlChar to be parsed. + * CUR returns the current xmlChar value, i.e. a 8 bit value + * in ISO-Latin or UTF-8. + * This should be used internally by the parser + * only to compare to ASCII values otherwise it would break when + * running with UTF-8 encoding. + * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only + * to compare on ASCII based substring. + * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined + * strings within the parser. + * CURRENT Returns the current char value, with the full decoding of + * UTF-8 if we are using this mode. It returns an int. + * NEXT Skip to the next character, this does the proper decoding + * in UTF-8 mode. It also pop-up unfinished entities on the fly. + * It returns the pointer to the current xmlChar. + */ + +#define CUR (*ctxt->cur) +#define SKIP(val) ctxt->cur += (val) +#define NXT(val) ctxt->cur[(val)] +#define CUR_PTR ctxt->cur +#define CUR_CHAR(l) xmlXPathCurrentChar(ctxt, &l) + +#define COPY_BUF(l,b,i,v) \ + if (l == 1) b[i++] = (xmlChar) v; \ + else i += xmlCopyChar(l,&b[i],v) + +#define NEXTL(l) ctxt->cur += l + +#define SKIP_BLANKS \ + while (IS_BLANK_CH(*(ctxt->cur))) NEXT + +#define CURRENT (*ctxt->cur) +#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur) + + +#ifndef DBL_DIG +#define DBL_DIG 16 +#endif +#ifndef DBL_EPSILON +#define DBL_EPSILON 1E-9 +#endif + +#define UPPER_DOUBLE 1E9 +#define LOWER_DOUBLE 1E-5 + +#define INTEGER_DIGITS DBL_DIG +#define FRACTION_DIGITS (DBL_DIG + 1) +#define EXPONENT_DIGITS (3 + 2) + +/** + * xmlXPathFormatNumber: + * @number: number to format + * @buffer: output buffer + * @buffersize: size of output buffer + * + * Convert the number into a string representation. + */ +static void +xmlXPathFormatNumber(double number, char buffer[], int buffersize) +{ + switch (xmlXPathIsInf(number)) { + case 1: + if (buffersize > (int)sizeof("Infinity")) + snprintf(buffer, buffersize, "Infinity"); + break; + case -1: + if (buffersize > (int)sizeof("-Infinity")) + snprintf(buffer, buffersize, "-Infinity"); + break; + default: + if (xmlXPathIsNaN(number)) { + if (buffersize > (int)sizeof("NaN")) + snprintf(buffer, buffersize, "NaN"); + } else if (number == 0 && xmlXPathGetSign(number) != 0) { + snprintf(buffer, buffersize, "0"); + } else if (number == ((int) number)) { + char work[30]; + char *ptr, *cur; + int value = (int) number; + + ptr = &buffer[0]; + if (value == 0) { + *ptr++ = '0'; + } else { + snprintf(work, 29, "%d", value); + cur = &work[0]; + while ((*cur) && (ptr - buffer < buffersize)) { + *ptr++ = *cur++; + } + } + if (ptr - buffer < buffersize) { + *ptr = 0; + } else if (buffersize > 0) { + ptr--; + *ptr = 0; + } + } else { + /* 3 is sign, decimal point, and terminating zero */ + char work[DBL_DIG + EXPONENT_DIGITS + 3]; + int integer_place, fraction_place; + char *ptr; + char *after_fraction; + double absolute_value; + int size; + + absolute_value = fabs(number); + + /* + * First choose format - scientific or regular floating point. + * In either case, result is in work, and after_fraction points + * just past the fractional part. + */ + if ( ((absolute_value > UPPER_DOUBLE) || + (absolute_value < LOWER_DOUBLE)) && + (absolute_value != 0.0) ) { + /* Use scientific notation */ + integer_place = DBL_DIG + EXPONENT_DIGITS + 1; + fraction_place = DBL_DIG - 1; + size = snprintf(work, sizeof(work),"%*.*e", + integer_place, fraction_place, number); + while ((size > 0) && (work[size] != 'e')) size--; + after_fraction = work + size; + + } + else { + /* Use regular notation */ + if (absolute_value > 0.0) + integer_place = 1 + (int)log10(absolute_value); + else + integer_place = 0; + fraction_place = (integer_place > 0) + ? DBL_DIG - integer_place + : DBL_DIG; + size = snprintf(work, sizeof(work), "%0.*f", + fraction_place, number); + after_fraction = work + size; + } + + /* Remove fractional trailing zeroes */ + ptr = after_fraction; + while (*(--ptr) == '0') + ; + if (*ptr != '.') + ptr++; + while ((*ptr++ = *after_fraction++) != 0); + + /* Finally copy result back to caller */ + size = strlen(work) + 1; + if (size > buffersize) { + work[buffersize - 1] = 0; + size = buffersize; + } + memmove(buffer, work, size); + } + break; + } +} + + +/************************************************************************ + * * + * Routines to handle NodeSets * + * * + ************************************************************************/ + +/** + * xmlXPathOrderDocElems: + * @doc: an input document + * + * Call this routine to speed up XPath computation on static documents. + * This stamps all the element nodes with the document order + * Like for line information, the order is kept in the element->content + * field, the value stored is actually - the node number (starting at -1) + * to be able to differentiate from line numbers. + * + * Returns the number of elements found in the document or -1 in case + * of error. + */ +long +xmlXPathOrderDocElems(xmlDocPtr doc) { + long count = 0; + xmlNodePtr cur; + + if (doc == NULL) + return(-1); + cur = doc->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + cur->content = (void *) (-(++count)); + if (cur->children != NULL) { + cur = cur->children; + continue; + } + } + if (cur->next != NULL) { + cur = cur->next; + continue; + } + do { + cur = cur->parent; + if (cur == NULL) + break; + if (cur == (xmlNodePtr) doc) { + cur = NULL; + break; + } + if (cur->next != NULL) { + cur = cur->next; + break; + } + } while (cur != NULL); + } + return(count); +} + +/** + * xmlXPathCmpNodes: + * @node1: the first node + * @node2: the second node + * + * Compare two nodes w.r.t document order + * + * Returns -2 in case of error 1 if first point < second point, 0 if + * it's the same node, -1 otherwise + */ +int +xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) { + int depth1, depth2; + int attr1 = 0, attr2 = 0; + xmlNodePtr attrNode1 = NULL, attrNode2 = NULL; + xmlNodePtr cur, root; + + if ((node1 == NULL) || (node2 == NULL)) + return(-2); + /* + * a couple of optimizations which will avoid computations in most cases + */ + if (node1->type == XML_ATTRIBUTE_NODE) { + attr1 = 1; + attrNode1 = node1; + node1 = node1->parent; + } + if (node2->type == XML_ATTRIBUTE_NODE) { + attr2 = 1; + attrNode2 = node2; + node2 = node2->parent; + } + if (node1 == node2) { + if (attr1 == attr2) { + /* not required, but we keep attributes in order */ + if (attr1 != 0) { + cur = attrNode2->prev; + while (cur != NULL) { + if (cur == attrNode1) + return (1); + cur = cur->prev; + } + return (-1); + } + return(0); + } + if (attr2 == 1) + return(1); + return(-1); + } + if ((node1->type == XML_NAMESPACE_DECL) || + (node2->type == XML_NAMESPACE_DECL)) + return(1); + if (node1 == node2->prev) + return(1); + if (node1 == node2->next) + return(-1); + + /* + * Speedup using document order if availble. + */ + if ((node1->type == XML_ELEMENT_NODE) && + (node2->type == XML_ELEMENT_NODE) && + (0 > (long) node1->content) && + (0 > (long) node2->content) && + (node1->doc == node2->doc)) { + long l1, l2; + + l1 = -((long) node1->content); + l2 = -((long) node2->content); + if (l1 < l2) + return(1); + if (l1 > l2) + return(-1); + } + + /* + * compute depth to root + */ + for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) { + if (cur == node1) + return(1); + depth2++; + } + root = cur; + for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) { + if (cur == node2) + return(-1); + depth1++; + } + /* + * Distinct document (or distinct entities :-( ) case. + */ + if (root != cur) { + return(-2); + } + /* + * get the nearest common ancestor. + */ + while (depth1 > depth2) { + depth1--; + node1 = node1->parent; + } + while (depth2 > depth1) { + depth2--; + node2 = node2->parent; + } + while (node1->parent != node2->parent) { + node1 = node1->parent; + node2 = node2->parent; + /* should not happen but just in case ... */ + if ((node1 == NULL) || (node2 == NULL)) + return(-2); + } + /* + * Find who's first. + */ + if (node1 == node2->prev) + return(1); + if (node1 == node2->next) + return(-1); + /* + * Speedup using document order if availble. + */ + if ((node1->type == XML_ELEMENT_NODE) && + (node2->type == XML_ELEMENT_NODE) && + (0 > (long) node1->content) && + (0 > (long) node2->content) && + (node1->doc == node2->doc)) { + long l1, l2; + + l1 = -((long) node1->content); + l2 = -((long) node2->content); + if (l1 < l2) + return(1); + if (l1 > l2) + return(-1); + } + + for (cur = node1->next;cur != NULL;cur = cur->next) + if (cur == node2) + return(1); + return(-1); /* assume there is no sibling list corruption */ +} + +#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON +/** + * xmlXPathCmpNodesExt: + * @node1: the first node + * @node2: the second node + * + * Compare two nodes w.r.t document order. + * This one is optimized for handling of non-element nodes. + * + * Returns -2 in case of error 1 if first point < second point, 0 if + * it's the same node, -1 otherwise + */ +static int +xmlXPathCmpNodesExt(xmlNodePtr node1, xmlNodePtr node2) { + int depth1, depth2; + int misc = 0, precedence1 = 0, precedence2 = 0; + xmlNodePtr miscNode1 = NULL, miscNode2 = NULL; + xmlNodePtr cur, root; + long l1, l2; + + if ((node1 == NULL) || (node2 == NULL)) + return(-2); + + if (node1 == node2) + return(0); + + /* + * a couple of optimizations which will avoid computations in most cases + */ + switch (node1->type) { + case XML_ELEMENT_NODE: + if (node2->type == XML_ELEMENT_NODE) { + if ((0 > (long) node1->content) && /* TODO: Would a != 0 suffice here? */ + (0 > (long) node2->content) && + (node1->doc == node2->doc)) + { + l1 = -((long) node1->content); + l2 = -((long) node2->content); + if (l1 < l2) + return(1); + if (l1 > l2) + return(-1); + } else + goto turtle_comparison; + } + break; + case XML_ATTRIBUTE_NODE: + precedence1 = 1; /* element is owner */ + miscNode1 = node1; + node1 = node1->parent; + misc = 1; + break; + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_COMMENT_NODE: + case XML_PI_NODE: { + miscNode1 = node1; + /* + * Find nearest element node. + */ + if (node1->prev != NULL) { + do { + node1 = node1->prev; + if (node1->type == XML_ELEMENT_NODE) { + precedence1 = 3; /* element in prev-sibl axis */ + break; + } + if (node1->prev == NULL) { + precedence1 = 2; /* element is parent */ + /* + * URGENT TODO: Are there any cases, where the + * parent of such a node is not an element node? + */ + node1 = node1->parent; + break; + } + } while (1); + } else { + precedence1 = 2; /* element is parent */ + node1 = node1->parent; + } + if ((node1 == NULL) || (node1->type != XML_ELEMENT_NODE)) { + /* + * Fallback for whatever case. + */ + node1 = miscNode1; + precedence1 = 0; + } else + misc = 1; + } + break; + case XML_NAMESPACE_DECL: + /* + * TODO: why do we return 1 for namespace nodes? + */ + return(1); + default: + break; + } + switch (node2->type) { + case XML_ELEMENT_NODE: + break; + case XML_ATTRIBUTE_NODE: + precedence2 = 1; /* element is owner */ + miscNode2 = node2; + node2 = node2->parent; + misc = 1; + break; + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_COMMENT_NODE: + case XML_PI_NODE: { + miscNode2 = node2; + if (node2->prev != NULL) { + do { + node2 = node2->prev; + if (node2->type == XML_ELEMENT_NODE) { + precedence2 = 3; /* element in prev-sibl axis */ + break; + } + if (node2->prev == NULL) { + precedence2 = 2; /* element is parent */ + node2 = node2->parent; + break; + } + } while (1); + } else { + precedence2 = 2; /* element is parent */ + node2 = node2->parent; + } + if ((node2 == NULL) || (node2->type != XML_ELEMENT_NODE) || + (0 <= (long) node1->content)) + { + node2 = miscNode2; + precedence2 = 0; + } else + misc = 1; + } + break; + case XML_NAMESPACE_DECL: + return(1); + default: + break; + } + if (misc) { + if (node1 == node2) { + if (precedence1 == precedence2) { + /* + * The ugly case; but normally there aren't many + * adjacent non-element nodes around. + */ + cur = miscNode2->prev; + while (cur != NULL) { + if (cur == miscNode1) + return(1); + if (cur->type == XML_ELEMENT_NODE) + return(-1); + cur = cur->prev; + } + return (-1); + } else { + /* + * Evaluate based on higher precedence wrt to the element. + * TODO: This assumes attributes are sorted before content. + * Is this 100% correct? + */ + if (precedence1 < precedence2) + return(1); + else + return(-1); + } + } + /* + * Special case: One of the helper-elements is contained by the other. + * + * + * Text-1(precedence1 == 2) + * + * Text-6(precedence2 == 3) + * + */ + if ((precedence2 == 3) && (precedence1 > 1)) { + cur = node1->parent; + while (cur) { + if (cur == node2) + return(1); + cur = cur->parent; + } + } + if ((precedence1 == 3) && (precedence2 > 1)) { + cur = node2->parent; + while (cur) { + if (cur == node1) + return(-1); + cur = cur->parent; + } + } + } + + /* + * Speedup using document order if availble. + */ + if ((node1->type == XML_ELEMENT_NODE) && + (node2->type == XML_ELEMENT_NODE) && + (0 > (long) node1->content) && + (0 > (long) node2->content) && + (node1->doc == node2->doc)) { + + l1 = -((long) node1->content); + l2 = -((long) node2->content); + if (l1 < l2) + return(1); + if (l1 > l2) + return(-1); + } + +turtle_comparison: + + if (node1 == node2->prev) + return(1); + if (node1 == node2->next) + return(-1); + /* + * compute depth to root + */ + for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) { + if (cur == node1) + return(1); + depth2++; + } + root = cur; + for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) { + if (cur == node2) + return(-1); + depth1++; + } + /* + * Distinct document (or distinct entities :-( ) case. + */ + if (root != cur) { + return(-2); + } + /* + * get the nearest common ancestor. + */ + while (depth1 > depth2) { + depth1--; + node1 = node1->parent; + } + while (depth2 > depth1) { + depth2--; + node2 = node2->parent; + } + while (node1->parent != node2->parent) { + node1 = node1->parent; + node2 = node2->parent; + /* should not happen but just in case ... */ + if ((node1 == NULL) || (node2 == NULL)) + return(-2); + } + /* + * Find who's first. + */ + if (node1 == node2->prev) + return(1); + if (node1 == node2->next) + return(-1); + /* + * Speedup using document order if availble. + */ + if ((node1->type == XML_ELEMENT_NODE) && + (node2->type == XML_ELEMENT_NODE) && + (0 > (long) node1->content) && + (0 > (long) node2->content) && + (node1->doc == node2->doc)) { + + l1 = -((long) node1->content); + l2 = -((long) node2->content); + if (l1 < l2) + return(1); + if (l1 > l2) + return(-1); + } + + for (cur = node1->next;cur != NULL;cur = cur->next) + if (cur == node2) + return(1); + return(-1); /* assume there is no sibling list corruption */ +} +#endif /* XP_OPTIMIZED_NON_ELEM_COMPARISON */ + +/** + * xmlXPathNodeSetSort: + * @set: the node set + * + * Sort the node set in document order + */ +void +xmlXPathNodeSetSort(xmlNodeSetPtr set) { + int i, j, incr, len; + xmlNodePtr tmp; + + if (set == NULL) + return; + + /* Use Shell's sort to sort the node-set */ + len = set->nodeNr; + for (incr = len / 2; incr > 0; incr /= 2) { + for (i = incr; i < len; i++) { + j = i - incr; + while (j >= 0) { +#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON + if (xmlXPathCmpNodesExt(set->nodeTab[j], + set->nodeTab[j + incr]) == -1) +#else + if (xmlXPathCmpNodes(set->nodeTab[j], + set->nodeTab[j + incr]) == -1) +#endif + { + tmp = set->nodeTab[j]; + set->nodeTab[j] = set->nodeTab[j + incr]; + set->nodeTab[j + incr] = tmp; + j -= incr; + } else + break; + } + } + } +} + +#define XML_NODESET_DEFAULT 10 +/** + * xmlXPathNodeSetDupNs: + * @node: the parent node of the namespace XPath node + * @ns: the libxml namespace declaration node. + * + * Namespace node in libxml don't match the XPath semantic. In a node set + * the namespace nodes are duplicated and the next pointer is set to the + * parent node in the XPath semantic. + * + * Returns the newly created object. + */ +static xmlNodePtr +xmlXPathNodeSetDupNs(xmlNodePtr node, xmlNsPtr ns) { + xmlNsPtr cur; + + if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL)) + return(NULL); + if ((node == NULL) || (node->type == XML_NAMESPACE_DECL)) + return((xmlNodePtr) ns); + + /* + * Allocate a new Namespace and fill the fields. + */ + cur = (xmlNsPtr) xmlMalloc(sizeof(xmlNs)); + if (cur == NULL) { + xmlXPathErrMemory(NULL, "duplicating namespace\n"); + return(NULL); + } + memset(cur, 0, sizeof(xmlNs)); + cur->type = XML_NAMESPACE_DECL; + if (ns->href != NULL) + cur->href = xmlStrdup(ns->href); + if (ns->prefix != NULL) + cur->prefix = xmlStrdup(ns->prefix); + cur->next = (xmlNsPtr) node; + return((xmlNodePtr) cur); +} + +/** + * xmlXPathNodeSetFreeNs: + * @ns: the XPath namespace node found in a nodeset. + * + * Namespace nodes in libxml don't match the XPath semantic. In a node set + * the namespace nodes are duplicated and the next pointer is set to the + * parent node in the XPath semantic. Check if such a node needs to be freed + */ +void +xmlXPathNodeSetFreeNs(xmlNsPtr ns) { + if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL)) + return; + + if ((ns->next != NULL) && (ns->next->type != XML_NAMESPACE_DECL)) { + if (ns->href != NULL) + xmlFree((xmlChar *)ns->href); + if (ns->prefix != NULL) + xmlFree((xmlChar *)ns->prefix); + xmlFree(ns); + } +} + +/** + * xmlXPathNodeSetCreate: + * @val: an initial xmlNodePtr, or NULL + * + * Create a new xmlNodeSetPtr of type double and of value @val + * + * Returns the newly created object. + */ +xmlNodeSetPtr +xmlXPathNodeSetCreate(xmlNodePtr val) { + xmlNodeSetPtr ret; + + ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet)); + if (ret == NULL) { + xmlXPathErrMemory(NULL, "creating nodeset\n"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlNodeSet)); + if (val != NULL) { + ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * + sizeof(xmlNodePtr)); + if (ret->nodeTab == NULL) { + xmlXPathErrMemory(NULL, "creating nodeset\n"); + xmlFree(ret); + return(NULL); + } + memset(ret->nodeTab, 0 , + XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); + ret->nodeMax = XML_NODESET_DEFAULT; + if (val->type == XML_NAMESPACE_DECL) { + xmlNsPtr ns = (xmlNsPtr) val; + + ret->nodeTab[ret->nodeNr++] = + xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); + } else + ret->nodeTab[ret->nodeNr++] = val; + } + return(ret); +} + +/** + * xmlXPathNodeSetContains: + * @cur: the node-set + * @val: the node + * + * checks whether @cur contains @val + * + * Returns true (1) if @cur contains @val, false (0) otherwise + */ +int +xmlXPathNodeSetContains (xmlNodeSetPtr cur, xmlNodePtr val) { + int i; + + if ((cur == NULL) || (val == NULL)) return(0); + if (val->type == XML_NAMESPACE_DECL) { + for (i = 0; i < cur->nodeNr; i++) { + if (cur->nodeTab[i]->type == XML_NAMESPACE_DECL) { + xmlNsPtr ns1, ns2; + + ns1 = (xmlNsPtr) val; + ns2 = (xmlNsPtr) cur->nodeTab[i]; + if (ns1 == ns2) + return(1); + if ((ns1->next != NULL) && (ns2->next == ns1->next) && + (xmlStrEqual(ns1->prefix, ns2->prefix))) + return(1); + } + } + } else { + for (i = 0; i < cur->nodeNr; i++) { + if (cur->nodeTab[i] == val) + return(1); + } + } + return(0); +} + +/** + * xmlXPathNodeSetAddNs: + * @cur: the initial node set + * @node: the hosting node + * @ns: a the namespace node + * + * add a new namespace node to an existing NodeSet + */ +void +xmlXPathNodeSetAddNs(xmlNodeSetPtr cur, xmlNodePtr node, xmlNsPtr ns) { + int i; + + + if ((cur == NULL) || (ns == NULL) || (node == NULL) || + (ns->type != XML_NAMESPACE_DECL) || + (node->type != XML_ELEMENT_NODE)) + return; + + /* @@ with_ns to check whether namespace nodes should be looked at @@ */ + /* + * prevent duplicates + */ + for (i = 0;i < cur->nodeNr;i++) { + if ((cur->nodeTab[i] != NULL) && + (cur->nodeTab[i]->type == XML_NAMESPACE_DECL) && + (((xmlNsPtr)cur->nodeTab[i])->next == (xmlNsPtr) node) && + (xmlStrEqual(ns->prefix, ((xmlNsPtr)cur->nodeTab[i])->prefix))) + return; + } + + /* + * grow the nodeTab if needed + */ + if (cur->nodeMax == 0) { + cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * + sizeof(xmlNodePtr)); + if (cur->nodeTab == NULL) { + xmlXPathErrMemory(NULL, "growing nodeset\n"); + return; + } + memset(cur->nodeTab, 0 , + XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); + cur->nodeMax = XML_NODESET_DEFAULT; + } else if (cur->nodeNr == cur->nodeMax) { + xmlNodePtr *temp; + + cur->nodeMax *= 2; + temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * + sizeof(xmlNodePtr)); + if (temp == NULL) { + xmlXPathErrMemory(NULL, "growing nodeset\n"); + return; + } + cur->nodeTab = temp; + } + cur->nodeTab[cur->nodeNr++] = xmlXPathNodeSetDupNs(node, ns); +} + +/** + * xmlXPathNodeSetAdd: + * @cur: the initial node set + * @val: a new xmlNodePtr + * + * add a new xmlNodePtr to an existing NodeSet + */ +void +xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) { + int i; + + if ((cur == NULL) || (val == NULL)) return; + +#if 0 + if ((val->type == XML_ELEMENT_NODE) && (val->name[0] == ' ')) + return; /* an XSLT fake node */ +#endif + + /* @@ with_ns to check whether namespace nodes should be looked at @@ */ + /* + * prevent duplcates + */ + for (i = 0;i < cur->nodeNr;i++) + if (cur->nodeTab[i] == val) return; + + /* + * grow the nodeTab if needed + */ + if (cur->nodeMax == 0) { + cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * + sizeof(xmlNodePtr)); + if (cur->nodeTab == NULL) { + xmlXPathErrMemory(NULL, "growing nodeset\n"); + return; + } + memset(cur->nodeTab, 0 , + XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); + cur->nodeMax = XML_NODESET_DEFAULT; + } else if (cur->nodeNr == cur->nodeMax) { + xmlNodePtr *temp; + + cur->nodeMax *= 2; + temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * + sizeof(xmlNodePtr)); + if (temp == NULL) { + xmlXPathErrMemory(NULL, "growing nodeset\n"); + return; + } + cur->nodeTab = temp; + } + if (val->type == XML_NAMESPACE_DECL) { + xmlNsPtr ns = (xmlNsPtr) val; + + cur->nodeTab[cur->nodeNr++] = + xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); + } else + cur->nodeTab[cur->nodeNr++] = val; +} + +/** + * xmlXPathNodeSetAddUnique: + * @cur: the initial node set + * @val: a new xmlNodePtr + * + * add a new xmlNodePtr to an existing NodeSet, optimized version + * when we are sure the node is not already in the set. + */ +void +xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) { + if ((cur == NULL) || (val == NULL)) return; + +#if 0 + if ((val->type == XML_ELEMENT_NODE) && (val->name[0] == ' ')) + return; /* an XSLT fake node */ +#endif + + /* @@ with_ns to check whether namespace nodes should be looked at @@ */ + /* + * grow the nodeTab if needed + */ + if (cur->nodeMax == 0) { + cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * + sizeof(xmlNodePtr)); + if (cur->nodeTab == NULL) { + xmlXPathErrMemory(NULL, "growing nodeset\n"); + return; + } + memset(cur->nodeTab, 0 , + XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); + cur->nodeMax = XML_NODESET_DEFAULT; + } else if (cur->nodeNr == cur->nodeMax) { + xmlNodePtr *temp; + + cur->nodeMax *= 2; + temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * + sizeof(xmlNodePtr)); + if (temp == NULL) { + xmlXPathErrMemory(NULL, "growing nodeset\n"); + return; + } + cur->nodeTab = temp; + } + if (val->type == XML_NAMESPACE_DECL) { + xmlNsPtr ns = (xmlNsPtr) val; + + cur->nodeTab[cur->nodeNr++] = + xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); + } else + cur->nodeTab[cur->nodeNr++] = val; +} + +/** + * xmlXPathNodeSetMerge: + * @val1: the first NodeSet or NULL + * @val2: the second NodeSet + * + * Merges two nodesets, all nodes from @val2 are added to @val1 + * if @val1 is NULL, a new set is created and copied from @val2 + * + * Returns @val1 once extended or NULL in case of error. + */ +xmlNodeSetPtr +xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) { + int i, j, initNr, skip; + xmlNodePtr n1, n2; + + if (val2 == NULL) return(val1); + if (val1 == NULL) { + val1 = xmlXPathNodeSetCreate(NULL); +#if 0 + /* + * TODO: The optimization won't work in every case, since + * those nasty namespace nodes need to be added with + * xmlXPathNodeSetDupNs() to the set; thus a pure + * memcpy is not possible. + * If there was a flag on the nodesetval, indicating that + * some temporary nodes are in, that would be helpfull. + */ + /* + * Optimization: Create an equally sized node-set + * and memcpy the content. + */ + val1 = xmlXPathNodeSetCreateSize(val2->nodeNr); + if (val1 == NULL) + return(NULL); + if (val2->nodeNr != 0) { + if (val2->nodeNr == 1) + *(val1->nodeTab) = *(val2->nodeTab); + else { + memcpy(val1->nodeTab, val2->nodeTab, + val2->nodeNr * sizeof(xmlNodePtr)); + } + val1->nodeNr = val2->nodeNr; + } + return(val1); +#endif + } + + /* @@ with_ns to check whether namespace nodes should be looked at @@ */ + initNr = val1->nodeNr; + + for (i = 0;i < val2->nodeNr;i++) { + n2 = val2->nodeTab[i]; + /* + * check against duplicates + */ + skip = 0; + for (j = 0; j < initNr; j++) { + n1 = val1->nodeTab[j]; + if (n1 == n2) { + skip = 1; + break; + } else if ((n1->type == XML_NAMESPACE_DECL) && + (n2->type == XML_NAMESPACE_DECL)) { + if ((((xmlNsPtr) n1)->next == ((xmlNsPtr) n2)->next) && + (xmlStrEqual(((xmlNsPtr) n1)->prefix, + ((xmlNsPtr) n2)->prefix))) + { + skip = 1; + break; + } + } + } + if (skip) + continue; + + /* + * grow the nodeTab if needed + */ + if (val1->nodeMax == 0) { + val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * + sizeof(xmlNodePtr)); + if (val1->nodeTab == NULL) { + xmlXPathErrMemory(NULL, "merging nodeset\n"); + return(NULL); + } + memset(val1->nodeTab, 0 , + XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); + val1->nodeMax = XML_NODESET_DEFAULT; + } else if (val1->nodeNr == val1->nodeMax) { + xmlNodePtr *temp; + + val1->nodeMax *= 2; + temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax * + sizeof(xmlNodePtr)); + if (temp == NULL) { + xmlXPathErrMemory(NULL, "merging nodeset\n"); + return(NULL); + } + val1->nodeTab = temp; + } + if (n2->type == XML_NAMESPACE_DECL) { + xmlNsPtr ns = (xmlNsPtr) n2; + + val1->nodeTab[val1->nodeNr++] = + xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); + } else + val1->nodeTab[val1->nodeNr++] = n2; + } + + return(val1); +} + +/** + * xmlXPathNodeSetMergeUnique: + * @val1: the first NodeSet or NULL + * @val2: the second NodeSet + * + * Merges two nodesets, all nodes from @val2 are added to @val1 + * if @val1 is NULL, a new set is created and copied from @val2 + * + * Returns @val1 once extended or NULL in case of error. + */ +static xmlNodeSetPtr +xmlXPathNodeSetMergeUnique(xmlNodeSetPtr val1, xmlNodeSetPtr val2) { + int i; + + if (val2 == NULL) return(val1); + if (val1 == NULL) { + val1 = xmlXPathNodeSetCreate(NULL); + } + + /* @@ with_ns to check whether namespace nodes should be looked at @@ */ + + for (i = 0;i < val2->nodeNr;i++) { + /* + * grow the nodeTab if needed + */ + if (val1->nodeMax == 0) { + val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * + sizeof(xmlNodePtr)); + if (val1->nodeTab == NULL) { + xmlXPathErrMemory(NULL, "merging nodeset\n"); + return(NULL); + } + memset(val1->nodeTab, 0 , + XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); + val1->nodeMax = XML_NODESET_DEFAULT; + } else if (val1->nodeNr == val1->nodeMax) { + xmlNodePtr *temp; + + val1->nodeMax *= 2; + temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax * + sizeof(xmlNodePtr)); + if (temp == NULL) { + xmlXPathErrMemory(NULL, "merging nodeset\n"); + return(NULL); + } + val1->nodeTab = temp; + } + if (val2->nodeTab[i]->type == XML_NAMESPACE_DECL) { + xmlNsPtr ns = (xmlNsPtr) val2->nodeTab[i]; + + val1->nodeTab[val1->nodeNr++] = + xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); + } else + val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i]; + } + + return(val1); +} + +/** + * xmlXPathNodeSetDel: + * @cur: the initial node set + * @val: an xmlNodePtr + * + * Removes an xmlNodePtr from an existing NodeSet + */ +void +xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) { + int i; + + if (cur == NULL) return; + if (val == NULL) return; + + /* + * find node in nodeTab + */ + for (i = 0;i < cur->nodeNr;i++) + if (cur->nodeTab[i] == val) break; + + if (i >= cur->nodeNr) { /* not found */ +#ifdef DEBUG + xmlGenericError(xmlGenericErrorContext, + "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n", + val->name); +#endif + return; + } + if ((cur->nodeTab[i] != NULL) && + (cur->nodeTab[i]->type == XML_NAMESPACE_DECL)) + xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[i]); + cur->nodeNr--; + for (;i < cur->nodeNr;i++) + cur->nodeTab[i] = cur->nodeTab[i + 1]; + cur->nodeTab[cur->nodeNr] = NULL; +} + +/** + * xmlXPathNodeSetRemove: + * @cur: the initial node set + * @val: the index to remove + * + * Removes an entry from an existing NodeSet list. + */ +void +xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) { + if (cur == NULL) return; + if (val >= cur->nodeNr) return; + if ((cur->nodeTab[val] != NULL) && + (cur->nodeTab[val]->type == XML_NAMESPACE_DECL)) + xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[val]); + cur->nodeNr--; + for (;val < cur->nodeNr;val++) + cur->nodeTab[val] = cur->nodeTab[val + 1]; + cur->nodeTab[cur->nodeNr] = NULL; +} + +/** + * xmlXPathFreeNodeSet: + * @obj: the xmlNodeSetPtr to free + * + * Free the NodeSet compound (not the actual nodes !). + */ +void +xmlXPathFreeNodeSet(xmlNodeSetPtr obj) { + if (obj == NULL) return; + if (obj->nodeTab != NULL) { + int i; + + /* @@ with_ns to check whether namespace nodes should be looked at @@ */ + for (i = 0;i < obj->nodeNr;i++) + if ((obj->nodeTab[i] != NULL) && + (obj->nodeTab[i]->type == XML_NAMESPACE_DECL)) + xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]); + xmlFree(obj->nodeTab); + } + xmlFree(obj); +} + +/** + * xmlXPathNodeSetClear: + * @set: the xmlNodeSetPtr to free + * + * Clears the list from all temporary XPath objects (e.g. namespace nodes + * are feed), but does *not* free the list itself. Sets the length of the + * list to 0. + */ +static void +xmlXPathNodeSetClear(xmlNodeSetPtr set) +{ + int i; + xmlNodePtr node; + + if ((set == NULL) || (set->nodeNr <= 0)) + return; + + for (i = 0; i < set->nodeNr; i++) { + node = set->nodeTab[i]; + if ((node != NULL) && + (node->type == XML_NAMESPACE_DECL)) + { + xmlXPathNodeSetFreeNs((xmlNsPtr) node); + } + } + set->nodeNr = 0; +} + +/** + * xmlXPathFreeValueTree: + * @obj: the xmlNodeSetPtr to free + * + * Free the NodeSet compound and the actual tree, this is different + * from xmlXPathFreeNodeSet() + */ +static void +xmlXPathFreeValueTree(xmlNodeSetPtr obj) { + int i; + + if (obj == NULL) return; + + if (obj->nodeTab != NULL) { + for (i = 0;i < obj->nodeNr;i++) { + if (obj->nodeTab[i] != NULL) { + if (obj->nodeTab[i]->type == XML_NAMESPACE_DECL) { + xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]); + } else { + xmlFreeNodeList(obj->nodeTab[i]); + } + } + } + xmlFree(obj->nodeTab); + } + xmlFree(obj); +} + +#if defined(DEBUG) || defined(DEBUG_STEP) +/** + * xmlGenericErrorContextNodeSet: + * @output: a FILE * for the output + * @obj: the xmlNodeSetPtr to display + * + * Quick display of a NodeSet + */ +void +xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) { + int i; + + if (output == NULL) output = xmlGenericErrorContext; + if (obj == NULL) { + fprintf(output, "NodeSet == NULL !\n"); + return; + } + if (obj->nodeNr == 0) { + fprintf(output, "NodeSet is empty\n"); + return; + } + if (obj->nodeTab == NULL) { + fprintf(output, " nodeTab == NULL !\n"); + return; + } + for (i = 0; i < obj->nodeNr; i++) { + if (obj->nodeTab[i] == NULL) { + fprintf(output, " NULL !\n"); + return; + } + if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) || + (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE)) + fprintf(output, " /"); + else if (obj->nodeTab[i]->name == NULL) + fprintf(output, " noname!"); + else fprintf(output, " %s", obj->nodeTab[i]->name); + } + fprintf(output, "\n"); +} +#endif + +/** + * xmlXPathNewNodeSet: + * @val: the NodePtr value + * + * Create a new xmlXPathObjectPtr of type NodeSet and initialize + * it with the single Node @val + * + * Returns the newly created object. + */ +xmlXPathObjectPtr +xmlXPathNewNodeSet(xmlNodePtr val) { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); + if (ret == NULL) { + xmlXPathErrMemory(NULL, "creating nodeset\n"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); + ret->type = XPATH_NODESET; + ret->boolval = 0; + ret->nodesetval = xmlXPathNodeSetCreate(val); + /* @@ with_ns to check whether namespace nodes should be looked at @@ */ +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(NULL, XPATH_NODESET); +#endif + return(ret); +} + +/** + * xmlXPathNewValueTree: + * @val: the NodePtr value + * + * Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize + * it with the tree root @val + * + * Returns the newly created object. + */ +xmlXPathObjectPtr +xmlXPathNewValueTree(xmlNodePtr val) { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); + if (ret == NULL) { + xmlXPathErrMemory(NULL, "creating result value tree\n"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); + ret->type = XPATH_XSLT_TREE; + ret->boolval = 1; + ret->user = (void *) val; + ret->nodesetval = xmlXPathNodeSetCreate(val); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(NULL, XPATH_XSLT_TREE); +#endif + return(ret); +} + +/** + * xmlXPathNewNodeSetList: + * @val: an existing NodeSet + * + * Create a new xmlXPathObjectPtr of type NodeSet and initialize + * it with the Nodeset @val + * + * Returns the newly created object. + */ +xmlXPathObjectPtr +xmlXPathNewNodeSetList(xmlNodeSetPtr val) +{ + xmlXPathObjectPtr ret; + int i; + + if (val == NULL) + ret = NULL; + else if (val->nodeTab == NULL) + ret = xmlXPathNewNodeSet(NULL); + else { + ret = xmlXPathNewNodeSet(val->nodeTab[0]); + for (i = 1; i < val->nodeNr; ++i) + xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]); + } + + return (ret); +} + +/** + * xmlXPathWrapNodeSet: + * @val: the NodePtr value + * + * Wrap the Nodeset @val in a new xmlXPathObjectPtr + * + * Returns the newly created object. + */ +xmlXPathObjectPtr +xmlXPathWrapNodeSet(xmlNodeSetPtr val) { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); + if (ret == NULL) { + xmlXPathErrMemory(NULL, "creating node set object\n"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); + ret->type = XPATH_NODESET; + ret->nodesetval = val; +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(NULL, XPATH_NODESET); +#endif + return(ret); +} + +/** + * xmlXPathFreeNodeSetList: + * @obj: an existing NodeSetList object + * + * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in + * the list contrary to xmlXPathFreeObject(). + */ +void +xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) { + if (obj == NULL) return; +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageReleased(NULL, obj->type); +#endif + xmlFree(obj); +} + +/** + * xmlXPathDifference: + * @nodes1: a node-set + * @nodes2: a node-set + * + * Implements the EXSLT - Sets difference() function: + * node-set set:difference (node-set, node-set) + * + * Returns the difference between the two node sets, or nodes1 if + * nodes2 is empty + */ +xmlNodeSetPtr +xmlXPathDifference (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { + xmlNodeSetPtr ret; + int i, l1; + xmlNodePtr cur; + + if (xmlXPathNodeSetIsEmpty(nodes2)) + return(nodes1); + + ret = xmlXPathNodeSetCreate(NULL); + if (xmlXPathNodeSetIsEmpty(nodes1)) + return(ret); + + l1 = xmlXPathNodeSetGetLength(nodes1); + + for (i = 0; i < l1; i++) { + cur = xmlXPathNodeSetItem(nodes1, i); + if (!xmlXPathNodeSetContains(nodes2, cur)) + xmlXPathNodeSetAddUnique(ret, cur); + } + return(ret); +} + +/** + * xmlXPathIntersection: + * @nodes1: a node-set + * @nodes2: a node-set + * + * Implements the EXSLT - Sets intersection() function: + * node-set set:intersection (node-set, node-set) + * + * Returns a node set comprising the nodes that are within both the + * node sets passed as arguments + */ +xmlNodeSetPtr +xmlXPathIntersection (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { + xmlNodeSetPtr ret = xmlXPathNodeSetCreate(NULL); + int i, l1; + xmlNodePtr cur; + + if (xmlXPathNodeSetIsEmpty(nodes1)) + return(ret); + if (xmlXPathNodeSetIsEmpty(nodes2)) + return(ret); + + l1 = xmlXPathNodeSetGetLength(nodes1); + + for (i = 0; i < l1; i++) { + cur = xmlXPathNodeSetItem(nodes1, i); + if (xmlXPathNodeSetContains(nodes2, cur)) + xmlXPathNodeSetAddUnique(ret, cur); + } + return(ret); +} + +/** + * xmlXPathDistinctSorted: + * @nodes: a node-set, sorted by document order + * + * Implements the EXSLT - Sets distinct() function: + * node-set set:distinct (node-set) + * + * Returns a subset of the nodes contained in @nodes, or @nodes if + * it is empty + */ +xmlNodeSetPtr +xmlXPathDistinctSorted (xmlNodeSetPtr nodes) { + xmlNodeSetPtr ret; + xmlHashTablePtr hash; + int i, l; + xmlChar * strval; + xmlNodePtr cur; + + if (xmlXPathNodeSetIsEmpty(nodes)) + return(nodes); + + ret = xmlXPathNodeSetCreate(NULL); + l = xmlXPathNodeSetGetLength(nodes); + hash = xmlHashCreate (l); + for (i = 0; i < l; i++) { + cur = xmlXPathNodeSetItem(nodes, i); + strval = xmlXPathCastNodeToString(cur); + if (xmlHashLookup(hash, strval) == NULL) { + xmlHashAddEntry(hash, strval, strval); + xmlXPathNodeSetAddUnique(ret, cur); + } else { + xmlFree(strval); + } + } + xmlHashFree(hash, (xmlHashDeallocator) xmlFree); + return(ret); +} + +/** + * xmlXPathDistinct: + * @nodes: a node-set + * + * Implements the EXSLT - Sets distinct() function: + * node-set set:distinct (node-set) + * @nodes is sorted by document order, then #exslSetsDistinctSorted + * is called with the sorted node-set + * + * Returns a subset of the nodes contained in @nodes, or @nodes if + * it is empty + */ +xmlNodeSetPtr +xmlXPathDistinct (xmlNodeSetPtr nodes) { + if (xmlXPathNodeSetIsEmpty(nodes)) + return(nodes); + + xmlXPathNodeSetSort(nodes); + return(xmlXPathDistinctSorted(nodes)); +} + +/** + * xmlXPathHasSameNodes: + * @nodes1: a node-set + * @nodes2: a node-set + * + * Implements the EXSLT - Sets has-same-nodes function: + * boolean set:has-same-node(node-set, node-set) + * + * Returns true (1) if @nodes1 shares any node with @nodes2, false (0) + * otherwise + */ +int +xmlXPathHasSameNodes (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { + int i, l; + xmlNodePtr cur; + + if (xmlXPathNodeSetIsEmpty(nodes1) || + xmlXPathNodeSetIsEmpty(nodes2)) + return(0); + + l = xmlXPathNodeSetGetLength(nodes1); + for (i = 0; i < l; i++) { + cur = xmlXPathNodeSetItem(nodes1, i); + if (xmlXPathNodeSetContains(nodes2, cur)) + return(1); + } + return(0); +} + +/** + * xmlXPathNodeLeadingSorted: + * @nodes: a node-set, sorted by document order + * @node: a node + * + * Implements the EXSLT - Sets leading() function: + * node-set set:leading (node-set, node-set) + * + * Returns the nodes in @nodes that precede @node in document order, + * @nodes if @node is NULL or an empty node-set if @nodes + * doesn't contain @node + */ +xmlNodeSetPtr +xmlXPathNodeLeadingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) { + int i, l; + xmlNodePtr cur; + xmlNodeSetPtr ret; + + if (node == NULL) + return(nodes); + + ret = xmlXPathNodeSetCreate(NULL); + if (xmlXPathNodeSetIsEmpty(nodes) || + (!xmlXPathNodeSetContains(nodes, node))) + return(ret); + + l = xmlXPathNodeSetGetLength(nodes); + for (i = 0; i < l; i++) { + cur = xmlXPathNodeSetItem(nodes, i); + if (cur == node) + break; + xmlXPathNodeSetAddUnique(ret, cur); + } + return(ret); +} + +/** + * xmlXPathNodeLeading: + * @nodes: a node-set + * @node: a node + * + * Implements the EXSLT - Sets leading() function: + * node-set set:leading (node-set, node-set) + * @nodes is sorted by document order, then #exslSetsNodeLeadingSorted + * is called. + * + * Returns the nodes in @nodes that precede @node in document order, + * @nodes if @node is NULL or an empty node-set if @nodes + * doesn't contain @node + */ +xmlNodeSetPtr +xmlXPathNodeLeading (xmlNodeSetPtr nodes, xmlNodePtr node) { + xmlXPathNodeSetSort(nodes); + return(xmlXPathNodeLeadingSorted(nodes, node)); +} + +/** + * xmlXPathLeadingSorted: + * @nodes1: a node-set, sorted by document order + * @nodes2: a node-set, sorted by document order + * + * Implements the EXSLT - Sets leading() function: + * node-set set:leading (node-set, node-set) + * + * Returns the nodes in @nodes1 that precede the first node in @nodes2 + * in document order, @nodes1 if @nodes2 is NULL or empty or + * an empty node-set if @nodes1 doesn't contain @nodes2 + */ +xmlNodeSetPtr +xmlXPathLeadingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { + if (xmlXPathNodeSetIsEmpty(nodes2)) + return(nodes1); + return(xmlXPathNodeLeadingSorted(nodes1, + xmlXPathNodeSetItem(nodes2, 1))); +} + +/** + * xmlXPathLeading: + * @nodes1: a node-set + * @nodes2: a node-set + * + * Implements the EXSLT - Sets leading() function: + * node-set set:leading (node-set, node-set) + * @nodes1 and @nodes2 are sorted by document order, then + * #exslSetsLeadingSorted is called. + * + * Returns the nodes in @nodes1 that precede the first node in @nodes2 + * in document order, @nodes1 if @nodes2 is NULL or empty or + * an empty node-set if @nodes1 doesn't contain @nodes2 + */ +xmlNodeSetPtr +xmlXPathLeading (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { + if (xmlXPathNodeSetIsEmpty(nodes2)) + return(nodes1); + if (xmlXPathNodeSetIsEmpty(nodes1)) + return(xmlXPathNodeSetCreate(NULL)); + xmlXPathNodeSetSort(nodes1); + xmlXPathNodeSetSort(nodes2); + return(xmlXPathNodeLeadingSorted(nodes1, + xmlXPathNodeSetItem(nodes2, 1))); +} + +/** + * xmlXPathNodeTrailingSorted: + * @nodes: a node-set, sorted by document order + * @node: a node + * + * Implements the EXSLT - Sets trailing() function: + * node-set set:trailing (node-set, node-set) + * + * Returns the nodes in @nodes that follow @node in document order, + * @nodes if @node is NULL or an empty node-set if @nodes + * doesn't contain @node + */ +xmlNodeSetPtr +xmlXPathNodeTrailingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) { + int i, l; + xmlNodePtr cur; + xmlNodeSetPtr ret; + + if (node == NULL) + return(nodes); + + ret = xmlXPathNodeSetCreate(NULL); + if (xmlXPathNodeSetIsEmpty(nodes) || + (!xmlXPathNodeSetContains(nodes, node))) + return(ret); + + l = xmlXPathNodeSetGetLength(nodes); + for (i = l; i > 0; i--) { + cur = xmlXPathNodeSetItem(nodes, i); + if (cur == node) + break; + xmlXPathNodeSetAddUnique(ret, cur); + } + return(ret); +} + +/** + * xmlXPathNodeTrailing: + * @nodes: a node-set + * @node: a node + * + * Implements the EXSLT - Sets trailing() function: + * node-set set:trailing (node-set, node-set) + * @nodes is sorted by document order, then #xmlXPathNodeTrailingSorted + * is called. + * + * Returns the nodes in @nodes that follow @node in document order, + * @nodes if @node is NULL or an empty node-set if @nodes + * doesn't contain @node + */ +xmlNodeSetPtr +xmlXPathNodeTrailing (xmlNodeSetPtr nodes, xmlNodePtr node) { + xmlXPathNodeSetSort(nodes); + return(xmlXPathNodeTrailingSorted(nodes, node)); +} + +/** + * xmlXPathTrailingSorted: + * @nodes1: a node-set, sorted by document order + * @nodes2: a node-set, sorted by document order + * + * Implements the EXSLT - Sets trailing() function: + * node-set set:trailing (node-set, node-set) + * + * Returns the nodes in @nodes1 that follow the first node in @nodes2 + * in document order, @nodes1 if @nodes2 is NULL or empty or + * an empty node-set if @nodes1 doesn't contain @nodes2 + */ +xmlNodeSetPtr +xmlXPathTrailingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { + if (xmlXPathNodeSetIsEmpty(nodes2)) + return(nodes1); + return(xmlXPathNodeTrailingSorted(nodes1, + xmlXPathNodeSetItem(nodes2, 0))); +} + +/** + * xmlXPathTrailing: + * @nodes1: a node-set + * @nodes2: a node-set + * + * Implements the EXSLT - Sets trailing() function: + * node-set set:trailing (node-set, node-set) + * @nodes1 and @nodes2 are sorted by document order, then + * #xmlXPathTrailingSorted is called. + * + * Returns the nodes in @nodes1 that follow the first node in @nodes2 + * in document order, @nodes1 if @nodes2 is NULL or empty or + * an empty node-set if @nodes1 doesn't contain @nodes2 + */ +xmlNodeSetPtr +xmlXPathTrailing (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { + if (xmlXPathNodeSetIsEmpty(nodes2)) + return(nodes1); + if (xmlXPathNodeSetIsEmpty(nodes1)) + return(xmlXPathNodeSetCreate(NULL)); + xmlXPathNodeSetSort(nodes1); + xmlXPathNodeSetSort(nodes2); + return(xmlXPathNodeTrailingSorted(nodes1, + xmlXPathNodeSetItem(nodes2, 0))); +} + +/************************************************************************ + * * + * Routines to handle extra functions * + * * + ************************************************************************/ + +/** + * xmlXPathRegisterFunc: + * @ctxt: the XPath context + * @name: the function name + * @f: the function implementation or NULL + * + * Register a new function. If @f is NULL it unregisters the function + * + * Returns 0 in case of success, -1 in case of error + */ +int +xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name, + xmlXPathFunction f) { + return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f)); +} + +/** + * xmlXPathRegisterFuncNS: + * @ctxt: the XPath context + * @name: the function name + * @ns_uri: the function namespace URI + * @f: the function implementation or NULL + * + * Register a new function. If @f is NULL it unregisters the function + * + * Returns 0 in case of success, -1 in case of error + */ +int +xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name, + const xmlChar *ns_uri, xmlXPathFunction f) { + if (ctxt == NULL) + return(-1); + if (name == NULL) + return(-1); + + if (ctxt->funcHash == NULL) + ctxt->funcHash = xmlHashCreate(0); + if (ctxt->funcHash == NULL) + return(-1); + if (f == NULL) + return(xmlHashRemoveEntry2(ctxt->funcHash, name, ns_uri, NULL)); + return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, XML_CAST_FPTR(f))); +} + +/** + * xmlXPathRegisterFuncLookup: + * @ctxt: the XPath context + * @f: the lookup function + * @funcCtxt: the lookup data + * + * Registers an external mechanism to do function lookup. + */ +void +xmlXPathRegisterFuncLookup (xmlXPathContextPtr ctxt, + xmlXPathFuncLookupFunc f, + void *funcCtxt) { + if (ctxt == NULL) + return; + ctxt->funcLookupFunc = f; + ctxt->funcLookupData = funcCtxt; +} + +/** + * xmlXPathFunctionLookup: + * @ctxt: the XPath context + * @name: the function name + * + * Search in the Function array of the context for the given + * function. + * + * Returns the xmlXPathFunction or NULL if not found + */ +xmlXPathFunction +xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) { + if (ctxt == NULL) + return (NULL); + + if (ctxt->funcLookupFunc != NULL) { + xmlXPathFunction ret; + xmlXPathFuncLookupFunc f; + + f = ctxt->funcLookupFunc; + ret = f(ctxt->funcLookupData, name, NULL); + if (ret != NULL) + return(ret); + } + return(xmlXPathFunctionLookupNS(ctxt, name, NULL)); +} + +/** + * xmlXPathFunctionLookupNS: + * @ctxt: the XPath context + * @name: the function name + * @ns_uri: the function namespace URI + * + * Search in the Function array of the context for the given + * function. + * + * Returns the xmlXPathFunction or NULL if not found + */ +xmlXPathFunction +xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name, + const xmlChar *ns_uri) { + xmlXPathFunction ret; + + if (ctxt == NULL) + return(NULL); + if (name == NULL) + return(NULL); + + if (ctxt->funcLookupFunc != NULL) { + xmlXPathFuncLookupFunc f; + + f = ctxt->funcLookupFunc; + ret = f(ctxt->funcLookupData, name, ns_uri); + if (ret != NULL) + return(ret); + } + + if (ctxt->funcHash == NULL) + return(NULL); + + XML_CAST_FPTR(ret) = xmlHashLookup2(ctxt->funcHash, name, ns_uri); + return(ret); +} + +/** + * xmlXPathRegisteredFuncsCleanup: + * @ctxt: the XPath context + * + * Cleanup the XPath context data associated to registered functions + */ +void +xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) { + if (ctxt == NULL) + return; + + xmlHashFree(ctxt->funcHash, NULL); + ctxt->funcHash = NULL; +} + +/************************************************************************ + * * + * Routines to handle Variables * + * * + ************************************************************************/ + +/** + * xmlXPathRegisterVariable: + * @ctxt: the XPath context + * @name: the variable name + * @value: the variable value or NULL + * + * Register a new variable value. If @value is NULL it unregisters + * the variable + * + * Returns 0 in case of success, -1 in case of error + */ +int +xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name, + xmlXPathObjectPtr value) { + return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value)); +} + +/** + * xmlXPathRegisterVariableNS: + * @ctxt: the XPath context + * @name: the variable name + * @ns_uri: the variable namespace URI + * @value: the variable value or NULL + * + * Register a new variable value. If @value is NULL it unregisters + * the variable + * + * Returns 0 in case of success, -1 in case of error + */ +int +xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name, + const xmlChar *ns_uri, + xmlXPathObjectPtr value) { + if (ctxt == NULL) + return(-1); + if (name == NULL) + return(-1); + + if (ctxt->varHash == NULL) + ctxt->varHash = xmlHashCreate(0); + if (ctxt->varHash == NULL) + return(-1); + if (value == NULL) + return(xmlHashRemoveEntry2(ctxt->varHash, name, ns_uri, + (xmlHashDeallocator)xmlXPathFreeObject)); + return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri, + (void *) value, + (xmlHashDeallocator)xmlXPathFreeObject)); +} + +/** + * xmlXPathRegisterVariableLookup: + * @ctxt: the XPath context + * @f: the lookup function + * @data: the lookup data + * + * register an external mechanism to do variable lookup + */ +void +xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt, + xmlXPathVariableLookupFunc f, void *data) { + if (ctxt == NULL) + return; + ctxt->varLookupFunc = f; + ctxt->varLookupData = data; +} + +/** + * xmlXPathVariableLookup: + * @ctxt: the XPath context + * @name: the variable name + * + * Search in the Variable array of the context for the given + * variable value. + * + * Returns a copy of the value or NULL if not found + */ +xmlXPathObjectPtr +xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) { + if (ctxt == NULL) + return(NULL); + + if (ctxt->varLookupFunc != NULL) { + xmlXPathObjectPtr ret; + + ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc) + (ctxt->varLookupData, name, NULL); + return(ret); + } + return(xmlXPathVariableLookupNS(ctxt, name, NULL)); +} + +/** + * xmlXPathVariableLookupNS: + * @ctxt: the XPath context + * @name: the variable name + * @ns_uri: the variable namespace URI + * + * Search in the Variable array of the context for the given + * variable value. + * + * Returns the a copy of the value or NULL if not found + */ +xmlXPathObjectPtr +xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name, + const xmlChar *ns_uri) { + if (ctxt == NULL) + return(NULL); + + if (ctxt->varLookupFunc != NULL) { + xmlXPathObjectPtr ret; + + ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc) + (ctxt->varLookupData, name, ns_uri); + if (ret != NULL) return(ret); + } + + if (ctxt->varHash == NULL) + return(NULL); + if (name == NULL) + return(NULL); + + return(xmlXPathCacheObjectCopy(ctxt, (xmlXPathObjectPtr) + xmlHashLookup2(ctxt->varHash, name, ns_uri))); +} + +/** + * xmlXPathRegisteredVariablesCleanup: + * @ctxt: the XPath context + * + * Cleanup the XPath context data associated to registered variables + */ +void +xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) { + if (ctxt == NULL) + return; + + xmlHashFree(ctxt->varHash, (xmlHashDeallocator)xmlXPathFreeObject); + ctxt->varHash = NULL; +} + +/** + * xmlXPathRegisterNs: + * @ctxt: the XPath context + * @prefix: the namespace prefix + * @ns_uri: the namespace name + * + * Register a new namespace. If @ns_uri is NULL it unregisters + * the namespace + * + * Returns 0 in case of success, -1 in case of error + */ +int +xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix, + const xmlChar *ns_uri) { + if (ctxt == NULL) + return(-1); + if (prefix == NULL) + return(-1); + + if (ctxt->nsHash == NULL) + ctxt->nsHash = xmlHashCreate(10); + if (ctxt->nsHash == NULL) + return(-1); + if (ns_uri == NULL) + return(xmlHashRemoveEntry(ctxt->nsHash, prefix, + (xmlHashDeallocator)xmlFree)); + return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) xmlStrdup(ns_uri), + (xmlHashDeallocator)xmlFree)); +} + +/** + * xmlXPathNsLookup: + * @ctxt: the XPath context + * @prefix: the namespace prefix value + * + * Search in the namespace declaration array of the context for the given + * namespace name associated to the given prefix + * + * Returns the value or NULL if not found + */ +const xmlChar * +xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) { + if (ctxt == NULL) + return(NULL); + if (prefix == NULL) + return(NULL); + +#ifdef XML_XML_NAMESPACE + if (xmlStrEqual(prefix, (const xmlChar *) "xml")) + return(XML_XML_NAMESPACE); +#endif + + if (ctxt->namespaces != NULL) { + int i; + + for (i = 0;i < ctxt->nsNr;i++) { + if ((ctxt->namespaces[i] != NULL) && + (xmlStrEqual(ctxt->namespaces[i]->prefix, prefix))) + return(ctxt->namespaces[i]->href); + } + } + + return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix)); +} + +/** + * xmlXPathRegisteredNsCleanup: + * @ctxt: the XPath context + * + * Cleanup the XPath context data associated to registered variables + */ +void +xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) { + if (ctxt == NULL) + return; + + xmlHashFree(ctxt->nsHash, (xmlHashDeallocator)xmlFree); + ctxt->nsHash = NULL; +} + +/************************************************************************ + * * + * Routines to handle Values * + * * + ************************************************************************/ + +/* Allocations are terrible, one needs to optimize all this !!! */ + +/** + * xmlXPathNewFloat: + * @val: the double value + * + * Create a new xmlXPathObjectPtr of type double and of value @val + * + * Returns the newly created object. + */ +xmlXPathObjectPtr +xmlXPathNewFloat(double val) { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); + if (ret == NULL) { + xmlXPathErrMemory(NULL, "creating float object\n"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); + ret->type = XPATH_NUMBER; + ret->floatval = val; +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(NULL, XPATH_NUMBER); +#endif + return(ret); +} + +/** + * xmlXPathNewBoolean: + * @val: the boolean value + * + * Create a new xmlXPathObjectPtr of type boolean and of value @val + * + * Returns the newly created object. + */ +xmlXPathObjectPtr +xmlXPathNewBoolean(int val) { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); + if (ret == NULL) { + xmlXPathErrMemory(NULL, "creating boolean object\n"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); + ret->type = XPATH_BOOLEAN; + ret->boolval = (val != 0); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(NULL, XPATH_BOOLEAN); +#endif + return(ret); +} + +/** + * xmlXPathNewString: + * @val: the xmlChar * value + * + * Create a new xmlXPathObjectPtr of type string and of value @val + * + * Returns the newly created object. + */ +xmlXPathObjectPtr +xmlXPathNewString(const xmlChar *val) { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); + if (ret == NULL) { + xmlXPathErrMemory(NULL, "creating string object\n"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); + ret->type = XPATH_STRING; + if (val != NULL) + ret->stringval = xmlStrdup(val); + else + ret->stringval = xmlStrdup((const xmlChar *)""); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING); +#endif + return(ret); +} + +/** + * xmlXPathWrapString: + * @val: the xmlChar * value + * + * Wraps the @val string into an XPath object. + * + * Returns the newly created object. + */ +xmlXPathObjectPtr +xmlXPathWrapString (xmlChar *val) { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); + if (ret == NULL) { + xmlXPathErrMemory(NULL, "creating string object\n"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); + ret->type = XPATH_STRING; + ret->stringval = val; +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING); +#endif + return(ret); +} + +/** + * xmlXPathNewCString: + * @val: the char * value + * + * Create a new xmlXPathObjectPtr of type string and of value @val + * + * Returns the newly created object. + */ +xmlXPathObjectPtr +xmlXPathNewCString(const char *val) { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); + if (ret == NULL) { + xmlXPathErrMemory(NULL, "creating string object\n"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); + ret->type = XPATH_STRING; + ret->stringval = xmlStrdup(BAD_CAST val); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING); +#endif + return(ret); +} + +/** + * xmlXPathWrapCString: + * @val: the char * value + * + * Wraps a string into an XPath object. + * + * Returns the newly created object. + */ +xmlXPathObjectPtr +xmlXPathWrapCString (char * val) { + return(xmlXPathWrapString((xmlChar *)(val))); +} + +/** + * xmlXPathWrapExternal: + * @val: the user data + * + * Wraps the @val data into an XPath object. + * + * Returns the newly created object. + */ +xmlXPathObjectPtr +xmlXPathWrapExternal (void *val) { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); + if (ret == NULL) { + xmlXPathErrMemory(NULL, "creating user object\n"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); + ret->type = XPATH_USERS; + ret->user = val; +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(NULL, XPATH_USERS); +#endif + return(ret); +} + +/** + * xmlXPathObjectCopy: + * @val: the original object + * + * allocate a new copy of a given object + * + * Returns the newly created object. + */ +xmlXPathObjectPtr +xmlXPathObjectCopy(xmlXPathObjectPtr val) { + xmlXPathObjectPtr ret; + + if (val == NULL) + return(NULL); + + ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); + if (ret == NULL) { + xmlXPathErrMemory(NULL, "copying object\n"); + return(NULL); + } + memcpy(ret, val , (size_t) sizeof(xmlXPathObject)); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageRequested(NULL, val->type); +#endif + switch (val->type) { + case XPATH_BOOLEAN: + case XPATH_NUMBER: + case XPATH_POINT: + case XPATH_RANGE: + break; + case XPATH_STRING: + ret->stringval = xmlStrdup(val->stringval); + break; + case XPATH_XSLT_TREE: +#if 0 +/* + Removed 11 July 2004 - the current handling of xslt tmpRVT nodes means that + this previous handling is no longer correct, and can cause some serious + problems (ref. bug 145547) +*/ + if ((val->nodesetval != NULL) && + (val->nodesetval->nodeTab != NULL)) { + xmlNodePtr cur, tmp; + xmlDocPtr top; + + ret->boolval = 1; + top = xmlNewDoc(NULL); + top->name = (char *) + xmlStrdup(val->nodesetval->nodeTab[0]->name); + ret->user = top; + if (top != NULL) { + top->doc = top; + cur = val->nodesetval->nodeTab[0]->children; + while (cur != NULL) { + tmp = xmlDocCopyNode(cur, top, 1); + xmlAddChild((xmlNodePtr) top, tmp); + cur = cur->next; + } + } + + ret->nodesetval = xmlXPathNodeSetCreate((xmlNodePtr) top); + } else + ret->nodesetval = xmlXPathNodeSetCreate(NULL); + /* Deallocate the copied tree value */ + break; +#endif + case XPATH_NODESET: + ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval); + /* Do not deallocate the copied tree value */ + ret->boolval = 0; + break; + case XPATH_LOCATIONSET: +#ifdef LIBXML_XPTR_ENABLED + { + xmlLocationSetPtr loc = val->user; + ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc); + break; + } +#endif + case XPATH_USERS: + ret->user = val->user; + break; + case XPATH_UNDEFINED: + xmlGenericError(xmlGenericErrorContext, + "xmlXPathObjectCopy: unsupported type %d\n", + val->type); + break; + } + return(ret); +} + +/** + * xmlXPathFreeObject: + * @obj: the object to free + * + * Free up an xmlXPathObjectPtr object. + */ +void +xmlXPathFreeObject(xmlXPathObjectPtr obj) { + if (obj == NULL) return; + if ((obj->type == XPATH_NODESET) || (obj->type == XPATH_XSLT_TREE)) { + if (obj->boolval) { +#if 0 + if (obj->user != NULL) { + xmlXPathFreeNodeSet(obj->nodesetval); + xmlFreeNodeList((xmlNodePtr) obj->user); + } else +#endif + obj->type = XPATH_XSLT_TREE; /* TODO: Just for debugging. */ + if (obj->nodesetval != NULL) + xmlXPathFreeValueTree(obj->nodesetval); + } else { + if (obj->nodesetval != NULL) + xmlXPathFreeNodeSet(obj->nodesetval); + } +#ifdef LIBXML_XPTR_ENABLED + } else if (obj->type == XPATH_LOCATIONSET) { + if (obj->user != NULL) + xmlXPtrFreeLocationSet(obj->user); +#endif + } else if (obj->type == XPATH_STRING) { + if (obj->stringval != NULL) + xmlFree(obj->stringval); + } +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageReleased(NULL, obj->type); +#endif + xmlFree(obj); +} + +/** + * xmlXPathReleaseObject: + * @obj: the xmlXPathObjectPtr to free or to cache + * + * Depending on the state of the cache this frees the given + * XPath object or stores it in the cache. + */ +static void +xmlXPathReleaseObject(xmlXPathContextPtr ctxt, xmlXPathObjectPtr obj) +{ +#define XP_CACHE_ADD(sl, o) if (sl == NULL) { \ + sl = xmlPointerListCreate(10); if (sl == NULL) goto free_obj; } \ + if (xmlPointerListAddSize(sl, obj, 0) == -1) goto free_obj; + +#define XP_CACHE_WANTS(sl, n) ((sl == NULL) || ((sl)->number < n)) + + if (obj == NULL) + return; + if ((ctxt == NULL) || (ctxt->cache == NULL)) { + xmlXPathFreeObject(obj); + } else { + xmlXPathContextCachePtr cache = + (xmlXPathContextCachePtr) ctxt->cache; + + switch (obj->type) { + case XPATH_NODESET: + case XPATH_XSLT_TREE: + if (obj->nodesetval != NULL) { + if (obj->boolval) { + /* + * It looks like the @boolval is used for + * evaluation if this an XSLT Result Tree Fragment. + * TODO: Check if this assumption is correct. + */ + obj->type = XPATH_XSLT_TREE; /* just for debugging */ + xmlXPathFreeValueTree(obj->nodesetval); + obj->nodesetval = NULL; + } else if ((obj->nodesetval->nodeMax <= 40) && + (XP_CACHE_WANTS(cache->nodesetObjs, + cache->maxNodeset))) + { + XP_CACHE_ADD(cache->nodesetObjs, obj); + goto obj_cached; + } else { + xmlXPathFreeNodeSet(obj->nodesetval); + obj->nodesetval = NULL; + } + } + break; + case XPATH_STRING: + if (obj->stringval != NULL) + xmlFree(obj->stringval); + + if (XP_CACHE_WANTS(cache->stringObjs, cache->maxString)) { + XP_CACHE_ADD(cache->stringObjs, obj); + goto obj_cached; + } + break; + case XPATH_BOOLEAN: + if (XP_CACHE_WANTS(cache->booleanObjs, cache->maxBoolean)) { + XP_CACHE_ADD(cache->booleanObjs, obj); + goto obj_cached; + } + break; + case XPATH_NUMBER: + if (XP_CACHE_WANTS(cache->numberObjs, cache->maxNumber)) { + XP_CACHE_ADD(cache->numberObjs, obj); + goto obj_cached; + } + break; +#ifdef LIBXML_XPTR_ENABLED + case XPATH_LOCATIONSET: + if (obj->user != NULL) { + xmlXPtrFreeLocationSet(obj->user); + } + goto free_obj; +#endif + default: + goto free_obj; + } + + /* + * Fallback to adding to the misc-objects slot. + */ + if (XP_CACHE_WANTS(cache->miscObjs, cache->maxMisc)) { + XP_CACHE_ADD(cache->miscObjs, obj); + } else + goto free_obj; + +obj_cached: + +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageReleased(ctxt, obj->type); +#endif + + if (obj->nodesetval != NULL) { + xmlNodeSetPtr tmpset = obj->nodesetval; + + /* + * TODO: Due to those nasty ns-nodes, we need to traverse + * the list and free the ns-nodes. + * URGENT TODO: Check if it's actually slowing things down. + * Maybe we shouldn't try to preserve the list. + */ + if (tmpset->nodeNr > 1) { + int i; + xmlNodePtr node; + + for (i = 0; i < tmpset->nodeNr; i++) { + node = tmpset->nodeTab[i]; + if ((node != NULL) && + (node->type == XML_NAMESPACE_DECL)) + { + xmlXPathNodeSetFreeNs((xmlNsPtr) node); + } + } + } else if (tmpset->nodeNr == 1) { + if ((tmpset->nodeTab[0] != NULL) && + (tmpset->nodeTab[0]->type == XML_NAMESPACE_DECL)) + xmlXPathNodeSetFreeNs((xmlNsPtr) tmpset->nodeTab[0]); + } + tmpset->nodeNr = 0; + memset(obj, 0, sizeof(xmlXPathObject)); + obj->nodesetval = tmpset; + } else + memset(obj, 0, sizeof(xmlXPathObject)); + + return; + +free_obj: + /* + * Cache is full; free the object. + */ + if (obj->nodesetval != NULL) + xmlXPathFreeNodeSet(obj->nodesetval); +#ifdef XP_DEBUG_OBJ_USAGE + xmlXPathDebugObjUsageReleased(NULL, obj->type); +#endif + xmlFree(obj); + } + return; +} + + +/************************************************************************ + * * + * Type Casting Routines * + * * + ************************************************************************/ + +/** + * xmlXPathCastBooleanToString: + * @val: a boolean + * + * Converts a boolean to its string value. + * + * Returns a newly allocated string. + */ +xmlChar * +xmlXPathCastBooleanToString (int val) { + xmlChar *ret; + if (val) + ret = xmlStrdup((const xmlChar *) "true"); + else + ret = xmlStrdup((const xmlChar *) "false"); + return(ret); +} + +/** + * xmlXPathCastNumberToString: + * @val: a number + * + * Converts a number to its string value. + * + * Returns a newly allocated string. + */ +xmlChar * +xmlXPathCastNumberToString (double val) { + xmlChar *ret; + switch (xmlXPathIsInf(val)) { + case 1: + ret = xmlStrdup((const xmlChar *) "Infinity"); + break; + case -1: + ret = xmlStrdup((const xmlChar *) "-Infinity"); + break; + default: + if (xmlXPathIsNaN(val)) { + ret = xmlStrdup((const xmlChar *) "NaN"); + } else if (val == 0 && xmlXPathGetSign(val) != 0) { + ret = xmlStrdup((const xmlChar *) "0"); + } else { + /* could be improved */ + char buf[100]; + xmlXPathFormatNumber(val, buf, 99); + buf[99] = 0; + ret = xmlStrdup((const xmlChar *) buf); + } + } + return(ret); +} + +/** + * xmlXPathCastNodeToString: + * @node: a node + * + * Converts a node to its string value. + * + * Returns a newly allocated string. + */ +xmlChar * +xmlXPathCastNodeToString (xmlNodePtr node) { + return(xmlNodeGetContent(node)); +} + +/** + * xmlXPathCastNodeSetToString: + * @ns: a node-set + * + * Converts a node-set to its string value. + * + * Returns a newly allocated string. + */ +xmlChar * +xmlXPathCastNodeSetToString (xmlNodeSetPtr ns) { + if ((ns == NULL) || (ns->nodeNr == 0) || (ns->nodeTab == NULL)) + return(xmlStrdup((const xmlChar *) "")); + + if (ns->nodeNr > 1) + xmlXPathNodeSetSort(ns); + return(xmlXPathCastNodeToString(ns->nodeTab[0])); +} + +/** + * xmlXPathCastToString: + * @val: an XPath object + * + * Converts an existing object to its string() equivalent + * + * Returns the string value of the object, NULL in case of error. + * A new string is allocated only if needed (@val isn't a + * string object). + */ +xmlChar * +xmlXPathCastToString(xmlXPathObjectPtr val) { + xmlChar *ret = NULL; + + if (val == NULL) + return(xmlStrdup((const xmlChar *) "")); + switch (val->type) { + case XPATH_UNDEFINED: +#ifdef DEBUG_EXPR + xmlGenericError(xmlGenericErrorContext, "String: undefined\n"); +#endif + ret = xmlStrdup((const xmlChar *) ""); + break; + case XPATH_NODESET: + case XPATH_XSLT_TREE: + ret = xmlXPathCastNodeSetToString(val->nodesetval); + break; + case XPATH_STRING: + return(xmlStrdup(val->stringval)); + case XPATH_BOOLEAN: + ret = xmlXPathCastBooleanToString(val->boolval); + break; + case XPATH_NUMBER: { + ret = xmlXPathCastNumberToString(val->floatval); + break; + } + case XPATH_USERS: + case XPATH_POINT: + case XPATH_RANGE: + case XPATH_LOCATIONSET: + TODO + ret = xmlStrdup((const xmlChar *) ""); + break; + } + return(ret); +} + +/** + * xmlXPathConvertString: + * @val: an XPath object + * + * Converts an existing object to its string() equivalent + * + * Returns the new object, the old one is freed (or the operation + * is done directly on @val) + */ +xmlXPathObjectPtr +xmlXPathConvertString(xmlXPathObjectPtr val) { + xmlChar *res = NULL; + + if (val == NULL) + return(xmlXPathNewCString("")); + + switch (val->type) { + case XPATH_UNDEFINED: +#ifdef DEBUG_EXPR + xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n"); +#endif + break; + case XPATH_NODESET: + case XPATH_XSLT_TREE: + res = xmlXPathCastNodeSetToString(val->nodesetval); + break; + case XPATH_STRING: + return(val); + case XPATH_BOOLEAN: + res = xmlXPathCastBooleanToString(val->boolval); + break; + case XPATH_NUMBER: + res = xmlXPathCastNumberToString(val->floatval); + break; + case XPATH_USERS: + case XPATH_POINT: + case XPATH_RANGE: + case XPATH_LOCATIONSET: + TODO; + break; + } + xmlXPathFreeObject(val); + if (res == NULL) + return(xmlXPathNewCString("")); + return(xmlXPathWrapString(res)); +} + +/** + * xmlXPathCastBooleanToNumber: + * @val: a boolean + * + * Converts a boolean to its number value + * + * Returns the number value + */ +double +xmlXPathCastBooleanToNumber(int val) { + if (val) + return(1.0); + return(0.0); +} + +/** + * xmlXPathCastStringToNumber: + * @val: a string + * + * Converts a string to its number value + * + * Returns the number value + */ +double +xmlXPathCastStringToNumber(const xmlChar * val) { + return(xmlXPathStringEvalNumber(val)); +} + +/** + * xmlXPathCastNodeToNumber: + * @node: a node + * + * Converts a node to its number value + * + * Returns the number value + */ +double +xmlXPathCastNodeToNumber (xmlNodePtr node) { + xmlChar *strval; + double ret; + + if (node == NULL) + return(xmlXPathNAN); + strval = xmlXPathCastNodeToString(node); + if (strval == NULL) + return(xmlXPathNAN); + ret = xmlXPathCastStringToNumber(strval); + xmlFree(strval); + + return(ret); +} + +/** + * xmlXPathCastNodeSetToNumber: + * @ns: a node-set + * + * Converts a node-set to its number value + * + * Returns the number value + */ +double +xmlXPathCastNodeSetToNumber (xmlNodeSetPtr ns) { + xmlChar *str; + double ret; + + if (ns == NULL) + return(xmlXPathNAN); + str = xmlXPathCastNodeSetToString(ns); + ret = xmlXPathCastStringToNumber(str); + xmlFree(str); + return(ret); +} + +/** + * xmlXPathCastToNumber: + * @val: an XPath object + * + * Converts an XPath object to its number value + * + * Returns the number value + */ +double +xmlXPathCastToNumber(xmlXPathObjectPtr val) { + double ret = 0.0; + + if (val == NULL) + return(xmlXPathNAN); + switch (val->type) { + case XPATH_UNDEFINED: +#ifdef DEGUB_EXPR + xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n"); +#endif + ret = xmlXPathNAN; + break; + case XPATH_NODESET: + case XPATH_XSLT_TREE: + ret = xmlXPathCastNodeSetToNumber(val->nodesetval); + break; + case XPATH_STRING: + ret = xmlXPathCastStringToNumber(val->stringval); + break; + case XPATH_NUMBER: + ret = val->floatval; + break; + case XPATH_BOOLEAN: + ret = xmlXPathCastBooleanToNumber(val->boolval); + break; + case XPATH_USERS: + case XPATH_POINT: + case XPATH_RANGE: + case XPATH_LOCATIONSET: + TODO; + ret = xmlXPathNAN; + break; + } + return(ret); +} + +/** + * xmlXPathConvertNumber: + * @val: an XPath object + * + * Converts an existing object to its number() equivalent + * + * Returns the new object, the old one is freed (or the operation + * is done directly on @val) + */ +xmlXPathObjectPtr +xmlXPathConvertNumber(xmlXPathObjectPtr val) { + xmlXPathObjectPtr ret; + + if (val == NULL) + return(xmlXPathNewFloat(0.0)); + if (val->type == XPATH_NUMBER) + return(val); + ret = xmlXPathNewFloat(xmlXPathCastToNumber(val)); + xmlXPathFreeObject(val); + return(ret); +} + +/** + * xmlXPathCastNumberToBoolean: + * @val: a number + * + * Converts a number to its boolean value + * + * Returns the boolean value + */ +int +xmlXPathCastNumberToBoolean (double val) { + if (xmlXPathIsNaN(val) || (val == 0.0)) + return(0); + return(1); +} + +/** + * xmlXPathCastStringToBoolean: + * @val: a string + * + * Converts a string to its boolean value + * + * Returns the boolean value + */ +int +xmlXPathCastStringToBoolean (const xmlChar *val) { + if ((val == NULL) || (xmlStrlen(val) == 0)) + return(0); + return(1); +} + +/** + * xmlXPathCastNodeSetToBoolean: + * @ns: a node-set + * + * Converts a node-set to its boolean value + * + * Returns the boolean value + */ +int +xmlXPathCastNodeSetToBoolean (xmlNodeSetPtr ns) { + if ((ns == NULL) || (ns->nodeNr == 0)) + return(0); + return(1); +} + +/** + * xmlXPathCastToBoolean: + * @val: an XPath object + * + * Converts an XPath object to its boolean value + * + * Returns the boolean value + */ +int +xmlXPathCastToBoolean (xmlXPathObjectPtr val) { + int ret = 0; + + if (val == NULL) + return(0); + switch (val->type) { + case XPATH_UNDEFINED: +#ifdef DEBUG_EXPR + xmlGenericError(xmlGenericErrorContext, "BOOLEAN: undefined\n"); +#endif + ret = 0; + break; + case XPATH_NODESET: + case XPATH_XSLT_TREE: + ret = xmlXPathCastNodeSetToBoolean(val->nodesetval); + break; + case XPATH_STRING: + ret = xmlXPathCastStringToBoolean(val->stringval); + break; + case XPATH_NUMBER: + ret = xmlXPathCastNumberToBoolean(val->floatval); + break; + case XPATH_BOOLEAN: + ret = val->boolval; + break; + case XPATH_USERS: + case XPATH_POINT: + case XPATH_RANGE: + case XPATH_LOCATIONSET: + TODO; + ret = 0; + break; + } + return(ret); +} + + +/** + * xmlXPathConvertBoolean: + * @val: an XPath object + * + * Converts an existing object to its boolean() equivalent + * + * Returns the new object, the old one is freed (or the operation + * is done directly on @val) + */ +xmlXPathObjectPtr +xmlXPathConvertBoolean(xmlXPathObjectPtr val) { + xmlXPathObjectPtr ret; + + if (val == NULL) + return(xmlXPathNewBoolean(0)); + if (val->type == XPATH_BOOLEAN) + return(val); + ret = xmlXPathNewBoolean(xmlXPathCastToBoolean(val)); + xmlXPathFreeObject(val); + return(ret); +} + +/************************************************************************ + * * + * Routines to handle XPath contexts * + * * + ************************************************************************/ + +/** + * xmlXPathNewContext: + * @doc: the XML document + * + * Create a new xmlXPathContext + * + * Returns the xmlXPathContext just allocated. The caller will need to free it. + */ +xmlXPathContextPtr +xmlXPathNewContext(xmlDocPtr doc) { + xmlXPathContextPtr ret; + + ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext)); + if (ret == NULL) { + xmlXPathErrMemory(NULL, "creating context\n"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathContext)); + ret->doc = doc; + ret->node = NULL; + + ret->varHash = NULL; + + ret->nb_types = 0; + ret->max_types = 0; + ret->types = NULL; + + ret->funcHash = xmlHashCreate(0); + + ret->nb_axis = 0; + ret->max_axis = 0; + ret->axis = NULL; + + ret->nsHash = NULL; + ret->user = NULL; + + ret->contextSize = -1; + ret->proximityPosition = -1; + +#ifdef XP_DEFAULT_CACHE_ON + if (xmlXPathContextSetCache(ret, 1, -1, 0) == -1) { + xmlXPathFreeContext(ret); + return(NULL); + } +#endif + + xmlXPathRegisterAllFunctions(ret); + + return(ret); +} + +/** + * xmlXPathFreeContext: + * @ctxt: the context to free + * + * Free up an xmlXPathContext + */ +void +xmlXPathFreeContext(xmlXPathContextPtr ctxt) { + if (ctxt == NULL) return; + + if (ctxt->cache != NULL) + xmlXPathFreeCache((xmlXPathContextCachePtr) ctxt->cache); + xmlXPathRegisteredNsCleanup(ctxt); + xmlXPathRegisteredFuncsCleanup(ctxt); + xmlXPathRegisteredVariablesCleanup(ctxt); + xmlResetError(&ctxt->lastError); + xmlFree(ctxt); +} + +/************************************************************************ + * * + * Routines to handle XPath parser contexts * + * * + ************************************************************************/ + +#define CHECK_CTXT(ctxt) \ + if (ctxt == NULL) { \ + __xmlRaiseError(NULL, NULL, NULL, \ + NULL, NULL, XML_FROM_XPATH, \ + XML_ERR_INTERNAL_ERROR, XML_ERR_FATAL, \ + __FILE__, __LINE__, \ + NULL, NULL, NULL, 0, 0, \ + "NULL context pointer\n"); \ + return(NULL); \ + } \ + + +#define CHECK_CONTEXT(ctxt) \ + if ((ctxt == NULL) || (ctxt->doc == NULL) || \ + (ctxt->doc->children == NULL)) { \ + xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_INVALID_CTXT); \ + return(NULL); \ + } + + +/** + * xmlXPathNewParserContext: + * @str: the XPath expression + * @ctxt: the XPath context + * + * Create a new xmlXPathParserContext + * + * Returns the xmlXPathParserContext just allocated. + */ +xmlXPathParserContextPtr +xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) { + xmlXPathParserContextPtr ret; + + ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext)); + if (ret == NULL) { + xmlXPathErrMemory(ctxt, "creating parser context\n"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext)); + ret->cur = ret->base = str; + ret->context = ctxt; + + ret->comp = xmlXPathNewCompExpr(); + if (ret->comp == NULL) { + xmlFree(ret->valueTab); + xmlFree(ret); + return(NULL); + } + if ((ctxt != NULL) && (ctxt->dict != NULL)) { + ret->comp->dict = ctxt->dict; + xmlDictReference(ret->comp->dict); + } + + return(ret); +} + +/** + * xmlXPathCompParserContext: + * @comp: the XPath compiled expression + * @ctxt: the XPath context + * + * Create a new xmlXPathParserContext when processing a compiled expression + * + * Returns the xmlXPathParserContext just allocated. + */ +static xmlXPathParserContextPtr +xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) { + xmlXPathParserContextPtr ret; + + ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext)); + if (ret == NULL) { + xmlXPathErrMemory(ctxt, "creating evaluation context\n"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext)); + + /* Allocate the value stack */ + ret->valueTab = (xmlXPathObjectPtr *) + xmlMalloc(10 * sizeof(xmlXPathObjectPtr)); + if (ret->valueTab == NULL) { + xmlFree(ret); + xmlXPathErrMemory(ctxt, "creating evaluation context\n"); + return(NULL); + } + ret->valueNr = 0; + ret->valueMax = 10; + ret->value = NULL; + + ret->context = ctxt; + ret->comp = comp; + + return(ret); +} + +/** + * xmlXPathFreeParserContext: + * @ctxt: the context to free + * + * Free up an xmlXPathParserContext + */ +void +xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) { + if (ctxt->valueTab != NULL) { + xmlFree(ctxt->valueTab); + } + if (ctxt->comp != NULL) { +#ifdef XPATH_STREAMING + if (ctxt->comp->stream != NULL) { + xmlFreePatternList(ctxt->comp->stream); + ctxt->comp->stream = NULL; + } +#endif + xmlXPathFreeCompExpr(ctxt->comp); + } + xmlFree(ctxt); +} + +/************************************************************************ + * * + * The implicit core function library * + * * + ************************************************************************/ + +/** + * xmlXPathNodeValHash: + * @node: a node pointer + * + * Function computing the beginning of the string value of the node, + * used to speed up comparisons + * + * Returns an int usable as a hash + */ +static unsigned int +xmlXPathNodeValHash(xmlNodePtr node) { + int len = 2; + const xmlChar * string = NULL; + xmlNodePtr tmp = NULL; + unsigned int ret = 0; + + if (node == NULL) + return(0); + + if (node->type == XML_DOCUMENT_NODE) { + tmp = xmlDocGetRootElement((xmlDocPtr) node); + if (tmp == NULL) + node = node->children; + else + node = tmp; + + if (node == NULL) + return(0); + } + + switch (node->type) { + case XML_COMMENT_NODE: + case XML_PI_NODE: + case XML_CDATA_SECTION_NODE: + case XML_TEXT_NODE: + string = node->content; + if (string == NULL) + return(0); + if (string[0] == 0) + return(0); + return(((unsigned int) string[0]) + + (((unsigned int) string[1]) << 8)); + case XML_NAMESPACE_DECL: + string = ((xmlNsPtr)node)->href; + if (string == NULL) + return(0); + if (string[0] == 0) + return(0); + return(((unsigned int) string[0]) + + (((unsigned int) string[1]) << 8)); + case XML_ATTRIBUTE_NODE: + tmp = ((xmlAttrPtr) node)->children; + break; + case XML_ELEMENT_NODE: + tmp = node->children; + break; + default: + return(0); + } + while (tmp != NULL) { + switch (tmp->type) { + case XML_COMMENT_NODE: + case XML_PI_NODE: + case XML_CDATA_SECTION_NODE: + case XML_TEXT_NODE: + string = tmp->content; + break; + case XML_NAMESPACE_DECL: + string = ((xmlNsPtr)tmp)->href; + break; + default: + break; + } + if ((string != NULL) && (string[0] != 0)) { + if (len == 1) { + return(ret + (((unsigned int) string[0]) << 8)); + } + if (string[1] == 0) { + len = 1; + ret = (unsigned int) string[0]; + } else { + return(((unsigned int) string[0]) + + (((unsigned int) string[1]) << 8)); + } + } + /* + * Skip to next node + */ + if ((tmp->children != NULL) && (tmp->type != XML_DTD_NODE)) { + if (tmp->children->type != XML_ENTITY_DECL) { + tmp = tmp->children; + continue; + } + } + if (tmp == node) + break; + + if (tmp->next != NULL) { + tmp = tmp->next; + continue; + } + + do { + tmp = tmp->parent; + if (tmp == NULL) + break; + if (tmp == node) { + tmp = NULL; + break; + } + if (tmp->next != NULL) { + tmp = tmp->next; + break; + } + } while (tmp != NULL); + } + return(ret); +} + +/** + * xmlXPathStringHash: + * @string: a string + * + * Function computing the beginning of the string value of the node, + * used to speed up comparisons + * + * Returns an int usable as a hash + */ +static unsigned int +xmlXPathStringHash(const xmlChar * string) { + if (string == NULL) + return((unsigned int) 0); + if (string[0] == 0) + return(0); + return(((unsigned int) string[0]) + + (((unsigned int) string[1]) << 8)); +} + +/** + * xmlXPathCompareNodeSetFloat: + * @ctxt: the XPath Parser context + * @inf: less than (1) or greater than (0) + * @strict: is the comparison strict + * @arg: the node set + * @f: the value + * + * Implement the compare operation between a nodeset and a number + * @ns < @val (1, 1, ... + * @ns <= @val (1, 0, ... + * @ns > @val (0, 1, ... + * @ns >= @val (0, 0, ... + * + * If one object to be compared is a node-set and the other is a number, + * then the comparison will be true if and only if there is a node in the + * node-set such that the result of performing the comparison on the number + * to be compared and on the result of converting the string-value of that + * node to a number using the number function is true. + * + * Returns 0 or 1 depending on the results of the test. + */ +static int +xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict, + xmlXPathObjectPtr arg, xmlXPathObjectPtr f) { + int i, ret = 0; + xmlNodeSetPtr ns; + xmlChar *str2; + + if ((f == NULL) || (arg == NULL) || + ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) { + xmlXPathReleaseObject(ctxt->context, arg); + xmlXPathReleaseObject(ctxt->context, f); + return(0); + } + ns = arg->nodesetval; + if (ns != NULL) { + for (i = 0;i < ns->nodeNr;i++) { + str2 = xmlXPathCastNodeToString(ns->nodeTab[i]); + if (str2 != NULL) { + valuePush(ctxt, + xmlXPathCacheNewString(ctxt->context, str2)); + xmlFree(str2); + xmlXPathNumberFunction(ctxt, 1); + valuePush(ctxt, xmlXPathCacheObjectCopy(ctxt->context, f)); + ret = xmlXPathCompareValues(ctxt, inf, strict); + if (ret) + break; + } + } + } + xmlXPathReleaseObject(ctxt->context, arg); + xmlXPathReleaseObject(ctxt->context, f); + return(ret); +} + +/** + * xmlXPathCompareNodeSetString: + * @ctxt: the XPath Parser context + * @inf: less than (1) or greater than (0) + * @strict: is the comparison strict + * @arg: the node set + * @s: the value + * + * Implement the compare operation between a nodeset and a string + * @ns < @val (1, 1, ... + * @ns <= @val (1, 0, ... + * @ns > @val (0, 1, ... + * @ns >= @val (0, 0, ... + * + * If one object to be compared is a node-set and the other is a string, + * then the comparison will be true if and only if there is a node in + * the node-set such that the result of performing the comparison on the + * string-value of the node and the other string is true. + * + * Returns 0 or 1 depending on the results of the test. + */ +static int +xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict, + xmlXPathObjectPtr arg, xmlXPathObjectPtr s) { + int i, ret = 0; + xmlNodeSetPtr ns; + xmlChar *str2; + + if ((s == NULL) || (arg == NULL) || + ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) { + xmlXPathReleaseObject(ctxt->context, arg); + xmlXPathReleaseObject(ctxt->context, s); + return(0); + } + ns = arg->nodesetval; + if (ns != NULL) { + for (i = 0;i < ns->nodeNr;i++) { + str2 = xmlXPathCastNodeToString(ns->nodeTab[i]); + if (str2 != NULL) { + valuePush(ctxt, + xmlXPathCacheNewString(ctxt->context, str2)); + xmlFree(str2); + valuePush(ctxt, xmlXPathCacheObjectCopy(ctxt->context, s)); + ret = xmlXPathCompareValues(ctxt, inf, strict); + if (ret) + break; + } + } + } + xmlXPathReleaseObject(ctxt->context, arg); + xmlXPathReleaseObject(ctxt->context, s); + return(ret); +} + +/** + * xmlXPathCompareNodeSets: + * @inf: less than (1) or greater than (0) + * @strict: is the comparison strict + * @arg1: the first node set object + * @arg2: the second node set object + * + * Implement the compare operation on nodesets: + * + * If both objects to be compared are node-sets, then the comparison + * will be true if and only if there is a node in the first node-set + * and a node in the second node-set such that the result of performing + * the comparison on the string-values of the two nodes is true. + * .... + * When neither object to be compared is a node-set and the operator + * is <=, <, >= or >, then the objects are compared by converting both + * objects to numbers and comparing the numbers according to IEEE 754. + * .... + * The number function converts its argument to a number as follows: + * - a string that consists of optional whitespace followed by an + * optional minus sign followed by a Number followed by whitespace + * is converted to the IEEE 754 number that is nearest (according + * to the IEEE 754 round-to-nearest rule) to the mathematical value + * represented by the string; any other string is converted to NaN + * + * Conclusion all nodes need to be converted first to their string value + * and then the comparison must be done when possible + */ +static int +xmlXPathCompareNodeSets(int inf, int strict, + xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) { + int i, j, init = 0; + double val1; + double *values2; + int ret = 0; + xmlNodeSetPtr ns1; + xmlNodeSetPtr ns2; + + if ((arg1 == NULL) || + ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) { + xmlXPathFreeObject(arg2); + return(0); + } + if ((arg2 == NULL) || + ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) { + xmlXPathFreeObject(arg1); + xmlXPathFreeObject(arg2); + return(0); + } + + ns1 = arg1->nodesetval; + ns2 = arg2->nodesetval; + + if ((ns1 == NULL) || (ns1->nodeNr <= 0)) { + xmlXPathFreeObject(arg1); + xmlXPathFreeObject(arg2); + return(0); + } + if ((ns2 == NULL) || (ns2->nodeNr <= 0)) { + xmlXPathFreeObject(arg1); + xmlXPathFreeObject(arg2); + return(0); + } + + values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double)); + if (values2 == NULL) { + xmlXPathErrMemory(NULL, "comparing nodesets\n"); + xmlXPathFreeObject(arg1); + xmlXPathFreeObject(arg2); + return(0); + } + for (i = 0;i < ns1->nodeNr;i++) { + val1 = xmlXPathCastNodeToNumber(ns1->nodeTab[i]); + if (xmlXPathIsNaN(val1)) + continue; + for (j = 0;j < ns2->nodeNr;j++) { + if (init == 0) { + values2[j] = xmlXPathCastNodeToNumber(ns2->nodeTab[j]); + } + if (xmlXPathIsNaN(values2[j])) + continue; + if (inf && strict) + ret = (val1 < values2[j]); + else if (inf && !strict) + ret = (val1 <= values2[j]); + else if (!inf && strict) + ret = (val1 > values2[j]); + else if (!inf && !strict) + ret = (val1 >= values2[j]); + if (ret) + break; + } + if (ret) + break; + init = 1; + } + xmlFree(values2); + xmlXPathFreeObject(arg1); + xmlXPathFreeObject(arg2); + return(ret); +} + +/** + * xmlXPathCompareNodeSetValue: + * @ctxt: the XPath Parser context + * @inf: less than (1) or greater than (0) + * @strict: is the comparison strict + * @arg: the node set + * @val: the value + * + * Implement the compare operation between a nodeset and a value + * @ns < @val (1, 1, ... + * @ns <= @val (1, 0, ... + * @ns > @val (0, 1, ... + * @ns >= @val (0, 0, ... + * + * If one object to be compared is a node-set and the other is a boolean, + * then the comparison will be true if and only if the result of performing + * the comparison on the boolean and on the result of converting + * the node-set to a boolean using the boolean function is true. + * + * Returns 0 or 1 depending on the results of the test. + */ +static int +xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict, + xmlXPathObjectPtr arg, xmlXPathObjectPtr val) { + if ((val == NULL) || (arg == NULL) || + ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) + return(0); + + switch(val->type) { + case XPATH_NUMBER: + return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val)); + case XPATH_NODESET: + case XPATH_XSLT_TREE: + return(xmlXPathCompareNodeSets(inf, strict, arg, val)); + case XPATH_STRING: + return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val)); + case XPATH_BOOLEAN: + valuePush(ctxt, arg); + xmlXPathBooleanFunction(ctxt, 1); + valuePush(ctxt, val); + return(xmlXPathCompareValues(ctxt, inf, strict)); + default: + TODO + } + return(0); +} + +/** + * xmlXPathEqualNodeSetString: + * @arg: the nodeset object argument + * @str: the string to compare to. + * @neq: flag to show whether for '=' (0) or '!=' (1) + * + * Implement the equal operation on XPath objects content: @arg1 == @arg2 + * If one object to be compared is a node-set and the other is a string, + * then the comparison will be true if and only if there is a node in + * the node-set such that the result of performing the comparison on the + * string-value of the node and the other string is true. + * + * Returns 0 or 1 depending on the results of the test. + */ +static int +xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar * str, int neq) +{ + int i; + xmlNodeSetPtr ns; + xmlChar *str2; + unsigned int hash; + + if ((str == NULL) || (arg == NULL) || + ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) + return (0); + ns = arg->nodesetval; + /* + * A NULL nodeset compared with a string is always false + * (since there is no node equal, and no node not equal) + */ + if ((ns == NULL) || (ns->nodeNr <= 0) ) + return (0); + hash = xmlXPathStringHash(str); + for (i = 0; i < ns->nodeNr; i++) { + if (xmlXPathNodeValHash(ns->nodeTab[i]) == hash) { + str2 = xmlNodeGetContent(ns->nodeTab[i]); + if ((str2 != NULL) && (xmlStrEqual(str, str2))) { + xmlFree(str2); + if (neq) + continue; + return (1); + } else if ((str2 == NULL) && (xmlStrEqual(str, BAD_CAST ""))) { + if (neq) + continue; + return (1); + } else if (neq) { + if (str2 != NULL) + xmlFree(str2); + return (1); + } + if (str2 != NULL) + xmlFree(str2); + } else if (neq) + return (1); + } + return (0); +} + +/** + * xmlXPathEqualNodeSetFloat: + * @arg: the nodeset object argument + * @f: the float to compare to + * @neq: flag to show whether to compare '=' (0) or '!=' (1) + * + * Implement the equal operation on XPath objects content: @arg1 == @arg2 + * If one object to be compared is a node-set and the other is a number, + * then the comparison will be true if and only if there is a node in + * the node-set such that the result of performing the comparison on the + * number to be compared and on the result of converting the string-value + * of that node to a number using the number function is true. + * + * Returns 0 or 1 depending on the results of the test. + */ +static int +xmlXPathEqualNodeSetFloat(xmlXPathParserContextPtr ctxt, + xmlXPathObjectPtr arg, double f, int neq) { + int i, ret=0; + xmlNodeSetPtr ns; + xmlChar *str2; + xmlXPathObjectPtr val; + double v; + + if ((arg == NULL) || + ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) + return(0); + + ns = arg->nodesetval; + if (ns != NULL) { + for (i=0;inodeNr;i++) { + str2 = xmlXPathCastNodeToString(ns->nodeTab[i]); + if (str2 != NULL) { + valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, str2)); + xmlFree(str2); + xmlXPathNumberFunction(ctxt, 1); + val = valuePop(ctxt); + v = val->floatval; + xmlXPathReleaseObject(ctxt->context, val); + if (!xmlXPathIsNaN(v)) { + if ((!neq) && (v==f)) { + ret = 1; + break; + } else if ((neq) && (v!=f)) { + ret = 1; + break; + } + } else { /* NaN is unequal to any value */ + if (neq) + ret = 1; + } + } + } + } + + return(ret); +} + + +/** + * xmlXPathEqualNodeSets: + * @arg1: first nodeset object argument + * @arg2: second nodeset object argument + * @neq: flag to show whether to test '=' (0) or '!=' (1) + * + * Implement the equal / not equal operation on XPath nodesets: + * @arg1 == @arg2 or @arg1 != @arg2 + * If both objects to be compared are node-sets, then the comparison + * will be true if and only if there is a node in the first node-set and + * a node in the second node-set such that the result of performing the + * comparison on the string-values of the two nodes is true. + * + * (needless to say, this is a costly operation) + * + * Returns 0 or 1 depending on the results of the test. + */ +static int +xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2, int neq) { + int i, j; + unsigned int *hashs1; + unsigned int *hashs2; + xmlChar **values1; + xmlChar **values2; + int ret = 0; + xmlNodeSetPtr ns1; + xmlNodeSetPtr ns2; + + if ((arg1 == NULL) || + ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) + return(0); + if ((arg2 == NULL) || + ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) + return(0); + + ns1 = arg1->nodesetval; + ns2 = arg2->nodesetval; + + if ((ns1 == NULL) || (ns1->nodeNr <= 0)) + return(0); + if ((ns2 == NULL) || (ns2->nodeNr <= 0)) + return(0); + + /* + * for equal, check if there is a node pertaining to both sets + */ + if (neq == 0) + for (i = 0;i < ns1->nodeNr;i++) + for (j = 0;j < ns2->nodeNr;j++) + if (ns1->nodeTab[i] == ns2->nodeTab[j]) + return(1); + + values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *)); + if (values1 == NULL) { + xmlXPathErrMemory(NULL, "comparing nodesets\n"); + return(0); + } + hashs1 = (unsigned int *) xmlMalloc(ns1->nodeNr * sizeof(unsigned int)); + if (hashs1 == NULL) { + xmlXPathErrMemory(NULL, "comparing nodesets\n"); + xmlFree(values1); + return(0); + } + memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *)); + values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *)); + if (values2 == NULL) { + xmlXPathErrMemory(NULL, "comparing nodesets\n"); + xmlFree(hashs1); + xmlFree(values1); + return(0); + } + hashs2 = (unsigned int *) xmlMalloc(ns2->nodeNr * sizeof(unsigned int)); + if (hashs2 == NULL) { + xmlXPathErrMemory(NULL, "comparing nodesets\n"); + xmlFree(hashs1); + xmlFree(values1); + xmlFree(values2); + return(0); + } + memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *)); + for (i = 0;i < ns1->nodeNr;i++) { + hashs1[i] = xmlXPathNodeValHash(ns1->nodeTab[i]); + for (j = 0;j < ns2->nodeNr;j++) { + if (i == 0) + hashs2[j] = xmlXPathNodeValHash(ns2->nodeTab[j]); + if (hashs1[i] != hashs2[j]) { + if (neq) { + ret = 1; + break; + } + } + else { + if (values1[i] == NULL) + values1[i] = xmlNodeGetContent(ns1->nodeTab[i]); + if (values2[j] == NULL) + values2[j] = xmlNodeGetContent(ns2->nodeTab[j]); + ret = xmlStrEqual(values1[i], values2[j]) ^ neq; + if (ret) + break; + } + } + if (ret) + break; + } + for (i = 0;i < ns1->nodeNr;i++) + if (values1[i] != NULL) + xmlFree(values1[i]); + for (j = 0;j < ns2->nodeNr;j++) + if (values2[j] != NULL) + xmlFree(values2[j]); + xmlFree(values1); + xmlFree(values2); + xmlFree(hashs1); + xmlFree(hashs2); + return(ret); +} + +static int +xmlXPathEqualValuesCommon(xmlXPathParserContextPtr ctxt, + xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) { + int ret = 0; + /* + *At this point we are assured neither arg1 nor arg2 + *is a nodeset, so we can just pick the appropriate routine. + */ + switch (arg1->type) { + case XPATH_UNDEFINED: +#ifdef DEBUG_EXPR + xmlGenericError(xmlGenericErrorContext, + "Equal: undefined\n"); +#endif + break; + case XPATH_BOOLEAN: + switch (arg2->type) { + case XPATH_UNDEFINED: +#ifdef DEBUG_EXPR + xmlGenericError(xmlGenericErrorContext, + "Equal: undefined\n"); +#endif + break; + case XPATH_BOOLEAN: +#ifdef DEBUG_EXPR + xmlGenericError(xmlGenericErrorContext, + "Equal: %d boolean %d \n", + arg1->boolval, arg2->boolval); +#endif + ret = (arg1->boolval == arg2->boolval); + break; + case XPATH_NUMBER: + ret = (arg1->boolval == + xmlXPathCastNumberToBoolean(arg2->floatval)); + break; + case XPATH_STRING: + if ((arg2->stringval == NULL) || + (arg2->stringval[0] == 0)) ret = 0; + else + ret = 1; + ret = (arg1->boolval == ret); + break; + case XPATH_USERS: + case XPATH_POINT: + case XPATH_RANGE: + case XPATH_LOCATIONSET: + TODO + break; + case XPATH_NODESET: + case XPATH_XSLT_TREE: + break; + } + break; + case XPATH_NUMBER: + switch (arg2->type) { + case XPATH_UNDEFINED: +#ifdef DEBUG_EXPR + xmlGenericError(xmlGenericErrorContext, + "Equal: undefined\n"); +#endif + break; + case XPATH_BOOLEAN: + ret = (arg2->boolval== + xmlXPathCastNumberToBoolean(arg1->floatval)); + break; + case XPATH_STRING: + valuePush(ctxt, arg2); + xmlXPathNumberFunction(ctxt, 1); + arg2 = valuePop(ctxt); + /* no break on purpose */ + case XPATH_NUMBER: + /* Hand check NaN and Infinity equalities */ + if (xmlXPathIsNaN(arg1->floatval) || + xmlXPathIsNaN(arg2->floatval)) { + ret = 0; + } else if (xmlXPathIsInf(arg1->floatval) == 1) { + if (xmlXPathIsInf(arg2->floatval) == 1) + ret = 1; + else + ret = 0; + } else if (xmlXPathIsInf(arg1->floatval) == -1) { + if (xmlXPathIsInf(arg2->floatval) == -1) + ret = 1; + else + ret = 0; + } else if (xmlXPathIsInf(arg2->floatval) == 1) { + if (xmlXPathIsInf(arg1->floatval) == 1) + ret = 1; + else + ret = 0; + } else if (xmlXPathIsInf(arg2->floatval) == -1) { + if (xmlXPathIsInf(arg1->floatval) == -1) + ret = 1; + else + ret = 0; + } else { + ret = (arg1->floatval == arg2->floatval); + } + break; + case XPATH_USERS: + case XPATH_POINT: + case XPATH_RANGE: + case XPATH_LOCATIONSET: + TODO + break; + case XPATH_NODESET: + case XPATH_XSLT_TREE: + break; + } + break; + case XPATH_STRING: + switch (arg2->type) { + case XPATH_UNDEFINED: +#ifdef DEBUG_EXPR + xmlGenericError(xmlGenericErrorContext, + "Equal: undefined\n"); +#endif + break; + case XPATH_BOOLEAN: + if ((arg1->stringval == NULL) || + (arg1->stringval[0] == 0)) ret = 0; + else + ret = 1; + ret = (arg2->boolval == ret); + break; + case XPATH_STRING: + ret = xmlStrEqual(arg1->stringval, arg2->stringval); + break; + case XPATH_NUMBER: + valuePush(ctxt, arg1); + xmlXPathNumberFunction(ctxt, 1); + arg1 = valuePop(ctxt); + /* Hand check NaN and Infinity equalities */ + if (xmlXPathIsNaN(arg1->floatval) || + xmlXPathIsNaN(arg2->floatval)) { + ret = 0; + } else if (xmlXPathIsInf(arg1->floatval) == 1) { + if (xmlXPathIsInf(arg2->floatval) == 1) + ret = 1; + else + ret = 0; + } else if (xmlXPathIsInf(arg1->floatval) == -1) { + if (xmlXPathIsInf(arg2->floatval) == -1) + ret = 1; + else + ret = 0; + } else if (xmlXPathIsInf(arg2->floatval) == 1) { + if (xmlXPathIsInf(arg1->floatval) == 1) + ret = 1; + else + ret = 0; + } else if (xmlXPathIsInf(arg2->floatval) == -1) { + if (xmlXPathIsInf(arg1->floatval) == -1) + ret = 1; + else + ret = 0; + } else { + ret = (arg1->floatval == arg2->floatval); + } + break; + case XPATH_USERS: + case XPATH_POINT: + case XPATH_RANGE: + case XPATH_LOCATIONSET: + TODO + break; + case XPATH_NODESET: + case XPATH_XSLT_TREE: + break; + } + break; + case XPATH_USERS: + case XPATH_POINT: + case XPATH_RANGE: + case XPATH_LOCATIONSET: + TODO + break; + case XPATH_NODESET: + case XPATH_XSLT_TREE: + break; + } + xmlXPathReleaseObject(ctxt->context, arg1); + xmlXPathReleaseObject(ctxt->context, arg2); + return(ret); +} + +/** + * xmlXPathEqualValues: + * @ctxt: the XPath Parser context + * + * Implement the equal operation on XPath objects content: @arg1 == @arg2 + * + * Returns 0 or 1 depending on the results of the test. + */ +int +xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) { + xmlXPathObjectPtr arg1, arg2, argtmp; + int ret = 0; + + if ((ctxt == NULL) || (ctxt->context == NULL)) return(0); + arg2 = valuePop(ctxt); + arg1 = valuePop(ctxt); + if ((arg1 == NULL) || (arg2 == NULL)) { + if (arg1 != NULL) + xmlXPathReleaseObject(ctxt->context, arg1); + else + xmlXPathReleaseObject(ctxt->context, arg2); + XP_ERROR0(XPATH_INVALID_OPERAND); + } + + if (arg1 == arg2) { +#ifdef DEBUG_EXPR + xmlGenericError(xmlGenericErrorContext, + "Equal: by pointer\n"); +#endif + xmlXPathFreeObject(arg1); + return(1); + } + + /* + *If either argument is a nodeset, it's a 'special case' + */ + if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) || + (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) { + /* + *Hack it to assure arg1 is the nodeset + */ + if ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)) { + argtmp = arg2; + arg2 = arg1; + arg1 = argtmp; + } + switch (arg2->type) { + case XPATH_UNDEFINED: +#ifdef DEBUG_EXPR + xmlGenericError(xmlGenericErrorContext, + "Equal: undefined\n"); +#endif + break; + case XPATH_NODESET: + case XPATH_XSLT_TREE: + ret = xmlXPathEqualNodeSets(arg1, arg2, 0); + break; + case XPATH_BOOLEAN: + if ((arg1->nodesetval == NULL) || + (arg1->nodesetval->nodeNr == 0)) ret = 0; + else + ret = 1; + ret = (ret == arg2->boolval); + break; + case XPATH_NUMBER: + ret = xmlXPathEqualNodeSetFloat(ctxt, arg1, arg2->floatval, 0); + break; + case XPATH_STRING: + ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval, 0); + break; + case XPATH_USERS: + case XPATH_POINT: + case XPATH_RANGE: + case XPATH_LOCATIONSET: + TODO + break; + } + xmlXPathReleaseObject(ctxt->context, arg1); + xmlXPathReleaseObject(ctxt->context, arg2); + return(ret); + } + + return (xmlXPathEqualValuesCommon(ctxt, arg1, arg2)); +} + +/** + * xmlXPathNotEqualValues: + * @ctxt: the XPath Parser context + * + * Implement the equal operation on XPath objects content: @arg1 == @arg2 + * + * Returns 0 or 1 depending on the results of the test. + */ +int +xmlXPathNotEqualValues(xmlXPathParserContextPtr ctxt) { + xmlXPathObjectPtr arg1, arg2, argtmp; + int ret = 0; + + if ((ctxt == NULL) || (ctxt->context == NULL)) return(0); + arg2 = valuePop(ctxt); + arg1 = valuePop(ctxt); + if ((arg1 == NULL) || (arg2 == NULL)) { + if (arg1 != NULL) + xmlXPathReleaseObject(ctxt->context, arg1); + else + xmlXPathReleaseObject(ctxt->context, arg2); + XP_ERROR0(XPATH_INVALID_OPERAND); + } + + if (arg1 == arg2) { +#ifdef DEBUG_EXPR + xmlGenericError(xmlGenericErrorContext, + "NotEqual: by pointer\n"); +#endif + xmlXPathReleaseObject(ctxt->context, arg1); + return(0); + } + + /* + *If either argument is a nodeset, it's a 'special case' + */ + if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) || + (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) { + /* + *Hack it to assure arg1 is the nodeset + */ + if ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)) { + argtmp = arg2; + arg2 = arg1; + arg1 = argtmp; + } + switch (arg2->type) { + case XPATH_UNDEFINED: +#ifdef DEBUG_EXPR + xmlGenericError(xmlGenericErrorContext, + "NotEqual: undefined\n"); +#endif + break; + case XPATH_NODESET: + case XPATH_XSLT_TREE: + ret = xmlXPathEqualNodeSets(arg1, arg2, 1); + break; + case XPATH_BOOLEAN: + if ((arg1->nodesetval == NULL) || + (arg1->nodesetval->nodeNr == 0)) ret = 0; + else + ret = 1; + ret = (ret != arg2->boolval); + break; + case XPATH_NUMBER: + ret = xmlXPathEqualNodeSetFloat(ctxt, arg1, arg2->floatval, 1); + break; + case XPATH_STRING: + ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval,1); + break; + case XPATH_USERS: + case XPATH_POINT: + case XPATH_RANGE: + case XPATH_LOCATIONSET: + TODO + break; + } + xmlXPathReleaseObject(ctxt->context, arg1); + xmlXPathReleaseObject(ctxt->context, arg2); + return(ret); + } + + return (!xmlXPathEqualValuesCommon(ctxt, arg1, arg2)); +} + +/** + * xmlXPathCompareValues: + * @ctxt: the XPath Parser context + * @inf: less than (1) or greater than (0) + * @strict: is the comparison strict + * + * Implement the compare operation on XPath objects: + * @arg1 < @arg2 (1, 1, ... + * @arg1 <= @arg2 (1, 0, ... + * @arg1 > @arg2 (0, 1, ... + * @arg1 >= @arg2 (0, 0, ... + * + * When neither object to be compared is a node-set and the operator is + * <=, <, >=, >, then the objects are compared by converted both objects + * to numbers and comparing the numbers according to IEEE 754. The < + * comparison will be true if and only if the first number is less than the + * second number. The <= comparison will be true if and only if the first + * number is less than or equal to the second number. The > comparison + * will be true if and only if the first number is greater than the second + * number. The >= comparison will be true if and only if the first number + * is greater than or equal to the second number. + * + * Returns 1 if the comparison succeeded, 0 if it failed + */ +int +xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) { + int ret = 0, arg1i = 0, arg2i = 0; + xmlXPathObjectPtr arg1, arg2; + + if ((ctxt == NULL) || (ctxt->context == NULL)) return(0); + arg2 = valuePop(ctxt); + arg1 = valuePop(ctxt); + if ((arg1 == NULL) || (arg2 == NULL)) { + if (arg1 != NULL) + xmlXPathReleaseObject(ctxt->context, arg1); + else + xmlXPathReleaseObject(ctxt->context, arg2); + XP_ERROR0(XPATH_INVALID_OPERAND); + } + + if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) || + (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) { + /* + * If either argument is a XPATH_NODESET or XPATH_XSLT_TREE the two arguments + * are not freed from within this routine; they will be freed from the + * called routine, e.g. xmlXPathCompareNodeSets or xmlXPathCompareNodeSetValue + */ + if (((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE)) && + ((arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE))){ + ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2); + } else { + if ((arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) { + ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict, + arg1, arg2); + } else { + ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict, + arg2, arg1); + } + } + return(ret); + } + + if (arg1->type != XPATH_NUMBER) { + valuePush(ctxt, arg1); + xmlXPathNumberFunction(ctxt, 1); + arg1 = valuePop(ctxt); + } + if (arg1->type != XPATH_NUMBER) { + xmlXPathFreeObject(arg1); + xmlXPathFreeObject(arg2); + XP_ERROR0(XPATH_INVALID_OPERAND); + } + if (arg2->type != XPATH_NUMBER) { + valuePush(ctxt, arg2); + xmlXPathNumberFunction(ctxt, 1); + arg2 = valuePop(ctxt); + } + if (arg2->type != XPATH_NUMBER) { + xmlXPathReleaseObject(ctxt->context, arg1); + xmlXPathReleaseObject(ctxt->context, arg2); + XP_ERROR0(XPATH_INVALID_OPERAND); + } + /* + * Add tests for infinity and nan + * => feedback on 3.4 for Inf and NaN + */ + /* Hand check NaN and Infinity comparisons */ + if (xmlXPathIsNaN(arg1->floatval) || xmlXPathIsNaN(arg2->floatval)) { + ret=0; + } else { + arg1i=xmlXPathIsInf(arg1->floatval); + arg2i=xmlXPathIsInf(arg2->floatval); + if (inf && strict) { + if ((arg1i == -1 && arg2i != -1) || + (arg2i == 1 && arg1i != 1)) { + ret = 1; + } else if (arg1i == 0 && arg2i == 0) { + ret = (arg1->floatval < arg2->floatval); + } else { + ret = 0; + } + } + else if (inf && !strict) { + if (arg1i == -1 || arg2i == 1) { + ret = 1; + } else if (arg1i == 0 && arg2i == 0) { + ret = (arg1->floatval <= arg2->floatval); + } else { + ret = 0; + } + } + else if (!inf && strict) { + if ((arg1i == 1 && arg2i != 1) || + (arg2i == -1 && arg1i != -1)) { + ret = 1; + } else if (arg1i == 0 && arg2i == 0) { + ret = (arg1->floatval > arg2->floatval); + } else { + ret = 0; + } + } + else if (!inf && !strict) { + if (arg1i == 1 || arg2i == -1) { + ret = 1; + } else if (arg1i == 0 && arg2i == 0) { + ret = (arg1->floatval >= arg2->floatval); + } else { + ret = 0; + } + } + } + xmlXPathReleaseObject(ctxt->context, arg1); + xmlXPathReleaseObject(ctxt->context, arg2); + return(ret); +} + +/** + * xmlXPathValueFlipSign: + * @ctxt: the XPath Parser context + * + * Implement the unary - operation on an XPath object + * The numeric operators convert their operands to numbers as if + * by calling the number function. + */ +void +xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) { + if ((ctxt == NULL) || (ctxt->context == NULL)) return; + CAST_TO_NUMBER; + CHECK_TYPE(XPATH_NUMBER); + if (xmlXPathIsNaN(ctxt->value->floatval)) + ctxt->value->floatval=xmlXPathNAN; + else if (xmlXPathIsInf(ctxt->value->floatval) == 1) + ctxt->value->floatval=xmlXPathNINF; + else if (xmlXPathIsInf(ctxt->value->floatval) == -1) + ctxt->value->floatval=xmlXPathPINF; + else if (ctxt->value->floatval == 0) { + if (xmlXPathGetSign(ctxt->value->floatval) == 0) + ctxt->value->floatval = xmlXPathNZERO; + else + ctxt->value->floatval = 0; + } + else + ctxt->value->floatval = - ctxt->value->floatval; +} + +/** + * xmlXPathAddValues: + * @ctxt: the XPath Parser context + * + * Implement the add operation on XPath objects: + * The numeric operators convert their operands to numbers as if + * by calling the number function. + */ +void +xmlXPathAddValues(xmlXPathParserContextPtr ctxt) { + xmlXPathObjectPtr arg; + double val; + + arg = valuePop(ctxt); + if (arg == NULL) + XP_ERROR(XPATH_INVALID_OPERAND); + val = xmlXPathCastToNumber(arg); + xmlXPathReleaseObject(ctxt->context, arg); + CAST_TO_NUMBER; + CHECK_TYPE(XPATH_NUMBER); + ctxt->value->floatval += val; +} + +/** + * xmlXPathSubValues: + * @ctxt: the XPath Parser context + * + * Implement the subtraction operation on XPath objects: + * The numeric operators convert their operands to numbers as if + * by calling the number function. + */ +void +xmlXPathSubValues(xmlXPathParserContextPtr ctxt) { + xmlXPathObjectPtr arg; + double val; + + arg = valuePop(ctxt); + if (arg == NULL) + XP_ERROR(XPATH_INVALID_OPERAND); + val = xmlXPathCastToNumber(arg); + xmlXPathReleaseObject(ctxt->context, arg); + CAST_TO_NUMBER; + CHECK_TYPE(XPATH_NUMBER); + ctxt->value->floatval -= val; +} + +/** + * xmlXPathMultValues: + * @ctxt: the XPath Parser context + * + * Implement the multiply operation on XPath objects: + * The numeric operators convert their operands to numbers as if + * by calling the number function. + */ +void +xmlXPathMultValues(xmlXPathParserContextPtr ctxt) { + xmlXPathObjectPtr arg; + double val; + + arg = valuePop(ctxt); + if (arg == NULL) + XP_ERROR(XPATH_INVALID_OPERAND); + val = xmlXPathCastToNumber(arg); + xmlXPathReleaseObject(ctxt->context, arg); + CAST_TO_NUMBER; + CHECK_TYPE(XPATH_NUMBER); + ctxt->value->floatval *= val; +} + +/** + * xmlXPathDivValues: + * @ctxt: the XPath Parser context + * + * Implement the div operation on XPath objects @arg1 / @arg2: + * The numeric operators convert their operands to numbers as if + * by calling the number function. + */ +void +xmlXPathDivValues(xmlXPathParserContextPtr ctxt) { + xmlXPathObjectPtr arg; + double val; + + arg = valuePop(ctxt); + if (arg == NULL) + XP_ERROR(XPATH_INVALID_OPERAND); + val = xmlXPathCastToNumber(arg); + xmlXPathReleaseObject(ctxt->context, arg); + CAST_TO_NUMBER; + CHECK_TYPE(XPATH_NUMBER); + if (xmlXPathIsNaN(val) || xmlXPathIsNaN(ctxt->value->floatval)) + ctxt->value->floatval = xmlXPathNAN; + else if (val == 0 && xmlXPathGetSign(val) != 0) { + if (ctxt->value->floatval == 0) + ctxt->value->floatval = xmlXPathNAN; + else if (ctxt->value->floatval > 0) + ctxt->value->floatval = xmlXPathNINF; + else if (ctxt->value->floatval < 0) + ctxt->value->floatval = xmlXPathPINF; + } + else if (val == 0) { + if (ctxt->value->floatval == 0) + ctxt->value->floatval = xmlXPathNAN; + else if (ctxt->value->floatval > 0) + ctxt->value->floatval = xmlXPathPINF; + else if (ctxt->value->floatval < 0) + ctxt->value->floatval = xmlXPathNINF; + } else + ctxt->value->floatval /= val; +} + +/** + * xmlXPathModValues: + * @ctxt: the XPath Parser context + * + * Implement the mod operation on XPath objects: @arg1 / @arg2 + * The numeric operators convert their operands to numbers as if + * by calling the number function. + */ +void +xmlXPathModValues(xmlXPathParserContextPtr ctxt) { + xmlXPathObjectPtr arg; + double arg1, arg2; + + arg = valuePop(ctxt); + if (arg == NULL) + XP_ERROR(XPATH_INVALID_OPERAND); + arg2 = xmlXPathCastToNumber(arg); + xmlXPathReleaseObject(ctxt->context, arg); + CAST_TO_NUMBER; + CHECK_TYPE(XPATH_NUMBER); + arg1 = ctxt->value->floatval; + if (arg2 == 0) + ctxt->value->floatval = xmlXPathNAN; + else { + ctxt->value->floatval = fmod(arg1, arg2); + } +} + +/************************************************************************ + * * + * The traversal functions * + * * + ************************************************************************/ + +/* + * A traversal function enumerates nodes along an axis. + * Initially it must be called with NULL, and it indicates + * termination on the axis by returning NULL. + */ +typedef xmlNodePtr (*xmlXPathTraversalFunction) + (xmlXPathParserContextPtr ctxt, xmlNodePtr cur); + +/* + * xmlXPathTraversalFunctionExt: + * A traversal function enumerates nodes along an axis. + * Initially it must be called with NULL, and it indicates + * termination on the axis by returning NULL. + * The context node of the traversal is specified via @contextNode. + */ +typedef xmlNodePtr (*xmlXPathTraversalFunctionExt) + (xmlNodePtr cur, xmlNodePtr contextNode); + + +/** + * xmlXPathNextSelf: + * @ctxt: the XPath Parser context + * @cur: the current node in the traversal + * + * Traversal function for the "self" direction + * The self axis contains just the context node itself + * + * Returns the next element following that axis + */ +xmlNodePtr +xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { + if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL); + if (cur == NULL) + return(ctxt->context->node); + return(NULL); +} + +/** + * xmlXPathNextChild: + * @ctxt: the XPath Parser context + * @cur: the current node in the traversal + * + * Traversal function for the "child" direction + * The child axis contains the children of the context node in document order. + * + * Returns the next element following that axis + */ +xmlNodePtr +xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { + if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL); + if (cur == NULL) { + if (ctxt->context->node == NULL) return(NULL); + switch (ctxt->context->node->type) { + case XML_ELEMENT_NODE: + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_ENTITY_REF_NODE: + case XML_ENTITY_NODE: + case XML_PI_NODE: + case XML_COMMENT_NODE: + case XML_NOTATION_NODE: + case XML_DTD_NODE: + return(ctxt->context->node->children); + case XML_DOCUMENT_NODE: + case XML_DOCUMENT_TYPE_NODE: + case XML_DOCUMENT_FRAG_NODE: + case XML_HTML_DOCUMENT_NODE: +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: +#endif + return(((xmlDocPtr) ctxt->context->node)->children); + case XML_ELEMENT_DECL: + case XML_ATTRIBUTE_DECL: + case XML_ENTITY_DECL: + case XML_ATTRIBUTE_NODE: + case XML_NAMESPACE_DECL: + case XML_XINCLUDE_START: + case XML_XINCLUDE_END: + return(NULL); + } + return(NULL); + } + if ((cur->type == XML_DOCUMENT_NODE) || + (cur->type == XML_HTML_DOCUMENT_NODE)) + return(NULL); + return(cur->next); +} + +/** + * xmlXPathNextChildElement: + * @ctxt: the XPath Parser context + * @cur: the current node in the traversal + * + * Traversal function for the "child" direction and nodes of type element. + * The child axis contains the children of the context node in document order. + * + * Returns the next element following that axis + */ +static xmlNodePtr +xmlXPathNextChildElement(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { + if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL); + if (cur == NULL) { + cur = ctxt->context->node; + if (cur == NULL) return(NULL); + /* + * Get the first element child. + */ + switch (cur->type) { + case XML_ELEMENT_NODE: + case XML_DOCUMENT_FRAG_NODE: + case XML_ENTITY_REF_NODE: /* URGENT TODO: entify-refs as well? */ + case XML_ENTITY_NODE: + cur = cur->children; + if (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) + return(cur); + do { + cur = cur->next; + } while ((cur != NULL) && + (cur->type != XML_ELEMENT_NODE)); + return(cur); + } + return(NULL); + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: +#endif + return(xmlDocGetRootElement((xmlDocPtr) cur)); + default: + return(NULL); + } + return(NULL); + } + /* + * Get the next sibling element node. + */ + switch (cur->type) { + case XML_ELEMENT_NODE: + case XML_TEXT_NODE: + case XML_ENTITY_REF_NODE: + case XML_ENTITY_NODE: + case XML_CDATA_SECTION_NODE: + case XML_PI_NODE: + case XML_COMMENT_NODE: + case XML_XINCLUDE_END: + break; + /* case XML_DTD_NODE: */ /* URGENT TODO: DTD-node as well? */ + default: + return(NULL); + } + if (cur->next != NULL) { + if (cur->next->type == XML_ELEMENT_NODE) + return(cur->next); + cur = cur->next; + do { + cur = cur->next; + } while ((cur != NULL) && (cur->type != XML_ELEMENT_NODE)); + return(cur); + } + return(NULL); +} + +/** + * xmlXPathNextDescendantOrSelfElemParent: + * @ctxt: the XPath Parser context + * @cur: the current node in the traversal + * + * Traversal function for the "descendant-or-self" axis. + * Additionally it returns only nodes which can be parents of + * element nodes. + * + * + * Returns the next element following that axis + */ +static xmlNodePtr +xmlXPathNextDescendantOrSelfElemParent(xmlNodePtr cur, + xmlNodePtr contextNode) +{ + if (cur == NULL) { + if (contextNode == NULL) + return(NULL); + switch (contextNode->type) { + case XML_ELEMENT_NODE: + case XML_XINCLUDE_START: + case XML_DOCUMENT_FRAG_NODE: + case XML_DOCUMENT_NODE: +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: +#endif + case XML_HTML_DOCUMENT_NODE: + return(contextNode); + default: + return(NULL); + } + return(NULL); + } else { + xmlNodePtr start = cur; + + while (cur != NULL) { + switch (cur->type) { + case XML_ELEMENT_NODE: + /* TODO: OK to have XInclude here? */ + case XML_XINCLUDE_START: + case XML_DOCUMENT_FRAG_NODE: + if (cur != start) + return(cur); + if (cur->children != NULL) { + cur = cur->children; + continue; + } + break; +#ifdef LIBXML_DOCB_ENABLED + /* Not sure if we need those here. */ + case XML_DOCUMENT_NODE: + case XML_DOCB_DOCUMENT_NODE: +#endif + case XML_HTML_DOCUMENT_NODE: + if (cur != start) + return(cur); + return(xmlDocGetRootElement((xmlDocPtr) cur)); + default: + break; + } + +next_sibling: + if ((cur == NULL) || (cur == contextNode)) + return(NULL); + if (cur->next != NULL) { + cur = cur->next; + } else { + cur = cur->parent; + goto next_sibling; + } + } + } + return(NULL); +} + +/** + * xmlXPathNextDescendant: + * @ctxt: the XPath Parser context + * @cur: the current node in the traversal + * + * Traversal function for the "descendant" direction + * the descendant axis contains the descendants of the context node in document + * order; a descendant is a child or a child of a child and so on. + * + * Returns the next element following that axis + */ +xmlNodePtr +xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { + if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL); + if (cur == NULL) { + if (ctxt->context->node == NULL) + return(NULL); + if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) || + (ctxt->context->node->type == XML_NAMESPACE_DECL)) + return(NULL); + + if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc) + return(ctxt->context->doc->children); + return(ctxt->context->node->children); + } + + if (cur->children != NULL) { + /* + * Do not descend on entities declarations + */ + if (cur->children->type != XML_ENTITY_DECL) { + cur = cur->children; + /* + * Skip DTDs + */ + if (cur->type != XML_DTD_NODE) + return(cur); + } + } + + if (cur == ctxt->context->node) return(NULL); + + while (cur->next != NULL) { + cur = cur->next; + if ((cur->type != XML_ENTITY_DECL) && + (cur->type != XML_DTD_NODE)) + return(cur); + } + + do { + cur = cur->parent; + if (cur == NULL) break; + if (cur == ctxt->context->node) return(NULL); + if (cur->next != NULL) { + cur = cur->next; + return(cur); + } + } while (cur != NULL); + return(cur); +} + +/** + * xmlXPathNextDescendantOrSelf: + * @ctxt: the XPath Parser context + * @cur: the current node in the traversal + * + * Traversal function for the "descendant-or-self" direction + * the descendant-or-self axis contains the context node and the descendants + * of the context node in document order; thus the context node is the first + * node on the axis, and the first child of the context node is the second node + * on the axis + * + * Returns the next element following that axis + */ +xmlNodePtr +xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { + if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL); + if (cur == NULL) { + if (ctxt->context->node == NULL) + return(NULL); + if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) || + (ctxt->context->node->type == XML_NAMESPACE_DECL)) + return(NULL); + return(ctxt->context->node); + } + + return(xmlXPathNextDescendant(ctxt, cur)); +} + +/** + * xmlXPathNextParent: + * @ctxt: the XPath Parser context + * @cur: the current node in the traversal + * + * Traversal function for the "parent" direction + * The parent axis contains the parent of the context node, if there is one. + * + * Returns the next element following that axis + */ +xmlNodePtr +xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { + if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL); + /* + * the parent of an attribute or namespace node is the element + * to which the attribute or namespace node is attached + * Namespace handling !!! + */ + if (cur == NULL) { + if (ctxt->context->node == NULL) return(NULL); + switch (ctxt->context->node->type) { + case XML_ELEMENT_NODE: + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_ENTITY_REF_NODE: + case XML_ENTITY_NODE: + case XML_PI_NODE: + case XML_COMMENT_NODE: + case XML_NOTATION_NODE: + case XML_DTD_NODE: + case XML_ELEMENT_DECL: + case XML_ATTRIBUTE_DECL: + case XML_XINCLUDE_START: + case XML_XINCLUDE_END: + case XML_ENTITY_DECL: + if (ctxt->context->node->parent == NULL) + return((xmlNodePtr) ctxt->context->doc); + if ((ctxt->context->node->parent->type == XML_ELEMENT_NODE) && + ((ctxt->context->node->parent->name[0] == ' ') || + (xmlStrEqual(ctxt->context->node->parent->name, + BAD_CAST "fake node libxslt")))) + return(NULL); + return(ctxt->context->node->parent); + case XML_ATTRIBUTE_NODE: { + xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node; + + return(att->parent); + } + case XML_DOCUMENT_NODE: + case XML_DOCUMENT_TYPE_NODE: + case XML_DOCUMENT_FRAG_NODE: + case XML_HTML_DOCUMENT_NODE: +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: +#endif + return(NULL); + case XML_NAMESPACE_DECL: { + xmlNsPtr ns = (xmlNsPtr) ctxt->context->node; + + if ((ns->next != NULL) && + (ns->next->type != XML_NAMESPACE_DECL)) + return((xmlNodePtr) ns->next); + return(NULL); + } + } + } + return(NULL); +} + +/** + * xmlXPathNextAncestor: + * @ctxt: the XPath Parser context + * @cur: the current node in the traversal + * + * Traversal function for the "ancestor" direction + * the ancestor axis contains the ancestors of the context node; the ancestors + * of the context node consist of the parent of context node and the parent's + * parent and so on; the nodes are ordered in reverse document order; thus the + * parent is the first node on the axis, and the parent's parent is the second + * node on the axis + * + * Returns the next element following that axis + */ +xmlNodePtr +xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { + if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL); + /* + * the parent of an attribute or namespace node is the element + * to which the attribute or namespace node is attached + * !!!!!!!!!!!!! + */ + if (cur == NULL) { + if (ctxt->context->node == NULL) return(NULL); + switch (ctxt->context->node->type) { + case XML_ELEMENT_NODE: + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_ENTITY_REF_NODE: + case XML_ENTITY_NODE: + case XML_PI_NODE: + case XML_COMMENT_NODE: + case XML_DTD_NODE: + case XML_ELEMENT_DECL: + case XML_ATTRIBUTE_DECL: + case XML_ENTITY_DECL: + case XML_NOTATION_NODE: + case XML_XINCLUDE_START: + case XML_XINCLUDE_END: + if (ctxt->context->node->parent == NULL) + return((xmlNodePtr) ctxt->context->doc); + if ((ctxt->context->node->parent->type == XML_ELEMENT_NODE) && + ((ctxt->context->node->parent->name[0] == ' ') || + (xmlStrEqual(ctxt->context->node->parent->name, + BAD_CAST "fake node libxslt")))) + return(NULL); + return(ctxt->context->node->parent); + case XML_ATTRIBUTE_NODE: { + xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node; + + return(tmp->parent); + } + case XML_DOCUMENT_NODE: + case XML_DOCUMENT_TYPE_NODE: + case XML_DOCUMENT_FRAG_NODE: + case XML_HTML_DOCUMENT_NODE: +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: +#endif + return(NULL); + case XML_NAMESPACE_DECL: { + xmlNsPtr ns = (xmlNsPtr) ctxt->context->node; + + if ((ns->next != NULL) && + (ns->next->type != XML_NAMESPACE_DECL)) + return((xmlNodePtr) ns->next); + /* Bad, how did that namespace end up here ? */ + return(NULL); + } + } + return(NULL); + } + if (cur == ctxt->context->doc->children) + return((xmlNodePtr) ctxt->context->doc); + if (cur == (xmlNodePtr) ctxt->context->doc) + return(NULL); + switch (cur->type) { + case XML_ELEMENT_NODE: + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_ENTITY_REF_NODE: + case XML_ENTITY_NODE: + case XML_PI_NODE: + case XML_COMMENT_NODE: + case XML_NOTATION_NODE: + case XML_DTD_NODE: + case XML_ELEMENT_DECL: + case XML_ATTRIBUTE_DECL: + case XML_ENTITY_DECL: + case XML_XINCLUDE_START: + case XML_XINCLUDE_END: + if (cur->parent == NULL) + return(NULL); + if ((cur->parent->type == XML_ELEMENT_NODE) && + ((cur->parent->name[0] == ' ') || + (xmlStrEqual(cur->parent->name, + BAD_CAST "fake node libxslt")))) + return(NULL); + return(cur->parent); + case XML_ATTRIBUTE_NODE: { + xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node; + + return(att->parent); + } + case XML_NAMESPACE_DECL: { + xmlNsPtr ns = (xmlNsPtr) ctxt->context->node; + + if ((ns->next != NULL) && + (ns->next->type != XML_NAMESPACE_DECL)) + return((xmlNodePtr) ns->next); + /* Bad, how did that namespace end up here ? */ + return(NULL); + } + case XML_DOCUMENT_NODE: + case XML_DOCUMENT_TYPE_NODE: + case XML_DOCUMENT_FRAG_NODE: + case XML_HTML_DOCUMENT_NODE: +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: +#endif + return(NULL); + } + return(NULL); +} + +/** + * xmlXPathNextAncestorOrSelf: + * @ctxt: the XPath Parser context + * @cur: the current node in the traversal + * + * Traversal function for the "ancestor-or-self" direction + * he ancestor-or-self axis contains the context node and ancestors of + * the context node in reverse document order; thus the context node is + * the first node on the axis, and the context node's parent the second; + * parent here is defined the same as with the parent axis. + * + * Returns the next element following that axis + */ +xmlNodePtr +xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { + if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL); + if (cur == NULL) + return(ctxt->context->node); + return(xmlXPathNextAncestor(ctxt, cur)); +} + +/** + * xmlXPathNextFollowingSibling: + * @ctxt: the XPath Parser context + * @cur: the current node in the traversal + * + * Traversal function for the "following-sibling" direction + * The following-sibling axis contains the following siblings of the context + * node in document order. + * + * Returns the next element following that axis + */ +xmlNodePtr +xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { + if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL); + if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) || + (ctxt->context->node->type == XML_NAMESPACE_DECL)) + return(NULL); + if (cur == (xmlNodePtr) ctxt->context->doc) + return(NULL); + if (cur == NULL) + return(ctxt->context->node->next); + return(cur->next); +} + +/** + * xmlXPathNextPrecedingSibling: + * @ctxt: the XPath Parser context + * @cur: the current node in the traversal + * + * Traversal function for the "preceding-sibling" direction + * The preceding-sibling axis contains the preceding siblings of the context + * node in reverse document order; the first preceding sibling is first on the + * axis; the sibling preceding that node is the second on the axis and so on. + * + * Returns the next element following that axis + */ +xmlNodePtr +xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { + if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL); + if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) || + (ctxt->context->node->type == XML_NAMESPACE_DECL)) + return(NULL); + if (cur == (xmlNodePtr) ctxt->context->doc) + return(NULL); + if (cur == NULL) + return(ctxt->context->node->prev); + if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) { + cur = cur->prev; + if (cur == NULL) + return(ctxt->context->node->prev); + } + return(cur->prev); +} + +/** + * xmlXPathNextFollowing: + * @ctxt: the XPath Parser context + * @cur: the current node in the traversal + * + * Traversal function for the "following" direction + * The following axis contains all nodes in the same document as the context + * node that are after the context node in document order, excluding any + * descendants and excluding attribute nodes and namespace nodes; the nodes + * are ordered in document order + * + * Returns the next element following that axis + */ +xmlNodePtr +xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { + if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL); + if (cur != NULL && cur->children != NULL) + return cur->children ; + if (cur == NULL) cur = ctxt->context->node; + if (cur == NULL) return(NULL) ; /* ERROR */ + if (cur->next != NULL) return(cur->next) ; + do { + cur = cur->parent; + if (cur == NULL) break; + if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL); + if (cur->next != NULL) return(cur->next); + } while (cur != NULL); + return(cur); +} + +/* + * xmlXPathIsAncestor: + * @ancestor: the ancestor node + * @node: the current node + * + * Check that @ancestor is a @node's ancestor + * + * returns 1 if @ancestor is a @node's ancestor, 0 otherwise. + */ +static int +xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) { + if ((ancestor == NULL) || (node == NULL)) return(0); + /* nodes need to be in the same document */ + if (ancestor->doc != node->doc) return(0); + /* avoid searching if ancestor or node is the root node */ + if (ancestor == (xmlNodePtr) node->doc) return(1); + if (node == (xmlNodePtr) ancestor->doc) return(0); + while (node->parent != NULL) { + if (node->parent == ancestor) + return(1); + node = node->parent; + } + return(0); +} + +/** + * xmlXPathNextPreceding: + * @ctxt: the XPath Parser context + * @cur: the current node in the traversal + * + * Traversal function for the "preceding" direction + * the preceding axis contains all nodes in the same document as the context + * node that are before the context node in document order, excluding any + * ancestors and excluding attribute nodes and namespace nodes; the nodes are + * ordered in reverse document order + * + * Returns the next element following that axis + */ +xmlNodePtr +xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) +{ + if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL); + if (cur == NULL) + cur = ctxt->context->node; + if (cur == NULL) + return (NULL); + if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) + cur = cur->prev; + do { + if (cur->prev != NULL) { + for (cur = cur->prev; cur->last != NULL; cur = cur->last) ; + return (cur); + } + + cur = cur->parent; + if (cur == NULL) + return (NULL); + if (cur == ctxt->context->doc->children) + return (NULL); + } while (xmlXPathIsAncestor(cur, ctxt->context->node)); + return (cur); +} + +/** + * xmlXPathNextPrecedingInternal: + * @ctxt: the XPath Parser context + * @cur: the current node in the traversal + * + * Traversal function for the "preceding" direction + * the preceding axis contains all nodes in the same document as the context + * node that are before the context node in document order, excluding any + * ancestors and excluding attribute nodes and namespace nodes; the nodes are + * ordered in reverse document order + * This is a faster implementation but internal only since it requires a + * state kept in the parser context: ctxt->ancestor. + * + * Returns the next element following that axis + */ +static xmlNodePtr +xmlXPathNextPrecedingInternal(xmlXPathParserContextPtr ctxt, + xmlNodePtr cur) +{ + if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL); + if (cur == NULL) { + cur = ctxt->context->node; + if (cur == NULL) + return (NULL); + if (cur->type == XML_NAMESPACE_DECL) + cur = (xmlNodePtr)((xmlNsPtr)cur)->next; + ctxt->ancestor = cur->parent; + } + if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) + cur = cur->prev; + while (cur->prev == NULL) { + cur = cur->parent; + if (cur == NULL) + return (NULL); + if (cur == ctxt->context->doc->children) + return (NULL); + if (cur != ctxt->ancestor) + return (cur); + ctxt->ancestor = cur->parent; + } + cur = cur->prev; + while (cur->last != NULL) + cur = cur->last; + return (cur); +} + +/** + * xmlXPathNextNamespace: + * @ctxt: the XPath Parser context + * @cur: the current attribute in the traversal + * + * Traversal function for the "namespace" direction + * the namespace axis contains the namespace nodes of the context node; + * the order of nodes on this axis is implementation-defined; the axis will + * be empty unless the context node is an element + * + * We keep the XML namespace node at the end of the list. + * + * Returns the next element following that axis + */ +xmlNodePtr +xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { + if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL); + if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL); + if (ctxt->context->tmpNsList == NULL && cur != (xmlNodePtr) xmlXPathXMLNamespace) { + if (ctxt->context->tmpNsList != NULL) + xmlFree(ctxt->context->tmpNsList); + ctxt->context->tmpNsList = + xmlGetNsList(ctxt->context->doc, ctxt->context->node); + ctxt->context->tmpNsNr = 0; + if (ctxt->context->tmpNsList != NULL) { + while (ctxt->context->tmpNsList[ctxt->context->tmpNsNr] != NULL) { + ctxt->context->tmpNsNr++; + } + } + return((xmlNodePtr) xmlXPathXMLNamespace); + } + if (ctxt->context->tmpNsNr > 0) { + return (xmlNodePtr)ctxt->context->tmpNsList[--ctxt->context->tmpNsNr]; + } else { + if (ctxt->context->tmpNsList != NULL) + xmlFree(ctxt->context->tmpNsList); + ctxt->context->tmpNsList = NULL; + return(NULL); + } +} + +/** + * xmlXPathNextAttribute: + * @ctxt: the XPath Parser context + * @cur: the current attribute in the traversal + * + * Traversal function for the "attribute" direction + * TODO: support DTD inherited default attributes + * + * Returns the next element following that axis + */ +xmlNodePtr +xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { + if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL); + if (ctxt->context->node == NULL) + return(NULL); + if (ctxt->context->node->type != XML_ELEMENT_NODE) + return(NULL); + if (cur == NULL) { + if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc) + return(NULL); + return((xmlNodePtr)ctxt->context->node->properties); + } + return((xmlNodePtr)cur->next); +} + +/************************************************************************ + * * + * NodeTest Functions * + * * + ************************************************************************/ + +#define IS_FUNCTION 200 + + +/************************************************************************ + * * + * Implicit tree core function library * + * * + ************************************************************************/ + +/** + * xmlXPathRoot: + * @ctxt: the XPath Parser context + * + * Initialize the context to the root of the document + */ +void +xmlXPathRoot(xmlXPathParserContextPtr ctxt) { + if ((ctxt == NULL) || (ctxt->context == NULL)) + return; + ctxt->context->node = (xmlNodePtr) ctxt->context->doc; + valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node)); +} + +/************************************************************************ + * * + * The explicit core function library * + *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib * + * * + ************************************************************************/ + + +/** + * xmlXPathLastFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the last() XPath function + * number last() + * The last function returns the number of nodes in the context node list. + */ +void +xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) { + CHECK_ARITY(0); + if (ctxt->context->contextSize >= 0) { + valuePush(ctxt, + xmlXPathCacheNewFloat(ctxt->context, + (double) ctxt->context->contextSize)); +#ifdef DEBUG_EXPR + xmlGenericError(xmlGenericErrorContext, + "last() : %d\n", ctxt->context->contextSize); +#endif + } else { + XP_ERROR(XPATH_INVALID_CTXT_SIZE); + } +} + +/** + * xmlXPathPositionFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the position() XPath function + * number position() + * The position function returns the position of the context node in the + * context node list. The first position is 1, and so the last position + * will be equal to last(). + */ +void +xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) { + CHECK_ARITY(0); + if (ctxt->context->proximityPosition >= 0) { + valuePush(ctxt, + xmlXPathCacheNewFloat(ctxt->context, + (double) ctxt->context->proximityPosition)); +#ifdef DEBUG_EXPR + xmlGenericError(xmlGenericErrorContext, "position() : %d\n", + ctxt->context->proximityPosition); +#endif + } else { + XP_ERROR(XPATH_INVALID_CTXT_POSITION); + } +} + +/** + * xmlXPathCountFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the count() XPath function + * number count(node-set) + */ +void +xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr cur; + + CHECK_ARITY(1); + if ((ctxt->value == NULL) || + ((ctxt->value->type != XPATH_NODESET) && + (ctxt->value->type != XPATH_XSLT_TREE))) + XP_ERROR(XPATH_INVALID_TYPE); + cur = valuePop(ctxt); + + if ((cur == NULL) || (cur->nodesetval == NULL)) + valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, (double) 0)); + else if ((cur->type == XPATH_NODESET) || (cur->type == XPATH_XSLT_TREE)) { + valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, + (double) cur->nodesetval->nodeNr)); + } else { + if ((cur->nodesetval->nodeNr != 1) || + (cur->nodesetval->nodeTab == NULL)) { + valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, (double) 0)); + } else { + xmlNodePtr tmp; + int i = 0; + + tmp = cur->nodesetval->nodeTab[0]; + if (tmp != NULL) { + tmp = tmp->children; + while (tmp != NULL) { + tmp = tmp->next; + i++; + } + } + valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, (double) i)); + } + } + xmlXPathReleaseObject(ctxt->context, cur); +} + +/** + * xmlXPathGetElementsByIds: + * @doc: the document + * @ids: a whitespace separated list of IDs + * + * Selects elements by their unique ID. + * + * Returns a node-set of selected elements. + */ +static xmlNodeSetPtr +xmlXPathGetElementsByIds (xmlDocPtr doc, const xmlChar *ids) { + xmlNodeSetPtr ret; + const xmlChar *cur = ids; + xmlChar *ID; + xmlAttrPtr attr; + xmlNodePtr elem = NULL; + + if (ids == NULL) return(NULL); + + ret = xmlXPathNodeSetCreate(NULL); + + while (IS_BLANK_CH(*cur)) cur++; + while (*cur != 0) { + while ((!IS_BLANK_CH(*cur)) && (*cur != 0)) + cur++; + + ID = xmlStrndup(ids, cur - ids); + if (ID != NULL) { + /* + * We used to check the fact that the value passed + * was an NCName, but this generated much troubles for + * me and Aleksey Sanin, people blatantly violated that + * constaint, like Visa3D spec. + * if (xmlValidateNCName(ID, 1) == 0) + */ + attr = xmlGetID(doc, ID); + if (attr != NULL) { + if (attr->type == XML_ATTRIBUTE_NODE) + elem = attr->parent; + else if (attr->type == XML_ELEMENT_NODE) + elem = (xmlNodePtr) attr; + else + elem = NULL; + if (elem != NULL) + xmlXPathNodeSetAdd(ret, elem); + } + xmlFree(ID); + } + + while (IS_BLANK_CH(*cur)) cur++; + ids = cur; + } + return(ret); +} + +/** + * xmlXPathIdFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the id() XPath function + * node-set id(object) + * The id function selects elements by their unique ID + * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set, + * then the result is the union of the result of applying id to the + * string value of each of the nodes in the argument node-set. When the + * argument to id is of any other type, the argument is converted to a + * string as if by a call to the string function; the string is split + * into a whitespace-separated list of tokens (whitespace is any sequence + * of characters matching the production S); the result is a node-set + * containing the elements in the same document as the context node that + * have a unique ID equal to any of the tokens in the list. + */ +void +xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xmlChar *tokens; + xmlNodeSetPtr ret; + xmlXPathObjectPtr obj; + + CHECK_ARITY(1); + obj = valuePop(ctxt); + if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND); + if ((obj->type == XPATH_NODESET) || (obj->type == XPATH_XSLT_TREE)) { + xmlNodeSetPtr ns; + int i; + + ret = xmlXPathNodeSetCreate(NULL); + + if (obj->nodesetval != NULL) { + for (i = 0; i < obj->nodesetval->nodeNr; i++) { + tokens = + xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]); + ns = xmlXPathGetElementsByIds(ctxt->context->doc, tokens); + ret = xmlXPathNodeSetMerge(ret, ns); + xmlXPathFreeNodeSet(ns); + if (tokens != NULL) + xmlFree(tokens); + } + } + xmlXPathReleaseObject(ctxt->context, obj); + valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, ret)); + return; + } + obj = xmlXPathCacheConvertString(ctxt->context, obj); + ret = xmlXPathGetElementsByIds(ctxt->context->doc, obj->stringval); + valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, ret)); + xmlXPathReleaseObject(ctxt->context, obj); + return; +} + +/** + * xmlXPathLocalNameFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the local-name() XPath function + * string local-name(node-set?) + * The local-name function returns a string containing the local part + * of the name of the node in the argument node-set that is first in + * document order. If the node-set is empty or the first node has no + * name, an empty string is returned. If the argument is omitted it + * defaults to the context node. + */ +void +xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr cur; + + if (ctxt == NULL) return; + + if (nargs == 0) { + valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node)); + nargs = 1; + } + + CHECK_ARITY(1); + if ((ctxt->value == NULL) || + ((ctxt->value->type != XPATH_NODESET) && + (ctxt->value->type != XPATH_XSLT_TREE))) + XP_ERROR(XPATH_INVALID_TYPE); + cur = valuePop(ctxt); + + if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) { + valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, "")); + } else { + int i = 0; /* Should be first in document order !!!!! */ + switch (cur->nodesetval->nodeTab[i]->type) { + case XML_ELEMENT_NODE: + case XML_ATTRIBUTE_NODE: + case XML_PI_NODE: + if (cur->nodesetval->nodeTab[i]->name[0] == ' ') + valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, "")); + else + valuePush(ctxt, + xmlXPathCacheNewString(ctxt->context, + cur->nodesetval->nodeTab[i]->name)); + break; + case XML_NAMESPACE_DECL: + valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, + ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix)); + break; + default: + valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, "")); + } + } + xmlXPathReleaseObject(ctxt->context, cur); +} + +/** + * xmlXPathNamespaceURIFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the namespace-uri() XPath function + * string namespace-uri(node-set?) + * The namespace-uri function returns a string containing the + * namespace URI of the expanded name of the node in the argument + * node-set that is first in document order. If the node-set is empty, + * the first node has no name, or the expanded name has no namespace + * URI, an empty string is returned. If the argument is omitted it + * defaults to the context node. + */ +void +xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr cur; + + if (ctxt == NULL) return; + + if (nargs == 0) { + valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node)); + nargs = 1; + } + CHECK_ARITY(1); + if ((ctxt->value == NULL) || + ((ctxt->value->type != XPATH_NODESET) && + (ctxt->value->type != XPATH_XSLT_TREE))) + XP_ERROR(XPATH_INVALID_TYPE); + cur = valuePop(ctxt); + + if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) { + valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, "")); + } else { + int i = 0; /* Should be first in document order !!!!! */ + switch (cur->nodesetval->nodeTab[i]->type) { + case XML_ELEMENT_NODE: + case XML_ATTRIBUTE_NODE: + if (cur->nodesetval->nodeTab[i]->ns == NULL) + valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, "")); + else + valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, + cur->nodesetval->nodeTab[i]->ns->href)); + break; + default: + valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, "")); + } + } + xmlXPathReleaseObject(ctxt->context, cur); +} + +/** + * xmlXPathNameFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the name() XPath function + * string name(node-set?) + * The name function returns a string containing a QName representing + * the name of the node in the argument node-set that is first in document + * order. The QName must represent the name with respect to the namespace + * declarations in effect on the node whose name is being represented. + * Typically, this will be the form in which the name occurred in the XML + * source. This need not be the case if there are namespace declarations + * in effect on the node that associate multiple prefixes with the same + * namespace. However, an implementation may include information about + * the original prefix in its representation of nodes; in this case, an + * implementation can ensure that the returned string is always the same + * as the QName used in the XML source. If the argument it omitted it + * defaults to the context node. + * Libxml keep the original prefix so the "real qualified name" used is + * returned. + */ +static void +xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) +{ + xmlXPathObjectPtr cur; + + if (nargs == 0) { + valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node)); + nargs = 1; + } + + CHECK_ARITY(1); + if ((ctxt->value == NULL) || + ((ctxt->value->type != XPATH_NODESET) && + (ctxt->value->type != XPATH_XSLT_TREE))) + XP_ERROR(XPATH_INVALID_TYPE); + cur = valuePop(ctxt); + + if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) { + valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, "")); + } else { + int i = 0; /* Should be first in document order !!!!! */ + + switch (cur->nodesetval->nodeTab[i]->type) { + case XML_ELEMENT_NODE: + case XML_ATTRIBUTE_NODE: + if (cur->nodesetval->nodeTab[i]->name[0] == ' ') + valuePush(ctxt, + xmlXPathCacheNewCString(ctxt->context, "")); + else if ((cur->nodesetval->nodeTab[i]->ns == NULL) || + (cur->nodesetval->nodeTab[i]->ns->prefix == NULL)) { + valuePush(ctxt, + xmlXPathCacheNewString(ctxt->context, + cur->nodesetval->nodeTab[i]->name)); + } else { + xmlChar *fullname; + + fullname = xmlBuildQName(cur->nodesetval->nodeTab[i]->name, + cur->nodesetval->nodeTab[i]->ns->prefix, + NULL, 0); + if (fullname == cur->nodesetval->nodeTab[i]->name) + fullname = xmlStrdup(cur->nodesetval->nodeTab[i]->name); + if (fullname == NULL) { + XP_ERROR(XPATH_MEMORY_ERROR); + } + valuePush(ctxt, xmlXPathCacheWrapString( + ctxt->context, fullname)); + } + break; + default: + valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context, + cur->nodesetval->nodeTab[i])); + xmlXPathLocalNameFunction(ctxt, 1); + } + } + xmlXPathReleaseObject(ctxt->context, cur); +} + + +/** + * xmlXPathStringFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the string() XPath function + * string string(object?) + * The string function converts an object to a string as follows: + * - A node-set is converted to a string by returning the value of + * the node in the node-set that is first in document order. + * If the node-set is empty, an empty string is returned. + * - A number is converted to a string as follows + * + NaN is converted to the string NaN + * + positive zero is converted to the string 0 + * + negative zero is converted to the string 0 + * + positive infinity is converted to the string Infinity + * + negative infinity is converted to the string -Infinity + * + if the number is an integer, the number is represented in + * decimal form as a Number with no decimal point and no leading + * zeros, preceded by a minus sign (-) if the number is negative + * + otherwise, the number is represented in decimal form as a + * Number including a decimal point with at least one digit + * before the decimal point and at least one digit after the + * decimal point, preceded by a minus sign (-) if the number + * is negative; there must be no leading zeros before the decimal + * point apart possibly from the one required digit immediately + * before the decimal point; beyond the one required digit + * after the decimal point there must be as many, but only as + * many, more digits as are needed to uniquely distinguish the + * number from all other IEEE 754 numeric values. + * - The boolean false value is converted to the string false. + * The boolean true value is converted to the string true. + * + * If the argument is omitted, it defaults to a node-set with the + * context node as its only member. + */ +void +xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr cur; + + if (ctxt == NULL) return; + if (nargs == 0) { + valuePush(ctxt, + xmlXPathCacheWrapString(ctxt->context, + xmlXPathCastNodeToString(ctxt->context->node))); + return; + } + + CHECK_ARITY(1); + cur = valuePop(ctxt); + if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND); + valuePush(ctxt, xmlXPathCacheConvertString(ctxt->context, cur)); +} + +/** + * xmlXPathStringLengthFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the string-length() XPath function + * number string-length(string?) + * The string-length returns the number of characters in the string + * (see [3.6 Strings]). If the argument is omitted, it defaults to + * the context node converted to a string, in other words the value + * of the context node. + */ +void +xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr cur; + + if (nargs == 0) { + if ((ctxt == NULL) || (ctxt->context == NULL)) + return; + if (ctxt->context->node == NULL) { + valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, 0)); + } else { + xmlChar *content; + + content = xmlXPathCastNodeToString(ctxt->context->node); + valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, + xmlUTF8Strlen(content))); + xmlFree(content); + } + return; + } + CHECK_ARITY(1); + CAST_TO_STRING; + CHECK_TYPE(XPATH_STRING); + cur = valuePop(ctxt); + valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, + xmlUTF8Strlen(cur->stringval))); + xmlXPathReleaseObject(ctxt->context, cur); +} + +/** + * xmlXPathConcatFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the concat() XPath function + * string concat(string, string, string*) + * The concat function returns the concatenation of its arguments. + */ +void +xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr cur, newobj; + xmlChar *tmp; + + if (ctxt == NULL) return; + if (nargs < 2) { + CHECK_ARITY(2); + } + + CAST_TO_STRING; + cur = valuePop(ctxt); + if ((cur == NULL) || (cur->type != XPATH_STRING)) { + xmlXPathReleaseObject(ctxt->context, cur); + return; + } + nargs--; + + while (nargs > 0) { + CAST_TO_STRING; + newobj = valuePop(ctxt); + if ((newobj == NULL) || (newobj->type != XPATH_STRING)) { + xmlXPathReleaseObject(ctxt->context, newobj); + xmlXPathReleaseObject(ctxt->context, cur); + XP_ERROR(XPATH_INVALID_TYPE); + } + tmp = xmlStrcat(newobj->stringval, cur->stringval); + newobj->stringval = cur->stringval; + cur->stringval = tmp; + xmlXPathReleaseObject(ctxt->context, newobj); + nargs--; + } + valuePush(ctxt, cur); +} + +/** + * xmlXPathContainsFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the contains() XPath function + * boolean contains(string, string) + * The contains function returns true if the first argument string + * contains the second argument string, and otherwise returns false. + */ +void +xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr hay, needle; + + CHECK_ARITY(2); + CAST_TO_STRING; + CHECK_TYPE(XPATH_STRING); + needle = valuePop(ctxt); + CAST_TO_STRING; + hay = valuePop(ctxt); + + if ((hay == NULL) || (hay->type != XPATH_STRING)) { + xmlXPathReleaseObject(ctxt->context, hay); + xmlXPathReleaseObject(ctxt->context, needle); + XP_ERROR(XPATH_INVALID_TYPE); + } + if (xmlStrstr(hay->stringval, needle->stringval)) + valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1)); + else + valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0)); + xmlXPathReleaseObject(ctxt->context, hay); + xmlXPathReleaseObject(ctxt->context, needle); +} + +/** + * xmlXPathStartsWithFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the starts-with() XPath function + * boolean starts-with(string, string) + * The starts-with function returns true if the first argument string + * starts with the second argument string, and otherwise returns false. + */ +void +xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr hay, needle; + int n; + + CHECK_ARITY(2); + CAST_TO_STRING; + CHECK_TYPE(XPATH_STRING); + needle = valuePop(ctxt); + CAST_TO_STRING; + hay = valuePop(ctxt); + + if ((hay == NULL) || (hay->type != XPATH_STRING)) { + xmlXPathReleaseObject(ctxt->context, hay); + xmlXPathReleaseObject(ctxt->context, needle); + XP_ERROR(XPATH_INVALID_TYPE); + } + n = xmlStrlen(needle->stringval); + if (xmlStrncmp(hay->stringval, needle->stringval, n)) + valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0)); + else + valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1)); + xmlXPathReleaseObject(ctxt->context, hay); + xmlXPathReleaseObject(ctxt->context, needle); +} + +/** + * xmlXPathSubstringFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the substring() XPath function + * string substring(string, number, number?) + * The substring function returns the substring of the first argument + * starting at the position specified in the second argument with + * length specified in the third argument. For example, + * substring("12345",2,3) returns "234". If the third argument is not + * specified, it returns the substring starting at the position specified + * in the second argument and continuing to the end of the string. For + * example, substring("12345",2) returns "2345". More precisely, each + * character in the string (see [3.6 Strings]) is considered to have a + * numeric position: the position of the first character is 1, the position + * of the second character is 2 and so on. The returned substring contains + * those characters for which the position of the character is greater than + * or equal to the second argument and, if the third argument is specified, + * less than the sum of the second and third arguments; the comparisons + * and addition used for the above follow the standard IEEE 754 rules. Thus: + * - substring("12345", 1.5, 2.6) returns "234" + * - substring("12345", 0, 3) returns "12" + * - substring("12345", 0 div 0, 3) returns "" + * - substring("12345", 1, 0 div 0) returns "" + * - substring("12345", -42, 1 div 0) returns "12345" + * - substring("12345", -1 div 0, 1 div 0) returns "" + */ +void +xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr str, start, len; + double le=0, in; + int i, l, m; + xmlChar *ret; + + if (nargs < 2) { + CHECK_ARITY(2); + } + if (nargs > 3) { + CHECK_ARITY(3); + } + /* + * take care of possible last (position) argument + */ + if (nargs == 3) { + CAST_TO_NUMBER; + CHECK_TYPE(XPATH_NUMBER); + len = valuePop(ctxt); + le = len->floatval; + xmlXPathReleaseObject(ctxt->context, len); + } + + CAST_TO_NUMBER; + CHECK_TYPE(XPATH_NUMBER); + start = valuePop(ctxt); + in = start->floatval; + xmlXPathReleaseObject(ctxt->context, start); + CAST_TO_STRING; + CHECK_TYPE(XPATH_STRING); + str = valuePop(ctxt); + m = xmlUTF8Strlen((const unsigned char *)str->stringval); + + /* + * If last pos not present, calculate last position + */ + if (nargs != 3) { + le = (double)m; + if (in < 1.0) + in = 1.0; + } + + /* Need to check for the special cases where either + * the index is NaN, the length is NaN, or both + * arguments are infinity (relying on Inf + -Inf = NaN) + */ + if (!xmlXPathIsNaN(in + le) && !xmlXPathIsInf(in)) { + /* + * To meet the requirements of the spec, the arguments + * must be converted to integer format before + * initial index calculations are done + * + * First we go to integer form, rounding up + * and checking for special cases + */ + i = (int) in; + if (((double)i)+0.5 <= in) i++; + + if (xmlXPathIsInf(le) == 1) { + l = m; + if (i < 1) + i = 1; + } + else if (xmlXPathIsInf(le) == -1 || le < 0.0) + l = 0; + else { + l = (int) le; + if (((double)l)+0.5 <= le) l++; + } + + /* Now we normalize inidices */ + i -= 1; + l += i; + if (i < 0) + i = 0; + if (l > m) + l = m; + + /* number of chars to copy */ + l -= i; + + ret = xmlUTF8Strsub(str->stringval, i, l); + } + else { + ret = NULL; + } + if (ret == NULL) + valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, "")); + else { + valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, ret)); + xmlFree(ret); + } + xmlXPathReleaseObject(ctxt->context, str); +} + +/** + * xmlXPathSubstringBeforeFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the substring-before() XPath function + * string substring-before(string, string) + * The substring-before function returns the substring of the first + * argument string that precedes the first occurrence of the second + * argument string in the first argument string, or the empty string + * if the first argument string does not contain the second argument + * string. For example, substring-before("1999/04/01","/") returns 1999. + */ +void +xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr str; + xmlXPathObjectPtr find; + xmlBufferPtr target; + const xmlChar *point; + int offset; + + CHECK_ARITY(2); + CAST_TO_STRING; + find = valuePop(ctxt); + CAST_TO_STRING; + str = valuePop(ctxt); + + target = xmlBufferCreate(); + if (target) { + point = xmlStrstr(str->stringval, find->stringval); + if (point) { + offset = (int)(point - str->stringval); + xmlBufferAdd(target, str->stringval, offset); + } + valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, + xmlBufferContent(target))); + xmlBufferFree(target); + } + xmlXPathReleaseObject(ctxt->context, str); + xmlXPathReleaseObject(ctxt->context, find); +} + +/** + * xmlXPathSubstringAfterFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the substring-after() XPath function + * string substring-after(string, string) + * The substring-after function returns the substring of the first + * argument string that follows the first occurrence of the second + * argument string in the first argument string, or the empty stringi + * if the first argument string does not contain the second argument + * string. For example, substring-after("1999/04/01","/") returns 04/01, + * and substring-after("1999/04/01","19") returns 99/04/01. + */ +void +xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr str; + xmlXPathObjectPtr find; + xmlBufferPtr target; + const xmlChar *point; + int offset; + + CHECK_ARITY(2); + CAST_TO_STRING; + find = valuePop(ctxt); + CAST_TO_STRING; + str = valuePop(ctxt); + + target = xmlBufferCreate(); + if (target) { + point = xmlStrstr(str->stringval, find->stringval); + if (point) { + offset = (int)(point - str->stringval) + xmlStrlen(find->stringval); + xmlBufferAdd(target, &str->stringval[offset], + xmlStrlen(str->stringval) - offset); + } + valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, + xmlBufferContent(target))); + xmlBufferFree(target); + } + xmlXPathReleaseObject(ctxt->context, str); + xmlXPathReleaseObject(ctxt->context, find); +} + +/** + * xmlXPathNormalizeFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the normalize-space() XPath function + * string normalize-space(string?) + * The normalize-space function returns the argument string with white + * space normalized by stripping leading and trailing whitespace + * and replacing sequences of whitespace characters by a single + * space. Whitespace characters are the same allowed by the S production + * in XML. If the argument is omitted, it defaults to the context + * node converted to a string, in other words the value of the context node. + */ +void +xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr obj = NULL; + xmlChar *source = NULL; + xmlBufferPtr target; + xmlChar blank; + + if (ctxt == NULL) return; + if (nargs == 0) { + /* Use current context node */ + valuePush(ctxt, + xmlXPathCacheWrapString(ctxt->context, + xmlXPathCastNodeToString(ctxt->context->node))); + nargs = 1; + } + + CHECK_ARITY(1); + CAST_TO_STRING; + CHECK_TYPE(XPATH_STRING); + obj = valuePop(ctxt); + source = obj->stringval; + + target = xmlBufferCreate(); + if (target && source) { + + /* Skip leading whitespaces */ + while (IS_BLANK_CH(*source)) + source++; + + /* Collapse intermediate whitespaces, and skip trailing whitespaces */ + blank = 0; + while (*source) { + if (IS_BLANK_CH(*source)) { + blank = 0x20; + } else { + if (blank) { + xmlBufferAdd(target, &blank, 1); + blank = 0; + } + xmlBufferAdd(target, source, 1); + } + source++; + } + valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, + xmlBufferContent(target))); + xmlBufferFree(target); + } + xmlXPathReleaseObject(ctxt->context, obj); +} + +/** + * xmlXPathTranslateFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the translate() XPath function + * string translate(string, string, string) + * The translate function returns the first argument string with + * occurrences of characters in the second argument string replaced + * by the character at the corresponding position in the third argument + * string. For example, translate("bar","abc","ABC") returns the string + * BAr. If there is a character in the second argument string with no + * character at a corresponding position in the third argument string + * (because the second argument string is longer than the third argument + * string), then occurrences of that character in the first argument + * string are removed. For example, translate("--aaa--","abc-","ABC") + * returns "AAA". If a character occurs more than once in second + * argument string, then the first occurrence determines the replacement + * character. If the third argument string is longer than the second + * argument string, then excess characters are ignored. + */ +void +xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr str; + xmlXPathObjectPtr from; + xmlXPathObjectPtr to; + xmlBufferPtr target; + int offset, max; + xmlChar ch; + const xmlChar *point; + xmlChar *cptr; + + CHECK_ARITY(3); + + CAST_TO_STRING; + to = valuePop(ctxt); + CAST_TO_STRING; + from = valuePop(ctxt); + CAST_TO_STRING; + str = valuePop(ctxt); + + target = xmlBufferCreate(); + if (target) { + max = xmlUTF8Strlen(to->stringval); + for (cptr = str->stringval; (ch=*cptr); ) { + offset = xmlUTF8Strloc(from->stringval, cptr); + if (offset >= 0) { + if (offset < max) { + point = xmlUTF8Strpos(to->stringval, offset); + if (point) + xmlBufferAdd(target, point, xmlUTF8Strsize(point, 1)); + } + } else + xmlBufferAdd(target, cptr, xmlUTF8Strsize(cptr, 1)); + + /* Step to next character in input */ + cptr++; + if ( ch & 0x80 ) { + /* if not simple ascii, verify proper format */ + if ( (ch & 0xc0) != 0xc0 ) { + xmlGenericError(xmlGenericErrorContext, + "xmlXPathTranslateFunction: Invalid UTF8 string\n"); + break; + } + /* then skip over remaining bytes for this char */ + while ( (ch <<= 1) & 0x80 ) + if ( (*cptr++ & 0xc0) != 0x80 ) { + xmlGenericError(xmlGenericErrorContext, + "xmlXPathTranslateFunction: Invalid UTF8 string\n"); + break; + } + if (ch & 0x80) /* must have had error encountered */ + break; + } + } + } + valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, + xmlBufferContent(target))); + xmlBufferFree(target); + xmlXPathReleaseObject(ctxt->context, str); + xmlXPathReleaseObject(ctxt->context, from); + xmlXPathReleaseObject(ctxt->context, to); +} + +/** + * xmlXPathBooleanFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the boolean() XPath function + * boolean boolean(object) + * The boolean function converts its argument to a boolean as follows: + * - a number is true if and only if it is neither positive or + * negative zero nor NaN + * - a node-set is true if and only if it is non-empty + * - a string is true if and only if its length is non-zero + */ +void +xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr cur; + + CHECK_ARITY(1); + cur = valuePop(ctxt); + if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND); + cur = xmlXPathCacheConvertBoolean(ctxt->context, cur); + valuePush(ctxt, cur); +} + +/** + * xmlXPathNotFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the not() XPath function + * boolean not(boolean) + * The not function returns true if its argument is false, + * and false otherwise. + */ +void +xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) { + CHECK_ARITY(1); + CAST_TO_BOOLEAN; + CHECK_TYPE(XPATH_BOOLEAN); + ctxt->value->boolval = ! ctxt->value->boolval; +} + +/** + * xmlXPathTrueFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the true() XPath function + * boolean true() + */ +void +xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) { + CHECK_ARITY(0); + valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1)); +} + +/** + * xmlXPathFalseFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the false() XPath function + * boolean false() + */ +void +xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) { + CHECK_ARITY(0); + valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0)); +} + +/** + * xmlXPathLangFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the lang() XPath function + * boolean lang(string) + * The lang function returns true or false depending on whether the + * language of the context node as specified by xml:lang attributes + * is the same as or is a sublanguage of the language specified by + * the argument string. The language of the context node is determined + * by the value of the xml:lang attribute on the context node, or, if + * the context node has no xml:lang attribute, by the value of the + * xml:lang attribute on the nearest ancestor of the context node that + * has an xml:lang attribute. If there is no such attribute, then lang + * returns false. If there is such an attribute, then lang returns + * true if the attribute value is equal to the argument ignoring case, + * or if there is some suffix starting with - such that the attribute + * value is equal to the argument ignoring that suffix of the attribute + * value and ignoring case. + */ +void +xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr val = NULL; + const xmlChar *theLang = NULL; + const xmlChar *lang; + int ret = 0; + int i; + + CHECK_ARITY(1); + CAST_TO_STRING; + CHECK_TYPE(XPATH_STRING); + val = valuePop(ctxt); + lang = val->stringval; + theLang = xmlNodeGetLang(ctxt->context->node); + if ((theLang != NULL) && (lang != NULL)) { + for (i = 0;lang[i] != 0;i++) + if (toupper(lang[i]) != toupper(theLang[i])) + goto not_equal; + if ((theLang[i] == 0) || (theLang[i] == '-')) + ret = 1; + } +not_equal: + if (theLang != NULL) + xmlFree((void *)theLang); + + xmlXPathReleaseObject(ctxt->context, val); + valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, ret)); +} + +/** + * xmlXPathNumberFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the number() XPath function + * number number(object?) + */ +void +xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr cur; + double res; + + if (ctxt == NULL) return; + if (nargs == 0) { + if (ctxt->context->node == NULL) { + valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, 0.0)); + } else { + xmlChar* content = xmlNodeGetContent(ctxt->context->node); + + res = xmlXPathStringEvalNumber(content); + valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, res)); + xmlFree(content); + } + return; + } + + CHECK_ARITY(1); + cur = valuePop(ctxt); + valuePush(ctxt, xmlXPathCacheConvertNumber(ctxt->context, cur)); +} + +/** + * xmlXPathSumFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the sum() XPath function + * number sum(node-set) + * The sum function returns the sum of the values of the nodes in + * the argument node-set. + */ +void +xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr cur; + int i; + double res = 0.0; + + CHECK_ARITY(1); + if ((ctxt->value == NULL) || + ((ctxt->value->type != XPATH_NODESET) && + (ctxt->value->type != XPATH_XSLT_TREE))) + XP_ERROR(XPATH_INVALID_TYPE); + cur = valuePop(ctxt); + + if ((cur->nodesetval != NULL) && (cur->nodesetval->nodeNr != 0)) { + for (i = 0; i < cur->nodesetval->nodeNr; i++) { + res += xmlXPathCastNodeToNumber(cur->nodesetval->nodeTab[i]); + } + } + valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, res)); + xmlXPathReleaseObject(ctxt->context, cur); +} + +/* + * To assure working code on multiple platforms, we want to only depend + * upon the characteristic truncation of converting a floating point value + * to an integer. Unfortunately, because of the different storage sizes + * of our internal floating point value (double) and integer (int), we + * can't directly convert (see bug 301162). This macro is a messy + * 'workaround' + */ +#define XTRUNC(f, v) \ + f = fmod((v), INT_MAX); \ + f = (v) - (f) + (double)((int)(f)); + +/** + * xmlXPathFloorFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the floor() XPath function + * number floor(number) + * The floor function returns the largest (closest to positive infinity) + * number that is not greater than the argument and that is an integer. + */ +void +xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) { + double f; + + CHECK_ARITY(1); + CAST_TO_NUMBER; + CHECK_TYPE(XPATH_NUMBER); + + XTRUNC(f, ctxt->value->floatval); + if (f != ctxt->value->floatval) { + if (ctxt->value->floatval > 0) + ctxt->value->floatval = f; + else + ctxt->value->floatval = f - 1; + } +} + +/** + * xmlXPathCeilingFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the ceiling() XPath function + * number ceiling(number) + * The ceiling function returns the smallest (closest to negative infinity) + * number that is not less than the argument and that is an integer. + */ +void +xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) { + double f; + + CHECK_ARITY(1); + CAST_TO_NUMBER; + CHECK_TYPE(XPATH_NUMBER); + +#if 0 + ctxt->value->floatval = ceil(ctxt->value->floatval); +#else + XTRUNC(f, ctxt->value->floatval); + if (f != ctxt->value->floatval) { + if (ctxt->value->floatval > 0) + ctxt->value->floatval = f + 1; + else { + if (ctxt->value->floatval < 0 && f == 0) + ctxt->value->floatval = xmlXPathNZERO; + else + ctxt->value->floatval = f; + } + + } +#endif +} + +/** + * xmlXPathRoundFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the round() XPath function + * number round(number) + * The round function returns the number that is closest to the + * argument and that is an integer. If there are two such numbers, + * then the one that is even is returned. + */ +void +xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) { + double f; + + CHECK_ARITY(1); + CAST_TO_NUMBER; + CHECK_TYPE(XPATH_NUMBER); + + if ((xmlXPathIsNaN(ctxt->value->floatval)) || + (xmlXPathIsInf(ctxt->value->floatval) == 1) || + (xmlXPathIsInf(ctxt->value->floatval) == -1) || + (ctxt->value->floatval == 0.0)) + return; + + XTRUNC(f, ctxt->value->floatval); + if (ctxt->value->floatval < 0) { + if (ctxt->value->floatval < f - 0.5) + ctxt->value->floatval = f - 1; + else + ctxt->value->floatval = f; + if (ctxt->value->floatval == 0) + ctxt->value->floatval = xmlXPathNZERO; + } else { + if (ctxt->value->floatval < f + 0.5) + ctxt->value->floatval = f; + else + ctxt->value->floatval = f + 1; + } +} + +/************************************************************************ + * * + * The Parser * + * * + ************************************************************************/ + +/* + * a few forward declarations since we use a recursive call based + * implementation. + */ +static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort); +static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter); +static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt); +static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt); +static xmlChar * xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, + int qualified); + +/** + * xmlXPathCurrentChar: + * @ctxt: the XPath parser context + * @cur: pointer to the beginning of the char + * @len: pointer to the length of the char read + * + * The current char value, if using UTF-8 this may actually span multiple + * bytes in the input buffer. + * + * Returns the current char value and its length + */ + +static int +xmlXPathCurrentChar(xmlXPathParserContextPtr ctxt, int *len) { + unsigned char c; + unsigned int val; + const xmlChar *cur; + + if (ctxt == NULL) + return(0); + cur = ctxt->cur; + + /* + * We are supposed to handle UTF8, check it's valid + * From rfc2044: encoding of the Unicode values on UTF-8: + * + * UCS-4 range (hex.) UTF-8 octet sequence (binary) + * 0000 0000-0000 007F 0xxxxxxx + * 0000 0080-0000 07FF 110xxxxx 10xxxxxx + * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx + * + * Check for the 0x110000 limit too + */ + c = *cur; + if (c & 0x80) { + if ((cur[1] & 0xc0) != 0x80) + goto encoding_error; + if ((c & 0xe0) == 0xe0) { + + if ((cur[2] & 0xc0) != 0x80) + goto encoding_error; + if ((c & 0xf0) == 0xf0) { + if (((c & 0xf8) != 0xf0) || + ((cur[3] & 0xc0) != 0x80)) + goto encoding_error; + /* 4-byte code */ + *len = 4; + val = (cur[0] & 0x7) << 18; + val |= (cur[1] & 0x3f) << 12; + val |= (cur[2] & 0x3f) << 6; + val |= cur[3] & 0x3f; + } else { + /* 3-byte code */ + *len = 3; + val = (cur[0] & 0xf) << 12; + val |= (cur[1] & 0x3f) << 6; + val |= cur[2] & 0x3f; + } + } else { + /* 2-byte code */ + *len = 2; + val = (cur[0] & 0x1f) << 6; + val |= cur[1] & 0x3f; + } + if (!IS_CHAR(val)) { + XP_ERROR0(XPATH_INVALID_CHAR_ERROR); + } + return(val); + } else { + /* 1-byte code */ + *len = 1; + return((int) *cur); + } +encoding_error: + /* + * If we detect an UTF8 error that probably means that the + * input encoding didn't get properly advertised in the + * declaration header. Report the error and switch the encoding + * to ISO-Latin-1 (if you don't like this policy, just declare the + * encoding !) + */ + *len = 0; + XP_ERROR0(XPATH_ENCODING_ERROR); +} + +/** + * xmlXPathParseNCName: + * @ctxt: the XPath Parser context + * + * parse an XML namespace non qualified name. + * + * [NS 3] NCName ::= (Letter | '_') (NCNameChar)* + * + * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' | + * CombiningChar | Extender + * + * Returns the namespace name or NULL + */ + +xmlChar * +xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) { + const xmlChar *in; + xmlChar *ret; + int count = 0; + + if ((ctxt == NULL) || (ctxt->cur == NULL)) return(NULL); + /* + * Accelerator for simple ASCII names + */ + in = ctxt->cur; + if (((*in >= 0x61) && (*in <= 0x7A)) || + ((*in >= 0x41) && (*in <= 0x5A)) || + (*in == '_')) { + in++; + while (((*in >= 0x61) && (*in <= 0x7A)) || + ((*in >= 0x41) && (*in <= 0x5A)) || + ((*in >= 0x30) && (*in <= 0x39)) || + (*in == '_') || (*in == '.') || + (*in == '-')) + in++; + if ((*in == ' ') || (*in == '>') || (*in == '/') || + (*in == '[') || (*in == ']') || (*in == ':') || + (*in == '@') || (*in == '*')) { + count = in - ctxt->cur; + if (count == 0) + return(NULL); + ret = xmlStrndup(ctxt->cur, count); + ctxt->cur = in; + return(ret); + } + } + return(xmlXPathParseNameComplex(ctxt, 0)); +} + + +/** + * xmlXPathParseQName: + * @ctxt: the XPath Parser context + * @prefix: a xmlChar ** + * + * parse an XML qualified name + * + * [NS 5] QName ::= (Prefix ':')? LocalPart + * + * [NS 6] Prefix ::= NCName + * + * [NS 7] LocalPart ::= NCName + * + * Returns the function returns the local part, and prefix is updated + * to get the Prefix if any. + */ + +static xmlChar * +xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) { + xmlChar *ret = NULL; + + *prefix = NULL; + ret = xmlXPathParseNCName(ctxt); + if (CUR == ':') { + *prefix = ret; + NEXT; + ret = xmlXPathParseNCName(ctxt); + } + return(ret); +} + +/** + * xmlXPathParseName: + * @ctxt: the XPath Parser context + * + * parse an XML name + * + * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | + * CombiningChar | Extender + * + * [5] Name ::= (Letter | '_' | ':') (NameChar)* + * + * Returns the namespace name or NULL + */ + +xmlChar * +xmlXPathParseName(xmlXPathParserContextPtr ctxt) { + const xmlChar *in; + xmlChar *ret; + int count = 0; + + if ((ctxt == NULL) || (ctxt->cur == NULL)) return(NULL); + /* + * Accelerator for simple ASCII names + */ + in = ctxt->cur; + if (((*in >= 0x61) && (*in <= 0x7A)) || + ((*in >= 0x41) && (*in <= 0x5A)) || + (*in == '_') || (*in == ':')) { + in++; + while (((*in >= 0x61) && (*in <= 0x7A)) || + ((*in >= 0x41) && (*in <= 0x5A)) || + ((*in >= 0x30) && (*in <= 0x39)) || + (*in == '_') || (*in == '-') || + (*in == ':') || (*in == '.')) + in++; + if ((*in > 0) && (*in < 0x80)) { + count = in - ctxt->cur; + ret = xmlStrndup(ctxt->cur, count); + ctxt->cur = in; + return(ret); + } + } + return(xmlXPathParseNameComplex(ctxt, 1)); +} + +static xmlChar * +xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, int qualified) { + xmlChar buf[XML_MAX_NAMELEN + 5]; + int len = 0, l; + int c; + + /* + * Handler for more complex cases + */ + c = CUR_CHAR(l); + if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */ + (c == '[') || (c == ']') || (c == '@') || /* accelerators */ + (c == '*') || /* accelerators */ + (!IS_LETTER(c) && (c != '_') && + ((qualified) && (c != ':')))) { + return(NULL); + } + + while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */ + ((IS_LETTER(c)) || (IS_DIGIT(c)) || + (c == '.') || (c == '-') || + (c == '_') || ((qualified) && (c == ':')) || + (IS_COMBINING(c)) || + (IS_EXTENDER(c)))) { + COPY_BUF(l,buf,len,c); + NEXTL(l); + c = CUR_CHAR(l); + if (len >= XML_MAX_NAMELEN) { + /* + * Okay someone managed to make a huge name, so he's ready to pay + * for the processing speed. + */ + xmlChar *buffer; + int max = len * 2; + + buffer = (xmlChar *) xmlMallocAtomic(max * sizeof(xmlChar)); + if (buffer == NULL) { + XP_ERRORNULL(XPATH_MEMORY_ERROR); + } + memcpy(buffer, buf, len); + while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigname.xml */ + (c == '.') || (c == '-') || + (c == '_') || ((qualified) && (c == ':')) || + (IS_COMBINING(c)) || + (IS_EXTENDER(c))) { + if (len + 10 > max) { + max *= 2; + buffer = (xmlChar *) xmlRealloc(buffer, + max * sizeof(xmlChar)); + if (buffer == NULL) { + XP_ERRORNULL(XPATH_MEMORY_ERROR); + } + } + COPY_BUF(l,buffer,len,c); + NEXTL(l); + c = CUR_CHAR(l); + } + buffer[len] = 0; + return(buffer); + } + } + if (len == 0) + return(NULL); + return(xmlStrndup(buf, len)); +} + +#define MAX_FRAC 20 + +/* + * These are used as divisors for the fractional part of a number. + * Since the table includes 1.0 (representing '0' fractional digits), + * it must be dimensioned at MAX_FRAC+1 (bug 133921) + */ +static double my_pow10[MAX_FRAC+1] = { + 1.0, 10.0, 100.0, 1000.0, 10000.0, + 100000.0, 1000000.0, 10000000.0, 100000000.0, 1000000000.0, + 10000000000.0, 100000000000.0, 1000000000000.0, 10000000000000.0, + 100000000000000.0, + 1000000000000000.0, 10000000000000000.0, 100000000000000000.0, + 1000000000000000000.0, 10000000000000000000.0, 100000000000000000000.0 +}; + +/** + * xmlXPathStringEvalNumber: + * @str: A string to scan + * + * [30a] Float ::= Number ('e' Digits?)? + * + * [30] Number ::= Digits ('.' Digits?)? + * | '.' Digits + * [31] Digits ::= [0-9]+ + * + * Compile a Number in the string + * In complement of the Number expression, this function also handles + * negative values : '-' Number. + * + * Returns the double value. + */ +double +xmlXPathStringEvalNumber(const xmlChar *str) { + const xmlChar *cur = str; + double ret; + int ok = 0; + int isneg = 0; + int exponent = 0; + int is_exponent_negative = 0; +#ifdef __GNUC__ + unsigned long tmp = 0; + double temp; +#endif + if (cur == NULL) return(0); + while (IS_BLANK_CH(*cur)) cur++; + if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) { + return(xmlXPathNAN); + } + if (*cur == '-') { + isneg = 1; + cur++; + } + +#ifdef __GNUC__ + /* + * tmp/temp is a workaround against a gcc compiler bug + * http://veillard.com/gcc.bug + */ + ret = 0; + while ((*cur >= '0') && (*cur <= '9')) { + ret = ret * 10; + tmp = (*cur - '0'); + ok = 1; + cur++; + temp = (double) tmp; + ret = ret + temp; + } +#else + ret = 0; + while ((*cur >= '0') && (*cur <= '9')) { + ret = ret * 10 + (*cur - '0'); + ok = 1; + cur++; + } +#endif + + if (*cur == '.') { + int v, frac = 0; + double fraction = 0; + + cur++; + if (((*cur < '0') || (*cur > '9')) && (!ok)) { + return(xmlXPathNAN); + } + while (((*cur >= '0') && (*cur <= '9')) && (frac < MAX_FRAC)) { + v = (*cur - '0'); + fraction = fraction * 10 + v; + frac = frac + 1; + cur++; + } + fraction /= my_pow10[frac]; + ret = ret + fraction; + while ((*cur >= '0') && (*cur <= '9')) + cur++; + } + if ((*cur == 'e') || (*cur == 'E')) { + cur++; + if (*cur == '-') { + is_exponent_negative = 1; + cur++; + } else if (*cur == '+') { + cur++; + } + while ((*cur >= '0') && (*cur <= '9')) { + exponent = exponent * 10 + (*cur - '0'); + cur++; + } + } + while (IS_BLANK_CH(*cur)) cur++; + if (*cur != 0) return(xmlXPathNAN); + if (isneg) ret = -ret; + if (is_exponent_negative) exponent = -exponent; + ret *= pow(10.0, (double)exponent); + return(ret); +} + +/** + * xmlXPathCompNumber: + * @ctxt: the XPath Parser context + * + * [30] Number ::= Digits ('.' Digits?)? + * | '.' Digits + * [31] Digits ::= [0-9]+ + * + * Compile a Number, then push it on the stack + * + */ +static void +xmlXPathCompNumber(xmlXPathParserContextPtr ctxt) +{ + double ret = 0.0; + double mult = 1; + int ok = 0; + int exponent = 0; + int is_exponent_negative = 0; +#ifdef __GNUC__ + unsigned long tmp = 0; + double temp; +#endif + + CHECK_ERROR; + if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) { + XP_ERROR(XPATH_NUMBER_ERROR); + } +#ifdef __GNUC__ + /* + * tmp/temp is a workaround against a gcc compiler bug + * http://veillard.com/gcc.bug + */ + ret = 0; + while ((CUR >= '0') && (CUR <= '9')) { + ret = ret * 10; + tmp = (CUR - '0'); + ok = 1; + NEXT; + temp = (double) tmp; + ret = ret + temp; + } +#else + ret = 0; + while ((CUR >= '0') && (CUR <= '9')) { + ret = ret * 10 + (CUR - '0'); + ok = 1; + NEXT; + } +#endif + if (CUR == '.') { + NEXT; + if (((CUR < '0') || (CUR > '9')) && (!ok)) { + XP_ERROR(XPATH_NUMBER_ERROR); + } + while ((CUR >= '0') && (CUR <= '9')) { + mult /= 10; + ret = ret + (CUR - '0') * mult; + NEXT; + } + } + if ((CUR == 'e') || (CUR == 'E')) { + NEXT; + if (CUR == '-') { + is_exponent_negative = 1; + NEXT; + } else if (CUR == '+') { + NEXT; + } + while ((CUR >= '0') && (CUR <= '9')) { + exponent = exponent * 10 + (CUR - '0'); + NEXT; + } + if (is_exponent_negative) + exponent = -exponent; + ret *= pow(10.0, (double) exponent); + } + PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0, + xmlXPathCacheNewFloat(ctxt->context, ret), NULL); +} + +/** + * xmlXPathParseLiteral: + * @ctxt: the XPath Parser context + * + * Parse a Literal + * + * [29] Literal ::= '"' [^"]* '"' + * | "'" [^']* "'" + * + * Returns the value found or NULL in case of error + */ +static xmlChar * +xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) { + const xmlChar *q; + xmlChar *ret = NULL; + + if (CUR == '"') { + NEXT; + q = CUR_PTR; + while ((IS_CHAR_CH(CUR)) && (CUR != '"')) + NEXT; + if (!IS_CHAR_CH(CUR)) { + XP_ERRORNULL(XPATH_UNFINISHED_LITERAL_ERROR); + } else { + ret = xmlStrndup(q, CUR_PTR - q); + NEXT; + } + } else if (CUR == '\'') { + NEXT; + q = CUR_PTR; + while ((IS_CHAR_CH(CUR)) && (CUR != '\'')) + NEXT; + if (!IS_CHAR_CH(CUR)) { + XP_ERRORNULL(XPATH_UNFINISHED_LITERAL_ERROR); + } else { + ret = xmlStrndup(q, CUR_PTR - q); + NEXT; + } + } else { + XP_ERRORNULL(XPATH_START_LITERAL_ERROR); + } + return(ret); +} + +/** + * xmlXPathCompLiteral: + * @ctxt: the XPath Parser context + * + * Parse a Literal and push it on the stack. + * + * [29] Literal ::= '"' [^"]* '"' + * | "'" [^']* "'" + * + * TODO: xmlXPathCompLiteral memory allocation could be improved. + */ +static void +xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) { + const xmlChar *q; + xmlChar *ret = NULL; + + if (CUR == '"') { + NEXT; + q = CUR_PTR; + while ((IS_CHAR_CH(CUR)) && (CUR != '"')) + NEXT; + if (!IS_CHAR_CH(CUR)) { + XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); + } else { + ret = xmlStrndup(q, CUR_PTR - q); + NEXT; + } + } else if (CUR == '\'') { + NEXT; + q = CUR_PTR; + while ((IS_CHAR_CH(CUR)) && (CUR != '\'')) + NEXT; + if (!IS_CHAR_CH(CUR)) { + XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); + } else { + ret = xmlStrndup(q, CUR_PTR - q); + NEXT; + } + } else { + XP_ERROR(XPATH_START_LITERAL_ERROR); + } + if (ret == NULL) return; + PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0, + xmlXPathCacheNewString(ctxt->context, ret), NULL); + xmlFree(ret); +} + +/** + * xmlXPathCompVariableReference: + * @ctxt: the XPath Parser context + * + * Parse a VariableReference, evaluate it and push it on the stack. + * + * The variable bindings consist of a mapping from variable names + * to variable values. The value of a variable is an object, which can be + * of any of the types that are possible for the value of an expression, + * and may also be of additional types not specified here. + * + * Early evaluation is possible since: + * The variable bindings [...] used to evaluate a subexpression are + * always the same as those used to evaluate the containing expression. + * + * [36] VariableReference ::= '$' QName + */ +static void +xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) { + xmlChar *name; + xmlChar *prefix; + + SKIP_BLANKS; + if (CUR != '$') { + XP_ERROR(XPATH_VARIABLE_REF_ERROR); + } + NEXT; + name = xmlXPathParseQName(ctxt, &prefix); + if (name == NULL) { + XP_ERROR(XPATH_VARIABLE_REF_ERROR); + } + ctxt->comp->last = -1; + PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0, + name, prefix); + SKIP_BLANKS; + if ((ctxt->context != NULL) && (ctxt->context->flags & XML_XPATH_NOVAR)) { + XP_ERROR(XPATH_UNDEF_VARIABLE_ERROR); + } +} + +/** + * xmlXPathIsNodeType: + * @name: a name string + * + * Is the name given a NodeType one. + * + * [38] NodeType ::= 'comment' + * | 'text' + * | 'processing-instruction' + * | 'node' + * + * Returns 1 if true 0 otherwise + */ +int +xmlXPathIsNodeType(const xmlChar *name) { + if (name == NULL) + return(0); + + if (xmlStrEqual(name, BAD_CAST "node")) + return(1); + if (xmlStrEqual(name, BAD_CAST "text")) + return(1); + if (xmlStrEqual(name, BAD_CAST "comment")) + return(1); + if (xmlStrEqual(name, BAD_CAST "processing-instruction")) + return(1); + return(0); +} + +/** + * xmlXPathCompFunctionCall: + * @ctxt: the XPath Parser context + * + * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')' + * [17] Argument ::= Expr + * + * Compile a function call, the evaluation of all arguments are + * pushed on the stack + */ +static void +xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) { + xmlChar *name; + xmlChar *prefix; + int nbargs = 0; + int sort = 1; + + name = xmlXPathParseQName(ctxt, &prefix); + if (name == NULL) { + XP_ERROR(XPATH_EXPR_ERROR); + } + SKIP_BLANKS; +#ifdef DEBUG_EXPR + if (prefix == NULL) + xmlGenericError(xmlGenericErrorContext, "Calling function %s\n", + name); + else + xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n", + prefix, name); +#endif + + if (CUR != '(') { + XP_ERROR(XPATH_EXPR_ERROR); + } + NEXT; + SKIP_BLANKS; + + /* + * Optimization for count(): we don't need the node-set to be sorted. + */ + if ((prefix == NULL) && (name[0] == 'c') && + xmlStrEqual(name, BAD_CAST "count")) + { + sort = 0; + } + ctxt->comp->last = -1; + if (CUR != ')') { + while (CUR != 0) { + int op1 = ctxt->comp->last; + ctxt->comp->last = -1; + xmlXPathCompileExpr(ctxt, sort); + CHECK_ERROR; + PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0); + nbargs++; + if (CUR == ')') break; + if (CUR != ',') { + XP_ERROR(XPATH_EXPR_ERROR); + } + NEXT; + SKIP_BLANKS; + } + } + PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0, + name, prefix); + NEXT; + SKIP_BLANKS; +} + +/** + * xmlXPathCompPrimaryExpr: + * @ctxt: the XPath Parser context + * + * [15] PrimaryExpr ::= VariableReference + * | '(' Expr ')' + * | Literal + * | Number + * | FunctionCall + * + * Compile a primary expression. + */ +static void +xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) { + SKIP_BLANKS; + if (CUR == '$') xmlXPathCompVariableReference(ctxt); + else if (CUR == '(') { + NEXT; + SKIP_BLANKS; + xmlXPathCompileExpr(ctxt, 1); + CHECK_ERROR; + if (CUR != ')') { + XP_ERROR(XPATH_EXPR_ERROR); + } + NEXT; + SKIP_BLANKS; + } else if (IS_ASCII_DIGIT(CUR) || (CUR == '.' && IS_ASCII_DIGIT(NXT(1)))) { + xmlXPathCompNumber(ctxt); + } else if ((CUR == '\'') || (CUR == '"')) { + xmlXPathCompLiteral(ctxt); + } else { + xmlXPathCompFunctionCall(ctxt); + } + SKIP_BLANKS; +} + +/** + * xmlXPathCompFilterExpr: + * @ctxt: the XPath Parser context + * + * [20] FilterExpr ::= PrimaryExpr + * | FilterExpr Predicate + * + * Compile a filter expression. + * Square brackets are used to filter expressions in the same way that + * they are used in location paths. It is an error if the expression to + * be filtered does not evaluate to a node-set. The context node list + * used for evaluating the expression in square brackets is the node-set + * to be filtered listed in document order. + */ + +static void +xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) { + xmlXPathCompPrimaryExpr(ctxt); + CHECK_ERROR; + SKIP_BLANKS; + + while (CUR == '[') { + xmlXPathCompPredicate(ctxt, 1); + SKIP_BLANKS; + } + + +} + +/** + * xmlXPathScanName: + * @ctxt: the XPath Parser context + * + * Trickery: parse an XML name but without consuming the input flow + * Needed to avoid insanity in the parser state. + * + * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | + * CombiningChar | Extender + * + * [5] Name ::= (Letter | '_' | ':') (NameChar)* + * + * [6] Names ::= Name (S Name)* + * + * Returns the Name parsed or NULL + */ + +static xmlChar * +xmlXPathScanName(xmlXPathParserContextPtr ctxt) { + int len = 0, l; + int c; + const xmlChar *cur; + xmlChar *ret; + + cur = ctxt->cur; + + c = CUR_CHAR(l); + if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */ + (!IS_LETTER(c) && (c != '_') && + (c != ':'))) { + return(NULL); + } + + while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */ + ((IS_LETTER(c)) || (IS_DIGIT(c)) || + (c == '.') || (c == '-') || + (c == '_') || (c == ':') || + (IS_COMBINING(c)) || + (IS_EXTENDER(c)))) { + len += l; + NEXTL(l); + c = CUR_CHAR(l); + } + ret = xmlStrndup(cur, ctxt->cur - cur); + ctxt->cur = cur; + return(ret); +} + +/** + * xmlXPathCompPathExpr: + * @ctxt: the XPath Parser context + * + * [19] PathExpr ::= LocationPath + * | FilterExpr + * | FilterExpr '/' RelativeLocationPath + * | FilterExpr '//' RelativeLocationPath + * + * Compile a path expression. + * The / operator and // operators combine an arbitrary expression + * and a relative location path. It is an error if the expression + * does not evaluate to a node-set. + * The / operator does composition in the same way as when / is + * used in a location path. As in location paths, // is short for + * /descendant-or-self::node()/. + */ + +static void +xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) { + int lc = 1; /* Should we branch to LocationPath ? */ + xmlChar *name = NULL; /* we may have to preparse a name to find out */ + + SKIP_BLANKS; + if ((CUR == '$') || (CUR == '(') || + (IS_ASCII_DIGIT(CUR)) || + (CUR == '\'') || (CUR == '"') || + (CUR == '.' && IS_ASCII_DIGIT(NXT(1)))) { + lc = 0; + } else if (CUR == '*') { + /* relative or absolute location path */ + lc = 1; + } else if (CUR == '/') { + /* relative or absolute location path */ + lc = 1; + } else if (CUR == '@') { + /* relative abbreviated attribute location path */ + lc = 1; + } else if (CUR == '.') { + /* relative abbreviated attribute location path */ + lc = 1; + } else { + /* + * Problem is finding if we have a name here whether it's: + * - a nodetype + * - a function call in which case it's followed by '(' + * - an axis in which case it's followed by ':' + * - a element name + * We do an a priori analysis here rather than having to + * maintain parsed token content through the recursive function + * calls. This looks uglier but makes the code easier to + * read/write/debug. + */ + SKIP_BLANKS; + name = xmlXPathScanName(ctxt); + if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) { +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "PathExpr: Axis\n"); +#endif + lc = 1; + xmlFree(name); + } else if (name != NULL) { + int len =xmlStrlen(name); + + + while (NXT(len) != 0) { + if (NXT(len) == '/') { + /* element name */ +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "PathExpr: AbbrRelLocation\n"); +#endif + lc = 1; + break; + } else if (IS_BLANK_CH(NXT(len))) { + /* ignore blanks */ + ; + } else if (NXT(len) == ':') { +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "PathExpr: AbbrRelLocation\n"); +#endif + lc = 1; + break; + } else if ((NXT(len) == '(')) { + /* Note Type or Function */ + if (xmlXPathIsNodeType(name)) { +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "PathExpr: Type search\n"); +#endif + lc = 1; + } else { +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "PathExpr: function call\n"); +#endif + lc = 0; + } + break; + } else if ((NXT(len) == '[')) { + /* element name */ +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "PathExpr: AbbrRelLocation\n"); +#endif + lc = 1; + break; + } else if ((NXT(len) == '<') || (NXT(len) == '>') || + (NXT(len) == '=')) { + lc = 1; + break; + } else { + lc = 1; + break; + } + len++; + } + if (NXT(len) == 0) { +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "PathExpr: AbbrRelLocation\n"); +#endif + /* element name */ + lc = 1; + } + xmlFree(name); + } else { + /* make sure all cases are covered explicitly */ + XP_ERROR(XPATH_EXPR_ERROR); + } + } + + if (lc) { + if (CUR == '/') { + PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0); + } else { + PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0); + } + xmlXPathCompLocationPath(ctxt); + } else { + xmlXPathCompFilterExpr(ctxt); + CHECK_ERROR; + if ((CUR == '/') && (NXT(1) == '/')) { + SKIP(2); + SKIP_BLANKS; + + PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF, + NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL); + PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0); + + xmlXPathCompRelativeLocationPath(ctxt); + } else if (CUR == '/') { + xmlXPathCompRelativeLocationPath(ctxt); + } + } + SKIP_BLANKS; +} + +/** + * xmlXPathCompUnionExpr: + * @ctxt: the XPath Parser context + * + * [18] UnionExpr ::= PathExpr + * | UnionExpr '|' PathExpr + * + * Compile an union expression. + */ + +static void +xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) { + xmlXPathCompPathExpr(ctxt); + CHECK_ERROR; + SKIP_BLANKS; + while (CUR == '|') { + int op1 = ctxt->comp->last; + PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0); + + NEXT; + SKIP_BLANKS; + xmlXPathCompPathExpr(ctxt); + + PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0); + + SKIP_BLANKS; + } +} + +/** + * xmlXPathCompUnaryExpr: + * @ctxt: the XPath Parser context + * + * [27] UnaryExpr ::= UnionExpr + * | '-' UnaryExpr + * + * Compile an unary expression. + */ + +static void +xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) { + int minus = 0; + int found = 0; + + SKIP_BLANKS; + while (CUR == '-') { + minus = 1 - minus; + found = 1; + NEXT; + SKIP_BLANKS; + } + + xmlXPathCompUnionExpr(ctxt); + CHECK_ERROR; + if (found) { + if (minus) + PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0); + else + PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0); + } +} + +/** + * xmlXPathCompMultiplicativeExpr: + * @ctxt: the XPath Parser context + * + * [26] MultiplicativeExpr ::= UnaryExpr + * | MultiplicativeExpr MultiplyOperator UnaryExpr + * | MultiplicativeExpr 'div' UnaryExpr + * | MultiplicativeExpr 'mod' UnaryExpr + * [34] MultiplyOperator ::= '*' + * + * Compile an Additive expression. + */ + +static void +xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) { + xmlXPathCompUnaryExpr(ctxt); + CHECK_ERROR; + SKIP_BLANKS; + while ((CUR == '*') || + ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) || + ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) { + int op = -1; + int op1 = ctxt->comp->last; + + if (CUR == '*') { + op = 0; + NEXT; + } else if (CUR == 'd') { + op = 1; + SKIP(3); + } else if (CUR == 'm') { + op = 2; + SKIP(3); + } + SKIP_BLANKS; + xmlXPathCompUnaryExpr(ctxt); + CHECK_ERROR; + PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0); + SKIP_BLANKS; + } +} + +/** + * xmlXPathCompAdditiveExpr: + * @ctxt: the XPath Parser context + * + * [25] AdditiveExpr ::= MultiplicativeExpr + * | AdditiveExpr '+' MultiplicativeExpr + * | AdditiveExpr '-' MultiplicativeExpr + * + * Compile an Additive expression. + */ + +static void +xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) { + + xmlXPathCompMultiplicativeExpr(ctxt); + CHECK_ERROR; + SKIP_BLANKS; + while ((CUR == '+') || (CUR == '-')) { + int plus; + int op1 = ctxt->comp->last; + + if (CUR == '+') plus = 1; + else plus = 0; + NEXT; + SKIP_BLANKS; + xmlXPathCompMultiplicativeExpr(ctxt); + CHECK_ERROR; + PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0); + SKIP_BLANKS; + } +} + +/** + * xmlXPathCompRelationalExpr: + * @ctxt: the XPath Parser context + * + * [24] RelationalExpr ::= AdditiveExpr + * | RelationalExpr '<' AdditiveExpr + * | RelationalExpr '>' AdditiveExpr + * | RelationalExpr '<=' AdditiveExpr + * | RelationalExpr '>=' AdditiveExpr + * + * A <= B > C is allowed ? Answer from James, yes with + * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr + * which is basically what got implemented. + * + * Compile a Relational expression, then push the result + * on the stack + */ + +static void +xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) { + xmlXPathCompAdditiveExpr(ctxt); + CHECK_ERROR; + SKIP_BLANKS; + while ((CUR == '<') || + (CUR == '>') || + ((CUR == '<') && (NXT(1) == '=')) || + ((CUR == '>') && (NXT(1) == '='))) { + int inf, strict; + int op1 = ctxt->comp->last; + + if (CUR == '<') inf = 1; + else inf = 0; + if (NXT(1) == '=') strict = 0; + else strict = 1; + NEXT; + if (!strict) NEXT; + SKIP_BLANKS; + xmlXPathCompAdditiveExpr(ctxt); + CHECK_ERROR; + PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict); + SKIP_BLANKS; + } +} + +/** + * xmlXPathCompEqualityExpr: + * @ctxt: the XPath Parser context + * + * [23] EqualityExpr ::= RelationalExpr + * | EqualityExpr '=' RelationalExpr + * | EqualityExpr '!=' RelationalExpr + * + * A != B != C is allowed ? Answer from James, yes with + * (RelationalExpr = RelationalExpr) = RelationalExpr + * (RelationalExpr != RelationalExpr) != RelationalExpr + * which is basically what got implemented. + * + * Compile an Equality expression. + * + */ +static void +xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) { + xmlXPathCompRelationalExpr(ctxt); + CHECK_ERROR; + SKIP_BLANKS; + while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) { + int eq; + int op1 = ctxt->comp->last; + + if (CUR == '=') eq = 1; + else eq = 0; + NEXT; + if (!eq) NEXT; + SKIP_BLANKS; + xmlXPathCompRelationalExpr(ctxt); + CHECK_ERROR; + PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0); + SKIP_BLANKS; + } +} + +/** + * xmlXPathCompAndExpr: + * @ctxt: the XPath Parser context + * + * [22] AndExpr ::= EqualityExpr + * | AndExpr 'and' EqualityExpr + * + * Compile an AND expression. + * + */ +static void +xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) { + xmlXPathCompEqualityExpr(ctxt); + CHECK_ERROR; + SKIP_BLANKS; + while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) { + int op1 = ctxt->comp->last; + SKIP(3); + SKIP_BLANKS; + xmlXPathCompEqualityExpr(ctxt); + CHECK_ERROR; + PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0); + SKIP_BLANKS; + } +} + +/** + * xmlXPathCompileExpr: + * @ctxt: the XPath Parser context + * + * [14] Expr ::= OrExpr + * [21] OrExpr ::= AndExpr + * | OrExpr 'or' AndExpr + * + * Parse and compile an expression + */ +static void +xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort) { + xmlXPathCompAndExpr(ctxt); + CHECK_ERROR; + SKIP_BLANKS; + while ((CUR == 'o') && (NXT(1) == 'r')) { + int op1 = ctxt->comp->last; + SKIP(2); + SKIP_BLANKS; + xmlXPathCompAndExpr(ctxt); + CHECK_ERROR; + PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0); + op1 = ctxt->comp->nbStep; + SKIP_BLANKS; + } + if ((sort) && (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE)) { + /* more ops could be optimized too */ + /* + * This is the main place to eliminate sorting for + * operations which don't require a sorted node-set. + * E.g. count(). + */ + PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0); + } +} + +/** + * xmlXPathCompPredicate: + * @ctxt: the XPath Parser context + * @filter: act as a filter + * + * [8] Predicate ::= '[' PredicateExpr ']' + * [9] PredicateExpr ::= Expr + * + * Compile a predicate expression + */ +static void +xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) { + int op1 = ctxt->comp->last; + + SKIP_BLANKS; + if (CUR != '[') { + XP_ERROR(XPATH_INVALID_PREDICATE_ERROR); + } + NEXT; + SKIP_BLANKS; + + ctxt->comp->last = -1; + xmlXPathCompileExpr(ctxt, 1); + CHECK_ERROR; + + if (CUR != ']') { + XP_ERROR(XPATH_INVALID_PREDICATE_ERROR); + } + + if (filter) + PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0); + else + PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0); + + NEXT; + SKIP_BLANKS; +} + +/** + * xmlXPathCompNodeTest: + * @ctxt: the XPath Parser context + * @test: pointer to a xmlXPathTestVal + * @type: pointer to a xmlXPathTypeVal + * @prefix: placeholder for a possible name prefix + * + * [7] NodeTest ::= NameTest + * | NodeType '(' ')' + * | 'processing-instruction' '(' Literal ')' + * + * [37] NameTest ::= '*' + * | NCName ':' '*' + * | QName + * [38] NodeType ::= 'comment' + * | 'text' + * | 'processing-instruction' + * | 'node' + * + * Returns the name found and updates @test, @type and @prefix appropriately + */ +static xmlChar * +xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test, + xmlXPathTypeVal *type, const xmlChar **prefix, + xmlChar *name) { + int blanks; + + if ((test == NULL) || (type == NULL) || (prefix == NULL)) { + STRANGE; + return(NULL); + } + *type = (xmlXPathTypeVal) 0; + *test = (xmlXPathTestVal) 0; + *prefix = NULL; + SKIP_BLANKS; + + if ((name == NULL) && (CUR == '*')) { + /* + * All elements + */ + NEXT; + *test = NODE_TEST_ALL; + return(NULL); + } + + if (name == NULL) + name = xmlXPathParseNCName(ctxt); + if (name == NULL) { + XP_ERRORNULL(XPATH_EXPR_ERROR); + } + + blanks = IS_BLANK_CH(CUR); + SKIP_BLANKS; + if (CUR == '(') { + NEXT; + /* + * NodeType or PI search + */ + if (xmlStrEqual(name, BAD_CAST "comment")) + *type = NODE_TYPE_COMMENT; + else if (xmlStrEqual(name, BAD_CAST "node")) + *type = NODE_TYPE_NODE; + else if (xmlStrEqual(name, BAD_CAST "processing-instruction")) + *type = NODE_TYPE_PI; + else if (xmlStrEqual(name, BAD_CAST "text")) + *type = NODE_TYPE_TEXT; + else { + if (name != NULL) + xmlFree(name); + XP_ERRORNULL(XPATH_EXPR_ERROR); + } + + *test = NODE_TEST_TYPE; + + SKIP_BLANKS; + if (*type == NODE_TYPE_PI) { + /* + * Specific case: search a PI by name. + */ + if (name != NULL) + xmlFree(name); + name = NULL; + if (CUR != ')') { + name = xmlXPathParseLiteral(ctxt); + CHECK_ERROR NULL; + *test = NODE_TEST_PI; + SKIP_BLANKS; + } + } + if (CUR != ')') { + if (name != NULL) + xmlFree(name); + XP_ERRORNULL(XPATH_UNCLOSED_ERROR); + } + NEXT; + return(name); + } + *test = NODE_TEST_NAME; + if ((!blanks) && (CUR == ':')) { + NEXT; + + /* + * Since currently the parser context don't have a + * namespace list associated: + * The namespace name for this prefix can be computed + * only at evaluation time. The compilation is done + * outside of any context. + */ +#if 0 + *prefix = xmlXPathNsLookup(ctxt->context, name); + if (name != NULL) + xmlFree(name); + if (*prefix == NULL) { + XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR); + } +#else + *prefix = name; +#endif + + if (CUR == '*') { + /* + * All elements + */ + NEXT; + *test = NODE_TEST_ALL; + return(NULL); + } + + name = xmlXPathParseNCName(ctxt); + if (name == NULL) { + XP_ERRORNULL(XPATH_EXPR_ERROR); + } + } + return(name); +} + +/** + * xmlXPathIsAxisName: + * @name: a preparsed name token + * + * [6] AxisName ::= 'ancestor' + * | 'ancestor-or-self' + * | 'attribute' + * | 'child' + * | 'descendant' + * | 'descendant-or-self' + * | 'following' + * | 'following-sibling' + * | 'namespace' + * | 'parent' + * | 'preceding' + * | 'preceding-sibling' + * | 'self' + * + * Returns the axis or 0 + */ +static xmlXPathAxisVal +xmlXPathIsAxisName(const xmlChar *name) { + xmlXPathAxisVal ret = (xmlXPathAxisVal) 0; + switch (name[0]) { + case 'a': + if (xmlStrEqual(name, BAD_CAST "ancestor")) + ret = AXIS_ANCESTOR; + if (xmlStrEqual(name, BAD_CAST "ancestor-or-self")) + ret = AXIS_ANCESTOR_OR_SELF; + if (xmlStrEqual(name, BAD_CAST "attribute")) + ret = AXIS_ATTRIBUTE; + break; + case 'c': + if (xmlStrEqual(name, BAD_CAST "child")) + ret = AXIS_CHILD; + break; + case 'd': + if (xmlStrEqual(name, BAD_CAST "descendant")) + ret = AXIS_DESCENDANT; + if (xmlStrEqual(name, BAD_CAST "descendant-or-self")) + ret = AXIS_DESCENDANT_OR_SELF; + break; + case 'f': + if (xmlStrEqual(name, BAD_CAST "following")) + ret = AXIS_FOLLOWING; + if (xmlStrEqual(name, BAD_CAST "following-sibling")) + ret = AXIS_FOLLOWING_SIBLING; + break; + case 'n': + if (xmlStrEqual(name, BAD_CAST "namespace")) + ret = AXIS_NAMESPACE; + break; + case 'p': + if (xmlStrEqual(name, BAD_CAST "parent")) + ret = AXIS_PARENT; + if (xmlStrEqual(name, BAD_CAST "preceding")) + ret = AXIS_PRECEDING; + if (xmlStrEqual(name, BAD_CAST "preceding-sibling")) + ret = AXIS_PRECEDING_SIBLING; + break; + case 's': + if (xmlStrEqual(name, BAD_CAST "self")) + ret = AXIS_SELF; + break; + } + return(ret); +} + +/** + * xmlXPathCompStep: + * @ctxt: the XPath Parser context + * + * [4] Step ::= AxisSpecifier NodeTest Predicate* + * | AbbreviatedStep + * + * [12] AbbreviatedStep ::= '.' | '..' + * + * [5] AxisSpecifier ::= AxisName '::' + * | AbbreviatedAxisSpecifier + * + * [13] AbbreviatedAxisSpecifier ::= '@'? + * + * Modified for XPtr range support as: + * + * [4xptr] Step ::= AxisSpecifier NodeTest Predicate* + * | AbbreviatedStep + * | 'range-to' '(' Expr ')' Predicate* + * + * Compile one step in a Location Path + * A location step of . is short for self::node(). This is + * particularly useful in conjunction with //. For example, the + * location path .//para is short for + * self::node()/descendant-or-self::node()/child::para + * and so will select all para descendant elements of the context + * node. + * Similarly, a location step of .. is short for parent::node(). + * For example, ../title is short for parent::node()/child::title + * and so will select the title children of the parent of the context + * node. + */ +static void +xmlXPathCompStep(xmlXPathParserContextPtr ctxt) { +#ifdef LIBXML_XPTR_ENABLED + int rangeto = 0; + int op2 = -1; +#endif + + SKIP_BLANKS; + if ((CUR == '.') && (NXT(1) == '.')) { + SKIP(2); + SKIP_BLANKS; + PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT, + NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL); + } else if (CUR == '.') { + NEXT; + SKIP_BLANKS; + } else { + xmlChar *name = NULL; + const xmlChar *prefix = NULL; + xmlXPathTestVal test = (xmlXPathTestVal) 0; + xmlXPathAxisVal axis = (xmlXPathAxisVal) 0; + xmlXPathTypeVal type = (xmlXPathTypeVal) 0; + int op1; + + /* + * The modification needed for XPointer change to the production + */ +#ifdef LIBXML_XPTR_ENABLED + if (ctxt->xptr) { + name = xmlXPathParseNCName(ctxt); + if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) { + op2 = ctxt->comp->last; + xmlFree(name); + SKIP_BLANKS; + if (CUR != '(') { + XP_ERROR(XPATH_EXPR_ERROR); + } + NEXT; + SKIP_BLANKS; + + xmlXPathCompileExpr(ctxt, 1); + /* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */ + CHECK_ERROR; + + SKIP_BLANKS; + if (CUR != ')') { + XP_ERROR(XPATH_EXPR_ERROR); + } + NEXT; + rangeto = 1; + goto eval_predicates; + } + } +#endif + if (CUR == '*') { + axis = AXIS_CHILD; + } else { + if (name == NULL) + name = xmlXPathParseNCName(ctxt); + if (name != NULL) { + axis = xmlXPathIsAxisName(name); + if (axis != 0) { + SKIP_BLANKS; + if ((CUR == ':') && (NXT(1) == ':')) { + SKIP(2); + xmlFree(name); + name = NULL; + } else { + /* an element name can conflict with an axis one :-\ */ + axis = AXIS_CHILD; + } + } else { + axis = AXIS_CHILD; + } + } else if (CUR == '@') { + NEXT; + axis = AXIS_ATTRIBUTE; + } else { + axis = AXIS_CHILD; + } + } + + CHECK_ERROR; + + name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name); + if (test == 0) + return; + + if ((prefix != NULL) && (ctxt->context != NULL) && + (ctxt->context->flags & XML_XPATH_CHECKNS)) { + if (xmlXPathNsLookup(ctxt->context, prefix) == NULL) { + xmlXPathErr(ctxt, XPATH_UNDEF_PREFIX_ERROR); + } + } +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "Basis : computing new set\n"); +#endif + +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, "Basis : "); + if (ctxt->value == NULL) + xmlGenericError(xmlGenericErrorContext, "no value\n"); + else if (ctxt->value->nodesetval == NULL) + xmlGenericError(xmlGenericErrorContext, "Empty\n"); + else + xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval); +#endif + +#ifdef LIBXML_XPTR_ENABLED +eval_predicates: +#endif + op1 = ctxt->comp->last; + ctxt->comp->last = -1; + + SKIP_BLANKS; + while (CUR == '[') { + xmlXPathCompPredicate(ctxt, 0); + } + +#ifdef LIBXML_XPTR_ENABLED + if (rangeto) { + PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, op1, 0, 0); + } else +#endif + PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis, + test, type, (void *)prefix, (void *)name); + + } +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, "Step : "); + if (ctxt->value == NULL) + xmlGenericError(xmlGenericErrorContext, "no value\n"); + else if (ctxt->value->nodesetval == NULL) + xmlGenericError(xmlGenericErrorContext, "Empty\n"); + else + xmlGenericErrorContextNodeSet(xmlGenericErrorContext, + ctxt->value->nodesetval); +#endif +} + +/** + * xmlXPathCompRelativeLocationPath: + * @ctxt: the XPath Parser context + * + * [3] RelativeLocationPath ::= Step + * | RelativeLocationPath '/' Step + * | AbbreviatedRelativeLocationPath + * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step + * + * Compile a relative location path. + */ +static void +xmlXPathCompRelativeLocationPath +(xmlXPathParserContextPtr ctxt) { + SKIP_BLANKS; + if ((CUR == '/') && (NXT(1) == '/')) { + SKIP(2); + SKIP_BLANKS; + PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF, + NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL); + } else if (CUR == '/') { + NEXT; + SKIP_BLANKS; + } + xmlXPathCompStep(ctxt); + SKIP_BLANKS; + while (CUR == '/') { + if ((CUR == '/') && (NXT(1) == '/')) { + SKIP(2); + SKIP_BLANKS; + PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF, + NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL); + xmlXPathCompStep(ctxt); + } else if (CUR == '/') { + NEXT; + SKIP_BLANKS; + xmlXPathCompStep(ctxt); + } + SKIP_BLANKS; + } +} + +/** + * xmlXPathCompLocationPath: + * @ctxt: the XPath Parser context + * + * [1] LocationPath ::= RelativeLocationPath + * | AbsoluteLocationPath + * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath? + * | AbbreviatedAbsoluteLocationPath + * [10] AbbreviatedAbsoluteLocationPath ::= + * '//' RelativeLocationPath + * + * Compile a location path + * + * // is short for /descendant-or-self::node()/. For example, + * //para is short for /descendant-or-self::node()/child::para and + * so will select any para element in the document (even a para element + * that is a document element will be selected by //para since the + * document element node is a child of the root node); div//para is + * short for div/descendant-or-self::node()/child::para and so will + * select all para descendants of div children. + */ +static void +xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) { + SKIP_BLANKS; + if (CUR != '/') { + xmlXPathCompRelativeLocationPath(ctxt); + } else { + while (CUR == '/') { + if ((CUR == '/') && (NXT(1) == '/')) { + SKIP(2); + SKIP_BLANKS; + PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF, + NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL); + xmlXPathCompRelativeLocationPath(ctxt); + } else if (CUR == '/') { + NEXT; + SKIP_BLANKS; + if ((CUR != 0 ) && + ((IS_ASCII_LETTER(CUR)) || (CUR == '_') || (CUR == '.') || + (CUR == '@') || (CUR == '*'))) + xmlXPathCompRelativeLocationPath(ctxt); + } + } + } +} + +/************************************************************************ + * * + * XPath precompiled expression evaluation * + * * + ************************************************************************/ + +static int +xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op); + +/** + * xmlXPathNodeCollectAndTest: + * @ctxt: the XPath Parser context + * @op: the XPath precompiled step operation + * @first: pointer to the first element in document order + * @last: pointer to the last element in document order + * + * This is the function implementing a step: based on the current list + * of nodes, it builds up a new list, looking at all nodes under that + * axis and selecting them. It also does the predicate filtering + * + * Pushes the new NodeSet resulting from the search. + * + * Returns the number of nodes traversed + */ +static int +xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, + xmlXPathStepOpPtr op, + xmlNodePtr * first, xmlNodePtr * last) +{ + xmlXPathAxisVal axis = (xmlXPathAxisVal) op->value; + xmlXPathTestVal test = (xmlXPathTestVal) op->value2; + xmlXPathTypeVal type = (xmlXPathTypeVal) op->value3; + const xmlChar *prefix = op->value4; + const xmlChar *name = op->value5; + const xmlChar *URI = NULL; + +#ifdef DEBUG_STEP + int nbMatches = 0; +#endif + int inputIdx, total = 0, specialNodeInSet = 0; + xmlNodeSetPtr inputList, resultList, list; + xmlXPathTraversalFunction next = NULL; + xmlXPathTraversalFunctionExt compoundNext = NULL; + void (*addNode) (xmlNodeSetPtr, xmlNodePtr); + xmlNodeSetPtr (*mergeNodeSet) (xmlNodeSetPtr, xmlNodeSetPtr); + xmlNodePtr oldContextNode, contextNode, cur, compoundContextNode; + xmlXPathObjectPtr obj; + xmlXPathContextPtr xpctxt = ctxt->context; + + CHECK_TYPE0(XPATH_NODESET); + obj = valuePop(ctxt); + + /* + * Setup wrt namespaces. + */ + if (prefix != NULL) { + URI = xmlXPathNsLookup(xpctxt, prefix); + if (URI == NULL) { + xmlXPathFreeObject(obj); + XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR); + } + } + +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, "new step : "); +#endif + + /* + * Setup wrt the axis. + */ + addNode = xmlXPathNodeSetAdd; + mergeNodeSet = xmlXPathNodeSetMerge; + switch (axis) { + case AXIS_ANCESTOR: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' "); +#endif + first = NULL; + next = xmlXPathNextAncestor; + break; + case AXIS_ANCESTOR_OR_SELF: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "axis 'ancestors-or-self' "); +#endif + first = NULL; + next = xmlXPathNextAncestorOrSelf; + break; + case AXIS_ATTRIBUTE: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, "axis 'attributes' "); +#endif + first = NULL; + last = NULL; + next = xmlXPathNextAttribute; + mergeNodeSet = xmlXPathNodeSetMergeUnique; + break; + case AXIS_CHILD: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, "axis 'child' "); +#endif + last = NULL; + if (op->rewriteType == XP_REWRITE_DOS_CHILD_ELEM) { + /* + * This iterator will give us only nodes which can + * hold element nodes. + */ + compoundNext = xmlXPathNextDescendantOrSelfElemParent; + } + if ((test == NODE_TEST_NAME) && (type == NODE_TYPE_NODE)) { + /* + * Optimization if an element node type is 'element'. + */ + next = xmlXPathNextChildElement; + } else + next = xmlXPathNextChild; + mergeNodeSet = xmlXPathNodeSetMergeUnique; + break; + case AXIS_DESCENDANT: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, "axis 'descendant' "); +#endif + last = NULL; + next = xmlXPathNextDescendant; + break; + case AXIS_DESCENDANT_OR_SELF: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "axis 'descendant-or-self' "); +#endif + last = NULL; + next = xmlXPathNextDescendantOrSelf; + break; + case AXIS_FOLLOWING: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, "axis 'following' "); +#endif + last = NULL; + next = xmlXPathNextFollowing; + break; + case AXIS_FOLLOWING_SIBLING: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "axis 'following-siblings' "); +#endif + last = NULL; + next = xmlXPathNextFollowingSibling; + break; + case AXIS_NAMESPACE: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, "axis 'namespace' "); +#endif + first = NULL; + last = NULL; + next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; + mergeNodeSet = xmlXPathNodeSetMergeUnique; + break; + case AXIS_PARENT: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, "axis 'parent' "); +#endif + first = NULL; + next = xmlXPathNextParent; + break; + case AXIS_PRECEDING: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, "axis 'preceding' "); +#endif + first = NULL; + next = xmlXPathNextPrecedingInternal; + break; + case AXIS_PRECEDING_SIBLING: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "axis 'preceding-sibling' "); +#endif + first = NULL; + next = xmlXPathNextPrecedingSibling; + break; + case AXIS_SELF: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, "axis 'self' "); +#endif + first = NULL; + last = NULL; + next = xmlXPathNextSelf; + mergeNodeSet = xmlXPathNodeSetMergeUnique; + break; + } + if (next == NULL) { + xmlXPathReleaseObject(xpctxt, obj); + return(0); + } + + inputList = obj->nodesetval; + if ((inputList == NULL) || (inputList->nodeNr <= 0)) { + xmlXPathReleaseObject(xpctxt, obj); + valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, NULL)); + return(0); + } + +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + " context contains %d nodes\n", nodelist->nodeNr); + switch (test) { + case NODE_TEST_NONE: + xmlGenericError(xmlGenericErrorContext, + " searching for none !!!\n"); + break; + case NODE_TEST_TYPE: + xmlGenericError(xmlGenericErrorContext, + " searching for type %d\n", type); + break; + case NODE_TEST_PI: + xmlGenericError(xmlGenericErrorContext, + " searching for PI !!!\n"); + break; + case NODE_TEST_ALL: + xmlGenericError(xmlGenericErrorContext, + " searching for *\n"); + break; + case NODE_TEST_NS: + xmlGenericError(xmlGenericErrorContext, + " searching for namespace %s\n", + prefix); + break; + case NODE_TEST_NAME: + xmlGenericError(xmlGenericErrorContext, + " searching for name %s\n", name); + if (prefix != NULL) + xmlGenericError(xmlGenericErrorContext, + " with namespace %s\n", prefix); + break; + } + xmlGenericError(xmlGenericErrorContext, "Testing : "); +#endif + /* + * 2.3 Node Tests + * - For the attribute axis, the principal node type is attribute. + * - For the namespace axis, the principal node type is namespace. + * - For other axes, the principal node type is element. + * + * A node test * is true for any node of the + * principal node type. For example, child::* will + * select all element children of the context node + */ + oldContextNode = xpctxt->node; + addNode = xmlXPathNodeSetAddUnique; + resultList = NULL; + list = NULL; + compoundContextNode = NULL; + contextNode = NULL; + inputIdx = 0; + + while ((inputIdx < inputList->nodeNr) || (contextNode != NULL)) { + if (compoundNext != NULL) { + /* + * This is a compound traversal. + */ + if (contextNode == NULL) { + /* + * Set the context for the initial traversal. + */ + compoundContextNode = inputList->nodeTab[inputIdx++]; + contextNode = compoundNext(NULL, compoundContextNode); + } else + contextNode = compoundNext(contextNode, compoundContextNode); + if (contextNode == NULL) + continue; + /* + * Set the context for the main traversal. + */ + xpctxt->node = contextNode; + } else + xpctxt->node = inputList->nodeTab[inputIdx++]; + + if (list == NULL) { + list = xmlXPathNodeSetCreate(NULL); + if (list == NULL) { + total = 0; + goto error; + } + } + cur = NULL; + specialNodeInSet = 0; + do { + cur = next(ctxt, cur); + if (cur == NULL) + break; + + if (first != NULL) { + if (*first == cur) + break; + if ((*first != NULL) && + ((total % 256) == 0) && +#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON + (xmlXPathCmpNodesExt(*first, cur) >= 0)) +#else + (xmlXPathCmpNodes(*first, cur) >= 0)) +#endif + { + break; + } + } + if (last != NULL) { + if (*last == cur) + break; + if ((*last != NULL) && + ((total % 256) == 0) && +#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON + (xmlXPathCmpNodesExt(cur, *last) >= 0)) +#else + (xmlXPathCmpNodes(cur, *last) >= 0)) +#endif + { + break; + } + } + + total++; +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, " %s", cur->name); +#endif + switch (test) { + case NODE_TEST_NONE: + STRANGE + goto error; + case NODE_TEST_TYPE: + if ((cur->type == type) || + ((type == NODE_TYPE_NODE) && + ((cur->type == XML_DOCUMENT_NODE) || + (cur->type == XML_HTML_DOCUMENT_NODE) || + (cur->type == XML_ELEMENT_NODE) || + (cur->type == XML_NAMESPACE_DECL) || + (cur->type == XML_ATTRIBUTE_NODE) || + (cur->type == XML_PI_NODE) || + (cur->type == XML_COMMENT_NODE) || + (cur->type == XML_CDATA_SECTION_NODE) || + (cur->type == XML_TEXT_NODE))) || + ((type == NODE_TYPE_TEXT) && + (cur->type == XML_CDATA_SECTION_NODE))) + { +#ifdef DEBUG_STEP + nbMatches++; +#endif + if (cur->type == XML_NAMESPACE_DECL) + specialNodeInSet = 1; + /* + * TODO: Don't we need to use xmlXPathNodeSetAddNs() + * for namespace nodes here ? + */ + addNode(list, cur); + } + break; + case NODE_TEST_PI: + if ((cur->type == XML_PI_NODE) && + ((name == NULL) || xmlStrEqual(name, cur->name))) + { +#ifdef DEBUG_STEP + nbMatches++; +#endif + addNode(list, cur); + } + break; + case NODE_TEST_ALL: + if (axis == AXIS_ATTRIBUTE) { + if (cur->type == XML_ATTRIBUTE_NODE) { +#ifdef DEBUG_STEP + nbMatches++; +#endif + addNode(list, cur); + } + } else if (axis == AXIS_NAMESPACE) { + if (cur->type == XML_NAMESPACE_DECL) { +#ifdef DEBUG_STEP + nbMatches++; +#endif + specialNodeInSet = 1; + xmlXPathNodeSetAddNs(list, xpctxt->node, + (xmlNsPtr) cur); + } + } else { + if (cur->type == XML_ELEMENT_NODE) { + if (prefix == NULL) { +#ifdef DEBUG_STEP + nbMatches++; +#endif + addNode(list, cur); + } else if ((cur->ns != NULL) && + (xmlStrEqual(URI, cur->ns->href))) + { +#ifdef DEBUG_STEP + nbMatches++; +#endif + addNode(list, cur); + } + } + } + break; + case NODE_TEST_NS:{ + TODO; + break; + } + case NODE_TEST_NAME: + switch (cur->type) { + case XML_ELEMENT_NODE: + if (xmlStrEqual(name, cur->name)) { + if (prefix == NULL) { + if (cur->ns == NULL) { +#ifdef DEBUG_STEP + nbMatches++; +#endif + addNode(list, cur); + } + } else { + if ((cur->ns != NULL) && + (xmlStrEqual(URI, + cur->ns->href))) + { +#ifdef DEBUG_STEP + nbMatches++; +#endif + addNode(list, cur); + } + } + } + break; + case XML_ATTRIBUTE_NODE:{ + xmlAttrPtr attr = (xmlAttrPtr) cur; + + if (xmlStrEqual(name, attr->name)) { + if (prefix == NULL) { + if ((attr->ns == NULL) || + (attr->ns->prefix == NULL)) { +#ifdef DEBUG_STEP + nbMatches++; +#endif + addNode(list, + (xmlNodePtr) attr); + } + } else { + if ((attr->ns != NULL) && + (xmlStrEqual(URI, + attr->ns-> + href))) + { +#ifdef DEBUG_STEP + nbMatches++; +#endif + addNode(list, + (xmlNodePtr) attr); + } + } + } + break; + } + case XML_NAMESPACE_DECL: + if (cur->type == XML_NAMESPACE_DECL) { + xmlNsPtr ns = (xmlNsPtr) cur; + + if ((ns->prefix != NULL) && (name != NULL) + && (xmlStrEqual(ns->prefix, name))) + { +#ifdef DEBUG_STEP + nbMatches++; +#endif + specialNodeInSet = 1; + xmlXPathNodeSetAddNs(list, + xpctxt->node, (xmlNsPtr) cur); + } + } + break; + default: + break; + } /* switch (cur->type) */ + break; /* case NODE_TEST_NAME: */ + } /* switch (test) */ + } while (cur != NULL); + + /* + * If there is some predicate filtering do it now + */ + if ((op->ch2 != -1) && (list != NULL) && (list->nodeNr > 0)) { + xmlXPathObjectPtr obj2; + + valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, list)); + xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch2]); + CHECK_TYPE0(XPATH_NODESET); + obj2 = valuePop(ctxt); + list = obj2->nodesetval; + obj2->nodesetval = NULL; + xmlXPathReleaseObject(xpctxt, obj2); + + if (ctxt->error != XPATH_EXPRESSION_OK) { + total = 0; + goto error; + } + } + if (resultList == NULL) { + resultList = list; + list = NULL; + } else if ((list != NULL) && (list->nodeNr > 0)) { + resultList = mergeNodeSet(resultList, list); + /* + * This is the list containing the current matching nodes. + * Avoid massive creation/freeing and preserve it for the + * next iterations. + */ + /* If a namespace node was put it, then we need a more + * time consuming cleanup. + */ + if (specialNodeInSet) + xmlXPathNodeSetClear(list); + else + list->nodeNr = 0; + } + } + + xpctxt->node = oldContextNode; + /* + * Cleanup the temporary list of current node-test matches. + */ + if ((list != NULL) && (list != resultList)) { + xmlXPathFreeNodeSet(list); + list = NULL; + } + +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "\nExamined %d nodes, found %d nodes at that step\n", + total, nbMatches); +#endif + + valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, resultList)); + + if ((obj->boolval) && (obj->user != NULL)) { + /* + * QUESTION TODO: What does this do and why? + */ + ctxt->value->boolval = 1; + ctxt->value->user = obj->user; + obj->user = NULL; + obj->boolval = 0; + } + xmlXPathReleaseObject(xpctxt, obj); + return(total); + +error: + xpctxt->node = oldContextNode; + xmlXPathReleaseObject(xpctxt, obj); + if ((list != NULL) && (list != resultList)) { + xmlXPathFreeNodeSet(list); + } + if (resultList != NULL) + xmlXPathFreeNodeSet(resultList); + return(total); +} + +/** + * xmlXPathNodeCollectAndTestNth: + * @ctxt: the XPath Parser context + * @op: the XPath precompiled step operation + * @reqpos: the requested position wrt to the axis + * @first: pointer to the first element in document order + * @last: pointer to the last element in document order + * + * This is the function implementing a step: based on the current list + * of nodes, it builds up a new list, looking at all nodes under that + * axis and selecting them. It also does the predicate filtering + * + * Pushes the new NodeSet resulting from the search. + * Returns the number of node traversed + */ +static int +xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt, + xmlXPathStepOpPtr op, int reqpos, + xmlNodePtr * first, xmlNodePtr * last) +{ + xmlXPathAxisVal axis = (xmlXPathAxisVal) op->value; + xmlXPathTestVal test = (xmlXPathTestVal) op->value2; + xmlXPathTypeVal type = (xmlXPathTypeVal) op->value3; + const xmlChar *prefix = op->value4; + const xmlChar *name = op->value5; + const xmlChar *URI = NULL; + int pos; /* The current context position */ + + int inputIdx, total = 0; + xmlNodeSetPtr inputList, list; + xmlXPathTraversalFunction next = NULL; + xmlXPathTraversalFunctionExt compoundNext = NULL; + void (*addNode) (xmlNodeSetPtr, xmlNodePtr); + xmlNodePtr oldContextNode, contextNode, cur, compoundContextNode; + xmlXPathObjectPtr obj; + xmlXPathContextPtr xpctxt = ctxt->context; + + + CHECK_TYPE0(XPATH_NODESET); + obj = valuePop(ctxt); + addNode = xmlXPathNodeSetAdd; + + if (prefix != NULL) { + URI = xmlXPathNsLookup(xpctxt, prefix); + if (URI == NULL) { + xmlXPathFreeObject(obj); + XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR); + } + } + +#ifdef DEBUG_STEP_NTH + xmlGenericError(xmlGenericErrorContext, "new step : "); + if (first != NULL) { + if (*first != NULL) + xmlGenericError(xmlGenericErrorContext, "first = %s ", + (*first)->name); + else + xmlGenericError(xmlGenericErrorContext, "first = NULL "); + } + if (last != NULL) { + if (*last != NULL) + xmlGenericError(xmlGenericErrorContext, "last = %s ", + (*last)->name); + else + xmlGenericError(xmlGenericErrorContext, "last = NULL "); + } +#endif + switch (axis) { + case AXIS_ANCESTOR: +#ifdef DEBUG_STEP_NTH + xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' "); +#endif + first = NULL; + next = xmlXPathNextAncestor; + break; + case AXIS_ANCESTOR_OR_SELF: +#ifdef DEBUG_STEP_NTH + xmlGenericError(xmlGenericErrorContext, + "axis 'ancestors-or-self' "); +#endif + first = NULL; + next = xmlXPathNextAncestorOrSelf; + break; + case AXIS_ATTRIBUTE: +#ifdef DEBUG_STEP_NTH + xmlGenericError(xmlGenericErrorContext, "axis 'attributes' "); +#endif + first = NULL; + last = NULL; + next = xmlXPathNextAttribute; + break; + case AXIS_CHILD: +#ifdef DEBUG_STEP_NTH + xmlGenericError(xmlGenericErrorContext, "axis 'child' "); +#endif + last = NULL; + if (op->rewriteType == XP_REWRITE_DOS_CHILD_ELEM) { + /* + * This iterator will give us only nodes which can + * hold element nodes. + */ + compoundNext = xmlXPathNextDescendantOrSelfElemParent; + } + if ((test == NODE_TEST_NAME) && (type == NODE_TYPE_NODE)) { + /* + * Optimization if an element node type is 'element'. + */ + next = xmlXPathNextChildElement; + } else + next = xmlXPathNextChild; + break; + case AXIS_DESCENDANT: +#ifdef DEBUG_STEP_NTH + xmlGenericError(xmlGenericErrorContext, "axis 'descendant' "); +#endif + last = NULL; + next = xmlXPathNextDescendant; + break; + case AXIS_DESCENDANT_OR_SELF: +#ifdef DEBUG_STEP_NTH + xmlGenericError(xmlGenericErrorContext, + "axis 'descendant-or-self' "); +#endif + last = NULL; + next = xmlXPathNextDescendantOrSelf; + break; + case AXIS_FOLLOWING: +#ifdef DEBUG_STEP_NTH + xmlGenericError(xmlGenericErrorContext, "axis 'following' "); +#endif + last = NULL; + next = xmlXPathNextFollowing; + break; + case AXIS_FOLLOWING_SIBLING: +#ifdef DEBUG_STEP_NTH + xmlGenericError(xmlGenericErrorContext, + "axis 'following-siblings' "); +#endif + last = NULL; + next = xmlXPathNextFollowingSibling; + break; + case AXIS_NAMESPACE: +#ifdef DEBUG_STEP_NTH + xmlGenericError(xmlGenericErrorContext, "axis 'namespace' "); +#endif + last = NULL; + first = NULL; + next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; + break; + case AXIS_PARENT: +#ifdef DEBUG_STEP_NTH + xmlGenericError(xmlGenericErrorContext, "axis 'parent' "); +#endif + first = NULL; + next = xmlXPathNextParent; + break; + case AXIS_PRECEDING: +#ifdef DEBUG_STEP_NTH + xmlGenericError(xmlGenericErrorContext, "axis 'preceding' "); +#endif + first = NULL; + next = xmlXPathNextPrecedingInternal; + break; + case AXIS_PRECEDING_SIBLING: +#ifdef DEBUG_STEP_NTH + xmlGenericError(xmlGenericErrorContext, + "axis 'preceding-sibling' "); +#endif + first = NULL; + next = xmlXPathNextPrecedingSibling; + break; + case AXIS_SELF: +#ifdef DEBUG_STEP_NTH + xmlGenericError(xmlGenericErrorContext, "axis 'self' "); +#endif + first = NULL; + last = NULL; + next = xmlXPathNextSelf; + break; + } + if (next == NULL) { + xmlXPathReleaseObject(xpctxt, obj); + return(0); + } + + inputList = obj->nodesetval; + if ((inputList == NULL) || (inputList->nodeNr <= 0)) { + xmlXPathReleaseObject(xpctxt, obj); + valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, NULL)); + return(0); + } + +#ifdef DEBUG_STEP_NTH + xmlGenericError(xmlGenericErrorContext, + " context contains %d nodes\n", nodelist->nodeNr); + switch (test) { + case NODE_TEST_NONE: + xmlGenericError(xmlGenericErrorContext, + " searching for none !!!\n"); + break; + case NODE_TEST_TYPE: + xmlGenericError(xmlGenericErrorContext, + " searching for type %d\n", type); + break; + case NODE_TEST_PI: + xmlGenericError(xmlGenericErrorContext, + " searching for PI !!!\n"); + break; + case NODE_TEST_ALL: + xmlGenericError(xmlGenericErrorContext, + " searching for *\n"); + break; + case NODE_TEST_NS: + xmlGenericError(xmlGenericErrorContext, + " searching for namespace %s\n", + prefix); + break; + case NODE_TEST_NAME: + xmlGenericError(xmlGenericErrorContext, + " searching for name %s\n", name); + if (prefix != NULL) + xmlGenericError(xmlGenericErrorContext, + " with namespace %s\n", prefix); + break; + } + xmlGenericError(xmlGenericErrorContext, "Testing : "); +#endif + /* + * 2.3 Node Tests + * - For the attribute axis, the principal node type is attribute. + * - For the namespace axis, the principal node type is namespace. + * - For other axes, the principal node type is element. + * + * A node test * is true for any node of the + * principal node type. For example, child::* will + * select all element children of the context node + */ + oldContextNode = xpctxt->node; + addNode = xmlXPathNodeSetAddUnique; + list = NULL; + compoundContextNode = NULL; + contextNode = NULL; + inputIdx = 0; + list = xmlXPathNodeSetCreate(NULL); + + while ((inputIdx < inputList->nodeNr) || (contextNode != NULL)) { + if (compoundNext != NULL) { + /* + * This is a compound traversal. + */ + if (contextNode == NULL) { + /* + * Set the context for the initial traversal. + */ + compoundContextNode = inputList->nodeTab[inputIdx++]; + contextNode = compoundNext(NULL, compoundContextNode); + } else + contextNode = compoundNext(contextNode, compoundContextNode); + if (contextNode == NULL) + continue; + /* + * Set the context for the main traversal. + */ + xpctxt->node = contextNode; + } else + xpctxt->node = inputList->nodeTab[inputIdx++]; + + cur = NULL; + pos = 0; + do { + cur = next(ctxt, cur); + if (cur == NULL) + break; + + if (first != NULL) { + if (*first == cur) + break; + if ((*first != NULL) && + ((total % 256) == 0) && +#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON + (xmlXPathCmpNodesExt(*first, cur) >= 0)) +#else + (xmlXPathCmpNodes(*first, cur) >= 0)) +#endif + { + break; + } + } + if (last != NULL) { + if (*last == cur) + break; + if ((*last != NULL) && + ((total % 256) == 0) && +#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON + (xmlXPathCmpNodesExt(cur, *last) >= 0)) +#else + (xmlXPathCmpNodes(cur, *last) >= 0)) +#endif + { + break; + } + } + + total++; + switch (test) { + case NODE_TEST_NONE: + total = 0; + STRANGE + goto error; + case NODE_TEST_TYPE: + if ((cur->type == type) || + ((type == NODE_TYPE_NODE) && + ((cur->type == XML_DOCUMENT_NODE) || + (cur->type == XML_HTML_DOCUMENT_NODE) || + (cur->type == XML_ELEMENT_NODE) || + (cur->type == XML_PI_NODE) || + (cur->type == XML_COMMENT_NODE) || + (cur->type == XML_CDATA_SECTION_NODE) || + (cur->type == XML_TEXT_NODE))) || + ((type == NODE_TYPE_TEXT) && + (cur->type == XML_CDATA_SECTION_NODE))) { + pos++; + if (pos == reqpos) + addNode(list, cur); + } + break; + case NODE_TEST_PI: + if (cur->type == XML_PI_NODE) { + if ((name != NULL) && + (!xmlStrEqual(name, cur->name))) + break; + pos++; + if (pos == reqpos) + addNode(list, cur); + } + break; + case NODE_TEST_ALL: + if (axis == AXIS_ATTRIBUTE) { + if (cur->type == XML_ATTRIBUTE_NODE) { + pos++; + if (pos == reqpos) + addNode(list, cur); + } + } else if (axis == AXIS_NAMESPACE) { + if (cur->type == XML_NAMESPACE_DECL) { + pos++; + if (pos == reqpos) + xmlXPathNodeSetAddNs(list, xpctxt->node, + (xmlNsPtr) cur); + } + } else { + if (cur->type == XML_ELEMENT_NODE) { + if (prefix == NULL) { + pos++; + if (pos == reqpos) + addNode(list, cur); + } else if ((cur->ns != NULL) && + (xmlStrEqual(URI, cur->ns->href))) { + pos++; + if (pos == reqpos) + addNode(list, cur); + } + } + } + break; + case NODE_TEST_NS:{ + TODO; + break; + } + case NODE_TEST_NAME: + switch (cur->type) { + case XML_ELEMENT_NODE: + if (xmlStrEqual(name, cur->name)) { + if (prefix == NULL) { + if (cur->ns == NULL) { + pos++; + if (pos == reqpos) + addNode(list, cur); + } + } else { + if ((cur->ns != NULL) && + (xmlStrEqual(URI, + cur->ns->href))) + { + pos++; + if (pos == reqpos) + addNode(list, cur); + } + } + } + break; + case XML_ATTRIBUTE_NODE:{ + xmlAttrPtr attr = (xmlAttrPtr) cur; + + if (xmlStrEqual(name, attr->name)) { + if (prefix == NULL) { + if ((attr->ns == NULL) || + (attr->ns->prefix == NULL)) + { + pos++; + if (pos == reqpos) + addNode(list, cur); + } + } else { + if ((attr->ns != NULL) && + (xmlStrEqual(URI, + attr->ns->href))) + { + pos++; + if (pos == reqpos) + addNode(list, cur); + } + } + } + break; + } + case XML_NAMESPACE_DECL: + if (cur->type == XML_NAMESPACE_DECL) { + xmlNsPtr ns = (xmlNsPtr) cur; + + if ((ns->prefix != NULL) && (name != NULL) + && (xmlStrEqual(ns->prefix, name))) { + pos++; + if (pos == reqpos) + xmlXPathNodeSetAddNs(list, + xpctxt->node, (xmlNsPtr) cur); + } + } + break; + default: + break; + } + break; + } + } while (pos < reqpos); + } + xpctxt->node = oldContextNode; + +#ifdef DEBUG_STEP_NTH + xmlGenericError(xmlGenericErrorContext, + "\nExamined %d nodes, found %d nodes at that step\n", + total, list->nodeNr); +#endif + + valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, list)); + + if ((obj->boolval) && (obj->user != NULL)) { + ctxt->value->boolval = 1; + ctxt->value->user = obj->user; + obj->user = NULL; + obj->boolval = 0; + } + xmlXPathReleaseObject(xpctxt, obj); + return(total); + +error: + xpctxt->node = oldContextNode; + xmlXPathReleaseObject(xpctxt, obj); + if (list != NULL) + xmlXPathFreeNodeSet(list); + return(total); +} + +static int +xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt, + xmlXPathStepOpPtr op, xmlNodePtr * first); + +/** + * xmlXPathCompOpEvalFirst: + * @ctxt: the XPath parser context with the compiled expression + * @op: an XPath compiled operation + * @first: the first elem found so far + * + * Evaluate the Precompiled XPath operation searching only the first + * element in document order + * + * Returns the number of examined objects. + */ +static int +xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt, + xmlXPathStepOpPtr op, xmlNodePtr * first) +{ + int total = 0, cur; + xmlXPathCompExprPtr comp; + xmlXPathObjectPtr arg1, arg2; + + CHECK_ERROR0; + comp = ctxt->comp; + switch (op->op) { + case XPATH_OP_END: + return (0); + case XPATH_OP_UNION: + total = + xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1], + first); + CHECK_ERROR0; + if ((ctxt->value != NULL) + && (ctxt->value->type == XPATH_NODESET) + && (ctxt->value->nodesetval != NULL) + && (ctxt->value->nodesetval->nodeNr >= 1)) { + /* + * limit tree traversing to first node in the result + */ + /* + * OPTIMIZE TODO: This implicitely sorts + * the result, even if not needed. E.g. if the argument + * of the count() function, no sorting is needed. + * OPTIMIZE TODO: How do we know if the node-list wasn't + * aready sorted? + */ + if (ctxt->value->nodesetval->nodeNr > 1) + xmlXPathNodeSetSort(ctxt->value->nodesetval); + *first = ctxt->value->nodesetval->nodeTab[0]; + } + cur = + xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch2], + first); + CHECK_ERROR0; + CHECK_TYPE0(XPATH_NODESET); + arg2 = valuePop(ctxt); + + CHECK_TYPE0(XPATH_NODESET); + arg1 = valuePop(ctxt); + + arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval, + arg2->nodesetval); + valuePush(ctxt, arg1); + xmlXPathReleaseObject(ctxt->context, arg2); + /* optimizer */ + if (total > cur) + xmlXPathCompSwap(op); + return (total + cur); + case XPATH_OP_ROOT: + xmlXPathRoot(ctxt); + return (0); + case XPATH_OP_NODE: + if (op->ch1 != -1) + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + CHECK_ERROR0; + if (op->ch2 != -1) + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); + CHECK_ERROR0; + valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node)); + return (total); + case XPATH_OP_RESET: + if (op->ch1 != -1) + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + CHECK_ERROR0; + if (op->ch2 != -1) + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); + CHECK_ERROR0; + ctxt->context->node = NULL; + return (total); + case XPATH_OP_COLLECT:{ + if (op->ch1 == -1) + return (total); + + total = xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + CHECK_ERROR0; + + /* + * Optimization for [n] selection where n is a number + */ + if ((op->ch2 != -1) && + (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) && + (comp->steps[op->ch2].ch1 == -1) && + (comp->steps[op->ch2].ch2 != -1) && + (comp->steps[comp->steps[op->ch2].ch2].op == + XPATH_OP_VALUE)) { + xmlXPathObjectPtr val; + + val = comp->steps[comp->steps[op->ch2].ch2].value4; + if ((val != NULL) && (val->type == XPATH_NUMBER)) { + int indx = (int) val->floatval; + + if (val->floatval == (float) indx) { + xmlXPathNodeCollectAndTestNth(ctxt, op, indx, + first, NULL); + return (total); + } + } + } + total += xmlXPathNodeCollectAndTest(ctxt, op, first, NULL); + return (total); + } + case XPATH_OP_VALUE: + valuePush(ctxt, + xmlXPathCacheObjectCopy(ctxt->context, + (xmlXPathObjectPtr) op->value4)); + return (0); + case XPATH_OP_SORT: + if (op->ch1 != -1) + total += + xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1], + first); + CHECK_ERROR0; + if ((ctxt->value != NULL) + && (ctxt->value->type == XPATH_NODESET) + && (ctxt->value->nodesetval != NULL) + && (ctxt->value->nodesetval->nodeNr > 1)) + xmlXPathNodeSetSort(ctxt->value->nodesetval); + return (total); +#ifdef XP_OPTIMIZED_FILTER_FIRST + case XPATH_OP_FILTER: + total =+ xmlXPathCompOpEvalFilterFirst(ctxt, op, first); + return (total); +#endif + default: + return (xmlXPathCompOpEval(ctxt, op)); + } +} + +/** + * xmlXPathCompOpEvalLast: + * @ctxt: the XPath parser context with the compiled expression + * @op: an XPath compiled operation + * @last: the last elem found so far + * + * Evaluate the Precompiled XPath operation searching only the last + * element in document order + * + * Returns the number of nodes traversed + */ +static int +xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op, + xmlNodePtr * last) +{ + int total = 0, cur; + xmlXPathCompExprPtr comp; + xmlXPathObjectPtr arg1, arg2; + xmlNodePtr bak; + xmlDocPtr bakd; + int pp; + int cs; + + CHECK_ERROR0; + comp = ctxt->comp; + switch (op->op) { + case XPATH_OP_END: + return (0); + case XPATH_OP_UNION: + bakd = ctxt->context->doc; + bak = ctxt->context->node; + pp = ctxt->context->proximityPosition; + cs = ctxt->context->contextSize; + total = + xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], last); + CHECK_ERROR0; + if ((ctxt->value != NULL) + && (ctxt->value->type == XPATH_NODESET) + && (ctxt->value->nodesetval != NULL) + && (ctxt->value->nodesetval->nodeNr >= 1)) { + /* + * limit tree traversing to first node in the result + */ + if (ctxt->value->nodesetval->nodeNr > 1) + xmlXPathNodeSetSort(ctxt->value->nodesetval); + *last = + ctxt->value->nodesetval->nodeTab[ctxt->value-> + nodesetval->nodeNr - + 1]; + } + ctxt->context->doc = bakd; + ctxt->context->node = bak; + ctxt->context->proximityPosition = pp; + ctxt->context->contextSize = cs; + cur = + xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch2], last); + CHECK_ERROR0; + if ((ctxt->value != NULL) + && (ctxt->value->type == XPATH_NODESET) + && (ctxt->value->nodesetval != NULL) + && (ctxt->value->nodesetval->nodeNr >= 1)) { /* TODO: NOP ? */ + } + CHECK_TYPE0(XPATH_NODESET); + arg2 = valuePop(ctxt); + + CHECK_TYPE0(XPATH_NODESET); + arg1 = valuePop(ctxt); + + arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval, + arg2->nodesetval); + valuePush(ctxt, arg1); + xmlXPathReleaseObject(ctxt->context, arg2); + /* optimizer */ + if (total > cur) + xmlXPathCompSwap(op); + return (total + cur); + case XPATH_OP_ROOT: + xmlXPathRoot(ctxt); + return (0); + case XPATH_OP_NODE: + if (op->ch1 != -1) + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + CHECK_ERROR0; + if (op->ch2 != -1) + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); + CHECK_ERROR0; + valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node)); + return (total); + case XPATH_OP_RESET: + if (op->ch1 != -1) + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + CHECK_ERROR0; + if (op->ch2 != -1) + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); + CHECK_ERROR0; + ctxt->context->node = NULL; + return (total); + case XPATH_OP_COLLECT:{ + if (op->ch1 == -1) + return (0); + + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + CHECK_ERROR0; + + /* + * Optimization for [n] selection where n is a number + */ + if ((op->ch2 != -1) && + (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) && + (comp->steps[op->ch2].ch1 == -1) && + (comp->steps[op->ch2].ch2 != -1) && + (comp->steps[comp->steps[op->ch2].ch2].op == + XPATH_OP_VALUE)) { + xmlXPathObjectPtr val; + + val = comp->steps[comp->steps[op->ch2].ch2].value4; + if ((val != NULL) && (val->type == XPATH_NUMBER)) { + int indx = (int) val->floatval; + + if (val->floatval == (float) indx) { + total += + xmlXPathNodeCollectAndTestNth(ctxt, op, + indx, NULL, + last); + return (total); + } + } + } + total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, last); + return (total); + } + case XPATH_OP_VALUE: + valuePush(ctxt, + xmlXPathCacheObjectCopy(ctxt->context, + (xmlXPathObjectPtr) op->value4)); + return (0); + case XPATH_OP_SORT: + if (op->ch1 != -1) + total += + xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], + last); + CHECK_ERROR0; + if ((ctxt->value != NULL) + && (ctxt->value->type == XPATH_NODESET) + && (ctxt->value->nodesetval != NULL) + && (ctxt->value->nodesetval->nodeNr > 1)) + xmlXPathNodeSetSort(ctxt->value->nodesetval); + return (total); + default: + return (xmlXPathCompOpEval(ctxt, op)); + } +} + +#ifdef XP_OPTIMIZED_FILTER_FIRST +static int +xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt, + xmlXPathStepOpPtr op, xmlNodePtr * first) +{ + int total = 0; + xmlXPathCompExprPtr comp; + xmlXPathObjectPtr res; + xmlXPathObjectPtr obj; + xmlNodeSetPtr oldset; + xmlNodePtr oldnode; + xmlDocPtr oldDoc; + int i; + + CHECK_ERROR0; + comp = ctxt->comp; + /* + * Optimization for ()[last()] selection i.e. the last elem + */ + if ((op->ch1 != -1) && (op->ch2 != -1) && + (comp->steps[op->ch1].op == XPATH_OP_SORT) && + (comp->steps[op->ch2].op == XPATH_OP_SORT)) { + int f = comp->steps[op->ch2].ch1; + + if ((f != -1) && + (comp->steps[f].op == XPATH_OP_FUNCTION) && + (comp->steps[f].value5 == NULL) && + (comp->steps[f].value == 0) && + (comp->steps[f].value4 != NULL) && + (xmlStrEqual + (comp->steps[f].value4, BAD_CAST "last"))) { + xmlNodePtr last = NULL; + + total += + xmlXPathCompOpEvalLast(ctxt, + &comp->steps[op->ch1], + &last); + CHECK_ERROR0; + /* + * The nodeset should be in document order, + * Keep only the last value + */ + if ((ctxt->value != NULL) && + (ctxt->value->type == XPATH_NODESET) && + (ctxt->value->nodesetval != NULL) && + (ctxt->value->nodesetval->nodeTab != NULL) && + (ctxt->value->nodesetval->nodeNr > 1)) { + ctxt->value->nodesetval->nodeTab[0] = + ctxt->value->nodesetval->nodeTab[ctxt-> + value-> + nodesetval-> + nodeNr - + 1]; + ctxt->value->nodesetval->nodeNr = 1; + *first = *(ctxt->value->nodesetval->nodeTab); + } + return (total); + } + } + + if (op->ch1 != -1) + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + CHECK_ERROR0; + if (op->ch2 == -1) + return (total); + if (ctxt->value == NULL) + return (total); + +#ifdef LIBXML_XPTR_ENABLED + oldnode = ctxt->context->node; + /* + * Hum are we filtering the result of an XPointer expression + */ + if (ctxt->value->type == XPATH_LOCATIONSET) { + xmlXPathObjectPtr tmp = NULL; + xmlLocationSetPtr newlocset = NULL; + xmlLocationSetPtr oldlocset; + + /* + * Extract the old locset, and then evaluate the result of the + * expression for all the element in the locset. use it to grow + * up a new locset. + */ + CHECK_TYPE0(XPATH_LOCATIONSET); + obj = valuePop(ctxt); + oldlocset = obj->user; + ctxt->context->node = NULL; + + if ((oldlocset == NULL) || (oldlocset->locNr == 0)) { + ctxt->context->contextSize = 0; + ctxt->context->proximityPosition = 0; + if (op->ch2 != -1) + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); + res = valuePop(ctxt); + if (res != NULL) { + xmlXPathReleaseObject(ctxt->context, res); + } + valuePush(ctxt, obj); + CHECK_ERROR0; + return (total); + } + newlocset = xmlXPtrLocationSetCreate(NULL); + + for (i = 0; i < oldlocset->locNr; i++) { + /* + * Run the evaluation with a node list made of a + * single item in the nodelocset. + */ + ctxt->context->node = oldlocset->locTab[i]->user; + ctxt->context->contextSize = oldlocset->locNr; + ctxt->context->proximityPosition = i + 1; + if (tmp == NULL) { + tmp = xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node); + } else { + xmlXPathNodeSetAddUnique(tmp->nodesetval, + ctxt->context->node); + } + valuePush(ctxt, tmp); + if (op->ch2 != -1) + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); + if (ctxt->error != XPATH_EXPRESSION_OK) { + xmlXPathFreeObject(obj); + return(0); + } + /* + * The result of the evaluation need to be tested to + * decided whether the filter succeeded or not + */ + res = valuePop(ctxt); + if (xmlXPathEvaluatePredicateResult(ctxt, res)) { + xmlXPtrLocationSetAdd(newlocset, + xmlXPathCacheObjectCopy(ctxt->context, + oldlocset->locTab[i])); + } + /* + * Cleanup + */ + if (res != NULL) { + xmlXPathReleaseObject(ctxt->context, res); + } + if (ctxt->value == tmp) { + valuePop(ctxt); + xmlXPathNodeSetClear(tmp->nodesetval); + /* + * REVISIT TODO: Don't create a temporary nodeset + * for everly iteration. + */ + /* OLD: xmlXPathFreeObject(res); */ + } else + tmp = NULL; + ctxt->context->node = NULL; + /* + * Only put the first node in the result, then leave. + */ + if (newlocset->locNr > 0) { + *first = (xmlNodePtr) oldlocset->locTab[i]->user; + break; + } + } + if (tmp != NULL) { + xmlXPathReleaseObject(ctxt->context, tmp); + } + /* + * The result is used as the new evaluation locset. + */ + xmlXPathReleaseObject(ctxt->context, obj); + ctxt->context->node = NULL; + ctxt->context->contextSize = -1; + ctxt->context->proximityPosition = -1; + valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset)); + ctxt->context->node = oldnode; + return (total); + } +#endif /* LIBXML_XPTR_ENABLED */ + + /* + * Extract the old set, and then evaluate the result of the + * expression for all the element in the set. use it to grow + * up a new set. + */ + CHECK_TYPE0(XPATH_NODESET); + obj = valuePop(ctxt); + oldset = obj->nodesetval; + + oldnode = ctxt->context->node; + oldDoc = ctxt->context->doc; + ctxt->context->node = NULL; + + if ((oldset == NULL) || (oldset->nodeNr == 0)) { + ctxt->context->contextSize = 0; + ctxt->context->proximityPosition = 0; + /* QUESTION TODO: Why was this code commented out? + if (op->ch2 != -1) + total += + xmlXPathCompOpEval(ctxt, + &comp->steps[op->ch2]); + CHECK_ERROR0; + res = valuePop(ctxt); + if (res != NULL) + xmlXPathFreeObject(res); + */ + valuePush(ctxt, obj); + ctxt->context->node = oldnode; + CHECK_ERROR0; + } else { + xmlNodeSetPtr newset; + xmlXPathObjectPtr tmp = NULL; + /* + * Initialize the new set. + * Also set the xpath document in case things like + * key() evaluation are attempted on the predicate + */ + newset = xmlXPathNodeSetCreate(NULL); + + for (i = 0; i < oldset->nodeNr; i++) { + /* + * Run the evaluation with a node list made of + * a single item in the nodeset. + */ + ctxt->context->node = oldset->nodeTab[i]; + if ((oldset->nodeTab[i]->type != XML_NAMESPACE_DECL) && + (oldset->nodeTab[i]->doc != NULL)) + ctxt->context->doc = oldset->nodeTab[i]->doc; + if (tmp == NULL) { + tmp = xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node); + } else { + xmlXPathNodeSetAddUnique(tmp->nodesetval, + ctxt->context->node); + } + valuePush(ctxt, tmp); + ctxt->context->contextSize = oldset->nodeNr; + ctxt->context->proximityPosition = i + 1; + if (op->ch2 != -1) + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); + if (ctxt->error != XPATH_EXPRESSION_OK) { + xmlXPathFreeNodeSet(newset); + xmlXPathFreeObject(obj); + return(0); + } + /* + * The result of the evaluation needs to be tested to + * decide whether the filter succeeded or not + */ + res = valuePop(ctxt); + if (xmlXPathEvaluatePredicateResult(ctxt, res)) { + xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]); + } + /* + * Cleanup + */ + if (res != NULL) { + xmlXPathReleaseObject(ctxt->context, res); + } + if (ctxt->value == tmp) { + valuePop(ctxt); + /* + * Don't free the temporary nodeset + * in order to avoid massive recreation inside this + * loop. + */ + xmlXPathNodeSetClear(tmp->nodesetval); + } else + tmp = NULL; + ctxt->context->node = NULL; + /* + * Only put the first node in the result, then leave. + */ + if (newset->nodeNr > 0) { + *first = *(newset->nodeTab); + break; + } + } + if (tmp != NULL) { + xmlXPathReleaseObject(ctxt->context, tmp); + } + /* + * The result is used as the new evaluation set. + */ + xmlXPathReleaseObject(ctxt->context, obj); + ctxt->context->node = NULL; + ctxt->context->contextSize = -1; + ctxt->context->proximityPosition = -1; + /* may want to move this past the '}' later */ + ctxt->context->doc = oldDoc; + valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, newset)); + } + ctxt->context->node = oldnode; + return(total); +} +#endif /* XP_OPTIMIZED_FILTER_FIRST */ + +/** + * xmlXPathCompOpEval: + * @ctxt: the XPath parser context with the compiled expression + * @op: an XPath compiled operation + * + * Evaluate the Precompiled XPath operation + * Returns the number of nodes traversed + */ +static int +xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) +{ + int total = 0; + int equal, ret; + xmlXPathCompExprPtr comp; + xmlXPathObjectPtr arg1, arg2; + xmlNodePtr bak; + xmlDocPtr bakd; + int pp; + int cs; + + CHECK_ERROR0; + comp = ctxt->comp; + switch (op->op) { + case XPATH_OP_END: + return (0); + case XPATH_OP_AND: + bakd = ctxt->context->doc; + bak = ctxt->context->node; + pp = ctxt->context->proximityPosition; + cs = ctxt->context->contextSize; + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + CHECK_ERROR0; + xmlXPathBooleanFunction(ctxt, 1); + if ((ctxt->value == NULL) || (ctxt->value->boolval == 0)) + return (total); + arg2 = valuePop(ctxt); + ctxt->context->doc = bakd; + ctxt->context->node = bak; + ctxt->context->proximityPosition = pp; + ctxt->context->contextSize = cs; + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); + if (ctxt->error) { + xmlXPathFreeObject(arg2); + return(0); + } + xmlXPathBooleanFunction(ctxt, 1); + arg1 = valuePop(ctxt); + arg1->boolval &= arg2->boolval; + valuePush(ctxt, arg1); + xmlXPathReleaseObject(ctxt->context, arg2); + return (total); + case XPATH_OP_OR: + bakd = ctxt->context->doc; + bak = ctxt->context->node; + pp = ctxt->context->proximityPosition; + cs = ctxt->context->contextSize; + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + CHECK_ERROR0; + xmlXPathBooleanFunction(ctxt, 1); + if ((ctxt->value == NULL) || (ctxt->value->boolval == 1)) + return (total); + arg2 = valuePop(ctxt); + ctxt->context->doc = bakd; + ctxt->context->node = bak; + ctxt->context->proximityPosition = pp; + ctxt->context->contextSize = cs; + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); + if (ctxt->error) { + xmlXPathFreeObject(arg2); + return(0); + } + xmlXPathBooleanFunction(ctxt, 1); + arg1 = valuePop(ctxt); + arg1->boolval |= arg2->boolval; + valuePush(ctxt, arg1); + xmlXPathReleaseObject(ctxt->context, arg2); + return (total); + case XPATH_OP_EQUAL: + bakd = ctxt->context->doc; + bak = ctxt->context->node; + pp = ctxt->context->proximityPosition; + cs = ctxt->context->contextSize; + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + CHECK_ERROR0; + ctxt->context->doc = bakd; + ctxt->context->node = bak; + ctxt->context->proximityPosition = pp; + ctxt->context->contextSize = cs; + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); + CHECK_ERROR0; + if (op->value) + equal = xmlXPathEqualValues(ctxt); + else + equal = xmlXPathNotEqualValues(ctxt); + valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, equal)); + return (total); + case XPATH_OP_CMP: + bakd = ctxt->context->doc; + bak = ctxt->context->node; + pp = ctxt->context->proximityPosition; + cs = ctxt->context->contextSize; + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + CHECK_ERROR0; + ctxt->context->doc = bakd; + ctxt->context->node = bak; + ctxt->context->proximityPosition = pp; + ctxt->context->contextSize = cs; + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); + CHECK_ERROR0; + ret = xmlXPathCompareValues(ctxt, op->value, op->value2); + valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, ret)); + return (total); + case XPATH_OP_PLUS: + bakd = ctxt->context->doc; + bak = ctxt->context->node; + pp = ctxt->context->proximityPosition; + cs = ctxt->context->contextSize; + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + CHECK_ERROR0; + if (op->ch2 != -1) { + ctxt->context->doc = bakd; + ctxt->context->node = bak; + ctxt->context->proximityPosition = pp; + ctxt->context->contextSize = cs; + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); + } + CHECK_ERROR0; + if (op->value == 0) + xmlXPathSubValues(ctxt); + else if (op->value == 1) + xmlXPathAddValues(ctxt); + else if (op->value == 2) + xmlXPathValueFlipSign(ctxt); + else if (op->value == 3) { + CAST_TO_NUMBER; + CHECK_TYPE0(XPATH_NUMBER); + } + return (total); + case XPATH_OP_MULT: + bakd = ctxt->context->doc; + bak = ctxt->context->node; + pp = ctxt->context->proximityPosition; + cs = ctxt->context->contextSize; + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + CHECK_ERROR0; + ctxt->context->doc = bakd; + ctxt->context->node = bak; + ctxt->context->proximityPosition = pp; + ctxt->context->contextSize = cs; + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); + CHECK_ERROR0; + if (op->value == 0) + xmlXPathMultValues(ctxt); + else if (op->value == 1) + xmlXPathDivValues(ctxt); + else if (op->value == 2) + xmlXPathModValues(ctxt); + return (total); + case XPATH_OP_UNION: + bakd = ctxt->context->doc; + bak = ctxt->context->node; + pp = ctxt->context->proximityPosition; + cs = ctxt->context->contextSize; + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + CHECK_ERROR0; + ctxt->context->doc = bakd; + ctxt->context->node = bak; + ctxt->context->proximityPosition = pp; + ctxt->context->contextSize = cs; + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); + CHECK_ERROR0; + CHECK_TYPE0(XPATH_NODESET); + arg2 = valuePop(ctxt); + + CHECK_TYPE0(XPATH_NODESET); + arg1 = valuePop(ctxt); + + if ((arg1->nodesetval == NULL) || + ((arg2->nodesetval != NULL) && + (arg2->nodesetval->nodeNr != 0))) + { + arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval, + arg2->nodesetval); + } + + valuePush(ctxt, arg1); + xmlXPathReleaseObject(ctxt->context, arg2); + return (total); + case XPATH_OP_ROOT: + xmlXPathRoot(ctxt); + return (total); + case XPATH_OP_NODE: + if (op->ch1 != -1) + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + CHECK_ERROR0; + if (op->ch2 != -1) + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); + CHECK_ERROR0; + valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node)); + return (total); + case XPATH_OP_RESET: + if (op->ch1 != -1) + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + CHECK_ERROR0; + if (op->ch2 != -1) + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); + CHECK_ERROR0; + ctxt->context->node = NULL; + return (total); + case XPATH_OP_COLLECT:{ + if (op->ch1 == -1) + return (total); + + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + CHECK_ERROR0; + + if ((op->ch2 != -1) && + (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) && + (comp->steps[op->ch2].ch1 == -1) && + (comp->steps[op->ch2].ch2 != -1) && + (comp->steps[comp->steps[op->ch2].ch2].op == + XPATH_OP_VALUE)) + { + xmlXPathObjectPtr val; + /* + * Optimization for [n] selection where n is a number + */ + val = comp->steps[comp->steps[op->ch2].ch2].value4; + if ((val != NULL) && (val->type == XPATH_NUMBER)) { + int indx = (int) val->floatval; + + if (val->floatval == (float) indx) { + total += + xmlXPathNodeCollectAndTestNth(ctxt, op, + indx, NULL, + NULL); + return (total); + } + } + } + total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL); + return (total); + } + case XPATH_OP_VALUE: + valuePush(ctxt, + xmlXPathCacheObjectCopy(ctxt->context, + (xmlXPathObjectPtr) op->value4)); + return (total); + case XPATH_OP_VARIABLE:{ + xmlXPathObjectPtr val; + + if (op->ch1 != -1) + total += + xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + if (op->value5 == NULL) { + val = xmlXPathVariableLookup(ctxt->context, op->value4); + if (val == NULL) { + ctxt->error = XPATH_UNDEF_VARIABLE_ERROR; + return(0); + } + valuePush(ctxt, val); + } else { + const xmlChar *URI; + + URI = xmlXPathNsLookup(ctxt->context, op->value5); + if (URI == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xmlXPathCompOpEval: variable %s bound to undefined prefix %s\n", + op->value4, op->value5); + return (total); + } + val = xmlXPathVariableLookupNS(ctxt->context, + op->value4, URI); + if (val == NULL) { + ctxt->error = XPATH_UNDEF_VARIABLE_ERROR; + return(0); + } + valuePush(ctxt, val); + } + return (total); + } + case XPATH_OP_FUNCTION:{ + xmlXPathFunction func; + const xmlChar *oldFunc, *oldFuncURI; + int i; + + if (op->ch1 != -1) + total += + xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + if (ctxt->valueNr < op->value) { + xmlGenericError(xmlGenericErrorContext, + "xmlXPathCompOpEval: parameter error\n"); + ctxt->error = XPATH_INVALID_OPERAND; + return (total); + } + for (i = 0; i < op->value; i++) + if (ctxt->valueTab[(ctxt->valueNr - 1) - i] == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xmlXPathCompOpEval: parameter error\n"); + ctxt->error = XPATH_INVALID_OPERAND; + return (total); + } + if (op->cache != NULL) + XML_CAST_FPTR(func) = op->cache; + else { + const xmlChar *URI = NULL; + + if (op->value5 == NULL) + func = + xmlXPathFunctionLookup(ctxt->context, + op->value4); + else { + URI = xmlXPathNsLookup(ctxt->context, op->value5); + if (URI == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xmlXPathCompOpEval: function %s bound to undefined prefix %s\n", + op->value4, op->value5); + return (total); + } + func = xmlXPathFunctionLookupNS(ctxt->context, + op->value4, URI); + } + if (func == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xmlXPathCompOpEval: function %s not found\n", + op->value4); + XP_ERROR0(XPATH_UNKNOWN_FUNC_ERROR); + } + op->cache = XML_CAST_FPTR(func); + op->cacheURI = (void *) URI; + } + oldFunc = ctxt->context->function; + oldFuncURI = ctxt->context->functionURI; + ctxt->context->function = op->value4; + ctxt->context->functionURI = op->cacheURI; + func(ctxt, op->value); + ctxt->context->function = oldFunc; + ctxt->context->functionURI = oldFuncURI; + return (total); + } + case XPATH_OP_ARG: + bakd = ctxt->context->doc; + bak = ctxt->context->node; + pp = ctxt->context->proximityPosition; + cs = ctxt->context->contextSize; + if (op->ch1 != -1) + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + ctxt->context->contextSize = cs; + ctxt->context->proximityPosition = pp; + ctxt->context->node = bak; + ctxt->context->doc = bakd; + CHECK_ERROR0; + if (op->ch2 != -1) { + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); + ctxt->context->doc = bakd; + ctxt->context->node = bak; + CHECK_ERROR0; + } + return (total); + case XPATH_OP_PREDICATE: + case XPATH_OP_FILTER:{ + xmlXPathObjectPtr res; + xmlXPathObjectPtr obj, tmp; + xmlNodeSetPtr newset = NULL; + xmlNodeSetPtr oldset; + xmlNodePtr oldnode; + xmlDocPtr oldDoc; + int i; + + /* + * Optimization for ()[1] selection i.e. the first elem + */ + if ((op->ch1 != -1) && (op->ch2 != -1) && +#ifdef XP_OPTIMIZED_FILTER_FIRST + /* + * FILTER TODO: Can we assume that the inner processing + * will result in an ordered list if we have an + * XPATH_OP_FILTER? + * What about an additional field or flag on + * xmlXPathObject like @sorted ? This way we wouln'd need + * to assume anything, so it would be more robust and + * easier to optimize. + */ + ((comp->steps[op->ch1].op == XPATH_OP_SORT) || /* 18 */ + (comp->steps[op->ch1].op == XPATH_OP_FILTER)) && /* 17 */ +#else + (comp->steps[op->ch1].op == XPATH_OP_SORT) && +#endif + (comp->steps[op->ch2].op == XPATH_OP_VALUE)) { /* 12 */ + xmlXPathObjectPtr val; + + val = comp->steps[op->ch2].value4; + if ((val != NULL) && (val->type == XPATH_NUMBER) && + (val->floatval == 1.0)) { + xmlNodePtr first = NULL; + + total += + xmlXPathCompOpEvalFirst(ctxt, + &comp->steps[op->ch1], + &first); + CHECK_ERROR0; + /* + * The nodeset should be in document order, + * Keep only the first value + */ + if ((ctxt->value != NULL) && + (ctxt->value->type == XPATH_NODESET) && + (ctxt->value->nodesetval != NULL) && + (ctxt->value->nodesetval->nodeNr > 1)) + ctxt->value->nodesetval->nodeNr = 1; + return (total); + } + } + /* + * Optimization for ()[last()] selection i.e. the last elem + */ + if ((op->ch1 != -1) && (op->ch2 != -1) && + (comp->steps[op->ch1].op == XPATH_OP_SORT) && + (comp->steps[op->ch2].op == XPATH_OP_SORT)) { + int f = comp->steps[op->ch2].ch1; + + if ((f != -1) && + (comp->steps[f].op == XPATH_OP_FUNCTION) && + (comp->steps[f].value5 == NULL) && + (comp->steps[f].value == 0) && + (comp->steps[f].value4 != NULL) && + (xmlStrEqual + (comp->steps[f].value4, BAD_CAST "last"))) { + xmlNodePtr last = NULL; + + total += + xmlXPathCompOpEvalLast(ctxt, + &comp->steps[op->ch1], + &last); + CHECK_ERROR0; + /* + * The nodeset should be in document order, + * Keep only the last value + */ + if ((ctxt->value != NULL) && + (ctxt->value->type == XPATH_NODESET) && + (ctxt->value->nodesetval != NULL) && + (ctxt->value->nodesetval->nodeTab != NULL) && + (ctxt->value->nodesetval->nodeNr > 1)) { + ctxt->value->nodesetval->nodeTab[0] = + ctxt->value->nodesetval->nodeTab[ctxt-> + value-> + nodesetval-> + nodeNr - + 1]; + ctxt->value->nodesetval->nodeNr = 1; + } + return (total); + } + } + /* + * Process inner predicates first. + * Example "index[parent::book][1]": + * ... + * PREDICATE <-- we are here "[1]" + * PREDICATE <-- process "[parent::book]" first + * SORT + * COLLECT 'parent' 'name' 'node' book + * NODE + * ELEM Object is a number : 1 + */ + if (op->ch1 != -1) + total += + xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + CHECK_ERROR0; + if (op->ch2 == -1) + return (total); + if (ctxt->value == NULL) + return (total); + + oldnode = ctxt->context->node; + +#ifdef LIBXML_XPTR_ENABLED + /* + * Hum are we filtering the result of an XPointer expression + */ + if (ctxt->value->type == XPATH_LOCATIONSET) { + xmlLocationSetPtr newlocset = NULL; + xmlLocationSetPtr oldlocset; + + /* + * Extract the old locset, and then evaluate the result of the + * expression for all the element in the locset. use it to grow + * up a new locset. + */ + CHECK_TYPE0(XPATH_LOCATIONSET); + obj = valuePop(ctxt); + oldlocset = obj->user; + ctxt->context->node = NULL; + + if ((oldlocset == NULL) || (oldlocset->locNr == 0)) { + ctxt->context->contextSize = 0; + ctxt->context->proximityPosition = 0; + if (op->ch2 != -1) + total += + xmlXPathCompOpEval(ctxt, + &comp->steps[op->ch2]); + res = valuePop(ctxt); + if (res != NULL) { + xmlXPathReleaseObject(ctxt->context, res); + } + valuePush(ctxt, obj); + CHECK_ERROR0; + return (total); + } + newlocset = xmlXPtrLocationSetCreate(NULL); + + for (i = 0; i < oldlocset->locNr; i++) { + /* + * Run the evaluation with a node list made of a + * single item in the nodelocset. + */ + ctxt->context->node = oldlocset->locTab[i]->user; + ctxt->context->contextSize = oldlocset->locNr; + ctxt->context->proximityPosition = i + 1; + tmp = xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node); + valuePush(ctxt, tmp); + + if (op->ch2 != -1) + total += + xmlXPathCompOpEval(ctxt, + &comp->steps[op->ch2]); + if (ctxt->error != XPATH_EXPRESSION_OK) { + xmlXPathFreeObject(obj); + return(0); + } + + /* + * The result of the evaluation need to be tested to + * decided whether the filter succeeded or not + */ + res = valuePop(ctxt); + if (xmlXPathEvaluatePredicateResult(ctxt, res)) { + xmlXPtrLocationSetAdd(newlocset, + xmlXPathObjectCopy + (oldlocset->locTab[i])); + } + + /* + * Cleanup + */ + if (res != NULL) { + xmlXPathReleaseObject(ctxt->context, res); + } + if (ctxt->value == tmp) { + res = valuePop(ctxt); + xmlXPathReleaseObject(ctxt->context, res); + } + + ctxt->context->node = NULL; + } + + /* + * The result is used as the new evaluation locset. + */ + xmlXPathReleaseObject(ctxt->context, obj); + ctxt->context->node = NULL; + ctxt->context->contextSize = -1; + ctxt->context->proximityPosition = -1; + valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset)); + ctxt->context->node = oldnode; + return (total); + } +#endif /* LIBXML_XPTR_ENABLED */ + + /* + * Extract the old set, and then evaluate the result of the + * expression for all the element in the set. use it to grow + * up a new set. + */ + CHECK_TYPE0(XPATH_NODESET); + obj = valuePop(ctxt); + oldset = obj->nodesetval; + + oldnode = ctxt->context->node; + oldDoc = ctxt->context->doc; + ctxt->context->node = NULL; + + if ((oldset == NULL) || (oldset->nodeNr == 0)) { + ctxt->context->contextSize = 0; + ctxt->context->proximityPosition = 0; +/* + if (op->ch2 != -1) + total += + xmlXPathCompOpEval(ctxt, + &comp->steps[op->ch2]); + CHECK_ERROR0; + res = valuePop(ctxt); + if (res != NULL) + xmlXPathFreeObject(res); +*/ + valuePush(ctxt, obj); + ctxt->context->node = oldnode; + CHECK_ERROR0; + } else { + tmp = NULL; + /* + * Initialize the new set. + * Also set the xpath document in case things like + * key() evaluation are attempted on the predicate + */ + newset = xmlXPathNodeSetCreate(NULL); + /* + * SPEC XPath 1.0: + * "For each node in the node-set to be filtered, the + * PredicateExpr is evaluated with that node as the + * context node, with the number of nodes in the + * node-set as the context size, and with the proximity + * position of the node in the node-set with respect to + * the axis as the context position;" + * @oldset is the node-set" to be filtered. + * + * SPEC XPath 1.0: + * "only predicates change the context position and + * context size (see [2.4 Predicates])." + * Example: + * node-set context pos + * nA 1 + * nB 2 + * nC 3 + * After applying predicate [position() > 1] : + * node-set context pos + * nB 1 + * nC 2 + * + * removed the first node in the node-set, then + * the context position of the + */ + for (i = 0; i < oldset->nodeNr; i++) { + /* + * Run the evaluation with a node list made of + * a single item in the nodeset. + */ + ctxt->context->node = oldset->nodeTab[i]; + if ((oldset->nodeTab[i]->type != XML_NAMESPACE_DECL) && + (oldset->nodeTab[i]->doc != NULL)) + ctxt->context->doc = oldset->nodeTab[i]->doc; + if (tmp == NULL) { + tmp = xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node); + } else { + xmlXPathNodeSetAddUnique(tmp->nodesetval, + ctxt->context->node); + } + valuePush(ctxt, tmp); + ctxt->context->contextSize = oldset->nodeNr; + ctxt->context->proximityPosition = i + 1; + /* + * Evaluate the predicate against the context node. + * Can/should we optimize position() predicates + * here (e.g. "[1]")? + */ + if (op->ch2 != -1) + total += + xmlXPathCompOpEval(ctxt, + &comp->steps[op->ch2]); + if (ctxt->error != XPATH_EXPRESSION_OK) { + xmlXPathFreeNodeSet(newset); + xmlXPathFreeObject(obj); + return(0); + } + + /* + * The result of the evaluation needs to be tested to + * decide whether the filter succeeded or not + */ + /* + * OPTIMIZE TODO: Can we use + * xmlXPathNodeSetAdd*Unique()* instead? + */ + res = valuePop(ctxt); + if (xmlXPathEvaluatePredicateResult(ctxt, res)) { + xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]); + } + + /* + * Cleanup + */ + if (res != NULL) { + xmlXPathReleaseObject(ctxt->context, res); + } + if (ctxt->value == tmp) { + valuePop(ctxt); + xmlXPathNodeSetClear(tmp->nodesetval); + /* + * Don't free the temporary nodeset + * in order to avoid massive recreation inside this + * loop. + */ + } else + tmp = NULL; + ctxt->context->node = NULL; + } + if (tmp != NULL) + xmlXPathReleaseObject(ctxt->context, tmp); + /* + * The result is used as the new evaluation set. + */ + xmlXPathReleaseObject(ctxt->context, obj); + ctxt->context->node = NULL; + ctxt->context->contextSize = -1; + ctxt->context->proximityPosition = -1; + /* may want to move this past the '}' later */ + ctxt->context->doc = oldDoc; + valuePush(ctxt, + xmlXPathCacheWrapNodeSet(ctxt->context, newset)); + } + ctxt->context->node = oldnode; + return (total); + } + case XPATH_OP_SORT: + if (op->ch1 != -1) + total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + CHECK_ERROR0; + if ((ctxt->value != NULL) && + (ctxt->value->type == XPATH_NODESET) && + (ctxt->value->nodesetval != NULL) && + (ctxt->value->nodesetval->nodeNr > 1)) + { + xmlXPathNodeSetSort(ctxt->value->nodesetval); + } + return (total); +#ifdef LIBXML_XPTR_ENABLED + case XPATH_OP_RANGETO:{ + xmlXPathObjectPtr range; + xmlXPathObjectPtr res, obj; + xmlXPathObjectPtr tmp; + xmlLocationSetPtr newlocset = NULL; + xmlLocationSetPtr oldlocset; + xmlNodeSetPtr oldset; + int i, j; + + if (op->ch1 != -1) + total += + xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + if (op->ch2 == -1) + return (total); + + if (ctxt->value->type == XPATH_LOCATIONSET) { + /* + * Extract the old locset, and then evaluate the result of the + * expression for all the element in the locset. use it to grow + * up a new locset. + */ + CHECK_TYPE0(XPATH_LOCATIONSET); + obj = valuePop(ctxt); + oldlocset = obj->user; + + if ((oldlocset == NULL) || (oldlocset->locNr == 0)) { + ctxt->context->node = NULL; + ctxt->context->contextSize = 0; + ctxt->context->proximityPosition = 0; + total += xmlXPathCompOpEval(ctxt,&comp->steps[op->ch2]); + res = valuePop(ctxt); + if (res != NULL) { + xmlXPathReleaseObject(ctxt->context, res); + } + valuePush(ctxt, obj); + CHECK_ERROR0; + return (total); + } + newlocset = xmlXPtrLocationSetCreate(NULL); + + for (i = 0; i < oldlocset->locNr; i++) { + /* + * Run the evaluation with a node list made of a + * single item in the nodelocset. + */ + ctxt->context->node = oldlocset->locTab[i]->user; + ctxt->context->contextSize = oldlocset->locNr; + ctxt->context->proximityPosition = i + 1; + tmp = xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node); + valuePush(ctxt, tmp); + + if (op->ch2 != -1) + total += + xmlXPathCompOpEval(ctxt, + &comp->steps[op->ch2]); + if (ctxt->error != XPATH_EXPRESSION_OK) { + xmlXPathFreeObject(obj); + return(0); + } + + res = valuePop(ctxt); + if (res->type == XPATH_LOCATIONSET) { + xmlLocationSetPtr rloc = + (xmlLocationSetPtr)res->user; + for (j=0; jlocNr; j++) { + range = xmlXPtrNewRange( + oldlocset->locTab[i]->user, + oldlocset->locTab[i]->index, + rloc->locTab[j]->user2, + rloc->locTab[j]->index2); + if (range != NULL) { + xmlXPtrLocationSetAdd(newlocset, range); + } + } + } else { + range = xmlXPtrNewRangeNodeObject( + (xmlNodePtr)oldlocset->locTab[i]->user, res); + if (range != NULL) { + xmlXPtrLocationSetAdd(newlocset,range); + } + } + + /* + * Cleanup + */ + if (res != NULL) { + xmlXPathReleaseObject(ctxt->context, res); + } + if (ctxt->value == tmp) { + res = valuePop(ctxt); + xmlXPathReleaseObject(ctxt->context, res); + } + + ctxt->context->node = NULL; + } + } else { /* Not a location set */ + CHECK_TYPE0(XPATH_NODESET); + obj = valuePop(ctxt); + oldset = obj->nodesetval; + ctxt->context->node = NULL; + + newlocset = xmlXPtrLocationSetCreate(NULL); + + if (oldset != NULL) { + for (i = 0; i < oldset->nodeNr; i++) { + /* + * Run the evaluation with a node list made of a single item + * in the nodeset. + */ + ctxt->context->node = oldset->nodeTab[i]; + /* + * OPTIMIZE TODO: Avoid recreation for every iteration. + */ + tmp = xmlXPathCacheNewNodeSet(ctxt->context, + ctxt->context->node); + valuePush(ctxt, tmp); + + if (op->ch2 != -1) + total += + xmlXPathCompOpEval(ctxt, + &comp->steps[op->ch2]); + if (ctxt->error != XPATH_EXPRESSION_OK) { + xmlXPathFreeObject(obj); + return(0); + } + + res = valuePop(ctxt); + range = + xmlXPtrNewRangeNodeObject(oldset->nodeTab[i], + res); + if (range != NULL) { + xmlXPtrLocationSetAdd(newlocset, range); + } + + /* + * Cleanup + */ + if (res != NULL) { + xmlXPathReleaseObject(ctxt->context, res); + } + if (ctxt->value == tmp) { + res = valuePop(ctxt); + xmlXPathReleaseObject(ctxt->context, res); + } + + ctxt->context->node = NULL; + } + } + } + + /* + * The result is used as the new evaluation set. + */ + xmlXPathReleaseObject(ctxt->context, obj); + ctxt->context->node = NULL; + ctxt->context->contextSize = -1; + ctxt->context->proximityPosition = -1; + valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset)); + return (total); + } +#endif /* LIBXML_XPTR_ENABLED */ + } + xmlGenericError(xmlGenericErrorContext, + "XPath: unknown precompiled operation %d\n", op->op); + return (total); +} + +#ifdef XPATH_STREAMING +/** + * xmlXPathRunStreamEval: + * @ctxt: the XPath parser context with the compiled expression + * + * Evaluate the Precompiled Streamable XPath expression in the given context. + */ +static xmlXPathObjectPtr +xmlXPathRunStreamEval(xmlXPathContextPtr ctxt, xmlPatternPtr comp) { + int max_depth, min_depth; + int from_root; + int ret, depth; +#ifdef XP_PATTERN_TO_ANY_NODE_ENABLED + int eval_all_nodes; +#endif + xmlNodePtr cur = NULL, limit = NULL; + xmlXPathObjectPtr retval; + xmlStreamCtxtPtr patstream; + + int nb_nodes = 0; + + if ((ctxt == NULL) || (comp == NULL)) + return(NULL); + max_depth = xmlPatternMaxDepth(comp); + if (max_depth == -1) + return(NULL); + if (max_depth == -2) + max_depth = 10000; + min_depth = xmlPatternMinDepth(comp); + if (min_depth == -1) + return(NULL); + from_root = xmlPatternFromRoot(comp); + if (from_root < 0) + return(NULL); +#if 0 + printf("stream eval: depth %d from root %d\n", max_depth, from_root); +#endif + + retval = xmlXPathCacheNewNodeSet(ctxt, NULL); + if (retval == NULL) + return(NULL); + + /* + * handle the special cases of / amd . being matched + */ + if (min_depth == 0) { + if (from_root) { + xmlXPathNodeSetAddUnique(retval->nodesetval, (xmlNodePtr) ctxt->doc); + } else { + xmlXPathNodeSetAddUnique(retval->nodesetval, ctxt->node); + } + } + if (max_depth == 0) { + return(retval); + } + + if (from_root) { + cur = (xmlNodePtr)ctxt->doc; + } else if (ctxt->node != NULL) { + switch (ctxt->node->type) { + case XML_ELEMENT_NODE: + case XML_DOCUMENT_NODE: + case XML_DOCUMENT_FRAG_NODE: + case XML_HTML_DOCUMENT_NODE: +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: +#endif + cur = ctxt->node; + break; + case XML_ATTRIBUTE_NODE: + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_ENTITY_REF_NODE: + case XML_ENTITY_NODE: + case XML_PI_NODE: + case XML_COMMENT_NODE: + case XML_NOTATION_NODE: + case XML_DTD_NODE: + case XML_DOCUMENT_TYPE_NODE: + case XML_ELEMENT_DECL: + case XML_ATTRIBUTE_DECL: + case XML_ENTITY_DECL: + case XML_NAMESPACE_DECL: + case XML_XINCLUDE_START: + case XML_XINCLUDE_END: + break; + } + limit = cur; + } + if (cur == NULL) + return(retval); + + patstream = xmlPatternGetStreamCtxt(comp); + if (patstream == NULL) { + return(retval); + } + +#ifdef XP_PATTERN_TO_ANY_NODE_ENABLED + eval_all_nodes = xmlStreamWantsAnyNode(patstream); +#endif + + if (from_root) { + ret = xmlStreamPush(patstream, NULL, NULL); + if (ret < 0) { + } else if (ret == 1) { + xmlXPathNodeSetAddUnique(retval->nodesetval, cur); + } + } + depth = 0; + goto scan_children; +next_node: + do { + nb_nodes++; + + switch (cur->type) { + case XML_ELEMENT_NODE: +#ifdef XP_PATTERN_TO_ANY_NODE_ENABLED + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_COMMENT_NODE: + case XML_PI_NODE: +#endif + if (cur->type == XML_ELEMENT_NODE) { + ret = xmlStreamPush(patstream, cur->name, + (cur->ns ? cur->ns->href : NULL)); + } +#ifdef XP_PATTERN_TO_ANY_NODE_ENABLED + else if (eval_all_nodes) + ret = xmlStreamPushNode(patstream, NULL, NULL, cur->type); + else + break; +#endif + + if (ret < 0) { + /* NOP. */ + } else if (ret == 1) { + xmlXPathNodeSetAddUnique(retval->nodesetval, cur); + } + if ((cur->children == NULL) || (depth >= max_depth)) { + ret = xmlStreamPop(patstream); + while (cur->next != NULL) { + cur = cur->next; + if ((cur->type != XML_ENTITY_DECL) && + (cur->type != XML_DTD_NODE)) + goto next_node; + } + } + default: + break; + } + +scan_children: + if ((cur->children != NULL) && (depth < max_depth)) { + /* + * Do not descend on entities declarations + */ + if (cur->children->type != XML_ENTITY_DECL) { + cur = cur->children; + depth++; + /* + * Skip DTDs + */ + if (cur->type != XML_DTD_NODE) + continue; + } + } + + if (cur == limit) + break; + + while (cur->next != NULL) { + cur = cur->next; + if ((cur->type != XML_ENTITY_DECL) && + (cur->type != XML_DTD_NODE)) + goto next_node; + } + + do { + cur = cur->parent; + depth--; + if ((cur == NULL) || (cur == limit)) + goto done; + if (cur->type == XML_ELEMENT_NODE) { + ret = xmlStreamPop(patstream); + } +#ifdef XP_PATTERN_TO_ANY_NODE_ENABLED + else if ((eval_all_nodes) && + ((cur->type == XML_TEXT_NODE) || + (cur->type == XML_CDATA_SECTION_NODE) || + (cur->type == XML_COMMENT_NODE) || + (cur->type == XML_PI_NODE))) + { + ret = xmlStreamPop(patstream); + } +#endif + if (cur->next != NULL) { + cur = cur->next; + break; + } + } while (cur != NULL); + + } while ((cur != NULL) && (depth >= 0)); +done: +#if 0 + printf("stream eval: checked %d nodes selected %d\n", + nb_nodes, retval->nodesetval->nodeNr); +#endif + xmlFreeStreamCtxt(patstream); + return(retval); +} +#endif /* XPATH_STREAMING */ + +/** + * xmlXPathRunEval: + * @ctxt: the XPath parser context with the compiled expression + * + * Evaluate the Precompiled XPath expression in the given context. + */ +static void +xmlXPathRunEval(xmlXPathParserContextPtr ctxt) { + xmlXPathCompExprPtr comp; + + if ((ctxt == NULL) || (ctxt->comp == NULL)) + return; + + if (ctxt->valueTab == NULL) { + /* Allocate the value stack */ + ctxt->valueTab = (xmlXPathObjectPtr *) + xmlMalloc(10 * sizeof(xmlXPathObjectPtr)); + if (ctxt->valueTab == NULL) { + xmlXPathPErrMemory(ctxt, "creating evaluation context\n"); + xmlFree(ctxt); + } + ctxt->valueNr = 0; + ctxt->valueMax = 10; + ctxt->value = NULL; + } +#ifdef XPATH_STREAMING + if (ctxt->comp->stream) { + xmlXPathObjectPtr ret; + ret = xmlXPathRunStreamEval(ctxt->context, ctxt->comp->stream); + if (ret != NULL) { + valuePush(ctxt, ret); + return; + } + } +#endif + comp = ctxt->comp; + if(comp->last < 0) { + xmlGenericError(xmlGenericErrorContext, + "xmlXPathRunEval: last is less than zero\n"); + return; + } + xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]); +} + +/************************************************************************ + * * + * Public interfaces * + * * + ************************************************************************/ + +/** + * xmlXPathEvalPredicate: + * @ctxt: the XPath context + * @res: the Predicate Expression evaluation result + * + * Evaluate a predicate result for the current node. + * A PredicateExpr is evaluated by evaluating the Expr and converting + * the result to a boolean. If the result is a number, the result will + * be converted to true if the number is equal to the position of the + * context node in the context node list (as returned by the position + * function) and will be converted to false otherwise; if the result + * is not a number, then the result will be converted as if by a call + * to the boolean function. + * + * Returns 1 if predicate is true, 0 otherwise + */ +int +xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) { + if ((ctxt == NULL) || (res == NULL)) return(0); + switch (res->type) { + case XPATH_BOOLEAN: + return(res->boolval); + case XPATH_NUMBER: + return(res->floatval == ctxt->proximityPosition); + case XPATH_NODESET: + case XPATH_XSLT_TREE: + if (res->nodesetval == NULL) + return(0); + return(res->nodesetval->nodeNr != 0); + case XPATH_STRING: + return((res->stringval != NULL) && + (xmlStrlen(res->stringval) != 0)); + default: + STRANGE + } + return(0); +} + +/** + * xmlXPathEvaluatePredicateResult: + * @ctxt: the XPath Parser context + * @res: the Predicate Expression evaluation result + * + * Evaluate a predicate result for the current node. + * A PredicateExpr is evaluated by evaluating the Expr and converting + * the result to a boolean. If the result is a number, the result will + * be converted to true if the number is equal to the position of the + * context node in the context node list (as returned by the position + * function) and will be converted to false otherwise; if the result + * is not a number, then the result will be converted as if by a call + * to the boolean function. + * + * Returns 1 if predicate is true, 0 otherwise + */ +int +xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt, + xmlXPathObjectPtr res) { + if ((ctxt == NULL) || (res == NULL)) return(0); + switch (res->type) { + case XPATH_BOOLEAN: + return(res->boolval); + case XPATH_NUMBER: +#if defined(__BORLANDC__) || (defined(_MSC_VER) && (_MSC_VER == 1200)) + return((res->floatval == ctxt->context->proximityPosition) && + (!xmlXPathIsNaN(res->floatval))); /* MSC pbm Mark Vakoc !*/ +#else + return(res->floatval == ctxt->context->proximityPosition); +#endif + case XPATH_NODESET: + case XPATH_XSLT_TREE: + if (res->nodesetval == NULL) + return(0); + return(res->nodesetval->nodeNr != 0); + case XPATH_STRING: + return((res->stringval != NULL) && + (xmlStrlen(res->stringval) != 0)); +#ifdef LIBXML_XPTR_ENABLED + case XPATH_LOCATIONSET:{ + xmlLocationSetPtr ptr = res->user; + if (ptr == NULL) + return(0); + return (ptr->locNr != 0); + } +#endif + default: + STRANGE + } + return(0); +} + +#ifdef XPATH_STREAMING +/** + * xmlXPathTryStreamCompile: + * @ctxt: an XPath context + * @str: the XPath expression + * + * Try to compile the XPath expression as a streamable subset. + * + * Returns the compiled expression or NULL if failed to compile. + */ +static xmlXPathCompExprPtr +xmlXPathTryStreamCompile(xmlXPathContextPtr ctxt, const xmlChar *str) { + /* + * Optimization: use streaming patterns when the XPath expression can + * be compiled to a stream lookup + */ + xmlPatternPtr stream; + xmlXPathCompExprPtr comp; + xmlDictPtr dict = NULL; + const xmlChar **namespaces = NULL; + xmlNsPtr ns; + int i, j; + + if ((!xmlStrchr(str, '[')) && (!xmlStrchr(str, '(')) && + (!xmlStrchr(str, '@'))) { + const xmlChar *tmp; + + /* + * We don't try to handle expressions using the verbose axis + * specifiers ("::"), just the simplied form at this point. + * Additionally, if there is no list of namespaces available and + * there's a ":" in the expression, indicating a prefixed QName, + * then we won't try to compile either. xmlPatterncompile() needs + * to have a list of namespaces at compilation time in order to + * compile prefixed name tests. + */ + tmp = xmlStrchr(str, ':'); + if ((tmp != NULL) && + ((ctxt == NULL) || (ctxt->nsNr == 0) || (tmp[1] == ':'))) + return(NULL); + + if (ctxt != NULL) { + dict = ctxt->dict; + if (ctxt->nsNr > 0) { + namespaces = xmlMalloc(2 * (ctxt->nsNr + 1) * sizeof(xmlChar*)); + if (namespaces == NULL) { + xmlXPathErrMemory(ctxt, "allocating namespaces array\n"); + return(NULL); + } + for (i = 0, j = 0; (j < ctxt->nsNr); j++) { + ns = ctxt->namespaces[j]; + namespaces[i++] = ns->href; + namespaces[i++] = ns->prefix; + } + namespaces[i++] = NULL; + namespaces[i++] = NULL; + } + } + + stream = xmlPatterncompile(str, dict, XML_PATTERN_XPATH, + &namespaces[0]); + if (namespaces != NULL) { + xmlFree((xmlChar **)namespaces); + } + if ((stream != NULL) && (xmlPatternStreamable(stream) == 1)) { + comp = xmlXPathNewCompExpr(); + if (comp == NULL) { + xmlXPathErrMemory(ctxt, "allocating streamable expression\n"); + return(NULL); + } + comp->stream = stream; + comp->dict = dict; + if (comp->dict) + xmlDictReference(comp->dict); + return(comp); + } + xmlFreePattern(stream); + } + return(NULL); +} +#endif /* XPATH_STREAMING */ + +static int +xmlXPathCanRewriteDosExpression(xmlChar *expr) +{ + if (expr == NULL) + return(0); + do { + if ((*expr == '/') && (*(++expr) == '/')) + return(1); + } while (*expr++); + return(0); +} +static void +xmlXPathRewriteDOSExpression(xmlXPathCompExprPtr comp, xmlXPathStepOpPtr op) +{ + /* + * Try to rewrite "descendant-or-self::node()/foo" to an optimized + * internal representation. + */ + if (op->ch1 != -1) { + if ((op->op == XPATH_OP_COLLECT /* 11 */) && + ((xmlXPathAxisVal) op->value == AXIS_CHILD /* 4 */) && + ((xmlXPathTestVal) op->value2 == NODE_TEST_NAME /* 5 */) && + ((xmlXPathTypeVal) op->value3 == NODE_TYPE_NODE /* 0 */)) + { + /* + * This is an "foo" + */ + xmlXPathStepOpPtr prevop = &comp->steps[op->ch1]; + + if ((prevop->op == XPATH_OP_COLLECT /* 11 */) && + (prevop->ch1 != -1) && + ((xmlXPathAxisVal) prevop->value == + AXIS_DESCENDANT_OR_SELF) && + (prevop->ch2 == -1) && + ((xmlXPathTestVal) prevop->value2 == NODE_TEST_TYPE) && + ((xmlXPathTypeVal) prevop->value3 == NODE_TYPE_NODE) && + (comp->steps[prevop->ch1].op == XPATH_OP_ROOT)) + { + /* + * This is a "descendant-or-self::node()" without predicates. + * Eliminate it. + */ + op->ch1 = prevop->ch1; + op->rewriteType = XP_REWRITE_DOS_CHILD_ELEM; + } + } + if (op->ch1 != -1) + xmlXPathRewriteDOSExpression(comp, &comp->steps[op->ch1]); + } + if (op->ch2 != -1) + xmlXPathRewriteDOSExpression(comp, &comp->steps[op->ch2]); +} + +/** + * xmlXPathCtxtCompile: + * @ctxt: an XPath context + * @str: the XPath expression + * + * Compile an XPath expression + * + * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL. + * the caller has to free the object. + */ +xmlXPathCompExprPtr +xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) { + xmlXPathParserContextPtr pctxt; + xmlXPathCompExprPtr comp; + +#ifdef XPATH_STREAMING + comp = xmlXPathTryStreamCompile(ctxt, str); + if (comp != NULL) + return(comp); +#endif + + xmlXPathInit(); + + pctxt = xmlXPathNewParserContext(str, ctxt); + xmlXPathCompileExpr(pctxt, 1); + + if( pctxt->error != XPATH_EXPRESSION_OK ) + { + xmlXPathFreeParserContext(pctxt); + return(NULL); + } + + if (*pctxt->cur != 0) { + /* + * aleksey: in some cases this line prints *second* error message + * (see bug #78858) and probably this should be fixed. + * However, we are not sure that all error messages are printed + * out in other places. It's not critical so we leave it as-is for now + */ + xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR); + comp = NULL; + } else { + comp = pctxt->comp; + pctxt->comp = NULL; + } + xmlXPathFreeParserContext(pctxt); + if (comp != NULL) { + comp->expr = xmlStrdup(str); +#ifdef DEBUG_EVAL_COUNTS + comp->string = xmlStrdup(str); + comp->nb = 0; +#endif + if ((comp->nbStep > 2) && + (xmlXPathCanRewriteDosExpression(comp->expr) == 1)) + { + xmlXPathRewriteDOSExpression(comp, &comp->steps[comp->last]); + } + } + return(comp); +} + +/** + * xmlXPathCompile: + * @str: the XPath expression + * + * Compile an XPath expression + * + * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL. + * the caller has to free the object. + */ +xmlXPathCompExprPtr +xmlXPathCompile(const xmlChar *str) { + return(xmlXPathCtxtCompile(NULL, str)); +} + +/** + * xmlXPathCompiledEval: + * @comp: the compiled XPath expression + * @ctx: the XPath context + * + * Evaluate the Precompiled XPath expression in the given context. + * + * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL. + * the caller has to free the object. + */ +xmlXPathObjectPtr +xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) { + xmlXPathParserContextPtr ctxt; + xmlXPathObjectPtr res, tmp, init = NULL; + int stack = 0; +#ifndef LIBXML_THREAD_ENABLED + static int reentance = 0; +#endif + + CHECK_CTXT(ctx) + + if (comp == NULL) + return(NULL); + xmlXPathInit(); + +#ifndef LIBXML_THREAD_ENABLED + reentance++; + if (reentance > 1) + xmlXPathDisableOptimizer = 1; +#endif + +#ifdef DEBUG_EVAL_COUNTS + comp->nb++; + if ((comp->string != NULL) && (comp->nb > 100)) { + fprintf(stderr, "100 x %s\n", comp->string); + comp->nb = 0; + } +#endif + ctxt = xmlXPathCompParserContext(comp, ctx); + xmlXPathRunEval(ctxt); + + if (ctxt->value == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xmlXPathCompiledEval: evaluation failed\n"); + res = NULL; + } else { + res = valuePop(ctxt); + } + + + do { + tmp = valuePop(ctxt); + if (tmp != NULL) { + if (tmp != init) + stack++; + xmlXPathReleaseObject(ctx, tmp); + } + } while (tmp != NULL); + if ((stack != 0) && (res != NULL)) { + xmlGenericError(xmlGenericErrorContext, + "xmlXPathCompiledEval: %d object left on the stack\n", + stack); + } + if (ctxt->error != XPATH_EXPRESSION_OK) { + xmlXPathFreeObject(res); + res = NULL; + } + ctxt->comp = NULL; + xmlXPathFreeParserContext(ctxt); +#ifndef LIBXML_THREAD_ENABLED + reentance--; +#endif + return(res); +} + +/** + * xmlXPathEvalExpr: + * @ctxt: the XPath Parser context + * + * Parse and evaluate an XPath expression in the given context, + * then push the result on the context stack + */ +void +xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) { +#ifdef XPATH_STREAMING + xmlXPathCompExprPtr comp; +#endif + + if (ctxt == NULL) return; + +#ifdef XPATH_STREAMING + comp = xmlXPathTryStreamCompile(ctxt->context, ctxt->base); + if (comp != NULL) { + if (ctxt->comp != NULL) + xmlXPathFreeCompExpr(ctxt->comp); + ctxt->comp = comp; + if (ctxt->cur != NULL) + while (*ctxt->cur != 0) ctxt->cur++; + } else +#endif + { + xmlXPathCompileExpr(ctxt, 1); + if ((ctxt->comp != NULL) && + (ctxt->comp->nbStep > 2) && + (xmlXPathCanRewriteDosExpression(ctxt->comp->expr) == 1)) + { + xmlXPathRewriteDOSExpression(ctxt->comp, + &ctxt->comp->steps[comp->last]); + } + } + CHECK_ERROR; + xmlXPathRunEval(ctxt); +} + +/** + * xmlXPathEval: + * @str: the XPath expression + * @ctx: the XPath context + * + * Evaluate the XPath Location Path in the given context. + * + * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL. + * the caller has to free the object. + */ +xmlXPathObjectPtr +xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) { + xmlXPathParserContextPtr ctxt; + xmlXPathObjectPtr res, tmp, init = NULL; + int stack = 0; + + CHECK_CTXT(ctx) + + xmlXPathInit(); + + ctxt = xmlXPathNewParserContext(str, ctx); + xmlXPathEvalExpr(ctxt); + + if (ctxt->value == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xmlXPathEval: evaluation failed\n"); + res = NULL; + } else if ((*ctxt->cur != 0) && (ctxt->comp != NULL) +#ifdef XPATH_STREAMING + && (ctxt->comp->stream == NULL) +#endif + ) { + xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR); + res = NULL; + } else { + res = valuePop(ctxt); + } + + do { + tmp = valuePop(ctxt); + if (tmp != NULL) { + if (tmp != init) + stack++; + xmlXPathReleaseObject(ctx, tmp); + } + } while (tmp != NULL); + if ((stack != 0) && (res != NULL)) { + xmlGenericError(xmlGenericErrorContext, + "xmlXPathEval: %d object left on the stack\n", + stack); + } + if (ctxt->error != XPATH_EXPRESSION_OK) { + xmlXPathFreeObject(res); + res = NULL; + } + + xmlXPathFreeParserContext(ctxt); + return(res); +} + +/** + * xmlXPathEvalExpression: + * @str: the XPath expression + * @ctxt: the XPath context + * + * Evaluate the XPath expression in the given context. + * + * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL. + * the caller has to free the object. + */ +xmlXPathObjectPtr +xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) { + xmlXPathParserContextPtr pctxt; + xmlXPathObjectPtr res, tmp; + int stack = 0; + + CHECK_CTXT(ctxt) + + xmlXPathInit(); + + pctxt = xmlXPathNewParserContext(str, ctxt); + xmlXPathEvalExpr(pctxt); + + if (*pctxt->cur != 0) { + xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR); + res = NULL; + } else { + res = valuePop(pctxt); + } + do { + tmp = valuePop(pctxt); + if (tmp != NULL) { + xmlXPathReleaseObject(ctxt, tmp); + stack++; + } + } while (tmp != NULL); + if ((stack != 0) && (res != NULL)) { + xmlGenericError(xmlGenericErrorContext, + "xmlXPathEvalExpression: %d object left on the stack\n", + stack); + } + xmlXPathFreeParserContext(pctxt); + return(res); +} + +/************************************************************************ + * * + * Extra functions not pertaining to the XPath spec * + * * + ************************************************************************/ +/** + * xmlXPathEscapeUriFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the escape-uri() XPath function + * string escape-uri(string $str, bool $escape-reserved) + * + * This function applies the URI escaping rules defined in section 2 of [RFC + * 2396] to the string supplied as $uri-part, which typically represents all + * or part of a URI. The effect of the function is to replace any special + * character in the string by an escape sequence of the form %xx%yy..., + * where xxyy... is the hexadecimal representation of the octets used to + * represent the character in UTF-8. + * + * The set of characters that are escaped depends on the setting of the + * boolean argument $escape-reserved. + * + * If $escape-reserved is true, all characters are escaped other than lower + * case letters a-z, upper case letters A-Z, digits 0-9, and the characters + * referred to in [RFC 2396] as "marks": specifically, "-" | "_" | "." | "!" + * | "~" | "*" | "'" | "(" | ")". The "%" character itself is escaped only + * if it is not followed by two hexadecimal digits (that is, 0-9, a-f, and + * A-F). + * + * If $escape-reserved is false, the behavior differs in that characters + * referred to in [RFC 2396] as reserved characters are not escaped. These + * characters are ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ",". + * + * [RFC 2396] does not define whether escaped URIs should use lower case or + * upper case for hexadecimal digits. To ensure that escaped URIs can be + * compared using string comparison functions, this function must always use + * the upper-case letters A-F. + * + * Generally, $escape-reserved should be set to true when escaping a string + * that is to form a single part of a URI, and to false when escaping an + * entire URI or URI reference. + * + * In the case of non-ascii characters, the string is encoded according to + * utf-8 and then converted according to RFC 2396. + * + * Examples + * xf:escape-uri ("gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles#ocean"), true()) + * returns "gopher%3A%2F%2Fspinaltap.micro.umn.edu%2F00%2FWeather%2FCalifornia%2FLos%20Angeles%23ocean" + * xf:escape-uri ("gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles#ocean"), false()) + * returns "gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles%23ocean" + * + */ +static void +xmlXPathEscapeUriFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr str; + int escape_reserved; + xmlBufferPtr target; + xmlChar *cptr; + xmlChar escape[4]; + + CHECK_ARITY(2); + + escape_reserved = xmlXPathPopBoolean(ctxt); + + CAST_TO_STRING; + str = valuePop(ctxt); + + target = xmlBufferCreate(); + + escape[0] = '%'; + escape[3] = 0; + + if (target) { + for (cptr = str->stringval; *cptr; cptr++) { + if ((*cptr >= 'A' && *cptr <= 'Z') || + (*cptr >= 'a' && *cptr <= 'z') || + (*cptr >= '0' && *cptr <= '9') || + *cptr == '-' || *cptr == '_' || *cptr == '.' || + *cptr == '!' || *cptr == '~' || *cptr == '*' || + *cptr == '\''|| *cptr == '(' || *cptr == ')' || + (*cptr == '%' && + ((cptr[1] >= 'A' && cptr[1] <= 'F') || + (cptr[1] >= 'a' && cptr[1] <= 'f') || + (cptr[1] >= '0' && cptr[1] <= '9')) && + ((cptr[2] >= 'A' && cptr[2] <= 'F') || + (cptr[2] >= 'a' && cptr[2] <= 'f') || + (cptr[2] >= '0' && cptr[2] <= '9'))) || + (!escape_reserved && + (*cptr == ';' || *cptr == '/' || *cptr == '?' || + *cptr == ':' || *cptr == '@' || *cptr == '&' || + *cptr == '=' || *cptr == '+' || *cptr == '$' || + *cptr == ','))) { + xmlBufferAdd(target, cptr, 1); + } else { + if ((*cptr >> 4) < 10) + escape[1] = '0' + (*cptr >> 4); + else + escape[1] = 'A' - 10 + (*cptr >> 4); + if ((*cptr & 0xF) < 10) + escape[2] = '0' + (*cptr & 0xF); + else + escape[2] = 'A' - 10 + (*cptr & 0xF); + + xmlBufferAdd(target, &escape[0], 3); + } + } + } + valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, + xmlBufferContent(target))); + xmlBufferFree(target); + xmlXPathReleaseObject(ctxt->context, str); +} + +/** + * xmlXPathRegisterAllFunctions: + * @ctxt: the XPath context + * + * Registers all default XPath functions in this context + */ +void +xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt) +{ + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean", + xmlXPathBooleanFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling", + xmlXPathCeilingFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count", + xmlXPathCountFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat", + xmlXPathConcatFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains", + xmlXPathContainsFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id", + xmlXPathIdFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false", + xmlXPathFalseFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor", + xmlXPathFloorFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last", + xmlXPathLastFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang", + xmlXPathLangFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name", + xmlXPathLocalNameFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not", + xmlXPathNotFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name", + xmlXPathNameFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri", + xmlXPathNamespaceURIFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space", + xmlXPathNormalizeFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number", + xmlXPathNumberFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position", + xmlXPathPositionFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round", + xmlXPathRoundFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string", + xmlXPathStringFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length", + xmlXPathStringLengthFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with", + xmlXPathStartsWithFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring", + xmlXPathSubstringFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before", + xmlXPathSubstringBeforeFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after", + xmlXPathSubstringAfterFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum", + xmlXPathSumFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true", + xmlXPathTrueFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate", + xmlXPathTranslateFunction); + + xmlXPathRegisterFuncNS(ctxt, (const xmlChar *)"escape-uri", + (const xmlChar *)"http://www.w3.org/2002/08/xquery-functions", + xmlXPathEscapeUriFunction); +} + +#endif /* LIBXML_XPATH_ENABLED */ +#define bottom_xpath +#include "elfgcchack.h" diff --git a/Extras/LibXML/xpointer.c b/Extras/LibXML/xpointer.c new file mode 100644 index 000000000..5298fa5c5 --- /dev/null +++ b/Extras/LibXML/xpointer.c @@ -0,0 +1,3003 @@ +/* + * xpointer.c : Code to handle XML Pointer + * + * Base implementation was made accordingly to + * W3C Candidate Recommendation 7 June 2000 + * http://www.w3.org/TR/2000/CR-xptr-20000607 + * + * Added support for the element() scheme described in: + * W3C Proposed Recommendation 13 November 2002 + * http://www.w3.org/TR/2002/PR-xptr-element-20021113/ + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXML +#include "libxml.h" + +/* + * TODO: better handling of error cases, the full expression should + * be parsed beforehand instead of a progressive evaluation + * TODO: Access into entities references are not supported now ... + * need a start to be able to pop out of entities refs since + * parent is the endity declaration, not the ref. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef LIBXML_XPTR_ENABLED + +/* Add support of the xmlns() xpointer scheme to initialize the namespaces */ +#define XPTR_XMLNS_SCHEME + +/* #define DEBUG_RANGES */ +#ifdef DEBUG_RANGES +#ifdef LIBXML_DEBUG_ENABLED +#include +#endif +#endif + +#define TODO \ + xmlGenericError(xmlGenericErrorContext, \ + "Unimplemented block at %s:%d\n", \ + __FILE__, __LINE__); + +#define STRANGE \ + xmlGenericError(xmlGenericErrorContext, \ + "Internal error at %s:%d\n", \ + __FILE__, __LINE__); + +/************************************************************************ + * * + * Some factorized error routines * + * * + ************************************************************************/ + +/** + * xmlXPtrErrMemory: + * @extra: extra informations + * + * Handle a redefinition of attribute error + */ +static void +xmlXPtrErrMemory(const char *extra) +{ + __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_XPOINTER, + XML_ERR_NO_MEMORY, XML_ERR_ERROR, NULL, 0, extra, + NULL, NULL, 0, 0, + "Memory allocation failed : %s\n", extra); +} + +/** + * xmlXPtrErr: + * @ctxt: an XPTR evaluation context + * @extra: extra informations + * + * Handle a redefinition of attribute error + */ +static void +xmlXPtrErr(xmlXPathParserContextPtr ctxt, int error, + const char * msg, const xmlChar *extra) +{ + if (ctxt != NULL) + ctxt->error = error; + if ((ctxt == NULL) || (ctxt->context == NULL)) { + __xmlRaiseError(NULL, NULL, NULL, + NULL, NULL, XML_FROM_XPOINTER, error, + XML_ERR_ERROR, NULL, 0, + (const char *) extra, NULL, NULL, 0, 0, + msg, extra); + return; + } + ctxt->context->lastError.domain = XML_FROM_XPOINTER; + ctxt->context->lastError.code = error; + ctxt->context->lastError.level = XML_ERR_ERROR; + ctxt->context->lastError.str1 = (char *) xmlStrdup(ctxt->base); + ctxt->context->lastError.int1 = ctxt->cur - ctxt->base; + ctxt->context->lastError.node = ctxt->context->debugNode; + if (ctxt->context->error != NULL) { + ctxt->context->error(ctxt->context->userData, + &ctxt->context->lastError); + } else { + __xmlRaiseError(NULL, NULL, NULL, + NULL, ctxt->context->debugNode, XML_FROM_XPOINTER, + error, XML_ERR_ERROR, NULL, 0, + (const char *) extra, (const char *) ctxt->base, NULL, + ctxt->cur - ctxt->base, 0, + msg, extra); + } +} + +/************************************************************************ + * * + * A few helper functions for child sequences * + * * + ************************************************************************/ +/* xmlXPtrAdvanceNode is a private function, but used by xinclude.c */ +xmlNodePtr xmlXPtrAdvanceNode(xmlNodePtr cur, int *level); +/** + * xmlXPtrGetArity: + * @cur: the node + * + * Returns the number of child for an element, -1 in case of error + */ +static int +xmlXPtrGetArity(xmlNodePtr cur) { + int i; + if (cur == NULL) + return(-1); + cur = cur->children; + for (i = 0;cur != NULL;cur = cur->next) { + if ((cur->type == XML_ELEMENT_NODE) || + (cur->type == XML_DOCUMENT_NODE) || + (cur->type == XML_HTML_DOCUMENT_NODE)) { + i++; + } + } + return(i); +} + +/** + * xmlXPtrGetIndex: + * @cur: the node + * + * Returns the index of the node in its parent children list, -1 + * in case of error + */ +static int +xmlXPtrGetIndex(xmlNodePtr cur) { + int i; + if (cur == NULL) + return(-1); + for (i = 1;cur != NULL;cur = cur->prev) { + if ((cur->type == XML_ELEMENT_NODE) || + (cur->type == XML_DOCUMENT_NODE) || + (cur->type == XML_HTML_DOCUMENT_NODE)) { + i++; + } + } + return(i); +} + +/** + * xmlXPtrGetNthChild: + * @cur: the node + * @no: the child number + * + * Returns the @no'th element child of @cur or NULL + */ +static xmlNodePtr +xmlXPtrGetNthChild(xmlNodePtr cur, int no) { + int i; + if (cur == NULL) + return(cur); + cur = cur->children; + for (i = 0;i <= no;cur = cur->next) { + if (cur == NULL) + return(cur); + if ((cur->type == XML_ELEMENT_NODE) || + (cur->type == XML_DOCUMENT_NODE) || + (cur->type == XML_HTML_DOCUMENT_NODE)) { + i++; + if (i == no) + break; + } + } + return(cur); +} + +/************************************************************************ + * * + * Handling of XPointer specific types * + * * + ************************************************************************/ + +/** + * xmlXPtrCmpPoints: + * @node1: the first node + * @index1: the first index + * @node2: the second node + * @index2: the second index + * + * Compare two points w.r.t document order + * + * Returns -2 in case of error 1 if first point < second point, 0 if + * that's the same point, -1 otherwise + */ +static int +xmlXPtrCmpPoints(xmlNodePtr node1, int index1, xmlNodePtr node2, int index2) { + if ((node1 == NULL) || (node2 == NULL)) + return(-2); + /* + * a couple of optimizations which will avoid computations in most cases + */ + if (node1 == node2) { + if (index1 < index2) + return(1); + if (index1 > index2) + return(-1); + return(0); + } + return(xmlXPathCmpNodes(node1, node2)); +} + +/** + * xmlXPtrNewPoint: + * @node: the xmlNodePtr + * @indx: the indx within the node + * + * Create a new xmlXPathObjectPtr of type point + * + * Returns the newly created object. + */ +static xmlXPathObjectPtr +xmlXPtrNewPoint(xmlNodePtr node, int indx) { + xmlXPathObjectPtr ret; + + if (node == NULL) + return(NULL); + if (indx < 0) + return(NULL); + + ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); + if (ret == NULL) { + xmlXPtrErrMemory("allocating point"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); + ret->type = XPATH_POINT; + ret->user = (void *) node; + ret->index = indx; + return(ret); +} + +/** + * xmlXPtrRangeCheckOrder: + * @range: an object range + * + * Make sure the points in the range are in the right order + */ +static void +xmlXPtrRangeCheckOrder(xmlXPathObjectPtr range) { + int tmp; + xmlNodePtr tmp2; + if (range == NULL) + return; + if (range->type != XPATH_RANGE) + return; + if (range->user2 == NULL) + return; + tmp = xmlXPtrCmpPoints(range->user, range->index, + range->user2, range->index2); + if (tmp == -1) { + tmp2 = range->user; + range->user = range->user2; + range->user2 = tmp2; + tmp = range->index; + range->index = range->index2; + range->index2 = tmp; + } +} + +/** + * xmlXPtrRangesEqual: + * @range1: the first range + * @range2: the second range + * + * Compare two ranges + * + * Returns 1 if equal, 0 otherwise + */ +static int +xmlXPtrRangesEqual(xmlXPathObjectPtr range1, xmlXPathObjectPtr range2) { + if (range1 == range2) + return(1); + if ((range1 == NULL) || (range2 == NULL)) + return(0); + if (range1->type != range2->type) + return(0); + if (range1->type != XPATH_RANGE) + return(0); + if (range1->user != range2->user) + return(0); + if (range1->index != range2->index) + return(0); + if (range1->user2 != range2->user2) + return(0); + if (range1->index2 != range2->index2) + return(0); + return(1); +} + +/** + * xmlXPtrNewRange: + * @start: the starting node + * @startindex: the start index + * @end: the ending point + * @endindex: the ending index + * + * Create a new xmlXPathObjectPtr of type range + * + * Returns the newly created object. + */ +xmlXPathObjectPtr +xmlXPtrNewRange(xmlNodePtr start, int startindex, + xmlNodePtr end, int endindex) { + xmlXPathObjectPtr ret; + + if (start == NULL) + return(NULL); + if (end == NULL) + return(NULL); + if (startindex < 0) + return(NULL); + if (endindex < 0) + return(NULL); + + ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); + if (ret == NULL) { + xmlXPtrErrMemory("allocating range"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); + ret->type = XPATH_RANGE; + ret->user = start; + ret->index = startindex; + ret->user2 = end; + ret->index2 = endindex; + xmlXPtrRangeCheckOrder(ret); + return(ret); +} + +/** + * xmlXPtrNewRangePoints: + * @start: the starting point + * @end: the ending point + * + * Create a new xmlXPathObjectPtr of type range using 2 Points + * + * Returns the newly created object. + */ +xmlXPathObjectPtr +xmlXPtrNewRangePoints(xmlXPathObjectPtr start, xmlXPathObjectPtr end) { + xmlXPathObjectPtr ret; + + if (start == NULL) + return(NULL); + if (end == NULL) + return(NULL); + if (start->type != XPATH_POINT) + return(NULL); + if (end->type != XPATH_POINT) + return(NULL); + + ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); + if (ret == NULL) { + xmlXPtrErrMemory("allocating range"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); + ret->type = XPATH_RANGE; + ret->user = start->user; + ret->index = start->index; + ret->user2 = end->user; + ret->index2 = end->index; + xmlXPtrRangeCheckOrder(ret); + return(ret); +} + +/** + * xmlXPtrNewRangePointNode: + * @start: the starting point + * @end: the ending node + * + * Create a new xmlXPathObjectPtr of type range from a point to a node + * + * Returns the newly created object. + */ +xmlXPathObjectPtr +xmlXPtrNewRangePointNode(xmlXPathObjectPtr start, xmlNodePtr end) { + xmlXPathObjectPtr ret; + + if (start == NULL) + return(NULL); + if (end == NULL) + return(NULL); + if (start->type != XPATH_POINT) + return(NULL); + + ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); + if (ret == NULL) { + xmlXPtrErrMemory("allocating range"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); + ret->type = XPATH_RANGE; + ret->user = start->user; + ret->index = start->index; + ret->user2 = end; + ret->index2 = -1; + xmlXPtrRangeCheckOrder(ret); + return(ret); +} + +/** + * xmlXPtrNewRangeNodePoint: + * @start: the starting node + * @end: the ending point + * + * Create a new xmlXPathObjectPtr of type range from a node to a point + * + * Returns the newly created object. + */ +xmlXPathObjectPtr +xmlXPtrNewRangeNodePoint(xmlNodePtr start, xmlXPathObjectPtr end) { + xmlXPathObjectPtr ret; + + if (start == NULL) + return(NULL); + if (end == NULL) + return(NULL); + if (start->type != XPATH_POINT) + return(NULL); + if (end->type != XPATH_POINT) + return(NULL); + + ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); + if (ret == NULL) { + xmlXPtrErrMemory("allocating range"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); + ret->type = XPATH_RANGE; + ret->user = start; + ret->index = -1; + ret->user2 = end->user; + ret->index2 = end->index; + xmlXPtrRangeCheckOrder(ret); + return(ret); +} + +/** + * xmlXPtrNewRangeNodes: + * @start: the starting node + * @end: the ending node + * + * Create a new xmlXPathObjectPtr of type range using 2 nodes + * + * Returns the newly created object. + */ +xmlXPathObjectPtr +xmlXPtrNewRangeNodes(xmlNodePtr start, xmlNodePtr end) { + xmlXPathObjectPtr ret; + + if (start == NULL) + return(NULL); + if (end == NULL) + return(NULL); + + ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); + if (ret == NULL) { + xmlXPtrErrMemory("allocating range"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); + ret->type = XPATH_RANGE; + ret->user = start; + ret->index = -1; + ret->user2 = end; + ret->index2 = -1; + xmlXPtrRangeCheckOrder(ret); + return(ret); +} + +/** + * xmlXPtrNewCollapsedRange: + * @start: the starting and ending node + * + * Create a new xmlXPathObjectPtr of type range using a single nodes + * + * Returns the newly created object. + */ +xmlXPathObjectPtr +xmlXPtrNewCollapsedRange(xmlNodePtr start) { + xmlXPathObjectPtr ret; + + if (start == NULL) + return(NULL); + + ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); + if (ret == NULL) { + xmlXPtrErrMemory("allocating range"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); + ret->type = XPATH_RANGE; + ret->user = start; + ret->index = -1; + ret->user2 = NULL; + ret->index2 = -1; + return(ret); +} + +/** + * xmlXPtrNewRangeNodeObject: + * @start: the starting node + * @end: the ending object + * + * Create a new xmlXPathObjectPtr of type range from a not to an object + * + * Returns the newly created object. + */ +xmlXPathObjectPtr +xmlXPtrNewRangeNodeObject(xmlNodePtr start, xmlXPathObjectPtr end) { + xmlXPathObjectPtr ret; + + if (start == NULL) + return(NULL); + if (end == NULL) + return(NULL); + switch (end->type) { + case XPATH_POINT: + case XPATH_RANGE: + break; + case XPATH_NODESET: + /* + * Empty set ... + */ + if (end->nodesetval->nodeNr <= 0) + return(NULL); + break; + default: + /* TODO */ + return(NULL); + } + + ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); + if (ret == NULL) { + xmlXPtrErrMemory("allocating range"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); + ret->type = XPATH_RANGE; + ret->user = start; + ret->index = -1; + switch (end->type) { + case XPATH_POINT: + ret->user2 = end->user; + ret->index2 = end->index; + break; + case XPATH_RANGE: + ret->user2 = end->user2; + ret->index2 = end->index2; + break; + case XPATH_NODESET: { + ret->user2 = end->nodesetval->nodeTab[end->nodesetval->nodeNr - 1]; + ret->index2 = -1; + break; + } + default: + STRANGE + return(NULL); + } + xmlXPtrRangeCheckOrder(ret); + return(ret); +} + +#define XML_RANGESET_DEFAULT 10 + +/** + * xmlXPtrLocationSetCreate: + * @val: an initial xmlXPathObjectPtr, or NULL + * + * Create a new xmlLocationSetPtr of type double and of value @val + * + * Returns the newly created object. + */ +xmlLocationSetPtr +xmlXPtrLocationSetCreate(xmlXPathObjectPtr val) { + xmlLocationSetPtr ret; + + ret = (xmlLocationSetPtr) xmlMalloc(sizeof(xmlLocationSet)); + if (ret == NULL) { + xmlXPtrErrMemory("allocating locationset"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlLocationSet)); + if (val != NULL) { + ret->locTab = (xmlXPathObjectPtr *) xmlMalloc(XML_RANGESET_DEFAULT * + sizeof(xmlXPathObjectPtr)); + if (ret->locTab == NULL) { + xmlXPtrErrMemory("allocating locationset"); + xmlFree(ret); + return(NULL); + } + memset(ret->locTab, 0 , + XML_RANGESET_DEFAULT * (size_t) sizeof(xmlXPathObjectPtr)); + ret->locMax = XML_RANGESET_DEFAULT; + ret->locTab[ret->locNr++] = val; + } + return(ret); +} + +/** + * xmlXPtrLocationSetAdd: + * @cur: the initial range set + * @val: a new xmlXPathObjectPtr + * + * add a new xmlXPathObjectPtr to an existing LocationSet + * If the location already exist in the set @val is freed. + */ +void +xmlXPtrLocationSetAdd(xmlLocationSetPtr cur, xmlXPathObjectPtr val) { + int i; + + if ((cur == NULL) || (val == NULL)) return; + + /* + * check against doublons + */ + for (i = 0;i < cur->locNr;i++) { + if (xmlXPtrRangesEqual(cur->locTab[i], val)) { + xmlXPathFreeObject(val); + return; + } + } + + /* + * grow the locTab if needed + */ + if (cur->locMax == 0) { + cur->locTab = (xmlXPathObjectPtr *) xmlMalloc(XML_RANGESET_DEFAULT * + sizeof(xmlXPathObjectPtr)); + if (cur->locTab == NULL) { + xmlXPtrErrMemory("adding location to set"); + return; + } + memset(cur->locTab, 0 , + XML_RANGESET_DEFAULT * (size_t) sizeof(xmlXPathObjectPtr)); + cur->locMax = XML_RANGESET_DEFAULT; + } else if (cur->locNr == cur->locMax) { + xmlXPathObjectPtr *temp; + + cur->locMax *= 2; + temp = (xmlXPathObjectPtr *) xmlRealloc(cur->locTab, cur->locMax * + sizeof(xmlXPathObjectPtr)); + if (temp == NULL) { + xmlXPtrErrMemory("adding location to set"); + return; + } + cur->locTab = temp; + } + cur->locTab[cur->locNr++] = val; +} + +/** + * xmlXPtrLocationSetMerge: + * @val1: the first LocationSet + * @val2: the second LocationSet + * + * Merges two rangesets, all ranges from @val2 are added to @val1 + * + * Returns val1 once extended or NULL in case of error. + */ +xmlLocationSetPtr +xmlXPtrLocationSetMerge(xmlLocationSetPtr val1, xmlLocationSetPtr val2) { + int i; + + if (val1 == NULL) return(NULL); + if (val2 == NULL) return(val1); + + /* + * !!!!! this can be optimized a lot, knowing that both + * val1 and val2 already have unicity of their values. + */ + + for (i = 0;i < val2->locNr;i++) + xmlXPtrLocationSetAdd(val1, val2->locTab[i]); + + return(val1); +} + +/** + * xmlXPtrLocationSetDel: + * @cur: the initial range set + * @val: an xmlXPathObjectPtr + * + * Removes an xmlXPathObjectPtr from an existing LocationSet + */ +void +xmlXPtrLocationSetDel(xmlLocationSetPtr cur, xmlXPathObjectPtr val) { + int i; + + if (cur == NULL) return; + if (val == NULL) return; + + /* + * check against doublons + */ + for (i = 0;i < cur->locNr;i++) + if (cur->locTab[i] == val) break; + + if (i >= cur->locNr) { +#ifdef DEBUG + xmlGenericError(xmlGenericErrorContext, + "xmlXPtrLocationSetDel: Range wasn't found in RangeList\n"); +#endif + return; + } + cur->locNr--; + for (;i < cur->locNr;i++) + cur->locTab[i] = cur->locTab[i + 1]; + cur->locTab[cur->locNr] = NULL; +} + +/** + * xmlXPtrLocationSetRemove: + * @cur: the initial range set + * @val: the index to remove + * + * Removes an entry from an existing LocationSet list. + */ +void +xmlXPtrLocationSetRemove(xmlLocationSetPtr cur, int val) { + if (cur == NULL) return; + if (val >= cur->locNr) return; + cur->locNr--; + for (;val < cur->locNr;val++) + cur->locTab[val] = cur->locTab[val + 1]; + cur->locTab[cur->locNr] = NULL; +} + +/** + * xmlXPtrFreeLocationSet: + * @obj: the xmlLocationSetPtr to free + * + * Free the LocationSet compound (not the actual ranges !). + */ +void +xmlXPtrFreeLocationSet(xmlLocationSetPtr obj) { + int i; + + if (obj == NULL) return; + if (obj->locTab != NULL) { + for (i = 0;i < obj->locNr; i++) { + xmlXPathFreeObject(obj->locTab[i]); + } + xmlFree(obj->locTab); + } + xmlFree(obj); +} + +/** + * xmlXPtrNewLocationSetNodes: + * @start: the start NodePtr value + * @end: the end NodePtr value or NULL + * + * Create a new xmlXPathObjectPtr of type LocationSet and initialize + * it with the single range made of the two nodes @start and @end + * + * Returns the newly created object. + */ +xmlXPathObjectPtr +xmlXPtrNewLocationSetNodes(xmlNodePtr start, xmlNodePtr end) { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); + if (ret == NULL) { + xmlXPtrErrMemory("allocating locationset"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); + ret->type = XPATH_LOCATIONSET; + if (end == NULL) + ret->user = xmlXPtrLocationSetCreate(xmlXPtrNewCollapsedRange(start)); + else + ret->user = xmlXPtrLocationSetCreate(xmlXPtrNewRangeNodes(start,end)); + return(ret); +} + +/** + * xmlXPtrNewLocationSetNodeSet: + * @set: a node set + * + * Create a new xmlXPathObjectPtr of type LocationSet and initialize + * it with all the nodes from @set + * + * Returns the newly created object. + */ +xmlXPathObjectPtr +xmlXPtrNewLocationSetNodeSet(xmlNodeSetPtr set) { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); + if (ret == NULL) { + xmlXPtrErrMemory("allocating locationset"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); + ret->type = XPATH_LOCATIONSET; + if (set != NULL) { + int i; + xmlLocationSetPtr newset; + + newset = xmlXPtrLocationSetCreate(NULL); + if (newset == NULL) + return(ret); + + for (i = 0;i < set->nodeNr;i++) + xmlXPtrLocationSetAdd(newset, + xmlXPtrNewCollapsedRange(set->nodeTab[i])); + + ret->user = (void *) newset; + } + return(ret); +} + +/** + * xmlXPtrWrapLocationSet: + * @val: the LocationSet value + * + * Wrap the LocationSet @val in a new xmlXPathObjectPtr + * + * Returns the newly created object. + */ +xmlXPathObjectPtr +xmlXPtrWrapLocationSet(xmlLocationSetPtr val) { + xmlXPathObjectPtr ret; + + ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); + if (ret == NULL) { + xmlXPtrErrMemory("allocating locationset"); + return(NULL); + } + memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); + ret->type = XPATH_LOCATIONSET; + ret->user = (void *) val; + return(ret); +} + +/************************************************************************ + * * + * The parser * + * * + ************************************************************************/ + +static void xmlXPtrEvalChildSeq(xmlXPathParserContextPtr ctxt, xmlChar *name); + +/* + * Macros for accessing the content. Those should be used only by the parser, + * and not exported. + * + * Dirty macros, i.e. one need to make assumption on the context to use them + * + * CUR_PTR return the current pointer to the xmlChar to be parsed. + * CUR returns the current xmlChar value, i.e. a 8 bit value + * in ISO-Latin or UTF-8. + * This should be used internally by the parser + * only to compare to ASCII values otherwise it would break when + * running with UTF-8 encoding. + * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only + * to compare on ASCII based substring. + * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined + * strings within the parser. + * CURRENT Returns the current char value, with the full decoding of + * UTF-8 if we are using this mode. It returns an int. + * NEXT Skip to the next character, this does the proper decoding + * in UTF-8 mode. It also pop-up unfinished entities on the fly. + * It returns the pointer to the current xmlChar. + */ + +#define CUR (*ctxt->cur) +#define SKIP(val) ctxt->cur += (val) +#define NXT(val) ctxt->cur[(val)] +#define CUR_PTR ctxt->cur + +#define SKIP_BLANKS \ + while (IS_BLANK_CH(*(ctxt->cur))) NEXT + +#define CURRENT (*ctxt->cur) +#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur) + +/* + * xmlXPtrGetChildNo: + * @ctxt: the XPointer Parser context + * @index: the child number + * + * Move the current node of the nodeset on the stack to the + * given child if found + */ +static void +xmlXPtrGetChildNo(xmlXPathParserContextPtr ctxt, int indx) { + xmlNodePtr cur = NULL; + xmlXPathObjectPtr obj; + xmlNodeSetPtr oldset; + + CHECK_TYPE(XPATH_NODESET); + obj = valuePop(ctxt); + oldset = obj->nodesetval; + if ((indx <= 0) || (oldset == NULL) || (oldset->nodeNr != 1)) { + xmlXPathFreeObject(obj); + valuePush(ctxt, xmlXPathNewNodeSet(NULL)); + return; + } + cur = xmlXPtrGetNthChild(oldset->nodeTab[0], indx); + if (cur == NULL) { + xmlXPathFreeObject(obj); + valuePush(ctxt, xmlXPathNewNodeSet(NULL)); + return; + } + oldset->nodeTab[0] = cur; + valuePush(ctxt, obj); +} + +/** + * xmlXPtrEvalXPtrPart: + * @ctxt: the XPointer Parser context + * @name: the preparsed Scheme for the XPtrPart + * + * XPtrPart ::= 'xpointer' '(' XPtrExpr ')' + * | Scheme '(' SchemeSpecificExpr ')' + * + * Scheme ::= NCName - 'xpointer' [VC: Non-XPointer schemes] + * + * SchemeSpecificExpr ::= StringWithBalancedParens + * + * StringWithBalancedParens ::= + * [^()]* ('(' StringWithBalancedParens ')' [^()]*)* + * [VC: Parenthesis escaping] + * + * XPtrExpr ::= Expr [VC: Parenthesis escaping] + * + * VC: Parenthesis escaping: + * The end of an XPointer part is signaled by the right parenthesis ")" + * character that is balanced with the left parenthesis "(" character + * that began the part. Any unbalanced parenthesis character inside the + * expression, even within literals, must be escaped with a circumflex (^) + * character preceding it. If the expression contains any literal + * occurrences of the circumflex, each must be escaped with an additional + * circumflex (that is, ^^). If the unescaped parentheses in the expression + * are not balanced, a syntax error results. + * + * Parse and evaluate an XPtrPart. Basically it generates the unescaped + * string and if the scheme is 'xpointer' it will call the XPath interpreter. + * + * TODO: there is no new scheme registration mechanism + */ + +static void +xmlXPtrEvalXPtrPart(xmlXPathParserContextPtr ctxt, xmlChar *name) { + xmlChar *buffer, *cur; + int len; + int level; + + if (name == NULL) + name = xmlXPathParseName(ctxt); + if (name == NULL) + XP_ERROR(XPATH_EXPR_ERROR); + + if (CUR != '(') + XP_ERROR(XPATH_EXPR_ERROR); + NEXT; + level = 1; + + len = xmlStrlen(ctxt->cur); + len++; + buffer = (xmlChar *) xmlMallocAtomic(len * sizeof (xmlChar)); + if (buffer == NULL) { + xmlXPtrErrMemory("allocating buffer"); + return; + } + + cur = buffer; + while (CUR != 0) { + if (CUR == ')') { + level--; + if (level == 0) { + NEXT; + break; + } + *cur++ = CUR; + } else if (CUR == '(') { + level++; + *cur++ = CUR; + } else if (CUR == '^') { + NEXT; + if ((CUR == ')') || (CUR == '(') || (CUR == '^')) { + *cur++ = CUR; + } else { + *cur++ = '^'; + *cur++ = CUR; + } + } else { + *cur++ = CUR; + } + NEXT; + } + *cur = 0; + + if ((level != 0) && (CUR == 0)) { + xmlFree(buffer); + XP_ERROR(XPTR_SYNTAX_ERROR); + } + + if (xmlStrEqual(name, (xmlChar *) "xpointer")) { + const xmlChar *left = CUR_PTR; + + CUR_PTR = buffer; + /* + * To evaluate an xpointer scheme element (4.3) we need: + * context initialized to the root + * context position initalized to 1 + * context size initialized to 1 + */ + ctxt->context->node = (xmlNodePtr)ctxt->context->doc; + ctxt->context->proximityPosition = 1; + ctxt->context->contextSize = 1; + xmlXPathEvalExpr(ctxt); + CUR_PTR=left; + } else if (xmlStrEqual(name, (xmlChar *) "element")) { + const xmlChar *left = CUR_PTR; + xmlChar *name2; + + CUR_PTR = buffer; + if (buffer[0] == '/') { + xmlXPathRoot(ctxt); + xmlXPtrEvalChildSeq(ctxt, NULL); + } else { + name2 = xmlXPathParseName(ctxt); + if (name2 == NULL) { + CUR_PTR = left; + xmlFree(buffer); + XP_ERROR(XPATH_EXPR_ERROR); + } + xmlXPtrEvalChildSeq(ctxt, name2); + } + CUR_PTR = left; +#ifdef XPTR_XMLNS_SCHEME + } else if (xmlStrEqual(name, (xmlChar *) "xmlns")) { + const xmlChar *left = CUR_PTR; + xmlChar *prefix; + xmlChar *URI; + xmlURIPtr value; + + CUR_PTR = buffer; + prefix = xmlXPathParseNCName(ctxt); + if (prefix == NULL) { + xmlFree(buffer); + xmlFree(name); + XP_ERROR(XPTR_SYNTAX_ERROR); + } + SKIP_BLANKS; + if (CUR != '=') { + xmlFree(prefix); + xmlFree(buffer); + xmlFree(name); + XP_ERROR(XPTR_SYNTAX_ERROR); + } + NEXT; + SKIP_BLANKS; + /* @@ check escaping in the XPointer WD */ + + value = xmlParseURI((const char *)ctxt->cur); + if (value == NULL) { + xmlFree(prefix); + xmlFree(buffer); + xmlFree(name); + XP_ERROR(XPTR_SYNTAX_ERROR); + } + URI = xmlSaveUri(value); + xmlFreeURI(value); + if (URI == NULL) { + xmlFree(prefix); + xmlFree(buffer); + xmlFree(name); + XP_ERROR(XPATH_MEMORY_ERROR); + } + + xmlXPathRegisterNs(ctxt->context, prefix, URI); + CUR_PTR = left; + xmlFree(URI); + xmlFree(prefix); +#endif /* XPTR_XMLNS_SCHEME */ + } else { + xmlXPtrErr(ctxt, XML_XPTR_UNKNOWN_SCHEME, + "unsupported scheme '%s'\n", name); + } + xmlFree(buffer); + xmlFree(name); +} + +/** + * xmlXPtrEvalFullXPtr: + * @ctxt: the XPointer Parser context + * @name: the preparsed Scheme for the first XPtrPart + * + * FullXPtr ::= XPtrPart (S? XPtrPart)* + * + * As the specs says: + * ----------- + * When multiple XPtrParts are provided, they must be evaluated in + * left-to-right order. If evaluation of one part fails, the nexti + * is evaluated. The following conditions cause XPointer part failure: + * + * - An unknown scheme + * - A scheme that does not locate any sub-resource present in the resource + * - A scheme that is not applicable to the media type of the resource + * + * The XPointer application must consume a failed XPointer part and + * attempt to evaluate the next one, if any. The result of the first + * XPointer part whose evaluation succeeds is taken to be the fragment + * located by the XPointer as a whole. If all the parts fail, the result + * for the XPointer as a whole is a sub-resource error. + * ----------- + * + * Parse and evaluate a Full XPtr i.e. possibly a cascade of XPath based + * expressions or other schemes. + */ +static void +xmlXPtrEvalFullXPtr(xmlXPathParserContextPtr ctxt, xmlChar *name) { + if (name == NULL) + name = xmlXPathParseName(ctxt); + if (name == NULL) + XP_ERROR(XPATH_EXPR_ERROR); + while (name != NULL) { + xmlXPtrEvalXPtrPart(ctxt, name); + + /* in case of syntax error, break here */ + if (ctxt->error != XPATH_EXPRESSION_OK) + return; + + /* + * If the returned value is a non-empty nodeset + * or location set, return here. + */ + if (ctxt->value != NULL) { + xmlXPathObjectPtr obj = ctxt->value; + + switch (obj->type) { + case XPATH_LOCATIONSET: { + xmlLocationSetPtr loc = ctxt->value->user; + if ((loc != NULL) && (loc->locNr > 0)) + return; + break; + } + case XPATH_NODESET: { + xmlNodeSetPtr loc = ctxt->value->nodesetval; + if ((loc != NULL) && (loc->nodeNr > 0)) + return; + break; + } + default: + break; + } + + /* + * Evaluating to improper values is equivalent to + * a sub-resource error, clean-up the stack + */ + do { + obj = valuePop(ctxt); + if (obj != NULL) { + xmlXPathFreeObject(obj); + } + } while (obj != NULL); + } + + /* + * Is there another XPointer part. + */ + SKIP_BLANKS; + name = xmlXPathParseName(ctxt); + } +} + +/** + * xmlXPtrEvalChildSeq: + * @ctxt: the XPointer Parser context + * @name: a possible ID name of the child sequence + * + * ChildSeq ::= '/1' ('/' [0-9]*)* + * | Name ('/' [0-9]*)+ + * + * Parse and evaluate a Child Sequence. This routine also handle the + * case of a Bare Name used to get a document ID. + */ +static void +xmlXPtrEvalChildSeq(xmlXPathParserContextPtr ctxt, xmlChar *name) { + /* + * XPointer don't allow by syntax to address in mutirooted trees + * this might prove useful in some cases, warn about it. + */ + if ((name == NULL) && (CUR == '/') && (NXT(1) != '1')) { + xmlXPtrErr(ctxt, XML_XPTR_CHILDSEQ_START, + "warning: ChildSeq not starting by /1\n", NULL); + } + + if (name != NULL) { + valuePush(ctxt, xmlXPathNewString(name)); + xmlFree(name); + xmlXPathIdFunction(ctxt, 1); + CHECK_ERROR; + } + + while (CUR == '/') { + int child = 0; + NEXT; + + while ((CUR >= '0') && (CUR <= '9')) { + child = child * 10 + (CUR - '0'); + NEXT; + } + xmlXPtrGetChildNo(ctxt, child); + } +} + + +/** + * xmlXPtrEvalXPointer: + * @ctxt: the XPointer Parser context + * + * XPointer ::= Name + * | ChildSeq + * | FullXPtr + * + * Parse and evaluate an XPointer + */ +static void +xmlXPtrEvalXPointer(xmlXPathParserContextPtr ctxt) { + if (ctxt->valueTab == NULL) { + /* Allocate the value stack */ + ctxt->valueTab = (xmlXPathObjectPtr *) + xmlMalloc(10 * sizeof(xmlXPathObjectPtr)); + if (ctxt->valueTab == NULL) { + xmlXPtrErrMemory("allocating evaluation context"); + return; + } + ctxt->valueNr = 0; + ctxt->valueMax = 10; + ctxt->value = NULL; + } + SKIP_BLANKS; + if (CUR == '/') { + xmlXPathRoot(ctxt); + xmlXPtrEvalChildSeq(ctxt, NULL); + } else { + xmlChar *name; + + name = xmlXPathParseName(ctxt); + if (name == NULL) + XP_ERROR(XPATH_EXPR_ERROR); + if (CUR == '(') { + xmlXPtrEvalFullXPtr(ctxt, name); + /* Short evaluation */ + return; + } else { + /* this handle both Bare Names and Child Sequences */ + xmlXPtrEvalChildSeq(ctxt, name); + } + } + SKIP_BLANKS; + if (CUR != 0) + XP_ERROR(XPATH_EXPR_ERROR); +} + + +/************************************************************************ + * * + * General routines * + * * + ************************************************************************/ + +void xmlXPtrStringRangeFunction(xmlXPathParserContextPtr ctxt, int nargs); +void xmlXPtrStartPointFunction(xmlXPathParserContextPtr ctxt, int nargs); +void xmlXPtrEndPointFunction(xmlXPathParserContextPtr ctxt, int nargs); +void xmlXPtrHereFunction(xmlXPathParserContextPtr ctxt, int nargs); +void xmlXPtrOriginFunction(xmlXPathParserContextPtr ctxt, int nargs); +void xmlXPtrRangeInsideFunction(xmlXPathParserContextPtr ctxt, int nargs); +void xmlXPtrRangeFunction(xmlXPathParserContextPtr ctxt, int nargs); + +/** + * xmlXPtrNewContext: + * @doc: the XML document + * @here: the node that directly contains the XPointer being evaluated or NULL + * @origin: the element from which a user or program initiated traversal of + * the link, or NULL. + * + * Create a new XPointer context + * + * Returns the xmlXPathContext just allocated. + */ +xmlXPathContextPtr +xmlXPtrNewContext(xmlDocPtr doc, xmlNodePtr here, xmlNodePtr origin) { + xmlXPathContextPtr ret; + + ret = xmlXPathNewContext(doc); + if (ret == NULL) + return(ret); + ret->xptr = 1; + ret->here = here; + ret->origin = origin; + + xmlXPathRegisterFunc(ret, (xmlChar *)"range-to", + xmlXPtrRangeToFunction); + xmlXPathRegisterFunc(ret, (xmlChar *)"range", + xmlXPtrRangeFunction); + xmlXPathRegisterFunc(ret, (xmlChar *)"range-inside", + xmlXPtrRangeInsideFunction); + xmlXPathRegisterFunc(ret, (xmlChar *)"string-range", + xmlXPtrStringRangeFunction); + xmlXPathRegisterFunc(ret, (xmlChar *)"start-point", + xmlXPtrStartPointFunction); + xmlXPathRegisterFunc(ret, (xmlChar *)"end-point", + xmlXPtrEndPointFunction); + xmlXPathRegisterFunc(ret, (xmlChar *)"here", + xmlXPtrHereFunction); + xmlXPathRegisterFunc(ret, (xmlChar *)" origin", + xmlXPtrOriginFunction); + + return(ret); +} + +/** + * xmlXPtrEval: + * @str: the XPointer expression + * @ctx: the XPointer context + * + * Evaluate the XPath Location Path in the given context. + * + * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL. + * the caller has to free the object. + */ +xmlXPathObjectPtr +xmlXPtrEval(const xmlChar *str, xmlXPathContextPtr ctx) { + xmlXPathParserContextPtr ctxt; + xmlXPathObjectPtr res = NULL, tmp; + xmlXPathObjectPtr init = NULL; + int stack = 0; + + xmlXPathInit(); + + if ((ctx == NULL) || (str == NULL)) + return(NULL); + + ctxt = xmlXPathNewParserContext(str, ctx); + ctxt->xptr = 1; + xmlXPtrEvalXPointer(ctxt); + + if ((ctxt->value != NULL) && + (ctxt->value->type != XPATH_NODESET) && + (ctxt->value->type != XPATH_LOCATIONSET)) { + xmlXPtrErr(ctxt, XML_XPTR_EVAL_FAILED, + "xmlXPtrEval: evaluation failed to return a node set\n", + NULL); + } else { + res = valuePop(ctxt); + } + + do { + tmp = valuePop(ctxt); + if (tmp != NULL) { + if (tmp != init) { + if (tmp->type == XPATH_NODESET) { + /* + * Evaluation may push a root nodeset which is unused + */ + xmlNodeSetPtr set; + set = tmp->nodesetval; + if ((set->nodeNr != 1) || + (set->nodeTab[0] != (xmlNodePtr) ctx->doc)) + stack++; + } else + stack++; + } + xmlXPathFreeObject(tmp); + } + } while (tmp != NULL); + if (stack != 0) { + xmlXPtrErr(ctxt, XML_XPTR_EXTRA_OBJECTS, + "xmlXPtrEval: object(s) left on the eval stack\n", + NULL); + } + if (ctxt->error != XPATH_EXPRESSION_OK) { + xmlXPathFreeObject(res); + res = NULL; + } + + xmlXPathFreeParserContext(ctxt); + return(res); +} + +/** + * xmlXPtrBuildRangeNodeList: + * @range: a range object + * + * Build a node list tree copy of the range + * + * Returns an xmlNodePtr list or NULL. + * the caller has to free the node tree. + */ +static xmlNodePtr +xmlXPtrBuildRangeNodeList(xmlXPathObjectPtr range) { + /* pointers to generated nodes */ + xmlNodePtr list = NULL, last = NULL, parent = NULL, tmp; + /* pointers to traversal nodes */ + xmlNodePtr start, cur, end; + int index1, index2; + + if (range == NULL) + return(NULL); + if (range->type != XPATH_RANGE) + return(NULL); + start = (xmlNodePtr) range->user; + + if (start == NULL) + return(NULL); + end = range->user2; + if (end == NULL) + return(xmlCopyNode(start, 1)); + + cur = start; + index1 = range->index; + index2 = range->index2; + while (cur != NULL) { + if (cur == end) { + if (cur->type == XML_TEXT_NODE) { + const xmlChar *content = cur->content; + int len; + + if (content == NULL) { + tmp = xmlNewTextLen(NULL, 0); + } else { + len = index2; + if ((cur == start) && (index1 > 1)) { + content += (index1 - 1); + len -= (index1 - 1); + index1 = 0; + } else { + len = index2; + } + tmp = xmlNewTextLen(content, len); + } + /* single sub text node selection */ + if (list == NULL) + return(tmp); + /* prune and return full set */ + if (last != NULL) + xmlAddNextSibling(last, tmp); + else + xmlAddChild(parent, tmp); + return(list); + } else { + tmp = xmlCopyNode(cur, 0); + if (list == NULL) + list = tmp; + else { + if (last != NULL) + xmlAddNextSibling(last, tmp); + else + xmlAddChild(parent, tmp); + } + last = NULL; + parent = tmp; + + if (index2 > 1) { + end = xmlXPtrGetNthChild(cur, index2 - 1); + index2 = 0; + } + if ((cur == start) && (index1 > 1)) { + cur = xmlXPtrGetNthChild(cur, index1 - 1); + index1 = 0; + } else { + cur = cur->children; + } + /* + * Now gather the remaining nodes from cur to end + */ + continue; /* while */ + } + } else if ((cur == start) && + (list == NULL) /* looks superfluous but ... */ ) { + if ((cur->type == XML_TEXT_NODE) || + (cur->type == XML_CDATA_SECTION_NODE)) { + const xmlChar *content = cur->content; + + if (content == NULL) { + tmp = xmlNewTextLen(NULL, 0); + } else { + if (index1 > 1) { + content += (index1 - 1); + } + tmp = xmlNewText(content); + } + last = list = tmp; + } else { + if ((cur == start) && (index1 > 1)) { + tmp = xmlCopyNode(cur, 0); + list = tmp; + parent = tmp; + last = NULL; + cur = xmlXPtrGetNthChild(cur, index1 - 1); + index1 = 0; + /* + * Now gather the remaining nodes from cur to end + */ + continue; /* while */ + } + tmp = xmlCopyNode(cur, 1); + list = tmp; + parent = NULL; + last = tmp; + } + } else { + tmp = NULL; + switch (cur->type) { + case XML_DTD_NODE: + case XML_ELEMENT_DECL: + case XML_ATTRIBUTE_DECL: + case XML_ENTITY_NODE: + /* Do not copy DTD informations */ + break; + case XML_ENTITY_DECL: + TODO /* handle crossing entities -> stack needed */ + break; + case XML_XINCLUDE_START: + case XML_XINCLUDE_END: + /* don't consider it part of the tree content */ + break; + case XML_ATTRIBUTE_NODE: + /* Humm, should not happen ! */ + STRANGE + break; + default: + tmp = xmlCopyNode(cur, 1); + break; + } + if (tmp != NULL) { + if ((list == NULL) || ((last == NULL) && (parent == NULL))) { + STRANGE + return(NULL); + } + if (last != NULL) + xmlAddNextSibling(last, tmp); + else { + xmlAddChild(parent, tmp); + last = tmp; + } + } + } + /* + * Skip to next node in document order + */ + if ((list == NULL) || ((last == NULL) && (parent == NULL))) { + STRANGE + return(NULL); + } + cur = xmlXPtrAdvanceNode(cur, NULL); + } + return(list); +} + +/** + * xmlXPtrBuildNodeList: + * @obj: the XPointer result from the evaluation. + * + * Build a node list tree copy of the XPointer result. + * This will drop Attributes and Namespace declarations. + * + * Returns an xmlNodePtr list or NULL. + * the caller has to free the node tree. + */ +xmlNodePtr +xmlXPtrBuildNodeList(xmlXPathObjectPtr obj) { + xmlNodePtr list = NULL, last = NULL; + int i; + + if (obj == NULL) + return(NULL); + switch (obj->type) { + case XPATH_NODESET: { + xmlNodeSetPtr set = obj->nodesetval; + if (set == NULL) + return(NULL); + for (i = 0;i < set->nodeNr;i++) { + if (set->nodeTab[i] == NULL) + continue; + switch (set->nodeTab[i]->type) { + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_ELEMENT_NODE: + case XML_ENTITY_REF_NODE: + case XML_ENTITY_NODE: + case XML_PI_NODE: + case XML_COMMENT_NODE: + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: +#endif + case XML_XINCLUDE_START: + case XML_XINCLUDE_END: + break; + case XML_ATTRIBUTE_NODE: + case XML_NAMESPACE_DECL: + case XML_DOCUMENT_TYPE_NODE: + case XML_DOCUMENT_FRAG_NODE: + case XML_NOTATION_NODE: + case XML_DTD_NODE: + case XML_ELEMENT_DECL: + case XML_ATTRIBUTE_DECL: + case XML_ENTITY_DECL: + continue; /* for */ + } + if (last == NULL) + list = last = xmlCopyNode(set->nodeTab[i], 1); + else { + xmlAddNextSibling(last, xmlCopyNode(set->nodeTab[i], 1)); + if (last->next != NULL) + last = last->next; + } + } + break; + } + case XPATH_LOCATIONSET: { + xmlLocationSetPtr set = (xmlLocationSetPtr) obj->user; + if (set == NULL) + return(NULL); + for (i = 0;i < set->locNr;i++) { + if (last == NULL) + list = last = xmlXPtrBuildNodeList(set->locTab[i]); + else + xmlAddNextSibling(last, + xmlXPtrBuildNodeList(set->locTab[i])); + if (last != NULL) { + while (last->next != NULL) + last = last->next; + } + } + break; + } + case XPATH_RANGE: + return(xmlXPtrBuildRangeNodeList(obj)); + case XPATH_POINT: + return(xmlCopyNode(obj->user, 0)); + default: + break; + } + return(list); +} + +/************************************************************************ + * * + * XPointer functions * + * * + ************************************************************************/ + +/** + * xmlXPtrNbLocChildren: + * @node: an xmlNodePtr + * + * Count the number of location children of @node or the length of the + * string value in case of text/PI/Comments nodes + * + * Returns the number of location children + */ +static int +xmlXPtrNbLocChildren(xmlNodePtr node) { + int ret = 0; + if (node == NULL) + return(-1); + switch (node->type) { + case XML_HTML_DOCUMENT_NODE: + case XML_DOCUMENT_NODE: + case XML_ELEMENT_NODE: + node = node->children; + while (node != NULL) { + if (node->type == XML_ELEMENT_NODE) + ret++; + node = node->next; + } + break; + case XML_ATTRIBUTE_NODE: + return(-1); + + case XML_PI_NODE: + case XML_COMMENT_NODE: + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_ENTITY_REF_NODE: + ret = xmlStrlen(node->content); + break; + default: + return(-1); + } + return(ret); +} + +/** + * xmlXPtrHereFunction: + * @ctxt: the XPointer Parser context + * @nargs: the number of args + * + * Function implementing here() operation + * as described in 5.4.3 + */ +void +xmlXPtrHereFunction(xmlXPathParserContextPtr ctxt, int nargs) { + CHECK_ARITY(0); + + if (ctxt->context->here == NULL) + XP_ERROR(XPTR_SYNTAX_ERROR); + + valuePush(ctxt, xmlXPtrNewLocationSetNodes(ctxt->context->here, NULL)); +} + +/** + * xmlXPtrOriginFunction: + * @ctxt: the XPointer Parser context + * @nargs: the number of args + * + * Function implementing origin() operation + * as described in 5.4.3 + */ +void +xmlXPtrOriginFunction(xmlXPathParserContextPtr ctxt, int nargs) { + CHECK_ARITY(0); + + if (ctxt->context->origin == NULL) + XP_ERROR(XPTR_SYNTAX_ERROR); + + valuePush(ctxt, xmlXPtrNewLocationSetNodes(ctxt->context->origin, NULL)); +} + +/** + * xmlXPtrStartPointFunction: + * @ctxt: the XPointer Parser context + * @nargs: the number of args + * + * Function implementing start-point() operation + * as described in 5.4.3 + * ---------------- + * location-set start-point(location-set) + * + * For each location x in the argument location-set, start-point adds a + * location of type point to the result location-set. That point represents + * the start point of location x and is determined by the following rules: + * + * - If x is of type point, the start point is x. + * - If x is of type range, the start point is the start point of x. + * - If x is of type root, element, text, comment, or processing instruction, + * - the container node of the start point is x and the index is 0. + * - If x is of type attribute or namespace, the function must signal a + * syntax error. + * ---------------- + * + */ +void +xmlXPtrStartPointFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr tmp, obj, point; + xmlLocationSetPtr newset = NULL; + xmlLocationSetPtr oldset = NULL; + + CHECK_ARITY(1); + if ((ctxt->value == NULL) || + ((ctxt->value->type != XPATH_LOCATIONSET) && + (ctxt->value->type != XPATH_NODESET))) + XP_ERROR(XPATH_INVALID_TYPE) + + obj = valuePop(ctxt); + if (obj->type == XPATH_NODESET) { + /* + * First convert to a location set + */ + tmp = xmlXPtrNewLocationSetNodeSet(obj->nodesetval); + xmlXPathFreeObject(obj); + obj = tmp; + } + + newset = xmlXPtrLocationSetCreate(NULL); + if (newset == NULL) { + xmlXPathFreeObject(obj); + XP_ERROR(XPATH_MEMORY_ERROR); + } + oldset = (xmlLocationSetPtr) obj->user; + if (oldset != NULL) { + int i; + + for (i = 0; i < oldset->locNr; i++) { + tmp = oldset->locTab[i]; + if (tmp == NULL) + continue; + point = NULL; + switch (tmp->type) { + case XPATH_POINT: + point = xmlXPtrNewPoint(tmp->user, tmp->index); + break; + case XPATH_RANGE: { + xmlNodePtr node = tmp->user; + if (node != NULL) { + if (node->type == XML_ATTRIBUTE_NODE) { + /* TODO: Namespace Nodes ??? */ + xmlXPathFreeObject(obj); + xmlXPtrFreeLocationSet(newset); + XP_ERROR(XPTR_SYNTAX_ERROR); + } + point = xmlXPtrNewPoint(node, tmp->index); + } + break; + } + default: + /*** Should we raise an error ? + xmlXPathFreeObject(obj); + xmlXPathFreeObject(newset); + XP_ERROR(XPATH_INVALID_TYPE) + ***/ + break; + } + if (point != NULL) + xmlXPtrLocationSetAdd(newset, point); + } + } + xmlXPathFreeObject(obj); + valuePush(ctxt, xmlXPtrWrapLocationSet(newset)); +} + +/** + * xmlXPtrEndPointFunction: + * @ctxt: the XPointer Parser context + * @nargs: the number of args + * + * Function implementing end-point() operation + * as described in 5.4.3 + * ---------------------------- + * location-set end-point(location-set) + * + * For each location x in the argument location-set, end-point adds a + * location of type point to the result location-set. That point represents + * the end point of location x and is determined by the following rules: + * + * - If x is of type point, the resulting point is x. + * - If x is of type range, the resulting point is the end point of x. + * - If x is of type root or element, the container node of the resulting + * point is x and the index is the number of location children of x. + * - If x is of type text, comment, or processing instruction, the container + * node of the resulting point is x and the index is the length of the + * string-value of x. + * - If x is of type attribute or namespace, the function must signal a + * syntax error. + * ---------------------------- + */ +void +xmlXPtrEndPointFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr tmp, obj, point; + xmlLocationSetPtr newset = NULL; + xmlLocationSetPtr oldset = NULL; + + CHECK_ARITY(1); + if ((ctxt->value == NULL) || + ((ctxt->value->type != XPATH_LOCATIONSET) && + (ctxt->value->type != XPATH_NODESET))) + XP_ERROR(XPATH_INVALID_TYPE) + + obj = valuePop(ctxt); + if (obj->type == XPATH_NODESET) { + /* + * First convert to a location set + */ + tmp = xmlXPtrNewLocationSetNodeSet(obj->nodesetval); + xmlXPathFreeObject(obj); + obj = tmp; + } + + newset = xmlXPtrLocationSetCreate(NULL); + oldset = (xmlLocationSetPtr) obj->user; + if (oldset != NULL) { + int i; + + for (i = 0; i < oldset->locNr; i++) { + tmp = oldset->locTab[i]; + if (tmp == NULL) + continue; + point = NULL; + switch (tmp->type) { + case XPATH_POINT: + point = xmlXPtrNewPoint(tmp->user, tmp->index); + break; + case XPATH_RANGE: { + xmlNodePtr node = tmp->user2; + if (node != NULL) { + if (node->type == XML_ATTRIBUTE_NODE) { + /* TODO: Namespace Nodes ??? */ + xmlXPathFreeObject(obj); + xmlXPtrFreeLocationSet(newset); + XP_ERROR(XPTR_SYNTAX_ERROR); + } + point = xmlXPtrNewPoint(node, tmp->index2); + } else if (tmp->user == NULL) { + point = xmlXPtrNewPoint(node, + xmlXPtrNbLocChildren(node)); + } + break; + } + default: + /*** Should we raise an error ? + xmlXPathFreeObject(obj); + xmlXPathFreeObject(newset); + XP_ERROR(XPATH_INVALID_TYPE) + ***/ + break; + } + if (point != NULL) + xmlXPtrLocationSetAdd(newset, point); + } + } + xmlXPathFreeObject(obj); + valuePush(ctxt, xmlXPtrWrapLocationSet(newset)); +} + + +/** + * xmlXPtrCoveringRange: + * @ctxt: the XPointer Parser context + * @loc: the location for which the covering range must be computed + * + * A covering range is a range that wholly encompasses a location + * Section 5.3.3. Covering Ranges for All Location Types + * http://www.w3.org/TR/xptr#N2267 + * + * Returns a new location or NULL in case of error + */ +static xmlXPathObjectPtr +xmlXPtrCoveringRange(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr loc) { + if (loc == NULL) + return(NULL); + if ((ctxt == NULL) || (ctxt->context == NULL) || + (ctxt->context->doc == NULL)) + return(NULL); + switch (loc->type) { + case XPATH_POINT: + return(xmlXPtrNewRange(loc->user, loc->index, + loc->user, loc->index)); + case XPATH_RANGE: + if (loc->user2 != NULL) { + return(xmlXPtrNewRange(loc->user, loc->index, + loc->user2, loc->index2)); + } else { + xmlNodePtr node = (xmlNodePtr) loc->user; + if (node == (xmlNodePtr) ctxt->context->doc) { + return(xmlXPtrNewRange(node, 0, node, + xmlXPtrGetArity(node))); + } else { + switch (node->type) { + case XML_ATTRIBUTE_NODE: + /* !!! our model is slightly different than XPath */ + return(xmlXPtrNewRange(node, 0, node, + xmlXPtrGetArity(node))); + case XML_ELEMENT_NODE: + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_ENTITY_REF_NODE: + case XML_PI_NODE: + case XML_COMMENT_NODE: + case XML_DOCUMENT_NODE: + case XML_NOTATION_NODE: + case XML_HTML_DOCUMENT_NODE: { + int indx = xmlXPtrGetIndex(node); + + node = node->parent; + return(xmlXPtrNewRange(node, indx - 1, + node, indx + 1)); + } + default: + return(NULL); + } + } + } + default: + TODO /* missed one case ??? */ + } + return(NULL); +} + +/** + * xmlXPtrRangeFunction: + * @ctxt: the XPointer Parser context + * @nargs: the number of args + * + * Function implementing the range() function 5.4.3 + * location-set range(location-set ) + * + * The range function returns ranges covering the locations in + * the argument location-set. For each location x in the argument + * location-set, a range location representing the covering range of + * x is added to the result location-set. + */ +void +xmlXPtrRangeFunction(xmlXPathParserContextPtr ctxt, int nargs) { + int i; + xmlXPathObjectPtr set; + xmlLocationSetPtr oldset; + xmlLocationSetPtr newset; + + CHECK_ARITY(1); + if ((ctxt->value == NULL) || + ((ctxt->value->type != XPATH_LOCATIONSET) && + (ctxt->value->type != XPATH_NODESET))) + XP_ERROR(XPATH_INVALID_TYPE) + + set = valuePop(ctxt); + if (set->type == XPATH_NODESET) { + xmlXPathObjectPtr tmp; + + /* + * First convert to a location set + */ + tmp = xmlXPtrNewLocationSetNodeSet(set->nodesetval); + xmlXPathFreeObject(set); + set = tmp; + } + oldset = (xmlLocationSetPtr) set->user; + + /* + * The loop is to compute the covering range for each item and add it + */ + newset = xmlXPtrLocationSetCreate(NULL); + for (i = 0;i < oldset->locNr;i++) { + xmlXPtrLocationSetAdd(newset, + xmlXPtrCoveringRange(ctxt, oldset->locTab[i])); + } + + /* + * Save the new value and cleanup + */ + valuePush(ctxt, xmlXPtrWrapLocationSet(newset)); + xmlXPathFreeObject(set); +} + +/** + * xmlXPtrInsideRange: + * @ctxt: the XPointer Parser context + * @loc: the location for which the inside range must be computed + * + * A inside range is a range described in the range-inside() description + * + * Returns a new location or NULL in case of error + */ +static xmlXPathObjectPtr +xmlXPtrInsideRange(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr loc) { + if (loc == NULL) + return(NULL); + if ((ctxt == NULL) || (ctxt->context == NULL) || + (ctxt->context->doc == NULL)) + return(NULL); + switch (loc->type) { + case XPATH_POINT: { + xmlNodePtr node = (xmlNodePtr) loc->user; + switch (node->type) { + case XML_PI_NODE: + case XML_COMMENT_NODE: + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: { + if (node->content == NULL) { + return(xmlXPtrNewRange(node, 0, node, 0)); + } else { + return(xmlXPtrNewRange(node, 0, node, + xmlStrlen(node->content))); + } + } + case XML_ATTRIBUTE_NODE: + case XML_ELEMENT_NODE: + case XML_ENTITY_REF_NODE: + case XML_DOCUMENT_NODE: + case XML_NOTATION_NODE: + case XML_HTML_DOCUMENT_NODE: { + return(xmlXPtrNewRange(node, 0, node, + xmlXPtrGetArity(node))); + } + default: + break; + } + return(NULL); + } + case XPATH_RANGE: { + xmlNodePtr node = (xmlNodePtr) loc->user; + if (loc->user2 != NULL) { + return(xmlXPtrNewRange(node, loc->index, + loc->user2, loc->index2)); + } else { + switch (node->type) { + case XML_PI_NODE: + case XML_COMMENT_NODE: + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: { + if (node->content == NULL) { + return(xmlXPtrNewRange(node, 0, node, 0)); + } else { + return(xmlXPtrNewRange(node, 0, node, + xmlStrlen(node->content))); + } + } + case XML_ATTRIBUTE_NODE: + case XML_ELEMENT_NODE: + case XML_ENTITY_REF_NODE: + case XML_DOCUMENT_NODE: + case XML_NOTATION_NODE: + case XML_HTML_DOCUMENT_NODE: { + return(xmlXPtrNewRange(node, 0, node, + xmlXPtrGetArity(node))); + } + default: + break; + } + return(NULL); + } + } + default: + TODO /* missed one case ??? */ + } + return(NULL); +} + +/** + * xmlXPtrRangeInsideFunction: + * @ctxt: the XPointer Parser context + * @nargs: the number of args + * + * Function implementing the range-inside() function 5.4.3 + * location-set range-inside(location-set ) + * + * The range-inside function returns ranges covering the contents of + * the locations in the argument location-set. For each location x in + * the argument location-set, a range location is added to the result + * location-set. If x is a range location, then x is added to the + * result location-set. If x is not a range location, then x is used + * as the container location of the start and end points of the range + * location to be added; the index of the start point of the range is + * zero; if the end point is a character point then its index is the + * length of the string-value of x, and otherwise is the number of + * location children of x. + * + */ +void +xmlXPtrRangeInsideFunction(xmlXPathParserContextPtr ctxt, int nargs) { + int i; + xmlXPathObjectPtr set; + xmlLocationSetPtr oldset; + xmlLocationSetPtr newset; + + CHECK_ARITY(1); + if ((ctxt->value == NULL) || + ((ctxt->value->type != XPATH_LOCATIONSET) && + (ctxt->value->type != XPATH_NODESET))) + XP_ERROR(XPATH_INVALID_TYPE) + + set = valuePop(ctxt); + if (set->type == XPATH_NODESET) { + xmlXPathObjectPtr tmp; + + /* + * First convert to a location set + */ + tmp = xmlXPtrNewLocationSetNodeSet(set->nodesetval); + xmlXPathFreeObject(set); + set = tmp; + } + oldset = (xmlLocationSetPtr) set->user; + + /* + * The loop is to compute the covering range for each item and add it + */ + newset = xmlXPtrLocationSetCreate(NULL); + for (i = 0;i < oldset->locNr;i++) { + xmlXPtrLocationSetAdd(newset, + xmlXPtrInsideRange(ctxt, oldset->locTab[i])); + } + + /* + * Save the new value and cleanup + */ + valuePush(ctxt, xmlXPtrWrapLocationSet(newset)); + xmlXPathFreeObject(set); +} + +/** + * xmlXPtrRangeToFunction: + * @ctxt: the XPointer Parser context + * @nargs: the number of args + * + * Implement the range-to() XPointer function + */ +void +xmlXPtrRangeToFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr range; + const xmlChar *cur; + xmlXPathObjectPtr res, obj; + xmlXPathObjectPtr tmp; + xmlLocationSetPtr newset = NULL; + xmlNodeSetPtr oldset; + int i; + + if (ctxt == NULL) return; + CHECK_ARITY(1); + /* + * Save the expression pointer since we will have to evaluate + * it multiple times. Initialize the new set. + */ + CHECK_TYPE(XPATH_NODESET); + obj = valuePop(ctxt); + oldset = obj->nodesetval; + ctxt->context->node = NULL; + + cur = ctxt->cur; + newset = xmlXPtrLocationSetCreate(NULL); + + for (i = 0; i < oldset->nodeNr; i++) { + ctxt->cur = cur; + + /* + * Run the evaluation with a node list made of a single item + * in the nodeset. + */ + ctxt->context->node = oldset->nodeTab[i]; + tmp = xmlXPathNewNodeSet(ctxt->context->node); + valuePush(ctxt, tmp); + + xmlXPathEvalExpr(ctxt); + CHECK_ERROR; + + /* + * The result of the evaluation need to be tested to + * decided whether the filter succeeded or not + */ + res = valuePop(ctxt); + range = xmlXPtrNewRangeNodeObject(oldset->nodeTab[i], res); + if (range != NULL) { + xmlXPtrLocationSetAdd(newset, range); + } + + /* + * Cleanup + */ + if (res != NULL) + xmlXPathFreeObject(res); + if (ctxt->value == tmp) { + res = valuePop(ctxt); + xmlXPathFreeObject(res); + } + + ctxt->context->node = NULL; + } + + /* + * The result is used as the new evaluation set. + */ + xmlXPathFreeObject(obj); + ctxt->context->node = NULL; + ctxt->context->contextSize = -1; + ctxt->context->proximityPosition = -1; + valuePush(ctxt, xmlXPtrWrapLocationSet(newset)); +} + +/** + * xmlXPtrAdvanceNode: + * @cur: the node + * @level: incremented/decremented to show level in tree + * + * Advance to the next element or text node in document order + * TODO: add a stack for entering/exiting entities + * + * Returns -1 in case of failure, 0 otherwise + */ +xmlNodePtr +xmlXPtrAdvanceNode(xmlNodePtr cur, int *level) { +next: + if (cur == NULL) + return(NULL); + if (cur->children != NULL) { + cur = cur->children ; + if (level != NULL) + (*level)++; + goto found; + } +skip: /* This label should only be needed if something is wrong! */ + if (cur->next != NULL) { + cur = cur->next; + goto found; + } + do { + cur = cur->parent; + if (level != NULL) + (*level)--; + if (cur == NULL) return(NULL); + if (cur->next != NULL) { + cur = cur->next; + goto found; + } + } while (cur != NULL); + +found: + if ((cur->type != XML_ELEMENT_NODE) && + (cur->type != XML_TEXT_NODE) && + (cur->type != XML_DOCUMENT_NODE) && + (cur->type != XML_HTML_DOCUMENT_NODE) && + (cur->type != XML_CDATA_SECTION_NODE)) { + if (cur->type == XML_ENTITY_REF_NODE) { /* Shouldn't happen */ + TODO + goto skip; + } + goto next; + } + return(cur); +} + +/** + * xmlXPtrAdvanceChar: + * @node: the node + * @indx: the indx + * @bytes: the number of bytes + * + * Advance a point of the associated number of bytes (not UTF8 chars) + * + * Returns -1 in case of failure, 0 otherwise + */ +static int +xmlXPtrAdvanceChar(xmlNodePtr *node, int *indx, int bytes) { + xmlNodePtr cur; + int pos; + int len; + + if ((node == NULL) || (indx == NULL)) + return(-1); + cur = *node; + if (cur == NULL) + return(-1); + pos = *indx; + + while (bytes >= 0) { + /* + * First position to the beginning of the first text node + * corresponding to this point + */ + while ((cur != NULL) && + ((cur->type == XML_ELEMENT_NODE) || + (cur->type == XML_DOCUMENT_NODE) || + (cur->type == XML_HTML_DOCUMENT_NODE))) { + if (pos > 0) { + cur = xmlXPtrGetNthChild(cur, pos); + pos = 0; + } else { + cur = xmlXPtrAdvanceNode(cur, NULL); + pos = 0; + } + } + + if (cur == NULL) { + *node = NULL; + *indx = 0; + return(-1); + } + + /* + * if there is no move needed return the current value. + */ + if (pos == 0) pos = 1; + if (bytes == 0) { + *node = cur; + *indx = pos; + return(0); + } + /* + * We should have a text (or cdata) node ... + */ + len = 0; + if ((cur->type != XML_ELEMENT_NODE) && + (cur->content != NULL)) { + len = xmlStrlen(cur->content); + } + if (pos > len) { + /* Strange, the indx in the text node is greater than it's len */ + STRANGE + pos = len; + } + if (pos + bytes >= len) { + bytes -= (len - pos); + cur = xmlXPtrAdvanceNode(cur, NULL); + pos = 0; + } else if (pos + bytes < len) { + pos += bytes; + *node = cur; + *indx = pos; + return(0); + } + } + return(-1); +} + +/** + * xmlXPtrMatchString: + * @string: the string to search + * @start: the start textnode + * @startindex: the start index + * @end: the end textnode IN/OUT + * @endindex: the end index IN/OUT + * + * Check whether the document contains @string at the position + * (@start, @startindex) and limited by the (@end, @endindex) point + * + * Returns -1 in case of failure, 0 if not found, 1 if found in which case + * (@start, @startindex) will indicate the position of the beginning + * of the range and (@end, @endindex) will indicate the end + * of the range + */ +static int +xmlXPtrMatchString(const xmlChar *string, xmlNodePtr start, int startindex, + xmlNodePtr *end, int *endindex) { + xmlNodePtr cur; + int pos; /* 0 based */ + int len; /* in bytes */ + int stringlen; /* in bytes */ + int match; + + if (string == NULL) + return(-1); + if (start == NULL) + return(-1); + if ((end == NULL) || (endindex == NULL)) + return(-1); + cur = start; + if (cur == NULL) + return(-1); + pos = startindex - 1; + stringlen = xmlStrlen(string); + + while (stringlen > 0) { + if ((cur == *end) && (pos + stringlen > *endindex)) + return(0); + + if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) { + len = xmlStrlen(cur->content); + if (len >= pos + stringlen) { + match = (!xmlStrncmp(&cur->content[pos], string, stringlen)); + if (match) { +#ifdef DEBUG_RANGES + xmlGenericError(xmlGenericErrorContext, + "found range %d bytes at index %d of ->", + stringlen, pos + 1); + xmlDebugDumpString(stdout, cur->content); + xmlGenericError(xmlGenericErrorContext, "\n"); +#endif + *end = cur; + *endindex = pos + stringlen; + return(1); + } else { + return(0); + } + } else { + int sub = len - pos; + match = (!xmlStrncmp(&cur->content[pos], string, sub)); + if (match) { +#ifdef DEBUG_RANGES + xmlGenericError(xmlGenericErrorContext, + "found subrange %d bytes at index %d of ->", + sub, pos + 1); + xmlDebugDumpString(stdout, cur->content); + xmlGenericError(xmlGenericErrorContext, "\n"); +#endif + string = &string[sub]; + stringlen -= sub; + } else { + return(0); + } + } + } + cur = xmlXPtrAdvanceNode(cur, NULL); + if (cur == NULL) + return(0); + pos = 0; + } + return(1); +} + +/** + * xmlXPtrSearchString: + * @string: the string to search + * @start: the start textnode IN/OUT + * @startindex: the start index IN/OUT + * @end: the end textnode + * @endindex: the end index + * + * Search the next occurrence of @string within the document content + * until the (@end, @endindex) point is reached + * + * Returns -1 in case of failure, 0 if not found, 1 if found in which case + * (@start, @startindex) will indicate the position of the beginning + * of the range and (@end, @endindex) will indicate the end + * of the range + */ +static int +xmlXPtrSearchString(const xmlChar *string, xmlNodePtr *start, int *startindex, + xmlNodePtr *end, int *endindex) { + xmlNodePtr cur; + const xmlChar *str; + int pos; /* 0 based */ + int len; /* in bytes */ + xmlChar first; + + if (string == NULL) + return(-1); + if ((start == NULL) || (startindex == NULL)) + return(-1); + if ((end == NULL) || (endindex == NULL)) + return(-1); + cur = *start; + if (cur == NULL) + return(-1); + pos = *startindex - 1; + first = string[0]; + + while (cur != NULL) { + if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) { + len = xmlStrlen(cur->content); + while (pos <= len) { + if (first != 0) { + str = xmlStrchr(&cur->content[pos], first); + if (str != NULL) { + pos = (str - (xmlChar *)(cur->content)); +#ifdef DEBUG_RANGES + xmlGenericError(xmlGenericErrorContext, + "found '%c' at index %d of ->", + first, pos + 1); + xmlDebugDumpString(stdout, cur->content); + xmlGenericError(xmlGenericErrorContext, "\n"); +#endif + if (xmlXPtrMatchString(string, cur, pos + 1, + end, endindex)) { + *start = cur; + *startindex = pos + 1; + return(1); + } + pos++; + } else { + pos = len + 1; + } + } else { + /* + * An empty string is considered to match before each + * character of the string-value and after the final + * character. + */ +#ifdef DEBUG_RANGES + xmlGenericError(xmlGenericErrorContext, + "found '' at index %d of ->", + pos + 1); + xmlDebugDumpString(stdout, cur->content); + xmlGenericError(xmlGenericErrorContext, "\n"); +#endif + *start = cur; + *startindex = pos + 1; + *end = cur; + *endindex = pos + 1; + return(1); + } + } + } + if ((cur == *end) && (pos >= *endindex)) + return(0); + cur = xmlXPtrAdvanceNode(cur, NULL); + if (cur == NULL) + return(0); + pos = 1; + } + return(0); +} + +/** + * xmlXPtrGetLastChar: + * @node: the node + * @index: the index + * + * Computes the point coordinates of the last char of this point + * + * Returns -1 in case of failure, 0 otherwise + */ +static int +xmlXPtrGetLastChar(xmlNodePtr *node, int *indx) { + xmlNodePtr cur; + int pos, len = 0; + + if ((node == NULL) || (indx == NULL)) + return(-1); + cur = *node; + pos = *indx; + + if (cur == NULL) + return(-1); + + if ((cur->type == XML_ELEMENT_NODE) || + (cur->type == XML_DOCUMENT_NODE) || + (cur->type == XML_HTML_DOCUMENT_NODE)) { + if (pos > 0) { + cur = xmlXPtrGetNthChild(cur, pos); + pos = 0; + } + } + while (cur != NULL) { + if (cur->last != NULL) + cur = cur->last; + else if ((cur->type != XML_ELEMENT_NODE) && + (cur->content != NULL)) { + len = xmlStrlen(cur->content); + break; + } else { + return(-1); + } + } + if (cur == NULL) + return(-1); + *node = cur; + *indx = len; + return(0); +} + +/** + * xmlXPtrGetStartPoint: + * @obj: an range + * @node: the resulting node + * @indx: the resulting index + * + * read the object and return the start point coordinates. + * + * Returns -1 in case of failure, 0 otherwise + */ +static int +xmlXPtrGetStartPoint(xmlXPathObjectPtr obj, xmlNodePtr *node, int *indx) { + if ((obj == NULL) || (node == NULL) || (indx == NULL)) + return(-1); + + switch (obj->type) { + case XPATH_POINT: + *node = obj->user; + if (obj->index <= 0) + *indx = 0; + else + *indx = obj->index; + return(0); + case XPATH_RANGE: + *node = obj->user; + if (obj->index <= 0) + *indx = 0; + else + *indx = obj->index; + return(0); + default: + break; + } + return(-1); +} + +/** + * xmlXPtrGetEndPoint: + * @obj: an range + * @node: the resulting node + * @indx: the resulting indx + * + * read the object and return the end point coordinates. + * + * Returns -1 in case of failure, 0 otherwise + */ +static int +xmlXPtrGetEndPoint(xmlXPathObjectPtr obj, xmlNodePtr *node, int *indx) { + if ((obj == NULL) || (node == NULL) || (indx == NULL)) + return(-1); + + switch (obj->type) { + case XPATH_POINT: + *node = obj->user; + if (obj->index <= 0) + *indx = 0; + else + *indx = obj->index; + return(0); + case XPATH_RANGE: + *node = obj->user; + if (obj->index <= 0) + *indx = 0; + else + *indx = obj->index; + return(0); + default: + break; + } + return(-1); +} + +/** + * xmlXPtrStringRangeFunction: + * @ctxt: the XPointer Parser context + * @nargs: the number of args + * + * Function implementing the string-range() function + * range as described in 5.4.2 + * + * ------------------------------ + * [Definition: For each location in the location-set argument, + * string-range returns a set of string ranges, a set of substrings in a + * string. Specifically, the string-value of the location is searched for + * substrings that match the string argument, and the resulting location-set + * will contain a range location for each non-overlapping match.] + * An empty string is considered to match before each character of the + * string-value and after the final character. Whitespace in a string + * is matched literally, with no normalization except that provided by + * XML for line ends. The third argument gives the position of the first + * character to be in the resulting range, relative to the start of the + * match. The default value is 1, which makes the range start immediately + * before the first character of the matched string. The fourth argument + * gives the number of characters in the range; the default is that the + * range extends to the end of the matched string. + * + * Element boundaries, as well as entire embedded nodes such as processing + * instructions and comments, are ignored as defined in [XPath]. + * + * If the string in the second argument is not found in the string-value + * of the location, or if a value in the third or fourth argument indicates + * a string that is beyond the beginning or end of the document, the + * expression fails. + * + * The points of the range-locations in the returned location-set will + * all be character points. + * ------------------------------ + */ +void +xmlXPtrStringRangeFunction(xmlXPathParserContextPtr ctxt, int nargs) { + int i, startindex, endindex = 0, fendindex; + xmlNodePtr start, end = 0, fend; + xmlXPathObjectPtr set; + xmlLocationSetPtr oldset; + xmlLocationSetPtr newset; + xmlXPathObjectPtr string; + xmlXPathObjectPtr position = NULL; + xmlXPathObjectPtr number = NULL; + int found, pos = 0, num = 0; + + /* + * Grab the arguments + */ + if ((nargs < 2) || (nargs > 4)) + XP_ERROR(XPATH_INVALID_ARITY); + + if (nargs >= 4) { + CHECK_TYPE(XPATH_NUMBER); + number = valuePop(ctxt); + if (number != NULL) + num = (int) number->floatval; + } + if (nargs >= 3) { + CHECK_TYPE(XPATH_NUMBER); + position = valuePop(ctxt); + if (position != NULL) + pos = (int) position->floatval; + } + CHECK_TYPE(XPATH_STRING); + string = valuePop(ctxt); + if ((ctxt->value == NULL) || + ((ctxt->value->type != XPATH_LOCATIONSET) && + (ctxt->value->type != XPATH_NODESET))) + XP_ERROR(XPATH_INVALID_TYPE) + + set = valuePop(ctxt); + newset = xmlXPtrLocationSetCreate(NULL); + if (set->nodesetval == NULL) { + goto error; + } + if (set->type == XPATH_NODESET) { + xmlXPathObjectPtr tmp; + + /* + * First convert to a location set + */ + tmp = xmlXPtrNewLocationSetNodeSet(set->nodesetval); + xmlXPathFreeObject(set); + set = tmp; + } + oldset = (xmlLocationSetPtr) set->user; + + /* + * The loop is to search for each element in the location set + * the list of location set corresponding to that search + */ + for (i = 0;i < oldset->locNr;i++) { +#ifdef DEBUG_RANGES + xmlXPathDebugDumpObject(stdout, oldset->locTab[i], 0); +#endif + + xmlXPtrGetStartPoint(oldset->locTab[i], &start, &startindex); + xmlXPtrGetEndPoint(oldset->locTab[i], &end, &endindex); + xmlXPtrAdvanceChar(&start, &startindex, 0); + xmlXPtrGetLastChar(&end, &endindex); + +#ifdef DEBUG_RANGES + xmlGenericError(xmlGenericErrorContext, + "from index %d of ->", startindex); + xmlDebugDumpString(stdout, start->content); + xmlGenericError(xmlGenericErrorContext, "\n"); + xmlGenericError(xmlGenericErrorContext, + "to index %d of ->", endindex); + xmlDebugDumpString(stdout, end->content); + xmlGenericError(xmlGenericErrorContext, "\n"); +#endif + do { + fend = end; + fendindex = endindex; + found = xmlXPtrSearchString(string->stringval, &start, &startindex, + &fend, &fendindex); + if (found == 1) { + if (position == NULL) { + xmlXPtrLocationSetAdd(newset, + xmlXPtrNewRange(start, startindex, fend, fendindex)); + } else if (xmlXPtrAdvanceChar(&start, &startindex, + pos - 1) == 0) { + if ((number != NULL) && (num > 0)) { + int rindx; + xmlNodePtr rend; + rend = start; + rindx = startindex - 1; + if (xmlXPtrAdvanceChar(&rend, &rindx, + num) == 0) { + xmlXPtrLocationSetAdd(newset, + xmlXPtrNewRange(start, startindex, + rend, rindx)); + } + } else if ((number != NULL) && (num <= 0)) { + xmlXPtrLocationSetAdd(newset, + xmlXPtrNewRange(start, startindex, + start, startindex)); + } else { + xmlXPtrLocationSetAdd(newset, + xmlXPtrNewRange(start, startindex, + fend, fendindex)); + } + } + start = fend; + startindex = fendindex; + if (string->stringval[0] == 0) + startindex++; + } + } while (found == 1); + } + + /* + * Save the new value and cleanup + */ +error: + valuePush(ctxt, xmlXPtrWrapLocationSet(newset)); + xmlXPathFreeObject(set); + xmlXPathFreeObject(string); + if (position) xmlXPathFreeObject(position); + if (number) xmlXPathFreeObject(number); +} + +/** + * xmlXPtrEvalRangePredicate: + * @ctxt: the XPointer Parser context + * + * [8] Predicate ::= '[' PredicateExpr ']' + * [9] PredicateExpr ::= Expr + * + * Evaluate a predicate as in xmlXPathEvalPredicate() but for + * a Location Set instead of a node set + */ +void +xmlXPtrEvalRangePredicate(xmlXPathParserContextPtr ctxt) { + const xmlChar *cur; + xmlXPathObjectPtr res; + xmlXPathObjectPtr obj, tmp; + xmlLocationSetPtr newset = NULL; + xmlLocationSetPtr oldset; + int i; + + if (ctxt == NULL) return; + + SKIP_BLANKS; + if (CUR != '[') { + XP_ERROR(XPATH_INVALID_PREDICATE_ERROR); + } + NEXT; + SKIP_BLANKS; + + /* + * Extract the old set, and then evaluate the result of the + * expression for all the element in the set. use it to grow + * up a new set. + */ + CHECK_TYPE(XPATH_LOCATIONSET); + obj = valuePop(ctxt); + oldset = obj->user; + ctxt->context->node = NULL; + + if ((oldset == NULL) || (oldset->locNr == 0)) { + ctxt->context->contextSize = 0; + ctxt->context->proximityPosition = 0; + xmlXPathEvalExpr(ctxt); + res = valuePop(ctxt); + if (res != NULL) + xmlXPathFreeObject(res); + valuePush(ctxt, obj); + CHECK_ERROR; + } else { + /* + * Save the expression pointer since we will have to evaluate + * it multiple times. Initialize the new set. + */ + cur = ctxt->cur; + newset = xmlXPtrLocationSetCreate(NULL); + + for (i = 0; i < oldset->locNr; i++) { + ctxt->cur = cur; + + /* + * Run the evaluation with a node list made of a single item + * in the nodeset. + */ + ctxt->context->node = oldset->locTab[i]->user; + tmp = xmlXPathNewNodeSet(ctxt->context->node); + valuePush(ctxt, tmp); + ctxt->context->contextSize = oldset->locNr; + ctxt->context->proximityPosition = i + 1; + + xmlXPathEvalExpr(ctxt); + CHECK_ERROR; + + /* + * The result of the evaluation need to be tested to + * decided whether the filter succeeded or not + */ + res = valuePop(ctxt); + if (xmlXPathEvaluatePredicateResult(ctxt, res)) { + xmlXPtrLocationSetAdd(newset, + xmlXPathObjectCopy(oldset->locTab[i])); + } + + /* + * Cleanup + */ + if (res != NULL) + xmlXPathFreeObject(res); + if (ctxt->value == tmp) { + res = valuePop(ctxt); + xmlXPathFreeObject(res); + } + + ctxt->context->node = NULL; + } + + /* + * The result is used as the new evaluation set. + */ + xmlXPathFreeObject(obj); + ctxt->context->node = NULL; + ctxt->context->contextSize = -1; + ctxt->context->proximityPosition = -1; + valuePush(ctxt, xmlXPtrWrapLocationSet(newset)); + } + if (CUR != ']') { + XP_ERROR(XPATH_INVALID_PREDICATE_ERROR); + } + + NEXT; + SKIP_BLANKS; +} + +#define bottom_xpointer +#include "elfgcchack.h" +#endif +