From 47350adcb6ea48512d732bc323eb1835a5ac9908 Mon Sep 17 00:00:00 2001 From: Martin Sustrik Date: Fri, 11 Sep 2009 18:16:47 +0200 Subject: separate class for PUB-style socket added --- c/zmq.h | 6 +++--- src/Makefile.am | 2 ++ src/app_thread.cpp | 5 ++++- src/pub.cpp | 39 +++++++++++++++++++++++++++++++++++++++ src/pub.hpp | 41 +++++++++++++++++++++++++++++++++++++++++ src/sub.cpp | 12 ++++++++++++ src/sub.hpp | 2 ++ 7 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 src/pub.cpp create mode 100644 src/pub.hpp diff --git a/c/zmq.h b/c/zmq.h index df6e04c..cb86dcc 100644 --- a/c/zmq.h +++ b/c/zmq.h @@ -184,12 +184,12 @@ ZMQ_EXPORT int zmq_connect (void *s, const char *addr); // // Errors: EAGAIN - message cannot be sent at the moment (applies only to // non-blocking send). -// ENOTSUP - function isn't supported by particular socket type. +// EFAULT - function isn't supported by particular socket type. ZMQ_EXPORT int zmq_send (void *s, struct zmq_msg_t *msg, int flags); // Flush the messages that were send using ZMQ_NOFLUSH flag down the stream. // -// Errors: ENOTSUP - function isn't supported by particular socket type. +// Errors: FAULT - function isn't supported by particular socket type. ZMQ_EXPORT int zmq_flush (void *s); // Send a message from the socket 's'. 'flags' argument can be combination @@ -198,7 +198,7 @@ ZMQ_EXPORT int zmq_flush (void *s); // // Errors: EAGAIN - message cannot be received at the moment (applies only to // non-blocking receive). -// ENOTSUP - function isn't supported by particular socket type. +// EFAULT - function isn't supported by particular socket type. ZMQ_EXPORT int zmq_recv (void *s, struct zmq_msg_t *msg, int flags); // Helper functions used by perf tests so that they don't have to care diff --git a/src/Makefile.am b/src/Makefile.am index 68b34fa..a09c001 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -65,6 +65,7 @@ libzmq_la_SOURCES = $(pgm_sources) \ pipe.hpp \ platform.hpp \ poll.hpp \ + pub.hpp \ select.hpp \ session.hpp \ simple_semaphore.hpp \ @@ -103,6 +104,7 @@ libzmq_la_SOURCES = $(pgm_sources) \ owned.cpp \ pipe.cpp \ poll.cpp \ + pub.cpp \ select.cpp \ session.cpp \ socket_base.cpp \ diff --git a/src/app_thread.cpp b/src/app_thread.cpp index 2bcc724..c48657a 100644 --- a/src/app_thread.cpp +++ b/src/app_thread.cpp @@ -35,6 +35,7 @@ #include "pipe.hpp" #include "config.hpp" #include "socket_base.hpp" +#include "pub.hpp" #include "sub.hpp" // If the RDTSC is available we use it to prevent excessive @@ -138,11 +139,13 @@ zmq::socket_base_t *zmq::app_thread_t::create_socket (int type_) { socket_base_t *s = NULL; switch (type_) { + case ZMQ_PUB: + s = new pub_t (this); + break; case ZMQ_SUB: s = new sub_t (this); break; case ZMQ_P2P: - case ZMQ_PUB: case ZMQ_REQ: case ZMQ_REP: s = new socket_base_t (this); diff --git a/src/pub.cpp b/src/pub.cpp new file mode 100644 index 0000000..9f2729b --- /dev/null +++ b/src/pub.cpp @@ -0,0 +1,39 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see . +*/ + +#include "../c/zmq.h" + +#include "pub.hpp" +#include "err.hpp" + +zmq::pub_t::pub_t (class app_thread_t *parent_) : + socket_base_t (parent_) +{ +} + +zmq::pub_t::~pub_t () +{ +} + +int zmq::pub_t::recv (struct zmq_msg_t *msg_, int flags_) +{ + errno = EFAULT; + return -1; +} + diff --git a/src/pub.hpp b/src/pub.hpp new file mode 100644 index 0000000..2f03b8e --- /dev/null +++ b/src/pub.hpp @@ -0,0 +1,41 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify it under + the terms of the Lesser GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + 0MQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + Lesser GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __ZMQ_PUB_INCLUDED__ +#define __ZMQ_PUB_INCLUDED__ + +#include "socket_base.hpp" + +namespace zmq +{ + + class pub_t : public socket_base_t + { + public: + + pub_t (class app_thread_t *parent_); + ~pub_t (); + + // Overloads of API functions from socket_base_t. + int recv (struct zmq_msg_t *msg_, int flags_); + }; + +} + +#endif diff --git a/src/sub.cpp b/src/sub.cpp index 8c1ef9b..d4545b5 100644 --- a/src/sub.cpp +++ b/src/sub.cpp @@ -78,6 +78,18 @@ int zmq::sub_t::setsockopt (int option_, const void *optval_, return socket_base_t::setsockopt (option_, optval_, optvallen_); } +int zmq::sub_t::send (struct zmq_msg_t *msg_, int flags_) +{ + errno = EFAULT; + return -1; +} + +int zmq::sub_t::flush () +{ + errno = EFAULT; + return -1; +} + int zmq::sub_t::recv (struct zmq_msg_t *msg_, int flags_) { while (true) { diff --git a/src/sub.hpp b/src/sub.hpp index c88d30c..14fa687 100644 --- a/src/sub.hpp +++ b/src/sub.hpp @@ -37,6 +37,8 @@ namespace zmq // Overloads of API functions from socket_base_t. int setsockopt (int option_, const void *optval_, size_t optvallen_); + int send (struct zmq_msg_t *msg_, int flags_); + int flush (); int recv (struct zmq_msg_t *msg_, int flags_); private: -- cgit v1.2.3 From 36707529a7d82b164b59d42fe0d5d8a35c3dd279 Mon Sep 17 00:00:00 2001 From: Martin Sustrik Date: Mon, 14 Sep 2009 09:40:35 +0200 Subject: minor merge problem corrected --- src/pub.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pub.cpp b/src/pub.cpp index 9f2729b..d6eca01 100644 --- a/src/pub.cpp +++ b/src/pub.cpp @@ -23,7 +23,7 @@ #include "err.hpp" zmq::pub_t::pub_t (class app_thread_t *parent_) : - socket_base_t (parent_) + socket_base_t (parent_, ZMQ_SUB) { } -- cgit v1.2.3 From c806aabb2d3e6b1ba9e3f61319f23d45c7f9a007 Mon Sep 17 00:00:00 2001 From: Martin Sustrik Date: Mon, 14 Sep 2009 11:25:57 +0200 Subject: java binding sets socket options using setsockopt function --- c/zmq.h | 19 ++++---- java/Socket.cpp | 114 +++++++++++++++++++---------------------------- java/org/zmq/Socket.java | 58 +++++++----------------- src/options.cpp | 5 +-- src/options.hpp | 1 - src/socket_base.cpp | 16 ++----- 6 files changed, 77 insertions(+), 136 deletions(-) diff --git a/c/zmq.h b/c/zmq.h index 4ba8450..d2ca20a 100644 --- a/c/zmq.h +++ b/c/zmq.h @@ -44,16 +44,15 @@ extern "C" { #define ZMQ_VSM 32 // Socket options. -#define ZMQ_HWM 1 -#define ZMQ_LWM 2 -#define ZMQ_SWAP 3 -#define ZMQ_MASK 4 -#define ZMQ_AFFINITY 5 -#define ZMQ_IDENTITY 6 -#define ZMQ_SUBSCRIBE 7 -#define ZMQ_UNSUBSCRIBE 8 -#define ZMQ_RATE 9 -#define ZMQ_RECOVERY_IVL 10 +#define ZMQ_HWM 1 // int64_t +#define ZMQ_LWM 2 // int64_t +#define ZMQ_SWAP 3 // int64_t +#define ZMQ_AFFINITY 4 // int64_t +#define ZMQ_IDENTITY 5 // string +#define ZMQ_SUBSCRIBE 6 // string +#define ZMQ_UNSUBSCRIBE 7 // string +#define ZMQ_RATE 8 // int64_t +#define ZMQ_RECOVERY_IVL 9 // int64_t // The operation should be performed in non-blocking mode. I.e. if it cannot // be processed immediately, error should be returned with errno set to EAGAIN. diff --git a/java/Socket.cpp b/java/Socket.cpp index 51ee816..a7b2fe5 100644 --- a/java/Socket.cpp +++ b/java/Socket.cpp @@ -86,80 +86,60 @@ JNIEXPORT void JNICALL Java_org_zmq_Socket_finalize (JNIEnv *env, jobject obj) assert (rc == 0); } -JNIEXPORT void JNICALL Java_org_zmq_Socket_setHwm (JNIEnv *env, jobject obj, - jlong hwm) +JNIEXPORT void JNICALL Java_org_zmq_Socket_setsockopt__IJ (JNIEnv *env, + jobject obj, jint option, jlong optval) { - void *s = (void*) env->GetLongField (obj, socket_handle_fid); - assert (s); - int rc = zmq_setsockopt (s, ZMQ_HWM, &hwm, sizeof hwm); - if (rc == -1) - raise_exception (env, errno); -} - -JNIEXPORT void JNICALL Java_org_zmq_Socket_setLwm (JNIEnv *env, jobject obj, - jlong lwm) -{ - void *s = (void*) env->GetLongField (obj, socket_handle_fid); - assert (s); - - int rc = zmq_setsockopt (s, ZMQ_LWM, &lwm, sizeof lwm); - if (rc == -1) - raise_exception (env, errno); -} - -JNIEXPORT void JNICALL Java_org_zmq_Socket_setSwap (JNIEnv *env, jobject obj, - jlong swap_size) -{ - void *s = (void*) env->GetLongField (obj, socket_handle_fid); - assert (s); - - int rc = zmq_setsockopt (s, ZMQ_SWAP, &swap_size, sizeof swap_size); - if (rc == -1) - raise_exception (env, errno); -} - -JNIEXPORT void JNICALL Java_org_zmq_Socket_setMask (JNIEnv *env, jobject obj, - jlong mask) -{ - void *s = (void*) env->GetLongField (obj, socket_handle_fid); - assert (s); - - int rc = zmq_setsockopt (s, ZMQ_MASK, &mask, sizeof mask); - if (rc == -1) - raise_exception (env, errno); -} - -JNIEXPORT void JNICALL Java_org_zmq_Socket_setAffinity (JNIEnv *env, - jobject obj, jlong affinity) -{ - void *s = (void*) env->GetLongField (obj, socket_handle_fid); - assert (s); - - int rc = zmq_setsockopt (s, ZMQ_AFFINITY, &affinity, sizeof affinity); - if (rc == -1) - raise_exception (env, errno); + switch (option) { + case ZMQ_HWM: + case ZMQ_LWM: + case ZMQ_SWAP: + case ZMQ_AFFINITY: + case ZMQ_RATE: + case ZMQ_RECOVERY_IVL: + { + void *s = (void*) env->GetLongField (obj, socket_handle_fid); + assert (s); + + int64_t value = optval; + int rc = zmq_setsockopt (s, option, &value, sizeof (value)); + if (rc != 0) + raise_exception (env, errno); + return; + } + default: + raise_exception (env, EINVAL); + return; + } } -JNIEXPORT void JNICALL Java_org_zmq_Socket_setIdentity (JNIEnv *env, - jobject obj, jstring identity) +JNIEXPORT void JNICALL Java_org_zmq_Socket_setsockopt__ILjava_lang_String_2 ( + JNIEnv *env, jobject obj, jint option, jstring optval) { - void *s = (void*) env->GetLongField (obj, socket_handle_fid); - assert (s); - - if (identity == NULL) { + switch (option) { + case ZMQ_IDENTITY: + case ZMQ_SUBSCRIBE: + case ZMQ_UNSUBSCRIBE: + { + if (optval == NULL) { + raise_exception (env, EINVAL); + return; + } + + void *s = (void*) env->GetLongField (obj, socket_handle_fid); + assert (s); + + const char *value = env->GetStringUTFChars (optval, NULL); + assert (value); + int rc = zmq_setsockopt (s, option, value, strlen (value)); + env->ReleaseStringUTFChars (optval, value); + if (rc != 0) + raise_exception (env, errno); + return; + } + default: raise_exception (env, EINVAL); return; } - - const char *c_identity = env->GetStringUTFChars (identity, NULL); - if (c_identity == NULL) - return; - - int rc = zmq_setsockopt (s, ZMQ_IDENTITY, c_identity, sizeof c_identity); - env->ReleaseStringUTFChars (identity, c_identity); - - if (rc == -1) - raise_exception (env, errno); } JNIEXPORT void JNICALL Java_org_zmq_Socket_bind (JNIEnv *env, jobject obj, diff --git a/java/org/zmq/Socket.java b/java/org/zmq/Socket.java index 4c6a3d3..0d96c71 100644 --- a/java/org/zmq/Socket.java +++ b/java/org/zmq/Socket.java @@ -27,19 +27,24 @@ public class Socket } public static final int NOBLOCK = 1; - public static final int NOFLUSH = 2; public static final int P2P = 0; - public static final int PUB = 1; - public static final int SUB = 2; - public static final int REQ = 3; - public static final int REP = 4; + public static final int HWM = 1; + public static final int LWM = 2; + public static final int SWAP = 3; + public static final int AFFINITY = 4; + public static final int IDENTITY = 5; + public static final int SUBSCRIBE = 6; + public static final int UNSUBSCRIBE = 7; + public static final int RATE = 8; + public static final int RECOVERY_IVL = 9; + /** * Class constructor. * @@ -51,46 +56,13 @@ public class Socket } /** - * Set the high watermark on the socket. - * - * @param hwm high watermark. - */ - public native void setHwm (long hwm); - - /** - * Set the low watermark on the socket. - * - * @param lwm low watermark. - */ - public native void setLwm (long lwm); - - /** - * Set swap size. - * - * @param swap_size swap size. - */ - public native void setSwap (long swap_size); - - /** - * Set reception mask. - * - * @param mask mask. - */ - public native void setMask (long mask); - - /** - * Set affinity. - * - * @param affinity - */ - public native void setAffinity (long affinity); - - /** - * Set identity. + * Set the socket option value. * - * @param identity + * @param option ID of the option to set + * @param optval value to set the option to */ - public native void setIdentity (String identity); + public native void setsockopt (int option, long optval); + public native void setsockopt (int option, String optval); /** * Bind to network interface. Start listening for new connections. diff --git a/src/options.cpp b/src/options.cpp index 804cb4f..a39d312 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -23,9 +23,8 @@ zmq::options_t::options_t () : hwm (0), lwm (0), swap (0), - mask (0), affinity (0), - rate (0), - recovery_ivl (0) + rate (100), + recovery_ivl (10) { } diff --git a/src/options.hpp b/src/options.hpp index 9f4a264..4d359e3 100644 --- a/src/options.hpp +++ b/src/options.hpp @@ -34,7 +34,6 @@ namespace zmq int64_t hwm; int64_t lwm; int64_t swap; - uint64_t mask; uint64_t affinity; std::string identity; diff --git a/src/socket_base.cpp b/src/socket_base.cpp index 9412d67..0429726 100644 --- a/src/socket_base.cpp +++ b/src/socket_base.cpp @@ -125,14 +125,6 @@ int zmq::socket_base_t::setsockopt (int option_, const void *optval_, options.swap = *((int64_t*) optval_); return 0; - case ZMQ_MASK: - if (optvallen_ != sizeof (int64_t)) { - errno = EINVAL; - return -1; - } - options.mask = (uint64_t) *((int64_t*) optval_); - return 0; - case ZMQ_AFFINITY: if (optvallen_ != sizeof (int64_t)) { errno = EINVAL; @@ -151,19 +143,19 @@ int zmq::socket_base_t::setsockopt (int option_, const void *optval_, return -1; case ZMQ_RATE: - if (optvallen_ != sizeof (uint32_t)) { + if (optvallen_ != sizeof (int64_t)) { errno = EINVAL; return -1; } - options.rate = *((int32_t*) optval_); + options.rate = (uint32_t) *((int64_t*) optval_); return 0; case ZMQ_RECOVERY_IVL: - if (optvallen_ != sizeof (uint32_t)) { + if (optvallen_ != sizeof (int64_t)) { errno = EINVAL; return -1; } - options.recovery_ivl = *((int32_t*) optval_); + options.recovery_ivl = (uint32_t) *((int64_t*) optval_); return 0; default: -- cgit v1.2.3 From 37cacc5700eaaaddbe2df6e3affeca4a335b023a Mon Sep 17 00:00:00 2001 From: Martin Sustrik Date: Mon, 14 Sep 2009 12:28:13 +0200 Subject: ZMQII-1: Win32 - failure on shutdown --- java/Socket.cpp | 2 ++ msvc/libzmq/libzmq.vcproj | 4 ++++ src/select.cpp | 4 ++-- src/socket_base.cpp | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/java/Socket.cpp b/java/Socket.cpp index a7b2fe5..2a2f420 100644 --- a/java/Socket.cpp +++ b/java/Socket.cpp @@ -22,6 +22,8 @@ #include #include +#include "../src/stdint.hpp" + #include "zmq.h" #include "org_zmq_Socket.h" diff --git a/msvc/libzmq/libzmq.vcproj b/msvc/libzmq/libzmq.vcproj index 27986d4..697b75f 100644 --- a/msvc/libzmq/libzmq.vcproj +++ b/msvc/libzmq/libzmq.vcproj @@ -229,6 +229,10 @@ RelativePath="..\..\src\poll.cpp" > + + diff --git a/src/select.cpp b/src/select.cpp index f10acdc..cb17169 100644 --- a/src/select.cpp +++ b/src/select.cpp @@ -53,10 +53,10 @@ zmq::select_t::select_t () : zmq::select_t::~select_t () { + worker.stop (); + // Make sure there are no fds registered on shutdown. zmq_assert (load.get () == 0); - - worker.stop (); } zmq::handle_t zmq::select_t::add_fd (fd_t fd_, i_poll_events *events_) diff --git a/src/socket_base.cpp b/src/socket_base.cpp index 0429726..51649fb 100644 --- a/src/socket_base.cpp +++ b/src/socket_base.cpp @@ -281,7 +281,7 @@ int zmq::socket_base_t::connect (const char *addr_) #endif // Unknown address type. - errno = ENOTSUP; + errno = EFAULT; return -1; } -- cgit v1.2.3 From 2bc9419ced21151fe90c530758dc85b7024fdb70 Mon Sep 17 00:00:00 2001 From: Martin Sustrik Date: Mon, 14 Sep 2009 13:54:30 +0200 Subject: ZMQII-10: Make connections interrupted during the init phase be closed silently --- src/zmq_decoder.cpp | 13 +++++++++++-- src/zmq_listener_init.cpp | 7 +++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/zmq_decoder.cpp b/src/zmq_decoder.cpp index e51d802..53811a1 100644 --- a/src/zmq_decoder.cpp +++ b/src/zmq_decoder.cpp @@ -20,6 +20,7 @@ #include "zmq_decoder.hpp" #include "i_inout.hpp" #include "wire.hpp" +#include "err.hpp" zmq::zmq_decoder_t::zmq_decoder_t () : destination (NULL) @@ -48,7 +49,11 @@ bool zmq::zmq_decoder_t::one_byte_size_ready () if (*tmpbuf == 0xff) next_step (tmpbuf, 8, &zmq_decoder_t::eight_byte_size_ready); else { - zmq_msg_init_size (&in_progress, *tmpbuf); + + // TODO: Handle over-sized message decently. + int rc = zmq_msg_init_size (&in_progress, *tmpbuf); + errno_assert (rc == 0); + next_step (zmq_msg_data (&in_progress), *tmpbuf, &zmq_decoder_t::message_ready); } @@ -60,7 +65,11 @@ bool zmq::zmq_decoder_t::eight_byte_size_ready () // 8-byte size is read. Allocate the buffer for message body and // read the message data into it. size_t size = (size_t) get_uint64 (tmpbuf); - zmq_msg_init_size (&in_progress, size); + + // TODO: Handle over-sized message decently. + int rc = zmq_msg_init_size (&in_progress, size); + errno_assert (rc == 0); + next_step (zmq_msg_data (&in_progress), size, &zmq_decoder_t::message_ready); return true; diff --git a/src/zmq_listener_init.cpp b/src/zmq_listener_init.cpp index 98a3780..756e9d8 100644 --- a/src/zmq_listener_init.cpp +++ b/src/zmq_listener_init.cpp @@ -93,8 +93,11 @@ void zmq::zmq_listener_init_t::flush () void zmq::zmq_listener_init_t::detach () { - // TODO: Engine is closing down. Init object is to be closed as well. - zmq_assert (false); + // This function is called by engine when disconnection occurs. + // The engine will destroy itself, so we just drop the pointer here and + // start termination of the init object. + engine = NULL; + term (); } void zmq::zmq_listener_init_t::process_plug () -- cgit v1.2.3 From e2900ce0a1b11ec212aeaf42bbefb26a54697c25 Mon Sep 17 00:00:00 2001 From: Martin Sustrik Date: Mon, 14 Sep 2009 14:30:15 +0200 Subject: xmlParser added --- foreign/xmlParser/xmlParser.cpp | 2888 +++++++++++++++++++++++++++++++++++++++ foreign/xmlParser/xmlParser.hpp | 762 +++++++++++ 2 files changed, 3650 insertions(+) create mode 100644 foreign/xmlParser/xmlParser.cpp create mode 100644 foreign/xmlParser/xmlParser.hpp diff --git a/foreign/xmlParser/xmlParser.cpp b/foreign/xmlParser/xmlParser.cpp new file mode 100644 index 0000000..2414c01 --- /dev/null +++ b/foreign/xmlParser/xmlParser.cpp @@ -0,0 +1,2888 @@ +/** + **************************************************************************** + *

XML.c - implementation file for basic XML parser written in ANSI C++ + * for portability. It works by using recursion and a node tree for breaking + * down the elements of an XML document.

+ * + * @version V2.39 + * @author Frank Vanden Berghen + * + * NOTE: + * + * If you add "#define STRICT_PARSING", on the first line of this file + * the parser will see the following XML-stream: + * some textother text + * as an error. Otherwise, this tring will be equivalent to: + * some textother text + * + * NOTE: + * + * If you add "#define APPROXIMATE_PARSING" on the first line of this file + * the parser will see the following XML-stream: + * + * + * + * as equivalent to the following XML-stream: + * + * + * + * This can be useful for badly-formed XML-streams but prevent the use + * of the following XML-stream (problem is: tags at contiguous levels + * have the same names): + * + * + * + * + * + * + * NOTE: + * + * If you add "#define _XMLPARSER_NO_MESSAGEBOX_" on the first line of this file + * the "openFileHelper" function will always display error messages inside the + * console instead of inside a message-box-window. Message-box-windows are + * available on windows 9x/NT/2000/XP/Vista only. + * + * Copyright (c) 2002, Frank Vanden Berghen + * All rights reserved. + * + * The following license terms apply to projects that are in some way related to + * the "ZeroMQ project", including applications + * using "ZeroMQ project" and tools developed + * for enhancing "ZeroMQ project". All other projects + * (not related to "ZeroMQ project") have to use this + * code under the Aladdin Free Public License (AFPL) + * See the file "AFPL-license.txt" for more informations about the AFPL license. + * (see http://www.artifex.com/downloads/doc/Public.htm for detailed AFPL terms) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Frank Vanden Berghen nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Frank Vanden Berghen ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **************************************************************************** + */ +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE +#endif +#include +#ifdef _XMLWINDOWS +//#ifdef _DEBUG +//#define _CRTDBG_MAP_ALLOC +//#include +//#endif +#define WIN32_LEAN_AND_MEAN +#include // to have IsTextUnicode, MultiByteToWideChar, WideCharToMultiByte to handle unicode files + // to have "MessageBoxA" to display error messages for openFilHelper +#endif + +#include +#include +#include +#include +#include + +XMLCSTR XMLNode::getVersion() { return _CXML("v2.39"); } +void freeXMLString(XMLSTR t){if(t)free(t);} + +static XMLNode::XMLCharEncoding characterEncoding=XMLNode::char_encoding_UTF8; +static char guessWideCharChars=1, dropWhiteSpace=1, removeCommentsInMiddleOfText=1; + +inline int mmin( const int t1, const int t2 ) { return t1 < t2 ? t1 : t2; } + +// You can modify the initialization of the variable "XMLClearTags" below +// to change the clearTags that are currently recognized by the library. +// The number on the second columns is the length of the string inside the +// first column. The "") }, + { _CXML("") }, + { _CXML("") }, + { _CXML("
")    ,5,  _CXML("
") }, +// { _CXML("")}, + { NULL ,0, NULL } +}; + +// You can modify the initialization of the variable "XMLEntities" below +// to change the character entities that are currently recognized by the library. +// The number on the second columns is the length of the string inside the +// first column. Additionally, the syntaxes " " and " " are recognized. +typedef struct { XMLCSTR s; int l; XMLCHAR c;} XMLCharacterEntity; +static XMLCharacterEntity XMLEntities[] = +{ + { _CXML("&" ), 5, _CXML('&' )}, + { _CXML("<" ), 4, _CXML('<' )}, + { _CXML(">" ), 4, _CXML('>' )}, + { _CXML("""), 6, _CXML('\"')}, + { _CXML("'"), 6, _CXML('\'')}, + { NULL , 0, '\0' } +}; + +// When rendering the XMLNode to a string (using the "createXMLString" function), +// you can ask for a beautiful formatting. This formatting is using the +// following indentation character: +#define INDENTCHAR _CXML('\t') + +// The following function parses the XML errors into a user friendly string. +// You can edit this to change the output language of the library to something else. +XMLCSTR XMLNode::getError(XMLError xerror) +{ + switch (xerror) + { + case eXMLErrorNone: return _CXML("No error"); + case eXMLErrorMissingEndTag: return _CXML("Warning: Unmatched end tag"); + case eXMLErrorNoXMLTagFound: return _CXML("Warning: No XML tag found"); + case eXMLErrorEmpty: return _CXML("Error: No XML data"); + case eXMLErrorMissingTagName: return _CXML("Error: Missing start tag name"); + case eXMLErrorMissingEndTagName: return _CXML("Error: Missing end tag name"); + case eXMLErrorUnmatchedEndTag: return _CXML("Error: Unmatched end tag"); + case eXMLErrorUnmatchedEndClearTag: return _CXML("Error: Unmatched clear tag end"); + case eXMLErrorUnexpectedToken: return _CXML("Error: Unexpected token found"); + case eXMLErrorNoElements: return _CXML("Error: No elements found"); + case eXMLErrorFileNotFound: return _CXML("Error: File not found"); + case eXMLErrorFirstTagNotFound: return _CXML("Error: First Tag not found"); + case eXMLErrorUnknownCharacterEntity:return _CXML("Error: Unknown character entity"); + case eXMLErrorCharacterCodeAbove255: return _CXML("Error: Character code above 255 is forbidden in MultiByte char mode."); + case eXMLErrorCharConversionError: return _CXML("Error: unable to convert between WideChar and MultiByte chars"); + case eXMLErrorCannotOpenWriteFile: return _CXML("Error: unable to open file for writing"); + case eXMLErrorCannotWriteFile: return _CXML("Error: cannot write into file"); + + case eXMLErrorBase64DataSizeIsNotMultipleOf4: return _CXML("Warning: Base64-string length is not a multiple of 4"); + case eXMLErrorBase64DecodeTruncatedData: return _CXML("Warning: Base64-string is truncated"); + case eXMLErrorBase64DecodeIllegalCharacter: return _CXML("Error: Base64-string contains an illegal character"); + case eXMLErrorBase64DecodeBufferTooSmall: return _CXML("Error: Base64 decode output buffer is too small"); + }; + return _CXML("Unknown"); +} + +///////////////////////////////////////////////////////////////////////// +// Here start the abstraction layer to be OS-independent // +///////////////////////////////////////////////////////////////////////// + +// Here is an abstraction layer to access some common string manipulation functions. +// The abstraction layer is currently working for gcc, Microsoft Visual Studio 6.0, +// Microsoft Visual Studio .NET, CC (sun compiler) and Borland C++. +// If you plan to "port" the library to a new system/compiler, all you have to do is +// to edit the following lines. +#ifdef XML_NO_WIDE_CHAR +char myIsTextWideChar(const void *b, int len) { return FALSE; } +#else + #if defined (UNDER_CE) || !defined(_XMLWINDOWS) + char myIsTextWideChar(const void *b, int len) // inspired by the Wine API: RtlIsTextUnicode + { +#ifdef sun + // for SPARC processors: wchar_t* buffers must always be alligned, otherwise it's a char* buffer. + if ((((unsigned long)b)%sizeof(wchar_t))!=0) return FALSE; +#endif + const wchar_t *s=(const wchar_t*)b; + + // buffer too small: + if (len<(int)sizeof(wchar_t)) return FALSE; + + // odd length test + if (len&1) return FALSE; + + /* only checks the first 256 characters */ + len=mmin(256,len/sizeof(wchar_t)); + + // Check for the special byte order: + if (*((unsigned short*)s) == 0xFFFE) return TRUE; // IS_TEXT_UNICODE_REVERSE_SIGNATURE; + if (*((unsigned short*)s) == 0xFEFF) return TRUE; // IS_TEXT_UNICODE_SIGNATURE + + // checks for ASCII characters in the UNICODE stream + int i,stats=0; + for (i=0; ilen/2) return TRUE; + + // Check for UNICODE NULL chars + for (i=0; i + static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return wsncasecmp(c1,c2,l);} + static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return wsncmp(c1,c2,l);} + static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return wscasecmp(c1,c2); } + #else + // for gcc + static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return wcsncasecmp(c1,c2,l);} + static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return wcsncmp(c1,c2,l);} + static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return wcscasecmp(c1,c2); } + #endif + static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)wcsstr(c1,c2); } + static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) { return (XMLSTR)wcscpy(c1,c2); } + static inline FILE *xfopen(XMLCSTR filename,XMLCSTR mode) + { + char *filenameAscii=myWideCharToMultiByte(filename); + FILE *f; + if (mode[0]==_CXML('r')) f=fopen(filenameAscii,"rb"); + else f=fopen(filenameAscii,"wb"); + free(filenameAscii); + return f; + } + #else + static inline FILE *xfopen(XMLCSTR filename,XMLCSTR mode) { return fopen(filename,mode); } + static inline int xstrlen(XMLCSTR c) { return strlen(c); } + static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return strncasecmp(c1,c2,l);} + static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return strncmp(c1,c2,l);} + static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return strcasecmp(c1,c2); } + static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)strstr(c1,c2); } + static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) { return (XMLSTR)strcpy(c1,c2); } + #endif + static inline int _strnicmp(const char *c1,const char *c2, int l) { return strncasecmp(c1,c2,l);} +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// the "xmltoc,xmltob,xmltoi,xmltol,xmltof,xmltoa" functions // +/////////////////////////////////////////////////////////////////////////////// +// These 6 functions are not used inside the XMLparser. +// There are only here as "convenience" functions for the user. +// If you don't need them, you can delete them without any trouble. +#ifdef _XMLWIDECHAR + #ifdef _XMLWINDOWS + // for Microsoft Visual Studio 6.0 and Microsoft Visual Studio .NET and Borland C++ Builder 6.0 + char xmltob(XMLCSTR t,int v){ if (t&&(*t)) return (char)_wtoi(t); return v; } + int xmltoi(XMLCSTR t,int v){ if (t&&(*t)) return _wtoi(t); return v; } + long xmltol(XMLCSTR t,long v){ if (t&&(*t)) return _wtol(t); return v; } + double xmltof(XMLCSTR t,double v){ if (t&&(*t)) wscanf(t, "%f", &v); /*v=_wtof(t);*/ return v; } + #else + #ifdef sun + // for CC + #include + char xmltob(XMLCSTR t,int v){ if (t) return (char)wstol(t,NULL,10); return v; } + int xmltoi(XMLCSTR t,int v){ if (t) return (int)wstol(t,NULL,10); return v; } + long xmltol(XMLCSTR t,long v){ if (t) return wstol(t,NULL,10); return v; } + #else + // for gcc + char xmltob(XMLCSTR t,int v){ if (t) return (char)wcstol(t,NULL,10); return v; } + int xmltoi(XMLCSTR t,int v){ if (t) return (int)wcstol(t,NULL,10); return v; } + long xmltol(XMLCSTR t,long v){ if (t) return wcstol(t,NULL,10); return v; } + #endif + double xmltof(XMLCSTR t,double v){ if (t&&(*t)) wscanf(t, "%f", &v); /*v=_wtof(t);*/ return v; } + #endif +#else + char xmltob(XMLCSTR t,char v){ if (t&&(*t)) return (char)atoi(t); return v; } + int xmltoi(XMLCSTR t,int v){ if (t&&(*t)) return atoi(t); return v; } + long xmltol(XMLCSTR t,long v){ if (t&&(*t)) return atol(t); return v; } + double xmltof(XMLCSTR t,double v){ if (t&&(*t)) return atof(t); return v; } +#endif +XMLCSTR xmltoa(XMLCSTR t,XMLCSTR v){ if (t) return t; return v; } +XMLCHAR xmltoc(XMLCSTR t,XMLCHAR v){ if (t&&(*t)) return *t; return v; } + +///////////////////////////////////////////////////////////////////////// +// the "openFileHelper" function // +///////////////////////////////////////////////////////////////////////// + +// Since each application has its own way to report and deal with errors, you should modify & rewrite +// the following "openFileHelper" function to get an "error reporting mechanism" tailored to your needs. +XMLNode XMLNode::openFileHelper(XMLCSTR filename, XMLCSTR tag) +{ + // guess the value of the global parameter "characterEncoding" + // (the guess is based on the first 200 bytes of the file). + FILE *f=xfopen(filename,_CXML("rb")); + if (f) + { + char bb[205]; + int l=(int)fread(bb,1,200,f); + setGlobalOptions(guessCharEncoding(bb,l),guessWideCharChars,dropWhiteSpace,removeCommentsInMiddleOfText); + fclose(f); + } + + // parse the file + XMLResults pResults; + XMLNode xnode=XMLNode::parseFile(filename,tag,&pResults); + + // display error message (if any) + if (pResults.error != eXMLErrorNone) + { + // create message + char message[2000],*s1=(char*)"",*s3=(char*)""; XMLCSTR s2=_CXML(""); + if (pResults.error==eXMLErrorFirstTagNotFound) { s1=(char*)"First Tag should be '"; s2=tag; s3=(char*)"'.\n"; } + sprintf(message, +#ifdef _XMLWIDECHAR + "XML Parsing error inside file '%S'.\n%S\nAt line %i, column %i.\n%s%S%s" +#else + "XML Parsing error inside file '%s'.\n%s\nAt line %i, column %i.\n%s%s%s" +#endif + ,filename,XMLNode::getError(pResults.error),pResults.nLine,pResults.nColumn,s1,s2,s3); + + // display message +#if defined(_XMLWINDOWS) && !defined(UNDER_CE) && !defined(_XMLPARSER_NO_MESSAGEBOX_) + MessageBoxA(NULL,message,"XML Parsing error",MB_OK|MB_ICONERROR|MB_TOPMOST); +#else + printf("%s",message); +#endif + exit(255); + } + return xnode; +} + +///////////////////////////////////////////////////////////////////////// +// Here start the core implementation of the XMLParser library // +///////////////////////////////////////////////////////////////////////// + +// You should normally not change anything below this point. + +#ifndef _XMLWIDECHAR +// If "characterEncoding=ascii" then we assume that all characters have the same length of 1 byte. +// If "characterEncoding=UTF8" then the characters have different lengths (from 1 byte to 4 bytes). +// If "characterEncoding=ShiftJIS" then the characters have different lengths (from 1 byte to 2 bytes). +// This table is used as lookup-table to know the length of a character (in byte) based on the +// content of the first byte of the character. +// (note: if you modify this, you must always have XML_utf8ByteTable[0]=0 ). +static const char XML_utf8ByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x00 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x10 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x20 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x30 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x40 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x50 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x60 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x70 End of ASCII range + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x80 0x80 to 0xc1 invalid + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x90 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xa0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xb0 + 1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xc0 0xc2 to 0xdf 2 byte + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xd0 + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,// 0xe0 0xe0 to 0xef 3 byte + 4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid +}; +static const char XML_legacyByteTable[256] = +{ + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +}; +static const char XML_sjisByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x00 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x10 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x20 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x30 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x40 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x50 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x60 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x70 + 1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0x80 0x81 to 0x9F 2 bytes + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0x90 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xa0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xb0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xc0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xd0 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xe0 0xe0 to 0xef 2 bytes + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 // 0xf0 +}; +static const char XML_gb2312ByteTable[256] = +{ +// 0 1 2 3 4 5 6 7 8 9 a b c d e f + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x00 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x10 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x20 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x30 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x40 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x50 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x60 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x70 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x80 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x90 + 1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xa0 0xa1 to 0xf7 2 bytes + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xb0 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xc0 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xd0 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xe0 + 2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1 // 0xf0 +}; +static const char XML_gbk_big5_ByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x00 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x10 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x20 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x30 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x40 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x50 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x60 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x70 + 1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0x80 0x81 to 0xfe 2 bytes + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0x90 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xa0 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xb0 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xc0 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xd0 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xe0 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1 // 0xf0 +}; +static const char *XML_ByteTable=(const char *)XML_utf8ByteTable; // the default is "characterEncoding=XMLNode::encoding_UTF8" +#endif + + +XMLNode XMLNode::emptyXMLNode; +XMLClear XMLNode::emptyXMLClear={ NULL, NULL, NULL}; +XMLAttribute XMLNode::emptyXMLAttribute={ NULL, NULL}; + +// Enumeration used to decipher what type a token is +typedef enum XMLTokenTypeTag +{ + eTokenText = 0, + eTokenQuotedText, + eTokenTagStart, /* "<" */ + eTokenTagEnd, /* "" */ + eTokenEquals, /* "=" */ + eTokenDeclaration, /* "" */ + eTokenClear, + eTokenError +} XMLTokenType; + +// Main structure used for parsing XML +typedef struct XML +{ + XMLCSTR lpXML; + XMLCSTR lpszText; + int nIndex,nIndexMissigEndTag; + enum XMLError error; + XMLCSTR lpEndTag; + int cbEndTag; + XMLCSTR lpNewElement; + int cbNewElement; + int nFirst; +} XML; + +typedef struct +{ + ALLXMLClearTag *pClr; + XMLCSTR pStr; +} NextToken; + +// Enumeration used when parsing attributes +typedef enum Attrib +{ + eAttribName = 0, + eAttribEquals, + eAttribValue +} Attrib; + +// Enumeration used when parsing elements to dictate whether we are currently +// inside a tag +typedef enum Status +{ + eInsideTag = 0, + eOutsideTag +} Status; + +XMLError XMLNode::writeToFile(XMLCSTR filename, const char *encoding, char nFormat) const +{ + if (!d) return eXMLErrorNone; + FILE *f=xfopen(filename,_CXML("wb")); + if (!f) return eXMLErrorCannotOpenWriteFile; +#ifdef _XMLWIDECHAR + unsigned char h[2]={ 0xFF, 0xFE }; + if (!fwrite(h,2,1,f)) return eXMLErrorCannotWriteFile; + if ((!isDeclaration())&&((d->lpszName)||(!getChildNode().isDeclaration()))) + { + if (!fwrite(L"\n",sizeof(wchar_t)*40,1,f)) + return eXMLErrorCannotWriteFile; + } +#else + if ((!isDeclaration())&&((d->lpszName)||(!getChildNode().isDeclaration()))) + { + if (characterEncoding==char_encoding_UTF8) + { + // header so that windows recognize the file as UTF-8: + unsigned char h[3]={0xEF,0xBB,0xBF}; if (!fwrite(h,3,1,f)) return eXMLErrorCannotWriteFile; + encoding="utf-8"; + } else if (characterEncoding==char_encoding_ShiftJIS) encoding="SHIFT-JIS"; + + if (!encoding) encoding="ISO-8859-1"; + if (fprintf(f,"\n",encoding)<0) return eXMLErrorCannotWriteFile; + } else + { + if (characterEncoding==char_encoding_UTF8) + { + unsigned char h[3]={0xEF,0xBB,0xBF}; if (!fwrite(h,3,1,f)) return eXMLErrorCannotWriteFile; + } + } +#endif + int i; + XMLSTR t=createXMLString(nFormat,&i); + if (!fwrite(t,sizeof(XMLCHAR)*i,1,f)) return eXMLErrorCannotWriteFile; + if (fclose(f)!=0) return eXMLErrorCannotWriteFile; + free(t); + return eXMLErrorNone; +} + +// Duplicate a given string. +XMLSTR stringDup(XMLCSTR lpszData, int cbData) +{ + if (lpszData==NULL) return NULL; + + XMLSTR lpszNew; + if (cbData==-1) cbData=(int)xstrlen(lpszData); + lpszNew = (XMLSTR)malloc((cbData+1) * sizeof(XMLCHAR)); + if (lpszNew) + { + memcpy(lpszNew, lpszData, (cbData) * sizeof(XMLCHAR)); + lpszNew[cbData] = (XMLCHAR)NULL; + } + return lpszNew; +} + +XMLSTR ToXMLStringTool::toXMLUnSafe(XMLSTR dest,XMLCSTR source) +{ + XMLSTR dd=dest; + XMLCHAR ch; + XMLCharacterEntity *entity; + while ((ch=*source)) + { + entity=XMLEntities; + do + { + if (ch==entity->c) {xstrcpy(dest,entity->s); dest+=entity->l; source++; goto out_of_loop1; } + entity++; + } while(entity->s); +#ifdef _XMLWIDECHAR + *(dest++)=*(source++); +#else + switch(XML_ByteTable[(unsigned char)ch]) + { + case 4: *(dest++)=*(source++); + case 3: *(dest++)=*(source++); + case 2: *(dest++)=*(source++); + case 1: *(dest++)=*(source++); + } +#endif +out_of_loop1: + ; + } + *dest=0; + return dd; +} + +// private (used while rendering): +int ToXMLStringTool::lengthXMLString(XMLCSTR source) +{ + int r=0; + XMLCharacterEntity *entity; + XMLCHAR ch; + while ((ch=*source)) + { + entity=XMLEntities; + do + { + if (ch==entity->c) { r+=entity->l; source++; goto out_of_loop1; } + entity++; + } while(entity->s); +#ifdef _XMLWIDECHAR + r++; source++; +#else + ch=XML_ByteTable[(unsigned char)ch]; r+=ch; source+=ch; +#endif +out_of_loop1: + ; + } + return r; +} + +ToXMLStringTool::~ToXMLStringTool(){ freeBuffer(); } +void ToXMLStringTool::freeBuffer(){ if (buf) free(buf); buf=NULL; buflen=0; } +XMLSTR ToXMLStringTool::toXML(XMLCSTR source) +{ + int l=lengthXMLString(source)+1; + if (l>buflen) { buflen=l; buf=(XMLSTR)realloc(buf,l*sizeof(XMLCHAR)); } + return toXMLUnSafe(buf,source); +} + +// private: +XMLSTR fromXMLString(XMLCSTR s, int lo, XML *pXML) +{ + // This function is the opposite of the function "toXMLString". It decodes the escape + // sequences &, ", ', <, > and replace them by the characters + // &,",',<,>. This function is used internally by the XML Parser. All the calls to + // the XML library will always gives you back "decoded" strings. + // + // in: string (s) and length (lo) of string + // out: new allocated string converted from xml + if (!s) return NULL; + + int ll=0,j; + XMLSTR d; + XMLCSTR ss=s; + XMLCharacterEntity *entity; + while ((lo>0)&&(*s)) + { + if (*s==_CXML('&')) + { + if ((lo>2)&&(s[1]==_CXML('#'))) + { + s+=2; lo-=2; + if ((*s==_CXML('X'))||(*s==_CXML('x'))) { s++; lo--; } + while ((*s)&&(*s!=_CXML(';'))&&((lo--)>0)) s++; + if (*s!=_CXML(';')) + { + pXML->error=eXMLErrorUnknownCharacterEntity; + return NULL; + } + s++; lo--; + } else + { + entity=XMLEntities; + do + { + if ((lo>=entity->l)&&(xstrnicmp(s,entity->s,entity->l)==0)) { s+=entity->l; lo-=entity->l; break; } + entity++; + } while(entity->s); + if (!entity->s) + { + pXML->error=eXMLErrorUnknownCharacterEntity; + return NULL; + } + } + } else + { +#ifdef _XMLWIDECHAR + s++; lo--; +#else + j=XML_ByteTable[(unsigned char)*s]; s+=j; lo-=j; ll+=j-1; +#endif + } + ll++; + } + + d=(XMLSTR)malloc((ll+1)*sizeof(XMLCHAR)); + s=d; + while (ll-->0) + { + if (*ss==_CXML('&')) + { + if (ss[1]==_CXML('#')) + { + ss+=2; j=0; + if ((*ss==_CXML('X'))||(*ss==_CXML('x'))) + { + ss++; + while (*ss!=_CXML(';')) + { + if ((*ss>=_CXML('0'))&&(*ss<=_CXML('9'))) j=(j<<4)+*ss-_CXML('0'); + else if ((*ss>=_CXML('A'))&&(*ss<=_CXML('F'))) j=(j<<4)+*ss-_CXML('A')+10; + else if ((*ss>=_CXML('a'))&&(*ss<=_CXML('f'))) j=(j<<4)+*ss-_CXML('a')+10; + else { free((void*)s); pXML->error=eXMLErrorUnknownCharacterEntity;return NULL;} + ss++; + } + } else + { + while (*ss!=_CXML(';')) + { + if ((*ss>=_CXML('0'))&&(*ss<=_CXML('9'))) j=(j*10)+*ss-_CXML('0'); + else { free((void*)s); pXML->error=eXMLErrorUnknownCharacterEntity;return NULL;} + ss++; + } + } +#ifndef _XMLWIDECHAR + if (j>255) { free((void*)s); pXML->error=eXMLErrorCharacterCodeAbove255;return NULL;} +#endif + (*d++)=(XMLCHAR)j; ss++; + } else + { + entity=XMLEntities; + do + { + if (xstrnicmp(ss,entity->s,entity->l)==0) { *(d++)=entity->c; ss+=entity->l; break; } + entity++; + } while(entity->s); + } + } else + { +#ifdef _XMLWIDECHAR + *(d++)=*(ss++); +#else + switch(XML_ByteTable[(unsigned char)*ss]) + { + case 4: *(d++)=*(ss++); ll--; + case 3: *(d++)=*(ss++); ll--; + case 2: *(d++)=*(ss++); ll--; + case 1: *(d++)=*(ss++); + } +#endif + } + } + *d=0; + return (XMLSTR)s; +} + +#define XML_isSPACECHAR(ch) ((ch==_CXML('\n'))||(ch==_CXML(' '))||(ch== _CXML('\t'))||(ch==_CXML('\r'))) + +// private: +char myTagCompare(XMLCSTR cclose, XMLCSTR copen) +// !!!! WARNING strange convention&: +// return 0 if equals +// return 1 if different +{ + if (!cclose) return 1; + int l=(int)xstrlen(cclose); + if (xstrnicmp(cclose, copen, l)!=0) return 1; + const XMLCHAR c=copen[l]; + if (XML_isSPACECHAR(c)|| + (c==_CXML('/' ))|| + (c==_CXML('<' ))|| + (c==_CXML('>' ))|| + (c==_CXML('=' ))) return 0; + return 1; +} + +// Obtain the next character from the string. +static inline XMLCHAR getNextChar(XML *pXML) +{ + XMLCHAR ch = pXML->lpXML[pXML->nIndex]; +#ifdef _XMLWIDECHAR + if (ch!=0) pXML->nIndex++; +#else + pXML->nIndex+=XML_ByteTable[(unsigned char)ch]; +#endif + return ch; +} + +// Find the next token in a string. +// pcbToken contains the number of characters that have been read. +static NextToken GetNextToken(XML *pXML, int *pcbToken, enum XMLTokenTypeTag *pType) +{ + NextToken result; + XMLCHAR ch; + XMLCHAR chTemp; + int indexStart,nFoundMatch,nIsText=FALSE; + result.pClr=NULL; // prevent warning + + // Find next non-white space character + do { indexStart=pXML->nIndex; ch=getNextChar(pXML); } while XML_isSPACECHAR(ch); + + if (ch) + { + // Cache the current string pointer + result.pStr = &pXML->lpXML[indexStart]; + + // First check whether the token is in the clear tag list (meaning it + // does not need formatting). + ALLXMLClearTag *ctag=XMLClearTags; + do + { + if (xstrncmp(ctag->lpszOpen, result.pStr, ctag->openTagLen)==0) + { + result.pClr=ctag; + pXML->nIndex+=ctag->openTagLen-1; + *pType=eTokenClear; + return result; + } + ctag++; + } while(ctag->lpszOpen); + + // If we didn't find a clear tag then check for standard tokens + switch(ch) + { + // Check for quotes + case _CXML('\''): + case _CXML('\"'): + // Type of token + *pType = eTokenQuotedText; + chTemp = ch; + + // Set the size + nFoundMatch = FALSE; + + // Search through the string to find a matching quote + while((ch = getNextChar(pXML))) + { + if (ch==chTemp) { nFoundMatch = TRUE; break; } + if (ch==_CXML('<')) break; + } + + // If we failed to find a matching quote + if (nFoundMatch == FALSE) + { + pXML->nIndex=indexStart+1; + nIsText=TRUE; + break; + } + +// 4.02.2002 +// if (FindNonWhiteSpace(pXML)) pXML->nIndex--; + + break; + + // Equals (used with attribute values) + case _CXML('='): + *pType = eTokenEquals; + break; + + // Close tag + case _CXML('>'): + *pType = eTokenCloseTag; + break; + + // Check for tag start and tag end + case _CXML('<'): + + // Peek at the next character to see if we have an end tag 'lpXML[pXML->nIndex]; + + // If we have a tag end... + if (chTemp == _CXML('/')) + { + // Set the type and ensure we point at the next character + getNextChar(pXML); + *pType = eTokenTagEnd; + } + + // If we have an XML declaration tag + else if (chTemp == _CXML('?')) + { + + // Set the type and ensure we point at the next character + getNextChar(pXML); + *pType = eTokenDeclaration; + } + + // Otherwise we must have a start tag + else + { + *pType = eTokenTagStart; + } + break; + + // Check to see if we have a short hand type end tag ('/>'). + case _CXML('/'): + + // Peek at the next character to see if we have a short end tag '/>' + chTemp = pXML->lpXML[pXML->nIndex]; + + // If we have a short hand end tag... + if (chTemp == _CXML('>')) + { + // Set the type and ensure we point at the next character + getNextChar(pXML); + *pType = eTokenShortHandClose; + break; + } + + // If we haven't found a short hand closing tag then drop into the + // text process + + // Other characters + default: + nIsText = TRUE; + } + + // If this is a TEXT node + if (nIsText) + { + // Indicate we are dealing with text + *pType = eTokenText; + while((ch = getNextChar(pXML))) + { + if XML_isSPACECHAR(ch) + { + indexStart++; break; + + } else if (ch==_CXML('/')) + { + // If we find a slash then this maybe text or a short hand end tag + // Peek at the next character to see it we have short hand end tag + ch=pXML->lpXML[pXML->nIndex]; + // If we found a short hand end tag then we need to exit the loop + if (ch==_CXML('>')) { pXML->nIndex--; break; } + + } else if ((ch==_CXML('<'))||(ch==_CXML('>'))||(ch==_CXML('='))) + { + pXML->nIndex--; break; + } + } + } + *pcbToken = pXML->nIndex-indexStart; + } else + { + // If we failed to obtain a valid character + *pcbToken = 0; + *pType = eTokenError; + result.pStr=NULL; + } + + return result; +} + +XMLCSTR XMLNode::updateName_WOSD(XMLSTR lpszName) +{ + if (!d) { free(lpszName); return NULL; } + if (d->lpszName&&(lpszName!=d->lpszName)) free((void*)d->lpszName); + d->lpszName=lpszName; + return lpszName; +} + +// private: +XMLNode::XMLNode(struct XMLNodeDataTag *p){ d=p; (p->ref_count)++; } +XMLNode::XMLNode(XMLNodeData *pParent, XMLSTR lpszName, char isDeclaration) +{ + d=(XMLNodeData*)malloc(sizeof(XMLNodeData)); + d->ref_count=1; + + d->lpszName=NULL; + d->nChild= 0; + d->nText = 0; + d->nClear = 0; + d->nAttribute = 0; + + d->isDeclaration = isDeclaration; + + d->pParent = pParent; + d->pChild= NULL; + d->pText= NULL; + d->pClear= NULL; + d->pAttribute= NULL; + d->pOrder= NULL; + + updateName_WOSD(lpszName); +} + +XMLNode XMLNode::createXMLTopNode_WOSD(XMLSTR lpszName, char isDeclaration) { return XMLNode(NULL,lpszName,isDeclaration); } +XMLNode XMLNode::createXMLTopNode(XMLCSTR lpszName, char isDeclaration) { return XMLNode(NULL,stringDup(lpszName),isDeclaration); } + +#define MEMORYINCREASE 50 + +static inline void myFree(void *p) { if (p) free(p); } +static inline void *myRealloc(void *p, int newsize, int memInc, int sizeofElem) +{ + if (p==NULL) { if (memInc) return malloc(memInc*sizeofElem); return malloc(sizeofElem); } + if ((memInc==0)||((newsize%memInc)==0)) p=realloc(p,(newsize+memInc)*sizeofElem); +// if (!p) +// { +// printf("XMLParser Error: Not enough memory! Aborting...\n"); exit(220); +// } + return p; +} + +// private: +XMLElemen