diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..439df79 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,384 @@ +# Notes: +# +# Author: Paul Harris, June 2012 +# Additions: Joakim Soderberg, Febuary 2013 +# +# Supports: building static/shared, release/debug/etc, can also build html docs +# and some of the tests. +# Note that its designed for out-of-tree builds, so it will not pollute your +# source tree. +# +# TODO 1: Finish implementing tests. api tests are working, but the valgrind +# variants are not flagging problems. +# +# TODO 2: There is a check_exports script that would try and incorporate. +# +# TODO 3: Consolidate version numbers, currently the version number is written +# into: * cmake (here) * autotools (the configure) * source code header files. +# Should not be written directly into header files, autotools/cmake can do +# that job. +# +# Brief intro on how to use cmake: +# > mkdir build (somewhere - we do out-of-tree builds) +# > use cmake, ccmake, or cmake-gui to configure the project. for linux, you +# can only choose one variant: release,debug,etc... and static or shared. +# >> example: +# >> cd build +# >> ccmake -i ../path_to_jansson_dir +# >> inside, configure your options. press C until there are no lines +# with * next to them. +# >> note, I like to configure the 'install' path to ../install, so I get +# self-contained clean installs I can point other projects to. +# >> press G to 'generate' the project files. +# >> make (to build the project) +# >> make install +# >> make test (to run the tests, if you enabled them) +# +# Brief description on how it works: +# There is a small heirachy of CMakeLists.txt files which define how the +# project is built. +# Header file detection etc is done, and the results are written into config.h +# and jansson_config.h, which are generated from the corresponding +# config.h.cmake and jansson_config.h.cmake template files. +# The generated header files end up in the build directory - not in +# the source directory. +# The rest is down to the usual make process. + + + +cmake_minimum_required (VERSION 2.8) +# required for exports? cmake_minimum_required (VERSION 2.8.6) +project (jansson C) + +# Options +OPTION (BUILD_SHARED_LIBS "Build shared libraries." OFF) + +# Set some nicer output dirs. +SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) +SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) +SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) + +# This is how I thought it should go +# set (JANSSON_VERSION "2.3.1") +# set (JANSSON_SOVERSION 2) + +# This is what is required to match the same numbers as automake's +set (JANSSON_VERSION "4.3.1") +set (JANSSON_SOVERSION 4) + +# for CheckFunctionKeywords +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +include (CheckFunctionExists) +include (CheckFunctionKeywords) +include (CheckIncludeFiles) +include (CheckTypeSize) + +# Turn off Microsofts "security" warnings. +if (MSVC) + add_definitions( "/W3 /D_CRT_SECURE_NO_WARNINGS /wd4005 /wd4996 /nologo" ) +endif() + +# Check for the int-type includes +check_include_files (sys/types.h HAVE_SYS_TYPES_H) +check_include_files (inttypes.h HAVE_INTTYPES_H) +check_include_files (stdint.h HAVE_STDINT_H) + + +# Check our 64 bit integer sizes +check_type_size (__int64 __INT64) +check_type_size (int64_t INT64_T) +check_type_size ("long long" LONG_LONG_INT) + +# Check our 32 bit integer sizes +check_type_size (int32_t INT32_T) +check_type_size (__int32 __INT32) +check_type_size ("long" LONG_INT) +check_type_size ("int" INT) + +if (HAVE_INT32_T) + set (JSON_INT32 int32_t) +elseif (HAVE___INT32) + set (JSON_INT32 __int32) +elseif (HAVE_LONG AND (${LONG_INT} EQUAL 4)) + set (JSON_INT32 long) +elseif (HAVE_INT AND (${INT} EQUAL 4)) + set (JSON_INT32 int) +else () + message (FATAL_ERROR "Could not detect a valid 32 bit integer type") +endif () + +# Check for ssize_t and SSIZE_T existance. +check_type_size(ssize_t SSIZE_T) +check_type_size(SSIZE_T UPPERCASE_SSIZE_T) +if(NOT HAVE_SSIZE_T) + if(HAVE_UPPERCASE_SSIZE_T) + set(JSON_SSIZE SSIZE_T) + else() + set(JSON_SSIZE int) + endif() +endif() +set(CMAKE_EXTRA_INCLUDE_FILES "") + +# Check for all the variants of strtoll +check_function_exists (strtoll HAVE_STRTOLL) +check_function_exists (strtoq HAVE_STRTOQ) +check_function_exists (_strtoi64 HAVE__STRTOI64) + +# Figure out what variant we should use +if (HAVE_STRTOLL) + set (JSON_STRTOINT strtoll) +elseif (HAVE_STRTOQ) + set (JSON_STRTOINT strtoq) +elseif (HAVE__STRTOI64) + set (JSON_STRTOINT _strtoi64) +else () + # fallback to strtol (32 bit) + # this will set all the required variables + set (JSON_STRTOINT strtol) + set (JSON_INT_T long) + set (JSON_INTEGER_FORMAT "\"ld\"") +endif () + +# if we haven't defined JSON_INT_T, then we have a 64 bit conversion function. +# detect what to use for the 64 bit type. +# Note: I will prefer long long if I can get it, as that is what the automake system aimed for. +if (NOT DEFINED JSON_INT_T) + if (HAVE_LONG_LONG_INT AND (${LONG_LONG_INT} EQUAL 8)) + set (JSON_INT_T "long long") + elseif (HAVE_INT64_T) + set (JSON_INT_T int64_t) + elseif (HAVE___INT64) + set (JSON_INT_T __int64) + else () + message (FATAL_ERROR "Could not detect 64 bit type, although I detected the strtoll equivalent") + endif () + + # Apparently, Borland BCC and MSVC wants I64d, + # Borland BCC could also accept LD + # and gcc wants ldd, + # I am not sure what cygwin will want, so I will assume I64d + + if (WIN32) # matches both msvc and cygwin + set (JSON_INTEGER_FORMAT "\"I64d\"") + else () + set (JSON_INTEGER_FORMAT "\"lld\"") + endif () +endif () + + +# If locale.h and localeconv() are available, define to 1, otherwise to 0. +check_include_files (locale.h HAVE_LOCALE_H) +check_function_exists (localeconv HAVE_LOCALECONV) + +if (HAVE_LOCALECONV AND HAVE_LOCALE_H) + set (JSON_HAVE_LOCALECONV 1) +else () + set (JSON_HAVE_LOCALECONV 0) +endif () + + +# check if we have setlocale +check_function_exists (setlocale HAVE_SETLOCALE) + + +# Check what the inline keyword is. +# Note that the original JSON_INLINE was always set to just 'inline', so this goes further. +check_function_keywords("inline") +check_function_keywords("__inline") +check_function_keywords("__inline__") + +if (HAVE_INLINE) + set (JSON_INLINE inline) +elseif (HAVE___INLINE) + set (JSON_INLINE __inline) +elseif (HAVE___INLINE__) + set (JSON_INLINE __inline__) +else (HAVE_INLINE) + # no inline on this platform + set (JSON_INLINE) +endif (HAVE_INLINE) + +# Find our snprintf +check_function_exists (snprintf HAVE_SNPRINTF) +check_function_exists (_snprintf HAVE__SNPRINTF) + +if (HAVE_SNPRINTF) + set (JSON_SNPRINTF snprintf) +elseif (HAVE__SNPRINTF) + set (JSON_SNPRINTF _snprintf) +endif () + +# configure the public config file +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/cmake/jansson_config.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/include/jansson_config.h) + +# Copy the jansson.h file to the public include folder +file (COPY ${CMAKE_CURRENT_SOURCE_DIR}/src/jansson.h + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/include/) + + +# configure the private config file +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/cmake/config.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/private_include/config.h) + +# and tell the source code to include it +add_definitions (-DHAVE_CONFIG_H) + +include_directories (${CMAKE_CURRENT_BINARY_DIR}/include) +include_directories (${CMAKE_CURRENT_BINARY_DIR}/private_include) + +# Add the lib sources. +file (GLOB C_FILES src/*.c) + +if (BUILD_SHARED_LIBS) + + add_library (jansson SHARED ${C_FILES} src/jansson.def) + + set_target_properties (jansson PROPERTIES + VERSION ${JANSSON_VERSION} + SOVERSION ${JANSSON_SOVERSION}) + +else () + + add_library (jansson ${C_FILES}) + +endif () + +# LIBRARY for linux +# RUNTIME for windows (when building shared) +install (TARGETS jansson + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) + +install (FILES + ${CMAKE_CURRENT_BINARY_DIR}/include/jansson_config.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/jansson.h + DESTINATION include) + +# For building Documentation (uses Sphinx) +OPTION (BUILD_DOCS "Build documentation (uses python-sphinx)." OFF) +if (BUILD_DOCS) + find_package(Sphinx REQUIRED) + + # configured documentation tools and intermediate build results + set(BINARY_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/_build") + + # Sphinx cache with pickled ReST documents + set(SPHINX_CACHE_DIR "${CMAKE_CURRENT_BINARY_DIR}/_doctrees") + + # HTML output directory + set(SPHINX_HTML_DIR "${CMAKE_CURRENT_BINARY_DIR}/html") + + # CMake could be used to build the conf.py file too, + # eg it could automatically write the version of the program or change the theme. + # if(NOT DEFINED SPHINX_THEME) + # set(SPHINX_THEME default) + # endif() + # + # if(NOT DEFINED SPHINX_THEME_DIR) + # set(SPHINX_THEME_DIR) + # endif() + # + # configure_file( + # "${CMAKE_CURRENT_SOURCE_DIR}/conf.py.in" + # "${BINARY_BUILD_DIR}/conf.py" + # @ONLY) + + add_custom_target(jansson_docs ALL + ${SPHINX_EXECUTABLE} + # -q # Enable for quiet mode + -b html + -d "${SPHINX_CACHE_DIR}" + # -c "${BINARY_BUILD_DIR}" # enable if using cmake-generated conf.py + "${CMAKE_CURRENT_SOURCE_DIR}/doc" + "${SPHINX_HTML_DIR}" + COMMENT "Building HTML documentation with Sphinx") + + message (STATUS "Documentation has been built in ${SPHINX_HTML_DIR}") +endif () + + +OPTION (WITHOUT_TESTS "Don't build tests ('make test' to execute tests)" OFF) + +if (NOT WITHOUT_TESTS) + OPTION (TEST_WITH_VALGRIND "Enable valgrind tests." OFF) + + ENABLE_TESTING() + + if (TEST_WITH_VALGRIND) + # TODO: Add FindValgrind.cmake instead of having a hardcoded path. + + # enable valgrind + set(CMAKE_MEMORYCHECK_COMMAND valgrind) + set(CMAKE_MEMORYCHECK_COMMAND_OPTIONS + "--leak-check=full --show-reachable=yes --track-origins=yes -q") + + set(MEMCHECK_COMMAND + "${CMAKE_MEMORYCHECK_COMMAND} ${CMAKE_MEMORYCHECK_COMMAND_OPTIONS}") + separate_arguments(MEMCHECK_COMMAND) + endif () + + # + # Test suites. + # + if (CMAKE_COMPILER_IS_GNUCC) + add_definitions(-Wall -Wextra -Wdeclaration-after-statement -Werror) + endif () + + set(api_tests + test_array + test_copy + test_dump + test_dump_callback + test_equal + test_load + test_loadb + test_number + test_object + test_pack + test_simple + test_unpack) + + # Doing arithmetic on void pointers is not allowed by Microsofts compiler + # such as secure_malloc and secure_free is doing, so exclude it for now. + if (NOT MSVC) + list(APPEND api_tests test_memory_funcs) + endif() + + # Helper macro for building and linking a test program. + macro(build_testprog name dir) + add_executable(${name} ${dir}/${name}.c) + add_dependencies(${name} jansson) + target_link_libraries(${name} jansson) + endmacro(build_testprog) + + # Create executables and tests/valgrind tests for API tests. + foreach (test ${api_tests}) + build_testprog(${test} ${PROJECT_SOURCE_DIR}/test/suites/api) + add_test(${test} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${test}) + + if (TEST_WITH_VALGRIND) + add_test(memcheck_${test} ${MEMCHECK_COMMAND} + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${test}) + endif () + endforeach () + + # Test harness for the suites tests. + build_testprog(json_process ${PROJECT_SOURCE_DIR}/test/bin) + + set(SUITES encoding-flags valid invalid invalid-unicode) + foreach (SUITE ${SUITES}) + file(GLOB TESTDIRS ${jansson_SOURCE_DIR}/test/suites/${SUITE}/*) + foreach (TESTDIR ${TESTDIRS}) + if (IS_DIRECTORY ${TESTDIR}) + get_filename_component(TNAME ${TESTDIR} NAME) + add_test(${SUITE}__${TNAME} + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/json_process ${TESTDIR}) + endif () + endforeach () + endforeach () +endif () + diff --git a/cmake/CheckFunctionKeywords.cmake b/cmake/CheckFunctionKeywords.cmake new file mode 100644 index 0000000..44601fd --- /dev/null +++ b/cmake/CheckFunctionKeywords.cmake @@ -0,0 +1,15 @@ +include(CheckCSourceCompiles) + +macro(check_function_keywords _wordlist) + set(${_result} "") + foreach(flag ${_wordlist}) + string(REGEX REPLACE "[-+/ ()]" "_" flagname "${flag}") + string(TOUPPER "${flagname}" flagname) + set(have_flag "HAVE_${flagname}") + check_c_source_compiles("${flag} void func(); void func() { } int main() { func(); return 0; }" ${have_flag}) + if(${have_flag} AND NOT ${_result}) + set(${_result} "${flag}") +# break() + endif(${have_flag} AND NOT ${_result}) + endforeach(flag) +endmacro(check_function_keywords) diff --git a/cmake/FindSphinx.cmake b/cmake/FindSphinx.cmake new file mode 100644 index 0000000..76f6e2a --- /dev/null +++ b/cmake/FindSphinx.cmake @@ -0,0 +1,16 @@ +find_program(SPHINX_EXECUTABLE NAMES sphinx-build + HINTS + $ENV{SPHINX_DIR} + PATH_SUFFIXES bin + DOC "Sphinx documentation generator" +) + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(Sphinx DEFAULT_MSG + SPHINX_EXECUTABLE +) + +mark_as_advanced( +  SPHINX_EXECUTABLE +) diff --git a/cmake/config.h.cmake b/cmake/config.h.cmake new file mode 100644 index 0000000..bc81178 --- /dev/null +++ b/cmake/config.h.cmake @@ -0,0 +1,45 @@ +/* Reduced down to the defines that are actually used in the code */ + +/* Define to 1 if you have the (and friends) header file. */ +#cmakedefine HAVE_INTTYPES_H 1 +#cmakedefine HAVE_STDINT_H 1 +#cmakedefine HAVE_SYS_TYPES_H 1 + +/* We must include this here, as in (eg) utf.h it will want to use + the integer type, which in MSVC2010 will be in stdint.h + (there is no inttypes.h in MSVC2010) */ +#if defined(HAVE_STDINT_H) +# include +#elif defined(HAVE_INTTYPES_H) +# include +#elif defined(HAVE_SYS_TYPES_H) +# include +#endif + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_LOCALE_H 1 + +/* Define to 1 if you have the 'setlocale' function. */ +#cmakedefine HAVE_SETLOCALE 1 + +/* Define to the type of a signed integer type of width exactly 32 bits if + such a type exists and the standard includes do not define it. */ +#cmakedefine HAVE_INT32_T 1 + +#ifndef HAVE_INT32_T +# define int32_t @JSON_INT32@ +#endif + +#cmakedefine HAVE_SSIZE_T 1 + +#ifndef HAVE_SSIZE_T +# define ssize_t @JSON_SSIZE@ +#endif + +#cmakedefine HAVE_SNPRINTF 1 + +#ifndef HAVE_SNPRINTF +# define snprintf @JSON_SNPRINTF@ +#endif + +#cmakedefine HAVE_VSNPRINTF diff --git a/cmake/jansson_config.h.cmake b/cmake/jansson_config.h.cmake new file mode 100644 index 0000000..335ccc4 --- /dev/null +++ b/cmake/jansson_config.h.cmake @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2010-2012 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + * + * + * This file specifies a part of the site-specific configuration for + * Jansson, namely those things that affect the public API in + * jansson.h. + * + * The CMake system will generate the jansson_config.h file and + * copy it to the build and install directories. + */ + +#ifndef JANSSON_CONFIG_H +#define JANSSON_CONFIG_H + +/* Define this so that we can disable scattered automake configuration in source files */ +#define JANSSON_USING_CMAKE + +/* Note: when using cmake, JSON_INTEGER_IS_LONG_LONG is not defined nor used, + * as we will also check for __int64 etc types. + * (the definition was used in the automake system) */ + +/* Bring in the cmake-detected defines */ +#cmakedefine HAVE_STDINT_H 1 +#cmakedefine HAVE_INTTYPES_H 1 +#cmakedefine HAVE_SYS_TYPES_H 1 + +/* Include our standard type header for the integer typedef */ + +#if defined(HAVE_STDINT_H) +# include +#elif defined(HAVE_INTTYPES_H) +# include +#elif defined(HAVE_SYS_TYPES_H) +# include +#endif + + +/* If your compiler supports the inline keyword in C, JSON_INLINE is + defined to `inline', otherwise empty. In C++, the inline is always + supported. */ +#ifdef __cplusplus +#define JSON_INLINE inline +#else +#define JSON_INLINE @JSON_INLINE@ +#endif + + +#define json_int_t @JSON_INT_T@ +#define json_strtoint @JSON_STRTOINT@ +#define JSON_INTEGER_FORMAT @JSON_INTEGER_FORMAT@ + + +/* If locale.h and localeconv() are available, define to 1, otherwise to 0. */ +#define JSON_HAVE_LOCALECONV @JSON_HAVE_LOCALECONV@ + + + +#endif diff --git a/src/jansson.h b/src/jansson.h index ec384d8..e53a301 100644 --- a/src/jansson.h +++ b/src/jansson.h @@ -52,6 +52,7 @@ typedef struct json_t { size_t refcount; } json_t; +#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */ #if JSON_INTEGER_IS_LONG_LONG #ifdef _WIN32 #define JSON_INTEGER_FORMAT "I64d" @@ -63,6 +64,7 @@ typedef long long json_int_t; #define JSON_INTEGER_FORMAT "ld" typedef long json_int_t; #endif /* JSON_INTEGER_IS_LONG_LONG */ +#endif #define json_typeof(json) ((json)->type) #define json_is_object(json) (json && json_typeof(json) == JSON_OBJECT) diff --git a/src/load.c b/src/load.c index 16ff53d..2cc7a5f 100644 --- a/src/load.c +++ b/src/load.c @@ -446,6 +446,7 @@ out: jsonp_free(lex->value.string); } +#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */ #if JSON_INTEGER_IS_LONG_LONG #ifdef _MSC_VER /* Microsoft Visual Studio */ #define json_strtoint _strtoi64 @@ -455,6 +456,7 @@ out: #else #define json_strtoint strtol #endif +#endif static int lex_scan_number(lex_t *lex, int c, json_error_t *error) { diff --git a/src/strconv.c b/src/strconv.c index caa9ab8..3e2cb7c 100644 --- a/src/strconv.c +++ b/src/strconv.c @@ -5,6 +5,11 @@ #include "jansson_private.h" #include "strbuffer.h" +/* need config.h to get the correct snprintf */ +#ifdef HAVE_CONFIG_H +#include +#endif + #if JSON_HAVE_LOCALECONV #include diff --git a/test/bin/json_process.c b/test/bin/json_process.c index 637dfd5..15eec5a 100644 --- a/test/bin/json_process.c +++ b/test/bin/json_process.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 Petri Lehtinen + * Copyright (c) 2009-2013 Petri Lehtinen * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -15,22 +15,213 @@ #include #include -#if HAVE_LOCALE_H +#ifdef HAVE_LOCALE_H #include -#endif + #endif #if _WIN32 #include /* for _setmode() */ #include /* for _O_BINARY */ + +static const char dir_sep = '\\'; +#else +static const char dir_sep = '/'; #endif + +struct config { + int indent; + int compact; + int preserve_order; + int ensure_ascii; + int sort_keys; + int strip; + int use_env; +} conf; + #define l_isspace(c) ((c) == ' ' || (c) == '\n' || (c) == '\r' || (c) == '\t') +/* Return a pointer to the first non-whitespace character of str. + Modifies str so that all trailing whitespace characters are + replaced by '\0'. */ +static const char *strip(char *str) +{ + size_t length; + char *result = str; + while (*result && l_isspace(*result)) + result++; + + length = strlen(result); + if (length == 0) + return result; + + while (l_isspace(result[length - 1])) + result[--length] = '\0'; + + return result; +} + + +static char *loadfile(FILE *file) +{ + long fsize, ret; + char *buf; + + fseek(file, 0, SEEK_END); + fsize = ftell(file); + fseek(file, 0, SEEK_SET); + + buf = malloc(fsize+1); + ret = fread(buf, 1, fsize, file); + if (ret != fsize) + exit(1); + buf[fsize] = '\0'; + + return buf; +} + + +static void read_conf(FILE *conffile) +{ + char *buffer, *line, *val; + + buffer = loadfile(conffile); + line = strtok(buffer, "\r\n"); + while (line) { + val = strchr(line, '='); + if (!val) { + printf("invalid configuration line\n"); + break; + } + *val++ = '\0'; + + if (!strcmp(line, "JSON_INDENT")) + conf.indent = atoi(val); + if (!strcmp(line, "JSON_COMPACT")) + conf.compact = atoi(val); + if (!strcmp(line, "JSON_ENSURE_ASCII")) + conf.ensure_ascii = atoi(val); + if (!strcmp(line, "JSON_PRESERVE_ORDER")) + conf.preserve_order = atoi(val); + if (!strcmp(line, "JSON_SORT_KEYS")) + conf.sort_keys = atoi(val); + if (!strcmp(line, "STRIP")) + conf.strip = atoi(val); + + line = strtok(NULL, "\r\n"); + } + + free(buffer); +} + + +static int cmpfile(const char *str, const char *path, const char *fname) +{ + char filename[1024], *buffer; + int ret; + FILE *file; + + sprintf(filename, "%s%c%s", path, dir_sep, fname); + file = fopen(filename, "rb"); + if (!file) { + if (conf.strip) + strcat(filename, ".strip"); + else + strcat(filename, ".normal"); + file = fopen(filename, "rb"); + } + if (!file) { + printf("Error: test result file could not be opened.\n"); + exit(1); + } + + buffer = loadfile(file); + if (strcmp(buffer, str) != 0) + ret = 1; + else + ret = 0; + free(buffer); + fclose(file); + + return ret; +} + +int use_conf(char *test_path) +{ + int ret; + size_t flags = 0; + char filename[1024], errstr[1024]; + char *buffer; + FILE *infile, *conffile; + json_t *json; + json_error_t error; + + sprintf(filename, "%s%cinput", test_path, dir_sep); + if (!(infile = fopen(filename, "rb"))) { + fprintf(stderr, "Could not open \"%s\"\n", filename); + return 2; + } + + sprintf(filename, "%s%cenv", test_path, dir_sep); + conffile = fopen(filename, "rb"); + if (conffile) { + read_conf(conffile); + fclose(conffile); + } + + if (conf.indent < 0 || conf.indent > 255) { + fprintf(stderr, "invalid value for JSON_INDENT: %d\n", conf.indent); + return 2; + } + + if (conf.indent) + flags |= JSON_INDENT(conf.indent); + + if (conf.compact) + flags |= JSON_COMPACT; + + if (conf.ensure_ascii) + flags |= JSON_ENSURE_ASCII; + + if (conf.preserve_order) + flags |= JSON_PRESERVE_ORDER; + + if (conf.sort_keys) + flags |= JSON_SORT_KEYS; + + if (conf.strip) { + /* Load to memory, strip leading and trailing whitespace */ + buffer = loadfile(infile); + json = json_loads(strip(buffer), 0, &error); + free(buffer); + } + else + json = json_loadf(infile, 0, &error); + + fclose(infile); + + if (!json) { + sprintf(errstr, "%d %d %d\n%s\n", + error.line, error.column, error.position, + error.text); + + ret = cmpfile(errstr, test_path, "error"); + return ret; + } + + buffer = json_dumps(json, flags); + ret = cmpfile(buffer, test_path, "output"); + free(buffer); + json_decref(json); + + return ret; +} + static int getenv_int(const char *name) { char *value, *end; long result; - + value = getenv(name); if(!value) return 0; @@ -42,49 +233,19 @@ static int getenv_int(const char *name) return (int)result; } -/* Return a pointer to the first non-whitespace character of str. - Modifies str so that all trailing whitespace characters are - replaced by '\0'. */ -static const char *strip(char *str) +int use_env() { - size_t length; - char *result = str; - while(*result && l_isspace(*result)) - result++; - - length = strlen(result); - if(length == 0) - return result; - - while(l_isspace(result[length - 1])) - result[--length] = '\0'; - - return result; -} - -int main(int argc, char *argv[]) -{ - int indent = 0; + int indent; size_t flags = 0; - json_t *json; json_error_t error; -#if HAVE_SETLOCALE - setlocale(LC_ALL, ""); -#endif - - if(argc != 1) { - fprintf(stderr, "usage: %s\n", argv[0]); - return 2; - } - -#ifdef _WIN32 + #ifdef _WIN32 /* On Windows, set stdout and stderr to binary mode to avoid outputting DOS line terminators */ _setmode(_fileno(stdout), _O_BINARY); _setmode(_fileno(stderr), _O_BINARY); -#endif + #endif indent = getenv_int("JSON_INDENT"); if(indent < 0 || indent > 255) { @@ -103,10 +264,10 @@ int main(int argc, char *argv[]) if(getenv_int("JSON_PRESERVE_ORDER")) flags |= JSON_PRESERVE_ORDER; - + if(getenv_int("JSON_SORT_KEYS")) - flags |= JSON_SORT_KEYS; - + flags |= JSON_SORT_KEYS; + if(getenv_int("STRIP")) { /* Load to memory, strip leading and trailing whitespace */ size_t size = 0, used = 0; @@ -129,7 +290,7 @@ int main(int argc, char *argv[]) } used += count; } - + json = json_loads(strip(buffer), 0, &error); free(buffer); } @@ -138,13 +299,50 @@ int main(int argc, char *argv[]) if(!json) { fprintf(stderr, "%d %d %d\n%s\n", - error.line, error.column, error.position, - error.text); + error.line, error.column, + error.position, error.text); return 1; } json_dumpf(json, stdout, flags); - json_decref(json); return 0; } + +int main(int argc, char *argv[]) +{ + int i; + char *test_path = NULL; + + #ifdef HAVE_SETLOCALE + setlocale(LC_ALL, ""); + #endif + + if (argc < 2) { + goto usage; + } + + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "--strip")) + conf.strip = 1; + else if (!strcmp(argv[i], "--env")) + conf.use_env = 1; + else + test_path = argv[i]; + } + + if (conf.use_env) + return use_env(); + else + { + if (!test_path) + goto usage; + + return use_conf(test_path); + } + +usage: + fprintf(stderr, "argc =%d\n", argc); + fprintf(stderr, "usage: %s [--strip] [--env] test_dir\n", argv[0]); + return 2; +} diff --git a/test/suites/.gitattributes b/test/suites/.gitattributes new file mode 100644 index 0000000..68d8861 --- /dev/null +++ b/test/suites/.gitattributes @@ -0,0 +1,2 @@ +api/ text=auto +* text eol=lf \ No newline at end of file diff --git a/test/suites/api/test_number.c b/test/suites/api/test_number.c index c256598..e789fe6 100644 --- a/test/suites/api/test_number.c +++ b/test/suites/api/test_number.c @@ -12,7 +12,7 @@ static void run_tests() { json_t *integer, *real; - int i; + json_int_t i; double d; integer = json_integer(5); diff --git a/test/suites/api/test_pack.c b/test/suites/api/test_pack.c index c7e7251..61c7409 100644 --- a/test/suites/api/test_pack.c +++ b/test/suites/api/test_pack.c @@ -6,6 +6,12 @@ * it under the terms of the MIT license. See LICENSE for details. */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + #include #include #include @@ -20,7 +26,6 @@ static void run_tests() /* * Simple, valid json_pack cases */ - /* true */ value = json_pack("b", 1); if(!json_is_true(value)) diff --git a/test/suites/encoding-flags/run b/test/suites/encoding-flags/run index c49d259..6a7d755 100755 --- a/test/suites/encoding-flags/run +++ b/test/suites/encoding-flags/run @@ -14,7 +14,7 @@ run_test() { if [ -f $test_path/env ]; then . $test_path/env fi - $json_process <$test_path/input >$test_log/stdout 2>$test_log/stderr + $json_process --env <$test_path/input >$test_log/stdout 2>$test_log/stderr ) valgrind_check $test_log/stderr || return 1 cmp -s $test_path/output $test_log/stdout diff --git a/test/suites/invalid-unicode/run b/test/suites/invalid-unicode/run index 0565584..fe63262 100755 --- a/test/suites/invalid-unicode/run +++ b/test/suites/invalid-unicode/run @@ -10,7 +10,7 @@ is_test() { } run_test() { - $json_process <$test_path/input >$test_log/stdout 2>$test_log/stderr + $json_process --env <$test_path/input >$test_log/stdout 2>$test_log/stderr valgrind_check $test_log/stderr || return 1 cmp -s $test_path/error $test_log/stderr } diff --git a/test/suites/invalid/run b/test/suites/invalid/run index d15eba3..0ff5381 100755 --- a/test/suites/invalid/run +++ b/test/suites/invalid/run @@ -20,7 +20,7 @@ do_run() { strip=1 fi - STRIP=$strip $json_process \ + STRIP=$strip $json_process --env \ <$test_path/input >$test_log/stdout$s 2>$test_log/stderr$s valgrind_check $test_log/stderr$s || return 1 diff --git a/test/suites/valid/complex-array/env b/test/suites/valid/complex-array/env new file mode 100644 index 0000000..bd89eff --- /dev/null +++ b/test/suites/valid/complex-array/env @@ -0,0 +1 @@ +JSON_SORT_KEYS=1 \ No newline at end of file diff --git a/test/suites/valid/run b/test/suites/valid/run index 170760b..679eb1a 100755 --- a/test/suites/valid/run +++ b/test/suites/valid/run @@ -19,7 +19,7 @@ do_run() { strip=0 [ "$variant" = "strip" ] && strip=1 - STRIP=$strip $json_process \ + STRIP=$strip $json_process --env \ <$test_path/input >$test_log/stdout$s 2>$test_log/stderr$s valgrind_check $test_log/stderr$s || return 1