diff --git a/.gitignore b/.gitignore index 5ca8da9..aef6a8d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ .sys_tags .tags build/ -json-c/ output/ *.swp diff --git a/cp.sh b/cp.sh index 98eab8a..9049688 100755 --- a/cp.sh +++ b/cp.sh @@ -1,7 +1,10 @@ #sshpass -p "&Over#B0Ost!" scp ./start.sh root@10.10.10.101:/root/ -sshpass -p "&Over#B0Ost!" scp ./output/bin/tx_server root@10.10.10.235:/root/ +#sshpass -p "&Over#B0Ost!" scp ./output/bin/tx_server root@10.10.12.2:/root/ #sshpass -p "&Over#B0Ost!" scp ./output/bin/tx_server root@10.10.10.101:/home/linaro/ #sshpass -p "PddloTSecPwdOnly!" scp ./output/bin/tx_server root@192.168.0.148:/home/linaro/ -#sshpass -p "PddloTSecPwdOnly!" scp ./output/bin/tx_server root@10.10.10.132:/root/ -#sshpass -p "PddloTSecPwdOnly!" scp ./start.sh root@10.10.10.225:/root/ -#sshpass -p "PddloTSecPwdOnly!" scp ./start.sh root@10.10.10.132:/root/ +#sshpass -p "TxApPwd#2025!" scp ./output/bin/tx_server root@10.10.12.2:/root +#sshpass -p "TxApPwd#2025!" scp ./*.zip root@10.10.12.4:/root/ota +sshpass -p "TxApPwd#2025!" scp ./tx_server_v1.1.10 root@10.10.12.2:/root +#sshpass -p "TxApPwd#2025!" scp tx_ota/*.bin root@10.10.12.4:/root/ota +#sshpass -p "PddloTSecPwdOnly!" scp ./output/bin/tx_server root@10.10.12.4:/root/ +#sshpass -p "PddloTSecPwdOnly!" scp tx_ota/*.bin root@10.10.12.6:/root/ota diff --git a/json-c/arraylist.c b/json-c/arraylist.c new file mode 100644 index 0000000..ddeb8d4 --- /dev/null +++ b/json-c/arraylist.c @@ -0,0 +1,146 @@ +/* + * $Id: arraylist.c,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include + +#ifdef STDC_HEADERS +# include +# include +#endif /* STDC_HEADERS */ + +#if defined(HAVE_STRINGS_H) && !defined(_STRING_H) && !defined(__USE_BSD) +# include +#endif /* HAVE_STRINGS_H */ + +#ifndef SIZE_T_MAX +#if SIZEOF_SIZE_T == SIZEOF_INT +#define SIZE_T_MAX UINT_MAX +#elif SIZEOF_SIZE_T == SIZEOF_LONG +#define SIZE_T_MAX ULONG_MAX +#elif SIZEOF_SIZE_T == SIZEOF_LONG_LONG +#define SIZE_T_MAX ULLONG_MAX +#else +#error Unable to determine size of size_t +#endif +#endif + +#include "arraylist.h" + +struct array_list* +array_list_new(array_list_free_fn *free_fn) +{ + struct array_list *arr; + + arr = (struct array_list*)calloc(1, sizeof(struct array_list)); + if(!arr) return NULL; + arr->size = ARRAY_LIST_DEFAULT_SIZE; + arr->length = 0; + arr->free_fn = free_fn; + if(!(arr->array = (void**)calloc(sizeof(void*), arr->size))) { + free(arr); + return NULL; + } + return arr; +} + +extern void +array_list_free(struct array_list *arr) +{ + size_t i; + for(i = 0; i < arr->length; i++) + if(arr->array[i]) arr->free_fn(arr->array[i]); + free(arr->array); + free(arr); +} + +void* +array_list_get_idx(struct array_list *arr, size_t i) +{ + if(i >= arr->length) return NULL; + return arr->array[i]; +} + +static int array_list_expand_internal(struct array_list *arr, size_t max) +{ + void *t; + size_t new_size; + + if(max < arr->size) return 0; + /* Avoid undefined behaviour on size_t overflow */ + if( arr->size >= SIZE_T_MAX / 2 ) + new_size = max; + else + { + new_size = arr->size << 1; + if (new_size < max) + new_size = max; + } + if (new_size > (~((size_t)0)) / sizeof(void*)) return -1; + if (!(t = realloc(arr->array, new_size*sizeof(void*)))) return -1; + arr->array = (void**)t; + (void)memset(arr->array + arr->size, 0, (new_size-arr->size)*sizeof(void*)); + arr->size = new_size; + return 0; +} + +int +array_list_put_idx(struct array_list *arr, size_t idx, void *data) +{ + if (idx > SIZE_T_MAX - 1 ) return -1; + if(array_list_expand_internal(arr, idx+1)) return -1; + if(idx < arr->length && arr->array[idx]) + arr->free_fn(arr->array[idx]); + arr->array[idx] = data; + if(arr->length <= idx) arr->length = idx + 1; + return 0; +} + +int +array_list_add(struct array_list *arr, void *data) +{ + return array_list_put_idx(arr, arr->length, data); +} + +void +array_list_sort(struct array_list *arr, int(*sort_fn)(const void *, const void *)) +{ + qsort(arr->array, arr->length, sizeof(arr->array[0]), sort_fn); +} + +void* array_list_bsearch(const void **key, struct array_list *arr, + int (*sort_fn)(const void *, const void *)) +{ + return bsearch(key, arr->array, arr->length, sizeof(arr->array[0]), + sort_fn); +} + +size_t +array_list_length(struct array_list *arr) +{ + return arr->length; +} + +int +array_list_del_idx( struct array_list *arr, size_t idx, size_t count ) +{ + size_t i, stop; + + stop = idx + count; + if ( idx >= arr->length || stop > arr->length ) return -1; + for ( i = idx; i < stop; ++i ) { + if ( arr->array[i] ) arr->free_fn( arr->array[i] ); + } + memmove( arr->array + idx, arr->array + stop, (arr->length - stop) * sizeof(void*) ); + arr->length -= count; + return 0; +} diff --git a/json-c/arraylist.h b/json-c/arraylist.h new file mode 100644 index 0000000..38603e8 --- /dev/null +++ b/json-c/arraylist.h @@ -0,0 +1,70 @@ +/* + * $Id: arraylist.h,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief Internal methods for working with json_type_array objects. + * Although this is exposed by the json_object_get_array() method, + * it is not recommended for direct use. + */ +#ifndef _arraylist_h_ +#define _arraylist_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define ARRAY_LIST_DEFAULT_SIZE 32 + +typedef void (array_list_free_fn) (void *data); + +struct array_list +{ + void **array; + size_t length; + size_t size; + array_list_free_fn *free_fn; +}; +typedef struct array_list array_list; + +extern struct array_list* +array_list_new(array_list_free_fn *free_fn); + +extern void +array_list_free(struct array_list *al); + +extern void* +array_list_get_idx(struct array_list *al, size_t i); + +extern int +array_list_put_idx(struct array_list *al, size_t i, void *data); + +extern int +array_list_add(struct array_list *al, void *data); + +extern size_t +array_list_length(struct array_list *al); + +extern void +array_list_sort(struct array_list *arr, int(*compar)(const void *, const void *)); + +extern void* array_list_bsearch(const void **key, + struct array_list *arr, + int (*sort_fn)(const void *, const void *)); + +extern int +array_list_del_idx(struct array_list *arr, size_t idx, size_t count); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/json-c/bits.h b/json-c/bits.h new file mode 100644 index 0000000..14c1c13 --- /dev/null +++ b/json-c/bits.h @@ -0,0 +1,36 @@ +/** + * @file + * @brief Do not use, only contains deprecated defines. + * @deprecated Use json_util.h instead. + * + * $Id: bits.h,v 1.10 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _bits_h_ +#define _bits_h_ + +/** + * @deprecated + */ +#define hexdigit(x) (((x) <= '9') ? (x) - '0' : ((x) & 7) + 9) +/** + * @deprecated + */ +#define error_ptr(error) ((void*)error) +/** + * @deprecated + */ +#define error_description(error) (json_tokener_get_error(error)) +/** + * @deprecated + */ +#define is_error(ptr) (ptr == NULL) + +#endif diff --git a/json-c/config.h b/json-c/config.h new file mode 100644 index 0000000..0d888e7 --- /dev/null +++ b/json-c/config.h @@ -0,0 +1,199 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Enable RDRAND Hardware RNG Hash Seed */ +/* #undef ENABLE_RDRAND */ + +/* Enable partial threading support */ +#define ENABLE_THREADING 1 + +/* Define if .gnu.warning accepts long strings. */ +/* #undef HAS_GNU_WARNING_LONG */ + +/* Has atomic builtins */ +#define HAVE_ATOMIC_BUILTINS 1 + +/* Define to 1 if you have the declaration of `INFINITY', and to 0 if you + don't. */ +#define HAVE_DECL_INFINITY 1 + +/* Define to 1 if you have the declaration of `isinf', and to 0 if you don't. + */ +#define HAVE_DECL_ISINF 1 + +/* Define to 1 if you have the declaration of `isnan', and to 0 if you don't. + */ +#define HAVE_DECL_ISNAN 1 + +/* Define to 1 if you have the declaration of `nan', and to 0 if you don't. */ +#define HAVE_DECL_NAN 1 + +/* Define to 1 if you have the declaration of `_finite', and to 0 if you + don't. */ +#define HAVE_DECL__FINITE 0 + +/* Define to 1 if you have the declaration of `_isnan', and to 0 if you don't. + */ +#define HAVE_DECL__ISNAN 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* #undef HAVE_DOPRNT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ENDIAN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `open' function. */ +#define HAVE_OPEN 1 + +/* Define to 1 if you have the `realloc' function. */ +#define HAVE_REALLOC 1 + +/* Define to 1 if you have the `setlocale' function. */ +#define HAVE_SETLOCALE 1 + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDARG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strcasecmp' function. */ +#define HAVE_STRCASECMP 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strncasecmp' function. */ +#define HAVE_STRNCASECMP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYSLOG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_CDEFS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `uselocale' function. */ +#define HAVE_USELOCALE 1 + +/* Define to 1 if you have the `vasprintf' function. */ +#define HAVE_VASPRINTF 1 + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* Define to 1 if you have the `vsyslog' function. */ +#define HAVE_VSYSLOG 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_XLOCALE_H */ + +/* Have __thread */ +#define HAVE___THREAD 1 + +/* Public define for json_inttypes.h */ +#define JSON_C_HAVE_INTTYPES_H 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "json-c" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "json-c@googlegroups.com" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "json-c" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "json-c 0.13.1" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "json-c" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.13.1" + +/* The number of bytes in type int */ +#define SIZEOF_INT 4 + +/* The number of bytes in type int64_t */ +#define SIZEOF_INT64_T 8 + +/* The number of bytes in type long */ +#define SIZEOF_LONG 8 + +/* The number of bytes in type long long */ +#define SIZEOF_LONG_LONG 8 + +/* The number of bytes in type size_t */ +#define SIZEOF_SIZE_T 8 + +/* Specifier for __thread */ +#define SPEC___THREAD __thread + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "0.13.1" + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ + +#define _GNU_SOURCE diff --git a/json-c/debug.c b/json-c/debug.c new file mode 100644 index 0000000..4b5140a --- /dev/null +++ b/json-c/debug.c @@ -0,0 +1,83 @@ +/* + * $Id: debug.c,v 1.5 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include +#include +#include +#include + +#if HAVE_SYSLOG_H +# include +#endif /* HAVE_SYSLOG_H */ + +#if HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ + +#if HAVE_SYS_PARAM_H +#include +#endif /* HAVE_SYS_PARAM_H */ + +#include "debug.h" + +static int _syslog = 0; +static int _debug = 0; + +void mc_set_debug(int debug) { _debug = debug; } +int mc_get_debug(void) { return _debug; } + +extern void mc_set_syslog(int syslog) +{ + _syslog = syslog; +} + +void mc_debug(const char *msg, ...) +{ + va_list ap; + if(_debug) { + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_DEBUG, msg, ap); + } else +#endif + vprintf(msg, ap); + va_end(ap); + } +} + +void mc_error(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_ERR, msg, ap); + } else +#endif + vfprintf(stderr, msg, ap); + va_end(ap); +} + +void mc_info(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_INFO, msg, ap); + } else +#endif + vfprintf(stderr, msg, ap); + va_end(ap); +} diff --git a/json-c/debug.h b/json-c/debug.h new file mode 100644 index 0000000..07fcc38 --- /dev/null +++ b/json-c/debug.h @@ -0,0 +1,75 @@ +/* + * $Id: debug.h,v 1.5 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void mc_set_debug(int debug); +extern int mc_get_debug(void); + +extern void mc_set_syslog(int syslog); + +extern void mc_debug(const char *msg, ...); +extern void mc_error(const char *msg, ...); +extern void mc_info(const char *msg, ...); + +#ifndef __STRING +#define __STRING(x) #x +#endif + +#ifndef PARSER_BROKEN_FIXED + +#define JASSERT(cond) do {} while(0) + +#else + +#define JASSERT(cond) do { \ + if (!(cond)) { \ + mc_error("cjson assert failure %s:%d : cond \"" __STRING(cond) "failed\n", __FILE__, __LINE__); \ + *(int *)0 = 1;\ + abort(); \ + }\ + } while(0) + +#endif + +#define MC_ERROR(x, ...) mc_error(x, ##__VA_ARGS__) + +#ifdef MC_MAINTAINER_MODE +#define MC_SET_DEBUG(x) mc_set_debug(x) +#define MC_GET_DEBUG() mc_get_debug() +#define MC_SET_SYSLOG(x) mc_set_syslog(x) +#define MC_DEBUG(x, ...) mc_debug(x, ##__VA_ARGS__) +#define MC_INFO(x, ...) mc_info(x, ##__VA_ARGS__) +#else +#define MC_SET_DEBUG(x) if (0) mc_set_debug(x) +#define MC_GET_DEBUG() (0) +#define MC_SET_SYSLOG(x) if (0) mc_set_syslog(x) +#define MC_DEBUG(x, ...) if (0) mc_debug(x, ##__VA_ARGS__) +#define MC_INFO(x, ...) if (0) mc_info(x, ##__VA_ARGS__) +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/json-c/json.h b/json-c/json.h new file mode 100644 index 0000000..fc2dadd --- /dev/null +++ b/json-c/json.h @@ -0,0 +1,38 @@ +/* + * $Id: json.h,v 1.6 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief A convenience header that may be included instead of other individual ones. + */ +#ifndef _json_h_ +#define _json_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "debug.h" +#include "linkhash.h" +#include "arraylist.h" +#include "json_util.h" +#include "json_object.h" +#include "json_pointer.h" +#include "json_tokener.h" +#include "json_object_iterator.h" +#include "json_c_version.h" + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/json-c/json_c_version.c b/json-c/json_c_version.c new file mode 100644 index 0000000..13eb188 --- /dev/null +++ b/json-c/json_c_version.c @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2012 Eric Haszlakiewicz + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + */ +#include "config.h" + +#include "json_c_version.h" + +const char *json_c_version(void) +{ + return JSON_C_VERSION; +} + +int json_c_version_num(void) +{ + return JSON_C_VERSION_NUM; +} + diff --git a/json-c/json_c_version.h b/json-c/json_c_version.h new file mode 100644 index 0000000..98838be --- /dev/null +++ b/json-c/json_c_version.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2012,2017 Eric Haszlakiewicz + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + */ + +/** + * @file + * @brief Methods for retrieving the json-c version. + */ +#ifndef _json_c_version_h_ +#define _json_c_version_h_ + +#define JSON_C_MAJOR_VERSION 0 +#define JSON_C_MINOR_VERSION 13 +#define JSON_C_MICRO_VERSION 01 +#define JSON_C_VERSION_NUM ((JSON_C_MAJOR_VERSION << 16) | \ + (JSON_C_MINOR_VERSION << 8) | \ + JSON_C_MICRO_VERSION) +#define JSON_C_VERSION "0.13.1" + +/** + * @see JSON_C_VERSION + * @return the version of the json-c library as a string + */ +const char *json_c_version(void); /* Returns JSON_C_VERSION */ + +/** + * The json-c version encoded into an int, with the low order 8 bits + * being the micro version, the next higher 8 bits being the minor version + * and the next higher 8 bits being the major version. + * For example, 7.12.99 would be 0x00070B63. + * + * @see JSON_C_VERSION_NUM + * @return the version of the json-c library as an int + */ +int json_c_version_num(void); /* Returns JSON_C_VERSION_NUM */ + +#endif diff --git a/json-c/json_config.h b/json-c/json_config.h new file mode 100644 index 0000000..965ff1c --- /dev/null +++ b/json-c/json_config.h @@ -0,0 +1,4 @@ +/* json_config.h. Generated from json_config.h.in by configure. */ + +/* Define to 1 if you have the header file. */ +#define JSON_C_HAVE_INTTYPES_H 1 diff --git a/json-c/json_inttypes.h b/json-c/json_inttypes.h new file mode 100644 index 0000000..3854913 --- /dev/null +++ b/json-c/json_inttypes.h @@ -0,0 +1,23 @@ + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ +#ifndef _json_inttypes_h_ +#define _json_inttypes_h_ + +#include "json_config.h" + +#ifdef JSON_C_HAVE_INTTYPES_H +/* inttypes.h includes stdint.h */ +#include + +#else +#include + +#define PRId64 "I64d" +#define SCNd64 "I64d" + +#endif + +#endif diff --git a/json-c/json_object.c b/json-c/json_object.c new file mode 100644 index 0000000..458163c --- /dev/null +++ b/json-c/json_object.c @@ -0,0 +1,1493 @@ +/* + * $Id: json_object.c,v 1.17 2006/07/25 03:24:50 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include "strerror_override.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "printbuf.h" +#include "linkhash.h" +#include "arraylist.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_object_private.h" +#include "json_util.h" +#include "math_compat.h" +#include "strdup_compat.h" +#include "snprintf_compat.h" + +#if SIZEOF_LONG_LONG != SIZEOF_INT64_T +#error "The long long type isn't 64-bits" +#endif + +// Don't define this. It's not thread-safe. +/* #define REFCOUNT_DEBUG 1 */ + +const char *json_number_chars = "0123456789.+-eE"; +const char *json_hex_chars = "0123456789abcdefABCDEF"; + +static void json_object_generic_delete(struct json_object* jso); +static struct json_object* json_object_new(enum json_type o_type); + +static json_object_to_json_string_fn json_object_object_to_json_string; +static json_object_to_json_string_fn json_object_boolean_to_json_string; +static json_object_to_json_string_fn json_object_double_to_json_string_default; +static json_object_to_json_string_fn json_object_int_to_json_string; +static json_object_to_json_string_fn json_object_string_to_json_string; +static json_object_to_json_string_fn json_object_array_to_json_string; + + +/* ref count debugging */ + +#ifdef REFCOUNT_DEBUG + +static struct lh_table *json_object_table; + +static void json_object_init(void) __attribute__ ((constructor)); +static void json_object_init(void) { + MC_DEBUG("json_object_init: creating object table\n"); + json_object_table = lh_kptr_table_new(128, NULL); +} + +static void json_object_fini(void) __attribute__ ((destructor)); +static void json_object_fini(void) +{ + struct lh_entry *ent; + if (MC_GET_DEBUG()) + { + if (json_object_table->count) + { + MC_DEBUG("json_object_fini: %d referenced objects at exit\n", + json_object_table->count); + lh_foreach(json_object_table, ent) + { + struct json_object* obj = + (struct json_object*) lh_entry_v(ent); + MC_DEBUG("\t%s:%p\n", + json_type_to_name(obj->o_type), obj); + } + } + } + MC_DEBUG("json_object_fini: freeing object table\n"); + lh_table_free(json_object_table); +} +#endif /* REFCOUNT_DEBUG */ + + +/* helper for accessing the optimized string data component in json_object + */ +static const char * +get_string_component(const struct json_object *jso) +{ + return (jso->o.c_string.len < LEN_DIRECT_STRING_DATA) ? + jso->o.c_string.str.data : jso->o.c_string.str.ptr; +} + +/* string escaping */ + +static int json_escape_str(struct printbuf *pb, const char *str, int len, int flags) +{ + int pos = 0, start_offset = 0; + unsigned char c; + while (len--) + { + c = str[pos]; + switch(c) + { + case '\b': + case '\n': + case '\r': + case '\t': + case '\f': + case '"': + case '\\': + case '/': + if((flags & JSON_C_TO_STRING_NOSLASHESCAPE) && c == '/') + { + pos++; + break; + } + + if(pos - start_offset > 0) + printbuf_memappend(pb, str + start_offset, pos - start_offset); + + if(c == '\b') printbuf_memappend(pb, "\\b", 2); + else if(c == '\n') printbuf_memappend(pb, "\\n", 2); + else if(c == '\r') printbuf_memappend(pb, "\\r", 2); + else if(c == '\t') printbuf_memappend(pb, "\\t", 2); + else if(c == '\f') printbuf_memappend(pb, "\\f", 2); + else if(c == '"') printbuf_memappend(pb, "\\\"", 2); + else if(c == '\\') printbuf_memappend(pb, "\\\\", 2); + else if(c == '/') printbuf_memappend(pb, "\\/", 2); + + start_offset = ++pos; + break; + default: + if(c < ' ') + { + char sbuf[7]; + if(pos - start_offset > 0) + printbuf_memappend(pb, + str + start_offset, + pos - start_offset); + snprintf(sbuf, sizeof(sbuf), + "\\u00%c%c", + json_hex_chars[c >> 4], + json_hex_chars[c & 0xf]); + printbuf_memappend_fast(pb, sbuf, (int) sizeof(sbuf) - 1); + start_offset = ++pos; + } else + pos++; + } + } + if (pos - start_offset > 0) + printbuf_memappend(pb, str + start_offset, pos - start_offset); + return 0; +} + + +/* reference counting */ + +extern struct json_object* json_object_get(struct json_object *jso) +{ + if (!jso) return jso; + +#if defined(HAVE_ATOMIC_BUILTINS) && defined(ENABLE_THREADING) + __sync_add_and_fetch(&jso->_ref_count, 1); +#else + ++jso->_ref_count; +#endif + + return jso; +} + +int json_object_put(struct json_object *jso) +{ + if(!jso) return 0; + + /* Avoid invalid free and crash explicitly instead of (silently) + * segfaulting. + */ + assert(jso->_ref_count > 0); + +#if defined(HAVE_ATOMIC_BUILTINS) && defined(ENABLE_THREADING) + /* Note: this only allow the refcount to remain correct + * when multiple threads are adjusting it. It is still an error + * for a thread to decrement the refcount if it doesn't "own" it, + * as that can result in the thread that loses the race to 0 + * operating on an already-freed object. + */ + if (__sync_sub_and_fetch(&jso->_ref_count, 1) > 0) return 0; +#else + if (--jso->_ref_count > 0) return 0; +#endif + + if (jso->_user_delete) + jso->_user_delete(jso, jso->_userdata); + jso->_delete(jso); + return 1; +} + + +/* generic object construction and destruction parts */ + +static void json_object_generic_delete(struct json_object* jso) +{ +#ifdef REFCOUNT_DEBUG + MC_DEBUG("json_object_delete_%s: %p\n", + json_type_to_name(jso->o_type), jso); + lh_table_delete(json_object_table, jso); +#endif /* REFCOUNT_DEBUG */ + printbuf_free(jso->_pb); + free(jso); +} + +static struct json_object* json_object_new(enum json_type o_type) +{ + struct json_object *jso; + + jso = (struct json_object*)calloc(sizeof(struct json_object), 1); + if (!jso) + return NULL; + jso->o_type = o_type; + jso->_ref_count = 1; + jso->_delete = &json_object_generic_delete; +#ifdef REFCOUNT_DEBUG + lh_table_insert(json_object_table, jso, jso); + MC_DEBUG("json_object_new_%s: %p\n", json_type_to_name(jso->o_type), jso); +#endif /* REFCOUNT_DEBUG */ + return jso; +} + + +/* type checking functions */ + +int json_object_is_type(const struct json_object *jso, enum json_type type) +{ + if (!jso) + return (type == json_type_null); + return (jso->o_type == type); +} + +enum json_type json_object_get_type(const struct json_object *jso) +{ + if (!jso) + return json_type_null; + return jso->o_type; +} + +void* json_object_get_userdata(json_object *jso) { + return jso ? jso->_userdata : NULL; +} + +void json_object_set_userdata(json_object *jso, void *userdata, + json_object_delete_fn *user_delete) +{ + // Can't return failure, so abort if we can't perform the operation. + assert(jso != NULL); + + // First, clean up any previously existing user info + if (jso->_user_delete) + jso->_user_delete(jso, jso->_userdata); + + jso->_userdata = userdata; + jso->_user_delete = user_delete; +} + +/* set a custom conversion to string */ + +void json_object_set_serializer(json_object *jso, + json_object_to_json_string_fn *to_string_func, + void *userdata, + json_object_delete_fn *user_delete) +{ + json_object_set_userdata(jso, userdata, user_delete); + + if (to_string_func == NULL) + { + // Reset to the standard serialization function + switch(jso->o_type) + { + case json_type_null: + jso->_to_json_string = NULL; + break; + case json_type_boolean: + jso->_to_json_string = &json_object_boolean_to_json_string; + break; + case json_type_double: + jso->_to_json_string = &json_object_double_to_json_string_default; + break; + case json_type_int: + jso->_to_json_string = &json_object_int_to_json_string; + break; + case json_type_object: + jso->_to_json_string = &json_object_object_to_json_string; + break; + case json_type_array: + jso->_to_json_string = &json_object_array_to_json_string; + break; + case json_type_string: + jso->_to_json_string = &json_object_string_to_json_string; + break; + } + return; + } + + jso->_to_json_string = to_string_func; +} + + +/* extended conversion to string */ + +const char* json_object_to_json_string_length(struct json_object *jso, int flags, size_t *length) +{ + const char *r = NULL; + size_t s = 0; + + if (!jso) + { + s = 4; + r = "null"; + } + else if ((jso->_pb) || (jso->_pb = printbuf_new())) + { + printbuf_reset(jso->_pb); + + if(jso->_to_json_string(jso, jso->_pb, 0, flags) >= 0) + { + s = (size_t)jso->_pb->bpos; + r = jso->_pb->buf; + } + } + + if (length) + *length = s; + return r; +} + +const char* json_object_to_json_string_ext(struct json_object *jso, int flags) +{ + return json_object_to_json_string_length(jso, flags, NULL); +} + +/* backwards-compatible conversion to string */ + +const char* json_object_to_json_string(struct json_object *jso) +{ + return json_object_to_json_string_ext(jso, JSON_C_TO_STRING_SPACED); +} + +static void indent(struct printbuf *pb, int level, int flags) +{ + if (flags & JSON_C_TO_STRING_PRETTY) + { + if (flags & JSON_C_TO_STRING_PRETTY_TAB) + { + printbuf_memset(pb, -1, '\t', level); + } + else + { + printbuf_memset(pb, -1, ' ', level * 2); + } + } +} + +/* json_object_object */ + +static int json_object_object_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + int had_children = 0; + struct json_object_iter iter; + + printbuf_strappend(pb, "{" /*}*/); + if (flags & JSON_C_TO_STRING_PRETTY) + printbuf_strappend(pb, "\n"); + json_object_object_foreachC(jso, iter) + { + if (had_children) + { + printbuf_strappend(pb, ","); + if (flags & JSON_C_TO_STRING_PRETTY) + printbuf_strappend(pb, "\n"); + } + had_children = 1; + if (flags & JSON_C_TO_STRING_SPACED) + printbuf_strappend(pb, " "); + indent(pb, level+1, flags); + printbuf_strappend(pb, "\""); + json_escape_str(pb, iter.key, strlen(iter.key), flags); + if (flags & JSON_C_TO_STRING_SPACED) + printbuf_strappend(pb, "\": "); + else + printbuf_strappend(pb, "\":"); + if(iter.val == NULL) + printbuf_strappend(pb, "null"); + else + if (iter.val->_to_json_string(iter.val, pb, level+1,flags) < 0) + return -1; + } + if (flags & JSON_C_TO_STRING_PRETTY) + { + if (had_children) + printbuf_strappend(pb, "\n"); + indent(pb,level,flags); + } + if (flags & JSON_C_TO_STRING_SPACED) + return printbuf_strappend(pb, /*{*/ " }"); + else + return printbuf_strappend(pb, /*{*/ "}"); +} + + +static void json_object_lh_entry_free(struct lh_entry *ent) +{ + if (!ent->k_is_constant) + free(lh_entry_k(ent)); + json_object_put((struct json_object*)lh_entry_v(ent)); +} + +static void json_object_object_delete(struct json_object* jso) +{ + lh_table_free(jso->o.c_object); + json_object_generic_delete(jso); +} + +struct json_object* json_object_new_object(void) +{ + struct json_object *jso = json_object_new(json_type_object); + if (!jso) + return NULL; + jso->_delete = &json_object_object_delete; + jso->_to_json_string = &json_object_object_to_json_string; + jso->o.c_object = lh_kchar_table_new(JSON_OBJECT_DEF_HASH_ENTRIES, + &json_object_lh_entry_free); + if (!jso->o.c_object) + { + json_object_generic_delete(jso); + errno = ENOMEM; + return NULL; + } + return jso; +} + +struct lh_table* json_object_get_object(const struct json_object *jso) +{ + if (!jso) + return NULL; + switch(jso->o_type) + { + case json_type_object: + return jso->o.c_object; + default: + return NULL; + } +} + +int json_object_object_add_ex(struct json_object* jso, + const char *const key, + struct json_object *const val, + const unsigned opts) +{ + struct json_object *existing_value = NULL; + struct lh_entry *existing_entry; + unsigned long hash; + + assert(json_object_get_type(jso) == json_type_object); + + // We lookup the entry and replace the value, rather than just deleting + // and re-adding it, so the existing key remains valid. + hash = lh_get_hash(jso->o.c_object, (const void *)key); + existing_entry = (opts & JSON_C_OBJECT_ADD_KEY_IS_NEW) ? NULL : + lh_table_lookup_entry_w_hash(jso->o.c_object, + (const void *)key, hash); + + // The caller must avoid creating loops in the object tree, but do a + // quick check anyway to make sure we're not creating a trivial loop. + if (jso == val) + return -1; + + if (!existing_entry) + { + const void *const k = (opts & JSON_C_OBJECT_KEY_IS_CONSTANT) ? + (const void *)key : strdup(key); + if (k == NULL) + return -1; + return lh_table_insert_w_hash(jso->o.c_object, k, val, hash, opts); + } + existing_value = (json_object *) lh_entry_v(existing_entry); + if (existing_value) + json_object_put(existing_value); + existing_entry->v = val; + return 0; +} + +int json_object_object_add(struct json_object* jso, const char *key, + struct json_object *val) +{ + return json_object_object_add_ex(jso, key, val, 0); +} + + +int json_object_object_length(const struct json_object *jso) +{ + assert(json_object_get_type(jso) == json_type_object); + return lh_table_length(jso->o.c_object); +} + +size_t json_c_object_sizeof(void) +{ + return sizeof(struct json_object); +} + +struct json_object* json_object_object_get(const struct json_object* jso, + const char *key) +{ + struct json_object *result = NULL; + json_object_object_get_ex(jso, key, &result); + return result; +} + +json_bool json_object_object_get_ex(const struct json_object* jso, const char *key, + struct json_object **value) +{ + if (value != NULL) + *value = NULL; + + if (NULL == jso) + return FALSE; + + switch(jso->o_type) + { + case json_type_object: + return lh_table_lookup_ex(jso->o.c_object, (const void *) key, + (void**) value); + default: + if (value != NULL) + *value = NULL; + return FALSE; + } +} + +void json_object_object_del(struct json_object* jso, const char *key) +{ + assert(json_object_get_type(jso) == json_type_object); + lh_table_delete(jso->o.c_object, key); +} + + +/* json_object_boolean */ + +static int json_object_boolean_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + if (jso->o.c_boolean) + return printbuf_strappend(pb, "true"); + return printbuf_strappend(pb, "false"); +} + +struct json_object* json_object_new_boolean(json_bool b) +{ + struct json_object *jso = json_object_new(json_type_boolean); + if (!jso) + return NULL; + jso->_to_json_string = &json_object_boolean_to_json_string; + jso->o.c_boolean = b; + return jso; +} + +json_bool json_object_get_boolean(const struct json_object *jso) +{ + if (!jso) + return FALSE; + switch(jso->o_type) + { + case json_type_boolean: + return jso->o.c_boolean; + case json_type_int: + return (jso->o.c_int64 != 0); + case json_type_double: + return (jso->o.c_double != 0); + case json_type_string: + return (jso->o.c_string.len != 0); + default: + return FALSE; + } +} + +int json_object_set_boolean(struct json_object *jso,json_bool new_value){ + if (!jso || jso->o_type!=json_type_boolean) + return 0; + jso->o.c_boolean=new_value; + return 1; +} + + +/* json_object_int */ + +static int json_object_int_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + /* room for 19 digits, the sign char, and a null term */ + char sbuf[21]; + snprintf(sbuf, sizeof(sbuf), "%" PRId64, jso->o.c_int64); + return printbuf_memappend (pb, sbuf, strlen(sbuf)); +} + +struct json_object* json_object_new_int(int32_t i) +{ + struct json_object *jso = json_object_new(json_type_int); + if (!jso) + return NULL; + jso->_to_json_string = &json_object_int_to_json_string; + jso->o.c_int64 = i; + return jso; +} + +int32_t json_object_get_int(const struct json_object *jso) +{ + int64_t cint64; + enum json_type o_type; + + if(!jso) return 0; + + o_type = jso->o_type; + cint64 = jso->o.c_int64; + + if (o_type == json_type_string) + { + /* + * Parse strings into 64-bit numbers, then use the + * 64-to-32-bit number handling below. + */ + if (json_parse_int64(get_string_component(jso), &cint64) != 0) + return 0; /* whoops, it didn't work. */ + o_type = json_type_int; + } + + switch(o_type) { + case json_type_int: + /* Make sure we return the correct values for out of range numbers. */ + if (cint64 <= INT32_MIN) + return INT32_MIN; + if (cint64 >= INT32_MAX) + return INT32_MAX; + return (int32_t) cint64; + case json_type_double: + if (jso->o.c_double <= INT32_MIN) + return INT32_MIN; + if (jso->o.c_double >= INT32_MAX) + return INT32_MAX; + return (int32_t)jso->o.c_double; + case json_type_boolean: + return jso->o.c_boolean; + default: + return 0; + } +} + +int json_object_set_int(struct json_object *jso,int new_value){ + if (!jso || jso->o_type!=json_type_int) + return 0; + jso->o.c_int64=new_value; + return 1; +} + +struct json_object* json_object_new_int64(int64_t i) +{ + struct json_object *jso = json_object_new(json_type_int); + if (!jso) + return NULL; + jso->_to_json_string = &json_object_int_to_json_string; + jso->o.c_int64 = i; + return jso; +} + +int64_t json_object_get_int64(const struct json_object *jso) +{ + int64_t cint; + + if (!jso) + return 0; + switch(jso->o_type) + { + case json_type_int: + return jso->o.c_int64; + case json_type_double: + if (jso->o.c_double >= INT64_MAX) + return INT64_MAX; + if (jso->o.c_double <= INT64_MIN) + return INT64_MIN; + return (int64_t)jso->o.c_double; + case json_type_boolean: + return jso->o.c_boolean; + case json_type_string: + if (json_parse_int64(get_string_component(jso), &cint) == 0) + return cint; + /* FALLTHRU */ + default: + return 0; + } +} + +int json_object_set_int64(struct json_object *jso,int64_t new_value){ + if (!jso || jso->o_type!=json_type_int) + return 0; + jso->o.c_int64=new_value; + return 1; +} + +int json_object_int_inc(struct json_object *jso, int64_t val) { + if (!jso || jso->o_type != json_type_int) + return 0; + if (val > 0 && jso->o.c_int64 > INT64_MAX - val) { + jso->o.c_int64 = INT64_MAX; + } else if (val < 0 && jso->o.c_int64 < INT64_MIN - val) { + jso->o.c_int64 = INT64_MIN; + } else { + jso->o.c_int64 += val; + } + return 1; +} + +/* json_object_double */ + +#if defined(HAVE___THREAD) +// i.e. __thread or __declspec(thread) +static SPEC___THREAD char *tls_serialization_float_format = NULL; +#endif +static char *global_serialization_float_format = NULL; + +int json_c_set_serialization_double_format(const char *double_format, int global_or_thread) +{ + if (global_or_thread == JSON_C_OPTION_GLOBAL) + { +#if defined(HAVE___THREAD) + if (tls_serialization_float_format) + { + free(tls_serialization_float_format); + tls_serialization_float_format = NULL; + } +#endif + if (global_serialization_float_format) + free(global_serialization_float_format); + global_serialization_float_format = double_format ? strdup(double_format) : NULL; + } + else if (global_or_thread == JSON_C_OPTION_THREAD) + { +#if defined(HAVE___THREAD) + if (tls_serialization_float_format) + { + free(tls_serialization_float_format); + tls_serialization_float_format = NULL; + } + tls_serialization_float_format = double_format ? strdup(double_format) : NULL; +#else + _json_c_set_last_err("json_c_set_option: not compiled with __thread support\n"); + return -1; +#endif + } + else + { + _json_c_set_last_err("json_c_set_option: invalid global_or_thread value: %d\n", global_or_thread); + return -1; + } + return 0; +} + + +static int json_object_double_to_json_string_format(struct json_object* jso, + struct printbuf *pb, + int level, + int flags, + const char *format) +{ + char buf[128], *p, *q; + int size; + /* Although JSON RFC does not support + NaN or Infinity as numeric values + ECMA 262 section 9.8.1 defines + how to handle these cases as strings */ + if (isnan(jso->o.c_double)) + { + size = snprintf(buf, sizeof(buf), "NaN"); + } + else if (isinf(jso->o.c_double)) + { + if(jso->o.c_double > 0) + size = snprintf(buf, sizeof(buf), "Infinity"); + else + size = snprintf(buf, sizeof(buf), "-Infinity"); + } + else + { + const char *std_format = "%.17g"; + int format_drops_decimals = 0; + + if (!format) + { +#if defined(HAVE___THREAD) + if (tls_serialization_float_format) + format = tls_serialization_float_format; + else +#endif + if (global_serialization_float_format) + format = global_serialization_float_format; + else + format = std_format; + } + size = snprintf(buf, sizeof(buf), format, jso->o.c_double); + + if (size < 0) + return -1; + + p = strchr(buf, ','); + if (p) + *p = '.'; + else + p = strchr(buf, '.'); + + if (format == std_format || strstr(format, ".0f") == NULL) + format_drops_decimals = 1; + + if (size < (int)sizeof(buf) - 2 && + isdigit((int)buf[0]) && /* Looks like *some* kind of number */ + !p && /* Has no decimal point */ + strchr(buf, 'e') == NULL && /* Not scientific notation */ + format_drops_decimals) + { + // Ensure it looks like a float, even if snprintf didn't, + // unless a custom format is set to omit the decimal. + strcat(buf, ".0"); + size += 2; + } + if (p && (flags & JSON_C_TO_STRING_NOZERO)) + { + /* last useful digit, always keep 1 zero */ + p++; + for (q=p ; *q ; q++) { + if (*q!='0') p=q; + } + /* drop trailing zeroes */ + *(++p) = 0; + size = p-buf; + } + } + // although unlikely, snprintf can fail + if (size < 0) + return -1; + + if (size >= (int)sizeof(buf)) + // The standard formats are guaranteed not to overrun the buffer, + // but if a custom one happens to do so, just silently truncate. + size = sizeof(buf) - 1; + printbuf_memappend(pb, buf, size); + return size; +} + +static int json_object_double_to_json_string_default(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + return json_object_double_to_json_string_format(jso, pb, level, flags, + NULL); +} + +int json_object_double_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + return json_object_double_to_json_string_format(jso, pb, level, flags, + (const char *)jso->_userdata); +} + +struct json_object* json_object_new_double(double d) +{ + struct json_object *jso = json_object_new(json_type_double); + if (!jso) + return NULL; + jso->_to_json_string = &json_object_double_to_json_string_default; + jso->o.c_double = d; + return jso; +} + +struct json_object* json_object_new_double_s(double d, const char *ds) +{ + char *new_ds; + struct json_object *jso = json_object_new_double(d); + if (!jso) + return NULL; + + new_ds = strdup(ds); + if (!new_ds) + { + json_object_generic_delete(jso); + errno = ENOMEM; + return NULL; + } + json_object_set_serializer(jso, json_object_userdata_to_json_string, + new_ds, json_object_free_userdata); + return jso; +} + +int json_object_userdata_to_json_string(struct json_object *jso, + struct printbuf *pb, int level, int flags) +{ + int userdata_len = strlen((const char *)jso->_userdata); + printbuf_memappend(pb, (const char *)jso->_userdata, userdata_len); + return userdata_len; +} + +void json_object_free_userdata(struct json_object *jso, void *userdata) +{ + free(userdata); +} + +double json_object_get_double(const struct json_object *jso) +{ + double cdouble; + char *errPtr = NULL; + + if(!jso) return 0.0; + switch(jso->o_type) { + case json_type_double: + return jso->o.c_double; + case json_type_int: + return jso->o.c_int64; + case json_type_boolean: + return jso->o.c_boolean; + case json_type_string: + errno = 0; + cdouble = strtod(get_string_component(jso), &errPtr); + + /* if conversion stopped at the first character, return 0.0 */ + if (errPtr == get_string_component(jso)) + return 0.0; + + /* + * Check that the conversion terminated on something sensible + * + * For example, { "pay" : 123AB } would parse as 123. + */ + if (*errPtr != '\0') + return 0.0; + + /* + * If strtod encounters a string which would exceed the + * capacity of a double, it returns +/- HUGE_VAL and sets + * errno to ERANGE. But +/- HUGE_VAL is also a valid result + * from a conversion, so we need to check errno. + * + * Underflow also sets errno to ERANGE, but it returns 0 in + * that case, which is what we will return anyway. + * + * See CERT guideline ERR30-C + */ + if ((HUGE_VAL == cdouble || -HUGE_VAL == cdouble) && + (ERANGE == errno)) + cdouble = 0.0; + return cdouble; + default: + return 0.0; + } +} + +int json_object_set_double(struct json_object *jso,double new_value){ + if (!jso || jso->o_type!=json_type_double) + return 0; + jso->o.c_double=new_value; + return 1; +} + +/* json_object_string */ + +static int json_object_string_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + printbuf_strappend(pb, "\""); + json_escape_str(pb, get_string_component(jso), jso->o.c_string.len, flags); + printbuf_strappend(pb, "\""); + return 0; +} + +static void json_object_string_delete(struct json_object* jso) +{ + if(jso->o.c_string.len >= LEN_DIRECT_STRING_DATA) + free(jso->o.c_string.str.ptr); + json_object_generic_delete(jso); +} + +struct json_object* json_object_new_string(const char *s) +{ + struct json_object *jso = json_object_new(json_type_string); + if (!jso) + return NULL; + jso->_delete = &json_object_string_delete; + jso->_to_json_string = &json_object_string_to_json_string; + jso->o.c_string.len = strlen(s); + if(jso->o.c_string.len < LEN_DIRECT_STRING_DATA) { + memcpy(jso->o.c_string.str.data, s, jso->o.c_string.len); + } else { + jso->o.c_string.str.ptr = strdup(s); + if (!jso->o.c_string.str.ptr) + { + json_object_generic_delete(jso); + errno = ENOMEM; + return NULL; + } + } + return jso; +} + +struct json_object* json_object_new_string_len(const char *s, int len) +{ + char *dstbuf; + struct json_object *jso = json_object_new(json_type_string); + if (!jso) + return NULL; + jso->_delete = &json_object_string_delete; + jso->_to_json_string = &json_object_string_to_json_string; + if(len < LEN_DIRECT_STRING_DATA) { + dstbuf = jso->o.c_string.str.data; + } else { + jso->o.c_string.str.ptr = (char*)malloc(len + 1); + if (!jso->o.c_string.str.ptr) + { + json_object_generic_delete(jso); + errno = ENOMEM; + return NULL; + } + dstbuf = jso->o.c_string.str.ptr; + } + memcpy(dstbuf, (const void *)s, len); + dstbuf[len] = '\0'; + jso->o.c_string.len = len; + return jso; +} + +const char* json_object_get_string(struct json_object *jso) +{ + if (!jso) + return NULL; + switch(jso->o_type) + { + case json_type_string: + return get_string_component(jso); + default: + return json_object_to_json_string(jso); + } +} + +int json_object_get_string_len(const struct json_object *jso) +{ + if (!jso) + return 0; + switch(jso->o_type) + { + case json_type_string: + return jso->o.c_string.len; + default: + return 0; + } +} + +int json_object_set_string(json_object* jso, const char* s) { + return json_object_set_string_len(jso, s, (int)(strlen(s))); +} + +int json_object_set_string_len(json_object* jso, const char* s, int len){ + char *dstbuf; + if (jso==NULL || jso->o_type!=json_type_string) return 0; + if (leno.c_string.str.data; + if (jso->o.c_string.len>=LEN_DIRECT_STRING_DATA) free(jso->o.c_string.str.ptr); + } else { + dstbuf=(char *)malloc(len+1); + if (dstbuf==NULL) return 0; + if (jso->o.c_string.len>=LEN_DIRECT_STRING_DATA) free(jso->o.c_string.str.ptr); + jso->o.c_string.str.ptr=dstbuf; + } + jso->o.c_string.len=len; + memcpy(dstbuf, (const void *)s, len); + dstbuf[len] = '\0'; + return 1; +} + +/* json_object_array */ + +static int json_object_array_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + int had_children = 0; + size_t ii; + + printbuf_strappend(pb, "["); + if (flags & JSON_C_TO_STRING_PRETTY) + printbuf_strappend(pb, "\n"); + for(ii=0; ii < json_object_array_length(jso); ii++) + { + struct json_object *val; + if (had_children) + { + printbuf_strappend(pb, ","); + if (flags & JSON_C_TO_STRING_PRETTY) + printbuf_strappend(pb, "\n"); + } + had_children = 1; + if (flags & JSON_C_TO_STRING_SPACED) + printbuf_strappend(pb, " "); + indent(pb, level + 1, flags); + val = json_object_array_get_idx(jso, ii); + if(val == NULL) + printbuf_strappend(pb, "null"); + else + if (val->_to_json_string(val, pb, level+1, flags) < 0) + return -1; + } + if (flags & JSON_C_TO_STRING_PRETTY) + { + if (had_children) + printbuf_strappend(pb, "\n"); + indent(pb,level,flags); + } + + if (flags & JSON_C_TO_STRING_SPACED) + return printbuf_strappend(pb, " ]"); + return printbuf_strappend(pb, "]"); +} + +static void json_object_array_entry_free(void *data) +{ + json_object_put((struct json_object*)data); +} + +static void json_object_array_delete(struct json_object* jso) +{ + array_list_free(jso->o.c_array); + json_object_generic_delete(jso); +} + +struct json_object* json_object_new_array(void) +{ + struct json_object *jso = json_object_new(json_type_array); + if (!jso) + return NULL; + jso->_delete = &json_object_array_delete; + jso->_to_json_string = &json_object_array_to_json_string; + jso->o.c_array = array_list_new(&json_object_array_entry_free); + if(jso->o.c_array == NULL) + { + free(jso); + return NULL; + } + return jso; +} + +struct array_list* json_object_get_array(const struct json_object *jso) +{ + if (!jso) + return NULL; + switch(jso->o_type) + { + case json_type_array: + return jso->o.c_array; + default: + return NULL; + } +} + +void json_object_array_sort(struct json_object *jso, + int(*sort_fn)(const void *, const void *)) +{ + assert(json_object_get_type(jso) == json_type_array); + array_list_sort(jso->o.c_array, sort_fn); +} + +struct json_object* json_object_array_bsearch( + const struct json_object *key, + const struct json_object *jso, + int (*sort_fn)(const void *, const void *)) +{ + struct json_object **result; + + assert(json_object_get_type(jso) == json_type_array); + result = (struct json_object **)array_list_bsearch( + (const void **)(void *)&key, jso->o.c_array, sort_fn); + + if (!result) + return NULL; + return *result; +} + +size_t json_object_array_length(const struct json_object *jso) +{ + assert(json_object_get_type(jso) == json_type_array); + return array_list_length(jso->o.c_array); +} + +int json_object_array_add(struct json_object *jso,struct json_object *val) +{ + assert(json_object_get_type(jso) == json_type_array); + return array_list_add(jso->o.c_array, val); +} + +int json_object_array_put_idx(struct json_object *jso, size_t idx, + struct json_object *val) +{ + assert(json_object_get_type(jso) == json_type_array); + return array_list_put_idx(jso->o.c_array, idx, val); +} + +int json_object_array_del_idx(struct json_object *jso, size_t idx, size_t count) +{ + assert(json_object_get_type(jso) == json_type_array); + return array_list_del_idx(jso->o.c_array, idx, count); +} + +struct json_object* json_object_array_get_idx(const struct json_object *jso, + size_t idx) +{ + assert(json_object_get_type(jso) == json_type_array); + return (struct json_object*)array_list_get_idx(jso->o.c_array, idx); +} + +static int json_array_equal(struct json_object* jso1, + struct json_object* jso2) +{ + size_t len, i; + + len = json_object_array_length(jso1); + if (len != json_object_array_length(jso2)) + return 0; + + for (i = 0; i < len; i++) { + if (!json_object_equal(json_object_array_get_idx(jso1, i), + json_object_array_get_idx(jso2, i))) + return 0; + } + return 1; +} + +static int json_object_all_values_equal(struct json_object* jso1, + struct json_object* jso2) +{ + struct json_object_iter iter; + struct json_object *sub; + + assert(json_object_get_type(jso1) == json_type_object); + assert(json_object_get_type(jso2) == json_type_object); + /* Iterate over jso1 keys and see if they exist and are equal in jso2 */ + json_object_object_foreachC(jso1, iter) { + if (!lh_table_lookup_ex(jso2->o.c_object, (void*)iter.key, + (void**)(void *)&sub)) + return 0; + if (!json_object_equal(iter.val, sub)) + return 0; + } + + /* Iterate over jso2 keys to see if any exist that are not in jso1 */ + json_object_object_foreachC(jso2, iter) { + if (!lh_table_lookup_ex(jso1->o.c_object, (void*)iter.key, + (void**)(void *)&sub)) + return 0; + } + + return 1; +} + +int json_object_equal(struct json_object* jso1, struct json_object* jso2) +{ + if (jso1 == jso2) + return 1; + + if (!jso1 || !jso2) + return 0; + + if (jso1->o_type != jso2->o_type) + return 0; + + switch(jso1->o_type) { + case json_type_boolean: + return (jso1->o.c_boolean == jso2->o.c_boolean); + + case json_type_double: + return (jso1->o.c_double == jso2->o.c_double); + + case json_type_int: + return (jso1->o.c_int64 == jso2->o.c_int64); + + case json_type_string: + return (jso1->o.c_string.len == jso2->o.c_string.len && + memcmp(get_string_component(jso1), + get_string_component(jso2), + jso1->o.c_string.len) == 0); + + case json_type_object: + return json_object_all_values_equal(jso1, jso2); + + case json_type_array: + return json_array_equal(jso1, jso2); + + case json_type_null: + return 1; + }; + + return 0; +} + +static int json_object_copy_serializer_data(struct json_object *src, struct json_object *dst) +{ + if (!src->_userdata && !src->_user_delete) + return 0; + + if (dst->_to_json_string == json_object_userdata_to_json_string) + { + dst->_userdata = strdup(src->_userdata); + } + // else if ... other supported serializers ... + else + { + _json_c_set_last_err("json_object_deep_copy: unable to copy unknown serializer data: %p\n", dst->_to_json_string); + return -1; + } + dst->_user_delete = src->_user_delete; + return 0; +} + + +/** + * The default shallow copy implementation. Simply creates a new object of the same + * type but does *not* copy over _userdata nor retain any custom serializer. + * If custom serializers are in use, json_object_deep_copy() must be passed a shallow copy + * implementation that is aware of how to copy them. + * + * This always returns -1 or 1. It will never return 2 since it does not copy the serializer. + */ +int json_c_shallow_copy_default(json_object *src, json_object *parent, const char *key, size_t index, json_object **dst) +{ + switch (src->o_type) { + case json_type_boolean: + *dst = json_object_new_boolean(src->o.c_boolean); + break; + + case json_type_double: + *dst = json_object_new_double(src->o.c_double); + break; + + case json_type_int: + *dst = json_object_new_int64(src->o.c_int64); + break; + + case json_type_string: + *dst = json_object_new_string(get_string_component(src)); + break; + + case json_type_object: + *dst = json_object_new_object(); + break; + + case json_type_array: + *dst = json_object_new_array(); + break; + + default: + errno = EINVAL; + return -1; + } + + if (!*dst) { + errno = ENOMEM; + return -1; + } + (*dst)->_to_json_string = src->_to_json_string; + // _userdata and _user_delete are copied later + return 1; +} + +/* + * The actual guts of json_object_deep_copy(), with a few additional args + * needed so we can keep track of where we are within the object tree. + * + * Note: caller is responsible for freeing *dst if this fails and returns -1. + */ +static int json_object_deep_copy_recursive(struct json_object *src, struct json_object *parent, const char *key_in_parent, size_t index_in_parent, struct json_object **dst, json_c_shallow_copy_fn *shallow_copy) +{ + struct json_object_iter iter; + size_t src_array_len, ii; + + int shallow_copy_rc = 0; + shallow_copy_rc = shallow_copy(src, parent, key_in_parent, index_in_parent, dst); + /* -1=error, 1=object created ok, 2=userdata set */ + if (shallow_copy_rc < 1) + { + errno = EINVAL; + return -1; + } + assert(*dst != NULL); + + switch (src->o_type) { + case json_type_object: + json_object_object_foreachC(src, iter) { + struct json_object *jso = NULL; + /* This handles the `json_type_null` case */ + if (!iter.val) + jso = NULL; + else if (json_object_deep_copy_recursive(iter.val, src, iter.key, -1, &jso, shallow_copy) < 0) + { + json_object_put(jso); + return -1; + } + + if (json_object_object_add(*dst, iter.key, jso) < 0) + { + json_object_put(jso); + return -1; + } + } + break; + + case json_type_array: + src_array_len = json_object_array_length(src); + for (ii = 0; ii < src_array_len; ii++) { + struct json_object *jso = NULL; + struct json_object *jso1 = json_object_array_get_idx(src, ii); + /* This handles the `json_type_null` case */ + if (!jso1) + jso = NULL; + else if (json_object_deep_copy_recursive(jso1, src, NULL, ii, &jso, shallow_copy) < 0) + { + json_object_put(jso); + return -1; + } + + if (json_object_array_add(*dst, jso) < 0) + { + json_object_put(jso); + return -1; + } + } + break; + + default: + break; + /* else, nothing to do, shallow_copy already did. */ + } + + if (shallow_copy_rc != 2) + return json_object_copy_serializer_data(src, *dst); + + return 0; +} + +int json_object_deep_copy(struct json_object *src, struct json_object **dst, json_c_shallow_copy_fn *shallow_copy) +{ + int rc; + + /* Check if arguments are sane ; *dst must not point to a non-NULL object */ + if (!src || !dst || *dst) { + errno = EINVAL; + return -1; + } + + if (shallow_copy == NULL) + shallow_copy = json_c_shallow_copy_default; + + rc = json_object_deep_copy_recursive(src, NULL, NULL, -1, dst, shallow_copy); + if (rc < 0) { + json_object_put(*dst); + *dst = NULL; + } + + return rc; +} + diff --git a/json-c/json_object.h b/json-c/json_object.h new file mode 100644 index 0000000..073d80c --- /dev/null +++ b/json-c/json_object.h @@ -0,0 +1,1034 @@ +/* + * $Id: json_object.h,v 1.12 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief Core json-c API. Start here, or with json_tokener.h + */ +#ifndef _json_object_h_ +#define _json_object_h_ + +#ifdef __GNUC__ +#define THIS_FUNCTION_IS_DEPRECATED(func) func __attribute__ ((deprecated)) +#elif defined(_MSC_VER) +#define THIS_FUNCTION_IS_DEPRECATED(func) __declspec(deprecated) func +#elif defined(__clang__) +#define THIS_FUNCTION_IS_DEPRECATED(func) func __deprecated +#else +#define THIS_FUNCTION_IS_DEPRECATED(func) func +#endif + +#ifdef __GNUC__ +#define JSON_C_CONST_FUNCTION(func) func __attribute__((const)) +#else +#define JSON_C_CONST_FUNCTION(func) func +#endif + +#if defined(_MSC_VER) +#define JSON_EXPORT __declspec(dllexport) +#else +#define JSON_EXPORT extern +#endif + +#include +#include "json_inttypes.h" +#include "printbuf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define JSON_OBJECT_DEF_HASH_ENTRIES 16 + +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes the output + * to have no extra whitespace or formatting applied. + */ +#define JSON_C_TO_STRING_PLAIN 0 +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes the output to have + * minimal whitespace inserted to make things slightly more readable. + */ +#define JSON_C_TO_STRING_SPACED (1<<0) +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes + * the output to be formatted. + * + * See the "Two Space Tab" option at http://jsonformatter.curiousconcept.com/ + * for an example of the format. + */ +#define JSON_C_TO_STRING_PRETTY (1<<1) +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes + * the output to be formatted. + * + * Instead of a "Two Space Tab" this gives a single tab character. + */ +#define JSON_C_TO_STRING_PRETTY_TAB (1<<3) +/** + * A flag to drop trailing zero for float values + */ +#define JSON_C_TO_STRING_NOZERO (1<<2) + +/** + * Don't escape forward slashes. + */ +#define JSON_C_TO_STRING_NOSLASHESCAPE (1<<4) + +/** + * A flag for the json_object_object_add_ex function which + * causes the value to be added without a check if it already exists. + * Note: it is the responsibilty of the caller to ensure that no + * key is added multiple times. If this is done, results are + * unpredictable. While this option is somewhat dangerous, it + * permits potentially large performance savings in code that + * knows for sure the key values are unique (e.g. because the + * code adds a well-known set of constant key values). + */ +#define JSON_C_OBJECT_ADD_KEY_IS_NEW (1<<1) +/** + * A flag for the json_object_object_add_ex function which + * flags the key as being constant memory. This means that + * the key will NOT be copied via strdup(), resulting in a + * potentially huge performance win (malloc, strdup and + * free are usually performance hogs). It is acceptable to + * use this flag for keys in non-constant memory blocks if + * the caller ensure that the memory holding the key lives + * longer than the corresponding json object. However, this + * is somewhat dangerous and should only be done if really + * justified. + * The general use-case for this flag is cases where the + * key is given as a real constant value in the function + * call, e.g. as in + * json_object_object_add_ex(obj, "ip", json, + * JSON_C_OBJECT_KEY_IS_CONSTANT); + */ +#define JSON_C_OBJECT_KEY_IS_CONSTANT (1<<2) + +#undef FALSE +#define FALSE ((json_bool)0) + +#undef TRUE +#define TRUE ((json_bool)1) + +/** + * Set the global value of an option, which will apply to all + * current and future threads that have not set a thread-local value. + * + * @see json_c_set_serialization_double_format + */ +#define JSON_C_OPTION_GLOBAL (0) +/** + * Set a thread-local value of an option, overriding the global value. + * This will fail if json-c is not compiled with threading enabled, and + * with the __thread specifier (or equivalent) available. + * + * @see json_c_set_serialization_double_format + */ +#define JSON_C_OPTION_THREAD (1) + +/** + * A structure to use with json_object_object_foreachC() loops. + * Contains key, val and entry members. + */ +struct json_object_iter +{ + char *key; + struct json_object *val; + struct lh_entry *entry; +}; +typedef struct json_object_iter json_object_iter; + +typedef int json_bool; + +/** + * @brief The core type for all type of JSON objects handled by json-c + */ +typedef struct json_object json_object; + +/** + * Type of custom user delete functions. See json_object_set_serializer. + */ +typedef void (json_object_delete_fn)(struct json_object *jso, void *userdata); + +/** + * Type of a custom serialization function. See json_object_set_serializer. + */ +typedef int (json_object_to_json_string_fn)(struct json_object *jso, + struct printbuf *pb, + int level, + int flags); + +/* supported object types */ + +typedef enum json_type { + /* If you change this, be sure to update json_type_to_name() too */ + json_type_null, + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string +} json_type; + +/* reference counting functions */ + +/** + * Increment the reference count of json_object, thereby grabbing shared + * ownership of obj. + * + * @param obj the json_object instance + */ +JSON_EXPORT struct json_object* json_object_get(struct json_object *obj); + +/** + * Decrement the reference count of json_object and free if it reaches zero. + * You must have ownership of obj prior to doing this or you will cause an + * imbalance in the reference count. + * + * @param obj the json_object instance + * @returns 1 if the object was freed. + */ +JSON_EXPORT int json_object_put(struct json_object *obj); + +/** + * Check if the json_object is of a given type + * @param obj the json_object instance + * @param type one of: + json_type_null (i.e. obj == NULL), + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string + */ +JSON_EXPORT int json_object_is_type(const struct json_object *obj, enum json_type type); + +/** + * Get the type of the json_object. See also json_type_to_name() to turn this + * into a string suitable, for instance, for logging. + * + * @param obj the json_object instance + * @returns type being one of: + json_type_null (i.e. obj == NULL), + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string + */ +JSON_EXPORT enum json_type json_object_get_type(const struct json_object *obj); + + +/** Stringify object to json format. + * Equivalent to json_object_to_json_string_ext(obj, JSON_C_TO_STRING_SPACED) + * The pointer you get is an internal of your json object. You don't + * have to free it, later use of json_object_put() should be sufficient. + * If you can not ensure there's no concurrent access to *obj use + * strdup(). + * @param obj the json_object instance + * @returns a string in JSON format + */ +JSON_EXPORT const char* json_object_to_json_string(struct json_object *obj); + +/** Stringify object to json format + * @see json_object_to_json_string() for details on how to free string. + * @param obj the json_object instance + * @param flags formatting options, see JSON_C_TO_STRING_PRETTY and other constants + * @returns a string in JSON format + */ +JSON_EXPORT const char* json_object_to_json_string_ext(struct json_object *obj, int +flags); + +/** Stringify object to json format + * @see json_object_to_json_string() for details on how to free string. + * @param obj the json_object instance + * @param flags formatting options, see JSON_C_TO_STRING_PRETTY and other constants + * @param length a pointer where, if not NULL, the length (without null) is stored + * @returns a string in JSON format and the length if not NULL + */ +JSON_EXPORT const char* json_object_to_json_string_length(struct json_object *obj, int +flags, size_t *length); + +/** + * Returns the userdata set by json_object_set_userdata() or + * json_object_set_serializer() + * + * @param jso the object to return the userdata for + */ +JSON_EXPORT void* json_object_get_userdata(json_object *jso); + +/** + * Set an opaque userdata value for an object + * + * The userdata can be retrieved using json_object_get_userdata(). + * + * If custom userdata is already set on this object, any existing user_delete + * function is called before the new one is set. + * + * The user_delete parameter is optional and may be passed as NULL, even if + * the userdata parameter is non-NULL. It will be called just before the + * json_object is deleted, after it's reference count goes to zero + * (see json_object_put()). + * If this is not provided, it is up to the caller to free the userdata at + * an appropriate time. (i.e. after the json_object is deleted) + * + * Note: Objects created by parsing strings may have custom serializers set + * which expect the userdata to contain specific data (due to use of + * json_object_new_double_s()). In this case, json_object_set_serialiser() with + * NULL as to_string_func should be used instead to set the userdata and reset + * the serializer to its default value. + * + * @param jso the object to set the userdata for + * @param userdata an optional opaque cookie + * @param user_delete an optional function from freeing userdata + */ +JSON_EXPORT void json_object_set_userdata(json_object *jso, void *userdata, + json_object_delete_fn *user_delete); + +/** + * Set a custom serialization function to be used when this particular object + * is converted to a string by json_object_to_json_string. + * + * If custom userdata is already set on this object, any existing user_delete + * function is called before the new one is set. + * + * If to_string_func is NULL the default behaviour is reset (but the userdata + * and user_delete fields are still set). + * + * The userdata parameter is optional and may be passed as NULL. It can be used + * to provide additional data for to_string_func to use. This parameter may + * be NULL even if user_delete is non-NULL. + * + * The user_delete parameter is optional and may be passed as NULL, even if + * the userdata parameter is non-NULL. It will be called just before the + * json_object is deleted, after it's reference count goes to zero + * (see json_object_put()). + * If this is not provided, it is up to the caller to free the userdata at + * an appropriate time. (i.e. after the json_object is deleted) + * + * Note that the userdata is the same as set by json_object_set_userdata(), so + * care must be taken not to overwrite the value when both a custom serializer + * and json_object_set_userdata() are used. + * + * @param jso the object to customize + * @param to_string_func the custom serialization function + * @param userdata an optional opaque cookie + * @param user_delete an optional function from freeing userdata + */ +JSON_EXPORT void json_object_set_serializer(json_object *jso, + json_object_to_json_string_fn *to_string_func, + void *userdata, + json_object_delete_fn *user_delete); + +#ifdef __clang__ +/* + * Clang doesn't pay attention to the parameters defined in the + * function typedefs used here, so turn off spurious doc warnings. + * { + */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdocumentation" +#endif + +/** + * Simply call free on the userdata pointer. + * Can be used with json_object_set_serializer(). + * + * @param jso unused + * @param userdata the pointer that is passed to free(). + */ +json_object_delete_fn json_object_free_userdata; + +/** + * Copy the jso->_userdata string over to pb as-is. + * Can be used with json_object_set_serializer(). + * + * @param jso The object whose _userdata is used. + * @param pb The destination buffer. + * @param level Ignored. + * @param flags Ignored. + */ +json_object_to_json_string_fn json_object_userdata_to_json_string; + +#ifdef __clang__ +/* } */ +#pragma clang diagnostic pop +#endif + + +/* object type methods */ + +/** Create a new empty object with a reference count of 1. The caller of + * this object initially has sole ownership. Remember, when using + * json_object_object_add or json_object_array_put_idx, ownership will + * transfer to the object/array. Call json_object_get if you want to maintain + * shared ownership or also add this object as a child of multiple objects or + * arrays. Any ownerships you acquired but did not transfer must be released + * through json_object_put. + * + * @returns a json_object of type json_type_object + */ +JSON_EXPORT struct json_object* json_object_new_object(void); + +/** Get the hashtable of a json_object of type json_type_object + * @param obj the json_object instance + * @returns a linkhash + */ +JSON_EXPORT struct lh_table* json_object_get_object(const struct json_object *obj); + +/** Get the size of an object in terms of the number of fields it has. + * @param obj the json_object whose length to return + */ +JSON_EXPORT int json_object_object_length(const struct json_object* obj); + +/** Get the sizeof (struct json_object). + * @returns a size_t with the sizeof (struct json_object) + */ +JSON_C_CONST_FUNCTION(JSON_EXPORT size_t json_c_object_sizeof(void)); + +/** Add an object field to a json_object of type json_type_object + * + * The reference count will *not* be incremented. This is to make adding + * fields to objects in code more compact. If you want to retain a reference + * to an added object, independent of the lifetime of obj, you must wrap the + * passed object with json_object_get. + * + * Upon calling this, the ownership of val transfers to obj. Thus you must + * make sure that you do in fact have ownership over this object. For instance, + * json_object_new_object will give you ownership until you transfer it, + * whereas json_object_object_get does not. + * + * @param obj the json_object instance + * @param key the object field name (a private copy will be duplicated) + * @param val a json_object or NULL member to associate with the given field + * + * @return On success, 0 is returned. + * On error, a negative value is returned. + */ +JSON_EXPORT int json_object_object_add(struct json_object* obj, const char *key, + struct json_object *val); + +/** Add an object field to a json_object of type json_type_object + * + * The semantics are identical to json_object_object_add, except that an + * additional flag fields gives you more control over some detail aspects + * of processing. See the description of JSON_C_OBJECT_ADD_* flags for more + * details. + * + * @param obj the json_object instance + * @param key the object field name (a private copy will be duplicated) + * @param val a json_object or NULL member to associate with the given field + * @param opts process-modifying options. To specify multiple options, use + * arithmetic or (OPT1|OPT2) + */ +JSON_EXPORT int json_object_object_add_ex(struct json_object* obj, + const char *const key, + struct json_object *const val, + const unsigned opts); + +/** Get the json_object associate with a given object field. + * Deprecated/discouraged: used json_object_object_get_ex instead. + * + * This returns NULL if the field is found but its value is null, or if + * the field is not found, or if obj is not a json_type_object. If you + * need to distinguis between these cases, use json_object_object_get_ex(). + * + * *No* reference counts will be changed. There is no need to manually adjust + * reference counts through the json_object_put/json_object_get methods unless + * you need to have the child (value) reference maintain a different lifetime + * than the owning parent (obj). Ownership of the returned value is retained + * by obj (do not do json_object_put unless you have done a json_object_get). + * If you delete the value from obj (json_object_object_del) and wish to access + * the returned reference afterwards, make sure you have first gotten shared + * ownership through json_object_get (& don't forget to do a json_object_put + * or transfer ownership to prevent a memory leak). + * + * @param obj the json_object instance + * @param key the object field name + * @returns the json_object associated with the given field name + */ +JSON_EXPORT struct json_object* json_object_object_get(const struct json_object* obj, + const char *key); + +/** Get the json_object associated with a given object field. + * + * This returns true if the key is found, false in all other cases (including + * if obj isn't a json_type_object). + * + * *No* reference counts will be changed. There is no need to manually adjust + * reference counts through the json_object_put/json_object_get methods unless + * you need to have the child (value) reference maintain a different lifetime + * than the owning parent (obj). Ownership of value is retained by obj. + * + * @param obj the json_object instance + * @param key the object field name + * @param value a pointer where to store a reference to the json_object + * associated with the given field name. + * + * It is safe to pass a NULL value. + * @returns whether or not the key exists + */ +JSON_EXPORT json_bool json_object_object_get_ex(const struct json_object* obj, + const char *key, + struct json_object **value); + +/** Delete the given json_object field + * + * The reference count will be decremented for the deleted object. If there + * are no more owners of the value represented by this key, then the value is + * freed. Otherwise, the reference to the value will remain in memory. + * + * @param obj the json_object instance + * @param key the object field name + */ +JSON_EXPORT void json_object_object_del(struct json_object* obj, const char *key); + +/** + * Iterate through all keys and values of an object. + * + * Adding keys to the object while iterating is NOT allowed. + * + * Deleting an existing key, or replacing an existing key with a + * new value IS allowed. + * + * @param obj the json_object instance + * @param key the local name for the char* key variable defined in the body + * @param val the local name for the json_object* object variable defined in + * the body + */ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && __STDC_VERSION__ >= 199901L + +# define json_object_object_foreach(obj,key,val) \ + char *key = NULL; \ + struct json_object *val __attribute__((__unused__)) = NULL; \ + for(struct lh_entry *entry ## key = json_object_get_object(obj)->head, *entry_next ## key = NULL; \ + ({ if(entry ## key) { \ + key = (char*)lh_entry_k(entry ## key); \ + val = (struct json_object*)lh_entry_v(entry ## key); \ + entry_next ## key = entry ## key->next; \ + } ; entry ## key; }); \ + entry ## key = entry_next ## key ) + +#else /* ANSI C or MSC */ + +# define json_object_object_foreach(obj,key,val) \ + char *key = NULL;\ + struct json_object *val = NULL; \ + struct lh_entry *entry ## key; \ + struct lh_entry *entry_next ## key = NULL; \ + for(entry ## key = json_object_get_object(obj)->head; \ + (entry ## key ? ( \ + key = (char*)lh_entry_k(entry ## key), \ + val = (struct json_object*)lh_entry_v(entry ## key), \ + entry_next ## key = entry ## key->next, \ + entry ## key) : 0); \ + entry ## key = entry_next ## key) + +#endif /* defined(__GNUC__) && !defined(__STRICT_ANSI__) && __STDC_VERSION__ >= 199901L */ + +/** Iterate through all keys and values of an object (ANSI C Safe) + * @param obj the json_object instance + * @param iter the object iterator, use type json_object_iter + */ +#define json_object_object_foreachC(obj,iter) \ + for(iter.entry = json_object_get_object(obj)->head; \ + (iter.entry ? (iter.key = (char*)lh_entry_k(iter.entry), iter.val = (struct json_object*)lh_entry_v(iter.entry), iter.entry) : 0); \ + iter.entry = iter.entry->next) + +/* Array type methods */ + +/** Create a new empty json_object of type json_type_array + * @returns a json_object of type json_type_array + */ +JSON_EXPORT struct json_object* json_object_new_array(void); + +/** Get the arraylist of a json_object of type json_type_array + * @param obj the json_object instance + * @returns an arraylist + */ +JSON_EXPORT struct array_list* json_object_get_array(const struct json_object *obj); + +/** Get the length of a json_object of type json_type_array + * @param obj the json_object instance + * @returns an int + */ +JSON_EXPORT size_t json_object_array_length(const struct json_object *obj); + +/** Sorts the elements of jso of type json_type_array +* +* Pointers to the json_object pointers will be passed as the two arguments +* to sort_fn +* +* @param jso the json_object instance +* @param sort_fn a sorting function +*/ +JSON_EXPORT void json_object_array_sort(struct json_object *jso, int(*sort_fn)(const void *, const void *)); + +/** Binary search a sorted array for a specified key object. + * + * It depends on your compare function what's sufficient as a key. + * Usually you create some dummy object with the parameter compared in + * it, to identify the right item you're actually looking for. + * + * @see json_object_array_sort() for hints on the compare function. + * + * @param key a dummy json_object with the right key + * @param jso the array object we're searching + * @param sort_fn the sort/compare function + * + * @return the wanted json_object instance + */ +JSON_EXPORT struct json_object* json_object_array_bsearch( + const struct json_object *key, + const struct json_object *jso, + int (*sort_fn)(const void *, const void *)); + +/** Add an element to the end of a json_object of type json_type_array + * + * The reference count will *not* be incremented. This is to make adding + * fields to objects in code more compact. If you want to retain a reference + * to an added object you must wrap the passed object with json_object_get + * + * @param obj the json_object instance + * @param val the json_object to be added + */ +JSON_EXPORT int json_object_array_add(struct json_object *obj, + struct json_object *val); + +/** Insert or replace an element at a specified index in an array (a json_object of type json_type_array) + * + * The reference count will *not* be incremented. This is to make adding + * fields to objects in code more compact. If you want to retain a reference + * to an added object you must wrap the passed object with json_object_get + * + * The reference count of a replaced object will be decremented. + * + * The array size will be automatically be expanded to the size of the + * index if the index is larger than the current size. + * + * @param obj the json_object instance + * @param idx the index to insert the element at + * @param val the json_object to be added + */ +JSON_EXPORT int json_object_array_put_idx(struct json_object *obj, size_t idx, + struct json_object *val); + +/** Get the element at specificed index of the array (a json_object of type json_type_array) + * @param obj the json_object instance + * @param idx the index to get the element at + * @returns the json_object at the specified index (or NULL) + */ +JSON_EXPORT struct json_object* json_object_array_get_idx(const struct json_object *obj, + size_t idx); + +/** Delete an elements from a specified index in an array (a json_object of type json_type_array) + * + * The reference count will be decremented for each of the deleted objects. If there + * are no more owners of an element that is being deleted, then the value is + * freed. Otherwise, the reference to the value will remain in memory. + * + * @param obj the json_object instance + * @param idx the index to start deleting elements at + * @param count the number of elements to delete + * @returns 0 if the elements were successfully deleted + */ +JSON_EXPORT int json_object_array_del_idx(struct json_object *obj, size_t idx, size_t count); + +/* json_bool type methods */ + +/** Create a new empty json_object of type json_type_boolean + * @param b a json_bool TRUE or FALSE (1 or 0) + * @returns a json_object of type json_type_boolean + */ +JSON_EXPORT struct json_object* json_object_new_boolean(json_bool b); + +/** Get the json_bool value of a json_object + * + * The type is coerced to a json_bool if the passed object is not a json_bool. + * integer and double objects will return FALSE if there value is zero + * or TRUE otherwise. If the passed object is a string it will return + * TRUE if it has a non zero length. If any other object type is passed + * TRUE will be returned if the object is not NULL. + * + * @param obj the json_object instance + * @returns a json_bool + */ +JSON_EXPORT json_bool json_object_get_boolean(const struct json_object *obj); + + +/** Set the json_bool value of a json_object + * + * The type of obj is checked to be a json_type_boolean and 0 is returned + * if it is not without any further actions. If type of obj is json_type_boolean + * the obect value is chaned to new_value + * + * @param obj the json_object instance + * @param new_value the value to be set + * @returns 1 if value is set correctly, 0 otherwise + */ +JSON_EXPORT int json_object_set_boolean(struct json_object *obj,json_bool new_value); + + +/* int type methods */ + +/** Create a new empty json_object of type json_type_int + * Note that values are stored as 64-bit values internally. + * To ensure the full range is maintained, use json_object_new_int64 instead. + * @param i the integer + * @returns a json_object of type json_type_int + */ +JSON_EXPORT struct json_object* json_object_new_int(int32_t i); + + +/** Create a new empty json_object of type json_type_int + * @param i the integer + * @returns a json_object of type json_type_int + */ +JSON_EXPORT struct json_object* json_object_new_int64(int64_t i); + + +/** Get the int value of a json_object + * + * The type is coerced to a int if the passed object is not a int. + * double objects will return their integer conversion. Strings will be + * parsed as an integer. If no conversion exists then 0 is returned + * and errno is set to EINVAL. null is equivalent to 0 (no error values set) + * + * Note that integers are stored internally as 64-bit values. + * If the value of too big or too small to fit into 32-bit, INT32_MAX or + * INT32_MIN are returned, respectively. + * + * @param obj the json_object instance + * @returns an int + */ +JSON_EXPORT int32_t json_object_get_int(const struct json_object *obj); + +/** Set the int value of a json_object + * + * The type of obj is checked to be a json_type_int and 0 is returned + * if it is not without any further actions. If type of obj is json_type_int + * the obect value is changed to new_value + * + * @param obj the json_object instance + * @param new_value the value to be set + * @returns 1 if value is set correctly, 0 otherwise + */ +JSON_EXPORT int json_object_set_int(struct json_object *obj,int new_value); + +/** Increment a json_type_int object by the given amount, which may be negative. + * + * If the type of obj is not json_type_int then 0 is returned with no further + * action taken. + * If the addition would result in a overflow, the object value + * is set to INT64_MAX. + * If the addition would result in a underflow, the object value + * is set to INT64_MIN. + * Neither overflow nor underflow affect the return value. + * + * @param obj the json_object instance + * @param val the value to add + * @returns 1 if the increment succeded, 0 otherwise + */ +JSON_EXPORT int json_object_int_inc(struct json_object *obj, int64_t val); + + +/** Get the int value of a json_object + * + * The type is coerced to a int64 if the passed object is not a int64. + * double objects will return their int64 conversion. Strings will be + * parsed as an int64. If no conversion exists then 0 is returned. + * + * NOTE: Set errno to 0 directly before a call to this function to determine + * whether or not conversion was successful (it does not clear the value for + * you). + * + * @param obj the json_object instance + * @returns an int64 + */ +JSON_EXPORT int64_t json_object_get_int64(const struct json_object *obj); + + +/** Set the int64_t value of a json_object + * + * The type of obj is checked to be a json_type_int and 0 is returned + * if it is not without any further actions. If type of obj is json_type_int + * the obect value is chaned to new_value + * + * @param obj the json_object instance + * @param new_value the value to be set + * @returns 1 if value is set correctly, 0 otherwise + */ +JSON_EXPORT int json_object_set_int64(struct json_object *obj,int64_t new_value); + +/* double type methods */ + +/** Create a new empty json_object of type json_type_double + * + * @see json_object_double_to_json_string() for how to set a custom format string. + * + * @param d the double + * @returns a json_object of type json_type_double + */ +JSON_EXPORT struct json_object* json_object_new_double(double d); + +/** + * Create a new json_object of type json_type_double, using + * the exact serialized representation of the value. + * + * This allows for numbers that would otherwise get displayed + * inefficiently (e.g. 12.3 => "12.300000000000001") to be + * serialized with the more convenient form. + * + * Notes: + * + * This is used by json_tokener_parse_ex() to allow for + * an exact re-serialization of a parsed object. + * + * The userdata field is used to store the string representation, so it + * can't be used for other data if this function is used. + * + * An equivalent sequence of calls is: + * @code + * jso = json_object_new_double(d); + * json_object_set_serializer(jso, json_object_userdata_to_json_string, + * strdup(ds), json_object_free_userdata); + * @endcode + * + * @param d the numeric value of the double. + * @param ds the string representation of the double. This will be copied. + */ +JSON_EXPORT struct json_object* json_object_new_double_s(double d, const char *ds); + +/** + * Set a global or thread-local json-c option, depending on whether + * JSON_C_OPTION_GLOBAL or JSON_C_OPTION_THREAD is passed. + * Thread-local options default to undefined, and inherit from the global + * value, even if the global value is changed after the thread is created. + * Attempting to set thread-local options when threading is not compiled in + * will result in an error. Be sure to check the return value. + * + * double_format is a "%g" printf format, such as "%.20g" + * + * @return -1 on errors, 0 on success. + */ +int json_c_set_serialization_double_format(const char *double_format, int global_or_thread); + + + +/** Serialize a json_object of type json_type_double to a string. + * + * This function isn't meant to be called directly. Instead, you can set a + * custom format string for the serialization of this double using the + * following call (where "%.17g" actually is the default): + * + * @code + * jso = json_object_new_double(d); + * json_object_set_serializer(jso, json_object_double_to_json_string, + * "%.17g", NULL); + * @endcode + * + * @see printf(3) man page for format strings + * + * @param jso The json_type_double object that is serialized. + * @param pb The destination buffer. + * @param level Ignored. + * @param flags Ignored. + */ +JSON_EXPORT int json_object_double_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags); + +/** Get the double floating point value of a json_object + * + * The type is coerced to a double if the passed object is not a double. + * integer objects will return their double conversion. Strings will be + * parsed as a double. If no conversion exists then 0.0 is returned and + * errno is set to EINVAL. null is equivalent to 0 (no error values set) + * + * If the value is too big to fit in a double, then the value is set to + * the closest infinity with errno set to ERANGE. If strings cannot be + * converted to their double value, then EINVAL is set & NaN is returned. + * + * Arrays of length 0 are interpreted as 0 (with no error flags set). + * Arrays of length 1 are effectively cast to the equivalent object and + * converted using the above rules. All other arrays set the error to + * EINVAL & return NaN. + * + * NOTE: Set errno to 0 directly before a call to this function to + * determine whether or not conversion was successful (it does not clear + * the value for you). + * + * @param obj the json_object instance + * @returns a double floating point number + */ +JSON_EXPORT double json_object_get_double(const struct json_object *obj); + + +/** Set the double value of a json_object + * + * The type of obj is checked to be a json_type_double and 0 is returned + * if it is not without any further actions. If type of obj is json_type_double + * the obect value is chaned to new_value + * + * @param obj the json_object instance + * @param new_value the value to be set + * @returns 1 if value is set correctly, 0 otherwise + */ +JSON_EXPORT int json_object_set_double(struct json_object *obj,double new_value); + + + +/* string type methods */ + +/** Create a new empty json_object of type json_type_string + * + * A copy of the string is made and the memory is managed by the json_object + * + * @param s the string + * @returns a json_object of type json_type_string + */ +JSON_EXPORT struct json_object* json_object_new_string(const char *s); + +JSON_EXPORT struct json_object* json_object_new_string_len(const char *s, int len); + +/** Get the string value of a json_object + * + * If the passed object is of type json_type_null (i.e. obj == NULL), + * NULL is returned. + * + * If the passed object of type json_type_string, the string contents + * are returned. + * + * Otherwise the JSON representation of the object is returned. + * + * The returned string memory is managed by the json_object and will + * be freed when the reference count of the json_object drops to zero. + * + * @param obj the json_object instance + * @returns a string or NULL + */ +JSON_EXPORT const char* json_object_get_string(struct json_object *obj); + +/** Get the string length of a json_object + * + * If the passed object is not of type json_type_string then zero + * will be returned. + * + * @param obj the json_object instance + * @returns int + */ +JSON_EXPORT int json_object_get_string_len(const struct json_object *obj); + + +/** Set the string value of a json_object with zero terminated strings + * equivalent to json_object_set_string_len (obj, new_value, strlen(new_value)) + * @returns 1 if value is set correctly, 0 otherwise + */ +JSON_EXPORT int json_object_set_string(json_object* obj, const char* new_value); + +/** Set the string value of a json_object str + * + * The type of obj is checked to be a json_type_string and 0 is returned + * if it is not without any further actions. If type of obj is json_type_string + * the obect value is chaned to new_value + * + * @param obj the json_object instance + * @param new_value the value to be set; Since string legth is given in len this need not be zero terminated + * @param len the length of new_value + * @returns 1 if value is set correctly, 0 otherwise + */ +JSON_EXPORT int json_object_set_string_len(json_object* obj, const char* new_value, int len); + +/** Check if two json_object's are equal + * + * If the passed objects are equal 1 will be returned. + * Equality is defined as follows: + * - json_objects of different types are never equal + * - json_objects of the same primitive type are equal if the + * c-representation of their value is equal + * - json-arrays are considered equal if all values at the same + * indices are equal (same order) + * - Complex json_objects are considered equal if all + * contained objects referenced by their key are equal, + * regardless their order. + * + * @param obj1 the first json_object instance + * @param obj2 the second json_object instance + * @returns whether both objects are equal or not + */ +JSON_EXPORT int json_object_equal(struct json_object *obj1, + struct json_object *obj2); + +/** + * Perform a shallow copy of src into *dst as part of an overall json_object_deep_copy(). + * + * If src is part of a containing object or array, parent will be non-NULL, + * and key or index will be provided. + * When shallow_copy is called *dst will be NULL, and must be non-NULL when it returns. + * src will never be NULL. + * + * If shallow_copy sets the serializer on an object, return 2 to indicate to + * json_object_deep_copy that it should not attempt to use the standard userdata + * copy function. + * + * @return On success 1 or 2, -1 on errors + */ +typedef int (json_c_shallow_copy_fn)(json_object *src, json_object *parent, const char *key, size_t index, json_object **dst); + +/** + * The default shallow copy implementation for use with json_object_deep_copy(). + * This simply calls the appropriate json_object_new_() function and + * copies over the serializer function (_to_json_string internal field of + * the json_object structure) but not any _userdata or _user_delete values. + * + * If you're writing a custom shallow_copy function, perhaps because you're using + * your own custom serializer, you can call this first to create the new object + * before customizing it with json_object_set_serializer(). + * + * @return 1 on success, -1 on errors, but never 2. + */ +json_c_shallow_copy_fn json_c_shallow_copy_default; + +/** + * Copy the contents of the JSON object. + * The destination object must be initialized to NULL, + * to make sure this function won't overwrite an existing JSON object. + * + * This does roughly the same thing as + * `json_tokener_parse(json_object_get_string(src))`. + * + * @param src source JSON object whose contents will be copied + * @param dst pointer to the destination object where the contents of `src`; + * make sure this pointer is initialized to NULL + * @param shallow_copy an optional function to copy individual objects, needed + * when custom serializers are in use. See also + * json_object set_serializer. + * + * @returns 0 if the copy went well, -1 if an error occured during copy + * or if the destination pointer is non-NULL + */ + +JSON_EXPORT int json_object_deep_copy(struct json_object *src, struct json_object **dst, json_c_shallow_copy_fn *shallow_copy); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/json-c/json_object_iterator.c b/json-c/json_object_iterator.c new file mode 100644 index 0000000..f8d69ab --- /dev/null +++ b/json-c/json_object_iterator.c @@ -0,0 +1,163 @@ +/** +******************************************************************************* +* @file json_object_iterator.c +* +* Copyright (c) 2009-2012 Hewlett-Packard Development Company, L.P. +* +* This library is free software; you can redistribute it and/or modify +* it under the terms of the MIT license. See COPYING for details. +* +******************************************************************************* +*/ + +#include + +#include "json.h" +#include "json_object_private.h" + +#include "json_object_iterator.h" + +/** + * How It Works + * + * For each JSON Object, json-c maintains a linked list of zero + * or more lh_entry (link-hash entry) structures inside the + * Object's link-hash table (lh_table). + * + * Each lh_entry structure on the JSON Object's linked list + * represents a single name/value pair. The "next" field of the + * last lh_entry in the list is set to NULL, which terminates + * the list. + * + * We represent a valid iterator that refers to an actual + * name/value pair via a pointer to the pair's lh_entry + * structure set as the iterator's opaque_ field. + * + * We follow json-c's current pair list representation by + * representing a valid "end" iterator (one that refers past the + * last pair) with a NULL value in the iterator's opaque_ field. + * + * A JSON Object without any pairs in it will have the "head" + * field of its lh_table structure set to NULL. For such an + * object, json_object_iter_begin will return an iterator with + * the opaque_ field set to NULL, which is equivalent to the + * "end" iterator. + * + * When iterating, we simply update the iterator's opaque_ field + * to point to the next lh_entry structure in the linked list. + * opaque_ will become NULL once we iterate past the last pair + * in the list, which makes the iterator equivalent to the "end" + * iterator. + */ + +/// Our current representation of the "end" iterator; +/// +/// @note May not always be NULL +static const void* kObjectEndIterValue = NULL; + +/** + * **************************************************************************** + */ +struct json_object_iterator +json_object_iter_begin(struct json_object* obj) +{ + struct json_object_iterator iter; + struct lh_table* pTable; + + /// @note json_object_get_object will return NULL if passed NULL + /// or a non-json_type_object instance + pTable = json_object_get_object(obj); + JASSERT(NULL != pTable); + + /// @note For a pair-less Object, head is NULL, which matches our + /// definition of the "end" iterator + iter.opaque_ = pTable->head; + return iter; +} + +/** + * **************************************************************************** + */ +struct json_object_iterator +json_object_iter_end(const struct json_object* obj) +{ + struct json_object_iterator iter; + + JASSERT(NULL != obj); + JASSERT(json_object_is_type(obj, json_type_object)); + + iter.opaque_ = kObjectEndIterValue; + + return iter; +} + +/** + * **************************************************************************** + */ +void +json_object_iter_next(struct json_object_iterator* iter) +{ + JASSERT(NULL != iter); + JASSERT(kObjectEndIterValue != iter->opaque_); + + iter->opaque_ = ((const struct lh_entry *)iter->opaque_)->next; +} + + +/** + * **************************************************************************** + */ +const char* +json_object_iter_peek_name(const struct json_object_iterator* iter) +{ + JASSERT(NULL != iter); + JASSERT(kObjectEndIterValue != iter->opaque_); + + return (const char*)(((const struct lh_entry *)iter->opaque_)->k); +} + + +/** + * **************************************************************************** + */ +struct json_object* +json_object_iter_peek_value(const struct json_object_iterator* iter) +{ + JASSERT(NULL != iter); + JASSERT(kObjectEndIterValue != iter->opaque_); + + return (struct json_object*)lh_entry_v((const struct lh_entry *)iter->opaque_); +} + + +/** + * **************************************************************************** + */ +json_bool +json_object_iter_equal(const struct json_object_iterator* iter1, + const struct json_object_iterator* iter2) +{ + JASSERT(NULL != iter1); + JASSERT(NULL != iter2); + + return (iter1->opaque_ == iter2->opaque_); +} + + +/** + * **************************************************************************** + */ +struct json_object_iterator +json_object_iter_init_default(void) +{ + struct json_object_iterator iter; + + /** + * @note Make this a negative, invalid value, such that + * accidental access to it would likely be trapped by the + * hardware as an invalid address. + */ + iter.opaque_ = NULL; + + return iter; +} diff --git a/json-c/json_object_iterator.h b/json-c/json_object_iterator.h new file mode 100644 index 0000000..f226cbd --- /dev/null +++ b/json-c/json_object_iterator.h @@ -0,0 +1,240 @@ +/** +******************************************************************************* +* @file json_object_iterator.h +* +* Copyright (c) 2009-2012 Hewlett-Packard Development Company, L.P. +* +* This library is free software; you can redistribute it and/or modify +* it under the terms of the MIT license. See COPYING for details. +* +* @brief An API for iterating over json_type_object objects, +* styled to be familiar to C++ programmers. +* Unlike json_object_object_foreach() and +* json_object_object_foreachC(), this avoids the need to expose +* json-c internals like lh_entry. +* +* API attributes:
+* * Thread-safe: NO
+* * Reentrant: NO +* +******************************************************************************* +*/ + + +#ifndef JSON_OBJECT_ITERATOR_H +#define JSON_OBJECT_ITERATOR_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Forward declaration for the opaque iterator information. + */ +struct json_object_iter_info_; + +/** + * The opaque iterator that references a name/value pair within + * a JSON Object instance or the "end" iterator value. + */ +struct json_object_iterator { + const void* opaque_; +}; + + +/** + * forward declaration of json-c's JSON value instance structure + */ +struct json_object; + + +/** + * Initializes an iterator structure to a "default" value that + * is convenient for initializing an iterator variable to a + * default state (e.g., initialization list in a class' + * constructor). + * + * @code + * struct json_object_iterator iter = json_object_iter_init_default(); + * MyClass() : iter_(json_object_iter_init_default()) + * @endcode + * + * @note The initialized value doesn't reference any specific + * pair, is considered an invalid iterator, and MUST NOT + * be passed to any json-c API that expects a valid + * iterator. + * + * @note User and internal code MUST NOT make any assumptions + * about and dependencies on the value of the "default" + * iterator value. + * + * @return json_object_iterator + */ +struct json_object_iterator +json_object_iter_init_default(void); + +/** Retrieves an iterator to the first pair of the JSON Object. + * + * @warning Any modification of the underlying pair invalidates all + * iterators to that pair. + * + * @param obj JSON Object instance (MUST be of type json_object) + * + * @return json_object_iterator If the JSON Object has at + * least one pair, on return, the iterator refers + * to the first pair. If the JSON Object doesn't + * have any pairs, the returned iterator is + * equivalent to the "end" iterator for the same + * JSON Object instance. + * + * @code + * struct json_object_iterator it; + * struct json_object_iterator itEnd; + * struct json_object* obj; + * + * obj = json_tokener_parse("{'first':'george', 'age':100}"); + * it = json_object_iter_begin(obj); + * itEnd = json_object_iter_end(obj); + * + * while (!json_object_iter_equal(&it, &itEnd)) { + * printf("%s\n", + * json_object_iter_peek_name(&it)); + * json_object_iter_next(&it); + * } + * + * @endcode + */ +struct json_object_iterator +json_object_iter_begin(struct json_object* obj); + +/** Retrieves the iterator that represents the position beyond the + * last pair of the given JSON Object instance. + * + * @warning Do NOT write code that assumes that the "end" + * iterator value is NULL, even if it is so in a + * particular instance of the implementation. + * + * @note The reason we do not (and MUST NOT) provide + * "json_object_iter_is_end(json_object_iterator* iter)" + * type of API is because it would limit the underlying + * representation of name/value containment (or force us + * to add additional, otherwise unnecessary, fields to + * the iterator structure). The "end" iterator and the + * equality test method, on the other hand, permit us to + * cleanly abstract pretty much any reasonable underlying + * representation without burdening the iterator + * structure with unnecessary data. + * + * @note For performance reasons, memorize the "end" iterator prior + * to any loop. + * + * @param obj JSON Object instance (MUST be of type json_object) + * + * @return json_object_iterator On return, the iterator refers + * to the "end" of the Object instance's pairs + * (i.e., NOT the last pair, but "beyond the last + * pair" value) + */ +struct json_object_iterator +json_object_iter_end(const struct json_object* obj); + +/** Returns an iterator to the next pair, if any + * + * @warning Any modification of the underlying pair + * invalidates all iterators to that pair. + * + * @param iter [IN/OUT] Pointer to iterator that references a + * name/value pair; MUST be a valid, non-end iterator. + * WARNING: bad things will happen if invalid or "end" + * iterator is passed. Upon return will contain the + * reference to the next pair if there is one; if there + * are no more pairs, will contain the "end" iterator + * value, which may be compared against the return value + * of json_object_iter_end() for the same JSON Object + * instance. + */ +void +json_object_iter_next(struct json_object_iterator* iter); + + +/** Returns a const pointer to the name of the pair referenced + * by the given iterator. + * + * @param iter pointer to iterator that references a name/value + * pair; MUST be a valid, non-end iterator. + * + * @warning bad things will happen if an invalid or + * "end" iterator is passed. + * + * @return const char* Pointer to the name of the referenced + * name/value pair. The name memory belongs to the + * name/value pair, will be freed when the pair is + * deleted or modified, and MUST NOT be modified or + * freed by the user. + */ +const char* +json_object_iter_peek_name(const struct json_object_iterator* iter); + + +/** Returns a pointer to the json-c instance representing the + * value of the referenced name/value pair, without altering + * the instance's reference count. + * + * @param iter pointer to iterator that references a name/value + * pair; MUST be a valid, non-end iterator. + * + * @warning bad things will happen if invalid or + * "end" iterator is passed. + * + * @return struct json_object* Pointer to the json-c value + * instance of the referenced name/value pair; the + * value's reference count is not changed by this + * function: if you plan to hold on to this json-c node, + * take a look at json_object_get() and + * json_object_put(). IMPORTANT: json-c API represents + * the JSON Null value as a NULL json_object instance + * pointer. + */ +struct json_object* +json_object_iter_peek_value(const struct json_object_iterator* iter); + + +/** Tests two iterators for equality. Typically used to test + * for end of iteration by comparing an iterator to the + * corresponding "end" iterator (that was derived from the same + * JSON Object instance). + * + * @note The reason we do not (and MUST NOT) provide + * "json_object_iter_is_end(json_object_iterator* iter)" + * type of API is because it would limit the underlying + * representation of name/value containment (or force us + * to add additional, otherwise unnecessary, fields to + * the iterator structure). The equality test method, on + * the other hand, permits us to cleanly abstract pretty + * much any reasonable underlying representation. + * + * @param iter1 Pointer to first valid, non-NULL iterator + * @param iter2 POinter to second valid, non-NULL iterator + * + * @warning if a NULL iterator pointer or an uninitialized + * or invalid iterator, or iterators derived from + * different JSON Object instances are passed, bad things + * will happen! + * + * @return json_bool non-zero if iterators are equal (i.e., both + * reference the same name/value pair or are both at + * "end"); zero if they are not equal. + */ +json_bool +json_object_iter_equal(const struct json_object_iterator* iter1, + const struct json_object_iterator* iter2); + + +#ifdef __cplusplus +} +#endif + + +#endif /* JSON_OBJECT_ITERATOR_H */ diff --git a/json-c/json_object_private.h b/json-c/json_object_private.h new file mode 100644 index 0000000..53be70d --- /dev/null +++ b/json-c/json_object_private.h @@ -0,0 +1,64 @@ +/* + * $Id: json_object_private.h,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ +#ifndef _json_object_private_h_ +#define _json_object_private_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define LEN_DIRECT_STRING_DATA 32 /**< how many bytes are directly stored in json_object for strings? */ + +typedef void (json_object_private_delete_fn)(struct json_object *o); + +struct json_object +{ + enum json_type o_type; + json_object_private_delete_fn *_delete; + json_object_to_json_string_fn *_to_json_string; + int _ref_count; + struct printbuf *_pb; + union data { + json_bool c_boolean; + double c_double; + int64_t c_int64; + struct lh_table *c_object; + struct array_list *c_array; + struct { + union { + /* optimize: if we have small strings, we can store them + * directly. This saves considerable CPU cycles AND memory. + */ + char *ptr; + char data[LEN_DIRECT_STRING_DATA]; + } str; + int len; + } c_string; + } o; + json_object_delete_fn *_user_delete; + void *_userdata; +}; + +void _json_c_set_last_err(const char *err_fmt, ...); + +extern const char *json_number_chars; +extern const char *json_hex_chars; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/json-c/json_pointer.c b/json-c/json_pointer.c new file mode 100644 index 0000000..2b2a9ef --- /dev/null +++ b/json-c/json_pointer.c @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2016 Alexandru Ardelean. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include "strerror_override.h" + +#include +#include +#include +#include +#include + +#include "json_pointer.h" +#include "strdup_compat.h" +#include "vasprintf_compat.h" + +/** + * JavaScript Object Notation (JSON) Pointer + * RFC 6901 - https://tools.ietf.org/html/rfc6901 + */ + +static void string_replace_all_occurrences_with_char(char *s, const char *occur, char repl_char) +{ + int slen = strlen(s); + int skip = strlen(occur) - 1; /* length of the occurence, minus the char we're replacing */ + char *p = s; + while ((p = strstr(p, occur))) { + *p = repl_char; + p++; + slen -= skip; + memmove(p, (p + skip), slen - (p - s) + 1); /* includes null char too */ + } +} + +static int is_valid_index(struct json_object *jo, const char *path, int32_t *idx) +{ + int i, len = strlen(path); + /* this code-path optimizes a bit, for when we reference the 0-9 index range in a JSON array + and because leading zeros not allowed */ + if (len == 1) { + if (isdigit((int)path[0])) { + *idx = (path[0] - '0'); + goto check_oob; + } + errno = EINVAL; + return 0; + } + /* leading zeros not allowed per RFC */ + if (path[0] == '0') { + errno = EINVAL; + return 0; + } + /* RFC states base-10 decimals */ + for (i = 0; i < len; i++) { + if (!isdigit((int)path[i])) { + errno = EINVAL; + return 0; + } + } + + *idx = strtol(path, NULL, 10); + if (*idx < 0) { + errno = EINVAL; + return 0; + } +check_oob: + len = json_object_array_length(jo); + if (*idx >= len) { + errno = ENOENT; + return 0; + } + + return 1; +} + +static int json_pointer_get_single_path(struct json_object *obj, char *path, struct json_object **value) +{ + if (json_object_is_type(obj, json_type_array)) { + int32_t idx; + if (!is_valid_index(obj, path, &idx)) + return -1; + obj = json_object_array_get_idx(obj, idx); + if (obj) { + if (value) + *value = obj; + return 0; + } + /* Entry not found */ + errno = ENOENT; + return -1; + } + + /* RFC states that we first must eval all ~1 then all ~0 */ + string_replace_all_occurrences_with_char(path, "~1", '/'); + string_replace_all_occurrences_with_char(path, "~0", '~'); + + if (!json_object_object_get_ex(obj, path, value)) { + errno = ENOENT; + return -1; + } + + return 0; +} + +static int json_pointer_set_single_path( + struct json_object *parent, + const char *path, + struct json_object *value) +{ + if (json_object_is_type(parent, json_type_array)) { + int32_t idx; + /* RFC (Chapter 4) states that '-' may be used to add new elements to an array */ + if (path[0] == '-' && path[1] == '\0') + return json_object_array_add(parent, value); + if (!is_valid_index(parent, path, &idx)) + return -1; + return json_object_array_put_idx(parent, idx, value); + } + + /* path replacements should have been done in json_pointer_get_single_path(), + and we should still be good here */ + if (json_object_is_type(parent, json_type_object)) + return json_object_object_add(parent, path, value); + + /* Getting here means that we tried to "dereference" a primitive JSON type (like string, int, bool). + i.e. add a sub-object to it */ + errno = ENOENT; + return -1; +} + +static int json_pointer_get_recursive( + struct json_object *obj, + char *path, + struct json_object **value) +{ + char *endp; + int rc; + + /* All paths (on each recursion level must have a leading '/' */ + if (path[0] != '/') { + errno = EINVAL; + return -1; + } + path++; + + endp = strchr(path, '/'); + if (endp) + *endp = '\0'; + + /* If we err-ed here, return here */ + if ((rc = json_pointer_get_single_path(obj, path, &obj))) + return rc; + + if (endp) { + *endp = '/'; /* Put the slash back, so that the sanity check passes on next recursion level */ + return json_pointer_get_recursive(obj, endp, value); + } + + /* We should be at the end of the recursion here */ + if (value) + *value = obj; + + return 0; +} + +int json_pointer_get(struct json_object *obj, const char *path, struct json_object **res) +{ + char *path_copy = NULL; + int rc; + + if (!obj || !path) { + errno = EINVAL; + return -1; + } + + if (path[0] == '\0') { + if (res) + *res = obj; + return 0; + } + + /* pass a working copy to the recursive call */ + if (!(path_copy = strdup(path))) { + errno = ENOMEM; + return -1; + } + rc = json_pointer_get_recursive(obj, path_copy, res); + free(path_copy); + + return rc; +} + +int json_pointer_getf(struct json_object *obj, struct json_object **res, const char *path_fmt, ...) +{ + char *path_copy = NULL; + int rc = 0; + va_list args; + + if (!obj || !path_fmt) { + errno = EINVAL; + return -1; + } + + va_start(args, path_fmt); + rc = vasprintf(&path_copy, path_fmt, args); + va_end(args); + + if (rc < 0) + return rc; + + if (path_copy[0] == '\0') { + if (res) + *res = obj; + goto out; + } + + rc = json_pointer_get_recursive(obj, path_copy, res); +out: + free(path_copy); + + return rc; +} + +int json_pointer_set(struct json_object **obj, const char *path, struct json_object *value) +{ + const char *endp; + char *path_copy = NULL; + struct json_object *set = NULL; + int rc; + + if (!obj || !path) { + errno = EINVAL; + return -1; + } + + if (path[0] == '\0') { + json_object_put(*obj); + *obj = value; + return 0; + } + + if (path[0] != '/') { + errno = EINVAL; + return -1; + } + + /* If there's only 1 level to set, stop here */ + if ((endp = strrchr(path, '/')) == path) { + path++; + return json_pointer_set_single_path(*obj, path, value); + } + + /* pass a working copy to the recursive call */ + if (!(path_copy = strdup(path))) { + errno = ENOMEM; + return -1; + } + path_copy[endp - path] = '\0'; + rc = json_pointer_get_recursive(*obj, path_copy, &set); + free(path_copy); + + if (rc) + return rc; + + endp++; + return json_pointer_set_single_path(set, endp, value); +} + +int json_pointer_setf(struct json_object **obj, struct json_object *value, const char *path_fmt, ...) +{ + char *endp; + char *path_copy = NULL; + struct json_object *set = NULL; + va_list args; + int rc = 0; + + if (!obj || !path_fmt) { + errno = EINVAL; + return -1; + } + + /* pass a working copy to the recursive call */ + va_start(args, path_fmt); + rc = vasprintf(&path_copy, path_fmt, args); + va_end(args); + + if (rc < 0) + return rc; + + if (path_copy[0] == '\0') { + json_object_put(*obj); + *obj = value; + goto out; + } + + if (path_copy[0] != '/') { + errno = EINVAL; + rc = -1; + goto out; + } + + /* If there's only 1 level to set, stop here */ + if ((endp = strrchr(path_copy, '/')) == path_copy) { + set = *obj; + goto set_single_path; + } + + *endp = '\0'; + rc = json_pointer_get_recursive(*obj, path_copy, &set); + + if (rc) + goto out; + +set_single_path: + endp++; + rc = json_pointer_set_single_path(set, endp, value); +out: + free(path_copy); + return rc; +} + diff --git a/json-c/json_pointer.h b/json-c/json_pointer.h new file mode 100644 index 0000000..b8746c0 --- /dev/null +++ b/json-c/json_pointer.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2016 Alexadru Ardelean. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief JSON Pointer (RFC 6901) implementation for retrieving + * objects from a json-c object tree. + */ +#ifndef _json_pointer_h_ +#define _json_pointer_h_ + +#include "json_object.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Retrieves a JSON sub-object from inside another JSON object + * using the JSON pointer notation as defined in RFC 6901 + * https://tools.ietf.org/html/rfc6901 + * + * The returned JSON sub-object is equivalent to parsing manually the + * 'obj' JSON tree ; i.e. it's not a new object that is created, but rather + * a pointer inside the JSON tree. + * + * Internally, this is equivalent to doing a series of 'json_object_object_get()' + * and 'json_object_array_get_idx()' along the given 'path'. + * + * Note that the 'path' string supports 'printf()' type arguments, so, whatever + * is added after the 'res' param will be treated as an argument for 'path' + * Example: json_pointer_get(obj, "/foo/%d/%s", &res, 0, bar) + * This means, that you need to escape '%' with '%%' (just like in printf()) + * + * @param obj the json_object instance/tree from where to retrieve sub-objects + * @param path a (RFC6901) string notation for the sub-object to retrieve + * @param res a pointer where to store a reference to the json_object + * associated with the given path + * + * @return negative if an error (or not found), or 0 if succeeded + */ +int json_pointer_get(struct json_object *obj, const char *path, struct json_object **res); + +/** + * This is a variant of 'json_pointer_get()' that supports printf() style arguments. + * + * Example: json_pointer_getf(obj, res, "/foo/%d/%s", 0, bak) + * This also means that you need to escape '%' with '%%' (just like in printf()) + * + * Please take into consideration all recommended 'printf()' format security + * aspects when using this function. + * + * @param obj the json_object instance/tree to which to add a sub-object + * @param res a pointer where to store a reference to the json_object + * associated with the given path + * @param path_fmt a printf() style format for the path + * + * @return negative if an error (or not found), or 0 if succeeded + */ +int json_pointer_getf(struct json_object *obj, struct json_object **res, const char *path_fmt, ...); + +/** + * Sets JSON object 'value' in the 'obj' tree at the location specified + * by the 'path'. 'path' is JSON pointer notation as defined in RFC 6901 + * https://tools.ietf.org/html/rfc6901 + * + * Note that 'obj' is a double pointer, mostly for the "" (empty string) + * case, where the entire JSON object would be replaced by 'value'. + * In the case of the "" path, the object at '*obj' will have it's refcount + * decremented with 'json_object_put()' and the 'value' object will be assigned to it. + * + * For other cases (JSON sub-objects) ownership of 'value' will be transferred into + * '*obj' via 'json_object_object_add()' & 'json_object_array_put_idx()', so the + * only time the refcount should be decremented for 'value' is when the return value of + * 'json_pointer_set()' is negative (meaning the 'value' object did not get set into '*obj'). + * + * That also implies that 'json_pointer_set()' does not do any refcount incrementing. + * (Just that single decrement that was mentioned above). + * + * Note that the 'path' string supports 'printf()' type arguments, so, whatever + * is added after the 'value' param will be treated as an argument for 'path' + * Example: json_pointer_set(obj, "/foo/%d/%s", value, 0, bak) + * This means, that you need to escape '%' with '%%' (just like in printf()) + * + * @param obj the json_object instance/tree to which to add a sub-object + * @param path a (RFC6901) string notation for the sub-object to set in the tree + * @param value object to set at path + * + * @return negative if an error (or not found), or 0 if succeeded + */ +int json_pointer_set(struct json_object **obj, const char *path, struct json_object *value); + +/** + * This is a variant of 'json_pointer_set()' that supports printf() style arguments. + * + * Example: json_pointer_setf(obj, value, "/foo/%d/%s", 0, bak) + * This also means that you need to escape '%' with '%%' (just like in printf()) + * + * Please take into consideration all recommended 'printf()' format security + * aspects when using this function. + * + * @param obj the json_object instance/tree to which to add a sub-object + * @param value object to set at path + * @param path_fmt a printf() style format for the path + * + * @return negative if an error (or not found), or 0 if succeeded + */ +int json_pointer_setf(struct json_object **obj, struct json_object *value, const char *path_fmt, ...); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/json-c/json_tokener.c b/json-c/json_tokener.c new file mode 100644 index 0000000..449a82d --- /dev/null +++ b/json-c/json_tokener.c @@ -0,0 +1,997 @@ +/* + * $Id: json_tokener.c,v 1.20 2006/07/25 03:24:50 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + * + * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. + * The copyrights to the contents of this file are licensed under the MIT License + * (http://www.opensource.org/licenses/mit-license.php) + */ + +#include "config.h" + +#include +#include "math_compat.h" +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "printbuf.h" +#include "arraylist.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_object_private.h" +#include "json_tokener.h" +#include "json_util.h" +#include "strdup_compat.h" + +#ifdef HAVE_LOCALE_H +#include +#endif /* HAVE_LOCALE_H */ +#ifdef HAVE_XLOCALE_H +#include +#endif + +#define jt_hexdigit(x) (((x) <= '9') ? (x) - '0' : ((x) & 7) + 9) + +#if !HAVE_STRNCASECMP && defined(_MSC_VER) + /* MSC has the version as _strnicmp */ +# define strncasecmp _strnicmp +#elif !HAVE_STRNCASECMP +# error You do not have strncasecmp on your system. +#endif /* HAVE_STRNCASECMP */ + +/* Use C99 NAN by default; if not available, nan("") should work too. */ +#ifndef NAN +#define NAN nan("") +#endif /* !NAN */ + +static const char json_null_str[] = "null"; +static const int json_null_str_len = sizeof(json_null_str) - 1; +static const char json_inf_str[] = "Infinity"; +static const char json_inf_str_lower[] = "infinity"; +static const unsigned int json_inf_str_len = sizeof(json_inf_str) - 1; +static const char json_nan_str[] = "NaN"; +static const int json_nan_str_len = sizeof(json_nan_str) - 1; +static const char json_true_str[] = "true"; +static const int json_true_str_len = sizeof(json_true_str) - 1; +static const char json_false_str[] = "false"; +static const int json_false_str_len = sizeof(json_false_str) - 1; + +static const char* json_tokener_errors[] = { + "success", + "continue", + "nesting too deep", + "unexpected end of data", + "unexpected character", + "null expected", + "boolean expected", + "number expected", + "array value separator ',' expected", + "quoted object property name expected", + "object property name separator ':' expected", + "object value separator ',' expected", + "invalid string sequence", + "expected comment", + "buffer size overflow" +}; + +const char *json_tokener_error_desc(enum json_tokener_error jerr) +{ + int jerr_int = (int) jerr; + if (jerr_int < 0 || + jerr_int >= (int)(sizeof(json_tokener_errors) / sizeof(json_tokener_errors[0]))) + return "Unknown error, " + "invalid json_tokener_error value passed to json_tokener_error_desc()"; + return json_tokener_errors[jerr]; +} + +enum json_tokener_error json_tokener_get_error(struct json_tokener *tok) +{ + return tok->err; +} + +/* Stuff for decoding unicode sequences */ +#define IS_HIGH_SURROGATE(uc) (((uc) & 0xFC00) == 0xD800) +#define IS_LOW_SURROGATE(uc) (((uc) & 0xFC00) == 0xDC00) +#define DECODE_SURROGATE_PAIR(hi,lo) ((((hi) & 0x3FF) << 10) + ((lo) & 0x3FF) + 0x10000) +static unsigned char utf8_replacement_char[3] = { 0xEF, 0xBF, 0xBD }; + +struct json_tokener* json_tokener_new_ex(int depth) +{ + struct json_tokener *tok; + + tok = (struct json_tokener*)calloc(1, sizeof(struct json_tokener)); + if (!tok) return NULL; + tok->stack = (struct json_tokener_srec *) calloc(depth, + sizeof(struct json_tokener_srec)); + if (!tok->stack) { + free(tok); + return NULL; + } + tok->pb = printbuf_new(); + tok->max_depth = depth; + json_tokener_reset(tok); + return tok; +} + +struct json_tokener* json_tokener_new(void) +{ + return json_tokener_new_ex(JSON_TOKENER_DEFAULT_DEPTH); +} + +void json_tokener_free(struct json_tokener *tok) +{ + json_tokener_reset(tok); + if (tok->pb) printbuf_free(tok->pb); + free(tok->stack); + free(tok); +} + +static void json_tokener_reset_level(struct json_tokener *tok, int depth) +{ + tok->stack[depth].state = json_tokener_state_eatws; + tok->stack[depth].saved_state = json_tokener_state_start; + json_object_put(tok->stack[depth].current); + tok->stack[depth].current = NULL; + free(tok->stack[depth].obj_field_name); + tok->stack[depth].obj_field_name = NULL; +} + +void json_tokener_reset(struct json_tokener *tok) +{ + int i; + if (!tok) + return; + + for(i = tok->depth; i >= 0; i--) + json_tokener_reset_level(tok, i); + tok->depth = 0; + tok->err = json_tokener_success; +} + +struct json_object* json_tokener_parse(const char *str) +{ + enum json_tokener_error jerr_ignored; + struct json_object* obj; + obj = json_tokener_parse_verbose(str, &jerr_ignored); + return obj; +} + +struct json_object* json_tokener_parse_verbose(const char *str, + enum json_tokener_error *error) +{ + struct json_tokener* tok; + struct json_object* obj; + + tok = json_tokener_new(); + if (!tok) + return NULL; + obj = json_tokener_parse_ex(tok, str, -1); + *error = tok->err; + if(tok->err != json_tokener_success) { + if (obj != NULL) + json_object_put(obj); + obj = NULL; + } + + json_tokener_free(tok); + return obj; +} + +#define state tok->stack[tok->depth].state +#define saved_state tok->stack[tok->depth].saved_state +#define current tok->stack[tok->depth].current +#define obj_field_name tok->stack[tok->depth].obj_field_name + +/* Optimization: + * json_tokener_parse_ex() consumed a lot of CPU in its main loop, + * iterating character-by character. A large performance boost is + * achieved by using tighter loops to locally handle units such as + * comments and strings. Loops that handle an entire token within + * their scope also gather entire strings and pass them to + * printbuf_memappend() in a single call, rather than calling + * printbuf_memappend() one char at a time. + * + * PEEK_CHAR() and ADVANCE_CHAR() macros are used for code that is + * common to both the main loop and the tighter loops. + */ + +/* PEEK_CHAR(dest, tok) macro: + * Peeks at the current char and stores it in dest. + * Returns 1 on success, sets tok->err and returns 0 if no more chars. + * Implicit inputs: str, len vars + */ +#define PEEK_CHAR(dest, tok) \ + (((tok)->char_offset == len) ? \ + (((tok)->depth == 0 && \ + state == json_tokener_state_eatws && \ + saved_state == json_tokener_state_finish \ + ) ? \ + (((tok)->err = json_tokener_success), 0) \ + : \ + (((tok)->err = json_tokener_continue), 0) \ + ) : \ + (((dest) = *str), 1) \ + ) + +/* ADVANCE_CHAR() macro: + * Incrementes str & tok->char_offset. + * For convenience of existing conditionals, returns the old value of c (0 on eof) + * Implicit inputs: c var + */ +#define ADVANCE_CHAR(str, tok) \ + ( ++(str), ((tok)->char_offset)++, c) + + +/* End optimization macro defs */ + + +struct json_object* json_tokener_parse_ex(struct json_tokener *tok, + const char *str, int len) +{ + struct json_object *obj = NULL; + char c = '\1'; +#ifdef HAVE_USELOCALE + locale_t oldlocale = uselocale(NULL); + locale_t newloc; +#elif defined(HAVE_SETLOCALE) + char *oldlocale = NULL; +#endif + + tok->char_offset = 0; + tok->err = json_tokener_success; + + /* this interface is presently not 64-bit clean due to the int len argument + and the internal printbuf interface that takes 32-bit int len arguments + so the function limits the maximum string size to INT32_MAX (2GB). + If the function is called with len == -1 then strlen is called to check + the string length is less than INT32_MAX (2GB) */ + if ((len < -1) || (len == -1 && strlen(str) > INT32_MAX)) { + tok->err = json_tokener_error_size; + return NULL; + } + +#ifdef HAVE_USELOCALE + { + locale_t duploc = duplocale(oldlocale); + newloc = newlocale(LC_NUMERIC, "C", duploc); + // XXX at least Debian 8.4 has a bug in newlocale where it doesn't + // change the decimal separator unless you set LC_TIME! + if (newloc) + { + duploc = newloc; // original duploc has been freed by newlocale() + newloc = newlocale(LC_TIME, "C", duploc); + } + if (newloc == NULL) + { + freelocale(duploc); + return NULL; + } + uselocale(newloc); + } +#elif defined(HAVE_SETLOCALE) + { + char *tmplocale; + tmplocale = setlocale(LC_NUMERIC, NULL); + if (tmplocale) oldlocale = strdup(tmplocale); + setlocale(LC_NUMERIC, "C"); + } +#endif + + while (PEEK_CHAR(c, tok)) { + + redo_char: + switch(state) { + + case json_tokener_state_eatws: + /* Advance until we change state */ + while (isspace((int)c)) { + if ((!ADVANCE_CHAR(str, tok)) || (!PEEK_CHAR(c, tok))) + goto out; + } + if(c == '/' && !(tok->flags & JSON_TOKENER_STRICT)) { + printbuf_reset(tok->pb); + printbuf_memappend_fast(tok->pb, &c, 1); + state = json_tokener_state_comment_start; + } else { + state = saved_state; + goto redo_char; + } + break; + + case json_tokener_state_start: + switch(c) { + case '{': + state = json_tokener_state_eatws; + saved_state = json_tokener_state_object_field_start; + current = json_object_new_object(); + if(current == NULL) + goto out; + break; + case '[': + state = json_tokener_state_eatws; + saved_state = json_tokener_state_array; + current = json_object_new_array(); + if(current == NULL) + goto out; + break; + case 'I': + case 'i': + state = json_tokener_state_inf; + printbuf_reset(tok->pb); + tok->st_pos = 0; + goto redo_char; + case 'N': + case 'n': + state = json_tokener_state_null; // or NaN + printbuf_reset(tok->pb); + tok->st_pos = 0; + goto redo_char; + case '\'': + if (tok->flags & JSON_TOKENER_STRICT) { + /* in STRICT mode only double-quote are allowed */ + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + /* FALLTHRU */ + case '"': + state = json_tokener_state_string; + printbuf_reset(tok->pb); + tok->quote_char = c; + break; + case 'T': + case 't': + case 'F': + case 'f': + state = json_tokener_state_boolean; + printbuf_reset(tok->pb); + tok->st_pos = 0; + goto redo_char; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + state = json_tokener_state_number; + printbuf_reset(tok->pb); + tok->is_double = 0; + goto redo_char; + default: + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + break; + + case json_tokener_state_finish: + if(tok->depth == 0) goto out; + obj = json_object_get(current); + json_tokener_reset_level(tok, tok->depth); + tok->depth--; + goto redo_char; + + case json_tokener_state_inf: /* aka starts with 'i' (or 'I', or "-i", or "-I") */ + { + /* If we were guaranteed to have len set, then we could (usually) handle + * the entire "Infinity" check in a single strncmp (strncasecmp), but + * since len might be -1 (i.e. "read until \0"), we need to check it + * a character at a time. + * Trying to handle it both ways would make this code considerably more + * complicated with likely little performance benefit. + */ + int is_negative = 0; + const char *_json_inf_str = json_inf_str; + if (!(tok->flags & JSON_TOKENER_STRICT)) + _json_inf_str = json_inf_str_lower; + + /* Note: tok->st_pos must be 0 when state is set to json_tokener_state_inf */ + while (tok->st_pos < (int)json_inf_str_len) + { + char inf_char = *str; + if (!(tok->flags & JSON_TOKENER_STRICT)) + inf_char = tolower((int)*str); + if (inf_char != _json_inf_str[tok->st_pos]) + { + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + tok->st_pos++; + (void)ADVANCE_CHAR(str, tok); + if (!PEEK_CHAR(c, tok)) + { + /* out of input chars, for now at least */ + goto out; + } + } + /* We checked the full length of "Infinity", so create the object. + * When handling -Infinity, the number parsing code will have dropped + * the "-" into tok->pb for us, so check it now. + */ + if (printbuf_length(tok->pb) > 0 && *(tok->pb->buf) == '-') + { + is_negative = 1; + } + current = json_object_new_double(is_negative + ? -INFINITY : INFINITY); + if (current == NULL) + goto out; + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + + } + break; + case json_tokener_state_null: /* aka starts with 'n' */ + { + int size; + int size_nan; + printbuf_memappend_fast(tok->pb, &c, 1); + size = json_min(tok->st_pos+1, json_null_str_len); + size_nan = json_min(tok->st_pos+1, json_nan_str_len); + if((!(tok->flags & JSON_TOKENER_STRICT) && + strncasecmp(json_null_str, tok->pb->buf, size) == 0) + || (strncmp(json_null_str, tok->pb->buf, size) == 0) + ) { + if (tok->st_pos == json_null_str_len) { + current = NULL; + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } + else if ((!(tok->flags & JSON_TOKENER_STRICT) && + strncasecmp(json_nan_str, tok->pb->buf, size_nan) == 0) || + (strncmp(json_nan_str, tok->pb->buf, size_nan) == 0) + ) + { + if (tok->st_pos == json_nan_str_len) + { + current = json_object_new_double(NAN); + if (current == NULL) + goto out; + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else { + tok->err = json_tokener_error_parse_null; + goto out; + } + tok->st_pos++; + } + break; + + case json_tokener_state_comment_start: + if(c == '*') { + state = json_tokener_state_comment; + } else if(c == '/') { + state = json_tokener_state_comment_eol; + } else { + tok->err = json_tokener_error_parse_comment; + goto out; + } + printbuf_memappend_fast(tok->pb, &c, 1); + break; + + case json_tokener_state_comment: + { + /* Advance until we change state */ + const char *case_start = str; + while(c != '*') { + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + printbuf_memappend_fast(tok->pb, case_start, 1+str-case_start); + state = json_tokener_state_comment_end; + } + break; + + case json_tokener_state_comment_eol: + { + /* Advance until we change state */ + const char *case_start = str; + while(c != '\n') { + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + MC_DEBUG("json_tokener_comment: %s\n", tok->pb->buf); + state = json_tokener_state_eatws; + } + break; + + case json_tokener_state_comment_end: + printbuf_memappend_fast(tok->pb, &c, 1); + if(c == '/') { + MC_DEBUG("json_tokener_comment: %s\n", tok->pb->buf); + state = json_tokener_state_eatws; + } else { + state = json_tokener_state_comment; + } + break; + + case json_tokener_state_string: + { + /* Advance until we change state */ + const char *case_start = str; + while(1) { + if(c == tok->quote_char) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + current = json_object_new_string_len(tok->pb->buf, tok->pb->bpos); + if(current == NULL) + goto out; + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + break; + } else if(c == '\\') { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + saved_state = json_tokener_state_string; + state = json_tokener_state_string_escape; + break; + } + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + } + break; + + case json_tokener_state_string_escape: + switch(c) { + case '"': + case '\\': + case '/': + printbuf_memappend_fast(tok->pb, &c, 1); + state = saved_state; + break; + case 'b': + case 'n': + case 'r': + case 't': + case 'f': + if(c == 'b') printbuf_memappend_fast(tok->pb, "\b", 1); + else if(c == 'n') printbuf_memappend_fast(tok->pb, "\n", 1); + else if(c == 'r') printbuf_memappend_fast(tok->pb, "\r", 1); + else if(c == 't') printbuf_memappend_fast(tok->pb, "\t", 1); + else if(c == 'f') printbuf_memappend_fast(tok->pb, "\f", 1); + state = saved_state; + break; + case 'u': + tok->ucs_char = 0; + tok->st_pos = 0; + state = json_tokener_state_escape_unicode; + break; + default: + tok->err = json_tokener_error_parse_string; + goto out; + } + break; + + case json_tokener_state_escape_unicode: + { + unsigned int got_hi_surrogate = 0; + + /* Handle a 4-byte sequence, or two sequences if a surrogate pair */ + while(1) { + if (c && strchr(json_hex_chars, c)) { + tok->ucs_char += ((unsigned int)jt_hexdigit(c) << ((3-tok->st_pos++)*4)); + if(tok->st_pos == 4) { + unsigned char unescaped_utf[4]; + + if (got_hi_surrogate) { + if (IS_LOW_SURROGATE(tok->ucs_char)) { + /* Recalculate the ucs_char, then fall thru to process normally */ + tok->ucs_char = DECODE_SURROGATE_PAIR(got_hi_surrogate, tok->ucs_char); + } else { + /* Hi surrogate was not followed by a low surrogate */ + /* Replace the hi and process the rest normally */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } + got_hi_surrogate = 0; + } + + if (tok->ucs_char < 0x80) { + unescaped_utf[0] = tok->ucs_char; + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 1); + } else if (tok->ucs_char < 0x800) { + unescaped_utf[0] = 0xc0 | (tok->ucs_char >> 6); + unescaped_utf[1] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 2); + } else if (IS_HIGH_SURROGATE(tok->ucs_char)) { + /* Got a high surrogate. Remember it and look for the + * the beginning of another sequence, which should be the + * low surrogate. + */ + got_hi_surrogate = tok->ucs_char; + /* Not at end, and the next two chars should be "\u" */ + if ((len == -1 || len > (tok->char_offset + 2)) && + // str[0] != '0' && // implied by json_hex_chars, above. + (str[1] == '\\') && + (str[2] == 'u')) + { + /* Advance through the 16 bit surrogate, and move on to the + * next sequence. The next step is to process the following + * characters. + */ + if( !ADVANCE_CHAR(str, tok) || !ADVANCE_CHAR(str, tok) ) { + printbuf_memappend_fast(tok->pb, + (char*) utf8_replacement_char, 3); + } + /* Advance to the first char of the next sequence and + * continue processing with the next sequence. + */ + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, + (char*) utf8_replacement_char, 3); + goto out; + } + tok->ucs_char = 0; + tok->st_pos = 0; + continue; /* other json_tokener_state_escape_unicode */ + } else { + /* Got a high surrogate without another sequence following + * it. Put a replacement char in for the hi surrogate + * and pretend we finished. + */ + printbuf_memappend_fast(tok->pb, + (char*) utf8_replacement_char, 3); + } + } else if (IS_LOW_SURROGATE(tok->ucs_char)) { + /* Got a low surrogate not preceded by a high */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } else if (tok->ucs_char < 0x10000) { + unescaped_utf[0] = 0xe0 | (tok->ucs_char >> 12); + unescaped_utf[1] = 0x80 | ((tok->ucs_char >> 6) & 0x3f); + unescaped_utf[2] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 3); + } else if (tok->ucs_char < 0x110000) { + unescaped_utf[0] = 0xf0 | ((tok->ucs_char >> 18) & 0x07); + unescaped_utf[1] = 0x80 | ((tok->ucs_char >> 12) & 0x3f); + unescaped_utf[2] = 0x80 | ((tok->ucs_char >> 6) & 0x3f); + unescaped_utf[3] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 4); + } else { + /* Don't know what we got--insert the replacement char */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } + state = saved_state; + break; + } + } else { + tok->err = json_tokener_error_parse_string; + goto out; + } + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + if (got_hi_surrogate) /* Clean up any pending chars */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + goto out; + } + } + } + break; + + case json_tokener_state_boolean: + { + int size1, size2; + printbuf_memappend_fast(tok->pb, &c, 1); + size1 = json_min(tok->st_pos+1, json_true_str_len); + size2 = json_min(tok->st_pos+1, json_false_str_len); + if((!(tok->flags & JSON_TOKENER_STRICT) && + strncasecmp(json_true_str, tok->pb->buf, size1) == 0) + || (strncmp(json_true_str, tok->pb->buf, size1) == 0) + ) { + if(tok->st_pos == json_true_str_len) { + current = json_object_new_boolean(1); + if(current == NULL) + goto out; + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else if((!(tok->flags & JSON_TOKENER_STRICT) && + strncasecmp(json_false_str, tok->pb->buf, size2) == 0) + || (strncmp(json_false_str, tok->pb->buf, size2) == 0)) { + if(tok->st_pos == json_false_str_len) { + current = json_object_new_boolean(0); + if(current == NULL) + goto out; + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else { + tok->err = json_tokener_error_parse_boolean; + goto out; + } + tok->st_pos++; + } + break; + + case json_tokener_state_number: + { + /* Advance until we change state */ + const char *case_start = str; + int case_len=0; + int is_exponent=0; + int negativesign_next_possible_location=1; + while(c && strchr(json_number_chars, c)) { + ++case_len; + + /* non-digit characters checks */ + /* note: since the main loop condition to get here was + an input starting with 0-9 or '-', we are + protected from input starting with '.' or + e/E. */ + if (c == '.') { + if (tok->is_double != 0) { + /* '.' can only be found once, and out of the exponent part. + Thus, if the input is already flagged as double, it + is invalid. */ + tok->err = json_tokener_error_parse_number; + goto out; + } + tok->is_double = 1; + } + if (c == 'e' || c == 'E') { + if (is_exponent != 0) { + /* only one exponent possible */ + tok->err = json_tokener_error_parse_number; + goto out; + } + is_exponent = 1; + tok->is_double = 1; + /* the exponent part can begin with a negative sign */ + negativesign_next_possible_location = case_len + 1; + } + if (c == '-' && case_len != negativesign_next_possible_location) { + /* If the negative sign is not where expected (ie + start of input or start of exponent part), the + input is invalid. */ + tok->err = json_tokener_error_parse_number; + goto out; + } + + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, case_len); + goto out; + } + } + if (case_len>0) + printbuf_memappend_fast(tok->pb, case_start, case_len); + + // Check for -Infinity + if (tok->pb->buf[0] == '-' && case_len <= 1 && + (c == 'i' || c == 'I')) + { + state = json_tokener_state_inf; + tok->st_pos = 0; + goto redo_char; + } + } + { + int64_t num64; + double numd; + if (!tok->is_double && json_parse_int64(tok->pb->buf, &num64) == 0) { + if (num64 && tok->pb->buf[0]=='0' && + (tok->flags & JSON_TOKENER_STRICT)) { + /* in strict mode, number must not start with 0 */ + tok->err = json_tokener_error_parse_number; + goto out; + } + current = json_object_new_int64(num64); + if(current == NULL) + goto out; + } + else if(tok->is_double && json_parse_double(tok->pb->buf, &numd) == 0) + { + current = json_object_new_double_s(numd, tok->pb->buf); + if(current == NULL) + goto out; + } else { + tok->err = json_tokener_error_parse_number; + goto out; + } + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + break; + + case json_tokener_state_array_after_sep: + case json_tokener_state_array: + if(c == ']') { + if (state == json_tokener_state_array_after_sep && + (tok->flags & JSON_TOKENER_STRICT)) + { + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else { + if(tok->depth >= tok->max_depth-1) { + tok->err = json_tokener_error_depth; + goto out; + } + state = json_tokener_state_array_add; + tok->depth++; + json_tokener_reset_level(tok, tok->depth); + goto redo_char; + } + break; + + case json_tokener_state_array_add: + if( json_object_array_add(current, obj) != 0 ) + goto out; + saved_state = json_tokener_state_array_sep; + state = json_tokener_state_eatws; + goto redo_char; + + case json_tokener_state_array_sep: + if(c == ']') { + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if(c == ',') { + saved_state = json_tokener_state_array_after_sep; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_array; + goto out; + } + break; + + case json_tokener_state_object_field_start: + case json_tokener_state_object_field_start_after_sep: + if(c == '}') { + if (state == json_tokener_state_object_field_start_after_sep && + (tok->flags & JSON_TOKENER_STRICT)) + { + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if (c == '"' || c == '\'') { + tok->quote_char = c; + printbuf_reset(tok->pb); + state = json_tokener_state_object_field; + } else { + tok->err = json_tokener_error_parse_object_key_name; + goto out; + } + break; + + case json_tokener_state_object_field: + { + /* Advance until we change state */ + const char *case_start = str; + while(1) { + if(c == tok->quote_char) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + obj_field_name = strdup(tok->pb->buf); + saved_state = json_tokener_state_object_field_end; + state = json_tokener_state_eatws; + break; + } else if(c == '\\') { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + saved_state = json_tokener_state_object_field; + state = json_tokener_state_string_escape; + break; + } + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + } + break; + + case json_tokener_state_object_field_end: + if(c == ':') { + saved_state = json_tokener_state_object_value; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_object_key_sep; + goto out; + } + break; + + case json_tokener_state_object_value: + if(tok->depth >= tok->max_depth-1) { + tok->err = json_tokener_error_depth; + goto out; + } + state = json_tokener_state_object_value_add; + tok->depth++; + json_tokener_reset_level(tok, tok->depth); + goto redo_char; + + case json_tokener_state_object_value_add: + json_object_object_add(current, obj_field_name, obj); + free(obj_field_name); + obj_field_name = NULL; + saved_state = json_tokener_state_object_sep; + state = json_tokener_state_eatws; + goto redo_char; + + case json_tokener_state_object_sep: + /* { */ + if(c == '}') { + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if(c == ',') { + saved_state = json_tokener_state_object_field_start_after_sep; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_object_value_sep; + goto out; + } + break; + + } + if (!ADVANCE_CHAR(str, tok)) + goto out; + } /* while(PEEK_CHAR) */ + + out: + if (c && + (state == json_tokener_state_finish) && + (tok->depth == 0) && + (tok->flags & JSON_TOKENER_STRICT)) { + /* unexpected char after JSON data */ + tok->err = json_tokener_error_parse_unexpected; + } + if (!c) { /* We hit an eof char (0) */ + if(state != json_tokener_state_finish && + saved_state != json_tokener_state_finish) + tok->err = json_tokener_error_parse_eof; + } + +#ifdef HAVE_USELOCALE + uselocale(oldlocale); + freelocale(newloc); +#elif defined(HAVE_SETLOCALE) + setlocale(LC_NUMERIC, oldlocale); + free(oldlocale); +#endif + + if (tok->err == json_tokener_success) + { + json_object *ret = json_object_get(current); + int ii; + + /* Partially reset, so we parse additional objects on subsequent calls. */ + for(ii = tok->depth; ii >= 0; ii--) + json_tokener_reset_level(tok, ii); + return ret; + } + + MC_DEBUG("json_tokener_parse_ex: error %s at offset %d\n", + json_tokener_errors[tok->err], tok->char_offset); + return NULL; +} + +void json_tokener_set_flags(struct json_tokener *tok, int flags) +{ + tok->flags = flags; +} diff --git a/json-c/json_tokener.h b/json-c/json_tokener.h new file mode 100644 index 0000000..ed272b6 --- /dev/null +++ b/json-c/json_tokener.h @@ -0,0 +1,216 @@ +/* + * $Id: json_tokener.h,v 1.10 2006/07/25 03:24:50 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief Methods to parse an input string into a tree of json_object objects. + */ +#ifndef _json_tokener_h_ +#define _json_tokener_h_ + +#include +#include "json_object.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum json_tokener_error { + json_tokener_success, + json_tokener_continue, + json_tokener_error_depth, + json_tokener_error_parse_eof, + json_tokener_error_parse_unexpected, + json_tokener_error_parse_null, + json_tokener_error_parse_boolean, + json_tokener_error_parse_number, + json_tokener_error_parse_array, + json_tokener_error_parse_object_key_name, + json_tokener_error_parse_object_key_sep, + json_tokener_error_parse_object_value_sep, + json_tokener_error_parse_string, + json_tokener_error_parse_comment, + json_tokener_error_size +}; + +enum json_tokener_state { + json_tokener_state_eatws, + json_tokener_state_start, + json_tokener_state_finish, + json_tokener_state_null, + json_tokener_state_comment_start, + json_tokener_state_comment, + json_tokener_state_comment_eol, + json_tokener_state_comment_end, + json_tokener_state_string, + json_tokener_state_string_escape, + json_tokener_state_escape_unicode, + json_tokener_state_boolean, + json_tokener_state_number, + json_tokener_state_array, + json_tokener_state_array_add, + json_tokener_state_array_sep, + json_tokener_state_object_field_start, + json_tokener_state_object_field, + json_tokener_state_object_field_end, + json_tokener_state_object_value, + json_tokener_state_object_value_add, + json_tokener_state_object_sep, + json_tokener_state_array_after_sep, + json_tokener_state_object_field_start_after_sep, + json_tokener_state_inf +}; + +struct json_tokener_srec +{ + enum json_tokener_state state, saved_state; + struct json_object *obj; + struct json_object *current; + char *obj_field_name; +}; + +#define JSON_TOKENER_DEFAULT_DEPTH 32 + +struct json_tokener +{ + char *str; + struct printbuf *pb; + int max_depth, depth, is_double, st_pos, char_offset; + enum json_tokener_error err; + unsigned int ucs_char; + char quote_char; + struct json_tokener_srec *stack; + int flags; +}; +/** + * @deprecated Unused in json-c code + */ +typedef struct json_tokener json_tokener; + +/** + * Be strict when parsing JSON input. Use caution with + * this flag as what is considered valid may become more + * restrictive from one release to the next, causing your + * code to fail on previously working input. + * + * This flag is not set by default. + * + * @see json_tokener_set_flags() + */ +#define JSON_TOKENER_STRICT 0x01 + +/** + * Given an error previously returned by json_tokener_get_error(), + * return a human readable description of the error. + * + * @return a generic error message is returned if an invalid error value is provided. + */ +const char *json_tokener_error_desc(enum json_tokener_error jerr); + +/** + * Retrieve the error caused by the last call to json_tokener_parse_ex(), + * or json_tokener_success if there is no error. + * + * When parsing a JSON string in pieces, if the tokener is in the middle + * of parsing this will return json_tokener_continue. + * + * See also json_tokener_error_desc(). + */ +JSON_EXPORT enum json_tokener_error json_tokener_get_error(struct json_tokener *tok); + +JSON_EXPORT struct json_tokener* json_tokener_new(void); +JSON_EXPORT struct json_tokener* json_tokener_new_ex(int depth); +JSON_EXPORT void json_tokener_free(struct json_tokener *tok); +JSON_EXPORT void json_tokener_reset(struct json_tokener *tok); +JSON_EXPORT struct json_object* json_tokener_parse(const char *str); +JSON_EXPORT struct json_object* json_tokener_parse_verbose(const char *str, enum json_tokener_error *error); + +/** + * Set flags that control how parsing will be done. + */ +JSON_EXPORT void json_tokener_set_flags(struct json_tokener *tok, int flags); + +/** + * Parse a string and return a non-NULL json_object if a valid JSON value + * is found. The string does not need to be a JSON object or array; + * it can also be a string, number or boolean value. + * + * A partial JSON string can be parsed. If the parsing is incomplete, + * NULL will be returned and json_tokener_get_error() will return + * json_tokener_continue. + * json_tokener_parse_ex() can then be called with additional bytes in str + * to continue the parsing. + * + * If json_tokener_parse_ex() returns NULL and the error is anything other than + * json_tokener_continue, a fatal error has occurred and parsing must be + * halted. Then, the tok object must not be reused until json_tokener_reset() is + * called. + * + * When a valid JSON value is parsed, a non-NULL json_object will be + * returned. Also, json_tokener_get_error() will return json_tokener_success. + * Be sure to check the type with json_object_is_type() or + * json_object_get_type() before using the object. + * + * @b XXX this shouldn't use internal fields: + * Trailing characters after the parsed value do not automatically cause an + * error. It is up to the caller to decide whether to treat this as an + * error or to handle the additional characters, perhaps by parsing another + * json value starting from that point. + * + * Extra characters can be detected by comparing the tok->char_offset against + * the length of the last len parameter passed in. + * + * The tokener does \b not maintain an internal buffer so the caller is + * responsible for calling json_tokener_parse_ex with an appropriate str + * parameter starting with the extra characters. + * + * This interface is presently not 64-bit clean due to the int len argument + * so the function limits the maximum string size to INT32_MAX (2GB). + * If the function is called with len == -1 then strlen is called to check + * the string length is less than INT32_MAX (2GB) + * + * Example: + * @code +json_object *jobj = NULL; +const char *mystring = NULL; +int stringlen = 0; +enum json_tokener_error jerr; +do { + mystring = ... // get JSON string, e.g. read from file, etc... + stringlen = strlen(mystring); + jobj = json_tokener_parse_ex(tok, mystring, stringlen); +} while ((jerr = json_tokener_get_error(tok)) == json_tokener_continue); +if (jerr != json_tokener_success) +{ + fprintf(stderr, "Error: %s\n", json_tokener_error_desc(jerr)); + // Handle errors, as appropriate for your application. +} +if (tok->char_offset < stringlen) // XXX shouldn't access internal fields +{ + // Handle extra characters after parsed object as desired. + // e.g. issue an error, parse another object from that point, etc... +} +// Success, use jobj here. + +@endcode + * + * @param tok a json_tokener previously allocated with json_tokener_new() + * @param str an string with any valid JSON expression, or portion of. This does not need to be null terminated. + * @param len the length of str + */ +JSON_EXPORT struct json_object* json_tokener_parse_ex(struct json_tokener *tok, + const char *str, int len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/json-c/json_util.c b/json-c/json_util.c new file mode 100644 index 0000000..ad7704a --- /dev/null +++ b/json-c/json_util.c @@ -0,0 +1,243 @@ +/* + * $Id: json_util.c,v 1.4 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" +#undef realloc + +#include "strerror_override.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif /* HAVE_SYS_TYPES_H */ + +#ifdef HAVE_SYS_STAT_H +#include +#endif /* HAVE_SYS_STAT_H */ + +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ + +#ifdef HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ + +#ifdef WIN32 +# if MSC_VER < 1800 +/* strtoll is available only since Visual Studio 2013 */ +# define strtoll _strtoi64 +# endif +# define WIN32_LEAN_AND_MEAN +# include +# include +#endif /* defined(WIN32) */ + +#if !defined(HAVE_OPEN) && defined(WIN32) +# define open _open +#endif + +#include "snprintf_compat.h" + +#include "debug.h" +#include "printbuf.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_tokener.h" +#include "json_util.h" + +static int _json_object_to_fd(int fd, struct json_object *obj, int flags, const char *filename); + +static char _last_err[256] = ""; + +const char *json_util_get_last_err() +{ + if (_last_err[0] == '\0') + return NULL; + return _last_err; +} + +void _json_c_set_last_err(const char *err_fmt, ...) +{ + va_list ap; + va_start(ap, err_fmt); + // Ignore (attempted) overruns from snprintf + (void)vsnprintf(_last_err, sizeof(_last_err), err_fmt, ap); + va_end(ap); +} + +struct json_object* json_object_from_fd(int fd) +{ + struct printbuf *pb; + struct json_object *obj; + char buf[JSON_FILE_BUF_SIZE]; + int ret; + + if(!(pb = printbuf_new())) { + _json_c_set_last_err("json_object_from_file: printbuf_new failed\n"); + return NULL; + } + while((ret = read(fd, buf, JSON_FILE_BUF_SIZE)) > 0) { + printbuf_memappend(pb, buf, ret); + } + if(ret < 0) { + _json_c_set_last_err("json_object_from_fd: error reading fd %d: %s\n", fd, strerror(errno)); + printbuf_free(pb); + return NULL; + } + obj = json_tokener_parse(pb->buf); + printbuf_free(pb); + return obj; +} + +struct json_object* json_object_from_file(const char *filename) +{ + struct json_object *obj; + int fd; + + if((fd = open(filename, O_RDONLY)) < 0) { + _json_c_set_last_err("json_object_from_file: error opening file %s: %s\n", + filename, strerror(errno)); + return NULL; + } + obj = json_object_from_fd(fd); + close(fd); + return obj; +} + +/* extended "format and write to file" function */ + +int json_object_to_file_ext(const char *filename, struct json_object *obj, int flags) +{ + int fd, ret; + int saved_errno; + + if (!obj) { + _json_c_set_last_err("json_object_to_file: object is null\n"); + return -1; + } + + if ((fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0) { + _json_c_set_last_err("json_object_to_file: error opening file %s: %s\n", + filename, strerror(errno)); + return -1; + } + ret = _json_object_to_fd(fd, obj, flags, filename); + saved_errno = errno; + close(fd); + errno = saved_errno; + return ret; +} + +int json_object_to_fd(int fd, struct json_object *obj, int flags) +{ + if (!obj) { + _json_c_set_last_err("json_object_to_fd: object is null\n"); + return -1; + } + + return _json_object_to_fd(fd, obj, flags, NULL); +} +static int _json_object_to_fd(int fd, struct json_object *obj, int flags, const char *filename) +{ + int ret; + const char *json_str; + unsigned int wpos, wsize; + + filename = filename ? filename : "(fd)"; + + if (!(json_str = json_object_to_json_string_ext(obj,flags))) { + return -1; + } + + wsize = (unsigned int)(strlen(json_str) & UINT_MAX); /* CAW: probably unnecessary, but the most 64bit safe */ + wpos = 0; + while(wpos < wsize) { + if((ret = write(fd, json_str + wpos, wsize-wpos)) < 0) { + _json_c_set_last_err("json_object_to_file: error writing file %s: %s\n", + filename, strerror(errno)); + return -1; + } + + /* because of the above check for ret < 0, we can safely cast and add */ + wpos += (unsigned int)ret; + } + + return 0; +} + +// backwards compatible "format and write to file" function + +int json_object_to_file(const char *filename, struct json_object *obj) +{ + return json_object_to_file_ext(filename, obj, JSON_C_TO_STRING_PLAIN); +} + +int json_parse_double(const char *buf, double *retval) +{ + char *end; + *retval = strtod(buf, &end); + return end == buf ? 1 : 0; +} + +int json_parse_int64(const char *buf, int64_t *retval) +{ + char *end = NULL; + int64_t val; + + errno = 0; + val = strtoll(buf, &end, 10); + if (end != buf) + *retval = val; + return ((val == 0 && errno != 0) || (end == buf)) ? 1 : 0; +} + +#ifndef HAVE_REALLOC +void* rpl_realloc(void* p, size_t n) +{ + if (n == 0) + n = 1; + if (p == 0) + return malloc(n); + return realloc(p, n); +} +#endif + +#define NELEM(a) (sizeof(a) / sizeof(a[0])) +static const char* json_type_name[] = { + /* If you change this, be sure to update the enum json_type definition too */ + "null", + "boolean", + "double", + "int", + "object", + "array", + "string", +}; + +const char *json_type_to_name(enum json_type o_type) +{ + int o_type_int = (int)o_type; + if (o_type_int < 0 || o_type_int >= (int)NELEM(json_type_name)) + { + _json_c_set_last_err("json_type_to_name: type %d is out of range [0,%d]\n", o_type, NELEM(json_type_name)); + return NULL; + } + return json_type_name[o_type]; +} + diff --git a/json-c/json_util.h b/json-c/json_util.h new file mode 100644 index 0000000..e065f60 --- /dev/null +++ b/json-c/json_util.h @@ -0,0 +1,106 @@ +/* + * $Id: json_util.h,v 1.4 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief Miscllaneous utility functions and macros. + */ +#ifndef _json_util_h_ +#define _json_util_h_ + +#include "json_object.h" + +#ifndef json_min +#define json_min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef json_max +#define json_max(a,b) ((a) > (b) ? (a) : (b)) +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +#define JSON_FILE_BUF_SIZE 4096 + +/* utility functions */ +/** + * Read the full contents of the given file, then convert it to a + * json_object using json_tokener_parse(). + * + * Returns -1 if something fails. See json_util_get_last_err() for details. + */ +extern struct json_object* json_object_from_file(const char *filename); + +/** + * Create a JSON object from already opened file descriptor. + * + * This function can be helpful, when you opened the file already, + * e.g. when you have a temp file. + * Note, that the fd must be readable at the actual position, i.e. + * use lseek(fd, 0, SEEK_SET) before. + * + * Returns -1 if something fails. See json_util_get_last_err() for details. + */ +extern struct json_object* json_object_from_fd(int fd); + +/** + * Equivalent to: + * json_object_to_file_ext(filename, obj, JSON_C_TO_STRING_PLAIN); + * + * Returns -1 if something fails. See json_util_get_last_err() for details. + */ +extern int json_object_to_file(const char *filename, struct json_object *obj); + +/** + * Open and truncate the given file, creating it if necessary, then + * convert the json_object to a string and write it to the file. + * + * Returns -1 if something fails. See json_util_get_last_err() for details. + */ +extern int json_object_to_file_ext(const char *filename, struct json_object *obj, int flags); + +/** + * Convert the json_object to a string and write it to the file descriptor. + * Handles partial writes and will keep writing until done, or an error + * occurs. + * + * @param fd an open, writable file descriptor to write to + * @param obj the object to serializer and write + * @param flags flags to pass to json_object_to_json_string_ext() + * @return -1 if something fails. See json_util_get_last_err() for details. + */ +extern int json_object_to_fd(int fd, struct json_object *obj, int flags); + +/** + * Return the last error from various json-c functions, including: + * json_object_to_file{,_ext}, json_object_to_fd() or + * json_object_from_{file,fd}, or NULL if there is none. + */ +const char *json_util_get_last_err(void); + + +extern int json_parse_int64(const char *buf, int64_t *retval); +extern int json_parse_double(const char *buf, double *retval); + +/** + * Return a string describing the type of the object. + * e.g. "int", or "object", etc... + */ +extern const char *json_type_to_name(enum json_type o_type); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/json-c/json_visit.c b/json-c/json_visit.c new file mode 100644 index 0000000..1126ff8 --- /dev/null +++ b/json-c/json_visit.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2016 Eric Haszlakiewicz + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + */ + +#include + +#include "config.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_visit.h" +#include "linkhash.h" + +static int _json_c_visit(json_object *jso, json_object *parent_jso, + const char *jso_key, size_t *jso_index, + json_c_visit_userfunc *userfunc, void *userarg); + +int json_c_visit(json_object *jso, int future_flags, + json_c_visit_userfunc *userfunc, void *userarg) +{ + int ret = _json_c_visit(jso, NULL, NULL, NULL, userfunc, userarg); + switch(ret) + { + case JSON_C_VISIT_RETURN_CONTINUE: + case JSON_C_VISIT_RETURN_SKIP: + case JSON_C_VISIT_RETURN_POP: + case JSON_C_VISIT_RETURN_STOP: + return 0; + default: + return JSON_C_VISIT_RETURN_ERROR; + } +} +static int _json_c_visit(json_object *jso, json_object *parent_jso, + const char *jso_key, size_t *jso_index, + json_c_visit_userfunc *userfunc, void *userarg) +{ + int userret = userfunc(jso, 0, parent_jso, jso_key, jso_index, userarg); + switch(userret) + { + case JSON_C_VISIT_RETURN_CONTINUE: + break; + case JSON_C_VISIT_RETURN_SKIP: + case JSON_C_VISIT_RETURN_POP: + case JSON_C_VISIT_RETURN_STOP: + case JSON_C_VISIT_RETURN_ERROR: + return userret; + default: + fprintf(stderr, "ERROR: invalid return value from json_c_visit userfunc: %d\n", userret); + return JSON_C_VISIT_RETURN_ERROR; + } + + switch(json_object_get_type(jso)) + { + case json_type_null: + case json_type_boolean: + case json_type_double: + case json_type_int: + case json_type_string: + // we already called userfunc above, move on to the next object + return JSON_C_VISIT_RETURN_CONTINUE; + + case json_type_object: + { + json_object_object_foreach(jso, key, child) + { + userret = _json_c_visit(child, jso, key, NULL, userfunc, userarg); + if (userret == JSON_C_VISIT_RETURN_POP) + break; + if (userret == JSON_C_VISIT_RETURN_STOP || + userret == JSON_C_VISIT_RETURN_ERROR) + return userret; + if (userret != JSON_C_VISIT_RETURN_CONTINUE && + userret != JSON_C_VISIT_RETURN_SKIP) + { + fprintf(stderr, "INTERNAL ERROR: _json_c_visit returned %d\n", userret); + return JSON_C_VISIT_RETURN_ERROR; + } + } + break; + } + case json_type_array: + { + size_t array_len = json_object_array_length(jso); + size_t ii; + for (ii = 0; ii < array_len; ii++) + { + json_object *child = json_object_array_get_idx(jso, ii); + userret = _json_c_visit(child, jso, NULL, &ii, userfunc, userarg); + if (userret == JSON_C_VISIT_RETURN_POP) + break; + if (userret == JSON_C_VISIT_RETURN_STOP || + userret == JSON_C_VISIT_RETURN_ERROR) + return userret; + if (userret != JSON_C_VISIT_RETURN_CONTINUE && + userret != JSON_C_VISIT_RETURN_SKIP) + { + fprintf(stderr, "INTERNAL ERROR: _json_c_visit returned %d\n", userret); + return JSON_C_VISIT_RETURN_ERROR; + } + } + break; + } + default: + fprintf(stderr, "INTERNAL ERROR: _json_c_visit found object of unknown type: %d\n", json_object_get_type(jso)); + return JSON_C_VISIT_RETURN_ERROR; + } + + // Call userfunc for the second type on container types, after all + // members of the container have been visited. + // Non-container types will have already returned before this point. + + userret = userfunc(jso, JSON_C_VISIT_SECOND, parent_jso, jso_key, jso_index, userarg); + switch(userret) + { + case JSON_C_VISIT_RETURN_SKIP: + case JSON_C_VISIT_RETURN_POP: + // These are not really sensible during JSON_C_VISIT_SECOND, + // but map them to JSON_C_VISIT_CONTINUE anyway. + // FALLTHROUGH + case JSON_C_VISIT_RETURN_CONTINUE: + return JSON_C_VISIT_RETURN_CONTINUE; + case JSON_C_VISIT_RETURN_STOP: + case JSON_C_VISIT_RETURN_ERROR: + return userret; + default: + fprintf(stderr, "ERROR: invalid return value from json_c_visit userfunc: %d\n", userret); + return JSON_C_VISIT_RETURN_ERROR; + } + // NOTREACHED +} + diff --git a/json-c/json_visit.h b/json-c/json_visit.h new file mode 100644 index 0000000..b147d99 --- /dev/null +++ b/json-c/json_visit.h @@ -0,0 +1,95 @@ + +#ifndef _json_c_json_visit_h_ +#define _json_c_json_visit_h_ + +/** + * @file + * @brief Methods for walking a tree of objects. + */ +#include "json_object.h" + +typedef int (json_c_visit_userfunc)(json_object *jso, int flags, + json_object *parent_jso, const char *jso_key, + size_t *jso_index, void *userarg); + +/** + * Visit each object in the JSON hierarchy starting at jso. + * For each object, userfunc is called, passing the object and userarg. + * If the object has a parent (i.e. anything other than jso itself) + * its parent will be passed as parent_jso, and either jso_key or jso_index + * will be set, depending on whether the parent is an object or an array. + * + * Nodes will be visited depth first, but containers (arrays and objects) + * will be visited twice, the second time with JSON_C_VISIT_SECOND set in + * flags. + * + * userfunc must return one of the defined return values, to indicate + * whether and how to continue visiting nodes, or one of various ways to stop. + * + * Returns 0 if nodes were visited successfully, even if some were + * intentionally skipped due to what userfunc returned. + * Returns <0 if an error occurred during iteration, including if + * userfunc returned JSON_C_VISIT_RETURN_ERROR. + */ +int json_c_visit(json_object *jso, int future_flags, + json_c_visit_userfunc *userfunc, void *userarg); + +/** + * Passed to json_c_visit_userfunc as one of the flags values to indicate + * that this is the second time a container (array or object) is being + * called, after all of it's members have been iterated over. + */ +#define JSON_C_VISIT_SECOND 0x02 + +/** + * This json_c_visit_userfunc return value indicates that iteration + * should proceed normally. + */ +#define JSON_C_VISIT_RETURN_CONTINUE 0 + + +/** + * This json_c_visit_userfunc return value indicates that iteration + * over the members of the current object should be skipped. + * If the current object isn't a container (array or object), this + * is no different than JSON_C_VISIT_RETURN_CONTINUE. + */ +#define JSON_C_VISIT_RETURN_SKIP 7547 + +/** + * This json_c_visit_userfunc return value indicates that iteration + * of the fields/elements of the containing object should stop + * and continue "popped up" a level of the object hierarchy. + * For example, returning this when handling arg will result in + * arg3 and any other fields being skipped. The next call to userfunc + * will be the JSON_C_VISIT_SECOND call on "foo", followed by a userfunc + * call on "bar". + *
+ * {
+ *   "foo": {
+ *     "arg1": 1,
+ *     "arg2": 2,
+ *     "arg3": 3,
+ *     ...
+ *   },
+ *   "bar": {
+ *     ...
+ *   }
+ * }
+ * 
+ */ +#define JSON_C_VISIT_RETURN_POP 767 + +/** + * This json_c_visit_userfunc return value indicates that iteration + * should stop immediately, and cause json_c_visit to return success. + */ +#define JSON_C_VISIT_RETURN_STOP 7867 + +/** + * This json_c_visit_userfunc return value indicates that iteration + * should stop immediately, and cause json_c_visit to return an error. + */ +#define JSON_C_VISIT_RETURN_ERROR -1 + +#endif /* _json_c_json_visit_h_ */ diff --git a/json-c/linkhash.c b/json-c/linkhash.c new file mode 100644 index 0000000..5497061 --- /dev/null +++ b/json-c/linkhash.c @@ -0,0 +1,690 @@ +/* + * $Id: linkhash.c,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_ENDIAN_H +# include /* attempt to define endianness */ +#endif + +#if defined(_MSC_VER) || defined(__MINGW32__) +# define WIN32_LEAN_AND_MEAN +# include /* Get InterlockedCompareExchange */ +#endif + +#include "random_seed.h" +#include "linkhash.h" + +/* hash functions */ +static unsigned long lh_char_hash(const void *k); +static unsigned long lh_perllike_str_hash(const void *k); +static lh_hash_fn *char_hash_fn = lh_char_hash; + +int +json_global_set_string_hash(const int h) +{ + switch(h) { + case JSON_C_STR_HASH_DFLT: + char_hash_fn = lh_char_hash; + break; + case JSON_C_STR_HASH_PERLLIKE: + char_hash_fn = lh_perllike_str_hash; + break; + default: + return -1; + } + return 0; +} + +void lh_abort(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + vprintf(msg, ap); + va_end(ap); + exit(1); +} + +static unsigned long lh_ptr_hash(const void *k) +{ + /* CAW: refactored to be 64bit nice */ + return (unsigned long)((((ptrdiff_t)k * LH_PRIME) >> 4) & ULONG_MAX); +} + +int lh_ptr_equal(const void *k1, const void *k2) +{ + return (k1 == k2); +} + +/* + * hashlittle from lookup3.c, by Bob Jenkins, May 2006, Public Domain. + * http://burtleburtle.net/bob/c/lookup3.c + * minor modifications to make functions static so no symbols are exported + * minor mofifications to compile with -Werror + */ + +/* +------------------------------------------------------------------------------- +lookup3.c, by Bob Jenkins, May 2006, Public Domain. + +These are functions for producing 32-bit hashes for hash table lookup. +hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() +are externally useful functions. Routines to test the hash are included +if SELF_TEST is defined. You can use this free for any purpose. It's in +the public domain. It has no warranty. + +You probably want to use hashlittle(). hashlittle() and hashbig() +hash byte arrays. hashlittle() is is faster than hashbig() on +little-endian machines. Intel and AMD are little-endian machines. +On second thought, you probably want hashlittle2(), which is identical to +hashlittle() except it returns two 32-bit hashes for the price of one. +You could implement hashbig2() if you wanted but I haven't bothered here. + +If you want to find a hash of, say, exactly 7 integers, do + a = i1; b = i2; c = i3; + mix(a,b,c); + a += i4; b += i5; c += i6; + mix(a,b,c); + a += i7; + final(a,b,c); +then use c as the hash value. If you have a variable length array of +4-byte integers to hash, use hashword(). If you have a byte array (like +a character string), use hashlittle(). If you have several byte arrays, or +a mix of things, see the comments above hashlittle(). + +Why is this so big? I read 12 bytes at a time into 3 4-byte integers, +then mix those integers. This is fast (you can do a lot more thorough +mixing with 12*3 instructions on 3 integers than you can with 3 instructions +on 1 byte), but shoehorning those bytes into integers efficiently is messy. +------------------------------------------------------------------------------- +*/ + +/* + * My best guess at if you are big-endian or little-endian. This may + * need adjustment. + */ +#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) +# define HASH_LITTLE_ENDIAN 1 +# define HASH_BIG_ENDIAN 0 +#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ + __BYTE_ORDER == __BIG_ENDIAN) || \ + (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 1 +#else +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 0 +#endif + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/* +------------------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. + +This is reversible, so any information in (a,b,c) before mix() is +still in (a,b,c) after mix(). + +If four pairs of (a,b,c) inputs are run through mix(), or through +mix() in reverse, there are at least 32 bits of the output that +are sometimes the same for one pair and different for another pair. +This was tested for: +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that +satisfy this are + 4 6 8 16 19 4 + 9 15 3 18 27 15 + 14 9 3 7 17 3 +Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing +for "differ" defined as + with a one-bit base and a two-bit delta. I +used http://burtleburtle.net/bob/hash/avalanche.html to choose +the operations, constants, and arrangements of the variables. + +This does not achieve avalanche. There are input bits of (a,b,c) +that fail to affect some output bits of (a,b,c), especially of a. The +most thoroughly mixed value is c, but it doesn't really even achieve +avalanche in c. + +This allows some parallelism. Read-after-writes are good at doubling +the number of bits affected, so the goal of mixing pulls in the opposite +direction as the goal of parallelism. I did what I could. Rotates +seem to cost as much as shifts on every machine I could lay my hands +on, and rotates are much kinder to the top and bottom bits, so I used +rotates. +------------------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +/* +------------------------------------------------------------------------------- +final -- final mixing of 3 32-bit values (a,b,c) into c + +Pairs of (a,b,c) values differing in only a few bits will usually +produce values of c that look totally different. This was tested for +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 +and these came close: + 4 8 15 26 3 22 24 + 10 8 15 26 3 22 24 + 11 8 15 26 3 22 24 +------------------------------------------------------------------------------- +*/ +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + + +/* +------------------------------------------------------------------------------- +hashlittle() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + length : the length of the key, counting by bytes + initval : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Two keys differing by one or two bits will have +totally different hash values. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (uint8_t **)k, do it like this: + for (i=0, h=0; i 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + * AddressSanitizer is similarly picky about overrunning + * the buffer. (http://clang.llvm.org/docs/AddressSanitizer.html + */ +#ifdef VALGRIND +# define PRECISE_MEMORY_ACCESS 1 +#elif defined(__SANITIZE_ADDRESS__) /* GCC's ASAN */ +# define PRECISE_MEMORY_ACCESS 1 +#elif defined(__has_feature) +# if __has_feature(address_sanitizer) /* Clang's ASAN */ +# define PRECISE_MEMORY_ACCESS 1 +# endif +#endif +#ifndef PRECISE_MEMORY_ACCESS + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + const uint8_t *k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : return c; + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : return c; /* zero length requires no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; /* FALLTHRU */ + case 11: c+=((uint32_t)k[10])<<16; /* FALLTHRU */ + case 10: c+=((uint32_t)k[9])<<8; /* FALLTHRU */ + case 9 : c+=k[8]; /* FALLTHRU */ + case 8 : b+=((uint32_t)k[7])<<24; /* FALLTHRU */ + case 7 : b+=((uint32_t)k[6])<<16; /* FALLTHRU */ + case 6 : b+=((uint32_t)k[5])<<8; /* FALLTHRU */ + case 5 : b+=k[4]; /* FALLTHRU */ + case 4 : a+=((uint32_t)k[3])<<24; /* FALLTHRU */ + case 3 : a+=((uint32_t)k[2])<<16; /* FALLTHRU */ + case 2 : a+=((uint32_t)k[1])<<8; /* FALLTHRU */ + case 1 : a+=k[0]; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} + +/* a simple hash function similiar to what perl does for strings. + * for good results, the string should not be excessivly large. + */ +static unsigned long lh_perllike_str_hash(const void *k) +{ + const char *rkey = (const char *)k; + unsigned hashval = 1; + + while (*rkey) + hashval = hashval * 33 + *rkey++; + + return hashval; +} + +static unsigned long lh_char_hash(const void *k) +{ +#if defined _MSC_VER || defined __MINGW32__ +#define RANDOM_SEED_TYPE LONG +#else +#define RANDOM_SEED_TYPE int +#endif + static volatile RANDOM_SEED_TYPE random_seed = -1; + + if (random_seed == -1) { + RANDOM_SEED_TYPE seed; + /* we can't use -1 as it is the unitialized sentinel */ + while ((seed = json_c_get_random_seed()) == -1); +#if SIZEOF_INT == 8 && defined __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 +#define USE_SYNC_COMPARE_AND_SWAP 1 +#endif +#if SIZEOF_INT == 4 && defined __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 +#define USE_SYNC_COMPARE_AND_SWAP 1 +#endif +#if SIZEOF_INT == 2 && defined __GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 +#define USE_SYNC_COMPARE_AND_SWAP 1 +#endif +#if defined USE_SYNC_COMPARE_AND_SWAP + (void)__sync_val_compare_and_swap(&random_seed, -1, seed); +#elif defined _MSC_VER || defined __MINGW32__ + InterlockedCompareExchange(&random_seed, seed, -1); +#else +//#warning "racy random seed initializtion if used by multiple threads" + random_seed = seed; /* potentially racy */ +#endif + } + + return hashlittle((const char*)k, strlen((const char*)k), random_seed); +} + +int lh_char_equal(const void *k1, const void *k2) +{ + return (strcmp((const char*)k1, (const char*)k2) == 0); +} + +struct lh_table* lh_table_new(int size, + lh_entry_free_fn *free_fn, + lh_hash_fn *hash_fn, + lh_equal_fn *equal_fn) +{ + int i; + struct lh_table *t; + + t = (struct lh_table*)calloc(1, sizeof(struct lh_table)); + if (!t) + return NULL; + + t->count = 0; + t->size = size; + t->table = (struct lh_entry*)calloc(size, sizeof(struct lh_entry)); + if (!t->table) + { + free(t); + return NULL; + } + t->free_fn = free_fn; + t->hash_fn = hash_fn; + t->equal_fn = equal_fn; + for(i = 0; i < size; i++) t->table[i].k = LH_EMPTY; + return t; +} + +struct lh_table* lh_kchar_table_new(int size, + lh_entry_free_fn *free_fn) +{ + return lh_table_new(size, free_fn, char_hash_fn, lh_char_equal); +} + +struct lh_table* lh_kptr_table_new(int size, + lh_entry_free_fn *free_fn) +{ + return lh_table_new(size, free_fn, lh_ptr_hash, lh_ptr_equal); +} + +int lh_table_resize(struct lh_table *t, int new_size) +{ + struct lh_table *new_t; + struct lh_entry *ent; + + new_t = lh_table_new(new_size, NULL, t->hash_fn, t->equal_fn); + if (new_t == NULL) + return -1; + + for (ent = t->head; ent != NULL; ent = ent->next) + { + unsigned long h = lh_get_hash(new_t, ent->k); + unsigned int opts = 0; + if (ent->k_is_constant) + opts = JSON_C_OBJECT_KEY_IS_CONSTANT; + if (lh_table_insert_w_hash(new_t, ent->k, ent->v, h, opts) != 0) + { + lh_table_free(new_t); + return -1; + } + } + free(t->table); + t->table = new_t->table; + t->size = new_size; + t->head = new_t->head; + t->tail = new_t->tail; + free(new_t); + + return 0; +} + +void lh_table_free(struct lh_table *t) +{ + struct lh_entry *c; + if(t->free_fn) { + for(c = t->head; c != NULL; c = c->next) + t->free_fn(c); + } + free(t->table); + free(t); +} + + +int lh_table_insert_w_hash(struct lh_table *t, const void *k, const void *v, const unsigned long h, const unsigned opts) +{ + unsigned long n; + + if (t->count >= t->size * LH_LOAD_FACTOR) + if (lh_table_resize(t, t->size * 2) != 0) + return -1; + + n = h % t->size; + + while( 1 ) { + if(t->table[n].k == LH_EMPTY || t->table[n].k == LH_FREED) break; + if ((int)++n == t->size) n = 0; + } + + t->table[n].k = k; + t->table[n].k_is_constant = (opts & JSON_C_OBJECT_KEY_IS_CONSTANT); + t->table[n].v = v; + t->count++; + + if(t->head == NULL) { + t->head = t->tail = &t->table[n]; + t->table[n].next = t->table[n].prev = NULL; + } else { + t->tail->next = &t->table[n]; + t->table[n].prev = t->tail; + t->table[n].next = NULL; + t->tail = &t->table[n]; + } + + return 0; +} +int lh_table_insert(struct lh_table *t, const void *k, const void *v) +{ + return lh_table_insert_w_hash(t, k, v, lh_get_hash(t, k), 0); +} + + +struct lh_entry* lh_table_lookup_entry_w_hash(struct lh_table *t, const void *k, const unsigned long h) +{ + unsigned long n = h % t->size; + int count = 0; + + while( count < t->size ) { + if(t->table[n].k == LH_EMPTY) return NULL; + if(t->table[n].k != LH_FREED && + t->equal_fn(t->table[n].k, k)) return &t->table[n]; + if ((int)++n == t->size) n = 0; + count++; + } + return NULL; +} + +struct lh_entry* lh_table_lookup_entry(struct lh_table *t, const void *k) +{ + return lh_table_lookup_entry_w_hash(t, k, lh_get_hash(t, k)); +} + +const void* lh_table_lookup(struct lh_table *t, const void *k) +{ + void *result; + lh_table_lookup_ex(t, k, &result); + return result; +} + +json_bool lh_table_lookup_ex(struct lh_table* t, const void* k, void **v) +{ + struct lh_entry *e = lh_table_lookup_entry(t, k); + if (e != NULL) { + if (v != NULL) *v = lh_entry_v(e); + return TRUE; /* key found */ + } + if (v != NULL) *v = NULL; + return FALSE; /* key not found */ +} + +int lh_table_delete_entry(struct lh_table *t, struct lh_entry *e) +{ + ptrdiff_t n = (ptrdiff_t)(e - t->table); /* CAW: fixed to be 64bit nice, still need the crazy negative case... */ + + /* CAW: this is bad, really bad, maybe stack goes other direction on this machine... */ + if(n < 0) { return -2; } + + if(t->table[n].k == LH_EMPTY || t->table[n].k == LH_FREED) return -1; + t->count--; + if(t->free_fn) t->free_fn(e); + t->table[n].v = NULL; + t->table[n].k = LH_FREED; + if(t->tail == &t->table[n] && t->head == &t->table[n]) { + t->head = t->tail = NULL; + } else if (t->head == &t->table[n]) { + t->head->next->prev = NULL; + t->head = t->head->next; + } else if (t->tail == &t->table[n]) { + t->tail->prev->next = NULL; + t->tail = t->tail->prev; + } else { + t->table[n].prev->next = t->table[n].next; + t->table[n].next->prev = t->table[n].prev; + } + t->table[n].next = t->table[n].prev = NULL; + return 0; +} + + +int lh_table_delete(struct lh_table *t, const void *k) +{ + struct lh_entry *e = lh_table_lookup_entry(t, k); + if(!e) return -1; + return lh_table_delete_entry(t, e); +} + +int lh_table_length(struct lh_table *t) +{ + return t->count; +} diff --git a/json-c/linkhash.h b/json-c/linkhash.h new file mode 100644 index 0000000..9c2f5c1 --- /dev/null +++ b/json-c/linkhash.h @@ -0,0 +1,406 @@ +/* + * $Id: linkhash.h,v 1.6 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief Internal methods for working with json_type_object objects. Although + * this is exposed by the json_object_get_object() function and within the + * json_object_iter type, it is not recommended for direct use. + */ +#ifndef _linkhash_h_ +#define _linkhash_h_ + +#include "json_object.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * golden prime used in hash functions + */ +#define LH_PRIME 0x9e370001UL + +/** + * The fraction of filled hash buckets until an insert will cause the table + * to be resized. + * This can range from just above 0 up to 1.0. + */ +#define LH_LOAD_FACTOR 0.66 + +/** + * sentinel pointer value for empty slots + */ +#define LH_EMPTY (void*)-1 + +/** + * sentinel pointer value for freed slots + */ +#define LH_FREED (void*)-2 + +/** + * default string hash function + */ +#define JSON_C_STR_HASH_DFLT 0 + +/** + * perl-like string hash function + */ +#define JSON_C_STR_HASH_PERLLIKE 1 + +/** + * This function sets the hash function to be used for strings. + * Must be one of the JSON_C_STR_HASH_* values. + * @returns 0 - ok, -1 if parameter was invalid + */ +int json_global_set_string_hash(const int h); + +struct lh_entry; + +/** + * callback function prototypes + */ +typedef void (lh_entry_free_fn) (struct lh_entry *e); +/** + * callback function prototypes + */ +typedef unsigned long (lh_hash_fn) (const void *k); +/** + * callback function prototypes + */ +typedef int (lh_equal_fn) (const void *k1, const void *k2); + +/** + * An entry in the hash table + */ +struct lh_entry { + /** + * The key. Use lh_entry_k() instead of accessing this directly. + */ + const void *k; + /** + * A flag for users of linkhash to know whether or not they + * need to free k. + */ + int k_is_constant; + /** + * The value. Use lh_entry_v() instead of accessing this directly. + */ + const void *v; + /** + * The next entry + */ + struct lh_entry *next; + /** + * The previous entry. + */ + struct lh_entry *prev; +}; + + +/** + * The hash table structure. + */ +struct lh_table { + /** + * Size of our hash. + */ + int size; + /** + * Numbers of entries. + */ + int count; + + /** + * The first entry. + */ + struct lh_entry *head; + + /** + * The last entry. + */ + struct lh_entry *tail; + + struct lh_entry *table; + + /** + * A pointer onto the function responsible for freeing an entry. + */ + lh_entry_free_fn *free_fn; + lh_hash_fn *hash_fn; + lh_equal_fn *equal_fn; +}; +typedef struct lh_table lh_table; + + +/** + * Convenience list iterator. + */ +#define lh_foreach(table, entry) \ +for(entry = table->head; entry; entry = entry->next) + +/** + * lh_foreach_safe allows calling of deletion routine while iterating. + * + * @param table a struct lh_table * to iterate over + * @param entry a struct lh_entry * variable to hold each element + * @param tmp a struct lh_entry * variable to hold a temporary pointer to the next element + */ +#define lh_foreach_safe(table, entry, tmp) \ +for(entry = table->head; entry && ((tmp = entry->next) || 1); entry = tmp) + + + +/** + * Create a new linkhash table. + * + * @param size initial table size. The table is automatically resized + * although this incurs a performance penalty. + * @param free_fn callback function used to free memory for entries + * when lh_table_free or lh_table_delete is called. + * If NULL is provided, then memory for keys and values + * must be freed by the caller. + * @param hash_fn function used to hash keys. 2 standard ones are defined: + * lh_ptr_hash and lh_char_hash for hashing pointer values + * and C strings respectively. + * @param equal_fn comparison function to compare keys. 2 standard ones defined: + * lh_ptr_hash and lh_char_hash for comparing pointer values + * and C strings respectively. + * @return On success, a pointer to the new linkhash table is returned. + * On error, a null pointer is returned. + */ +extern struct lh_table* lh_table_new(int size, + lh_entry_free_fn *free_fn, + lh_hash_fn *hash_fn, + lh_equal_fn *equal_fn); + +/** + * Convenience function to create a new linkhash table with char keys. + * + * @param size initial table size. + * @param free_fn callback function used to free memory for entries. + * @return On success, a pointer to the new linkhash table is returned. + * On error, a null pointer is returned. + */ +extern struct lh_table* lh_kchar_table_new(int size, + lh_entry_free_fn *free_fn); + + +/** + * Convenience function to create a new linkhash table with ptr keys. + * + * @param size initial table size. + * @param free_fn callback function used to free memory for entries. + * @return On success, a pointer to the new linkhash table is returned. + * On error, a null pointer is returned. + */ +extern struct lh_table* lh_kptr_table_new(int size, + lh_entry_free_fn *free_fn); + + +/** + * Free a linkhash table. + * + * If a lh_entry_free_fn callback free function was provided then it is + * called for all entries in the table. + * + * @param t table to free. + */ +extern void lh_table_free(struct lh_table *t); + + +/** + * Insert a record into the table. + * + * @param t the table to insert into. + * @param k a pointer to the key to insert. + * @param v a pointer to the value to insert. + * + * @return On success, 0 is returned. + * On error, a negative value is returned. + */ +extern int lh_table_insert(struct lh_table *t, const void *k, const void *v); + + +/** + * Insert a record into the table using a precalculated key hash. + * + * The hash h, which should be calculated with lh_get_hash() on k, is provided by + * the caller, to allow for optimization when multiple operations with the same + * key are known to be needed. + * + * @param t the table to insert into. + * @param k a pointer to the key to insert. + * @param v a pointer to the value to insert. + * @param h hash value of the key to insert + * @param opts if set to JSON_C_OBJECT_KEY_IS_CONSTANT, sets lh_entry.k_is_constant + * so t's free function knows to avoid freeing the key. + */ +extern int lh_table_insert_w_hash(struct lh_table *t, const void *k, const void *v, const unsigned long h, const unsigned opts); + + +/** + * Lookup a record in the table. + * + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @return a pointer to the record structure of the value or NULL if it does not exist. + */ +extern struct lh_entry* lh_table_lookup_entry(struct lh_table *t, const void *k); + +/** + * Lookup a record in the table using a precalculated key hash. + * + * The hash h, which should be calculated with lh_get_hash() on k, is provided by + * the caller, to allow for optimization when multiple operations with the same + * key are known to be needed. + * + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @param h hash value of the key to lookup + * @return a pointer to the record structure of the value or NULL if it does not exist. + */ +extern struct lh_entry* lh_table_lookup_entry_w_hash(struct lh_table *t, const void *k, const unsigned long h); + +/** + * Lookup a record into the table. + * + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @return a pointer to the found value or NULL if it does not exist. + * @deprecated Use lh_table_lookup_ex() instead. + */ +THIS_FUNCTION_IS_DEPRECATED(extern const void* lh_table_lookup(struct lh_table *t, const void *k)); + +/** + * Lookup a record in the table. + * + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @param v a pointer to a where to store the found value (set to NULL if it doesn't exist). + * @return whether or not the key was found + */ +extern json_bool lh_table_lookup_ex(struct lh_table *t, const void *k, void **v); + +/** + * Delete a record from the table. + * + * If a callback free function is provided then it is called for the + * for the item being deleted. + * @param t the table to delete from. + * @param e a pointer to the entry to delete. + * @return 0 if the item was deleted. + * @return -1 if it was not found. + */ +extern int lh_table_delete_entry(struct lh_table *t, struct lh_entry *e); + + +/** + * Delete a record from the table. + * + * If a callback free function is provided then it is called for the + * for the item being deleted. + * @param t the table to delete from. + * @param k a pointer to the key to delete. + * @return 0 if the item was deleted. + * @return -1 if it was not found. + */ +extern int lh_table_delete(struct lh_table *t, const void *k); + +extern int lh_table_length(struct lh_table *t); + +/** + * Prints a message to stdout, + * then exits the program with an exit code of 1. + * + * @param msg Message format string, like for printf. + * @param ... Format args. + * + * @deprecated Since it is not a good idea to exit the entire program + * because of an internal library failure, json-c will no longer + * use this function internally. + * However, because its interface is public, it will remain part of + * the API on the off chance of legacy software using it externally. + */ +THIS_FUNCTION_IS_DEPRECATED(void lh_abort(const char *msg, ...)); + +/** + * Resizes the specified table. + * + * @param t Pointer to table to resize. + * @param new_size New table size. Must be positive. + * + * @return On success, 0 is returned. + * On error, a negative value is returned. + */ +int lh_table_resize(struct lh_table *t, int new_size); + + +/** + * @deprecated Don't use this outside of linkhash.h: + */ +#if !defined(_MSC_VER) || (_MSC_VER > 1800) +/* VS2010 can't handle inline funcs, so skip it there */ +#define _LH_INLINE inline +#else +#define _LH_INLINE +#endif + +/** + * Calculate the hash of a key for a given table. + * + * This is an exension to support functions that need to calculate + * the hash several times and allows them to do it just once and then pass + * in the hash to all utility functions. Depending on use case, this can be a + * considerable performance improvement. + * @param t the table (used to obtain hash function) + * @param k a pointer to the key to lookup + * @return the key's hash + */ +static _LH_INLINE unsigned long lh_get_hash(const struct lh_table *t, const void *k) +{ + return t->hash_fn(k); +} + +#undef _LH_INLINE + +/** + * @deprecated Don't use this outside of linkhash.h: + */ +#ifdef __UNCONST +#define _LH_UNCONST(a) __UNCONST(a) +#else +#define _LH_UNCONST(a) ((void *)(uintptr_t)(const void *)(a)) +#endif + +/** + * Return a non-const version of lh_entry.k. + * + * lh_entry.k is const to indicate and help ensure that linkhash itself doesn't modify + * it, but callers are allowed to do what they want with it. + * See also lh_entry.k_is_constant + */ +#define lh_entry_k(entry) _LH_UNCONST((entry)->k) + +/** + * Return a non-const version of lh_entry.v. + * + * v is const to indicate and help ensure that linkhash itself doesn't modify + * it, but callers are allowed to do what they want with it. + */ +#define lh_entry_v(entry) _LH_UNCONST((entry)->v) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/json-c/math_compat.h b/json-c/math_compat.h new file mode 100644 index 0000000..d530537 --- /dev/null +++ b/json-c/math_compat.h @@ -0,0 +1,36 @@ +#ifndef __math_compat_h +#define __math_compat_h + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ + +/* Define isnan, isinf, infinity and nan on Windows/MSVC */ + +#ifndef HAVE_DECL_ISNAN +# ifdef HAVE_DECL__ISNAN +#include +#define isnan(x) _isnan(x) +# endif +#endif + +#ifndef HAVE_DECL_ISINF +# ifdef HAVE_DECL__FINITE +#include +#define isinf(x) (!_finite(x)) +# endif +#endif + +#ifndef HAVE_DECL_INFINITY +#include +#define INFINITY (DBL_MAX + DBL_MAX) +#define HAVE_DECL_INFINITY +#endif + +#ifndef HAVE_DECL_NAN +#define NAN (INFINITY - INFINITY) +#define HAVE_DECL_NAN +#endif + +#endif diff --git a/json-c/printbuf.c b/json-c/printbuf.c new file mode 100644 index 0000000..6c77b5d --- /dev/null +++ b/json-c/printbuf.c @@ -0,0 +1,155 @@ +/* + * $Id: printbuf.c,v 1.5 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + * + * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. + * The copyrights to the contents of this file are licensed under the MIT License + * (http://www.opensource.org/licenses/mit-license.php) + */ + +#include "config.h" + +#include +#include +#include + +#ifdef HAVE_STDARG_H +# include +#else /* !HAVE_STDARG_H */ +# error Not enough var arg support! +#endif /* HAVE_STDARG_H */ + +#include "debug.h" +#include "printbuf.h" +#include "snprintf_compat.h" +#include "vasprintf_compat.h" + +static int printbuf_extend(struct printbuf *p, int min_size); + +struct printbuf* printbuf_new(void) +{ + struct printbuf *p; + + p = (struct printbuf*)calloc(1, sizeof(struct printbuf)); + if(!p) return NULL; + p->size = 32; + p->bpos = 0; + if(!(p->buf = (char*)malloc(p->size))) { + free(p); + return NULL; + } + p->buf[0]= '\0'; + return p; +} + + +/** + * Extend the buffer p so it has a size of at least min_size. + * + * If the current size is large enough, nothing is changed. + * + * Note: this does not check the available space! The caller + * is responsible for performing those calculations. + */ +static int printbuf_extend(struct printbuf *p, int min_size) +{ + char *t; + int new_size; + + if (p->size >= min_size) + return 0; + + new_size = p->size * 2; + if (new_size < min_size + 8) + new_size = min_size + 8; +#ifdef PRINTBUF_DEBUG + MC_DEBUG("printbuf_memappend: realloc " + "bpos=%d min_size=%d old_size=%d new_size=%d\n", + p->bpos, min_size, p->size, new_size); +#endif /* PRINTBUF_DEBUG */ + if(!(t = (char*)realloc(p->buf, new_size))) + return -1; + p->size = new_size; + p->buf = t; + return 0; +} + +int printbuf_memappend(struct printbuf *p, const char *buf, int size) +{ + if (p->size <= p->bpos + size + 1) { + if (printbuf_extend(p, p->bpos + size + 1) < 0) + return -1; + } + memcpy(p->buf + p->bpos, buf, size); + p->bpos += size; + p->buf[p->bpos]= '\0'; + return size; +} + +int printbuf_memset(struct printbuf *pb, int offset, int charvalue, int len) +{ + int size_needed; + + if (offset == -1) + offset = pb->bpos; + size_needed = offset + len; + if (pb->size < size_needed) + { + if (printbuf_extend(pb, size_needed) < 0) + return -1; + } + + memset(pb->buf + offset, charvalue, len); + if (pb->bpos < size_needed) + pb->bpos = size_needed; + + return 0; +} + +int sprintbuf(struct printbuf *p, const char *msg, ...) +{ + va_list ap; + char *t; + int size; + char buf[128]; + + /* user stack buffer first */ + va_start(ap, msg); + size = vsnprintf(buf, 128, msg, ap); + va_end(ap); + /* if string is greater than stack buffer, then use dynamic string + with vasprintf. Note: some implementation of vsnprintf return -1 + if output is truncated whereas some return the number of bytes that + would have been written - this code handles both cases. */ + if(size == -1 || size > 127) { + va_start(ap, msg); + if((size = vasprintf(&t, msg, ap)) < 0) { va_end(ap); return -1; } + va_end(ap); + printbuf_memappend(p, t, size); + free(t); + return size; + } else { + printbuf_memappend(p, buf, size); + return size; + } +} + +void printbuf_reset(struct printbuf *p) +{ + p->buf[0] = '\0'; + p->bpos = 0; +} + +void printbuf_free(struct printbuf *p) +{ + if(p) { + free(p->buf); + free(p); + } +} diff --git a/json-c/printbuf.h b/json-c/printbuf.h new file mode 100644 index 0000000..567a6a0 --- /dev/null +++ b/json-c/printbuf.h @@ -0,0 +1,122 @@ +/* + * $Id: printbuf.h,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + * + * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. + * The copyrights to the contents of this file are licensed under the MIT License + * (http://www.opensource.org/licenses/mit-license.php) + */ + +/** + * @file + * @brief Internal string buffer handing. Unless you're writing a + * json_object_to_json_string_fn implementation for use with + * json_object_set_serializer() direct use of this is not + * recommended. + */ +#ifndef _printbuf_h_ +#define _printbuf_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct printbuf { + char *buf; + int bpos; + int size; +}; +typedef struct printbuf printbuf; + +extern struct printbuf* +printbuf_new(void); + +/* As an optimization, printbuf_memappend_fast() is defined as a macro + * that handles copying data if the buffer is large enough; otherwise + * it invokes printbuf_memappend() which performs the heavy + * lifting of realloc()ing the buffer and copying data. + * + * Your code should not use printbuf_memappend() directly unless it + * checks the return code. Use printbuf_memappend_fast() instead. + */ +extern int +printbuf_memappend(struct printbuf *p, const char *buf, int size); + +#define printbuf_memappend_fast(p, bufptr, bufsize) \ +do { \ + if ((p->size - p->bpos) > bufsize) { \ + memcpy(p->buf + p->bpos, (bufptr), bufsize); \ + p->bpos += bufsize; \ + p->buf[p->bpos]= '\0'; \ + } else { printbuf_memappend(p, (bufptr), bufsize); } \ +} while (0) + +#define printbuf_length(p) ((p)->bpos) + +/** + * Results in a compile error if the argument is not a string literal. + */ +#define _printbuf_check_literal(mystr) ("" mystr) + +/** + * This is an optimization wrapper around printbuf_memappend() that is useful + * for appending string literals. Since the size of string constants is known + * at compile time, using this macro can avoid a costly strlen() call. This is + * especially helpful when a constant string must be appended many times. If + * you got here because of a compilation error caused by passing something + * other than a string literal, use printbuf_memappend_fast() in conjunction + * with strlen(). + * + * See also: + * printbuf_memappend_fast() + * printbuf_memappend() + * sprintbuf() + */ +#define printbuf_strappend(pb, str) \ + printbuf_memappend ((pb), _printbuf_check_literal(str), sizeof(str) - 1) + +/** + * Set len bytes of the buffer to charvalue, starting at offset offset. + * Similar to calling memset(x, charvalue, len); + * + * The memory allocated for the buffer is extended as necessary. + * + * If offset is -1, this starts at the end of the current data in the buffer. + */ +extern int +printbuf_memset(struct printbuf *pb, int offset, int charvalue, int len); + +/** + * Formatted print to printbuf. + * + * This function is the most expensive of the available functions for appending + * string data to a printbuf and should be used only where convenience is more + * important than speed. Avoid using this function in high performance code or + * tight loops; in these scenarios, consider using snprintf() with a static + * buffer in conjunction with one of the printbuf_*append() functions. + * + * See also: + * printbuf_memappend_fast() + * printbuf_memappend() + * printbuf_strappend() + */ +extern int +sprintbuf(struct printbuf *p, const char *msg, ...); + +extern void +printbuf_reset(struct printbuf *p); + +extern void +printbuf_free(struct printbuf *p); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/json-c/random_seed.c b/json-c/random_seed.c new file mode 100644 index 0000000..3232777 --- /dev/null +++ b/json-c/random_seed.c @@ -0,0 +1,238 @@ +/* + * random_seed.c + * + * Copyright (c) 2013 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "strerror_override.h" +#include +#include "config.h" +#include "random_seed.h" + +#define DEBUG_SEED(s) + + +#if defined ENABLE_RDRAND + +/* cpuid */ + +#if defined __GNUC__ && (defined __i386__ || defined __x86_64__) +#define HAS_X86_CPUID 1 + +static void do_cpuid(int regs[], int h) +{ + __asm__ __volatile__( +#if defined __x86_64__ + "pushq %%rbx;\n" +#else + "pushl %%ebx;\n" +#endif + "cpuid;\n" +#if defined __x86_64__ + "popq %%rbx;\n" +#else + "popl %%ebx;\n" +#endif + : "=a"(regs[0]), [ebx] "=r"(regs[1]), "=c"(regs[2]), "=d"(regs[3]) + : "a"(h)); +} + +#elif defined _MSC_VER + +#define HAS_X86_CPUID 1 +#define do_cpuid __cpuid + +#endif + +/* has_rdrand */ + +#if HAS_X86_CPUID + +static int has_rdrand() +{ + // CPUID.01H:ECX.RDRAND[bit 30] == 1 + int regs[4]; + do_cpuid(regs, 1); + return (regs[2] & (1 << 30)) != 0; +} + +#endif + +/* get_rdrand_seed - GCC x86 and X64 */ + +#if defined __GNUC__ && (defined __i386__ || defined __x86_64__) + +#define HAVE_RDRAND 1 + +static int get_rdrand_seed() +{ + DEBUG_SEED("get_rdrand_seed"); + int _eax; + // rdrand eax + __asm__ __volatile__("1: .byte 0x0F\n" + " .byte 0xC7\n" + " .byte 0xF0\n" + " jnc 1b;\n" + : "=a" (_eax)); + return _eax; +} + +#endif + +#if defined _MSC_VER + +#if _MSC_VER >= 1700 +#define HAVE_RDRAND 1 + +/* get_rdrand_seed - Visual Studio 2012 and above */ + +static int get_rdrand_seed() +{ + DEBUG_SEED("get_rdrand_seed"); + int r; + while (_rdrand32_step(&r) == 0); + return r; +} + +#elif defined _M_IX86 +#define HAVE_RDRAND 1 + +/* get_rdrand_seed - Visual Studio 2010 and below - x86 only */ + +static int get_rdrand_seed() +{ + DEBUG_SEED("get_rdrand_seed"); + int _eax; +retry: + // rdrand eax + __asm _emit 0x0F __asm _emit 0xC7 __asm _emit 0xF0 + __asm jnc retry + __asm mov _eax, eax + return _eax; +} + +#endif +#endif + +#endif /* defined ENABLE_RDRAND */ + + +/* has_dev_urandom */ + +#if defined (__APPLE__) || defined(__unix__) || defined(__linux__) + +#include +#include +#include +#include +#include + +#define HAVE_DEV_RANDOM 1 + +static const char *dev_random_file = "/dev/urandom"; + +static int has_dev_urandom() +{ + struct stat buf; + if (stat(dev_random_file, &buf)) { + return 0; + } + return ((buf.st_mode & S_IFCHR) != 0); +} + + +/* get_dev_random_seed */ + +static int get_dev_random_seed() +{ + DEBUG_SEED("get_dev_random_seed"); + + int fd = open(dev_random_file, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "error opening %s: %s", dev_random_file, strerror(errno)); + exit(1); + } + + int r; + ssize_t nread = read(fd, &r, sizeof(r)); + if (nread != sizeof(r)) { + fprintf(stderr, "error short read %s: %s", dev_random_file, strerror(errno)); + exit(1); + } + + close(fd); + return r; +} + +#endif + + +/* get_cryptgenrandom_seed */ + +#ifdef WIN32 + +#define HAVE_CRYPTGENRANDOM 1 + +#include +#include +#ifndef __GNUC__ +#pragma comment(lib, "advapi32.lib") +#endif + +static int get_cryptgenrandom_seed() +{ + HCRYPTPROV hProvider = 0; + int r; + + DEBUG_SEED("get_cryptgenrandom_seed"); + + if (!CryptAcquireContextW(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { + fprintf(stderr, "error CryptAcquireContextW"); + exit(1); + } + + if (!CryptGenRandom(hProvider, sizeof(r), (BYTE*)&r)) { + fprintf(stderr, "error CryptGenRandom"); + exit(1); + } + + CryptReleaseContext(hProvider, 0); + + return r; +} + +#endif + + +/* get_time_seed */ + +#include + +static int get_time_seed() +{ + DEBUG_SEED("get_time_seed"); + + return (int)time(NULL) * 433494437; +} + + +/* json_c_get_random_seed */ + +int json_c_get_random_seed() +{ +#if HAVE_RDRAND + if (has_rdrand()) return get_rdrand_seed(); +#endif +#if HAVE_DEV_RANDOM + if (has_dev_urandom()) return get_dev_random_seed(); +#endif +#if HAVE_CRYPTGENRANDOM + return get_cryptgenrandom_seed(); +#endif + return get_time_seed(); +} diff --git a/json-c/random_seed.h b/json-c/random_seed.h new file mode 100644 index 0000000..2f43dad --- /dev/null +++ b/json-c/random_seed.h @@ -0,0 +1,29 @@ +/* + * random_seed.h + * + * Copyright (c) 2013 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ +#ifndef seed_h +#define seed_h + +#ifdef __cplusplus +extern "C" { +#endif + +extern int json_c_get_random_seed(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/json-c/snprintf_compat.h b/json-c/snprintf_compat.h new file mode 100644 index 0000000..8ca9b31 --- /dev/null +++ b/json-c/snprintf_compat.h @@ -0,0 +1,41 @@ +#ifndef __snprintf_compat_h +#define __snprintf_compat_h + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ + +/* + * Microsoft's _vsnprintf and _snprint don't always terminate + * the string, so use wrappers that ensure that. + */ + +#include + +#if !defined(HAVE_SNPRINTF) && defined(_MSC_VER) +static int json_c_vsnprintf(char *str, size_t size, const char *format, va_list ap) +{ + int ret; + ret = _vsnprintf(str, size, format, ap); + str[size - 1] = '\0'; + return ret; +} +#define vsnprintf json_c_vsnprintf + +static int json_c_snprintf(char *str, size_t size, const char *format, ...) +{ + va_list ap; + int ret; + va_start(ap, format); + ret = json_c_vsnprintf(str, size, format, ap); + va_end(ap); + return ret; +} +#define snprintf json_c_snprintf + +#elif !defined(HAVE_SNPRINTF) /* !HAVE_SNPRINTF */ +# error Need vsnprintf! +#endif /* !HAVE_SNPRINTF && defined(WIN32) */ + +#endif /* __snprintf_compat_h */ diff --git a/json-c/strdup_compat.h b/json-c/strdup_compat.h new file mode 100644 index 0000000..9c52359 --- /dev/null +++ b/json-c/strdup_compat.h @@ -0,0 +1,16 @@ +#ifndef __strdup_compat_h +#define __strdup_compat_h + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ + +#if !defined(HAVE_STRDUP) && defined(_MSC_VER) + /* MSC has the version as _strdup */ +# define strdup _strdup +#elif !defined(HAVE_STRDUP) +# error You do not have strdup on your system. +#endif /* HAVE_STRDUP */ + +#endif diff --git a/json-c/strerror_override.c b/json-c/strerror_override.c new file mode 100644 index 0000000..90a1581 --- /dev/null +++ b/json-c/strerror_override.c @@ -0,0 +1,101 @@ +#define STRERROR_OVERRIDE_IMPL 1 +#include "strerror_override.h" + +/* + * Override strerror() to get consistent output across platforms. + */ + +static struct { + int errno_value; + const char *errno_str; +} errno_list[] = { +#define STRINGIFY(x) #x +#define ENTRY(x) {x, &STRINGIFY(undef_ ## x)[6]} + ENTRY(EPERM), + ENTRY(ENOENT), + ENTRY(ESRCH), + ENTRY(EINTR), + ENTRY(EIO), + ENTRY(ENXIO), + ENTRY(E2BIG), + ENTRY(ENOEXEC), + ENTRY(EBADF), + ENTRY(ECHILD), + ENTRY(EDEADLK), + ENTRY(ENOMEM), + ENTRY(EACCES), + ENTRY(EFAULT), +#ifdef ENOTBLK + ENTRY(ENOTBLK), +#endif + ENTRY(EBUSY), + ENTRY(EEXIST), + ENTRY(EXDEV), + ENTRY(ENODEV), + ENTRY(ENOTDIR), + ENTRY(EISDIR), + ENTRY(EINVAL), + ENTRY(ENFILE), + ENTRY(EMFILE), + ENTRY(ENOTTY), +#ifdef ETXTBSY + ENTRY(ETXTBSY), +#endif + ENTRY(EFBIG), + ENTRY(ENOSPC), + ENTRY(ESPIPE), + ENTRY(EROFS), + ENTRY(EMLINK), + ENTRY(EPIPE), + ENTRY(EDOM), + ENTRY(ERANGE), + ENTRY(EAGAIN), + { 0, (char *)0 } +}; + +// Enabled during tests +int _json_c_strerror_enable = 0; + +#define PREFIX "ERRNO=" +static char errno_buf[128] = PREFIX; +char *_json_c_strerror(int errno_in) +{ + int start_idx; + char digbuf[20]; + int ii, jj; + + if (!_json_c_strerror_enable) + return strerror(errno_in); + + // Avoid standard functions, so we don't need to include any + // headers, or guess at signatures. + + for (ii = 0; errno_list[ii].errno_str != (char *)0; ii++) + { + const char *errno_str = errno_list[ii].errno_str; + if (errno_list[ii].errno_value != errno_in) + continue; + + for (start_idx = sizeof(PREFIX) - 1, jj = 0; errno_str[jj] != '\0'; jj++, start_idx++) + { + errno_buf[start_idx] = errno_str[jj]; + } + errno_buf[start_idx] = '\0'; + return errno_buf; + } + + // It's not one of the known errno values, return the numeric value. + for (ii = 0; errno_in > 10; errno_in /= 10, ii++) + { + digbuf[ii] = "0123456789"[(errno_in % 10)]; + } + digbuf[ii] = "0123456789"[(errno_in % 10)]; + + // Reverse the digits + for (start_idx = sizeof(PREFIX) - 1 ; ii >= 0; ii--, start_idx++) + { + errno_buf[start_idx] = digbuf[ii]; + } + return errno_buf; +} + diff --git a/json-c/strerror_override.h b/json-c/strerror_override.h new file mode 100644 index 0000000..5674381 --- /dev/null +++ b/json-c/strerror_override.h @@ -0,0 +1,30 @@ +#ifndef _json_strerror_override_h_ +#define _json_strerror_override_h_ + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ + +#include "config.h" +#include + +#include "json_object.h" /* for JSON_EXPORT */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +JSON_EXPORT char *_json_c_strerror(int errno_in); + +#ifndef STRERROR_OVERRIDE_IMPL +#define strerror _json_c_strerror +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _json_strerror_override_h_ */ diff --git a/json-c/strerror_override_private.h b/json-c/strerror_override_private.h new file mode 100644 index 0000000..5180b4f --- /dev/null +++ b/json-c/strerror_override_private.h @@ -0,0 +1,12 @@ +#ifndef __json_strerror_override_private_h__ +#define __json_strerror_override_private_h__ + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ + +/* Used by tests to get consistent output */ +extern int _json_c_strerror_enable; + +#endif diff --git a/json-c/vasprintf_compat.h b/json-c/vasprintf_compat.h new file mode 100644 index 0000000..43dbf89 --- /dev/null +++ b/json-c/vasprintf_compat.h @@ -0,0 +1,46 @@ +#ifndef __vasprintf_compat_h +#define __vasprintf_compat_h + +/** + * @file + * @brief Do not use, json-c internal, may be changed or removed at any time. + */ + +#include "snprintf_compat.h" + +#if !defined(HAVE_VASPRINTF) +/* CAW: compliant version of vasprintf */ +static int vasprintf(char **buf, const char *fmt, va_list ap) +{ +#ifndef WIN32 + static char _T_emptybuffer = '\0'; +#endif /* !defined(WIN32) */ + int chars; + char *b; + + if(!buf) { return -1; } + +#ifdef WIN32 + chars = _vscprintf(fmt, ap)+1; +#else /* !defined(WIN32) */ + /* CAW: RAWR! We have to hope to god here that vsnprintf doesn't overwrite + our buffer like on some 64bit sun systems.... but hey, its time to move on */ + chars = vsnprintf(&_T_emptybuffer, 0, fmt, ap)+1; + if(chars < 0) { chars *= -1; } /* CAW: old glibc versions have this problem */ +#endif /* defined(WIN32) */ + + b = (char*)malloc(sizeof(char)*chars); + if(!b) { return -1; } + + if((chars = vsprintf(b, fmt, ap)) < 0) + { + free(b); + } else { + *buf = b; + } + + return chars; +} +#endif /* !HAVE_VASPRINTF */ + +#endif /* __vasprintf_compat_h */ diff --git a/main.c b/main.c index 529c3a2..68413b1 100644 --- a/main.c +++ b/main.c @@ -3,15 +3,28 @@ #define DBG_TAG "main" #define DBG_LVL DBG_INFO #include "debug_print.h" +#if 0 +#include +#include +#include +#define DMA_START_TX _IOW('U', 0x10, struct dma_config) +struct dma_config { + void *buf; + size_t len; +}; +#endif pthread_t pt_uart_send; pthread_t pt_uart_recv_ack; pthread_t pt_uart_recv_data; pthread_t pt_uart_recv_back; pthread_t pt_mqtt_recv; pthread_t pt_readqr; +pthread_t pt_ota; +pthread_t pt_doota; pthread_t pt_mqtt; pthread_t pt_removelog; +pthread_t pt_reboot; pthread_t pt_watchdog; pthread_t pt_tagsearch; pthread_t pt_removeduplicatetag; @@ -22,21 +35,30 @@ mqtt_parm_t mqtt_parm={0}; mqtt_utils_t mqtt_config; struct input_event buff; -bool newappDownloaded=false; +bool isTimeToReboot=false; bool isLightOn=false; bool isLightOnByRule=false; bool isLabelUp=false; -bool isSearchLabel=false; +//bool isSearchLabel=false; +bool isSearchReport=false; bool isSearchLabelOn=false; bool isLightEnable=true; bool isSendComEnd=true; -jt_only_tag_t onlyTags[200]={0}; +bool isOtaEnable=false; +bool isLEDOtaSuccess=false; +bool isAPOtaSuccess=false; +bool isAPBack=false; +jt_only_tag_t onlyTags[5000]={0}; +jt_only_tag_t onlyTagsNew[5000]={0}; int tagCount=0; +int tagCountNew=0; +int tagIndex=0; +int otaCount=0; int fd; int UPCASE=0; int count_value=0; int getPayloadTime=120*1000;//usecond -char softwareVersion[16]="1.1.9"; +char softwareVersion[16]="1.1.11"; char stationsn[16]="TJ251372224247";//TJ250995217957 char productid[8]="10045"; char appKey[32]="fdhQmhqhvbL1cf1K9mUqt"; @@ -45,7 +67,7 @@ char mqttRawPassword[64]=""; char input_value[1024]={0};//94b4e39caf1d45ee9a071d701cc037b4 char input_value_copy[1024]={0}; char *getDevRegisterStatusUrl="https://gaea-zt-express.com/device/modifyRegisterInfo"; -char *logUploadUrl="http://pda.pupumall.net:1180/open/api/device/log/upload"; +//char *logUploadUrl="http://pda.pupumall.net:1180/open/api/device/log/upload"; char *getDevRawPasswordUrl="https://gaea.zt-express.com/device/modifyRegisterInfo"; char *hostDomain="tx-mqtt.zt-express.com"; char lightsn1[9]={0}; @@ -89,12 +111,12 @@ void do_removelog(void) fgets(buffer,sizeof(buffer),fp); logcount=atoi(buffer); LOG_I("logcount:%d\n",logcount); - if(logcount>=15){ - sprintf(syscmd,"ls Log.*|head -%d|xargs rm -fr",logcount-7); + if(logcount>=3){ + sprintf(syscmd,"ls Log.*|head -%d|xargs rm -fr",logcount-2); LOG_I("%s\n",syscmd); system(syscmd); }else{ - LOG_I("logcount less than 15,not remove\n"); + LOG_I("logcount less than 3,not remove\n"); } } @@ -115,22 +137,159 @@ void removeLog(){ } saved=atol(savedtime); LOG_I("now=%ld,saved=%ld,nowtime=%s,savedtime=%s\n",now,saved,nowtime,savedtime); - if(now-saved>=(60*60*24*30)){ + if(now-saved>=(60*60*24*5)){ //if(now-saved>=10){ buffer_to_file("logTime",nowtime,strlen(nowtime),"wb"); do_removelog(); } - sleep(60*60*10); + sleep(60*60*1); + } +} + +void checkOtaKey(void){ + int len=0; + int keycount=0; + char *readresult=NULL; + while(1){ + if(isOtaEnable){ + system("echo 0 > /sys/class/gpio/gpio113/value"); + sleep(1); + system("echo 1 > /sys/class/gpio/gpio113/value"); + sleep(1); + }else{ + readresult=file_to_buffer("/sys/class/gpio/gpio63/value",&len); + //LOG_I("resetKey:%s\n",readresult); + if(readresult!=NULL){ + if(strcmp(readresult,"0\n")==0){ + keycount++; + if(keycount==3){ + keycount=0; + isOtaEnable=true; + isLEDOtaSuccess=false; + isAPOtaSuccess=false; + isSendComEnd=true; + LOG_I("OTA enable\n"); + } + } + } + usleep(100*1000); + } + } +} + +void otaLeds(){ +#if 0 + int len=0,ver=0; + char *readresult=NULL; + char filename[128]={}; + uint16_t data_crc=0; + ver=getLedOtaVersion(filename); + LOG_I("ota %s ver:%d\n",filename,ver); + readresult=file_to_buffer(filename,&len); + if(readresult!=NULL){ + isSendComEnd=false; + LOG_I("data len:%d\n",len); + data_crc=CRC16_XMODEM(readresult,len); + LOG_I("crc :%04x\n",data_crc); + uart_data_send_head_ledota(&uartSend,len,data_crc,ver);//0xFF降级 + usleep(72*1000); + int i=0; + for(i=0;i /sys/class/gpio/export");//watchdog enable pin SGM820 - system("echo 112 > /sys/class/gpio/export");//feed watchdog pin 113 yellow - system("echo 113 > /sys/class/gpio/export");//feed watchdog pin 113 yellow + system("echo 112 > /sys/class/gpio/export");//feed watchdog pin 112 system("echo out > /sys/class/gpio/gpio1/direction"); system("echo out > /sys/class/gpio/gpio112/direction"); - system("echo out > /sys/class/gpio/gpio113/direction"); system("echo 1 > /sys/class/gpio/gpio1/value"); LOG_I("enable watchdog\n"); system("echo 1 > /sys/class/gpio/gpio112/value"); @@ -147,15 +306,15 @@ void feedWatchDog(){ } void rebootSystem(void){ - while(!newappDownloaded){ - if(!newappDownloaded){ + while(!isTimeToReboot){ + if(!isTimeToReboot){ struct tm *result; time_t timep; time (&timep); result = localtime(&timep); //printf("%d:%d:%d\n",result->tm_hour, result->tm_min, result->tm_sec); if(result->tm_hour==0 && result->tm_min==0 && result->tm_sec==10){ - newappDownloaded=true; + isTimeToReboot=true; LOG_I("prepare to reboot\n"); } } @@ -631,7 +790,7 @@ int readQrcode() error: return 1; } - +#if 0 void writeWpa(char *filename,char *ssid,char *psw){ LOG_I("%s\n",__func__); buffer_to_file(filename,"# WPA-PSK/TKIP\n",strlen("# WPA-PSK/TKIP\n"),"wb"); @@ -669,18 +828,70 @@ void writeWlan(char *filename,char *netway,char *gate,char *ip){ buffer_to_file(filename,"\n",strlen("\n"),"a"); } } +#endif +void writeNetworkInterface(char *filename,char *dest){ + buffer_to_file(filename,"# interfaces(5) file used by ifup(8) and ifdown(8)", + strlen("# interfaces(5) file used by ifup(8) and ifdown(8)"),"wb"); + buffer_to_file(filename,"\n",strlen("\n"),"a"); + buffer_to_file(filename,"# Include files from /etc/network/interfaces.d:",strlen("# Include files from /etc/network/interfaces.d:"),"a"); + buffer_to_file(filename,"\n",strlen("\n"),"a"); + buffer_to_file(filename,"source-directory /etc/network/interfaces.d",strlen("source-directory /etc/network/interfaces.d"),"a"); + buffer_to_file(filename,"\n",strlen("\n"),"a"); + buffer_to_file(filename,"auto lo",strlen("auto lo"),"a"); + buffer_to_file(filename,"\n",strlen("\n"),"a"); + buffer_to_file(filename,"iface lo inet loopback",strlen("iface lo inet loopback"),"a"); + buffer_to_file(filename,"\n",strlen("\n"),"a"); + buffer_to_file(filename,"auto eth0",strlen("auto eth0"),"a"); + buffer_to_file(filename,"\n",strlen("\n"),"a"); + buffer_to_file(filename,"iface eth0 inet dhcp",strlen("iface eth0 inet dhcp"),"a"); + buffer_to_file(filename,"\n",strlen("\n"),"a"); + buffer_to_file(filename,"# address 192.168.1.100",strlen("# address 192.168.1.100"),"a"); + buffer_to_file(filename,"\n",strlen("\n"),"a"); + buffer_to_file(filename,"# netmask 255.255.255.0",strlen("# netmask 255.255.255.0"),"a"); + buffer_to_file(filename,"\n",strlen("\n"),"a"); + buffer_to_file(filename,"# gateway 192.168.1.1",strlen("# gateway 192.168.1.1"),"a"); + buffer_to_file(filename,"\n",strlen("\n"),"a"); + buffer_to_file(filename,"hwaddress ether ",strlen("hwaddress ether "),"a"); + buffer_to_file(filename,dest,strlen(dest),"a"); + system("sync"); +} -void removeFile(char *filename){ - char cmd[32]={0}; - sprintf(cmd,"rm %s",filename); - //LOG_I("%s\n",cmd); - system(cmd); +char* insert_colon(const char* src, int interval) { + if (!src || interval <= 0) return NULL; + + size_t len = strlen(src); + if (len == 0) { // 空字符串返回空 + char* res = malloc(1); + if (res) res[0] = '\0'; + return res; + } + + // 计算新字符串长度 + size_t sections = (len + interval - 1) / interval; // 总段数 + size_t new_len = len + (sections - 1); // 新长度 = 原长 + 冒号数 + char* new_str = malloc(new_len + 1); // 分配内存:ml-citation{ref="2" data="citationList"} + if (!new_str) return NULL; + + size_t src_idx = 0, dst_idx = 0; + while (src_idx < len) { + // 计算本次复制的字符数 + size_t copy_size = (len - src_idx < (size_t)interval) ? (len - src_idx) : (size_t)interval; + memcpy(new_str + dst_idx, src + src_idx, copy_size); // 块复制:ml-citation{ref="3" data="citationList"} + src_idx += copy_size; + dst_idx += copy_size; + + // 非末尾段时插入冒号 + if (src_idx < len) new_str[dst_idx++] = ':'; + } + new_str[dst_idx] = '\0'; // 终止符:ml-citation{ref="7" data="citationList"} + return new_str; } void *actHandleQrcode(void *parm){ char *str=(char *)parm; LOG_I("scan:%s\n",str); if(strstr(str,"{")!=NULL){ +#if 0 char deviceSecret[64]={0}; char deviceCode[64]={0}; char netWorkType[64]={0}; @@ -720,9 +931,27 @@ void *actHandleQrcode(void *parm){ sleep(1); system("reboot"); } +#endif }else{ - if(strstr(str,"TJ")!=NULL && strlen(str)==14){ - buffer_to_file("savedDevSn",str,strlen(str),"wb"); + if(strstr(str,"TJ")!=NULL && strlen(str)==27){ + LOG_I("ok\n"); + char *result=NULL; + char *p; + result=strtok_r(str,":",&p); + while(result!=NULL){ + if(strstr(result,"TJ")!=NULL){ + LOG_I("save sn:%s\n",result); + buffer_to_file("savedDevSn",result,strlen(result),"wb"); + }else{ + //LOG_I("save mac:%s\n",result); + //buffer_to_file("savedDevMac",result,strlen(result),"wb"); + char* destmac = insert_colon(result, 2); + LOG_I("save mac:%s\n",destmac); + writeNetworkInterface("/etc/network/interfaces",destmac); + writeNetworkInterface("/etc/network/interfaces.default",destmac); + } + result=strtok_r(NULL,",",&p); + } } } pthread_exit(NULL); @@ -914,6 +1143,10 @@ error: } /*================================================================================*/ +void *thread_ota(void *arg){ + checkOtaKey(); +} + void *thread_mqtt(void *arg){ mqtt_init(); } @@ -922,6 +1155,10 @@ void *thread_removelog(void *arg){ removeLog(); } +void *thread_reboot(void *arg){ + rebootSystem(); +} + void *thread_feed_watchdog(void *arg){ feedWatchDog(); } @@ -933,19 +1170,32 @@ void *thread_readqr(void *arg){ void *thread_tag_search_send(void *arg){ while(1){ if(isSearchLabelOn){ - isSearchLabel=true; - uart_data_send_head_search(&uartSend,5); + //uart_data_send_head_search(&uartSend,5); sleep(10); searchTimeNew=getCurrentTime(); - LOG_I("searchTimeNew:%d\n",searchTimeNew); - if((searchTimeNew-searchTimeOld)>=searchTimeOut){ - isSearchLabelOn=false; + //LOG_I("searchTimeNew:%d\n",searchTimeNew); + if((searchTimeNew-searchTimeOld)>=(searchTimeOut-20)){ + isSearchReport=true; search_tag_report(); + isSearchReport=false; + isSearchLabelOn=false; } } } } +void *thread_do_ota(void *arg){ + while(1){ + if(isOtaEnable && isSendComEnd && !isLEDOtaSuccess){ + sleep(1); + otaLeds(); + } + //if(isOtaEnable && isSendComEnd && !isAPOtaSuccess){ + // otaAp(); + //} + } +} + //char *tag_1="00A04CCE"; //char *tag_2="00A04C0E"; //char *tag_3="00A04C0F"; @@ -996,12 +1246,12 @@ void *thread_uart_recv_ack(void *arg){ uart_data_send_lighton(&uartSend,led_ctrl.ch,atoi(flash),atoi(light),tag1,tag2,tag3,tag4,tag5,lightsnNum); isLightOn=false; } - +#if 0 if(isSearchLabel){ uart_data_send_search(&uartSend,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF); isSearchLabel=false; } - +#endif if(isLightOnByRule){ #if 0 uint16_t label2=strtol("FF01",NULL,16)&0xFFFF; @@ -1023,7 +1273,7 @@ void *thread_uart_recv_ack(void *arg){ uint16_t flash_i=strtol(mqtt_parm.msg_flashInterval,NULL,16)&0xFFFF; uint16_t light_d=strtol(mqtt_parm.msg_lightDuration,NULL,16)&0xFFFF; uint8_t changecolor=0; - //8灭 1红 2黄 3蓝 4绿 5青 6白 7紫 xitu + //8灭 1红 2黄 3蓝 4绿 5青 6白 7紫 tuxi //0灭 1蓝 2绿 3青 4红 5紫 6黄 7白 yitaile if(strcmp(mqtt_parm.msg_color,"1")==0){ changecolor=4; @@ -1080,10 +1330,29 @@ void *thread_uart_recv_ack(void *arg){ isLabelUp=false; } }else if(parm_ack==0x4646){ - isSendComEnd=true; + isSendComEnd=true; + }else if(parm_ack==0x4f4f){ + isSendComEnd=true; + if(isOtaEnable){ + otaCount++; + if(otaCount>=2){ + otaCount=0; + isLEDOtaSuccess=true; + isOtaEnable=false; + system("echo 1 > /sys/class/gpio/gpio113/value"); + } + } + }else if(parm_ack==0x5252){ + uart_data_receive_version(&uartSend); + isSendComEnd=true; + isAPOtaSuccess=true; + }else if(parm_ack==0x5454){ + isSendComEnd=true; + }else if(parm_ack==0x4848){ + isAPBack=true; } } - sleep(1); + usleep(100*1000); } } @@ -1127,14 +1396,19 @@ void *thread_uart_recv_back(void *arg){ &count,&batteryV,&version,&ledCtrl,&lable1,&lable2,&lable3); //LOG_I("recv_back:%04x,%08x,%02x,%02x,%02x,%02x,%04x,%04x,%02x,%02x,%04x,%08x\n",parmAck,tagCode,tagSignal,totalLen, //tagFeature,count,batteryV,version,ledCtrl,lable1,lable2,lable3); + //if(tagCode>0x90000000){ + // PutDataIntoQueue(tagCode,batteryV); + //} + PutDataIntoQueue(tagCode,batteryV); if(tagFeature==0x83){ - LOG_I("search tag:%08x,battery:%04x\n",tagCode,batteryV); - PutDataIntoQueue(tagCode,batteryV); + //LOG_I("search tag:%08x,battery:%04x\n",tagCode,batteryV); }else if(tagFeature==0x82){ - LOG_I("light down tag:%08x,battery:%04x\n",tagCode,batteryV); + //LOG_I("light down tag:%08x,battery:%04x\n",tagCode,batteryV); light_off_tag_report(tagCode,batteryV); }else if(tagFeature==0x81){ - LOG_I("key down tag:%08x,battery:%04x\n",tagCode,batteryV); + LOG_I("key down tag:%08x,battery:%04x,version:%4x\n",tagCode,batteryV,version); + }else if(tagFeature==0xff){ + //LOG_I("heart tag:%08x,battery:%04x,version:%4x\n",tagCode,batteryV,version); } } } @@ -1156,24 +1430,77 @@ void light_off_tag_report(uint32_t name,uint16_t batteryv){ } void search_tag_report(){ - json_object *lights = NULL; - lights=json_object_new_array(); - for(int i=0;i200){ + for(i=0;i /sys/class/gpio/export");//reset key + system("echo in > /sys/class/gpio/gpio63/direction"); + system("echo 113 > /sys/class/gpio/export");//pin 113 yellow + system("echo out > /sys/class/gpio/gpio113/direction"); + uart_open(&uartSend,"/dev/ttyS0");//GT U12 ttyS0,U14 ttyS4,U21 ttysWK0 U13 ttysWK1 U15 ttysWK2 U22 ttysWK3 U20 ttyS1 uart_init(&uartSend,115200,8,1,'N',0); - uart_open(&uartRecvData,"/dev/ttyS4"); + uart_open(&uartRecvData,"/dev/ttyS4");//DT uart_init(&uartRecvData,115200,8,1,'N',0); - - uart_open(&uartRecvBack,"/dev/ttysWK0"); + uart_open(&uartRecvBack,"/dev/ttysWK0");//HT uart_init(&uartRecvBack,115200,8,1,'N',0); +#if 0 + struct dma_config cfg = { + .buf = malloc(256), + .len = 256 + }; + // 填充测试数据 + memset(cfg.buf, 0x55, 256); + // 启动DMA传输 + ioctl(uartSend.uart_fd, DMA_START_TX, &cfg); +#endif //doCommand_help(0, NULL); - //sleep(5); #if 1 enableWatchDog(); ret = pthread_create(&pt_watchdog,NULL,thread_feed_watchdog,NULL); @@ -1535,6 +1923,15 @@ int main(int argc, char *argv[]) pthread_detach(pt_watchdog); } #endif + ret = pthread_create(&pt_reboot,NULL,thread_reboot,NULL); + if(ret!=0){ + LOG_I("pthread_create reboot fail\n"); + system("reboot"); + }else{ + LOG_I("pthread_create reboot success\n"); + pthread_detach(pt_reboot); + } + ret = pthread_create(&pt_uart_recv_ack,NULL,thread_uart_recv_ack,NULL); if(ret!=0){ LOG_I("pthread_create uart_recv_ack fail\n"); @@ -1574,6 +1971,7 @@ int main(int argc, char *argv[]) ret = pthread_create(&pt_tagsearch,NULL,thread_tag_search_send,NULL); if(ret!=0){ LOG_I("pthread_create tag search send fail\n"); + system("reboot"); }else{ LOG_I("pthread_create tag search success\n"); pthread_detach(pt_tagsearch); @@ -1582,6 +1980,7 @@ int main(int argc, char *argv[]) ret = pthread_create(&pt_removeduplicatetag,NULL,thread_remove_duplicate_tag,NULL); if(ret!=0){ LOG_I("pthread_create remove duplicate tag send fail\n"); + system("reboot"); }else{ LOG_I("pthread_create remove duplicate tag success\n"); pthread_detach(pt_removeduplicatetag); @@ -1596,25 +1995,42 @@ int main(int argc, char *argv[]) pthread_detach(pt_removelog); } + ret = pthread_create(&pt_ota,NULL,thread_ota,NULL); + if(ret!=0){ + LOG_I("pthread_create ota fail\n"); + system("reboot"); + }else{ + LOG_I("pthread_create ota success\n"); + pthread_detach(pt_ota); + } + + ret = pthread_create(&pt_doota,NULL,thread_do_ota,NULL); + if(ret!=0){ + LOG_I("pthread_create doota fail\n"); + system("reboot"); + }else{ + LOG_I("pthread_create doota success\n"); + pthread_detach(pt_doota); + } +#if 0 ret = pthread_create(&pt_readqr,NULL,thread_readqr,NULL); if(ret!=0){ LOG_I("pthread_create readqr fail\n"); - system("reboot"); }else{ LOG_I("pthread_create readqr success\n"); pthread_detach(pt_readqr); } - +#endif readresult=file_to_buffer("savedDevSn",&len); while(readresult==NULL){ readresult=file_to_buffer("savedDevSn",&len); sleep(5); - LOG_I("please scan sn\n"); + LOG_I("please scan sn use tx_test\n"); } strncpy(stationsn,readresult,len); readresult=NULL; LOG_I("saved stationsn:%s\n",stationsn); - +#if 1 readresult=file_to_buffer("mqttRawPassword",&len); if(readresult!=NULL){ strncpy(mqttRawPassword,readresult,len); @@ -1628,7 +2044,7 @@ int main(int argc, char *argv[]) sleep(3); } } - +#endif readresult=file_to_buffer("LightEnable",&len); if(readresult!=NULL){ if(strcmp(readresult,"enable")==0){ @@ -1638,7 +2054,7 @@ int main(int argc, char *argv[]) } readresult=NULL; } - +#if 1 while(1){ if((ping("8.8.8.8") == 0)||(ping("8.8.4.4") == 0)){ LOG_I("net ok\n"); @@ -1665,6 +2081,7 @@ int main(int argc, char *argv[]) sleep(3); } } +#endif #if 1 while(1) { diff --git a/main.h b/main.h index e356462..8941388 100644 --- a/main.h +++ b/main.h @@ -35,95 +35,13 @@ int wdt_val; #include "ping.h" #include "Hmacsha1.h" -#define SND_BATCHINBEGIN "S01" -#define SND_BATCHINEND "S02" -#define SND_BATCHOUTNORMALBEGIN "S03" -#define SND_BATCHOUTNORMALEND "S04" -#define SND_BATCHOUTREPAIRBEGIN "S05" -#define SND_BATCHOUTREPAIREND "S06" -#define SND_BORROWDEVBEGIN "S07" -#define SND_BORROWDEVEND "S08" -#define SND_RETURNDEVBEGIN "S09" -#define SND_RETURNDEVEND "S10" -#define SND_REPAIRDEVBEGIN "S11" -#define SND_REPAIRDEVEND "S12" -#define SND_REPAIRCELL "S13" -#define SND_DEVREADFAIL "S14" -#define SND_DEVSTILLIN "S15" -#define SND_DOORNOTCLOSED "S16" -#define SND_DOOROPENFAIL "S17" -#define SND_DOWNLOADFAIL "S18" -#define SND_DOWNLOADSUCCESS "S19" -#define SND_DOWNLOADING "S20" -#define SND_READDEVINFOBEGIN "S21" -#define SND_READDEVINFOEND "S22" -#define SND_NETDISCONNECTED "S23" -#define SND_NETWIFI "S24" -#define SND_NETWIFICONNECTBEGIN "S25" -#define SND_NETWIRED "S26" -#define SND_NETWIREDCONNECTBEGIN "S27" -#define SND_NETMOBILE "S28" -#define SND_NETMOBILECONNECTBEGIN "S29" -#define SND_MQTTCONNECTED "S30" -#define SND_MQTTDISCONNECTED "S31" -#define SND_WIFIPARMWRONG "S32" -#define SND_CABINETNOTCONFIGED "S33" -#define SND_SYSTEMBOOTEND "S34" -#define SND_DEVSNGETSUCCESS "S35" -#define SND_DEVSNGETFAIL "S36" -#define SND_DEVSNALREADYEXIST "S37" -#define SND_SCANSUCCESS "S38" -#define SND_DEVNOTBORROWED "S39" -#define SND_PARAMSETSUCCESS "S40" -#define SND_TIMEOUTBATCHIN "S41" -#define SND_TIMEOUTREPAIRDEVIN "S42" -#define SND_TIMEOUTRETURN "S43" - typedef struct { uint32_t name; uint16_t battery; + uint32_t time; }__attribute__((packed)) jt_only_tag_t; -void hgz_init_db_multy(mqtt_parm_t *mqtt_parm); -void hgz_init_db_single(mqtt_parm_t *mqtt_parm); -void hgz_reset_cells(mqtt_parm_t *mqtt_parm,char *operators); -void hgz_remove_db(char *cabinetName); -void hgz_remove_dev_by_cellname(char *cellname,int userid,char *username,char *batchid,char *optype,char *optime,char *opinfo); -void hgz_remove_dev_by_cellname_no_userid(char *cellname,char *batchid,char *optype,char *optime,char *opinfo); -void hgz_add_dev_by_cellname(char *cellname,int userid,char *username,char *devsn,long devpwr,char *optype,char *batchid, - char *optime,char *opinfo); -void hgz_repair_dev_by_cellname(char *cellname,int userid,char *username,char *devsn,long devpwr,char *batchid,char *optime,char *opinfo, - char *repairtype); -void hgz_repair_dev_by_cellname_misOperate(char *cellname,int userid,char *username,long devpwr,char *batchid,char *optime,char *opinfo, - char *repairtype); -void hgz_lose_dev_by_cellname(char *cellname,int userid,char *username,char *batchid,char *optime); -void hgz_repair_cell_by_cellname(char *cellname,int userid); -void hgz_reset_cell_by_cellname(char *cellname,int userid); -void hgz_get_cellstatus_by_cellname(char *cellname,char *cellstatus); -void hgz_get_devstatus_by_cellname(char *cellname,char *devstatus); -void hgz_get_idlecellnum_by_devtype(char *idlecellnum,char *devtype); -void hgz_get_cabinetnames(char *cabinetnames); -void hgz_get_devtypes(char *devtypes); -void hgz_get_repairdevcellnames_by_devtype(char *cellnames,char *devtype); -void hgz_get_indevcellnames_by_devtype(char *cellnames,char *devtype); -void hgz_get_repaircellcellnames_by_devtype(char *cellnames,char *devtype); -void hgz_get_activecellcellnames_by_devtype(char *cellnames,char *devtype); -void hgz_get_doublecheckcellcellnames_by_cabinetnames(char *cellnames,char *cabinetnames); -void hgz_get_activecellcellnames(char *cellnames); -void hgz_get_allcellnames(char *cellnames); -void hgz_get_idlecellcellnames_by_devtype(char *cellnames,char *devtype); -void hgz_select_cellnames_tosavedev_by_devtype(char *cellnames,char *devtype,int num); -void hgz_select_cellnames_tosavedev_by_operator(char *cellnames,int userid,char *operators); -void hgz_select_cellnames_totakenormaldev_by_devtype(char *cellnames,char *devtype,int num); -void hgz_select_cellnames_totakerepairdev_by_devtype(char *cellnames,char *devtype,int num); -void hgz_get_indevnum_by_devtype(char *indevnum,char *devtype); -void hgz_get_repairdevnum_by_devtype(char *repairdevnum,char *devtype); -void hgz_get_canborrowdevnum_by_devtype(char *canborrowdevnum,char *devtype); -void hgz_get_devsn_by_cellname(char *devsn,char *cellname); -void hgz_do_download(mqtt_parm_download_t *mqtt_parm); -int hgz_check_cabinet_status(void); - void myrand(char *randnum,int len); void removeValueFromMystr(char *oristr,char *val); void addValueToMystr(char *oristr,char *val); diff --git a/mqttRawPassword b/mqttRawPassword new file mode 100644 index 0000000..9ce2f76 --- /dev/null +++ b/mqttRawPassword @@ -0,0 +1 @@ +6bdb82a9953a4d1a8d6608b3a452baae \ No newline at end of file diff --git a/mqtt_utils/mqtt_utils.c b/mqtt_utils/mqtt_utils.c index 336d996..a0cfb24 100644 --- a/mqtt_utils/mqtt_utils.c +++ b/mqtt_utils/mqtt_utils.c @@ -67,6 +67,7 @@ void mqtt_server_light_status_report(char *sn,char *msg_id,char *scene,json_obje struct timeval tv; gettimeofday(&tv, NULL); snprintf(time_buffer,sizeof(time_buffer),"%ld",tv.tv_sec*1000+tv.tv_usec); + LOG_I("%s\n",__func__); root = json_object_new_object(); if(root == NULL){ @@ -98,9 +99,9 @@ void mqtt_server_light_status_report(char *sn,char *msg_id,char *scene,json_obje json_object_object_add(root, "msg", msg); snprintf(topic, sizeof(topic), "iot/%s/message/report",mqtt_conf->productcode); - LOG_I("publish topic:[%s]\n", topic); + //LOG_I("publish topic:[%s]\n", topic); payload = json_object_to_json_string(root); - LOG_I("payload[%d][%s]\n", strlen(payload), payload); + //LOG_I("payload[%d][%s]\n", strlen(payload), payload); mqtt_utils_publish(mqtt_conf, topic, 2, payload, strlen(payload)); json_error: json_object_put(root); @@ -118,8 +119,8 @@ void mqtt_server_station_status_report(char *msg_id,char *product_id,char *sn,ch struct timeval tv; gettimeofday(&tv, NULL); snprintf(time_buffer,sizeof(time_buffer),"%ld",tv.tv_sec*1000+tv.tv_usec); + LOG_I("%s\n",__func__); - //LOG_I("%s\n", __func__); root = json_object_new_object(); if(root == NULL){ LOG_I("json_object_new_object error\n"); @@ -151,9 +152,9 @@ void mqtt_server_station_status_report(char *msg_id,char *product_id,char *sn,ch json_object_object_add(msg, "msgType", json_object_new_string("9008")); json_object_object_add(root, "msg", msg); snprintf(topic, sizeof(topic), "iot/%s/message/report",mqtt_conf->productcode); - LOG_I("publish topic:[%s]\n", topic); + //LOG_I("publish topic:[%s]\n", topic); payload = json_object_to_json_string(root); - LOG_I("payload[%d][%s]\n", strlen(payload), payload); + //LOG_I("payload[%d][%s]\n", strlen(payload), payload); mqtt_utils_publish(mqtt_conf, topic, 2, payload, strlen(payload)); json_error: json_object_put(root); @@ -165,7 +166,7 @@ void mqtt_service_reply(char *sn,char *msg_id,char *msg,int success,char *produc const char *payload = NULL; json_object *root = NULL; - //LOG_I("%s\n", __func__); + LOG_I("%s\n", __func__); root = json_object_new_object(); if(root == NULL){ LOG_I("json_object_new_object error\n"); @@ -179,9 +180,9 @@ void mqtt_service_reply(char *sn,char *msg_id,char *msg,int success,char *produc json_object_object_add(root, "productId", json_object_new_string(mqtt_conf->productcode)); snprintf(topic, sizeof(topic), "iot/%s/adviceDevice/reply",mqtt_conf->productcode); - LOG_I("publish topic:[%s]\n", topic); + //LOG_I("publish topic:[%s]\n", topic); payload = json_object_to_json_string(root); - LOG_I("payload[%d][%s]\n", strlen(payload), payload); + //LOG_I("payload[%d][%s]\n", strlen(payload), payload); mqtt_utils_publish(mqtt_conf, topic, 2, payload, strlen(payload)); json_error: diff --git a/net_utils/http_utils.c b/net_utils/http_utils.c index c41286d..7d4fbf4 100644 --- a/net_utils/http_utils.c +++ b/net_utils/http_utils.c @@ -1056,7 +1056,7 @@ int http_download(char *url, char *msg_id,int task_id,char *filepath) if (written_len == http_response.content_length) { LOG_I("Download %s successed\n", file_name); sleep(2); - newappDownloaded=true; + //newappDownloaded=true; ret = 0; } else { LOG_I("Download %s failed\n", file_name); diff --git a/savedDevSn b/savedDevSn new file mode 100644 index 0000000..f7324c8 --- /dev/null +++ b/savedDevSn @@ -0,0 +1 @@ +TJ251679787196 \ No newline at end of file diff --git a/ssh.sh b/ssh.sh index 3e69811..48762d1 100755 --- a/ssh.sh +++ b/ssh.sh @@ -1,3 +1,3 @@ -sshpass -p "&Over#B0Ost!" ssh root@10.10.10.235 -#sshpass -p "PddloTSecPwdOnly!" ssh root@10.10.10.132 -#sshpass -p "PddloTSecPwdOnly!" ssh root@192.168.0.148 +#sshpass -p "&Over#B0Ost!" ssh root@10.10.12.3 +sshpass -p "TxApPwd#2025!" ssh root@10.10.12.2 +#sshpass -p "PddloTSecPwdOnly!" ssh root@10.10.12.4 diff --git a/start.sh b/start_v1.1.9.sh similarity index 100% rename from start.sh rename to start_v1.1.9.sh diff --git a/tx_ota/F8_20250519_V1.0.33_1422_W02CL0BSL_0x21.bin b/tx_ota/F8_20250519_V1.0.33_1422_W02CL0BSL_0x21.bin new file mode 100755 index 0000000..aacb25e Binary files /dev/null and b/tx_ota/F8_20250519_V1.0.33_1422_W02CL0BSL_0x21.bin differ diff --git a/tx_ota/start.sh b/tx_ota/start.sh new file mode 100755 index 0000000..c0e0544 --- /dev/null +++ b/tx_ota/start.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +cd /root +filename=/root/ota/tx_server +filesize=`ls -l $filename | awk '{ print $5 }'` +maxsize=$((440000)) +if [ $filesize -gt $maxsize ] +then + echo "$filesize > $maxsize copy" + cp $filename . + chmod 777 tx_server + ./tx_server +else + echo "$filesize < $maxsize not copy" + chmod 777 tx_server + ./tx_server +fi diff --git a/tx_ota/tx_server b/tx_ota/tx_server new file mode 100755 index 0000000..f4bc7ce Binary files /dev/null and b/tx_ota/tx_server differ diff --git a/tx_ota/tx_test b/tx_ota/tx_test new file mode 100755 index 0000000..89b0d0d Binary files /dev/null and b/tx_ota/tx_test differ diff --git a/tx_server_v1.1.10 b/tx_server_v1.1.10 new file mode 100755 index 0000000..550fc95 Binary files /dev/null and b/tx_server_v1.1.10 differ diff --git a/tx_server_v1.1.9 b/tx_server_v1.1.9 new file mode 100755 index 0000000..c36b1ed Binary files /dev/null and b/tx_server_v1.1.9 differ diff --git a/uart_can/uart_can.c b/uart_can/uart_can.c index 0457483..77c63bb 100644 --- a/uart_can/uart_can.c +++ b/uart_can/uart_can.c @@ -8,6 +8,29 @@ #define DBG_TAG "uart_can" #define DBG_LVL DBG_INFO #include "debug_print.h" +uint16_t CRC16_APOTA( unsigned char * pdat, unsigned int len) +{ + const uint16_t CRC_16_IBM_TableOTA[16] = + { + 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, + 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022 + }; + uint16_t i, j; + uint16_t crc = 0xffff; + uint8_t val; + + for (i = 0; i < len; i++) + { + val = pdat[i]; + for(j = 0; j < 2; j++) + { + crc = (crc << 4) ^ CRC_16_IBM_TableOTA[(((crc >> 8) ^ val) >> 4) & 0x0F]; + val <<= 4; + } + } + + return (crc); +} uint16_t CRC16_XMODEM(uint8_t *puchMsg, uint32_t usDataLen) { @@ -29,6 +52,139 @@ uint16_t CRC16_XMODEM(uint8_t *puchMsg, uint32_t usDataLen) return (wCRCin); } +int uart_data_send_head_apota(uart_utils_t *uart,uint32_t datalen,uint16_t packagenum){ + int ret = 0; + jt_apota_package_t jt_apota_package ={ + .head=ntohl(0x404F5441),//@OTA + .len1=(datalen>>16)&0xFF, + .len2=(datalen>>8)&0xFF, + .len3=datalen&0xFF, + .packagenum=ntohs(packagenum), + .reserve=ntohs(0x0000), + }; + LOG_I("%s:HEAD:%08x,%02x,%02x,%02x,%04x,%04x\r\n",__func__,jt_apota_package.head, + jt_apota_package.len1,jt_apota_package.len2,jt_apota_package.len3,jt_apota_package.packagenum,jt_apota_package.reserve); + + if (uart == NULL){ + LOG_I("uart NULL pointer\n"); + ret = -1; + goto error; + } + if(sizeof(jt_apota_package) == write(uart->uart_fd, &jt_apota_package, sizeof(jt_apota_package))){ + ret = 0; + //LOG_I("%s success\n",__func__); + }else{ + //LOG_I("%s fail\n", __func__); + ret = -2; + goto error; + } +error: + return ret; + return 0; +} + + +int uart_data_send_head_ledota(uart_utils_t *uart,uint32_t datalen,uint16_t datacrc,uint16_t version){ + int ret = 0; + jt_ledota_package_t jt_ledota_package ={ + .head=ntohl(0x404F5457),//@OTW + .len1=(datalen>>16)&0xFF, + .len2=(datalen>>8)&0xFF, + .len3=datalen&0xFF, + .data_crc=ntohs(datacrc), + .hdinfo=0x21, + .sfinfo=ntohs(version), + .custom_code=ntohs(0x0003), + .mode=0xF8, +#if 1 + .startmac1=0x00, + .startmac2=0x00, + .startmac3=0x00, + .endmac1=0xff, + .endmac2=0xff, + .endmac3=0xff, +#else + .startmac1=0xfe, + .startmac2=0x64, + .startmac3=0xa8, + .endmac1=0xfe, + .endmac2=0x64, + .endmac3=0xa8, +#endif + }; + LOG_I("%s:HEAD:%08x,%02x,%02x,%02x,%04x,%02x,%04x,%04x,%02x,%02x,%02x,%02x,%02x,%02x,%02x\r\n",__func__,jt_ledota_package.head, + jt_ledota_package.len1,jt_ledota_package.len2,jt_ledota_package.len3,jt_ledota_package.data_crc,jt_ledota_package.hdinfo, + jt_ledota_package.sfinfo,jt_ledota_package.custom_code,jt_ledota_package.mode,jt_ledota_package.startmac1, + jt_ledota_package.startmac2,jt_ledota_package.startmac3,jt_ledota_package.endmac1,jt_ledota_package.endmac2, + jt_ledota_package.endmac3); + + if (uart == NULL){ + LOG_I("uart NULL pointer\n"); + ret = -1; + goto error; + } + if(sizeof(jt_ledota_package) == write(uart->uart_fd, &jt_ledota_package, sizeof(jt_ledota_package))){ + ret = 0; + //LOG_I("%s success\n",__func__); + }else{ + //LOG_I("%s fail\n", __func__); + ret = -2; + goto error; + } + usleep(100*1000); +error: + return ret; + return 0; + +} + +int uart_data_send_ledota(uart_utils_t *uart,char *data,int size){ + int ret=0; + if(size==write(uart->uart_fd,data,size)){ + LOG_I("write size:%d success\n",size); + }else{ + LOG_I("write size:%d fail\n",size); + } + usleep(500*1000); + return ret; +} + +int uart_data_send_apota(uart_utils_t *uart,uint8_t dataori[],int datalen,uint16_t pindex,uint16_t datacrc){ + int ret = 0; + int sendlen = 0; + jt_apota_data_package_t jt_apota_data_package ={ + .start=0x24,//$ + .len1=(datalen>>16)&0xFF, + .len2=(datalen>>8)&0xFF, + .len3=datalen&0xFF, + .packageindex=ntohs(pindex), + //.data=dataori, + .data_crc=ntohs(datacrc), + }; + if(datalen>2048){ + sendlen=2048; + }else{ + sendlen=datalen; + } + memcpy(jt_apota_data_package.data,dataori,sendlen); + if (uart == NULL){ + LOG_I("uart NULL pointer\n"); + ret = -1; + goto error; + } + if(sizeof(jt_apota_data_package) == write(uart->uart_fd, &jt_apota_data_package, sizeof(jt_apota_data_package))){ + ret = 0; + LOG_I("%s success\n",__func__); + }else{ + LOG_I("%s fail\n", __func__); + ret = -2; + goto error; + } +error: + return ret; + return 0; +} + int uart_data_send_head_lighton(uart_utils_t *uart,uint8_t wakeup_time,uint16_t tag_num){ int ret = 0; uint8_t data_len=0; @@ -766,6 +922,20 @@ error: return 0; } +int uart_data_receive_version(uart_utils_t *uart){ + int ret = 0; + jt_receive_version_package_t jt_receive_version_package; + ret = uart_read_until_time(uart->uart_fd,(char *)&jt_receive_version_package,sizeof(jt_receive_version_package_t), 1000, 50); + if(ret == sizeof(jt_receive_version_package_t)){ + LOG_I("%s success:%04x,%04x\n", __func__,jt_receive_version_package.v1,jt_receive_version_package.v2); + }else{ + LOG_I("%s, failed, time out\n", __func__); + ret = -1; + } + + return ret; +} + int uart_data_receive_ack(uart_utils_t *uart,uint16_t *parm_ack){ int ret = 0; jt_receive_package_t jt_receive_package; diff --git a/uart_can/uart_can.h b/uart_can/uart_can.h index bef5c73..d114e8c 100644 --- a/uart_can/uart_can.h +++ b/uart_can/uart_can.h @@ -246,7 +246,54 @@ typedef struct uint16_t crc; }__attribute__((packed)) jt_search_package_t; -uint16_t CRC16_XMODEM(uint8_t *puchMsg, uint32_t usDataLen); +typedef struct +{ + uint32_t head; + uint8_t len1; + uint8_t len2; + uint8_t len3; + uint16_t data_crc; + uint8_t hdinfo; + uint16_t sfinfo; + uint16_t custom_code; + uint8_t mode; + uint8_t startmac1; + uint8_t startmac2; + uint8_t startmac3; + uint8_t endmac1; + uint8_t endmac2; + uint8_t endmac3; +}__attribute__((packed)) jt_ledota_package_t; + +typedef struct +{ + uint32_t head; + uint8_t len1; + uint8_t len2; + uint8_t len3; + uint16_t packagenum; + uint16_t reserve; +}__attribute__((packed)) jt_apota_package_t; + +typedef struct +{ + uint8_t start; + uint8_t len1; + uint8_t len2; + uint8_t len3; + uint16_t packageindex; + uint8_t data[2048]; + uint16_t data_crc; +}__attribute__((packed)) jt_apota_data_package_t; + +typedef struct +{ + uint16_t v1; + uint16_t v2; +}__attribute__((packed)) jt_receive_version_package_t; + +uint16_t CRC16_XMODEM(uint8_t *di, uint32_t len); +uint16_t CRC16_APOTA( unsigned char * pdat, unsigned int len); int uart_data_send_head_lighton(uart_utils_t *uart,uint8_t wakeup_time,uint16_t tag_num); int uart_data_send_head_lableup(uart_utils_t *uart,uint8_t wakeup_time,uint16_t tag_num); @@ -271,4 +318,8 @@ int uart_data_receive_data_back(uart_utils_t *uart,uint16_t *parmAck,uint32_t *t uint8_t *totalLen,uint8_t *tagFeature,uint8_t *count,uint16_t *batteryV,uint16_t *version,uint8_t *ledCtrl, uint8_t *lable1,uint16_t *lable2,uint32_t *lable3); int uart_data_receive_ack(uart_utils_t *uart,uint16_t *parm_ack); +int uart_data_send_head_ledota(uart_utils_t *uart,uint32_t datalen,uint16_t datacrc,uint16_t version); +int uart_data_send_ledota(uart_utils_t *uart,char *data,int size); +int uart_data_send_head_apota(uart_utils_t *uart,uint32_t datalen,uint16_t packagenum); +int uart_data_send_apota(uart_utils_t *uart,uint8_t dataori[],int datalen,uint16_t pnum,uint16_t datacrc); #endif