├── .gitignore
├── CMakeLists.txt
├── COPYING
├── COPYING.LESSER
├── COPYING.MIT
├── INSTALL
├── Makefile-all
├── README
├── README.md
├── README_PlanetLabs.md
├── cmake_uninstall.cmake.in
├── documentation
├── index.html
├── wjelement.html
├── wjreader.html
└── wjwriter.html
├── example
├── Makefile
├── example.c
├── layout.json
├── layout.schema
└── validate.c
├── include
├── CMakeLists.txt
├── memmgr.h
├── nmutil.h
├── wjelement.h
├── wjreader.h
├── wjwriter.h
└── xpl.h
├── pkg-config.pc.cmake
├── src
├── CMakeLists.txt
├── cli
│ ├── CMakeLists.txt
│ ├── cmds.c
│ ├── wje
│ ├── wjecli.c
│ └── wjecli.h
├── lib
│ ├── CMakeLists.txt
│ └── xpl.c
├── wjelement
│ ├── CMakeLists.txt
│ ├── element.c
│ ├── element.h
│ ├── hash.c
│ ├── schema.c
│ ├── search.c
│ ├── types.c
│ └── wjeunit.c
├── wjreader
│ ├── CMakeLists.txt
│ └── wjreader.c
└── wjwriter
│ ├── CMakeLists.txt
│ └── wjwriter.c
└── windows
├── wjelement-VC100.sln
├── wjelement-VC100.vcxproj
├── wjelement.sln
└── wjelement.vcproj
/.gitignore:
--------------------------------------------------------------------------------
1 | # CMake files
2 | CMakeCache.txt
3 | CMakeFiles
4 | CMakeScripts
5 | Makefile
6 | CTestTestfile.cmake
7 | cmake_install.cmake
8 | cmake_uninstall.cmake
9 | install_manifest.txt
10 |
11 | # Output files
12 | wjelement.pc
13 | wjecli
14 | wjeunit
15 |
16 | # Compiled Object files
17 | *.slo
18 | *.lo
19 | *.o
20 | *.obj
21 |
22 | # Precompiled Headers
23 | *.gch
24 | *.pch
25 |
26 | # Compiled Dynamic libraries
27 | *.so
28 | *.so.*
29 | *.dylib
30 | *.dll
31 |
32 | # Fortran module files
33 | *.mod
34 |
35 | # Compiled Static libraries
36 | *.lai
37 | *.la
38 | *.a
39 | *.lib
40 |
41 | # Executables
42 | *.exe
43 | *.out
44 | *.app
45 |
46 | # Log files
47 | *.log
48 |
49 | # Visual Studio specific files
50 | Win32/
51 | x64/
52 | ipch/
53 | *.suo
54 | *.sdf
55 | *.user
56 | *.tlog
57 |
58 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | project(WJElement)
2 | cmake_minimum_required(VERSION 2.6)
3 | include(CheckIncludeFiles)
4 | include(CheckSymbolExists)
5 |
6 | option(STATIC_LIB "Compile as static libraries" OFF)
7 |
8 | if(STATIC_LIB)
9 | set(BUILD_SHARED_LIBS OFF)
10 | add_definitions(-DCOMPILE_AS_STATIC)
11 | else(STATIC_LIB)
12 | set(BUILD_SHARED_LIBS ON)
13 | endif(STATIC_LIB)
14 |
15 | option(DISTINGUISH_INTEGERS "Distinguish between integer and non-integer numbers" OFF)
16 | if(DISTINGUISH_INTEGERS)
17 | add_definitions(-DWJE_DISTINGUISH_INTEGER_TYPE)
18 | endif()
19 |
20 | if("${CMAKE_SYSTEM}" MATCHES "Linux")
21 | add_definitions(-D_GNU_SOURCE)
22 | endif()
23 |
24 | if(NOT CMAKE_INSTALL_PREFIX)
25 | set(CMAKE_INSTALL_PREFIX /usr/local)
26 | endif(NOT CMAKE_INSTALL_PREFIX)
27 | set(INC_DEST_DIR ${CMAKE_INSTALL_PREFIX}/include)
28 | if(NOT LIB_DEST_DIR)
29 | set(LIB_DEST_DIR ${CMAKE_INSTALL_PREFIX}/lib)
30 | endif(NOT LIB_DEST_DIR)
31 | set(BIN_DEST_DIR ${CMAKE_INSTALL_PREFIX}/bin)
32 |
33 | if(CMAKE_BUILD_TYPE MATCHES Debug)
34 | add_definitions(-DDEBUG)
35 | option(SHOWNOTES "Show preprocessor notes" OFF)
36 |
37 | if(CMAKE_COMPILER_IS_GNUCC)
38 | # GCC specific debug options
39 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -g3 -ggdb3 -gdwarf-2")
40 | set(AVOID_VERSION -avoid-version)
41 | endif(CMAKE_COMPILER_IS_GNUCC)
42 | endif(CMAKE_BUILD_TYPE MATCHES Debug)
43 |
44 | add_definitions(-DHAVE_CONFIG_H)
45 |
46 | check_include_files(string.h HAVE_STRING_H)
47 | check_symbol_exists(strcasecmp "strings.h" HAVE_STRCASECMP)
48 |
49 | check_include_files(regex.h HAVE_REGEX_H)
50 | if(HAVE_REGEX_H)
51 | add_definitions(-DHAVE_REGEX_H)
52 | else(HAVE_REGEX_H)
53 | message("*** WARNING: GNU C regex library not found.")
54 | message(" WJESchemaValidate() will not support:")
55 | message(" pattern")
56 | message(" patternProperties")
57 | message(" format")
58 | endif(HAVE_REGEX_H)
59 |
60 | # use, i.e. don't skip the full RPATH for the build tree
61 | SET(CMAKE_SKIP_BUILD_RPATH FALSE)
62 | # when building, don't use the install RPATH already
63 | # (but later on when installing)
64 | SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
65 | # the RPATH to be used when installing
66 | SET(CMAKE_INSTALL_RPATH ${LIB_DEST_DIR})
67 | # add the automatically determined parts of the RPATH
68 | # which point to directories outside the build tree to the install RPATH
69 | SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
70 |
71 | ENABLE_TESTING(1)
72 |
73 | INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/documentation/
74 | DESTINATION doc/wjelement/
75 | )
76 |
77 | include_directories(include)
78 | add_subdirectory(src)
79 | add_subdirectory(include)
80 |
81 | # pkg-config .pc
82 | SET(PKG_CONFIG_REQUIRES glib-2.0)
83 | SET(PKG_CONFIG_LIBDIR
84 | ${LIB_DEST_DIR}
85 | )
86 | SET(PKG_CONFIG_INCLUDEDIR
87 | ${INC_DEST_DIR}
88 | )
89 | SET(PKG_CONFIG_LIBS
90 | "-L\${libdir} -lwjelement -lwjreader -lwjwriter"
91 | )
92 | SET(PKG_CONFIG_CFLAGS
93 | "-I\${includedir}"
94 | )
95 | CONFIGURE_FILE(
96 | "${CMAKE_CURRENT_SOURCE_DIR}/pkg-config.pc.cmake"
97 | "${CMAKE_CURRENT_BINARY_DIR}/wjelement.pc"
98 | )
99 | INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/wjelement.pc"
100 | DESTINATION ${LIB_DEST_DIR}/pkgconfig)
101 |
102 | # uninstall target
103 | configure_file(
104 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
105 | "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
106 | IMMEDIATE @ONLY)
107 |
108 | add_custom_target(uninstall
109 | COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
110 |
111 | SET(MAJOR_VERSION 1)
112 | SET(MINOR_VERSION 0)
113 | SET(PATCH_VERSION 1)
114 | IF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")
115 |
116 | INCLUDE (InstallRequiredSystemLibraries)
117 |
118 | SET (CPACK_SET_DESTDIR "on")
119 | SET (CPACK_PACKAGING_INSTALL_PREFIX "/tmp")
120 | SET (CPACK_GENERATOR "DEB")
121 |
122 | SET (CPACK_DEBIAN_PACKAGE_PRIORITY "extra")
123 | SET (CPACK_DEBIAN_PACKAGE_SECTION "libs")
124 | SET (CPACK_DEBIAN_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
125 | # autogenerate dependency information
126 | set (CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
127 |
128 | SET (CPACK_PACKAGE_DESCRIPTION "Short description")
129 | SET (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Long description")
130 | SET (CPACK_PACKAGE_VENDOR "Titan Lien (digbil)")
131 | SET (CPACK_PACKAGE_CONTACT "titan@digbil.com")
132 | SET (CPACK_PACKAGE_VERSION_MAJOR "${MAJOR_VERSION}")
133 | SET (CPACK_PACKAGE_VERSION_MINOR "${MINOR_VERSION}")
134 | SET (CPACK_PACKAGE_VERSION_PATCH "${PATCH_VERSION}")
135 |
136 | SET (CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${MAJOR_VERSION}.${MINOR_VERSION}.${CPACK_PACKAGE_VERSION_PATCH}")
137 | SET (CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${MAJOR_VERSION}.${MINOR_VERSION}.${CPACK_PACKAGE_VERSION_PATCH}")
138 |
139 | SET (CPACK_COMPONENTS_ALL Libraries ApplicationData)
140 | INCLUDE (CPack)
141 | ENDIF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")
142 |
143 |
--------------------------------------------------------------------------------
/COPYING.LESSER:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/COPYING.MIT:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2015 Netmail / Reveal Data
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/INSTALL:
--------------------------------------------------------------------------------
1 | Compile and install WJElement:
2 |
3 | $ ccmake . #change settings if desired, 'c'onfigure and 'g'enerate
4 | $ cmake -DLIB_DEST_DIR=/usr/local/lib64 #set lib path if needed
5 | $ make
6 | # make install
7 |
8 | You may also use:
9 | $ cmake-gui .
10 | instead of ccmake.
11 |
12 |
13 | Use it in your program:
14 |
15 | #include
16 |
17 |
18 | Link against it in your project:
19 |
20 | -lwjelement
21 | (simple usage)
22 | -lwjelement -lwjreader -lwjwriter
23 | (advanced and lower-level functionality)
24 |
25 |
26 | By default, WJElement will install to /usr/local. If linking fails to find
27 | WJElement immediately after installation, you may need to run ldconfig.
28 |
--------------------------------------------------------------------------------
/Makefile-all:
--------------------------------------------------------------------------------
1 | #This file can be called with `make -f Makefile-all`
2 | #The windows build for this file uses mingw
3 |
4 | THIS = Makefile-all
5 |
6 | # Compiler
7 | CC = _
8 | CC_WIN32 = i686-w64-mingw32-gcc
9 | CC_WIN64 = x86_64-w64-mingw32-gcc
10 | CC_LINUX = gcc
11 | CC_MACOS = gcc
12 |
13 |
14 | MAKEDIR = @mkdir -p $(@D)
15 | PLATFORMDIR = _
16 | BUILDDIR = $(PLATFORMDIR)release/
17 | NAME_LIB = libwjelement
18 | CFLAGS += -Wall -Wextra -DWJE_DISTINGUISH_INTEGER_TYPE -Iinclude
19 |
20 | release: export CFLAGS=-O3
21 | release: all
22 |
23 | debug: export CFLAGS=-g -DDEBUG
24 | debug: all
25 |
26 |
27 | HDRDIR = ./include/
28 | SRCDIR = ./src/
29 | OBJDIR = $(BUILDDIR)obj/
30 |
31 | # Set platform-specific variables
32 | ifeq ($(OS),Windows_NT)
33 | OSFLAG := "WIN"
34 | PLATFORMDIR = ./windows/
35 | ifeq ($(PROCESSOR_ARCHITECTURE),x86)
36 | CC := $(CC_WIN32)
37 | endif
38 | ifeq ($(PROCESSOR_ARCHITECTURE),AMD64)
39 | CC := $(CC_WIN64)
40 | endif
41 | else
42 | UNAME_S := $(shell uname -s)
43 | PLATFORMDIR = ./unix/
44 | ifeq ($(UNAME_S),Linux)
45 | OSFLAG := "LINUX"
46 | CC := $(CC_LINUX)
47 | CFLAGS += -fPIC
48 | endif
49 | ifeq ($(UNAME_S),Darwin)
50 | OSFLAG := "MACOS"
51 | CC := $(CC_MACOS)
52 | endif
53 | endif
54 |
55 |
56 | SRCS = lib/xpl.c \
57 | wjelement/element.c \
58 | wjelement/schema.c \
59 | wjelement/hash.c \
60 | wjelement/search.c \
61 | wjelement/types.c \
62 | wjreader/wjreader.c \
63 | wjwriter/wjwriter.c
64 |
65 | OBJS = ${SRCS:%.c=$(OBJDIR)%.o}
66 |
67 |
68 |
69 | all: $(NAME_LIB)_static $(NAME_LIB)_dynamic
70 |
71 |
72 | # This rule compiles object files from source files
73 | $(OBJDIR)%.o : $(SRCDIR)%.c
74 | @$(MAKEDIR)
75 | @printf "Compiling file: "$@" -> "
76 | @$(CC) $(CFLAGS) -c $< -o $@ -I$(HDRDIR)
77 | @printf $(GREEN)"OK!"$(RESET)"\n"
78 |
79 | #This rule compiles the static version of libwjelement
80 | $(BUILDDIR)$(NAME_LIB).a: $(OBJS)
81 | @ar rs $@ $(OBJS)
82 |
83 | #This rule compiles the static version of libwjelement
84 | $(NAME_LIB)_static: $(BUILDDIR)$(NAME_LIB).a
85 |
86 | #This rule compiles the platform-specific dynamic version of libwjelement
87 | $(NAME_LIB)_dynamic: $(OBJS)
88 | @if [ $(OSFLAG) = "WIN" ]; then printf \
89 | "Compiling DLL: "$(BUILDDIR)$(NAME_LIB).dll" -> " ; \
90 | $(CC) -shared -o $(BUILDDIR)$(NAME_LIB).dll $(CFLAGS) $(OBJS) \
91 | -Wl,--output-def,$(BUILDDIR)$(NAME_LIB).def \
92 | -Wl,--out-implib,$(BUILDDIR)$(NAME_LIB).lib \
93 | -Wl,--export-all-symbols ; \
94 | elif [ $(OSFLAG) = "MACOS" ]; then printf \
95 | "Compiling dylib: "$(BUILDDIR)$(NAME_LIB).dylib" -> " ; \
96 | $(CC) -shared -o $(BUILDDIR)$(NAME_LIB).dylib $(CFLAGS) $(OBJS) ; \
97 | elif [ $(OSFLAG) = "LINUX" ]; then \
98 | printf "Compiling so: "$(BUILDDIR)$(NAME_LIB).so" -> " ; \
99 | $(CC) -shared -o $(BUILDDIR)$(NAME_LIB).so $(CFLAGS) $(OBJS) ; \
100 | fi
101 | @printf $(GREEN)"OK!"$(RESET)"\n"
102 |
103 | clean:
104 | @rm -rf $(BUILDDIR)*
105 |
106 | re: clean all
107 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | WJElement - JSON manipulation in C
2 |
3 | WJElement is a very flexible JSON library developed by Messaging Architects.
4 | It was created for MA's "WARP" webserver ("Warp Json Elements"), and is built
5 | on top of the lower-level WJReader and WJWriter libraries (also included).
6 |
7 | WJReader and WJWriter are optimized for speed and memory-efficiency.
8 | WJElement focuses on flexibility and handy features, allowing C code to
9 | manipulate JSON documents with as few statements (fewer, sometimes!) as
10 | JavaScript itself. WJElement is also capable of json-schema validation.
11 |
12 | WJElement has grown into a generally-useful library, and is used across
13 | Messaging Architects' netmail and related projects. It is loved enough by
14 | MA's developers that we desire to use it elsewhere too, and we think others
15 | will enjoy it as well. So, here it is, ready to be consumed in any project,
16 | open or closed, as outlined by the GNU LGPL (any version). Include it as-is
17 | and link to it from your code, massage it into your own statically-linked
18 | package, or use it in ways we haven't thought of. Read the docs/headers, have
19 | fun, and if you use it for something awesome, let us know about it! :^)
20 |
21 |
22 | License:
23 | WJElement may be used, modified, and distributed under the terms of any of
24 | the following licenses.
25 | - GPL (see COPYING)
26 | - LGPL (see COPYING.LESSER)
27 | - MIT (aka Expat) (see COPYING.MIT)
28 |
29 |
30 | Owen Swerkstrom - community/repo admin, WJESchema
31 | Micah N Gorrell - primary author of WJElement
32 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | WJElement - JSON manipulation in C
2 | ==================================
3 |
4 | WJElement is a very flexible JSON library developed by Messaging Architects.
5 | It was created for MA's "WARP" webserver ("Warp Json Elements"), and is built
6 | on top of the lower-level WJReader and WJWriter libraries (also included).
7 |
8 | See the [wiki](https://github.com/netmail-open/wjelement/wiki) for more information,
9 | [example code](https://github.com/netmail-open/wjelement/wiki/WJElement-Example)
10 | and full [API](https://github.com/netmail-open/wjelement/wiki/WJElement-API) reference.
11 |
12 | WJReader and WJWriter are optimized for speed and memory-efficiency.
13 | WJElement focuses on flexibility and handy features, allowing C code to
14 | manipulate JSON documents with as few statements (fewer, sometimes!) as
15 | JavaScript itself. WJElement is also capable of json-schema validation.
16 |
17 | WJElement has grown into a generally-useful library, and is used across
18 | Messaging Architects' netmail and related projects. It is loved enough by
19 | MA's developers that we desire to use it elsewhere too, and we think others
20 | will enjoy it as well. So, here it is, ready to be consumed in any project,
21 | open or closed, as outlined by the GNU LGPL (any version). Include it as-is
22 | and link to it from your code, massage it into your own statically-linked
23 | package, or use it in ways we haven't thought of. Read the docs/headers, have
24 | fun, and if you use it for something awesome, let us know about it! :^)
25 |
26 |
27 | * Owen Swerkstrom <> - community/repo admin, WJESchema
28 | * Micah N Gorrell <> - primary author of WJElement
29 |
--------------------------------------------------------------------------------
/README_PlanetLabs.md:
--------------------------------------------------------------------------------
1 |
2 | Upstream: https://github.com/netmail-open/wjelement
3 |
4 | Packaging as a .deb in debuild/wjelement (package wjelement-pl)
5 |
6 | This package is used by plcompositor for JSON parsing and schema validation.
7 |
8 | This repo was cloned by Frank Warmerdam, and is lightly modified from
9 | upstream for clean building.
10 |
11 |
--------------------------------------------------------------------------------
/cmake_uninstall.cmake.in:
--------------------------------------------------------------------------------
1 | if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
2 | message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
3 | endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
4 |
5 | file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
6 | string(REGEX REPLACE "\n" ";" files "${files}")
7 | foreach(file ${files})
8 | message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
9 | if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
10 | exec_program(
11 | "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
12 | OUTPUT_VARIABLE rm_out
13 | RETURN_VALUE rm_retval
14 | )
15 | if(NOT "${rm_retval}" STREQUAL 0)
16 | message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
17 | endif(NOT "${rm_retval}" STREQUAL 0)
18 | else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
19 | message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
20 | endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
21 | endforeach(file)
22 |
--------------------------------------------------------------------------------
/documentation/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | WJElement Documentation: index
4 |
5 |
6 |
7 | About
8 |
9 | WJElement - JSON manipulation in C
10 |
11 |
12 | WJElement is a very flexible JSON library developed by Messaging Architects.
13 | It was created for MA's "WARP" webserver, and is built on top of the
14 | (also-included) lower-level WJReader and WJWriter libraries.
15 |
16 |
17 |
18 |
19 | WJReader and WJWriter are optimized for speed and memory-efficiency.
20 |
21 |
22 | WJElement focuses on flexibility and handy features, allowing C code to
23 | manipulate JSON documents with as few statements (fewer, sometimes!) as
24 | JavaScript itself.
25 |
26 |
27 | WJElement provides complete
28 | JSON-schema validation
29 | (draft
30 | v3
31 | is fully supported; v4 support is working but incomplete).
32 |
33 |
34 |
35 |
36 |
37 |
38 | License
39 |
40 | WJElement and its related libraries may be consumed in any project,
41 | open or closed, as outlined by the
42 | GNU Lesser
43 | General Public License (any version).
44 | Include it as-is and link to it
45 | from your code, massage it into your own statically-linked package, or use it
46 | in ways we haven't thought of. Read the docs/headers, have fun, and if you
47 | use it for something awesome, let us know about it! :^)
48 |
49 |
50 | API
51 |
52 |
69 |
70 |
71 | Quirks
72 |
73 | In the spirit of being upfront, and to help you guage whether WJElement is
74 | right for you without asking you to invest too much time, there are a few
75 | things to note about this library:
76 |
77 |
78 |
79 |
80 | XplBool return values
81 |
82 |
83 | Many functions return TRUE on success. Deal. :^)
84 |
85 |
86 |
87 |
88 | Schema "backlink"
89 |
90 |
91 | This is something we're making use of in netmail, but is not
92 | part of the base json-schema spec .
93 | You can safely ignore it (or dig in and use it, why not? :^)
94 |
95 |
96 |
97 |
98 | Libraries, headers (stubs and #define's)
99 |
100 |
101 | Xpl (cross-platform library) provides a consistent set of utility
102 | functions within netmail. WJElement uses a subset of these functions
103 | and definitions, so they have been included.
104 |
105 |
106 | MemMgr (memory manager) provides optimized, slab-based memory management
107 | in netmail; for the sake of a general WJElement release, the MemMgr API
108 | simply calls malloc et al.
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 | You are free and encouraged to add and contribute features if
117 | you are so inclined. In some cases, there are comments or even empty code
118 | blocks, just waiting to be filled in. WJElement is great, but you can make it
119 | even better!
120 |
121 |
122 | Contact
123 |
124 |
136 |
137 |
138 |
139 |
140 |
141 |
--------------------------------------------------------------------------------
/documentation/wjelement.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | WJElement Documentation: wjelement
4 |
5 |
6 |
7 | Index
8 |
9 | WJElement Synopsis
10 |
11 | The WJElement interfaces allow you to load an entire JSON document into
12 | memory and to then access elements within it more easily.
13 |
14 |
15 | WJElement uses UTF-8 encoding internally, as do the underlying
16 | WJReader
17 | and
18 | WJWriter .
19 | libraries.
20 | Adding other unicode support to WJR/WJW would not take too much work.
21 |
22 |
23 | WJElement is thread-safe, though an individual WJElement document and its
24 | children should not be used across threads without locking. WJElement does
25 | not provide locking, but its structure provides a handy client pointer which
26 | you may use for this or any other purpose.
27 |
28 |
29 | WJElement Data Types
30 |
31 | WJElement
32 | is the main structure used for JSON documents. In most circumstances
33 | it will not be necessary to access anything within such a structure directly,
34 | but there are times when peeking at a JSON object's name or type (and other
35 | properties) comes in handy.
36 |
37 |
38 | From wjelement.h:
39 |
40 | typedef struct WJElementPublic {
41 | char *name;
42 | WJRType type;
43 |
44 | struct WJElementPublic *next;
45 | struct WJElementPublic *prev;
46 |
47 | struct WJElementPublic *child;
48 | struct WJElementPublic *parent;
49 |
50 | /* The number of children */
51 | int count;
52 |
53 | /*
54 | A count of changes that have been performed on this element, which can
55 | be reset by the consumer.
56 | */
57 | int changes;
58 |
59 | void *client;
60 |
61 | /*
62 | If set then this freecb will be called before actually free'ing a
63 | WJElement. If it returns FALSE then the WJElement will NOT be free'd.
64 |
65 | This can be used to allow caching of objects. If used in this way then
66 | the consumer that set the callback is responsible for ensuring that it
67 | does get free'd correctly at the correct time.
68 | */
69 | XplBool (* freecb)(struct WJElementPublic *);
70 | } WJElementPublic;
71 | typedef WJElementPublic * WJElement;
72 |
73 |
74 |
75 | WJEAction
76 | specifies which operation to carry out when calling the JSON manipulation
77 | functions.
78 |
79 |
80 | From wjelement.h:
81 |
82 | typedef enum {
83 | WJE_GET = 0,
84 | WJE_SET,
85 | WJE_NEW,
86 | WJE_MOD
87 | } WJEAction;
88 |
89 |
90 |
91 |
92 |
93 | WJE_GET
94 |
95 |
96 | Return the value of an element. If the element does not exist then the
97 | provided default value will be returned.
98 |
99 |
100 |
101 |
102 | WJE_SET
103 |
104 |
105 | Assign the specified value to an element. If the element does not exist
106 | then it will be created.
107 |
108 |
109 | If the element can not be created then an appropriate value will be
110 | returned to indicate the error.
111 |
112 |
113 | When applicable a NULL will be returned. Otherwise a value that does
114 | not match the requested value will be returned.
115 |
116 |
117 |
118 |
119 | WJE_NEW
120 |
121 |
122 | Create a new element and assign it the specified value.
123 |
124 |
125 | If an element already exists then it will not be modified, and the value
126 | of that existing element will be returned.
127 |
128 |
129 |
130 |
131 | WJE_MOD
132 |
133 |
134 | Assign the specified value to an existing element, and return the value
135 | if successful.
136 |
137 |
138 | If the element does not exist then no elements are created or modified.
139 |
140 |
141 | When applicable a NULL will be returned. Otherwise a value that does
142 | not match the requested value will be returned.
143 |
144 |
145 |
146 |
147 |
148 |
149 | WJElement Interfaces
150 | Document/Element Management
151 |
152 | WJEParse
153 | - Parse the provided JSON document and return the new WJElement
154 |
155 |
156 |
157 | WJElement WJEParse(const char *json);
158 | WJElement _WJEParse(const char *json, char quote);
159 |
160 |
161 |
162 | The _WJEParse version can be used to parse a document with a non-standard
163 | quote character. This allows easy parsing of simple documents directly in a
164 | C source file without having to escape double quote characters. Example:
165 |
166 | doc = _WJEParse("{ 'foo': true, 'bar': 'yup' }", '\'');
167 |
168 |
169 |
170 | WJEOpenDocument
171 | - Load a WJElement object from the provided
172 | WJReader
173 |
174 |
175 |
176 | WJElement WJEOpenDocument(WJReader reader, char *where, WJELoadCB loadcb, void *data);
177 |
178 |
179 |
180 | If a load callback is provided then it will be called before adding any new
181 | children, allowing the consumer to leave specific elements of the hierarchy.
182 |
183 |
184 | WJEWriteDocument
185 | - Write a WJElement object to the provided
186 | WJWriter
187 |
188 | XplBool WJEWriteDocument(WJElement document, WJWriter writer, char *name);
189 |
190 |
191 |
192 | WJEWriteFILE
193 | - Write a WJElement object to the provided FILE*
194 |
195 | void WJEWriteFILE(WJElement document, FILE* fd);
196 |
197 |
198 |
199 | WJEWriteFILE() requires an already opened FILE* to write to.
200 | It is also used internally by WJEDump()
201 |
202 |
203 |
204 | WJEWriteMEM
205 | - Allocate and write to a string
206 |
207 | char * WJEWriteMEM(WJElement document, XplBool pretty, size_t maxlength);
208 |
209 |
210 |
211 | WJEWriteMEM() allocates and returns a string containing the document.
212 | A maxlength of 0 means the string may be of unlimited length.
213 | The returned string must be freed with MemFree by the consumer.
214 |
215 |
216 |
217 | WJECloseDocument
218 | - Destroy a WJElement object
219 |
220 |
221 |
222 | XplBool WJECloseDocument(WJElement document);
223 |
224 |
225 |
226 | WJECloseDocument is also used to delete/remove an item from a parent document:
227 | WJECloseDocument(WJEGet(...));
228 |
229 |
230 | WJECopyDocument
231 | - Duplicate an existing WJElement
232 |
233 |
234 |
235 | WJElement WJECopyDocument(WJElement to, WJElement from, WJECopyCB loadcb, void *data);
236 |
237 |
238 |
239 | If a copy callback is provided, then it will be called with each element in
240 | order to allow filtered copying on an element-by-element basis, using any
241 | criteria.
242 |
243 |
244 | WJECopyDocument is normally used to create a new copy or to populate a new,
245 | empty document, but that is not required or enforced. If "to" is already
246 | populated, WJEMergeObjects is probably the desired function for copying in
247 | other contents.
248 |
249 |
250 | WJEDetach
251 | - Remove a WJElement from it's parent (and siblings)
252 |
253 |
254 |
255 | XplBool WJEDetach(WJElement document);
256 |
257 |
258 |
259 | WJEAttach
260 | - Add a document to another document as a child
261 |
262 |
263 |
264 | XplBool WJEAttach(WJElement container, WJElement document);
265 |
266 |
267 |
268 | WJERename
269 | - Rename an element
270 |
271 |
272 |
273 | XplBool WJERename(WJElement document, const char *name);
274 |
275 |
276 |
277 | WJEMergeObjects
278 | - Merge all fields from one object to another
279 |
280 |
281 |
282 | XplBool WJEMergeObjects(WJElement to, WJElement from, XplBool overwrite);
283 |
284 |
285 | JSON Manipulation
286 |
287 | All JSON manipulation functions take a 'path' argument. This is a string as
288 | explained below, see WJElement Selectors
289 |
290 |
291 | Functions which take a 'last' parameter allow enumeration of multiple
292 | matching elements, if non-NULL is passed. Handy for looping through objects
293 | and arrays.
294 |
295 |
296 | WJEGet
297 | - Find the first element within the hierarchy of a WJElement that matches the
298 | specified path.
299 |
300 |
301 |
302 | WJElement WJEGet(WJElement container, char *path, WJElement last);
303 |
304 |
305 |
306 | In cases where a direct child of the WJElement is needed,
307 | WJEChild may be more suitable, especially if the child's
308 | name begins with a non-alphanumeric character.
309 |
310 |
311 | WJEBool
312 | - Access a boolean element
313 |
314 |
315 |
316 | XplBool WJEBool(WJElement container, char *path, WJEAction action, XplBool value);
317 | XplBool _WJEBool(WJElement container, char *path, WJEAction action, WJElement *last, XplBool value);
318 |
319 |
320 |
321 | WJEString, WJEStringN
322 | - Access a string element
323 |
324 |
325 |
326 | char * WJEString(WJElement container, char *path, WJEAction action, char *value);
327 | char * _WJEString(WJElement container, char *path, WJEAction action, WJElement *last, char *value);
328 |
329 | char * WJEStringN(WJElement container, char *path, WJEAction action, char *value, size_t len);
330 | char * _WJEStringN(WJElement container, char *path, WJEAction action, WJElement *last, char *value, size_t len);
331 |
332 |
333 |
334 | WJEObject
335 | - Access an object element
336 |
337 |
338 |
339 | WJElement WJEObject(WJElement container, char *path, WJEAction action);
340 | WJElement _WJEObject(WJElement container, char *path, WJEAction action, WJElement *last);
341 |
342 |
343 |
344 | WJEArray
345 | - Access an array element
346 |
347 |
348 |
349 | WJElement WJEArray(WJElement container, char *path, WJEAction action);
350 | WJElement _WJEArray(WJElement container, char *path, WJEAction action, WJElement *last);
351 |
352 |
353 |
354 | WJENull
355 | - Access a null element
356 |
357 |
358 |
359 | WJElement WJENull(WJElement container, char *path, WJEAction action);
360 | WJElement _WJENull(WJElement container, char *path, WJEAction action, WJElement *last);
361 |
362 |
363 |
364 | WJEInt32
365 | - Access a number element, as a 32-bit integer
366 |
367 |
368 |
369 | int32 WJEInt32(WJElement container, char *path, WJEAction action, int32 value);
370 | int32 _WJEInt32(WJElement container, char *path, WJEAction action, WJElement *last, int32 value);
371 |
372 |
373 |
374 | WJEUInt32
375 | - Access a number element, as a 32-bit unsigned integer
376 |
377 |
378 |
379 | uint32 WJEUInt32(WJElement container, char *path, WJEAction action, uint32 value);
380 | uint32 _WJEUInt32(WJElement container, char *path, WJEAction action, WJElement *last, uint32 value);
381 |
382 |
383 |
384 | WJEInt64
385 | - Access a number element, as a 64-bit integer
386 |
387 |
388 |
389 | int64 WJEInt64(WJElement container, char *path, WJEAction action, int64 value);
390 | int64 _WJEInt64(WJElement container, char *path, WJEAction action, WJElement *last, int64 value);
391 |
392 |
393 |
394 | WJEUInt64
395 | - Access a number element, as a 64-bit unsigned integer
396 |
397 |
398 |
399 | uint64 WJEUInt64(WJElement container, char *path, WJEAction action, uint64 value);
400 | uint64 _WJEUInt64(WJElement container, char *path, WJEAction action, WJElement *last, uint64 value);
401 |
402 |
403 |
404 | WJEDouble
405 | - Access a number element, as a double (as in double-precision floating point)
406 |
407 |
408 |
409 | double WJEDouble(WJElement container, char *path, WJEAction action, double value);
410 | double _WJEDouble(WJElement container, char *path, WJEAction action, WJElement *last, double value);
411 |
412 |
413 |
414 | WJE...F variants
415 | - Format-capable versions of element functions
416 |
417 |
418 | The following functions...
419 |
420 | WJEGetF
421 | WJEBoolF
422 | WJEStringF
423 | WJEStringNF
424 | WJEObjectF
425 | WJEArrayF
426 | WJENullF
427 | WJEInt32F
428 | WJEUInt32F
429 | WJEInt64F
430 | WJEUInt64F
431 | WJEDoubleF
432 |
433 | ...are identical to the non F variants except that the
434 | path argument has been moved to the end and is used as a format string. For
435 | example if you need a value by index, and that index is stored in the
436 | variable x:
437 |
438 | val = WJENumberF(doc, WJE_GET, NULL, 0, "foo[%d]", x);
439 |
440 |
441 |
442 | WJEChild
443 | - Find, create or update an element by name instead of path. This allows
444 | access to elements that would be difficult to reference by path. No selector
445 | syntax is used, so only direct children of the element can be found.
446 |
447 |
448 |
449 | WJElement WJEChild(WJElement container, char *name, WJEAction action);
450 |
451 |
452 |
453 | Type specific actions may be done by passing the resulting WJElement and a
454 | NULL path to WJEBool(), WJENumber(), WJEString(), WJEObject(), WJEArray() or
455 | WJENull().
456 |
457 | Data Processing
458 |
459 | WJEHash
460 | - Calculate a hash for a document
461 |
462 |
463 |
464 | typedef int (* WJEHashCB)(void *context, void *data, size_t size);
465 | EXPORT void WJEHash(WJElement document, WJEHashCB update, void *context);
466 |
467 |
468 | Schema
469 |
470 | Callbacks:
471 |
472 |
473 |
474 | typedef WJElement (* WJESchemaLoadCB)(const char *name, void *client, const char *file, const int line);
475 | typedef void (* WJESchemaFreeCB)(WJElement schema, void *client);
476 | typedef void (* WJESchemaMatchCB)(WJElement schema, const char *selector, void *client);
477 | typedef void (* WJEErrCB)(void *client, const char *format, ...);
478 |
479 |
480 |
481 | WJESchemaLoadCB callbacks are used to fetch schema as needed.
482 | WJESchemaFreeCB are called when schema is no longer needed.
483 |
484 |
485 | WJESchemaValidate
486 | - Validate a document against a given schema.
487 |
488 |
489 |
490 | XplBool WJESchemaValidate(WJElement schema, WJElement document,
491 | WJEErrCB err, WJESchemaLoadCB load,
492 | WJESchemaFreeCB freecb, void *client);
493 |
494 |
495 |
496 | Additional schema will be loaded
497 | via the load callback if needed. Any validation errors will be reported,
498 | printf-style, to errcb.
499 |
500 | If a the load callback is used to acquire schema but a NULL free callback is
501 | provided, WJECloseDocument will be used internally to release it.
502 |
503 |
504 | WJESchemaIsType
505 | - Determine if a document implements a specific schema.
506 |
507 |
508 |
509 | XplBool WJESchemaIsType(WJElement document, const char *type,
510 | WJESchemaLoadCB loadcb, WJESchemaFreeCB freecb,
511 | void *client);
512 |
513 |
514 |
515 | Additional schema will be loaded via the load callback if needed.
516 |
517 |
518 | If a load callback is not provided then the object type will still be checked
519 | but it will not be considered a match if it is a type that extends the
520 | specifed type.
521 |
522 |
523 | WJESchemaNameIsType
524 | - variation of WJESchemaIsType which acts on schema name instead of a document
525 |
526 |
527 |
528 | XplBool WJESchemaNameIsType(const char *describedby, const char *type,
529 | WJESchemaLoadCB loadcb,
530 | WJESchemaFreeCB freecb, void *client);
531 |
532 |
533 |
534 | WJESchemaGetSelectors
535 | - find type/format-matching properties
536 |
537 |
538 |
539 | void WJESchemaGetSelectors(WJElement document,
540 | char *type, char *format,
541 | WJESchemaLoadCB load,
542 | WJESchemaFreeCB freecb,
543 | WJESchemaMatchCB matchcb, void *client);
544 |
545 |
546 |
547 | WJESchemaGetSelectors
548 | calls back matchcb for each WJElement selector which will fetch a property
549 | of a given type and format, from a given document. The load callback will be
550 | used to load all necessary schema, starting with the document's "describedby".
551 | stripat-type wildcards may be used; "Date*" will find "date" and "date-time".
552 |
553 |
554 | WJESchemaGetAllSelectors
555 | - variation of WJESchemaGetSelectors which provides selectors that
556 | could exist in objects of the given "describedby" schema name
557 |
558 |
559 |
560 | void WJESchemaGetAllSelectors(char *describedby,
561 | char *type, char *format,
562 | WJESchemaLoadCB load,
563 | WJESchemaFreeCB freecb,
564 | WJESchemaMatchCB matchcb, void *client);
565 |
566 |
567 |
568 | WJESchemaFindBacklink
569 | - find "backlink" property by schema
570 |
571 |
572 |
573 | char * WJESchemaFindBacklink(WJElement document, const char *format,
574 | WJESchemaLoadCB loadcb, WJESchemaFreeCB freecb,
575 | void *client);
576 |
577 |
578 |
579 | WJESchemaNameFindBacklink
580 | - find "backlink" property by name
581 |
582 |
583 |
584 | char * WJESchemaNameFindBacklink(char *describedby, const char *format,
585 | WJESchemaLoadCB loadcb, WJESchemaFreeCB freecb,
586 | void *client);
587 |
588 |
589 |
590 | WJESchemaFreeBacklink
591 | - clean up a previously-found backlink
592 |
593 |
594 |
595 | void WJESchemaFreeBacklink(char *backlink);
596 |
597 |
598 | Debug
599 |
600 | WJEDump
601 | - write a document to stdout
602 |
603 |
604 |
605 | void WJEDump(WJElement document);
606 |
607 |
608 |
609 | WJElement Selectors
610 |
611 | Elements within the hierarchy of a JSON document can be referenced using a
612 | path. Multiple levels of hierarchy can be referenced to find any element
613 | below the provided container. The following rules apply to any WJE
614 | functions that take a path argument.
615 |
616 |
617 | A child may be referenced with an alpha-numeric name, or a subscript within
618 | square brackets:
619 |
620 | foo
621 | ["foo"]
622 | ["$foo"]
623 |
624 |
625 |
626 | Additional levels of heirarchy can be referenced by appending an additional
627 | subscript, or appending a dot and an additional alpha-numeric name:
628 |
629 | one.two.three.four
630 | one["two"]["three"].four
631 |
632 |
633 |
634 | Subscripts may contain double quoted names. Any special characters,
635 | (including .[]*?"'\) can be included by prefixing with a \.
636 |
637 | foo["bar.smeg"]
638 | foo["something with a \"quote\""]
639 |
640 |
641 |
642 | Subscripts may contain single quoted names, which behave as double quoted
643 | names but also allow for * and ? wild card substitution:
644 |
645 | foo['bar.*']
646 |
647 |
648 |
649 | Subscripts may reference an item by it's offset in an array (or object):
650 |
651 | foo[0]
652 | foo[3]
653 |
654 |
655 |
656 | Negative offsets are wrapped to the end of the array (or object) meaning
657 | that [-1] references the last item.
658 |
659 |
660 | Subscripts may reference a range of offsets by providing 2 offsets seperated
661 | by a colon:
662 |
663 | foo[2:5]
664 |
665 |
666 |
667 | Subscripts may reference a set of items by seperating offsets, offset,
668 | ranges, double quoted and single quoted values:
669 |
670 | foo[2,4:6,'bar.*', "smeg"]
671 |
672 |
673 |
674 | An empty subscript may be specified to reference all children.
675 |
676 | []
677 |
678 |
679 |
680 | A subscript of $ may be specified in actions perform creations to reference
681 | the item after the end of an array. This allows appending to an array.
682 |
683 | [$]
684 |
685 |
686 |
687 | A subscript may be prefixed by an | character to indicate that the subscript
688 | is optional. The portion of the selector from the | on will be ignored if
689 | there is a match and that match has no children.
690 |
691 |
692 | For example, if an element named foo may either be a string or an array of
693 | strings you can enumerate all values using a selector of:
694 |
695 | foo|[]
696 |
697 |
698 |
699 | A NULL path refers to the container itself.
700 |
701 |
702 | A path may end in a condition. The condition consists of an operator and a
703 | value. The value may be a number, a double quoted string, or a single
704 | quoted string. If a single quoted string is used it may contain * and ?
705 | wild cards.
706 |
707 |
708 | The following operators are supported for any value:
709 |
710 | ==, !=
711 |
712 |
713 |
714 | A number value may also use the following operators:
715 |
716 | <, >, <=, >=
717 |
718 |
719 |
720 | Example:
721 |
722 | foo.bar <= 3
723 | foo.bar != 'foo*'
724 | foo.bar != "one"
725 |
726 |
727 | A condition may be separated from a path with a ; character.
728 |
729 |
730 | In the following examples the object named "foo" will be returned if it has
731 | a child named "bar" that matches the condition.
732 |
733 | foo; bar <= 3
734 | foo; bar != 'foo*'
735 | foo; bar != "one"
736 |
737 |
738 |
739 |
740 |
741 |
--------------------------------------------------------------------------------
/documentation/wjreader.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | WJElement Documentation: wjreader
4 |
5 |
6 |
7 | Index
8 |
9 | WJReader Synopsis
10 |
11 | The WARP JSON Reader provides the ability to parse a JSON document as a
12 | stream. All parsing is done with a portion of the document loaded into a
13 | buffer, and the buffer is reloaded as needed.
14 |
15 |
16 | This allows the parser to be very fast, but does require that data in the
17 | document is accessed in the order of the document.
18 |
19 |
20 | WJReader Data Types
21 |
22 | WJReader
23 | is The WJReader document, the library's primary structure
24 |
25 |
26 | From wjreader.h:
27 |
28 | typedef struct {
29 | uint32 depth;
30 | uint32 maxdepth;
31 | void *userdata;
32 | } WJReaderPublic;
33 | typedef WJReaderPublic * WJReader;
34 |
35 |
36 |
37 | WJRType
38 | specifies the type of a value being read.
39 |
40 |
41 | From wjreader.h:
42 |
43 | typedef enum {
44 | WJR_TYPE_OBJECT = 'O',
45 | WJR_TYPE_ARRAY = 'A',
46 |
47 | WJR_TYPE_STRING = 'S',
48 | WJR_TYPE_NUMBER = 'N',
49 |
50 | WJR_TYPE_BOOL = 'B',
51 | WJR_TYPE_TRUE = 'T',
52 | WJR_TYPE_FALSE = 'F',
53 | WJR_TYPE_NULL = '0',
54 |
55 | WJR_TYPE_UNKNOWN = '?'
56 | } WJRType;
57 |
58 |
59 |
60 | WJReadCallback
61 | - Generic data reader (from file, socket, decoder, etc.)
62 |
63 |
64 |
65 | typedef size_t (* WJReadCallback)(char *data, size_t length, size_t seen, void *userdata);
66 |
67 |
68 |
69 | WJReader Interfaces
70 | Document/Reader Management
71 |
72 | WJROpenDocument
73 | - Open a JSON document and prepare to parse.
74 |
75 |
76 |
77 | WJReader WJROpenDocument(WJReadCallback callback, void *userdata, char *buffer,
78 | size_t buffersize);
79 | WJReader _WJROpenDocument(WJReadCallback callback, void *userdata, char *buffer,
80 | size_t buffersize, uint32 maxdepth);
81 |
82 |
83 |
84 | A callback function must be
85 | provided which will be used to read data as it is needed. The callback is
86 | expected to load data into the provided buffer and return the number of
87 | bytes that where loaded. If the number of bytes loaded is less than the
88 | requested length then it is assumed that the end of the document has been
89 | reached.
90 |
91 |
92 | If a buffer is provided to WJROpenDocument() then that buffer will be used,
93 | rather than allocating a new buffer. The size of the buffer must be
94 | provided as buffersize. If a buffer is not provided then one will be
95 | allocated of size buffersize.
96 |
97 |
98 | If a buffersize of 0 is passed to WJROpenDocument then the default size will
99 | be used (4KB). Some data is kept in the buffer while parsing, such as
100 | element names and values. If the buffer is not large enough for these then
101 | the document will fail to be parsed and will be aborted.
102 |
103 |
104 | WJROpenFILEDocument
105 | - helper to read from an open FILE *.
106 |
107 |
108 |
109 | WJReader WJROpenFILEDocument(FILE *jsonfile, char *buffer, size_t buffersize);
110 |
111 |
112 |
113 | An alternative method of opening a JSON document, which will provide a
114 | proper callback for reading the data from an open FILE *.
115 |
116 |
117 | As with the standard WJROpenDocument() a buffersize of 0 will use the
118 | default buffer size.
119 |
120 |
121 | WJROpenMemDocument
122 | - helper to read from memory.
123 |
124 |
125 |
126 | WJReader WJROpenMemDocument(char *json, char *buffer, size_t buffersize);
127 |
128 |
129 |
130 | An alternative method of opening a JSON document, which will provide a
131 | proper callback for reading from a pre-allocated buffer in memory.
132 |
133 |
134 | As with the standard WJROpenDocument() a buffersize of 0 will use the
135 | default buffer size.
136 |
137 |
138 | WJRCloseDocument
139 | - Close a WJReader document.
140 |
141 |
142 |
143 | XplBool WJRCloseDocument(WJReader doc);
144 |
145 |
146 | JSON Values
147 |
148 | WJRNext
149 | - Get the type and name of the next element in 'doc'.
150 |
151 |
152 |
153 | char * WJRNext(char *parent, size_t maxnamelen, WJReader doc);
154 |
155 |
156 |
157 | Returns a string, which contains the name of the next element of the
158 | specified parent, prefixed by a single character that represents the type.
159 | The pointer returned may be used as the parent argument in future calls to
160 | WJRNext() in order to return the children of the current object.
161 |
162 |
163 | If the object's name is larger than the maxnamelen argument then it will be
164 | truncated.
165 |
166 |
167 | These WJReader functions
168 | Return the value of the last object returned by WJRNext(). The calling
169 | application is expected to know the type of the value, and call the
170 | appropriate function. When possible the value will be converted if it does
171 | not match the requested type.
172 |
173 |
174 | If the buffer is not large enough to contain the entire value then the
175 | WJRString() function may return a partial value. It may be called multiple
176 | times until a NULL is returned to ensure that the entire value has been
177 | read.
178 |
179 |
180 |
181 | char * WJRString(XplBool *complete, WJReader doc);
182 | char * WJRStringEx(XplBool *complete, size_t *length, WJReader doc);
183 | XplBool WJRBoolean(WJReader doc);
184 |
185 | int32 WJRInt32(WJReader doc);
186 | uint32 WJRUInt32(WJReader doc);
187 | int64 WJRInt64(WJReader doc);
188 | uint64 WJRUInt64(WJReader doc);
189 | double WJRDouble(WJReader doc);
190 |
191 |
192 |
193 | WJRNegative
194 | - Check whether the current WJR_TYPE_NUMBER value is negative
195 |
196 |
197 |
198 | XplBool WJRNegative(WJReader doc);
199 |
200 |
201 |
202 | If the current element is a WJR_TYPE_NUMBER then this function can be used
203 | to determine if the value is negative or positive. If negative then TRUE
204 | will be returned.
205 |
206 |
207 |
208 |
209 |
--------------------------------------------------------------------------------
/documentation/wjwriter.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | WJElement Documentation: wjwriter
4 |
5 |
6 |
7 | Index
8 |
9 | WJWriter Synopsis
10 |
11 | The WARP JSON Writer provides the ability to easily write a JSON document to
12 | a stream. All data formatting is done automatically.
13 |
14 |
15 | WJWriter Data Types
16 |
17 | WJWriter
18 | is The WJWriter document, the library's primary structure
19 |
20 |
21 | From wjwriter.h:
22 |
23 | typedef struct {
24 | XplBool pretty;
25 |
26 | /*
27 | Base may be set to 8, 10 (default), or 16. Using a base other than 10
28 | will result in a non standard JSON document which may not be read
29 | properly by all parsers.
30 | */
31 | int base;
32 |
33 | struct {
34 | void *data;
35 | WJWriteCallback cb;
36 | } write;
37 |
38 | struct {
39 | void *data;
40 | void (* freecb)(void *data);
41 | } user;
42 | } WJWriterPublic;
43 | typedef WJWriterPublic* WJWriter;
44 |
45 |
46 |
47 | WJWriteCallback
48 | - Generic data writer (to file, socket, encoder, etc.)
49 |
50 |
51 |
52 | typedef size_t (* WJWriteCallback)(char *data, size_t size, void *writedata);
53 |
54 |
55 |
56 | WJWriter Interfaces
57 | Document/Writer Management
58 |
59 | WJWOpenDocument
60 | - Open a stream that is ready to accept a JSON document.
61 |
62 |
63 |
64 | WJWriter WJWOpenDocument(XplBool pretty, WJWriteCallback callback,
65 | void *writeData);
66 | WJWriter _WJWOpenDocument(XplBool pretty, WJWriteCallback callback,
67 | void *writedata, size_t buffersize);
68 |
69 |
70 |
71 | A callback function
72 | must be provided which will be used to write data as it is ready.
73 |
74 |
75 | A buffersize of 0 will disable write buffering entirely. The write buffer
76 | used will be at least the size requested, but may be larger.
77 |
78 |
79 | WJWOpenFILEDocument
80 | - helper to write to an open FILE *.
81 |
82 |
83 |
84 | WJWriter WJWOpenFILEDocument(XplBool pretty, FILE *file);
85 |
86 |
87 |
88 | An alternative method of opening a JSON document for writing, which will
89 | provide a proper callback for writing the data to an open FILE *.
90 |
91 |
92 | WJWCloseDocument
93 | - Close a WJWriter document
94 |
95 |
96 |
97 | XplBool WJWCloseDocument(WJWriter doc);
98 |
99 |
100 | JSON Structures
101 |
102 | WJWOpenArray
103 | - Open an array.
104 |
105 |
106 |
107 | XplBool WJWOpenArray(char *name, WJWriter doc);
108 |
109 |
110 |
111 | All objects that are direct children of the array MUST NOT
112 | be named. A value of NULL should be passed as name for any such values.
113 |
114 |
115 | When all values of the array have been written it can be closed with
116 | WJWCloseArray().
117 |
118 |
119 | WJWCloseArray
120 | - Close an array.
121 |
122 |
123 |
124 | XplBool WJWCloseArray(WJWriter doc);
125 |
126 |
127 |
128 | WJWOpenObject
129 | - Open an object.
130 |
131 |
132 |
133 | XplBool WJWOpenObject(char *name, WJWriter doc);
134 |
135 |
136 |
137 | Open an object. All objects that are direct children of the object MUST be
138 | named. A value of NULL should NOT be passed as name for any such values.
139 |
140 |
141 | When all values of the object have been written it can be closed with
142 | WJWCloseObject().
143 |
144 |
145 | WJWCloseObject
146 | - Close an object.
147 |
148 |
149 |
150 | XplBool WJWCloseObject(WJWriter doc);
151 |
152 |
153 | JSON Values
154 |
155 | These WJWriter functions
156 | write a value to the document. If any values are written that are a direct
157 | child of an object then a non-NULL name MUST be provided. If any values are
158 | written that are a direct child of an array then a non-NULL name MUST NOT be
159 | provided.
160 |
161 |
162 | A string value may be written in multiple pieces. A JSON string will be
163 | started with the first call to WJWString, or WJWStringN and the string will
164 | not be completed until the "done" argument is set to TRUE or there is a call
165 | to write a non-string type value.
166 |
167 |
168 |
169 | XplBool WJWStringN(char *name, char *value, size_t length, XplBool done, WJWriter doc);
170 | XplBool WJWString(char *name, char *value, XplBool done, WJWriter doc);
171 | XplBool WJWBoolean(char *name, XplBool value, WJWriter doc);
172 | XplBool WJWNull(char *name, WJWriter doc);
173 |
174 | XplBool WJWInt32(char *name, int32 value, WJWriter doc);
175 | XplBool WJWUInt32(char *name, uint32 value, WJWriter doc);
176 | XplBool WJWInt64(char *name, int64 value, WJWriter doc);
177 | XplBool WJWUInt64(char *name, uint64 value, WJWriter doc);
178 | XplBool WJWDouble(char *name, double value, WJWriter doc);
179 |
180 |
181 |
182 | WJWRawValue
183 | - Write a raw, pre-formatted value to the document.
184 |
185 |
186 |
187 | XplBool WJWRawValue(char *name, char *value, XplBool done, WJWriter doc);
188 |
189 |
190 |
191 | It is up to the caller to
192 | ensure that the data is properly formatted and complete.
193 |
194 |
195 |
196 |
197 |
--------------------------------------------------------------------------------
/example/Makefile:
--------------------------------------------------------------------------------
1 | TARGET=validate
2 | TARGET2=example
3 |
4 | all:
5 | gcc -o ${TARGET} ${TARGET}.c -lwjelement -lwjreader
6 | gcc -o ${TARGET2} ${TARGET2}.c -lwjelement -lwjreader
7 |
8 | clean:
9 | rm ${TARGET} ${TARGET2}
10 |
--------------------------------------------------------------------------------
/example/example.c:
--------------------------------------------------------------------------------
1 | /*
2 | example.c: an (incredibly basic) example of a WJElement consumer program
3 |
4 | after installing libwjelement (and running ldconfig if needed)...
5 | gcc -o example -lwjelement example.c
6 | or
7 | gcc -o example `pkg-config --libs wjelement` example.c
8 |
9 | the JSON operations performed in this example are parallel to the following
10 | javascript code. notice how similar the code is in both size and clarity:
11 |
12 | var doc = {
13 | "name": "Serenity",
14 | "class": "firefly",
15 | "crew": [
16 | {
17 | "name": "Malcolm Reynolds",
18 | "job": "captain",
19 | "born": 2468
20 | },
21 | {
22 | "name": "Kaywinnet Lee Fry",
23 | "job": "mechanic",
24 | "born": 2494
25 | },
26 | {
27 | "name": "Jayne Cobb",
28 | "job": "public relations"
29 | "born": 2485
30 | }
31 | ],
32 | "cameo" : [
33 | "Battlestar Galactica",
34 | "Star Wars Evasive Action",
35 | "Dr. Horrible's Sing-Along Blog",
36 | "Ready Player One"
37 | ],
38 | "shiny": true
39 | };
40 | var person = null;
41 | var cameo = null;
42 |
43 | for(i in doc.crew) { // note: tedious...
44 | person = doc.crew[i];
45 | if(person.born == 2468) {
46 | person.born = 2486;
47 | }
48 | }
49 | delete(doc.shiny);
50 |
51 | for(i in doc.crew) {
52 | person = doc.crew[i];
53 | console.log(person.name +" ("+ person.job +") is "+ (2517 - person.born));
54 | }
55 | for(i in doc.cameo) {
56 | cameo = doc.cameo[i];
57 | console.log("Cameo: " + cameo);
58 | }
59 |
60 | console.log(JSON.stringify(doc));
61 | */
62 |
63 |
64 | #include
65 | #include
66 |
67 | WJRType
68 | WJE_GET_TYPE(WJElement parent)
69 | {
70 | return parent->child->next->type;
71 | }
72 |
73 | int main(int argc, char **argv) {
74 | WJElement doc = NULL;
75 | WJElement person = NULL;
76 | WJElement cameo = NULL;
77 |
78 | doc = WJEObject(NULL, NULL, WJE_NEW);
79 | WJEString(doc, "name", WJE_SET, "Serenity");
80 | WJEString(doc, "class", WJE_SET, "firefly");
81 | WJEArray(doc, "crew", WJE_SET);
82 |
83 | WJEObject(doc, "crew[$]", WJE_NEW);
84 | WJEString(doc, "crew[-1].name", WJE_SET, "Malcolm Reynolds");
85 | WJEString(doc, "crew[-1].job", WJE_SET, "captain");
86 | WJEInt64(doc, "crew[-1].born", WJE_SET, 2468);
87 |
88 | WJEObject(doc, "crew[$]", WJE_NEW);
89 | WJEString(doc, "crew[-1].name", WJE_SET, "Kaywinnet Lee Fry");
90 | WJEString(doc, "crew[-1].job", WJE_SET, "mechanic");
91 | WJEInt64(doc, "crew[-1].born", WJE_SET, 2494);
92 |
93 | WJEObject(doc, "crew[$]", WJE_NEW);
94 | WJEString(doc, "crew[-1].name", WJE_SET, "Jayne Cobb");
95 | WJEString(doc, "crew[-1].job", WJE_SET, "public relations");
96 | WJEInt64(doc, "crew[-1].born", WJE_SET, 2485);
97 |
98 | WJEArray(doc, "cameo", WJE_SET);
99 | WJEString(doc, "cameo[$]", WJE_NEW, "Battlestar Galactica");
100 | WJEString(doc, "cameo[$]", WJE_NEW, "Star Wars Evasive Action");
101 | WJEString(doc, "cameo[$]", WJE_NEW, "Dr. Horrible's Sing-Along Blog");
102 | WJEString(doc, "cameo[$]", WJE_NEW, "Ready Player One");
103 |
104 | WJEBool(doc, "shiny", WJE_SET, TRUE);
105 |
106 | WJEInt64(doc, "crew[].born == 2468", WJE_SET, 2486); /* note: awesome! */
107 | WJECloseDocument(WJEGet(doc, "shiny", NULL));
108 |
109 | while((person = _WJEObject(doc, "crew[]", WJE_GET, &person))) {
110 | printf("person Type is %c\n", WJE_GET_TYPE(person));
111 | }
112 | while((cameo = WJEGet(doc, "cameo[]", cameo))) {
113 | printf("Cameo: %s\n", WJEString(cameo, NULL, WJE_GET, ""));
114 | }
115 |
116 | WJEDump(doc);
117 | WJECloseDocument(doc);
118 | return 0;
119 | }
120 |
--------------------------------------------------------------------------------
/example/layout.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "layout",
3 | "headers": {
4 | "msg_id": "DSCS1385112923586"
5 | },
6 | "obj": {
7 | "name": "test",
8 | "status": "draft",
9 | "orientation": "landscape",
10 | "widget": []
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/example/layout.schema:
--------------------------------------------------------------------------------
1 | {
2 | "name": "layout schema check",
3 | "type": "object",
4 | "properties": {
5 | "_id": {
6 | "type": "string",
7 | "required": true
8 | },
9 | "name": {
10 | "type": "string",
11 | "required": true
12 | },
13 | "template": {
14 | "type": "string",
15 | },
16 | "orientation": {
17 | "type": "string",
18 | "enum": [
19 | "portrait",
20 | "landscape"
21 | ],
22 | "required": true
23 | },
24 | "status": {
25 | "type": "string",
26 | "enum": [
27 | "draft",
28 | "ready"
29 | ],
30 | "default": "ready",
31 | "required": true
32 | },
33 | "ts": {
34 | "type": "string",
35 | "required": true
36 | },
37 | "widgets": {
38 | "type": "array",
39 | "required": true,
40 | "items": {
41 | "name": "widget setting",
42 | "type": "object",
43 | "additionalProperties": false,
44 | "properties": {
45 | "widget": {
46 | "type": "string",
47 | "required": true
48 | },
49 | "name": {
50 | "type": "string",
51 | "description": "Must be the widget name( package name )",
52 | "required": true
53 | },
54 | "h": {
55 | "type": "number",
56 | "description": " 0.0 ~ 1.0",
57 | "required": true
58 | },
59 | "w": {
60 | "type": "number",
61 | "description": " 0.0 ~ 1.0",
62 | "required": true
63 | },
64 | "top": {
65 | "type": "number",
66 | "description": " 0.0 ~ 1.0",
67 | "required": true
68 | },
69 | "left": {
70 | "type": "number",
71 | "description": " 0.0 ~ 1.0",
72 | "required": true
73 | },
74 | "_id": {
75 | "type": "string",
76 | "required": true
77 | },
78 | "param": {
79 | "type": "array",
80 | "items": {
81 | "type": "object",
82 | "additionalProperties": false,
83 | "properties": {
84 | "name": {
85 | "type": "string",
86 | "required": true
87 | },
88 | "val": {
89 | "required": true
90 | },
91 |
92 | "_id": {
93 | "type": "string",
94 | "required": true
95 | }
96 | }
97 | }
98 | }
99 | }
100 | }
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/example/validate.c:
--------------------------------------------------------------------------------
1 | /*
2 | validate.c: a proof-of-concept json-schema validator
3 | thanks to xiaoping.x.liu@intel.com
4 |
5 | after installing libwjelement (and running ldconfig if needed)...
6 | gcc -o validate -lwjelement -lwjreader validate.c
7 | or
8 | gcc -o validate `pkg-config --libs wjelement` validate.c
9 | */
10 |
11 |
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 |
19 |
20 | /*
21 | callback: load more schema from files based on "name" and a pattern argument
22 | */
23 | static WJElement schema_load(const char *name, void *client,
24 | const char *file, const int line) {
25 | char *format;
26 | char *path;
27 | FILE *schemafile;
28 | WJReader readschema;
29 | WJElement schema;
30 |
31 | schema = NULL;
32 | if(client && name) {
33 | format = (char *)client;
34 | path = malloc(strlen(format) + strlen(name));
35 | sprintf(path, format, name);
36 |
37 | if ((schemafile = fopen(path, "r"))) {
38 | if((readschema = WJROpenFILEDocument(schemafile, NULL, 0))) {
39 | schema = WJEOpenDocument(readschema, NULL, NULL, NULL);
40 | } else {
41 | fprintf(stderr, "json document failed to open: '%s'\n", path);
42 | }
43 | fclose(schemafile);
44 | } else {
45 | fprintf(stderr, "json file not found: '%s'\n", path);
46 | }
47 | free(path);
48 | }
49 | WJEDump(schema);
50 | return schema;
51 | }
52 |
53 | /*
54 | callback: cleanup/free open schema
55 | */
56 | static void schema_free(WJElement schema, void *client) {
57 | WJECloseDocument(schema);
58 | return;
59 | }
60 |
61 | /*
62 | callback: plop validation errors to stderr
63 | */
64 | static void schema_error(void *client, const char *format, ...) {
65 | va_list ap;
66 | va_start(ap, format);
67 | vfprintf(stderr, format, ap);
68 | va_end(ap);
69 | fprintf(stderr, "\n");
70 | }
71 |
72 |
73 | int main(int argc, char **argv) {
74 | int ret = 0;
75 | FILE *jsonfile = NULL;
76 | FILE *schemafile = NULL;
77 | WJReader readjson = NULL;
78 | WJReader readschema = NULL;
79 | WJElement json = NULL;
80 | WJElement schema = NULL;
81 | char *format = NULL;
82 |
83 | if(argc != 3 && argc != 4) {
84 | printf("usage:\n");
85 | printf("\t%s \n", argv[0]);
86 | printf("\t%s \n", argv[0]);
87 | printf(": \"path/to/%%s.json\" additional schemas\n");
88 | return 255;
89 | }
90 |
91 | if(!(jsonfile = fopen(argv[1], "r"))) {
92 | fprintf(stderr, "json file not found: '%s'\n", argv[1]);
93 | ret = 1;
94 | } else if(!(schemafile = fopen(argv[2], "r"))) {
95 | fprintf(stderr, "schema file not found: '%s'\n", argv[2]);
96 | ret = 2;
97 | }
98 | if(argc == 4) {
99 | format = argv[3];
100 | } else {
101 | format = NULL;
102 | }
103 |
104 | if(jsonfile && schemafile) {
105 | if(!(readjson = WJROpenFILEDocument(jsonfile, NULL, 0)) ||
106 | !(json = WJEOpenDocument(readjson, NULL, NULL, NULL))) {
107 | fprintf(stderr, "json could not be read.\n");
108 | ret = 3;
109 | } else if(!(readschema = WJROpenFILEDocument(schemafile, NULL, 0)) ||
110 | !(schema = WJEOpenDocument(readschema, NULL, NULL, NULL))) {
111 | fprintf(stderr, "schema could not be read.\n");
112 | ret = 4;
113 | }
114 | }
115 |
116 | if(json && schema) {
117 | WJEDump(json);
118 | printf("json: %s\n", readjson->depth ? "bad" : "good");
119 | WJEDump(schema);
120 | printf("schema: %s\n", readschema->depth ? "bad" : "good");
121 |
122 | if(WJESchemaValidate(schema, json, schema_error,
123 | schema_load, schema_free, format)) {
124 | printf("validation: PASS\n");
125 | } else {
126 | printf("validation: FAIL\n");
127 | }
128 | }
129 |
130 | if(json) WJECloseDocument(json);
131 | if(schema) WJECloseDocument(schema);
132 | if(readjson) WJRCloseDocument(readjson);
133 | if(readschema) WJRCloseDocument(readschema);
134 | if(jsonfile) fclose(jsonfile);
135 | if(schemafile) fclose(schemafile);
136 | return ret;
137 | }
138 |
--------------------------------------------------------------------------------
/include/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | FILE(GLOB INCLUDES_HEADERS *.h )
2 | LIST(REMOVE_ITEM INCLUDES_HEADERS ${PROJECT_SOURCE_DIR}/include/config.h)
3 | INSTALL(FILES ${INCLUDES_HEADERS} DESTINATION include/)
4 |
--------------------------------------------------------------------------------
/include/memmgr.h:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of WJElement.
3 |
4 | WJElement is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU Lesser General Public License as published
6 | by the Free Software Foundation.
7 |
8 | WJElement is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | GNU Lesser General Public License for more details.
12 |
13 | You should have received a copy of the GNU Lesser General Public License
14 | along with WJElement. If not, see .
15 | */
16 |
17 |
18 | /****************************************************************************
19 | * stub replacement for Messaging Architects' internal MemMgr library
20 | ****************************************************************************/
21 |
22 | #ifndef MEMMGR_H
23 | #define MEMMGR_H
24 |
25 | #include "xpl.h"
26 | #include
27 | #include
28 | #include
29 |
30 | #ifndef asprintf
31 | EXPORT int asprintf(char **strp, const char *fmt, ...);
32 | #else
33 | #define HAS_ASPRINTF
34 | #endif
35 |
36 | #define MemAssert(p)
37 |
38 | #define MemoryManagerOpen( c )
39 | #define MemoryManagerOpenEx( c, cfg )
40 | #define MemoryManagerClose( c )
41 | #define MemUpdateOwner( p, f, l )
42 | #define MemCopyOwner( d, s )
43 | #define MemGetOwner( p, f, l )
44 | #define MemMalloc( s ) malloc( (s) )
45 | #define MemMallocWait( s ) malloc( (s) )
46 | #define MemFree( p ) free( (p) )
47 | #define MemFreeEx( p, f, l ) free( (p) )
48 | #define MemRelease( p ) do { free( (*p) ); (*p) = NULL; } while (0)
49 | #define MemReleaseEx( p, f, l ) do { free( (*p) ); (*p) = NULL; } while (0)
50 | #define MemRealloc( p, s ) realloc( (p), (s) )
51 | #define MemReallocWait( p, s ) realloc( (p), (s) )
52 | #define MemStrdup( s ) strdup( (s) )
53 | #define MemStrdupWait( s ) strdup( (s) )
54 | #define MemStrndup( s, m ) strndup( (s), (m) )
55 | #define MemStrndupWait( s, m ) strndup( (s), (m) )
56 | #define MemSprintf( f, ... ) sprintf( (f), __VA_ARGS__ )
57 | #define MemAsprintf( p, f, ... ) (void)!asprintf( (p), (f), __VA_ARGS__ )
58 | #define MemCalloc( c, s ) calloc( (c), (s) )
59 | #define MemCallocWait( c, s ) calloc( (c), (s) )
60 |
61 | #define MemGenerateReports()
62 | #define MemDumpPools( f )
63 | #define MemConsumer()
64 |
65 | EXPORT void * MemMallocEx(void *ptr, size_t size, size_t *actual, XplBool wait, XplBool zero);
66 |
67 | #endif // MEMMGR_H
68 |
--------------------------------------------------------------------------------
/include/nmutil.h:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of WJElement.
3 |
4 | WJElement is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU Lesser General Public License as published
6 | by the Free Software Foundation.
7 |
8 | WJElement is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | GNU Lesser General Public License for more details.
12 |
13 | You should have received a copy of the GNU Lesser General Public License
14 | along with WJElement. If not, see .
15 | */
16 |
17 |
18 | /****************************************************************************
19 | * stub replacement for Messaging Architects' internal netmail utility library
20 | ****************************************************************************/
21 | #include "xpl.h"
22 | #include
23 | #include
24 |
25 |
26 | #ifndef NMUTIL_H
27 | #define NMUTIL_H
28 |
29 |
30 | #endif
31 |
--------------------------------------------------------------------------------
/include/wjelement.h:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of WJElement.
3 |
4 | WJElement is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU Lesser General Public License as published
6 | by the Free Software Foundation.
7 |
8 | WJElement is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | GNU Lesser General Public License for more details.
12 |
13 | You should have received a copy of the GNU Lesser General Public License
14 | along with WJElement. If not, see .
15 | */
16 |
17 |
18 | #ifndef WARP_JSON_ELEMENT_H
19 | #define WARP_JSON_ELEMENT_H
20 |
21 | #include
22 | #include
23 |
24 | #ifdef __cplusplus
25 | extern "C" {
26 | #endif
27 |
28 | /*
29 | WJElement Interfaces
30 |
31 | The WJElement interfaces allow you to load an entire JSON document into
32 | memory and to then access elements within it more easily.
33 |
34 | Elements within the hierarchy of a JSON document can be referenced using a
35 | path. Multiple levels of hierarchy can be referenced to find any element
36 | below the provided container. The following rules apply to any WJE
37 | functions that take a path argument.
38 |
39 | A child may be referenced with a alpha-numeric name, or a subscript within
40 | square brackets:
41 | foo
42 | ["foo"]
43 |
44 | Additional levels of heirarchy can be referenced by appending an additional
45 | subscript, or appending a dot and an additional alpha-numeric name:
46 | one.two.three.four
47 | one["two"]["three"].four
48 |
49 | Subscripts may contain double quoted names. Any special characters,
50 | (including .[]*?"'\) can be included by prefixing with a \.
51 | foo["bar.smeg"]
52 | foo["something with a \"quote\""]
53 |
54 | Subscripts may contain single quoted names, which behave as double quoted
55 | names but also allow for * and ? wild card substitution:
56 | foo['bar.*']
57 |
58 | Subscripts may reference an item by it's offset in an array (or object):
59 | foo[0]
60 | foo[3]
61 |
62 | Negative offsets are wrapped to the end of the array (or object) meaning
63 | that [-1] references the last item.
64 |
65 | Subscripts may reference a range of offsets by providing 2 offsets separated
66 | by a colon:
67 | foo[2:5]
68 |
69 | Subscripts may reference a set of items by separating offsets, offset,
70 | ranges, double quoted and single quoted values:
71 | foo[2,4:6,'bar.*', "smeg"]
72 |
73 | An empty subscript may be specified to reference all children.
74 | []
75 |
76 | A subscript of $ may be specified in actions perform creations to reference
77 | the item after the end of an array. This allows appending to an array.
78 | [$]
79 |
80 | A subscript may be prefixed by an | character to indicate that the subscript
81 | is optional. The portion of the selector from the | on will be ignored if
82 | there is a match and that match has no children.
83 |
84 | For example, if an element named foo may either be a string or an array of
85 | strings you can enumerate all values using a selector of:
86 | foo|[]
87 |
88 | A NULL path refers to the container itself.
89 |
90 | A path may end in a condition. The condition consists of an operator and a
91 | value. The value may be a number, a double quoted string, or a single
92 | quoted string. If a single quoted string is used it may contain * and ?
93 | wild cards.
94 |
95 | The following operators are supported for any value:
96 | ==, !=
97 |
98 | A number value may also use the following operators:
99 | <, >, <=, >=
100 |
101 | Example:
102 | foo.bar <= 3
103 | foo.bar != 'foo*'
104 | foo.bar != "one"
105 |
106 | A condition may be separated from a path with a ; character.
107 |
108 | In the following examples the object named "foo" will be returned if it has
109 | a child named "bar" that matches the condition.
110 | foo; bar <= 3
111 | foo; bar != 'foo*'
112 | foo; bar != "one"
113 | */
114 |
115 | typedef struct WJElementPublic
116 | {
117 | char *name;
118 | WJRType type;
119 |
120 | struct WJElementPublic *next;
121 | struct WJElementPublic *prev;
122 |
123 | struct WJElementPublic *child;
124 | struct WJElementPublic *last;
125 | struct WJElementPublic *parent;
126 |
127 | /* The number of children */
128 | int count;
129 |
130 | /* The length if the type is WJR_TYPE_STRING */
131 | size_t length;
132 |
133 | /*
134 | A count of changes that have been performed on this element, which can
135 | be reset by the consumer.
136 | */
137 | int changes;
138 |
139 | void *client;
140 |
141 | /*
142 | If set then this freecb will be called before actually free'ing a
143 | WJElement. If it returns FALSE then the WJElement will NOT be free'd.
144 |
145 | This can be used to allow caching of objects. If used in this way then
146 | the consumer that set the callback is responsible for ensuring that it
147 | does get free'd correctly at the correct time.
148 | */
149 | XplBool (* freecb)(struct WJElementPublic *);
150 |
151 | /*
152 | If set then this callback will be called when this element is written
153 | with WJEWriteDocument() and the callback is responsible for writing the
154 | JSON for this node and it's children.
155 | */
156 | XplBool (* writecb)(struct WJElementPublic *, WJWriter writer, char *name);
157 | } WJElementPublic;
158 | typedef WJElementPublic * WJElement;
159 |
160 | /*
161 | Parse the provided JSON document and return the new WJElement
162 |
163 | The _WJEParse version can be used to parse a document with a non-standard
164 | quote character. This allows easy parsing of simple documents directly in a
165 | C source file without having to escape double quote characters. Example:
166 | doc = _WJEParse("{ 'foo': true, 'bar': 'yup' }", '\'');
167 | */
168 | #define WJEFromString(j) __WJEFromString((j), '"', __FILE__, __LINE__)
169 | #define WJEParse(j) __WJEFromString((j), '"', __FILE__, __LINE__)
170 | #define _WJEFromString(j, q) __WJEFromString((j), (q), __FILE__, __LINE__)
171 | #define _WJEParse(j, q) __WJEFromString((j), (q), __FILE__, __LINE__)
172 | EXPORT WJElement __WJEFromString(const char *json, char quote, const char *file, const int line);
173 |
174 | /*
175 | Allocate a string and write the JSON source for the provided WJElement to
176 | it. The consumer is responsible for calling MemFree() on the result.
177 |
178 | If the consumer and WJE are linked to separate memory libraries, it may be
179 | necessary to call WJEMemFree() instead.
180 | */
181 | EXPORT char * _WJEToString(WJElement document, XplBool pretty, const char *file, const int line);
182 | #define WJEToString( d, p ) _WJEToString( (d), (p), __FILE__, __LINE__)
183 |
184 | /* Read or write a WJElement to a file by path */
185 | EXPORT WJElement WJEFromFile(const char *path);
186 | EXPORT XplBool WJEToFile(WJElement document, XplBool pretty, const char *path);
187 |
188 | /*
189 | Load a WJElement object from the provided WJReader
190 |
191 | If a load callback is provided then it will be called before adding any new
192 | children, allowing the consumer to leave ignore specific elements of the
193 | hierarchy.
194 | */
195 | typedef XplBool (* WJELoadCB)(WJElement parent, char *path, void *data, const char *file, const int line);
196 | EXPORT WJElement _WJEOpenDocument(WJReader reader, char *where, WJELoadCB loadcb, void *data, const char *file, const int line);
197 | #define WJEOpenDocument(r, w, lcb, d) _WJEOpenDocument((r), (w), (lcb), (d), __FILE__, __LINE__)
198 |
199 | /* Write a WJElement object to the provided WJWriter */
200 | typedef XplBool (* WJEWriteCB)(WJElement node, WJWriter writer, void *data);
201 | EXPORT XplBool _WJEWriteDocument(WJElement document, WJWriter writer, char *name,
202 | WJEWriteCB precb, WJEWriteCB postcb, void *data);
203 | #define WJEWriteDocument(d, w, n) _WJEWriteDocument((d), (w), (n), NULL, NULL, NULL)
204 |
205 | /* Write a WJElement object to the provided FILE* */
206 | EXPORT void WJEWriteFILE(WJElement document, FILE* fd);
207 |
208 | /* Read a WJElement object from the provided FILE* */
209 | EXPORT WJElement WJEReadFILE(FILE* fd);
210 |
211 | /* Destroy a WJElement object */
212 | EXPORT XplBool _WJECloseDocument(WJElement document, const char *file, const int line);
213 | #define WJECloseDocument(d) _WJECloseDocument((d), __FILE__, __LINE__)
214 |
215 | /*
216 | WJECloseDocument is also used to delete/remove an item from a parent
217 | document:
218 | WJECloseDocument(WJEGet(...));
219 | */
220 |
221 | /*
222 | Remove any items matching the specified selector
223 | */
224 | #define WJERemove(d, p) \
225 | while ((WJECloseDocument(WJEGet((d), (p), NULL)))) { ; }
226 |
227 | /* Duplicate an existing WJElement */
228 | typedef XplBool (* WJECopyCB)(WJElement destination, WJElement object, void *data, const char *file, const int line);
229 | EXPORT WJElement _WJECopyDocument(WJElement to, WJElement from, WJECopyCB loadcb, void *data, const char *file, const int line);
230 | #define WJECopyDocument(t, f, lcb, d) _WJECopyDocument((t), (f), (lcb), (d), __FILE__, __LINE__)
231 |
232 |
233 | /* Remove a WJElement from it's parent (and siblings) */
234 | EXPORT XplBool _WJEDetach(WJElement document, const char *file, const int line);
235 | #define WJEDetach( d ) _WJEDetach( (d), __FILE__, __LINE__ )
236 | #define WJEDettach( d ) _WJEDetach( (d), __FILE__, __LINE__ )
237 |
238 | /* Add a document to another document as a child */
239 | EXPORT XplBool WJEAttach(WJElement container, WJElement document);
240 |
241 | /* Rename an element */
242 | EXPORT XplBool WJERename(WJElement document, const char *name);
243 |
244 | /* Merge all fields from one object to another */
245 | EXPORT XplBool WJEMergeObjects(WJElement to, WJElement from, XplBool overwrite);
246 |
247 | /*
248 | Find the first element within the hierarchy of a WJElement that matches the
249 | specified path.
250 |
251 | If 'last' is non-NULL then the next match will be returned instead, allowing
252 | enumeration of multiple matching elements.
253 | */
254 | EXPORT WJElement _WJEGet(WJElement container, char *path, WJElement last, const char *file, const int line);
255 | #define WJEGet( c, p, l ) _WJEGet( (c), (p), (l), __FILE__, __LINE__ )
256 |
257 | typedef enum {
258 | /*
259 | Return the value of an element. If the element does not exist then the
260 | provided default value will be returned.
261 | */
262 | WJE_GET = 0,
263 |
264 | /*
265 | Assign the specified value to an element. If the element does not exist
266 | then it will be created.
267 |
268 | If the element can not be created then an appropriate value will be
269 | returned to indicate the error.
270 |
271 | When applicable a NULL will be returned. Otherwise a value that does
272 | not match the requested value will be returned.
273 | */
274 | WJE_SET = 1,
275 |
276 | /*
277 | Create a new element and assign it the specified value.
278 |
279 | If an element already exists then it will not be modified, and the value
280 | of that existing element will be returned.
281 | */
282 | WJE_NEW = 2,
283 |
284 | /*
285 | Assign the specified value to an existing element, and return the value
286 | if successful.
287 |
288 | If the element does not exist then no elements are created or modified.
289 | When applicable a NULL will be returned. Otherwise a value that does
290 | not match the requested value will be returned.
291 | */
292 | WJE_MOD = 3
293 | } WJEAction;
294 |
295 | /*
296 | The following flags can be OR'ed with a WJEAction value for any function
297 | that takes a WJEAction:
298 | */
299 | #define WJE_IGNORE_CASE 0x00010000
300 |
301 |
302 | // TODO Remove this. WJE_PUT was renamed to WJE_MOD.
303 | #define WJE_PUT WJE_MOD
304 |
305 | #define _WJEString( container, path, action, last, value) __WJEString( (container), (path), (action), (last), (value), __FILE__, __LINE__)
306 | #define WJEString( container, path, action, value) __WJEString( (container), (path), (action), NULL, (value), __FILE__, __LINE__)
307 | #define _WJEStringN(container, path, action, last, value, len) __WJEStringN( (container), (path), (action), (last), (value), (len), __FILE__, __LINE__)
308 | #define WJEStringN(container, path, action, value, len) __WJEStringN( (container), (path), (action), NULL, (value), (len), __FILE__, __LINE__)
309 | #define _WJEObject( container, path, action, last) __WJEObject( (container), (path), (action), (last), __FILE__, __LINE__)
310 | #define WJEObject( container, path, action) __WJEObject( (container), (path), (action), NULL, __FILE__, __LINE__)
311 | #define _WJEBool( container, path, action, last, value) __WJEBool( (container), (path), (action), (last), (value), __FILE__, __LINE__)
312 | #define WJEBool( container, path, action, value) __WJEBool( (container), (path), (action), NULL, (value), __FILE__, __LINE__)
313 | #define _WJEArray( container, path, action, last) __WJEArray( (container), (path), (action), (last), __FILE__, __LINE__)
314 | #define WJEArray( container, path, action) __WJEArray( (container), (path), (action), NULL, __FILE__, __LINE__)
315 | #define _WJENull( container, path, action, last) __WJENull( (container), (path), (action), (last), __FILE__, __LINE__)
316 | #define WJENull( container, path, action) __WJENull( (container), (path), (action), NULL, __FILE__, __LINE__)
317 | #define _WJEInt32( container, path, action, last, value) __WJEInt32( (container), (path), (action), (last), (value), __FILE__, __LINE__)
318 | #define WJEInt32( container, path, action, value) __WJEInt32( (container), (path), (action), NULL, (value), __FILE__, __LINE__)
319 | #define _WJENumber( container, path, action, last, value) __WJEInt32( (container), (path), (action), (last), (value), __FILE__, __LINE__)
320 | #define WJENumber( container, path, action, value) __WJEInt32( (container), (path), (action), NULL, (value), __FILE__, __LINE__)
321 | #define _WJEUInt32( container, path, action, last, value) __WJEUInt32( (container), (path), (action), (last), (value), __FILE__, __LINE__)
322 | #define WJEUInt32( container, path, action, value) __WJEUInt32( (container), (path), (action), NULL, (value), __FILE__, __LINE__)
323 | #define _WJEInt64( container, path, action, last, value) __WJEInt64( (container), (path), (action), (last), (value), __FILE__, __LINE__)
324 | #define WJEInt64( container, path, action, value) __WJEInt64( (container), (path), (action), NULL, (value), __FILE__, __LINE__)
325 | #define _WJEUInt64( container, path, action, last, value) __WJEUInt64( (container), (path), (action), (last), (value), __FILE__, __LINE__)
326 | #define WJEUInt64( container, path, action, value) __WJEUInt64( (container), (path), (action), NULL, (value), __FILE__, __LINE__)
327 | #define _WJEDouble( container, path, action, last, value) __WJEDouble( (container), (path), (action), (last), (value), __FILE__, __LINE__)
328 | #define WJEDouble( container, path, action, value) __WJEDouble( (container), (path), (action), NULL, (value), __FILE__, __LINE__)
329 |
330 | EXPORT XplBool __WJEBool( WJElement container, const char *path, WJEAction action, WJElement *last, XplBool value, const char *file, const int line);
331 | EXPORT char * __WJEString( WJElement container, const char *path, WJEAction action, WJElement *last, const char *value, const char *file, const int line);
332 | EXPORT char * __WJEStringN( WJElement container, const char *path, WJEAction action, WJElement *last, const char *value, size_t len,const char *file, const int line);
333 | EXPORT WJElement __WJEObject( WJElement container, const char *path, WJEAction action, WJElement *last, const char *file, const int line);
334 | EXPORT WJElement __WJEArray( WJElement container, const char *path, WJEAction action, WJElement *last, const char *file, const int line);
335 | EXPORT WJElement __WJENull( WJElement container, const char *path, WJEAction action, WJElement *last, const char *file, const int line);
336 | EXPORT int32 __WJEInt32( WJElement container, const char *path, WJEAction action, WJElement *last, int32 value, const char *file, const int line);
337 | EXPORT uint32 __WJEUInt32( WJElement container, const char *path, WJEAction action, WJElement *last, uint32 value, const char *file, const int line);
338 | EXPORT int64 __WJEInt64( WJElement container, const char *path, WJEAction action, WJElement *last, int64 value, const char *file, const int line);
339 | EXPORT uint64 __WJEUInt64( WJElement container, const char *path, WJEAction action, WJElement *last, uint64 value, const char *file, const int line);
340 | EXPORT double __WJEDouble( WJElement container, const char *path, WJEAction action, WJElement *last, double value, const char *file, const int line);
341 |
342 | /*
343 | The following functions are identical to the non F variants except that the
344 | path argument has been moved to the end and is used as a format string. For
345 | example if you need a value by index, and that index is stored in the
346 | variable x:
347 | val = WJENumberF(doc, WJE_GET, NULL, 0, "foo[%d]", x);
348 | */
349 | EXPORT WJElement WJEGetF( WJElement container, WJElement last, const char *pathf, ...) XplFormatString(3, 4);
350 | EXPORT XplBool WJEBoolF( WJElement container, WJEAction action, WJElement *last, XplBool value, const char *pathf, ...) XplFormatString(5, 6);
351 | EXPORT char * WJEStringF( WJElement container, WJEAction action, WJElement *last, const char *value, const char *pathf, ...) XplFormatString(5, 6);
352 | EXPORT char * WJEStringNF(WJElement container, WJEAction action, WJElement *last, const char *value, size_t len, const char *pathf, ...) XplFormatString(6, 7);
353 | EXPORT WJElement WJEObjectF( WJElement container, WJEAction action, WJElement *last, const char *pathf, ...) XplFormatString(4, 5);
354 | EXPORT WJElement WJEArrayF( WJElement container, WJEAction action, WJElement *last, const char *pathf, ...) XplFormatString(4, 5);
355 | EXPORT WJElement WJENullF( WJElement container, WJEAction action, WJElement *last, const char *pathf, ...) XplFormatString(4, 5);
356 | EXPORT int32 WJEInt32F( WJElement container, WJEAction action, WJElement *last, int32 value, const char *pathf, ...) XplFormatString(5, 6);
357 | EXPORT uint32 WJEUInt32F( WJElement container, WJEAction action, WJElement *last, uint32 value, const char *pathf, ...) XplFormatString(5, 6);
358 | EXPORT int64 WJEInt64F( WJElement container, WJEAction action, WJElement *last, int64 value, const char *pathf, ...) XplFormatString(5, 6);
359 | EXPORT uint64 WJEUInt64F( WJElement container, WJEAction action, WJElement *last, uint64 value, const char *pathf, ...) XplFormatString(5, 6);
360 | EXPORT double WJEDoubleF( WJElement container, WJEAction action, WJElement *last, double value, const char *pathf, ...) XplFormatString(5, 6);
361 |
362 |
363 |
364 | /*
365 | Find, create or update an element by name instead of path. This allows
366 | access to elements that would be difficult to reference by path.
367 |
368 | Type specific actions may be done by passing the resulting WJElement and a
369 | NULL path to WJEBool(), WJENumber(), WJEString(), WJEObject(), WJEArray() or
370 | WJENull().
371 | */
372 | EXPORT WJElement _WJEChild(WJElement container, char *name, WJEAction action, const char *file, const int line);
373 | #define WJEChild(c, n, a) _WJEChild((c), (n), (a), __FILE__, __LINE__)
374 |
375 |
376 | /*
377 | Find, create or update an element by path regardless of type.
378 |
379 | Type specific actions may be done by passing the resulting WJElement and a
380 | NULL path to WJEBool(), WJENumber(), WJEString(), WJEObject(), WJEArray() or
381 | WJENull().
382 | */
383 | EXPORT WJElement _WJEAny(WJElement container, char *path, WJEAction action, WJElement last, const char *file, const int line);
384 | #define WJEAny(c, p, a, l) _WJEAny((c), (p), (a), (l), __FILE__, __LINE__)
385 |
386 |
387 | /* Calculate a hash for a document */
388 | typedef int (* WJEHashCB)(void *context, void *data, size_t size);
389 | EXPORT void WJEHash(WJElement document, WJEHashCB update, void *context);
390 |
391 | /* WJElement Schema-related stuff */
392 | /*
393 | Validate or find selectors according to schema.
394 | WJESchemaLoadCB callbacks are used to fetch schema as needed.
395 | WJESchemaFreeCB are called when schema is no longer needed.
396 | */
397 |
398 | typedef WJElement (* WJESchemaLoadCB)(const char *name, void *client, const char *file, const int line);
399 | typedef void (* WJESchemaFreeCB)(WJElement schema, void *client);
400 | typedef void (* WJESchemaMatchCB)(WJElement schema, const char *selector, void *client);
401 | typedef void (* WJEErrCB)(void *client, const char *format, ...);
402 |
403 | /*
404 | Validate a document against a given schema. Additional schema will be loaded
405 | via the load callback if needed. Any validation errors will be reported,
406 | printf-style, to errcb.
407 | */
408 | EXPORT XplBool WJESchemaValidate(WJElement schema, WJElement document,
409 | WJEErrCB err, WJESchemaLoadCB load,
410 | WJESchemaFreeCB freecb, void *client);
411 |
412 | /*
413 | Determine if a document does or does not implement a specific schema.
414 | Additional schema will be loaded via the load callback if needed.
415 |
416 | If a load callback is not provided then the object type will still be checked
417 | but it will not be considered a match if it is a type that extends the
418 | specifed type.
419 | */
420 | EXPORT XplBool WJESchemaIsType(WJElement document, const char *type,
421 | WJESchemaLoadCB loadcb, WJESchemaFreeCB freecb,
422 | void *client);
423 | /*
424 | variation of WJESchemaIsType which acts on a schema name instead of a document
425 | */
426 | EXPORT XplBool WJESchemaNameIsType(const char *describedby, const char *type,
427 | WJESchemaLoadCB loadcb,
428 | WJESchemaFreeCB freecb, void *client);
429 | /*
430 | calls back matchcb for each WJElement selector which will fetch a property
431 | of a given type and format, from a given document. The load callback will be
432 | used to load all necessary schema, starting with the document's "describedby".
433 | stripat-type wildcards may be used; "Date*" will find "date" and "date-time".
434 | */
435 | EXPORT void WJESchemaGetSelectors(WJElement document,
436 | char *type, char *format,
437 | WJESchemaLoadCB load,
438 | WJESchemaFreeCB freecb,
439 | WJESchemaMatchCB matchcb, void *client);
440 | /*
441 | variation of WJESchemaGetSelectors which provides selectors that _could_ exist
442 | in objects of the given "describedby" schema name
443 | */
444 | EXPORT void WJESchemaGetAllSelectors(char *describedby,
445 | char *type, char *format,
446 | WJESchemaLoadCB load,
447 | WJESchemaFreeCB freecb,
448 | WJESchemaMatchCB matchcb, void *client);
449 |
450 |
451 | EXPORT char * WJESchemaNameFindBacklink(char *describedby, const char *format,
452 | WJESchemaLoadCB loadcb, WJESchemaFreeCB freecb,
453 | void *client);
454 |
455 | EXPORT char * WJESchemaFindBacklink(WJElement document, const char *format,
456 | WJESchemaLoadCB loadcb, WJESchemaFreeCB freecb,
457 | void *client);
458 |
459 | EXPORT void WJESchemaFreeBacklink(char *backlink);
460 |
461 |
462 | /* Debug function that will write a document to stdout */
463 | EXPORT void WJEDump(WJElement document);
464 |
465 | /* Debug function that will write a document to a file in the current dir */
466 | EXPORT void WJEDumpFile(WJElement document);
467 |
468 | /*
469 | Our own MemFree wrapper, in cases where consumer code uses a different
470 | memory library than WJElement.
471 | It is always safe (but usually unnecessary) to use this instead of MemFree.
472 | */
473 | EXPORT void WJEMemFree(void *mem);
474 | EXPORT void WJEMemRelease(void **mem);
475 |
476 |
477 | #ifdef __cplusplus
478 | }
479 | #endif
480 |
481 | #endif /* WARP_JSON_ELEMENT_H */
482 |
--------------------------------------------------------------------------------
/include/wjreader.h:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of WJElement.
3 |
4 | WJElement is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU Lesser General Public License as published
6 | by the Free Software Foundation.
7 |
8 | WJElement is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | GNU Lesser General Public License for more details.
12 |
13 | You should have received a copy of the GNU Lesser General Public License
14 | along with WJElement. If not, see .
15 | */
16 |
17 |
18 | #ifndef WARP_JSON_READER_H
19 | #define WARP_JSON_READER_H
20 |
21 | #include
22 |
23 | #ifdef __cplusplus
24 | extern "C"{
25 | #endif
26 |
27 | /*
28 | WARP JSON Reader
29 |
30 | The WARP JSON Reader provides the ability to parse a JSON document as a
31 | stream. All parsing is done with a portion of the document loaded into a
32 | buffer, and the buffer is reloaded as needed.
33 |
34 | This allows the parser to be very fast, but does require that data in the
35 | document is accessed in the order of the document.
36 | */
37 |
38 | typedef struct {
39 | uint32 depth;
40 | uint32 maxdepth;
41 | void *userdata;
42 | } WJReaderPublic;
43 | typedef WJReaderPublic * WJReader;
44 |
45 | typedef size_t (* WJReadCallback)(char *data, size_t length, size_t seen, void *userdata);
46 |
47 | /*
48 | Open a JSON document, and prepare to parse. A callback function must be
49 | provided which will be used to read data as it is needed. The callback is
50 | expected to load data into the provided buffer and return the number of
51 | bytes that where loaded. If the number of bytes loaded is less than the
52 | requested length then it is assumed that the end of the document has been
53 | reached.
54 |
55 | If a buffer is provided to WJROpenDocument() then that buffer will be used,
56 | rather than allocating a new buffer. The size of the buffer must be
57 | provided as buffersize. If a buffer is not provided then one will be
58 | allocated of size buffersize.
59 |
60 | If a buffersize of 0 is passed to WJROpenDocument then the default size will
61 | be used (4KB). Some data is kept in the buffer while parsing, such as
62 | element names and values. If the buffer is not large enough for these then
63 | the document will fail to be parsed and will be aborted.
64 | */
65 | EXPORT WJReader _WJROpenDocument(WJReadCallback callback, void *userdata, char *buffer, size_t buffersize, uint32 maxdepth);
66 | #define WJROpenDocument(c, u, b, s) \
67 | _WJROpenDocument((c), (u), (b), (s), 250)
68 | EXPORT XplBool WJRCloseDocument(WJReader doc);
69 |
70 | /*
71 | Return a string, which contains the name of the next element of the
72 | specified parent, prefixed by a single character that represents the type.
73 | The pointer returned may be used as the parent argument in future calls to
74 | WJRNext() in order to return the children of the current object.
75 |
76 | If the object's name is larger than the maxnamelen argument then it will be
77 | truncated.
78 | */
79 | typedef enum {
80 | WJR_TYPE_OBJECT = 'O',
81 | WJR_TYPE_ARRAY = 'A',
82 |
83 | WJR_TYPE_STRING = 'S',
84 | WJR_TYPE_NUMBER = 'N',
85 |
86 | #ifdef WJE_DISTINGUISH_INTEGER_TYPE
87 | WJR_TYPE_INTEGER = 'I',
88 | #endif
89 |
90 | WJR_TYPE_BOOL = 'B',
91 | WJR_TYPE_TRUE = 'T',
92 | WJR_TYPE_FALSE = 'F',
93 | WJR_TYPE_NULL = '0',
94 |
95 | WJR_TYPE_UNKNOWN = '?'
96 | } WJRType;
97 |
98 | EXPORT char * WJRNext(char *parent, size_t maxnamelen, WJReader doc);
99 |
100 | /*
101 | Return the value of the last object returned by WJRNext(). The calling
102 | application is expected to know the type of the value, and call the
103 | appropriate function. When possible the value will be converted if it does
104 | not match the requested type.
105 |
106 | If the buffer is not large enough to contain the entire value then the
107 | WJRString() function may return a partial value. It may be called multiple
108 | times until a NULL is returned to ensure that the entire value has been
109 | read.
110 | */
111 | EXPORT char * WJRStringEx(XplBool *complete, size_t *length, WJReader doc);
112 | #define WJRString(c, d) WJRStringEx((c), NULL, (d))
113 | EXPORT XplBool WJRBoolean(WJReader doc);
114 |
115 | EXPORT int32 WJRInt32(WJReader doc);
116 | EXPORT uint32 WJRUInt32(WJReader doc);
117 | EXPORT int64 WJRInt64(WJReader doc);
118 | EXPORT uint64 WJRUInt64(WJReader doc);
119 | EXPORT double WJRDouble(WJReader doc);
120 |
121 | /*
122 | If the number type is not known, then attempt to read it as either a uint64
123 | or a double.
124 |
125 | If a decimal point is found in the number then TRUE will be returned and the
126 | value will be stored in *d. Otherwise FALSE will be returned and the value
127 | stored in *i.
128 | */
129 | EXPORT XplBool WJRIntOrDouble(WJReader doc, uint64 *i, double *d);
130 |
131 | /*
132 | If the current element is a WJR_TYPE_NUMBER then this function can be used
133 | to determine if the value is negative or positive. If negative then TRUE
134 | will be returned.
135 | */
136 | EXPORT XplBool WJRNegative(WJReader doc);
137 |
138 |
139 | /*
140 | An alternative method of opening a JSON document, which will provide a
141 | proper callback for reading the data from an open FILE *.
142 |
143 | As with the standard WJROpenDocument() a buffersize of 0 will use the
144 | default buffer size.
145 | */
146 | EXPORT size_t WJRFileCallback(char *buffer, size_t length, size_t seen, void *data);
147 | #define WJROpenFILEDocument(jsonfile, buffer, buffersize) \
148 | WJROpenDocument(WJRFileCallback, (jsonfile), (buffer), (buffersize))
149 |
150 | /*
151 | An alternative method of opening a JSON document, which will provide a
152 | proper callback for reading from a pre-allocated buffer in memory.
153 |
154 | As with the standard WJROpenDocument() a buffersize of 0 will use the
155 | default buffer size.
156 | */
157 | EXPORT size_t WJRMemCallback(char *buffer, size_t length, size_t seen, void *userdata);
158 | #define WJROpenMemDocument(json, buffer, buffersize) \
159 | WJROpenDocument(WJRMemCallback, (json), (buffer), (buffersize))
160 |
161 | #ifdef __cplusplus
162 | }
163 | #endif
164 |
165 | #endif /* WARP_JSON_READER_H */
166 |
167 |
--------------------------------------------------------------------------------
/include/wjwriter.h:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of WJElement.
3 |
4 | WJElement is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU Lesser General Public License as published
6 | by the Free Software Foundation.
7 |
8 | WJElement is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | GNU Lesser General Public License for more details.
12 |
13 | You should have received a copy of the GNU Lesser General Public License
14 | along with WJElement. If not, see .
15 | */
16 |
17 |
18 | #ifndef WARP_JSON_WRITER_H
19 | #define WARP_JSON_WRITER_H
20 |
21 | #include
22 |
23 | #ifdef __cplusplus
24 | extern "C"{
25 | #endif
26 |
27 | /*
28 | WARP JSON Writer
29 |
30 | The WARP JSON Writer provides the ability to easily write a JSON document to
31 | a stream. All data formatting is done automatically.
32 | */
33 | typedef size_t (* WJWriteCallback)(char *data, size_t size, void *writedata);
34 |
35 | typedef struct {
36 | XplBool pretty;
37 |
38 | /*
39 | Base may be set to 8, 10 (default), or 16. Using a base other than 10
40 | will result in a non standard JSON document which may not be read
41 | properly by all parsers.
42 | */
43 | int base;
44 |
45 | /*
46 | If set to TRUE (enabled by default) then any character that can not be
47 | encoded as a UTF8 character will be written as \xXX where XX are a hex
48 | representation of the value.
49 |
50 | This can be disabled since it is non-standard JSON and will cause parse
51 | errors with some parsers.
52 |
53 | If disabled then any invalid characters will not be written to the
54 | document.
55 | */
56 | XplBool escapeInvalidChars;
57 |
58 | struct {
59 | void *data;
60 | WJWriteCallback cb;
61 | } write;
62 |
63 | struct {
64 | void *data;
65 | void (* freecb)(void *data);
66 | } user;
67 | } WJWriterPublic;
68 | typedef WJWriterPublic* WJWriter;
69 |
70 | /*
71 | Open a stream that is ready to accept a JSON document. A callback function
72 | must be provided which will be used to write data as it is ready.
73 |
74 | A buffersize of 0 will disable write buffering entirely. The write buffer
75 | used will be at least the size requested, but may be larger.
76 | */
77 | #define WJWOpenDocument(p, c, d) _WJWOpenDocument((p), (c), (d), (3 * 1024))
78 | EXPORT WJWriter _WJWOpenDocument(XplBool pretty, WJWriteCallback callback, void *writedata, size_t buffersize);
79 | EXPORT XplBool WJWCloseDocument(WJWriter doc);
80 |
81 | /*
82 | Open an array. All objects that are direct children of the array MUST NOT
83 | be named. A value of NULL should be passed as name for any such values.
84 |
85 | When all values of the array have been written it can be closed with
86 | WJWCloseArray().
87 | */
88 | EXPORT XplBool WJWOpenArray(char *name, WJWriter doc);
89 | EXPORT XplBool WJWCloseArray(WJWriter doc);
90 |
91 | /*
92 | Open an object. All objects that are direct children of the object MUST be
93 | named. A value of NULL should NOT be passed as name for any such values.
94 |
95 | When all values of the object have been written it can be closed with
96 | WJWCloseObject().
97 | */
98 | EXPORT XplBool WJWOpenObject(char *name, WJWriter doc);
99 | EXPORT XplBool WJWCloseObject(WJWriter doc);
100 |
101 | /*
102 | Write a value to the document. If any values are written that are a direct
103 | child of an object then a non-NULL name MUST be provided. If any values are
104 | written that are a direct child of an array then a non-NULL name MUST NOT be
105 | provided.
106 | */
107 | EXPORT XplBool WJWStringN(char *name, char *value, size_t length, XplBool done, WJWriter doc);
108 | EXPORT XplBool WJWString(char *name, char *value, XplBool done, WJWriter doc);
109 | EXPORT XplBool WJWBoolean(char *name, XplBool value, WJWriter doc);
110 | EXPORT XplBool WJWNull(char *name, WJWriter doc);
111 |
112 | EXPORT XplBool WJWInt32(char *name, int32 value, WJWriter doc);
113 | EXPORT XplBool WJWUInt32(char *name, uint32 value, WJWriter doc);
114 | EXPORT XplBool WJWInt64(char *name, int64 value, WJWriter doc);
115 | EXPORT XplBool WJWUInt64(char *name, uint64 value, WJWriter doc);
116 | EXPORT XplBool WJWDouble(char *name, double value, WJWriter doc);
117 |
118 | /*
119 | Write a raw pre-formatted value to the document. It is up to the caller to
120 | ensure that the data is properly formatted and complete.
121 | */
122 | EXPORT XplBool WJWRawValue(char *name, char *value, XplBool done, WJWriter doc);
123 |
124 | /*
125 | An alternative method of opening a JSON document for writing, which will
126 | provide a proper callback for writing the data to an open FILE *.
127 | */
128 | EXPORT size_t WJWFileCallback(char *buffer, size_t length, void *data);
129 | #define WJWOpenFILEDocument(pretty, file) _WJWOpenDocument((pretty), WJWFileCallback, (file), 0)
130 |
131 | /*
132 | An alternative method of opening a JSON document for writing, which will
133 | provide a proper callback for allocating and writing to a memory allocation.
134 | */
135 | EXPORT WJWriter WJWOpenMemDocument(XplBool pretty, char **mem);
136 |
137 | #ifdef __cplusplus
138 | }
139 | #endif
140 |
141 | #endif /* WARP_JSON_WRITER_H */
142 |
143 |
--------------------------------------------------------------------------------
/include/xpl.h:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of WJElement.
3 |
4 | WJElement is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU Lesser General Public License as published
6 | by the Free Software Foundation.
7 |
8 | WJElement is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | GNU Lesser General Public License for more details.
12 |
13 | You should have received a copy of the GNU Lesser General Public License
14 | along with WJElement. If not, see .
15 | */
16 |
17 |
18 | /****************************************************************************
19 | * stub replacement for netmail's internal Xpl library
20 | ****************************************************************************/
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 |
27 |
28 | #ifndef XPL_H
29 | #define XPL_H
30 |
31 |
32 | #ifdef _WIN32
33 | # define WIN_CDECL __cdecl
34 | # define WIN_STDCALL __stdcall
35 | # ifndef COMPILE_AS_STATIC
36 | # define EXPORT __declspec(dllexport)
37 | # define IMPORT __declspec(dllimport)
38 | # else
39 | # define EXPORT
40 | # define IMPORT
41 | #endif
42 | # define INLINE __inline
43 | #else
44 | # define WIN_CDECL
45 | # define WIN_STDCALL
46 | # define EXPORT
47 | # define IMPORT
48 | # define INLINE __inline
49 |
50 | # include
51 | # include
52 | # include
53 | #endif
54 |
55 |
56 | /* xpldefs.h */
57 |
58 | #ifndef xpl_min
59 | #define xpl_min(a, b) (((a) < (b))? (a): (b))
60 | #endif
61 |
62 | #ifndef xpl_max
63 | #define xpl_max(a, b) (((a) > (b))? (a): (b))
64 | #endif
65 |
66 |
67 | /* xpltypes.h */
68 |
69 | typedef int XplBool;
70 |
71 | #ifndef WIN32
72 | typedef long LONG;
73 | #endif
74 |
75 | #ifndef FALSE
76 | # define FALSE (0)
77 | #endif
78 |
79 | #ifndef TRUE
80 | # define TRUE (!FALSE)
81 | #endif
82 |
83 | #ifndef _UNSIGNED_INT_64
84 | #define _UNSIGNED_INT_64
85 | #ifdef __WATCOMC__
86 | typedef unsigned __int64 uint64;
87 | #elif UINT64_MAX == 18446744073709551615ULL
88 | typedef uint64_t uint64;
89 | #elif ULLONG_MAX == 18446744073709551615ULL
90 | typedef unsigned long long uint64;
91 | #elif ULONG_MAX == 18446744073709551615ULL
92 | typedef unsigned long uint64;
93 | #elif UINT_MAX == 18446744073709551615ULL
94 | typedef unsigned int uint64;
95 | #elif USHRT_MAX == 18446744073709551615ULL
96 | typedef unsigned short uint64;
97 | #else
98 | #error "Can't determine the size of uint64"
99 | #endif
100 | #endif
101 |
102 | #ifndef _SIGNED_INT_64
103 | #define _SIGNED_INT_64
104 | #ifdef __WATCOMC__
105 | typedef signed __int64 int64;
106 | #elif INT64_MAX == 9223372036854775807LL
107 | typedef int64_t int64;
108 | #elif LLONG_MAX == 9223372036854775807LL
109 | typedef signed long long int64;
110 | #elif LONG_MAX == 9223372036854775807LL
111 | typedef signed long int64;
112 | #elif INT_MAX == 9223372036854775807LL
113 | typedef signed int int64;
114 | #elif SHRT_MAX == 9223372036854775807LL
115 | typedef signed short int64;
116 | #else
117 | #error "Can't determine the size of int64"
118 | #endif
119 | #endif
120 |
121 | #ifndef _UNSIGNED_INT_32
122 | #define _UNSIGNED_INT_32
123 | #if ULONG_MAX == 4294967295UL
124 | typedef unsigned long uint32;
125 | #elif UINT_MAX == 4294967295UL
126 | typedef unsigned int uint32;
127 | #elif USHRT_MAX == 4294967295UL
128 | typedef unsigned short uint32;
129 | #else
130 | #error "Can't determine the size of uint32"
131 | #endif
132 | #endif
133 |
134 | #ifndef _SIGNED_INT_32
135 | #define _SIGNED_INT_32
136 | #if LONG_MAX == 2147483647L
137 | typedef signed long int32;
138 | #elif INT_MAX == 2147483647L
139 | typedef signed int int32;
140 | #elif SHRT_MAX == 2147483647L
141 | typedef signed short int32;
142 | #else
143 | #error "Can't determine the size of int32"
144 | #endif
145 | #endif
146 |
147 | #ifndef _UNSIGNED_INT_16
148 | #define _UNSIGNED_INT_16
149 | #if USHRT_MAX == 65535U
150 | typedef unsigned short uint16;
151 | #elif UCHAR_MAX == 65535U
152 | typedef signed char uint16;
153 | #else
154 | #error "Can't determine the size of uint16"
155 | #endif
156 | #endif
157 |
158 | #ifndef _SIGNED_INT_16
159 | #define _SIGNED_INT_16
160 | #if SHRT_MAX == 32767
161 | typedef signed short int16;
162 | #elif SCHAR_MAX == 32767
163 | typedef unsigned char uint16;
164 | #else
165 | #error "Can't determine the size of int16"
166 | #endif
167 | #endif
168 |
169 | #ifndef _UNSIGNED_INT_8
170 | #define _UNSIGNED_INT_8
171 | #if UCHAR_MAX == 255U
172 | typedef unsigned char uint8;
173 | #else
174 | #error "Can't determine the size of uint8"
175 | #endif
176 | #endif
177 |
178 | #ifndef _SIGNED_INT_8
179 | #define _SIGNED_INT_8
180 | #if SCHAR_MAX == 127
181 | typedef signed char int8;
182 | #else
183 | #error "Can't determine the size of int8"
184 | #endif
185 | #endif
186 |
187 |
188 | /* xplutil.h */
189 |
190 | #if defined __GNUC__
191 | #define XplFormatString(formatStringIndex, firstToCheck) __attribute__ ((format (printf, formatStringIndex, firstToCheck)))
192 | #else
193 | #define XplFormatString(formatStringIndex, firstToCheck)
194 | #endif
195 |
196 | # define DebugAssert( x )
197 |
198 | # if defined(_MSC_VER)
199 | # define stricmp(a,b) _stricmp(a,b)
200 | # define strnicmp(a,b,c) _strnicmp(a,b,c)
201 | #else
202 | # ifndef stricmp
203 | # define stricmp(a,b) strcasecmp(a,b)
204 | # endif
205 | # ifndef strnicmp
206 | # define strnicmp(a,b,c) strncasecmp(a,b,c)
207 | # endif
208 | #endif
209 |
210 | EXPORT char *_skipspace( char *source, const char *breakchars );
211 | #define skipspace(s) _skipspace((s), "\r\n")
212 | EXPORT char *chopspace( char *value );
213 |
214 | EXPORT size_t vstrcatf( char *buffer, size_t bufferSize, size_t *sizeNeeded, const char *format, va_list args );
215 | EXPORT size_t strprintf( char *buffer, size_t bufferSize, size_t *sizeNeeded, const char *format, ... ) XplFormatString(4, 5);
216 | EXPORT int stripat(char *str, char *pattern);
217 | EXPORT int stripatn(char *str, char *pattern, size_t len);
218 |
219 | EXPORT char * strspace( char *source );
220 |
221 | #if defined(_WIN32)
222 | EXPORT char * strndup(char* p, size_t maxlen);
223 | #endif
224 |
225 | #endif
226 |
--------------------------------------------------------------------------------
/pkg-config.pc.cmake:
--------------------------------------------------------------------------------
1 | Name: wjelement
2 | Description: ${PROJECT_DESCRIPTION}
3 | Version: ${PROJECT_VERSION}
4 | Requires: ${PKG_CONFIG_REQUIRES}
5 | prefix=${CMAKE_INSTALL_PREFIX}
6 | includedir=${PKG_CONFIG_INCLUDEDIR}
7 | libdir=${PKG_CONFIG_LIBDIR}
8 | Libs: ${PKG_CONFIG_LIBS}
9 | Cflags: ${PKG_CONFIG_CFLAGS}
10 |
--------------------------------------------------------------------------------
/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_subdirectory(lib)
2 | add_subdirectory(wjreader)
3 | add_subdirectory(wjwriter)
4 | add_subdirectory(wjelement)
5 | add_subdirectory(cli)
6 |
--------------------------------------------------------------------------------
/src/cli/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_executable(wjecli wjecli.c wjecli.h cmds.c)
2 |
3 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ALL_CFLAGS} ${PTHREAD_CFLAGS} ${OPENSSL_CFLAGS}")
4 |
5 | target_link_libraries(wjecli
6 | wjelement
7 | )
8 |
9 | install(TARGETS wjecli DESTINATION bin)
10 | install(PROGRAMS wje DESTINATION bin)
11 |
12 |
--------------------------------------------------------------------------------
/src/cli/wje:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | dir=`dirname $0`
4 |
5 | which rlwrap > /dev/null
6 | if [ $? == 0 ]; then
7 | rlwrap -pred $dir/wjecli $@
8 | else
9 | $dir/wjecli $@
10 | fi
11 |
--------------------------------------------------------------------------------
/src/cli/wjecli.c:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of WJElement.
3 |
4 | WJElement is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU Lesser General Public License as published
6 | by the Free Software Foundation.
7 |
8 | WJElement is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | GNU Lesser General Public License for more details.
12 |
13 | You should have received a copy of the GNU Lesser General Public License
14 | along with WJElement. If not, see .
15 | */
16 |
17 |
18 | #include "wjecli.h"
19 | #ifdef _WIN32
20 | #include
21 | #else
22 | #include
23 | #endif
24 |
25 | WJECLIGlobals wje;
26 |
27 | extern WJECLIcmd WJECLIcmds[];
28 |
29 | void usage(char *arg0)
30 | {
31 | WJECLIcmd *cmd;
32 | int i;
33 |
34 | if (arg0) {
35 | printf("Command line JSON document browser\n");
36 | printf("Usage: %s [json file]\n\n", arg0);
37 |
38 | printf("Commands:\n");
39 | }
40 |
41 | for (i = 0; (cmd = &WJECLIcmds[i]) && cmd->name; i++) {
42 | if (!cmd->description) {
43 | continue;
44 | }
45 |
46 | printf("\t");
47 |
48 | if (cmd->letter != '\0') {
49 | printf("%c|", cmd->letter);
50 | }
51 |
52 | printf("%s %s\n", cmd->name, cmd->args ? cmd->args : "");
53 | printf("\t\t%s\n\n", cmd->description);
54 | }
55 | }
56 |
57 | char * nextfield(char *value, char **end)
58 | {
59 | char *d, *s;
60 |
61 | if (end) {
62 | *end = NULL;
63 | }
64 |
65 | if (!value) {
66 | return(NULL);
67 | }
68 |
69 | value = skipspace(value);
70 |
71 | if (!*value) {
72 | return(NULL);
73 | }
74 |
75 | if ('"' == *value) {
76 | value++;
77 |
78 | d = s = value;
79 | while (s) {
80 | switch (*s) {
81 | case '\0':
82 | case '\n':
83 | case '\r':
84 | *d = '\0';
85 | return(*value ? value : NULL);
86 |
87 | case '"':
88 | *d = '\0';
89 | if (end) {
90 | *end = s + 1;
91 | }
92 | return(*value ? value : NULL);
93 |
94 | case '\\':
95 | switch (*(++s)) {
96 | case '"':
97 | case '\\':
98 | *(d++) = *(s++);
99 | break;
100 |
101 | default:
102 | *(d++) = '\\';
103 | }
104 | break;
105 |
106 | default:
107 | *(d++) = *(s++);
108 | break;
109 | }
110 | }
111 | } else {
112 | if ((d = strspace(value))) {
113 | *d = '\0';
114 | if (end) {
115 | *end = d + 1;
116 | }
117 | }
118 |
119 | return(*value ? value : NULL);
120 | }
121 |
122 | return(NULL);
123 | }
124 |
125 | int runcmd(WJElement *doc, WJElement *current, char *line)
126 | {
127 | WJECLIcmd *command;
128 | char *cmd = line;
129 | char *args = NULL;
130 | int i;
131 |
132 | // cmd = nextfield(line, &args);
133 |
134 | /* Look for a command using the letter, which does NOT require a space */
135 | for (i = 0; (command = &WJECLIcmds[i]) && command->name; i++) {
136 | if (command->letter != '\0' && *cmd == command->letter) {
137 | args = skipspace(++cmd);
138 | break;
139 | }
140 | }
141 |
142 | /* Look for a full command */
143 | if (!command || !command->name) {
144 | cmd = nextfield(line, &args);
145 |
146 | for (i = 0; (command = &WJECLIcmds[i]) && command->name; i++) {
147 | if (!stricmp(cmd, command->name)) {
148 | break;
149 | }
150 | }
151 | }
152 |
153 | if (!*current) {
154 | *current = *doc;
155 | }
156 |
157 | if (command && command->name) {
158 | return(command->cb(doc, current, args));
159 | } else {
160 | fprintf(stderr, "Unknown command: %s\n", cmd);
161 | return(3);
162 | }
163 | }
164 |
165 | int main(int argc, char **argv)
166 | {
167 | FILE *in = NULL;
168 | WJElement doc = NULL;
169 | WJElement current = NULL;
170 | int r = 0;
171 | WJReader reader;
172 | char *cmd;
173 | char line[1024];
174 |
175 | /* Print pretty documents by default */
176 | wje.pretty = TRUE;
177 |
178 | /* Print base 10 by default */
179 | wje.base = 10;
180 |
181 | if (argc > 2) {
182 | /* Umm, we only allow one argument... a filename */
183 | usage(argv[0]);
184 | return(1);
185 | }
186 |
187 | MemoryManagerOpen("wje-cli");
188 |
189 | if (argc == 2) {
190 | if (!stricmp(argv[1], "--help") || !stricmp(argv[1], "-h")) {
191 | usage(argv[0]);
192 | MemoryManagerClose("wje-cli");
193 | return(0);
194 | }
195 |
196 | if (!(in = fopen(argv[1], "rb"))) {
197 | perror(NULL);
198 | MemoryManagerClose("wje-cli");
199 | return(2);
200 | }
201 |
202 | /*
203 | A filename was specified on the command line. Does this look
204 | like a script, or a JSON document?
205 | */
206 | if (fgets(line, sizeof(line), in) &&
207 | !strncmp(line, "#!", 2)
208 | ) {
209 | /* This looks like a script, read commands from this file */
210 | ;
211 | } else {
212 | /* Assume it is a JSON document, rewind back to the start. */
213 | rewind(in);
214 |
215 | if ((reader = WJROpenFILEDocument(in, NULL, 0))) {
216 | doc = WJEOpenDocument(reader, NULL, NULL, NULL);
217 | WJRCloseDocument(reader);
218 |
219 | wje.filename = MemStrdup(argv[1]);
220 | }
221 |
222 | fclose(in);
223 | in = NULL;
224 |
225 | if (!doc) {
226 | fprintf(stderr, "Could not parse JSON document: %s\n", argv[1]);
227 | MemoryManagerClose("wje-cli");
228 | return(3);
229 | }
230 | }
231 | }
232 |
233 | if (!in) {
234 | /* Read commands from standard in */
235 | in = stdin;
236 | }
237 |
238 | if (!doc) {
239 | /* Start with an empty document if one wasn't specified */
240 | doc = WJEObject(NULL, NULL, WJE_SET);
241 | }
242 | current = doc;
243 |
244 | for (;;) {
245 | /* Read the next command */
246 | if (in == stdin && isatty(fileno(stdin))) {
247 | fprintf(stdout, "wje");
248 |
249 | if (r) {
250 | fprintf(stdout, " (%d)", r);
251 | }
252 |
253 | fprintf(stdout, "> ");
254 | fflush(stdout);
255 | }
256 |
257 | if (fgets(line, sizeof(line), in)) {
258 | cmd = _skipspace(line, NULL);
259 | } else {
260 | cmd = NULL;
261 | }
262 |
263 | if (!cmd || !*cmd) {
264 | /* Ignore blank lines */
265 | } else {
266 | r = runcmd(&doc, ¤t, cmd);
267 | }
268 | cmd = NULL;
269 |
270 | if (feof(in) || wje.exiting) {
271 | break;
272 | }
273 | }
274 |
275 | if (doc) {
276 | WJECloseDocument(doc);
277 | doc = NULL;
278 | }
279 |
280 | if (in && in != stdin) {
281 | fclose(in);
282 | in = NULL;
283 | }
284 |
285 | if (wje.filename) {
286 | MemRelease(&wje.filename);
287 | }
288 |
289 | MemoryManagerClose("wje-cli");
290 | return(r);
291 | }
292 |
293 |
--------------------------------------------------------------------------------
/src/cli/wjecli.h:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of WJElement.
3 |
4 | WJElement is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU Lesser General Public License as published
6 | by the Free Software Foundation.
7 |
8 | WJElement is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | GNU Lesser General Public License for more details.
12 |
13 | You should have received a copy of the GNU Lesser General Public License
14 | along with WJElement. If not, see .
15 | */
16 |
17 |
18 | #include
19 | #include
20 | #include
21 |
22 | typedef int (* cmdcb)(WJElement *document, WJElement *current, char *line);
23 |
24 | typedef struct
25 | {
26 | char letter;
27 | char *name;
28 | char *description;
29 | cmdcb cb;
30 | char *args;
31 | } WJECLIcmd;
32 |
33 | typedef struct
34 | {
35 | XplBool pretty;
36 | uint32 flags;
37 | int base;
38 |
39 | char *filename;
40 |
41 | XplBool exiting;
42 | } WJECLIGlobals;
43 |
44 | /* wjecli.c */
45 | char * nextfield(char *value, char **end);
46 | int runcmd(WJElement *doc, WJElement *current, char *line);
47 | void usage(char *arg0);
48 |
49 |
--------------------------------------------------------------------------------
/src/lib/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(xpl
2 | xpl.c
3 | )
4 |
5 | target_link_libraries(xpl
6 | ${ALL_LIBS}
7 | )
8 |
9 | SET_TARGET_PROPERTIES(
10 | xpl
11 | PROPERTIES
12 | VERSION 1.0.0
13 | SOVERSION 1
14 | INSTALL_NAME_DIR "${LIB_DEST_DIR}"
15 | )
16 |
17 | install(TARGETS xpl
18 | LIBRARY DESTINATION ${LIB_DEST_DIR}
19 | ARCHIVE DESTINATION ${LIB_DEST_DIR}
20 | )
21 |
--------------------------------------------------------------------------------
/src/lib/xpl.c:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of WJElement.
3 |
4 | WJElement is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU Lesser General Public License as published
6 | by the Free Software Foundation.
7 |
8 | WJElement is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | GNU Lesser General Public License for more details.
12 |
13 | You should have received a copy of the GNU Lesser General Public License
14 | along with WJElement. If not, see .
15 | */
16 |
17 |
18 | #include
19 | #include
20 | #include
21 |
22 | /* hack for aix, windows, and other asprintf-less systems */
23 | #include
24 | #include
25 | #include "memmgr.h"
26 | #ifndef asprintf
27 | EXPORT int asprintf(char **ret, const char *format, ...) {
28 | va_list ap;
29 | int count;
30 | *ret = NULL;
31 | va_start(ap, format);
32 | count = vsnprintf(NULL, 0, format, ap);
33 | va_end(ap);
34 | if(count >= 0) {
35 | char* buffer = MemMalloc(count + 1);
36 | if(buffer == NULL) {
37 | return -1;
38 | }
39 | va_start(ap, format);
40 | count = vsnprintf(buffer, count + 1, format, ap);
41 | va_end(ap);
42 |
43 | if(count < 0) {
44 | MemFree(buffer);
45 | } else {
46 | *ret = buffer;
47 | }
48 | }
49 | return count;
50 | }
51 | #endif
52 |
53 | EXPORT size_t vstrcatf( char *buffer, size_t bufferSize, size_t *sizeNeeded, const char *format, va_list args )
54 | {
55 | int needed; /* vsnprintf is prototyped as returning int */
56 | char *start = NULL; /* where to write if non-NULL */
57 | size_t used = 0; /* space consumed in buffer */
58 | size_t space = 0; /* space free in buffer */
59 | size_t ret = 0;
60 |
61 | /* have buffer with a NUL inside it? */
62 | if(buffer && bufferSize && (start = memchr(buffer, '\0', bufferSize))) {
63 | used = start - buffer; /* yes, number of used bytes */
64 | /* available space in buffer */
65 | space = bufferSize - used;
66 | }
67 | #ifdef _MSC_VER
68 | needed = _vsnprintf(start, space, format, args);
69 | if (-1==needed) {
70 | needed = _vscprintf(format, args);
71 | }
72 | #else
73 | needed = vsnprintf(start, space, format, args);
74 | #endif
75 | if(needed < 0) {
76 | DebugAssert(0); // output error from vsnprintf
77 | }
78 | else if(start) { /* have place to write? */
79 | if(needed >= space) {
80 | DebugAssert(sizeNeeded);// buffer overflow and size return not provided
81 | *start = '\0'; /* tie off buffer as if vsnprintf never happened */
82 | }
83 | else {
84 | ret = needed; /* success, return bytes written */
85 | }
86 | }
87 | else if(buffer) { /* no place to write, did we have a buffer? */
88 | DebugAssert(0); // no terminating NUL found in buffer
89 | // This may be a bug, as it will delete a byte and will return a size
90 | // count that is short by 1. However, we already overflowed the buffer
91 | // if we got here, so there isn't much hope of goodness anyway. If it
92 | // happens we need to fix the underlying bug, not try to recover here.
93 | used = bufferSize - 1; /* make space for one byte */
94 | buffer[used] = '\0'; /* put a NUL there */
95 | }
96 | if(sizeNeeded) { /* need to return size? */
97 | *sizeNeeded = used + needed;
98 | }
99 | return ret;
100 | }
101 |
102 | EXPORT size_t strprintf( char *buffer, size_t bufferSize, size_t *sizeNeeded, const char *format, ... )
103 | {
104 | size_t needed;
105 | va_list args;
106 |
107 | if( buffer && bufferSize )
108 | {
109 | *buffer = '\0';
110 | }
111 | va_start( args, format );
112 | needed = vstrcatf( buffer, bufferSize, sizeNeeded, format, args );
113 | va_end( args );
114 | return needed;
115 | }
116 |
117 | EXPORT char * strichr(char *str, char c)
118 | {
119 | c = toupper(c);
120 |
121 | if (str) {
122 | char *ptr;
123 |
124 | for (ptr = str; *ptr != '\0'; ptr++) {
125 | if (c == toupper(*ptr)) {
126 | return(ptr);
127 | }
128 | }
129 | }
130 |
131 | return(NULL);
132 | }
133 |
134 | EXPORT char * stristrn(char *haystack, char *needle, size_t len)
135 | {
136 | if (haystack && needle && *haystack && *needle) {
137 | char *ptr;
138 |
139 | for (ptr = strichr(haystack, *needle); ptr; ptr = strichr(ptr + 1, *needle)) {
140 | if (!strnicmp(ptr, needle, len)) {
141 | return(ptr);
142 | }
143 | }
144 | }
145 |
146 | return(NULL);
147 | }
148 |
149 | static int stripat_r(char *str, char *pattern, size_t len, int depth)
150 | {
151 | char *next;
152 | char *match;
153 | char *end = pattern + len;
154 |
155 | if (!str || !pattern || depth > 15) {
156 | return(-1);
157 | }
158 |
159 | for (;;) {
160 | if (pattern >= end || !*pattern) {
161 | if (!*str) {
162 | return(0);
163 | } else {
164 | return(-1);
165 | }
166 | }
167 |
168 | switch (*pattern) {
169 | case '?':
170 | /* A ? must match a single character */
171 | if (*str) {
172 | str++;
173 | pattern++;
174 | } else {
175 | return(-1);
176 | }
177 |
178 | case '*':
179 | /*
180 | A * may match 0 or more characters.
181 |
182 | Find the next wild card in the pattern, and then search for
183 | the exact value between the two wild cards in the string.
184 |
185 | Example, if pattern points to "*abc*" then "abc" must be
186 | found in the string.
187 | */
188 | pattern++;
189 |
190 | for (next = pattern; *next && next < end && *next != '*' && *next != '?'; next++);
191 | if (next > pattern) {
192 | match = str - 1;
193 | while ((match = stristrn(match + 1, pattern, next - pattern))) {
194 | if (!stripat_r(match + (next - pattern), next, end - next, depth + 1)) {
195 | return(0);
196 | }
197 | }
198 |
199 | return(-1);
200 | } else if (pattern >= end || !*pattern) {
201 | /* The last character of pattern is a *, so we match. */
202 | return(0);
203 | }
204 |
205 | break;
206 |
207 | default:
208 | if (isspace(*pattern)) {
209 | /* Any whitespace matches any whitespace */
210 | if (isspace(*str)) {
211 | for (str++; isspace(*str); str++);
212 | for (pattern++; *pattern && pattern < end && isspace(*pattern); pattern++);
213 | } else {
214 | return(-1);
215 | }
216 | } else if (toupper(*pattern) == toupper(*str)) {
217 | pattern++;
218 | str++;
219 | } else {
220 | return(-1);
221 | }
222 |
223 | break;
224 | }
225 | }
226 | }
227 |
228 | EXPORT int stripatn(char *str, char *pattern, size_t len)
229 | {
230 | if (!pattern) return(-1);
231 |
232 | return(stripat_r(str, pattern, len, 1));
233 | }
234 |
235 | EXPORT int stripat(char *str, char *pattern)
236 | {
237 | if (!pattern) return(-1);
238 |
239 | return(stripat_r(str, pattern, strlen(pattern), 1));
240 | }
241 |
242 | EXPORT char *chopspace( char *value )
243 | {
244 | char *p;
245 |
246 | if( value )
247 | {
248 | for(p=value+strlen(value);p>value;p--)
249 | {
250 | if( !isspace( *(p-1) ) )
251 | {
252 | break;
253 | }
254 | }
255 | *p = '\0';
256 | }
257 | return( skipspace( value ) );
258 | }
259 |
260 | EXPORT char * _skipspace( char *source, const char *breakchars )
261 | {
262 | if( source )
263 | {
264 | while( *source && isspace( *source ) )
265 | {
266 | #if 0
267 | DebugAssert(*source != '\r');
268 | DebugAssert(*source != '\n');
269 | #endif
270 | if (breakchars && strchr(breakchars, *source)) {
271 | break;
272 | }
273 |
274 | source++;
275 | }
276 | }
277 | return source;
278 | }
279 |
280 | EXPORT char * strspace( char *source )
281 | {
282 | while( source && *source )
283 | {
284 | if( isspace( *source ) )
285 | {
286 | return source;
287 | }
288 | source++;
289 | }
290 | return NULL;
291 | }
292 |
293 | #if defined(_WIN32)
294 | EXPORT char * strndup( char *p, size_t maxlen )
295 | {
296 | if( p )
297 | {
298 | char *r = malloc( maxlen + 1 );
299 | if( !r )
300 | {
301 | return r;
302 | }
303 | strncpy( r, p, maxlen );
304 | r[maxlen] = '\0';
305 | return r;
306 | }
307 |
308 | return NULL;
309 | }
310 | #endif
311 |
312 | EXPORT void * MemMallocEx(void *ptr, size_t size, size_t *actual, XplBool wait, XplBool zero)
313 | {
314 | void *result;
315 |
316 | do {
317 | if (ptr) {
318 | result = realloc(ptr, size);
319 |
320 | /*
321 | This implementation doesn't currently have any way to know how
322 | big the existing allocation is, and so it can't zero the new
323 | data without writing over the old data.
324 | */
325 | zero = FALSE;
326 | } else {
327 | result = malloc(size);
328 | }
329 |
330 | if (result) {
331 | if (actual) {
332 | /*
333 | In this implementation we aren't actually pulling from a
334 | pool so the allocation size will be exactly what the
335 | consumer asked for.
336 | */
337 | *actual = size;
338 | }
339 |
340 | if (zero) {
341 | memset(result, 0, size);
342 | }
343 | }
344 | } while (wait && !result);
345 |
346 | return(result);
347 | }
348 |
349 |
--------------------------------------------------------------------------------
/src/wjelement/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(wjelement
2 | element.c
3 | element.h
4 | search.c
5 | types.c
6 | schema.c
7 | hash.c
8 | )
9 |
10 | target_link_libraries(wjelement
11 | wjreader
12 | wjwriter
13 | xpl
14 | ${ALL_LIBS}
15 | )
16 |
17 | SET_TARGET_PROPERTIES(
18 | wjelement
19 | PROPERTIES
20 | VERSION 1.0.0
21 | SOVERSION 1
22 | INSTALL_NAME_DIR "${LIB_DEST_DIR}"
23 | )
24 |
25 | install(TARGETS wjelement
26 | LIBRARY DESTINATION ${LIB_DEST_DIR}
27 | ARCHIVE DESTINATION ${LIB_DEST_DIR}
28 | )
29 |
30 |
31 | # unit tests
32 | add_executable(wjeunit
33 | wjeunit.c
34 | )
35 |
36 | target_link_libraries(wjeunit
37 | wjreader
38 | wjwriter
39 | wjelement
40 | xpl
41 | ${PTHREAD_LIBS}
42 | ${ALL_LIBS}
43 | )
44 |
45 | # The use of ${EXECUTABLE_OUTPUT_PATH} is required for windows
46 | add_test(WJElement:SelfReference ${EXECUTABLE_OUTPUT_PATH}/wjeunit self )
47 | add_test(WJElement:ElementNames ${EXECUTABLE_OUTPUT_PATH}/wjeunit names )
48 | add_test(WJElement:Offsets ${EXECUTABLE_OUTPUT_PATH}/wjeunit offsets )
49 | add_test(WJElement:Lists ${EXECUTABLE_OUTPUT_PATH}/wjeunit lists )
50 | add_test(WJElement:Paths ${EXECUTABLE_OUTPUT_PATH}/wjeunit paths )
51 | add_test(WJElement:GetValues ${EXECUTABLE_OUTPUT_PATH}/wjeunit getvalue )
52 | add_test(WJElement:GetMultipleValues ${EXECUTABLE_OUTPUT_PATH}/wjeunit getvalues )
53 | add_test(WJElement:SetValues ${EXECUTABLE_OUTPUT_PATH}/wjeunit setvalue )
54 | add_test(WJElement:SetMultipleValues ${EXECUTABLE_OUTPUT_PATH}/wjeunit setvalues )
55 | add_test(WJElement:NewValues ${EXECUTABLE_OUTPUT_PATH}/wjeunit newvalue )
56 | add_test(WJElement:NewMultipleValues ${EXECUTABLE_OUTPUT_PATH}/wjeunit newvalues )
57 | add_test(WJElement:PutValues ${EXECUTABLE_OUTPUT_PATH}/wjeunit putvalue )
58 | add_test(WJElement:PutMultipleValues ${EXECUTABLE_OUTPUT_PATH}/wjeunit putvalues )
59 | add_test(WJElement:Append ${EXECUTABLE_OUTPUT_PATH}/wjeunit append )
60 | add_test(WJElement:Conditions ${EXECUTABLE_OUTPUT_PATH}/wjeunit conditions )
61 | add_test(WJElement:Optionals ${EXECUTABLE_OUTPUT_PATH}/wjeunit optionals )
62 | add_test(WJElement:Defaults ${EXECUTABLE_OUTPUT_PATH}/wjeunit defaults )
63 | add_test(WJElement:FormatStr ${EXECUTABLE_OUTPUT_PATH}/wjeunit formatstr )
64 | add_test(WJElement:BigDoc ${EXECUTABLE_OUTPUT_PATH}/wjeunit bigdoc )
65 | add_test(WJElement:RealBigDoc ${EXECUTABLE_OUTPUT_PATH}/wjeunit realbigdoc )
66 | add_test(WJElement:RealBigDoc2 ${EXECUTABLE_OUTPUT_PATH}/wjeunit realbigdoc2 )
67 | add_test(WJElement:Schema ${EXECUTABLE_OUTPUT_PATH}/wjeunit schema )
68 |
69 |
--------------------------------------------------------------------------------
/src/wjelement/element.c:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of WJElement.
3 |
4 | WJElement is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU Lesser General Public License as published
6 | by the Free Software Foundation.
7 |
8 | WJElement is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | GNU Lesser General Public License for more details.
12 |
13 | You should have received a copy of the GNU Lesser General Public License
14 | along with WJElement. If not, see .
15 | */
16 |
17 | #include "element.h"
18 | #include
19 |
20 | void WJEChanged(WJElement element)
21 | {
22 | for (; element; element = element->parent) {
23 | element->changes++;
24 | }
25 | }
26 |
27 | _WJElement * _WJENew(_WJElement *parent, char *name, size_t len, const char *file, int line)
28 | {
29 | _WJElement *result;
30 | WJElement prev;
31 |
32 | if (parent) {
33 | _WJEChanged((WJElement) parent);
34 |
35 | if (WJR_TYPE_ARRAY == parent->pub.type) {
36 | /* Children of an array can not have a name */
37 | name = NULL;
38 | len = 0;
39 | } else if (WJR_TYPE_OBJECT != parent->pub.type || !name || !*name) {
40 | /*
41 | Only arrays and objects can contain children, and elements in an
42 | object MUST be named.
43 | */
44 | return(NULL);
45 | }
46 | }
47 |
48 | if ((result = MemMalloc(sizeof(_WJElement) + len + 1))) {
49 | memset(result, 0, sizeof(_WJElement));
50 |
51 | MemUpdateOwner(result, file, line);
52 |
53 | if (name) {
54 | strncpy(result->_name, name, len);
55 | result->pub.name = result->_name;
56 | }
57 | result->_name[len] = '\0';
58 |
59 | if (parent) {
60 | result->pub.parent = (WJElement) parent;
61 |
62 | if (!parent->pub.child) {
63 | parent->pub.child = (WJElement) result;
64 | } else {
65 | prev = parent->pub.last;
66 | prev->next = (WJElement) result;
67 | result->pub.prev = prev;
68 | }
69 |
70 | parent->pub.last = (WJElement) result;
71 | parent->pub.count++;
72 | }
73 |
74 | result->pub.type = WJR_TYPE_OBJECT;
75 | }
76 |
77 | return(result);
78 | }
79 |
80 | _WJElement * _WJEReset(_WJElement *e, WJRType type)
81 | {
82 | WJElement child;
83 |
84 | if (!e) return(NULL);
85 |
86 | while ((child = e->pub.child)) {
87 | WJEDetach(child);
88 | WJECloseDocument(child);
89 | }
90 |
91 | if (WJR_TYPE_STRING == e->pub.type && e->value.string) {
92 | MemRelease(&(e->value.string));
93 | }
94 | e->value.string = NULL;
95 | e->pub.length = 0;
96 | e->pub.type = type;
97 |
98 | return(e);
99 | }
100 |
101 | EXPORT XplBool _WJEDetach(WJElement document, const char *file, const int line)
102 | {
103 | if (!document) {
104 | return(FALSE);
105 | }
106 |
107 | MemUpdateOwner(document, file, line);
108 |
109 | /* Remove references to the document */
110 | if (document->parent) {
111 | WJEChanged(document->parent);
112 |
113 | if (document->parent->child == document) {
114 | document->parent->child = document->next;
115 | }
116 | if (document->parent->last == document) {
117 | document->parent->last = document->prev;
118 | }
119 | document->parent->count--;
120 | document->parent = NULL;
121 | }
122 |
123 | if (document->prev) {
124 | document->prev->next = document->next;
125 | }
126 |
127 | if (document->next) {
128 | document->next->prev = document->prev;
129 | }
130 |
131 | document->prev = NULL;
132 | document->next = NULL;
133 |
134 | return(TRUE);
135 | }
136 |
137 | EXPORT XplBool WJEAttach(WJElement container, WJElement document)
138 | {
139 | WJElement prev;
140 |
141 | if (!document || !container) {
142 | return(FALSE);
143 | }
144 |
145 | if (document->parent == container) {
146 | return(TRUE);
147 | }
148 |
149 | if (document->name) {
150 | while ((prev = WJEChild(container, document->name, WJE_GET))) {
151 | WJEDetach(prev);
152 | WJECloseDocument(prev);
153 | }
154 | }
155 |
156 | WJEDetach(document);
157 |
158 | /* Insert it into the new container */
159 | document->parent = container;
160 | if (!container->child) {
161 | container->child = document;
162 | } else {
163 | prev = container->last;
164 | prev->next = document;
165 | document->prev = prev;
166 | }
167 | container->last = document;
168 | container->count++;
169 | WJEChanged(container);
170 |
171 | return(TRUE);
172 | }
173 |
174 | EXPORT XplBool WJERename(WJElement document, const char *name)
175 | {
176 | _WJElement *current = (_WJElement *) document;
177 | WJElement e;
178 |
179 | if (!document) {
180 | return(FALSE);
181 | }
182 |
183 | /* Look for any siblings with that name, and fail if found */
184 | if (name && document->parent) {
185 | for (e = document->parent->child; e; e = e->next) {
186 | if (e != document && !stricmp(e->name, name)) {
187 | return(FALSE);
188 | }
189 | }
190 | }
191 |
192 | /* Free the previous name if needed */
193 | if (document->name && current->_name != document->name) {
194 | MemRelease(&document->name);
195 | }
196 |
197 | /* Set the new name */
198 | if (name) {
199 | if (!(document->name = MemStrdup(name))) {
200 | return(FALSE);
201 | }
202 | } else {
203 | document->name = NULL;
204 | }
205 |
206 | return(TRUE);
207 | }
208 |
209 | static WJElement _WJELoad(_WJElement *parent, WJReader reader, char *where, WJELoadCB loadcb, void *data, const char *file, const int line)
210 | {
211 | char *current, *name, *value;
212 | _WJElement *l = NULL;
213 | XplBool complete;
214 | size_t actual, used, len;
215 |
216 | if (!reader) {
217 | return((WJElement) _WJENew(NULL, NULL, 0, file, line));
218 | }
219 |
220 | if (!where) {
221 | /*
222 | A NULL poisition in a WJReader indicates the root of the document,
223 | so we must read to find the first real object.
224 | */
225 | where = WJRNext(NULL, 2048, reader);
226 | }
227 |
228 | if (!where) {
229 | /* This appears to be an empty document */
230 | return(NULL);
231 | }
232 |
233 | name = where[1] ? where + 1 : NULL;
234 |
235 | if (name && (WJEChild((WJElement) parent, name, WJE_GET))) {
236 | /* Do not load duplicate names */
237 | return(NULL);
238 | }
239 |
240 | if (loadcb && !loadcb((WJElement)parent, name, data, file, line)) {
241 | /* The consumer has rejected this item */
242 | return(NULL);
243 | }
244 |
245 | if ((l = _WJENew(parent, name, name ? strlen(name) : 0, file, line))) {
246 | switch ((l->pub.type = *where)) {
247 | default:
248 | case WJR_TYPE_UNKNOWN:
249 | case WJR_TYPE_NULL:
250 | break;
251 |
252 | case WJR_TYPE_OBJECT:
253 | case WJR_TYPE_ARRAY:
254 | while (reader && (current = WJRNext(where, 2048, reader))) {
255 | _WJELoad(l, reader, current, loadcb, data, file, line);
256 | }
257 | break;
258 |
259 | case WJR_TYPE_STRING:
260 | actual = 0;
261 | used = 0;
262 | len = 0;
263 | complete = FALSE;
264 | l->value.string = NULL;
265 | l->pub.length = 0;
266 |
267 | do {
268 | if ((value = WJRStringEx(&complete, &len, reader))) {
269 | if (used + len >= actual) {
270 | l->value.string = MemMallocEx(l->value.string,
271 | len + 1 + used, &actual, TRUE, TRUE);
272 | MemUpdateOwner(l->value.string, file, line);
273 | }
274 |
275 | memcpy(l->value.string + used, value, len);
276 | used += len;
277 | l->value.string[used] = '\0';
278 | l->pub.length = used;
279 | }
280 | } while (!complete);
281 | break;
282 |
283 | case WJR_TYPE_NUMBER:
284 | #ifdef WJE_DISTINGUISH_INTEGER_TYPE
285 | case WJR_TYPE_INTEGER:
286 | #endif
287 | l->value.number.hasDecimalPoint = WJRIntOrDouble(reader,
288 | &l->value.number.i,
289 | &l->value.number.d);
290 |
291 | #ifdef WJE_DISTINGUISH_INTEGER_TYPE
292 | if (l->value.number.hasDecimalPoint) {
293 | l->pub.type = WJR_TYPE_NUMBER;
294 | } else {
295 | l->pub.type = WJR_TYPE_INTEGER;
296 | }
297 | #endif
298 |
299 | if (WJRNegative(reader)) {
300 | /*
301 | Store the number as a positive, but keep track of the
302 | fact that it was negative.
303 | */
304 | l->value.number.negative = TRUE;
305 | } else {
306 | l->value.number.negative = FALSE;
307 | }
308 | break;
309 |
310 | case WJR_TYPE_TRUE:
311 | l->value.boolean = TRUE;
312 | break;
313 |
314 | case WJR_TYPE_BOOL:
315 | case WJR_TYPE_FALSE:
316 | l->value.boolean = FALSE;
317 | break;
318 | }
319 | }
320 |
321 | return((WJElement) l);
322 | }
323 |
324 | EXPORT WJElement _WJEOpenDocument(WJReader reader, char *where, WJELoadCB loadcb, void *data, const char *file, const int line)
325 | {
326 | WJElement element;
327 |
328 | if ((element = _WJELoad(NULL, reader, where, loadcb, data, file, line))) {
329 | MemUpdateOwner(element, file, line);
330 | }
331 |
332 | return(element);
333 | }
334 |
335 | typedef struct WJEMemArgs
336 | {
337 | char *json;
338 | char quote;
339 |
340 | size_t len;
341 | } WJEMemArgs;
342 |
343 | static size_t WJEMemCallback(char *buffer, size_t length, size_t seen, void *userdata)
344 | {
345 | WJEMemArgs *args = (WJEMemArgs *) userdata;
346 | char *json;
347 | char *q;
348 | size_t len;
349 |
350 | if (!args || !args->json) {
351 | return(0);
352 | }
353 |
354 | len = args->len - seen;
355 | json = args->json + seen;
356 |
357 | if (!len) {
358 | /* Done */
359 | return(0);
360 | }
361 |
362 | if (len > length) {
363 | len = length;
364 | }
365 |
366 | if (len > 0) {
367 | memcpy(buffer, json, len);
368 |
369 | switch (args->quote) {
370 | case '"':
371 | case '\0':
372 | /* Default behavior, do nothing to quotes */
373 | break;
374 |
375 | default:
376 | /* Replace quotes in the data we just copied */
377 | json = buffer;
378 | length = len;
379 |
380 | while (length && (q = memchr(json, args->quote, length))) {
381 | *q = '"';
382 |
383 | length -= (q - json);
384 | json = q;
385 | }
386 |
387 | break;
388 | }
389 | }
390 | return(len);
391 | }
392 |
393 | /*
394 | Parse a JSON document already in memory directoy without requiring the
395 | consumer to create a WJReader. This is meant as a simple utility function
396 | and allows parsing documents with a non standard quote char for the sake of
397 | embedding documents directly in C code.
398 | */
399 | EXPORT WJElement __WJEFromString(const char *json, char quote, const char *file, const int line)
400 | {
401 | WJElement doc = NULL;
402 | WJEMemArgs args;
403 | WJReader reader;
404 |
405 | args.json = (char *) json;
406 | args.quote = quote;
407 |
408 | if (json) {
409 | args.len = strlen(json);
410 | }
411 |
412 | if (json && (reader = WJROpenDocument(WJEMemCallback, &args, NULL, 0))) {
413 | doc = _WJEOpenDocument(reader, NULL, NULL, NULL, file, line);
414 | WJRCloseDocument(reader);
415 | }
416 |
417 | return(doc);
418 | }
419 |
420 | EXPORT char * _WJEToString(WJElement document, XplBool pretty, const char *file, const int line)
421 | {
422 | WJWriter writer;
423 | char *mem = NULL;
424 |
425 | if ((writer = WJWOpenMemDocument(pretty, &mem))) {
426 | WJEWriteDocument(document, writer, NULL);
427 | WJWCloseDocument(writer);
428 | }
429 | if (mem) {
430 | MemUpdateOwner(mem, file, line);
431 | }
432 | return(mem);
433 | }
434 |
435 | EXPORT WJElement WJEFromFile(const char *path)
436 | {
437 | WJElement e = NULL;
438 | FILE *f;
439 | WJReader reader;
440 |
441 | if (!path) {
442 | errno = EINVAL;
443 | return(NULL);
444 | }
445 |
446 | if ((f = fopen(path, "rb"))) {
447 | if ((reader = WJROpenFILEDocument(f, NULL, 0))) {
448 | e = WJEOpenDocument(reader, NULL, NULL, NULL);
449 | WJRCloseDocument(reader);
450 | }
451 |
452 | fclose(f);
453 | }
454 |
455 | return(e);
456 | }
457 |
458 | EXPORT XplBool WJEToFile(WJElement document, XplBool pretty, const char *path)
459 | {
460 | FILE *f;
461 | WJWriter writer;
462 | XplBool ret = FALSE;
463 |
464 | if (!document || !path) {
465 | errno = EINVAL;
466 | return(FALSE);
467 | }
468 |
469 | if ((f = fopen(path, "wb"))) {
470 | if ((writer = WJWOpenFILEDocument(pretty, f))) {
471 | ret = WJEWriteDocument(document, writer, NULL);
472 |
473 | WJWCloseDocument(writer);
474 | }
475 |
476 | fclose(f);
477 | }
478 |
479 | return(ret);
480 | }
481 |
482 | static WJElement _WJECopy(_WJElement *parent, WJElement original, WJECopyCB copycb, void *data, const char *file, const int line)
483 | {
484 | _WJElement *l = NULL;
485 | _WJElement *o;
486 | WJElement c;
487 | char *tmp;
488 |
489 | if (!(o = (_WJElement *) original)) {
490 | return(NULL);
491 | }
492 |
493 | if (copycb && !copycb((WJElement) parent, (WJElement) original, data, file, line)) {
494 | /* The consumer has rejected this item */
495 | return(NULL);
496 | }
497 |
498 | if ((l = _WJENew(parent, original->name, original->name ? strlen(original->name) : 0, file, line))) {
499 | switch ((l->pub.type = original->type)) {
500 | default:
501 | case WJR_TYPE_UNKNOWN:
502 | case WJR_TYPE_NULL:
503 | break;
504 |
505 | case WJR_TYPE_OBJECT:
506 | case WJR_TYPE_ARRAY:
507 | for (c = original->child; c; c = c->next) {
508 | _WJECopy(l, c, copycb, data, file, line);
509 | }
510 | break;
511 |
512 | case WJR_TYPE_STRING:
513 | if ((tmp = WJEString(original, NULL, WJE_GET, ""))) {
514 | l->value.string = MemStrdup(tmp);
515 | l->pub.length = original->length;
516 | } else {
517 | l->value.string = MemStrdup("");
518 | l->pub.length = 0;
519 | }
520 | break;
521 |
522 | case WJR_TYPE_NUMBER:
523 | #ifdef WJE_DISTINGUISH_INTEGER_TYPE
524 | case WJR_TYPE_INTEGER:
525 | #endif
526 | l->value.number.negative = o->value.number.negative;
527 | l->value.number.i = o->value.number.i;
528 | l->value.number.d = o->value.number.d;
529 | l->value.number.hasDecimalPoint = o->value.number.hasDecimalPoint;
530 |
531 | #ifdef WJE_DISTINGUISH_INTEGER_TYPE
532 | if (l->value.number.hasDecimalPoint) {
533 | l->pub.type = WJR_TYPE_NUMBER;
534 | } else {
535 | l->pub.type = WJR_TYPE_INTEGER;
536 | }
537 | #endif
538 |
539 | break;
540 |
541 | case WJR_TYPE_TRUE:
542 | case WJR_TYPE_BOOL:
543 | case WJR_TYPE_FALSE:
544 | l->value.boolean = WJEBool(original, NULL, WJE_GET, FALSE);
545 | break;
546 | }
547 | }
548 |
549 | return((WJElement) l);
550 | }
551 |
552 | EXPORT WJElement _WJECopyDocument(WJElement to, WJElement from, WJECopyCB copycb, void *data, const char *file, const int line)
553 | {
554 | if (to) {
555 | WJElement c;
556 |
557 | for (c = from->child; c; c = c->next) {
558 | _WJECopy((_WJElement *) to, c, copycb, data, file, line);
559 | }
560 | } else {
561 | if ((to = _WJECopy(NULL, from, copycb, data, file, line))) {
562 | MemUpdateOwner(to, file, line);
563 | }
564 | }
565 |
566 | return(to);
567 | }
568 |
569 | EXPORT XplBool WJEMergeObjects(WJElement to, WJElement from, XplBool overwrite)
570 | {
571 | WJElement a, b;
572 |
573 | if (!to || !from ||
574 | WJR_TYPE_OBJECT != to->type || WJR_TYPE_OBJECT != from->type
575 | ) {
576 | return(FALSE);
577 | }
578 |
579 | for (a = from->child; a; a = a->next) {
580 | if ((b = WJEChild(to, a->name, WJE_GET))) {
581 | if (WJR_TYPE_OBJECT == b->type && WJR_TYPE_OBJECT == a->type) {
582 | /* Merge all the children */
583 | WJEMergeObjects(b, a, overwrite);
584 |
585 | continue;
586 | } else if (overwrite) {
587 | /* Remove the existing value and overwrite it */
588 | WJECloseDocument(b);
589 | } else {
590 | /* Do nothing with this element */
591 | continue;
592 | }
593 | }
594 |
595 | /* Copy the object over */
596 | WJEAttach(to, WJECopyDocument(NULL, a, NULL, NULL));
597 | }
598 |
599 | return(TRUE);
600 | }
601 |
602 | EXPORT XplBool _WJEWriteDocument(WJElement document, WJWriter writer, char *name,
603 | WJEWriteCB precb, WJEWriteCB postcb, void *data)
604 | {
605 | _WJElement *current = (_WJElement *) document;
606 | WJElement child;
607 |
608 | if (precb && !precb(document, writer, data)) {
609 | return(FALSE);
610 | }
611 |
612 | if (document) {
613 | if (document->writecb) {
614 | return(document->writecb(document, writer, name));
615 | }
616 |
617 | switch (current->pub.type) {
618 | default:
619 | case WJR_TYPE_UNKNOWN:
620 | break;
621 |
622 | case WJR_TYPE_NULL:
623 | WJWNull(name, writer);
624 | break;
625 |
626 | case WJR_TYPE_OBJECT:
627 | WJWOpenObject(name, writer);
628 |
629 | child = current->pub.child;
630 | do {
631 | _WJEWriteDocument(child, writer, child ? child->name : NULL,
632 | precb, postcb, data);
633 | } while (child && (child = child->next));
634 |
635 | WJWCloseObject(writer);
636 | break;
637 |
638 | case WJR_TYPE_ARRAY:
639 | WJWOpenArray(name, writer);
640 |
641 | child = current->pub.child;
642 | do {
643 | _WJEWriteDocument(child, writer, NULL,
644 | precb, postcb, data);
645 | } while (child && (child = child->next));
646 |
647 | WJWCloseArray(writer);
648 | break;
649 |
650 | case WJR_TYPE_STRING:
651 | WJWStringN(name, current->value.string, current->pub.length, TRUE, writer);
652 | break;
653 |
654 | case WJR_TYPE_NUMBER:
655 | #ifdef WJE_DISTINGUISH_INTEGER_TYPE
656 | case WJR_TYPE_INTEGER:
657 | #endif
658 | if (current->value.number.hasDecimalPoint) {
659 | current->pub.type = WJR_TYPE_NUMBER;
660 | if (!current->value.number.negative) {
661 | WJWDouble(name, current->value.number.d, writer);
662 | } else {
663 | WJWDouble(name, -current->value.number.d, writer);
664 | }
665 | } else {
666 | #ifdef WJE_DISTINGUISH_INTEGER_TYPE
667 | current->pub.type = WJR_TYPE_INTEGER;
668 | #endif
669 | if (!current->value.number.negative) {
670 | WJWUInt64(name, current->value.number.i, writer);
671 | } else {
672 | WJWInt64(name, -((int64) current->value.number.i), writer);
673 | }
674 | }
675 | break;
676 |
677 | case WJR_TYPE_TRUE:
678 | case WJR_TYPE_BOOL:
679 | case WJR_TYPE_FALSE:
680 | WJWBoolean(name, current->value.boolean, writer);
681 | break;
682 | }
683 | }
684 |
685 | if (postcb && !postcb(document, writer, data)) {
686 | return(FALSE);
687 | }
688 |
689 | return(TRUE);
690 | }
691 |
692 | EXPORT XplBool _WJECloseDocument(WJElement document, const char *file, const int line)
693 | {
694 | _WJElement *current = (_WJElement *) document;
695 | WJElement child;
696 |
697 | if (!document) {
698 | return(FALSE);
699 | }
700 |
701 | WJEDetach(document);
702 |
703 | if (document->freecb && !document->freecb(document)) {
704 | /* The callback has prevented free'ing the document */
705 | return(TRUE);
706 | }
707 |
708 | WJEChanged(document);
709 |
710 | /* Remove references to this object */
711 | if (document->parent) {
712 | if (document->parent->child == document) {
713 | document->parent->child = document->next;
714 | }
715 | if (document->parent->last == document) {
716 | document->parent->last = document->prev;
717 | }
718 | document->parent->count--;
719 | }
720 |
721 | if (document->prev) {
722 | document->prev->next = document->next;
723 | }
724 |
725 | if (document->next) {
726 | document->next->prev = document->prev;
727 | }
728 |
729 |
730 | /* Destroy all children */
731 | while ((child = document->child)) {
732 | WJEDetach(child);
733 | _WJECloseDocument(child, file, line);
734 | }
735 |
736 | if (current->pub.type == WJR_TYPE_STRING) {
737 | MemFreeEx(current->value.string, file, line);
738 | current->pub.length = 0;
739 | }
740 |
741 | if (document->name && current->_name != document->name) {
742 | MemReleaseEx(&document->name, file, line);
743 | }
744 |
745 | MemFreeEx(current, file, line);
746 |
747 | return(TRUE);
748 | }
749 |
750 | EXPORT void WJEDump(WJElement document)
751 | {
752 | WJEWriteFILE(document, stdout);
753 | }
754 |
755 | EXPORT void WJEWriteFILE(WJElement document, FILE* fd)
756 | {
757 | WJWriter dumpWriter;
758 |
759 | if ((dumpWriter = WJWOpenFILEDocument(TRUE, fd))) {
760 | WJEWriteDocument(document, dumpWriter, NULL);
761 | WJWCloseDocument(dumpWriter);
762 | }
763 | fprintf(fd, "\n");
764 | fflush(fd);
765 | }
766 |
767 | EXPORT void WJEDumpFile(WJElement document)
768 | {
769 | WJWriter dumpWriter;
770 | FILE *file;
771 | char path[1024];
772 |
773 | strprintf(path, sizeof(path), NULL, "%08lx.json", (long) time(NULL));
774 |
775 | if ((file = fopen(path, "wb"))) {
776 | if ((dumpWriter = WJWOpenFILEDocument(TRUE, file))) {
777 | WJEWriteDocument(document, dumpWriter, NULL);
778 | WJWCloseDocument(dumpWriter);
779 | }
780 | fprintf(file, "\n");
781 | fclose(file);
782 |
783 | printf("Dumped JSON document to %s\n", path);
784 | }
785 | }
786 |
787 | static size_t fileReaderCB( char *data, size_t length, size_t seen, void *client )
788 | {
789 | DebugAssert(length);
790 | DebugAssert(data);
791 | if(!data) {
792 | return 0;
793 | }
794 | return fread(data, 1, length, client);
795 | }
796 |
797 | EXPORT WJElement WJEReadFILE(FILE* fd)
798 | {
799 | WJReader reader;
800 | WJElement obj = NULL;
801 |
802 | if ((reader = WJROpenDocument(fileReaderCB, fd, NULL, 0))) {
803 | obj = WJEOpenDocument(reader, NULL, NULL, NULL);
804 | WJRCloseDocument(reader);
805 | }
806 | return obj;
807 | }
808 |
809 | EXPORT void WJEMemFree(void *mem)
810 | {
811 | if(mem != NULL) {
812 | MemFree(mem);
813 | }
814 | }
815 | EXPORT void WJEMemRelease(void **mem)
816 | {
817 | if(mem != NULL && *mem != NULL) {
818 | MemRelease(mem);
819 | }
820 | }
821 |
--------------------------------------------------------------------------------
/src/wjelement/element.h:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of WJElement.
3 |
4 | WJElement is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU Lesser General Public License as published
6 | by the Free Software Foundation.
7 |
8 | WJElement is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | GNU Lesser General Public License for more details.
12 |
13 | You should have received a copy of the GNU Lesser General Public License
14 | along with WJElement. If not, see .
15 | */
16 |
17 |
18 | #ifndef __WJ_ELEMENT_P_H
19 | #define __WJ_ELEMENT_P_H
20 |
21 | #include
22 | #include
23 |
24 | #include
25 | #include
26 | #include
27 |
28 | #include
29 |
30 | typedef struct {
31 | WJElementPublic pub;
32 | WJElementPublic *parent;
33 |
34 | union {
35 | char *string;
36 | XplBool boolean;
37 |
38 | struct {
39 | uint64 i;
40 | double d;
41 |
42 | XplBool hasDecimalPoint;
43 | XplBool negative;
44 | } number;
45 | } value;
46 |
47 | char _name[];
48 | } _WJElement;
49 |
50 | /* element.c */
51 | _WJElement * _WJENew(_WJElement *parent, char *name, size_t len, const char *file, int line);
52 | _WJElement * _WJEReset(_WJElement *e, WJRType type);
53 |
54 | /* search.c */
55 | typedef int (* WJEMatchCB)(WJElement root, WJElement parent, WJElement e, WJEAction action, char *name, size_t len);
56 | WJElement WJESearch(WJElement container, const char *path, WJEAction *action, WJElement last, const char *file, const int line);
57 |
58 | /*
59 | Allow a few extra characters in dot seperated alpha numeric names for the
60 | sake of backwards compatability.
61 | */
62 | #define isalnumx(c) ( \
63 | isalnum((c)) || \
64 | ' ' == (c) || \
65 | '_' == (c) || \
66 | '-' == (c) \
67 | )
68 |
69 |
70 | /* The upper bits of the action may contain modifier flags */
71 | #define WJE_ACTION_MASK 0x0000ffff
72 |
73 | /*
74 | Helpers to deal with string comparisons taking into account the
75 | WJE_IGNORE_CASE flag.
76 | */
77 | #define wstrcmp(a, b, actionval) (((actionval) & WJE_IGNORE_CASE) ? \
78 | stricmp((a), (b)) : \
79 | strcmp((a), (b)))
80 |
81 | #define wstrncmp(a, b, l, actionval) (((actionval) & WJE_IGNORE_CASE) ? \
82 | strnicmp((a), (b), (l)) : \
83 | strncmp((a), (b), (l)))
84 |
85 |
86 | /*
87 | The following macros allow using many internal functions against a public
88 | WJElement or a private _WJElement * without needing to cast the arguments or
89 | result.
90 |
91 | A function with a leading _ indicates that it uses the private versions, and
92 | without uses the public.
93 |
94 | This convention is not used on exported functions, which use a leading _ to
95 | indicate an advanced version of the function.
96 | */
97 | #define WJENew(p, n, l, f, ln) (WJElement) _WJENew((_WJElement *) (p), (n), (l), (f), (ln))
98 | #define WJEReset(e, t) (WJElement) _WJEReset((_WJElement *) (e), t)
99 | #define _WJESearch(c, p, a, l, f, ln) (_WJElement *) WJESearch((c), (p), (a), (l), (f), (ln))
100 |
101 | void WJEChanged(WJElement element);
102 | #define _WJEChanged(e) WJEChanged((WJElement) (e))
103 |
104 | #endif // __WJ_ELEMENT_P_H
105 |
--------------------------------------------------------------------------------
/src/wjelement/hash.c:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of WJElement.
3 |
4 | WJElement is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU Lesser General Public License as published
6 | by the Free Software Foundation.
7 |
8 | WJElement is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | GNU Lesser General Public License for more details.
12 |
13 | You should have received a copy of the GNU Lesser General Public License
14 | along with WJElement. If not, see .
15 | */
16 |
17 |
18 | #include "element.h"
19 | #include
20 |
21 | static void _WJEHash(WJElement document, int depth, WJEHashCB update, void *context)
22 | {
23 | WJElement child;
24 | char *s;
25 | int32 n;
26 | uint32 b;
27 |
28 | if (!document) {
29 | return;
30 | }
31 |
32 | switch (document->type) {
33 | default:
34 | case WJR_TYPE_UNKNOWN:
35 | break;
36 |
37 | case WJR_TYPE_NULL:
38 | update(context, "", 1);
39 | break;
40 |
41 | case WJR_TYPE_OBJECT:
42 | update(context, &depth, sizeof(depth));
43 |
44 | for (child = document->child; child; child = child->next) {
45 | if (child->name) {
46 | update(context, child->name, strlen(child->name) + 1);
47 | }
48 | _WJEHash(child, depth + 1, update, context);
49 | }
50 |
51 | update(context, &depth, sizeof(depth));
52 | break;
53 |
54 | case WJR_TYPE_ARRAY:
55 | update(context, &depth, sizeof(depth));
56 |
57 | for (child = document->child; child; child = child->next) {
58 | update(context, "", 1);
59 | _WJEHash(child, depth + 1, update, context);
60 | }
61 |
62 | update(context, &depth, sizeof(depth));
63 | break;
64 |
65 | case WJR_TYPE_STRING:
66 | if ((s = WJEString(document, NULL, WJE_GET, ""))) {
67 | update(context, s, strlen(s) + 1);
68 | }
69 |
70 | break;
71 |
72 | case WJR_TYPE_NUMBER:
73 | #ifdef WJE_DISTINGUISH_INTEGER_TYPE
74 | case WJR_TYPE_INTEGER:
75 | #endif
76 | n = WJENumber(document, NULL, WJE_GET, 0);
77 | update(context, &n, sizeof(n));
78 |
79 | break;
80 |
81 | case WJR_TYPE_TRUE:
82 | case WJR_TYPE_BOOL:
83 | case WJR_TYPE_FALSE:
84 | b = WJEBool(document, NULL, WJE_GET, FALSE);
85 | update(context, &b, sizeof(b));
86 | break;
87 | }
88 | }
89 |
90 | EXPORT void WJEHash(WJElement document, WJEHashCB update, void *context)
91 | {
92 | _WJEHash(document, 0, update, context);
93 | }
94 |
95 |
96 |
--------------------------------------------------------------------------------
/src/wjreader/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(wjreader
2 | wjreader.c
3 | )
4 |
5 | target_link_libraries(wjreader
6 | xpl
7 | ${ALL_LIBS}
8 | )
9 |
10 | SET_TARGET_PROPERTIES(
11 | wjreader
12 | PROPERTIES
13 | VERSION 1.0.0
14 | SOVERSION 1
15 | INSTALL_NAME_DIR "${LIB_DEST_DIR}"
16 | )
17 |
18 | install(TARGETS wjreader
19 | LIBRARY DESTINATION ${LIB_DEST_DIR}
20 | ARCHIVE DESTINATION ${LIB_DEST_DIR}
21 | )
22 |
--------------------------------------------------------------------------------
/src/wjwriter/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(wjwriter
2 | wjwriter.c
3 | )
4 |
5 | target_link_libraries(wjwriter
6 | xpl
7 | ${ALL_LIBS}
8 | )
9 | #link_directories($(MPLUS_GUARDIAN_BINARY_DIR)/src/libs/wjwriter)
10 |
11 | SET_TARGET_PROPERTIES(
12 | wjwriter
13 | PROPERTIES
14 | VERSION 1.0.0
15 | SOVERSION 1
16 | INSTALL_NAME_DIR "${LIB_DEST_DIR}"
17 | )
18 |
19 |
20 | install(TARGETS wjwriter
21 | LIBRARY DESTINATION ${LIB_DEST_DIR}
22 | ARCHIVE DESTINATION ${LIB_DEST_DIR}
23 | )
24 |
--------------------------------------------------------------------------------
/src/wjwriter/wjwriter.c:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of WJElement.
3 |
4 | WJElement is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU Lesser General Public License as published
6 | by the Free Software Foundation.
7 |
8 | WJElement is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | GNU Lesser General Public License for more details.
12 |
13 | You should have received a copy of the GNU Lesser General Public License
14 | along with WJElement. If not, see .
15 | */
16 |
17 |
18 | #include
19 | #include
20 |
21 | #include
22 | #include
23 | #include
24 |
25 | #include
26 |
27 | #if defined(_MSC_VER)
28 | #include
29 | typedef SSIZE_T ssize_t;
30 | #endif
31 |
32 | /*
33 | JSON syntax (http://www.json.org/)
34 | ==================================
35 | object
36 | {}
37 | { members }
38 |
39 | members
40 | pair
41 | pair , members
42 |
43 | pair
44 | string : value
45 |
46 | array
47 | []
48 | [ elements ]
49 |
50 | elements
51 | value
52 | value , elements
53 |
54 | value
55 | string
56 | number
57 | object
58 | array
59 | true
60 | false
61 | null
62 |
63 | string
64 | ""
65 | "chars"
66 |
67 | chars
68 | char
69 | char chars
70 |
71 | char
72 | any-Unicode-character- except-"-or-\-or- control-character
73 | \"
74 | \\
75 | \/
76 | \b
77 | \f
78 | \n
79 | \r
80 | \t
81 | \u four-hex-digits
82 |
83 | number
84 | int
85 | int frac
86 | int exp
87 | int frac exp
88 |
89 | int
90 | digit
91 | digit1-9 digits
92 | - digit
93 | - digit1-9 digits
94 |
95 | frac
96 | . digits
97 |
98 | exp
99 | e digits
100 | digits
101 | digit
102 | digit digits
103 |
104 | e
105 | e
106 | e+
107 | e-
108 | E
109 | E+
110 | E-
111 |
112 | Complex JSON Document Example
113 | =============================
114 |
115 | A complex JSON object:
116 |
117 | {
118 | "string1" : "This is a string",
119 | "bool1" : true,
120 | "bool2" : false,
121 | "object1" : {
122 | "string2" : "This is another string",
123 | "array1" : [
124 | "one",
125 | false,
126 | 92,
127 | "two"
128 | ],
129 | "array2" : [
130 | array3 : [ 1, 2, 3 ]
131 | ],
132 | "array4" : [ ]
133 | },
134 | "tricky":"foo","ugg":{"1":1,"2":2,"3:"3,"four":[1,2,3,4]}
135 | }
136 | */
137 |
138 | typedef struct {
139 | WJWriterPublic public;
140 |
141 | /*
142 | If the document, or an object, or an array have just been opened then
143 | the next value should not have a comma. All other values must be
144 | preceded with a comma.
145 | */
146 | XplBool skipcomma;
147 | XplBool skipbreak;
148 | XplBool instring;
149 | int depth;
150 |
151 | size_t size;
152 | size_t used;
153 | char buffer[1];
154 | } WJIWriter;
155 |
156 | static int WJWrite(WJIWriter *doc, char *data, size_t length)
157 | {
158 | size_t result = 0;
159 | size_t size;
160 | int offset;
161 |
162 | if (doc && !doc->size) {
163 | if (doc->public.write.cb) {
164 | return(doc->public.write.cb(data, length, doc->public.write.data));
165 | } else {
166 | return(0);
167 | }
168 | }
169 |
170 | if (!doc || !doc->public.write.cb) {
171 | return(0);
172 | }
173 |
174 | DebugAssert(doc->used <= doc->size);
175 | while (length) {
176 | /* Fill our buffer as much as possible */
177 | if (doc->used < doc->size) {
178 | size = xpl_min(length, doc->size - doc->used);
179 |
180 | memcpy(doc->buffer + doc->used, data, size);
181 | result += size;
182 | doc->used += size;
183 | data += size;
184 | length -= size;
185 | }
186 |
187 | /* Flush the buffer to make room for any data that didn't fit */
188 | offset = 0;
189 | while (offset < doc->used &&
190 | ((doc->size - doc->used) + offset) < length
191 | ) {
192 | size = doc->public.write.cb(doc->buffer + offset, doc->used - offset,
193 | doc->public.write.data);
194 | DebugAssert((signed int) size >= 0 && size <= doc->used - offset);
195 | offset += size;
196 |
197 | if (!size) {
198 | /* The callback failed */
199 | doc->public.write.cb = NULL;
200 | doc->used -= offset;
201 |
202 | DebugAssert(doc->used <= doc->size);
203 | return(result);
204 | }
205 | }
206 | doc->used -= offset;
207 |
208 | /* Reallign to the begining of the buffer */
209 | if (doc->used) {
210 | memmove(doc->buffer, doc->buffer + offset, doc->used);
211 | }
212 |
213 | /*
214 | If the data still won't fit in the buffer after flushing as much as
215 | possible then attempt to send it directly.
216 | */
217 | if (length > (doc->size - doc->used)) {
218 | size = doc->public.write.cb(data, length, doc->public.write.data);
219 | DebugAssert((signed int) size >= 0 && size <= length);
220 |
221 | result += size;
222 | data += size;
223 | length -= size;
224 | }
225 | }
226 |
227 | DebugAssert(doc->used <= doc->size);
228 | doc->buffer[doc->used] = '\0';
229 | return(result);
230 | }
231 |
232 | EXPORT WJWriter _WJWOpenDocument(XplBool pretty, WJWriteCallback callback, void *writedata, size_t buffersize)
233 | {
234 | WJIWriter *doc = NULL;
235 | size_t size = buffersize;
236 |
237 | if (!callback) {
238 | errno = EINVAL;
239 | return(NULL);
240 | }
241 |
242 | if (size < sizeof(WJIWriter)) {
243 | size = sizeof(WJIWriter);
244 | }
245 |
246 | doc = MemMallocEx(NULL, size, &size, TRUE, FALSE);
247 | memset(doc, 0, sizeof(WJIWriter));
248 |
249 | doc->public.write.cb = callback;
250 | doc->public.write.data = writedata;
251 | if (buffersize != 0) {
252 | /*
253 | Use the avaliable size returned by MemMallocEx so that the write
254 | buffer is at least buffersize, but may be larger.
255 | */
256 | doc->size = size - sizeof(WJIWriter);
257 | } else {
258 | /*
259 | Specifying a buffer size of 0 disables WJWriter buffering entirely
260 | which is important when using a callback that writes to a buffered
261 | interface to prevent double buffering.
262 | */
263 | doc->size = 0;
264 | }
265 |
266 | /*
267 | The first value after opening a document should not be preceded by a
268 | comma. skipcomma will be reset after reading that first value.
269 | */
270 | doc->public.pretty = pretty;
271 | doc->public.escapeInvalidChars = TRUE;
272 | doc->public.base = 10;
273 | doc->skipcomma = TRUE;
274 | doc->skipbreak = TRUE;
275 |
276 | return((WJWriter) doc);
277 | }
278 |
279 | EXPORT XplBool WJWCloseDocument(WJWriter indoc)
280 | {
281 | WJIWriter *doc = (WJIWriter *)indoc;
282 | XplBool result = FALSE;
283 |
284 | if (doc) {
285 | if (doc->size) {
286 | size_t size;
287 | size_t offset;
288 |
289 | DebugAssert(doc->used <= doc->size);
290 |
291 | /* Write any remaining buffered data */
292 | offset = 0;
293 | while (doc->public.write.cb && offset < doc->used) {
294 | size = doc->public.write.cb(doc->buffer + offset, doc->used - offset,
295 | doc->public.write.data);
296 | DebugAssert((signed int) size >= 0 && size <= doc->used - offset);
297 | offset += size;
298 |
299 | if (!size) {
300 | /* The callback failed */
301 | doc->public.write.cb = NULL;
302 | doc->used -= offset;
303 | break;
304 | }
305 | }
306 | doc->used -= offset;
307 | DebugAssert(doc->used <= doc->size);
308 | }
309 |
310 | if (doc->public.user.freecb) {
311 | doc->public.user.freecb(doc->public.user.data);
312 | }
313 |
314 | if (doc->public.write.cb) {
315 | /* If the callback is still set then there where no errors */
316 | result = TRUE;
317 | }
318 |
319 | MemFree(doc);
320 | }
321 |
322 | return(result);
323 | }
324 |
325 | /*
326 | Helper Open Functions
327 |
328 | Open a JSON document using pre-canned callback functions which can read from
329 | common data sources, such as an open file, or an open connection.
330 | */
331 | EXPORT size_t WJWFileCallback(char *buffer, size_t length, void *data)
332 | {
333 | if (data) {
334 | return((int)fwrite(buffer, sizeof(char), length, (FILE *)data));
335 | }
336 |
337 | return(0);
338 | }
339 |
340 | static size_t WJWMemCallback(char *buffer, size_t length, void *data)
341 | {
342 | char **mem = data;
343 | size_t l;
344 |
345 | if (mem) {
346 | if (!*mem) {
347 | *mem = MemMallocWait(length + 1);
348 | memcpy(*mem, buffer, length);
349 | (*mem)[length] = '\0';
350 | } else {
351 | l = strlen(*mem);
352 |
353 | *mem = MemReallocWait(*mem, l + length + 1);
354 | memcpy(*mem + l, buffer, length);
355 | (*mem)[l + length] = '\0';
356 | }
357 |
358 | return(length);
359 | }
360 |
361 | return(0);
362 | }
363 |
364 | EXPORT WJWriter WJWOpenMemDocument(XplBool pretty, char **mem)
365 | {
366 | if (!mem) {
367 | return(NULL);
368 | }
369 |
370 | return(_WJWOpenDocument(pretty, WJWMemCallback, mem, 0));
371 | }
372 |
373 | /*
374 | Verify that str points to a valid UTF8 character, and return the length of
375 | that character in bytes. If the value is not a full valid UTF8 character
376 | then -1 will be returned.
377 | */
378 | static ssize_t WJWUTF8CharSize(char *str, size_t length)
379 | {
380 | ssize_t r = -1;
381 | unsigned char test;
382 | int i;
383 |
384 | if (!str || !length) {
385 | return(0);
386 | }
387 |
388 | test = (unsigned char) *str;
389 |
390 | if (!(test & 0x80)) {
391 | /* ASCII */
392 | return(1);
393 | } else if ((test & 0xC0) == 0x80) {
394 | /*
395 | This is not a primary octet of a UTF8 char, but is valid as any
396 | other octet of the UTf8 char.
397 | */
398 | return(-1);
399 | } else if ((test & 0xE0) == 0xC0) {
400 | if (test == 0xC0 || test == 0xC1) {
401 | /* redundant code point. */
402 | return(-1);
403 | }
404 | r = 2;
405 | } else if ((test & 0xF0) == 0xE0) {
406 | r = 3;
407 | } else if ((test & 0xF8) == 0xF0) {
408 | if ((test & 0x07) >= 0x05) {
409 | /* undefined character range. RFC 3629 */
410 | return(-1);
411 | }
412 |
413 | r = 4;
414 | } else {
415 | /*
416 | Originally there was room for (more 4,) 5 and 6 byte characters but
417 | these where outlawed by RFC 3629.
418 | */
419 | return(-1);
420 | }
421 |
422 | if (r > length) {
423 | r = length;
424 | }
425 |
426 | for (i = 1; i < r; i++) {
427 | if (((unsigned char)(str[i]) & 0xC0) != 0x80) {
428 | /* This value is not valid as a non-primary UTF8 octet */
429 | return(-1);
430 | }
431 | }
432 |
433 | return(r);
434 | }
435 |
436 | static XplBool WJWriteString(char *value, size_t length, XplBool done, WJIWriter *doc)
437 | {
438 | char *v;
439 | char *e;
440 | ssize_t l;
441 | char esc[3];
442 |
443 | if (!doc || !doc->public.write.cb) {
444 | return(FALSE);
445 | }
446 |
447 | if (!doc->instring) {
448 | WJWrite(doc, "\"", 1);
449 | }
450 |
451 | *(esc + 0) = '\\';
452 | *(esc + 1) = '\0';
453 | *(esc + 2) = '\0';
454 |
455 | for (v = e = value; e < value + length; e++) {
456 | switch (*e) {
457 | case '\\': *(esc + 1) = '\\'; break;
458 | case '"': *(esc + 1) = '"'; break;
459 | case '\n': *(esc + 1) = 'n'; break;
460 | case '\b': *(esc + 1) = 'b'; break;
461 | case '\t': *(esc + 1) = 't'; break;
462 | case '\f': *(esc + 1) = 'f'; break;
463 | case '\r': *(esc + 1) = 'r'; break;
464 |
465 | default:
466 | l = WJWUTF8CharSize(e, length - (e - value));
467 | if (l > 1 || (l == 1 && *e >= '\x20')) {
468 | /*
469 | *e is the primary octect of a multi-octet UTF8
470 | character. The remaining characters have been verified
471 | as valid UTF8 and can be skipped.
472 | */
473 | e += (l - 1);
474 | } else if (l == 1) {
475 | /*
476 | *e is valid UTF8 but is not a printable character, and
477 | will be escaped before being sent, using the
478 | JSON-standard "\u00xx" form
479 | */
480 | char unicodeHex[sizeof("\\u0000")];
481 |
482 | WJWrite(doc, v, e - v);
483 |
484 | sprintf(unicodeHex, "\\u00%02x", (unsigned char) *e);
485 | WJWrite(doc, unicodeHex, sizeof(unicodeHex)-1);
486 |
487 | v = e + 1;
488 | } else if (l < 0) {
489 | /*
490 | *e is not valid UTF8 data, and must be escaped before
491 | being sent. But JSON-standard does not give us a
492 | mechanism so we chose "\xhh" format because of its
493 | almost universal comprehension.
494 | */
495 | char nonUnicodeHex[sizeof("\\x00")];
496 |
497 | WJWrite(doc, v, e - v);
498 |
499 | if (doc->public.escapeInvalidChars) {
500 | sprintf(nonUnicodeHex, "\\x%02x", (unsigned char) *e);
501 | WJWrite(doc, nonUnicodeHex, sizeof(nonUnicodeHex)-1);
502 | }
503 |
504 | v = e + 1;
505 | }
506 | continue;
507 | }
508 |
509 | WJWrite(doc, v, e - v);
510 | v = e + 1;
511 |
512 | WJWrite(doc, esc, 2);
513 | continue;
514 | }
515 |
516 | WJWrite(doc, v, length - (v - value));
517 |
518 | if (done) {
519 | WJWrite(doc, "\"", 1);
520 | }
521 | doc->instring = !done;
522 | return(TRUE);
523 | }
524 |
525 | EXPORT XplBool WJWOpenArray(char *name, WJWriter indoc)
526 | {
527 | WJIWriter *doc = (WJIWriter *)indoc;
528 |
529 | if (doc && doc->public.write.cb) {
530 | if (doc->public.pretty) {
531 | int i;
532 |
533 | if (!doc->skipcomma) {
534 | WJWrite(doc, ",", 1);
535 | }
536 |
537 | if (!doc->skipbreak) {
538 | WJWrite(doc, "\n", 1);
539 | }
540 | doc->skipbreak = FALSE;
541 |
542 | for (i = 0; i < doc->depth; i++) {
543 | WJWrite(doc, "\t", 1);
544 | }
545 | } else if (!doc->skipcomma) {
546 | WJWrite(doc, ",", 1);
547 | }
548 |
549 | doc->depth++;
550 |
551 | if (name) {
552 | WJWriteString(name, strlen(name), TRUE, doc);
553 | WJWrite(doc, ":", 1);
554 | }
555 |
556 | doc->skipcomma = TRUE;
557 | return(1 == WJWrite(doc, "[", 1));
558 | }
559 |
560 | return(FALSE);
561 | }
562 |
563 | EXPORT XplBool WJWCloseArray(WJWriter indoc)
564 | {
565 | WJIWriter *doc = (WJIWriter *)indoc;
566 |
567 | if (doc && doc->public.write.cb) {
568 | if (doc->depth > 0) {
569 | doc->depth--;
570 | }
571 |
572 | if (doc->public.pretty) {
573 | int i;
574 |
575 | WJWrite(doc, "\n", 1);
576 | for (i = 0; i < doc->depth; i++) {
577 | WJWrite(doc, "\t", 1);
578 | }
579 | }
580 |
581 | doc->skipcomma = FALSE;
582 | return(1 == WJWrite(doc, "]", 1));
583 | }
584 |
585 | return(FALSE);
586 | }
587 |
588 | EXPORT XplBool WJWOpenObject(char *name, WJWriter indoc)
589 | {
590 | WJIWriter *doc = (WJIWriter *)indoc;
591 |
592 | if (doc && doc->public.write.cb) {
593 | if (doc->public.pretty) {
594 | int i;
595 |
596 | if (!doc->skipcomma) {
597 | WJWrite(doc, ",", 1);
598 | }
599 |
600 | if (!doc->skipbreak) {
601 | WJWrite(doc, "\n", 1);
602 | }
603 | doc->skipbreak = FALSE;
604 | for (i = 0; i < doc->depth; i++) {
605 | WJWrite(doc, "\t", 1);
606 | }
607 | } else if (!doc->skipcomma) {
608 | WJWrite(doc, ",", 1);
609 | }
610 |
611 | doc->depth++;
612 |
613 | if (name) {
614 | WJWriteString(name, strlen(name), TRUE, doc);
615 | WJWrite(doc, ":", 1);
616 | }
617 |
618 | doc->skipcomma = TRUE;
619 | return(1 == WJWrite(doc, "{", 1));
620 | }
621 |
622 | return(FALSE);
623 | }
624 |
625 | EXPORT XplBool WJWCloseObject(WJWriter indoc)
626 | {
627 | WJIWriter *doc = (WJIWriter *)indoc;
628 |
629 | if (doc && doc->public.write.cb) {
630 | if (doc->depth > 0) {
631 | doc->depth--;
632 | }
633 |
634 | if (doc->public.pretty) {
635 | int i;
636 |
637 | WJWrite(doc, "\n", 1);
638 | for (i = 0; i < doc->depth; i++) {
639 | WJWrite(doc, "\t", 1);
640 | }
641 | }
642 |
643 | doc->skipcomma = FALSE;
644 | return(1 == WJWrite(doc, "}", 1));
645 | }
646 |
647 | return(FALSE);
648 | }
649 |
650 | EXPORT XplBool WJWStringN(char *name, char *value, size_t length, XplBool done, WJWriter indoc)
651 | {
652 | WJIWriter *doc = (WJIWriter *)indoc;
653 |
654 | if (doc && doc->public.write.cb && value) {
655 | if (!doc->instring) {
656 | if (doc->public.pretty) {
657 | int i;
658 |
659 | if (!doc->skipcomma) {
660 | WJWrite(doc, ",", 1);
661 | }
662 |
663 | WJWrite(doc, "\n", 1);
664 | for (i = 0; i < doc->depth; i++) {
665 | WJWrite(doc, "\t", 1);
666 | }
667 | } else if (!doc->skipcomma) {
668 | WJWrite(doc, ",", 1);
669 | }
670 | doc->skipcomma = FALSE;
671 |
672 | if (name) {
673 | WJWriteString(name, strlen(name), TRUE, doc);
674 | WJWrite(doc, ":", 1);
675 | }
676 | }
677 |
678 | return(WJWriteString(value, length, done, doc));
679 | }
680 |
681 | return(FALSE);
682 | }
683 |
684 | EXPORT XplBool WJWString(char *name, char *value, XplBool done, WJWriter doc)
685 | {
686 | if (value) {
687 | return(WJWStringN(name, value, strlen(value), done, doc));
688 | } else {
689 | return(WJWStringN(name, "", 0, done, doc));
690 | }
691 | }
692 |
693 | static XplBool WJWNumber(char *name, char *value, size_t size, WJWriter indoc)
694 | {
695 | WJIWriter *doc = (WJIWriter *)indoc;
696 |
697 | if (doc && doc->public.write.cb) {
698 | if (doc->public.pretty) {
699 | int i;
700 |
701 | if (!doc->skipcomma) {
702 | WJWrite(doc, ",", 1);
703 | }
704 |
705 | WJWrite(doc, "\n", 1);
706 | for (i = 0; i < doc->depth; i++) {
707 | WJWrite(doc, "\t", 1);
708 | }
709 | } else if (!doc->skipcomma) {
710 | WJWrite(doc, ",", 1);
711 | }
712 |
713 | doc->skipcomma = FALSE;
714 |
715 | if (name) {
716 | WJWriteString(name, strlen(name), TRUE, doc);
717 | WJWrite(doc, ":", 1);
718 | }
719 |
720 | if (size > 0) {
721 | size -= WJWrite(doc, value, size);
722 | return(size == 0);
723 | }
724 | }
725 |
726 | return(FALSE);
727 | }
728 |
729 | EXPORT XplBool WJWInt32(char *name, int32 value, WJWriter doc)
730 | {
731 | char v[256];
732 | size_t s;
733 |
734 | switch (doc->base) {
735 | default:
736 | case 10:
737 | s = strprintf(v, sizeof(v), NULL, "%ld", (long) value);
738 | break;
739 | case 16:
740 | s = strprintf(v, sizeof(v), NULL, "0x%08lx", (long) value);
741 | break;
742 |
743 | case 8:
744 | s = strprintf(v, sizeof(v), NULL, "0%lo", (long) value);
745 | break;
746 | }
747 |
748 | return(WJWNumber(name, v, s, doc));
749 | }
750 |
751 | EXPORT XplBool WJWUInt32(char *name, uint32 value, WJWriter doc)
752 | {
753 | char v[256];
754 | size_t s;
755 |
756 | switch (doc->base) {
757 | default:
758 | case 10:
759 | s = strprintf(v, sizeof(v), NULL, "%lu", (unsigned long) value);
760 | break;
761 |
762 | case 16:
763 | s = strprintf(v, sizeof(v), NULL, "0x%08lx", (unsigned long) value);
764 | break;
765 |
766 | case 8:
767 | s = strprintf(v, sizeof(v), NULL, "0%lo", (unsigned long) value);
768 | break;
769 | }
770 |
771 | return(WJWNumber(name, v, s, doc));
772 | }
773 |
774 | EXPORT XplBool WJWInt64(char *name, int64 value, WJWriter doc)
775 | {
776 | char v[256];
777 | size_t s;
778 |
779 | switch (doc->base) {
780 | default:
781 | case 10:
782 | s = strprintf(v, sizeof(v), NULL, "%lld", (long long) value);
783 | break;
784 |
785 | case 16:
786 | s = strprintf(v, sizeof(v), NULL, "0x%016llx", (long long) value);
787 | break;
788 |
789 | case 8:
790 | s = strprintf(v, sizeof(v), NULL, "0%llo", (long long) value);
791 | break;
792 | }
793 |
794 | return(WJWNumber(name, v, s, doc));
795 | }
796 |
797 | EXPORT XplBool WJWUInt64(char *name, uint64 value, WJWriter doc)
798 | {
799 | char v[256];
800 | size_t s;
801 |
802 | switch (doc->base) {
803 | default:
804 | case 10:
805 | s = strprintf(v, sizeof(v), NULL, "%llu", (unsigned long long) value);
806 | break;
807 |
808 | case 16:
809 | s = strprintf(v, sizeof(v), NULL, "0x%016llx", (unsigned long long) value);
810 | break;
811 |
812 | case 8:
813 | s = strprintf(v, sizeof(v), NULL, "0%llo", (unsigned long long) value);
814 | break;
815 | }
816 |
817 | return(WJWNumber(name, v, s, doc));
818 | }
819 |
820 | EXPORT XplBool WJWDouble(char *name, double value, WJWriter doc)
821 | {
822 | char v[256];
823 | size_t s;
824 |
825 | s = strprintf(v, sizeof(v), NULL, "%e", value);
826 | return(WJWNumber(name, v, s, doc));
827 | }
828 |
829 | EXPORT XplBool WJWBoolean(char *name, XplBool value, WJWriter indoc)
830 | {
831 | WJIWriter *doc = (WJIWriter *)indoc;
832 |
833 | if (doc && doc->public.write.cb) {
834 | if (doc->public.pretty) {
835 | int i;
836 |
837 | if (!doc->skipcomma) {
838 | WJWrite(doc, ",", 1);
839 | }
840 |
841 | WJWrite(doc, "\n", 1);
842 | for (i = 0; i < doc->depth; i++) {
843 | WJWrite(doc, "\t", 1);
844 | }
845 | } else if (!doc->skipcomma) {
846 | WJWrite(doc, ",", 1);
847 | }
848 |
849 |
850 | doc->skipcomma = FALSE;
851 |
852 | if (name) {
853 | WJWriteString(name, strlen(name), TRUE, doc);
854 | WJWrite(doc, ":", 1);
855 | }
856 |
857 | if (value) {
858 | WJWrite(doc, "true", 4);
859 | } else {
860 | WJWrite(doc, "false", 5);
861 | }
862 |
863 | return(TRUE);
864 | }
865 |
866 | return(FALSE);
867 | }
868 |
869 | EXPORT XplBool WJWNull(char *name, WJWriter indoc)
870 | {
871 | WJIWriter *doc = (WJIWriter *)indoc;
872 |
873 | if (doc && doc->public.write.cb) {
874 | if (doc->public.pretty) {
875 | int i;
876 |
877 | if (!doc->skipcomma) {
878 | WJWrite(doc, ",", 1);
879 | }
880 |
881 | WJWrite(doc, "\n", 1);
882 | for (i = 0; i < doc->depth; i++) {
883 | WJWrite(doc, "\t", 1);
884 | }
885 | } else if (!doc->skipcomma) {
886 | WJWrite(doc, ",", 1);
887 | }
888 |
889 | doc->skipcomma = FALSE;
890 |
891 | if (name) {
892 | WJWriteString(name, strlen(name), TRUE, doc);
893 | return(6 == WJWrite(doc, ":null", 5));
894 | } else {
895 | return(4 == WJWrite(doc, "null", 4));
896 | }
897 | }
898 |
899 | return(FALSE);
900 | }
901 |
902 | EXPORT XplBool WJWRawValue(char *name, char *value, XplBool done, WJWriter indoc)
903 | {
904 | WJIWriter *doc = (WJIWriter *)indoc;
905 |
906 | if (doc && doc->public.write.cb) {
907 | if (!doc->instring) {
908 | if (doc->public.pretty) {
909 | if (!doc->skipcomma) {
910 | WJWrite(doc, ",", 1);
911 | }
912 |
913 | WJWrite(doc, "\n", 1);
914 | /*
915 | Don't indent, because the raw value will not be indented to
916 | match. Its up to the caller to prettify their JSON
917 | */
918 | } else if (!doc->skipcomma) {
919 | WJWrite(doc, ",", 1);
920 | }
921 |
922 | doc->skipcomma = FALSE;
923 |
924 | if (name) {
925 | WJWriteString(name, strlen(name), TRUE, doc);
926 | WJWrite(doc, ":", 1);
927 | }
928 | }
929 |
930 | doc->instring = !done;
931 | return(strlen(value) == WJWrite(doc, value, strlen(value)));
932 | }
933 |
934 | return(FALSE);
935 | }
936 |
937 |
--------------------------------------------------------------------------------
/windows/wjelement-VC100.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 11.00
3 | # Visual C++ Express 2010
4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wjelement", "wjelement-VC100.vcxproj", "{978C7161-A255-4C13-8B2B-910F9A7FA557}"
5 | EndProject
6 | Global
7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
8 | Debug|Win32 = Debug|Win32
9 | Debug|x64 = Debug|x64
10 | Release|Win32 = Release|Win32
11 | Release|x64 = Release|x64
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {978C7161-A255-4C13-8B2B-910F9A7FA557}.Debug|Win32.ActiveCfg = Debug|Win32
15 | {978C7161-A255-4C13-8B2B-910F9A7FA557}.Debug|Win32.Build.0 = Debug|Win32
16 | {978C7161-A255-4C13-8B2B-910F9A7FA557}.Debug|x64.ActiveCfg = Debug|x64
17 | {978C7161-A255-4C13-8B2B-910F9A7FA557}.Debug|x64.Build.0 = Debug|x64
18 | {978C7161-A255-4C13-8B2B-910F9A7FA557}.Release|Win32.ActiveCfg = Release|Win32
19 | {978C7161-A255-4C13-8B2B-910F9A7FA557}.Release|Win32.Build.0 = Release|Win32
20 | {978C7161-A255-4C13-8B2B-910F9A7FA557}.Release|x64.ActiveCfg = Release|x64
21 | {978C7161-A255-4C13-8B2B-910F9A7FA557}.Release|x64.Build.0 = Release|x64
22 | EndGlobalSection
23 | GlobalSection(SolutionProperties) = preSolution
24 | HideSolutionNode = FALSE
25 | EndGlobalSection
26 | EndGlobal
27 |
--------------------------------------------------------------------------------
/windows/wjelement-VC100.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Debug
10 | x64
11 |
12 |
13 | Release
14 | Win32
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | wjelement
23 | {978C7161-A255-4C13-8B2B-910F9A7FA557}
24 | wjelement
25 | Win32Proj
26 |
27 |
28 |
29 | StaticLibrary
30 | NotSet
31 | true
32 |
33 |
34 | StaticLibrary
35 | NotSet
36 |
37 |
38 | StaticLibrary
39 | Windows7.1SDK
40 |
41 |
42 | StaticLibrary
43 | Windows7.1SDK
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | <_ProjectFileVersion>10.0.40219.1
63 | $(ProjDir)VC100\$(Platform)\$(Configuration)\
64 | $(OutDir)
65 | $(ProjDir)VC100\$(Platform)\$(Configuration)\
66 | $(OutDir)
67 | $(ProjectName)d
68 | $(ProjectName)d
69 | $(ProjDir)VC100\$(Platform)\$(Configuration)\
70 | $(OutDir)
71 | $(ProjDir)VC100\$(Platform)\$(Configuration)\
72 | $(OutDir)
73 |
74 |
75 |
76 | Disabled
77 | ../include;%(AdditionalIncludeDirectories)
78 | WIN32;_DEBUG;_LIB;WJE_DISTINGUISH_INTEGER_TYPE;%(PreprocessorDefinitions)
79 | true
80 | EnableFastChecks
81 | MultiThreadedDebugDLL
82 | NotUsing
83 | Level3
84 | EditAndContinue
85 | 4018;4244;4996;%(DisableSpecificWarnings)
86 |
87 |
88 | $(IntDir)$(TargetName)$(TargetExt)
89 |
90 |
91 |
92 |
93 | MaxSpeed
94 | true
95 | false
96 | ../include;%(AdditionalIncludeDirectories)
97 | WIN32;NDEBUG;_LIB;WJE_DISTINGUISH_INTEGER_TYPE;%(PreprocessorDefinitions)
98 | MultiThreadedDLL
99 | true
100 | NotUsing
101 | Level3
102 | ProgramDatabase
103 | 4018;4244;4996;%(DisableSpecificWarnings)
104 |
105 |
106 | $(IntDir)$(TargetName)$(TargetExt)
107 |
108 |
109 |
110 |
111 | WIN32;_DEBUG;_LIB;WJE_DISTINGUISH_INTEGER_TYPE;%(PreprocessorDefinitions)
112 | ../include;%(AdditionalIncludeDirectories)
113 | 4267;4018;4244;4996;%(DisableSpecificWarnings)
114 | Level3
115 | false
116 | Disabled
117 | true
118 | MultiThreadedDebugDLL
119 | EnableFastChecks
120 |
121 |
122 | $(IntDir)$(ProjectName)d.lib
123 |
124 |
125 | $(IntDir)$(TargetName)$(TargetExt)
126 |
127 |
128 |
129 |
130 | 4267;4018;4244;4996;%(DisableSpecificWarnings)
131 | ../include;%(AdditionalIncludeDirectories)
132 | WIN32;NDEBUG;_LIB;WJE_DISTINGUISH_INTEGER_TYPE;%(PreprocessorDefinitions)
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
--------------------------------------------------------------------------------
/windows/wjelement.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 10.00
3 | # Visual C++ Express 2008
4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wjelement", "wjelement.vcproj", "{978C7161-A255-4C13-8B2B-910F9A7FA557}"
5 | EndProject
6 | Global
7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
8 | Debug|Win32 = Debug|Win32
9 | Debug|x64 = Debug|x64
10 | Release|Win32 = Release|Win32
11 | Release|x64 = Release|x64
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {978C7161-A255-4C13-8B2B-910F9A7FA557}.Debug|Win32.ActiveCfg = Debug|Win32
15 | {978C7161-A255-4C13-8B2B-910F9A7FA557}.Debug|Win32.Build.0 = Debug|Win32
16 | {978C7161-A255-4C13-8B2B-910F9A7FA557}.Debug|x64.ActiveCfg = Debug|x64
17 | {978C7161-A255-4C13-8B2B-910F9A7FA557}.Debug|x64.Build.0 = Debug|x64
18 | {978C7161-A255-4C13-8B2B-910F9A7FA557}.Release|Win32.ActiveCfg = Release|Win32
19 | {978C7161-A255-4C13-8B2B-910F9A7FA557}.Release|Win32.Build.0 = Release|Win32
20 | {978C7161-A255-4C13-8B2B-910F9A7FA557}.Release|x64.ActiveCfg = Release|x64
21 | {978C7161-A255-4C13-8B2B-910F9A7FA557}.Release|x64.Build.0 = Release|x64
22 | EndGlobalSection
23 | GlobalSection(SolutionProperties) = preSolution
24 | HideSolutionNode = FALSE
25 | EndGlobalSection
26 | EndGlobal
27 |
--------------------------------------------------------------------------------
/windows/wjelement.vcproj:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
15 |
18 |
19 |
20 |
21 |
22 |
29 |
32 |
35 |
38 |
41 |
44 |
57 |
60 |
63 |
66 |
70 |
73 |
76 |
79 |
82 |
85 |
86 |
93 |
96 |
99 |
102 |
105 |
109 |
122 |
125 |
128 |
131 |
135 |
138 |
141 |
144 |
147 |
150 |
151 |
159 |
162 |
165 |
168 |
171 |
174 |
188 |
191 |
194 |
197 |
201 |
204 |
207 |
210 |
213 |
216 |
217 |
225 |
228 |
231 |
234 |
237 |
241 |
255 |
258 |
261 |
264 |
268 |
271 |
274 |
277 |
280 |
283 |
284 |
285 |
286 |
287 |
288 |
293 |
296 |
297 |
300 |
301 |
304 |
305 |
308 |
309 |
312 |
313 |
316 |
317 |
320 |
321 |
324 |
325 |
328 |
329 |
330 |
335 |
338 |
339 |
342 |
343 |
346 |
347 |
350 |
351 |
354 |
355 |
358 |
359 |
362 |
363 |
364 |
369 |
370 |
373 |
374 |
375 |
376 |
377 |
378 |
--------------------------------------------------------------------------------