diff options
Diffstat (limited to 'util_pkt_logger')
| -rw-r--r-- | util_pkt_logger/Makefile | 82 | ||||
| -rw-r--r-- | util_pkt_logger/cfg/global_conf.cn470.json | 51 | ||||
| -rw-r--r-- | util_pkt_logger/cfg/global_conf.empty.json | 66 | ||||
| -rw-r--r-- | util_pkt_logger/cfg/global_conf.eu433.json | 35 | ||||
| -rw-r--r-- | util_pkt_logger/cfg/global_conf.eu868.json | 45 | ||||
| -rw-r--r-- | util_pkt_logger/cfg/global_conf.us915.json | 46 | ||||
| -rw-r--r-- | util_pkt_logger/inc/parson.h | 111 | ||||
| -rw-r--r-- | util_pkt_logger/local_conf.json | 5 | ||||
| -rw-r--r-- | util_pkt_logger/obj/.gitkeep | 0 | ||||
| -rw-r--r-- | util_pkt_logger/readme.md | 143 | ||||
| -rw-r--r-- | util_pkt_logger/src/parson.c | 782 | ||||
| -rw-r--r-- | util_pkt_logger/src/util_pkt_logger.c | 570 |
12 files changed, 1936 insertions, 0 deletions
diff --git a/util_pkt_logger/Makefile b/util_pkt_logger/Makefile new file mode 100644 index 0000000..2c99dde --- /dev/null +++ b/util_pkt_logger/Makefile @@ -0,0 +1,82 @@ +### Application-specific constants + +APP_NAME := util_pkt_logger + +### Environment constants + +LGW_PATH := ../libloragw +CROSS_COMPILE := + +### External constant definitions +# must get library build option to know if mpsse must be linked or not + +include $(LGW_PATH)/library.cfg + +### Constant symbols + +CC := $(CROSS_COMPILE)gcc +AR := $(CROSS_COMPILE)ar + +CFLAGS=-O2 -Wall -Wextra -std=c99 -Iinc -I. + +### Constants for LoRa concentrator HAL library +# List the library sub-modules that are used by the application + +LGW_INC = $(LGW_PATH)/inc/config.h +LGW_INC += $(LGW_PATH)/inc/loragw_hal.h + +### Linking options + +ifeq ($(CFG_SPI),native) + LIBS := -lloragw -lrt +else ifeq ($(CFG_SPI),ftdi) + LIBS := -lloragw -lrt -lmpsse +endif + +### General build targets + +all: $(APP_NAME) global_conf.json + +clean: + rm -f obj/*.o + rm -f $(APP_NAME) + find . -name global_conf.json -exec rm -i {} \; + +### HAL library + +$(LGW_PATH)/libloragw.a: $(LGW_INC) + $(MAKE) all -C $(LGW_PATH) + +### Sub-modules compilation + +obj/parson.o: src/parson.c inc/parson.h + $(CC) -c $(CFLAGS) $< -o $@ + +### Select the proper configuration JSON for the program + +ifeq ($(CFG_BAND),eu868) +global_conf.json: cfg/global_conf.eu868.json + cp $< $@ +else ifeq ($(CFG_BAND),us915) +global_conf.json: cfg/global_conf.us915.json + cp $< $@ +else ifeq ($(CFG_BAND),cn470) +global_conf.json: cfg/global_conf.cn470.json + cp $< $@ +else ifeq ($(CFG_BAND),eu433) +global_conf.json: cfg/global_conf.eu433.json + cp $< $@ +else +global_conf.json: cfg/global_conf.empty.json + cp $< $@ +endif + +### Main program compilation and assembly + +obj/$(APP_NAME).o: src/$(APP_NAME).c $(LGW_INC) inc/parson.h + $(CC) -c $(CFLAGS) -I$(LGW_PATH)/inc $< -o $@ + +$(APP_NAME): obj/$(APP_NAME).o $(LGW_PATH)/libloragw.a obj/parson.o + $(CC) -L$(LGW_PATH) $< obj/parson.o -o $@ $(LIBS) + +### EOF
\ No newline at end of file diff --git a/util_pkt_logger/cfg/global_conf.cn470.json b/util_pkt_logger/cfg/global_conf.cn470.json new file mode 100644 index 0000000..26c7e63 --- /dev/null +++ b/util_pkt_logger/cfg/global_conf.cn470.json @@ -0,0 +1,51 @@ +{ + "SX1301_conf": { + "radio_0": { + "enable": true, + "freq": 471500000 + }, + "chan_multiSF_0": { + /* LoRa MAC channel, 125kHz, all SF, 471.1 MHz */ + "enable": true, + "radio": 0, + "if": -400000 + }, + "chan_multiSF_1": { + /* LoRa MAC channel, 125kHz, all SF, 471.3 MHz */ + "enable": true, + "radio": 0, + "if": -200000 + }, + "chan_multiSF_2": { + /* LoRa MAC channel, 125kHz, all SF, 471.5 MHz */ + "enable": true, + "radio": 0, + "if": 0 + }, + "chan_multiSF_3": { + /* LoRa MAC channel, 125kHz, all SF, 471.7 MHz */ + "enable": true, + "radio": 0, + "if": 200000 + }, + "chan_Lora_std": { + /* LoRa MAC channel, 125kHz, SF7, 471.9 MHz */ + "enable": true, + "radio": 0, + "if": 400000, + "bandwidth": 125000, + "spread_factor": 7 + }, + "chan_FSK": { + /* FSK 64kbps channel, 471.9 MHz */ + "enable": true, + "radio": 0, + "if": 400000, + "bandwidth": 200000, + "datarate": 64000 + } + }, + "gateway_conf": { + "gateway_ID": "AA555A0000000000" + } +} diff --git a/util_pkt_logger/cfg/global_conf.empty.json b/util_pkt_logger/cfg/global_conf.empty.json new file mode 100644 index 0000000..697e161 --- /dev/null +++ b/util_pkt_logger/cfg/global_conf.empty.json @@ -0,0 +1,66 @@ +{ + "SX1301_conf": { + "radio_0": { + "enable": false, + "freq": 0 + }, + "chan_multiSF_0": { + "enable": false, + "radio": 0, + "if": 0 + }, + "chan_multiSF_1": { + "enable": false, + "radio": 0, + "if": 0 + }, + "chan_multiSF_2": { + "enable": false, + "radio": 0, + "if": 0 + }, + "chan_multiSF_3": { + "enable": false, + "radio": 0, + "if": 0 + }, + "chan_multiSF_4": { + "enable": false, + "radio": 0, + "if": 0 + }, + "chan_multiSF_5": { + "enable": false, + "radio": 0, + "if": 0 + }, + "chan_multiSF_6": { + "enable": false, + "radio": 0, + "if": 0 + }, + "chan_multiSF_7": { + "enable": false, + "radio": 0, + "if": 0 + }, + "chan_Lora_std": { + "enable": false, + "radio": 0, + "if": 0, + "bandwidth": 250000, + "spread_factor": 7 + }, + + "chan_FSK": { + "enable": false, + "radio": 0, + "if": 100000, + "bandwidth": 250000, + "datarate": 100000 + } + }, + "gateway_conf": { + "gateway_ID": "AA555A0000000000" + } +} diff --git a/util_pkt_logger/cfg/global_conf.eu433.json b/util_pkt_logger/cfg/global_conf.eu433.json new file mode 100644 index 0000000..9988809 --- /dev/null +++ b/util_pkt_logger/cfg/global_conf.eu433.json @@ -0,0 +1,35 @@ +{ + "SX1301_conf": { + "radio_0": { + "enable": true, + "freq": 433500000 + }, + "chan_multiSF_0": { + /* LoRa MAC channel, 125kHz, all SF, 433.2 MHz */ + "enable": true, + "radio": 0, + "if": -300000 + }, + "chan_multiSF_1": { + /* LoRa MAC channel, 125kHz, all SF, 433.4 MHz */ + "enable": true, + "radio": 0, + "if": -100000 + }, + "chan_multiSF_2": { + /* LoRa MAC channel, 125kHz, all SF, 433.6 MHz */ + "enable": true, + "radio": 0, + "if": 100000 + }, + "chan_multiSF_3": { + /* LoRa MAC channel, 125kHz, all SF, 433.8 MHz */ + "enable": true, + "radio": 0, + "if": 300000 + } + }, + "gateway_conf": { + "gateway_ID": "AA555A0000000000" + } +} diff --git a/util_pkt_logger/cfg/global_conf.eu868.json b/util_pkt_logger/cfg/global_conf.eu868.json new file mode 100644 index 0000000..2d7ae96 --- /dev/null +++ b/util_pkt_logger/cfg/global_conf.eu868.json @@ -0,0 +1,45 @@ +{ + "SX1301_conf": { + "radio_0": { + "enable": true, + "freq": 868200000 + }, + "chan_multiSF_0": { + /* LoRa MAC channel, 125kHz, all SF, 868.1 MHz */ + "enable": true, + "radio": 0, + "if": -100000 + }, + "chan_multiSF_1": { + /* LoRa MAC channel, 125kHz, all SF, 868.3 MHz */ + "enable": true, + "radio": 0, + "if": 100000 + }, + "chan_multiSF_2": { + /* LoRa MAC channel, 125kHz, all SF, 868.5 MHz */ + "enable": true, + "radio": 0, + "if": 300000 + }, + "chan_Lora_std": { + /* LoRa MAC channel, 250kHz, SF7, 868.3 MHz */ + "enable": true, + "radio": 0, + "if": 100000, + "bandwidth": 250000, + "spread_factor": 7 + }, + "chan_FSK": { + /* FSK 100kbps channel, 868.3 MHz */ + "enable": true, + "radio": 0, + "if": 100000, + "bandwidth": 250000, + "datarate": 100000 + } + }, + "gateway_conf": { + "gateway_ID": "AA555A0000000000" + } +} diff --git a/util_pkt_logger/cfg/global_conf.us915.json b/util_pkt_logger/cfg/global_conf.us915.json new file mode 100644 index 0000000..c57c536 --- /dev/null +++ b/util_pkt_logger/cfg/global_conf.us915.json @@ -0,0 +1,46 @@ +{ + "SX1301_conf": { + "radio_0": { + "enable": true, + "freq": 927000000 + }, + "chan_multiSF_0": { + /* LoRa MAC channel, 125kHz, all SF, 926.7 MHz */ + "enable": true, + "radio": 0, + "if": -300000 + }, + "chan_multiSF_1": { + /* LoRa MAC channel, 125kHz, all SF, 926.9 MHz */ + "enable": true, + "radio": 0, + "if": -100000 + }, + "chan_multiSF_2": { + /* LoRa MAC channel, 125kHz, all SF, 927.1 MHz */ + "enable": true, + "radio": 0, + "if": 100000 + }, + "chan_Lora_std": { + /* LoRa MAC channel, 250kHz, SF7, 927.3 MHz */ + "enable": true, + "radio": 0, + "if": 300000, + "bandwidth": 250000, + "spread_factor": 7 + }, + + "chan_FSK": { + /* FSK 100kbps channel, 927.3 MHz */ + "enable": true, + "radio": 0, + "if": 300000, + "bandwidth": 250000, + "datarate": 100000 + } + }, + "gateway_conf": { + "gateway_ID": "AA555A0000000000" + } +} diff --git a/util_pkt_logger/inc/parson.h b/util_pkt_logger/inc/parson.h new file mode 100644 index 0000000..ef21890 --- /dev/null +++ b/util_pkt_logger/inc/parson.h @@ -0,0 +1,111 @@ +/* + Parson ( http://kgabis.github.com/parson/ ) + Copyright (c) 2013 Krzysztof Gabis + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef parson_parson_h +#define parson_parson_h + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include <stddef.h> /* size_t */ + +#define PARSON_VERSION 20131130 + +/* Types and enums */ +typedef struct json_object_t JSON_Object; +typedef struct json_array_t JSON_Array; +typedef struct json_value_t JSON_Value; + +typedef enum json_value_type { + JSONError = 0, + JSONNull = 1, + JSONString = 2, + JSONNumber = 3, + JSONObject = 4, + JSONArray = 5, + JSONBoolean = 6 +} JSON_Value_Type; + + +/* Parses first JSON value in a file, returns NULL in case of error */ +JSON_Value * json_parse_file(const char *filename); + +/* Parses first JSON value in a file and ignores comments (/ * * / and //), + returns NULL in case of error */ +JSON_Value * json_parse_file_with_comments(const char *filename); + +/* Parses first JSON value in a string, returns NULL in case of error */ +JSON_Value * json_parse_string(const char *string); + +/* Parses first JSON value in a string and ignores comments (/ * * / and //), + returns NULL in case of error */ +JSON_Value * json_parse_string_with_comments(const char *string); + +/* JSON Object */ +JSON_Value * json_object_get_value (const JSON_Object *object, const char *name); +const char * json_object_get_string (const JSON_Object *object, const char *name); +JSON_Object * json_object_get_object (const JSON_Object *object, const char *name); +JSON_Array * json_object_get_array (const JSON_Object *object, const char *name); +double json_object_get_number (const JSON_Object *object, const char *name); +int json_object_get_boolean(const JSON_Object *object, const char *name); + +/* dotget functions enable addressing values with dot notation in nested objects, + just like in structs or c++/java/c# objects (e.g. objectA.objectB.value). + Because valid names in JSON can contain dots, some values may be inaccessible + this way. */ +JSON_Value * json_object_dotget_value (const JSON_Object *object, const char *name); +const char * json_object_dotget_string (const JSON_Object *object, const char *name); +JSON_Object * json_object_dotget_object (const JSON_Object *object, const char *name); +JSON_Array * json_object_dotget_array (const JSON_Object *object, const char *name); +double json_object_dotget_number (const JSON_Object *object, const char *name); +int json_object_dotget_boolean(const JSON_Object *object, const char *name); + +/* Functions to get available names */ +size_t json_object_get_count(const JSON_Object *object); +const char * json_object_get_name (const JSON_Object *object, size_t index); + +/* JSON Array */ +JSON_Value * json_array_get_value (const JSON_Array *array, size_t index); +const char * json_array_get_string (const JSON_Array *array, size_t index); +JSON_Object * json_array_get_object (const JSON_Array *array, size_t index); +JSON_Array * json_array_get_array (const JSON_Array *array, size_t index); +double json_array_get_number (const JSON_Array *array, size_t index); +int json_array_get_boolean(const JSON_Array *array, size_t index); +size_t json_array_get_count (const JSON_Array *array); + +/* JSON Value */ +JSON_Value_Type json_value_get_type (const JSON_Value *value); +JSON_Object * json_value_get_object (const JSON_Value *value); +JSON_Array * json_value_get_array (const JSON_Value *value); +const char * json_value_get_string (const JSON_Value *value); +double json_value_get_number (const JSON_Value *value); +int json_value_get_boolean(const JSON_Value *value); +void json_value_free (JSON_Value *value); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/util_pkt_logger/local_conf.json b/util_pkt_logger/local_conf.json new file mode 100644 index 0000000..624893d --- /dev/null +++ b/util_pkt_logger/local_conf.json @@ -0,0 +1,5 @@ +{ + "gateway_conf": { + "gateway_ID": "AA555A0000000101" + } +} diff --git a/util_pkt_logger/obj/.gitkeep b/util_pkt_logger/obj/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/util_pkt_logger/obj/.gitkeep diff --git a/util_pkt_logger/readme.md b/util_pkt_logger/readme.md new file mode 100644 index 0000000..a980bd4 --- /dev/null +++ b/util_pkt_logger/readme.md @@ -0,0 +1,143 @@ + / _____) _ | | + ( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | + (______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +LoRa packet logger +=================== + +1. Introduction +---------------- + +This software is used to set up a LoRa concentrator using a JSON configuration +file and then record all the packets received in a log file, indefinitely, until +the user stops the application. +No filtering is done and all packets that are LoRa packets with the correct RF +parameters (frequency, datarate, bandwidth) should appear in the log. + +2. Dependencies +---------------- + +This program uses the Parson library (http://kgabis.github.com/parson/) by +Krzysztof Gabis for JSON parsing. +Many thanks to him for that very practical and well written library. + +This program is a typical example of LoRa concentrator HAL usage for receiving +packets. + +Only high-level functions are used (the ones contained in loragw_hal) so there +is no hardware dependencies assuming the HAL is matched with the proper version +of the hardware. +Data structures of the received packets are accessed by name (ie. not at a +binary level) so new functionalities can be added to the API without affecting +that program at all. + +It was tested with v1.3.0 of the libloragw library, and should be compatible +with any later version of the library assuming the API is downward-compatible. + +3. Usage +--------- + +To stop the application, press Ctrl+C. + +The only optional parameter when launching the application is the log rotation +time (in seconds). + +The way the program takes configuration files into account is the following: + * if there is a debug_conf.json parse it, others are ignored + * if there is a global_conf.json parse it and look for the next file + * if there is a local_conf.json parse it +If some parameters are defined in both global and local configuration files, the +local definition overwrites the global definition. + +The global configuration file should be exactly the same throughout your +network, contain all global parameters (parameters for "sensor" radio channels) +and preferably default "safe" values for parameters that are specific for each +gateway (eg. specify a default MAC address). + +If you have build the libloragw library for a specific radio band (eg. ETSI +868 MHz band) a ready-to-use global_conf.json file is generated by the Makefile +with a set of channels typical for a 'LoRa MAC' network application. +If you don't specify a radio band, an empty global_conf.json is generated and +must be filled with the settings you need. + +The local configuration file should contain parameters that are specific to each +gateway (eg. MAC address, frequency for backhaul radio channels). + +In each configuration file, the program looks for a JSON object named +"SX1301_conf" that should contain the parameters for the LoRa concentrator board +(RF channels definition, modem parameters, etc) and another JSON object called +"gateway_conf" that should contain the gateway parameters (gateway MAC address, +IP address of the LoRa MAC controller, network authentication parameters, etc). + +To learn more about the JSON configuration format, read the provided JSON files +and the API documentation. A dedicated document will be available later on. + +The received packets are put in a CSV file whose name include the MAC address of +the gateway in hexadecimal format and a UTC timestamp of log starting time in +ISO 8601 recommended compact format: +yyyymmddThhmmssZ (eg. 20131009T172345Z for October 9th, 2013 at 5:23:45PM UTC) + +To able continuous monitoring, the current log file is closed is closed and a +new one is opened every hour (by default, rotation interval is settable by the +user using -r command line option). +No packet is lost during that rotation of log file. +Every log file but the current one can then be modified, uploaded and/or deleted +without any consequence for the program execution. + +4. License +----------- + +Copyright (c) 2013, SEMTECH S.A. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of the Semtech corporation nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +5. License for Parson library +------------------------------ + +Parson ( http://kgabis.github.com/parson/ ) +Copyright (c) 2012 Krzysztof Gabis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*EOF*
\ No newline at end of file diff --git a/util_pkt_logger/src/parson.c b/util_pkt_logger/src/parson.c new file mode 100644 index 0000000..ecf733f --- /dev/null +++ b/util_pkt_logger/src/parson.c @@ -0,0 +1,782 @@ +/* + Parson ( http://kgabis.github.com/parson/ ) + Copyright (c) 2013 Krzysztof Gabis + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "parson.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#define ERROR 0 +#define SUCCESS 1 +#define STARTING_CAPACITY 15 +#define ARRAY_MAX_CAPACITY 122880 /* 15*(2^13) */ +#define OBJECT_MAX_CAPACITY 960 /* 15*(2^6) */ +#define MAX_NESTING 19 +#define sizeof_token(a) (sizeof(a) - 1) +#define skip_char(str) ((*str)++) +#define skip_whitespaces(str) while (isspace(**str)) { skip_char(str); } +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +#define parson_malloc(a) malloc(a) +#define parson_free(a) free((void*)a) +#define parson_realloc(a, b) realloc(a, b) + +/* Type definitions */ +typedef union json_value_value { + const char *string; + double number; + JSON_Object *object; + JSON_Array *array; + int boolean; + int null; +} JSON_Value_Value; + +struct json_value_t { + JSON_Value_Type type; + JSON_Value_Value value; +}; + +struct json_object_t { + const char **names; + JSON_Value **values; + size_t count; + size_t capacity; +}; + +struct json_array_t { + JSON_Value **items; + size_t count; + size_t capacity; +}; + +/* Various */ +static char * read_file(const char *filename); +static void remove_comments(char *string, const char *start_token, const char *end_token); +static int try_realloc(void **ptr, size_t new_size); +static char * parson_strndup(const char *string, size_t n); +static int is_utf(const unsigned char *string); +static int is_decimal(const char *string, size_t length); + +/* JSON Object */ +static JSON_Object * json_object_init(void); +static int json_object_add(JSON_Object *object, const char *name, JSON_Value *value); +static int json_object_resize(JSON_Object *object, size_t capacity); +static JSON_Value * json_object_nget_value(const JSON_Object *object, const char *name, size_t n); +static void json_object_free(JSON_Object *object); + +/* JSON Array */ +static JSON_Array * json_array_init(void); +static int json_array_add(JSON_Array *array, JSON_Value *value); +static int json_array_resize(JSON_Array *array, size_t capacity); +static void json_array_free(JSON_Array *array); + +/* JSON Value */ +static JSON_Value * json_value_init_object(void); +static JSON_Value * json_value_init_array(void); +static JSON_Value * json_value_init_string(const char *string); +static JSON_Value * json_value_init_number(double number); +static JSON_Value * json_value_init_boolean(int boolean); +static JSON_Value * json_value_init_null(void); + +/* Parser */ +static void skip_quotes(const char **string); +static const char * get_processed_string(const char **string); +static JSON_Value * parse_object_value(const char **string, size_t nesting); +static JSON_Value * parse_array_value(const char **string, size_t nesting); +static JSON_Value * parse_string_value(const char **string); +static JSON_Value * parse_boolean_value(const char **string); +static JSON_Value * parse_number_value(const char **string); +static JSON_Value * parse_null_value(const char **string); +static JSON_Value * parse_value(const char **string, size_t nesting); + +/* Various */ +static int try_realloc(void **ptr, size_t new_size) { + void *reallocated_ptr = parson_realloc(*ptr, new_size); + if (!reallocated_ptr) + return ERROR; + *ptr = reallocated_ptr; + return SUCCESS; +} + +static char * parson_strndup(const char *string, size_t n) { + char *output_string = (char*)parson_malloc(n + 1); + if (!output_string) + return NULL; + output_string[n] = '\0'; + strncpy(output_string, string, n); + return output_string; +} + +static int is_utf(const unsigned char *s) { + return isxdigit(s[0]) && isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]); +} + +static int is_decimal(const char *string, size_t length) { + if (length > 1 && string[0] == '0' && string[1] != '.') + return 0; + if (length > 2 && !strncmp(string, "-0", 2) && string[2] != '.') + return 0; + while (length--) + if (strchr("xX", string[length])) + return 0; + return 1; +} + +static char * read_file(const char * filename) { + FILE *fp = fopen(filename, "r"); + size_t file_size; + char *file_contents; + if (!fp) + return NULL; + fseek(fp, 0L, SEEK_END); + file_size = ftell(fp); + rewind(fp); + file_contents = (char*)parson_malloc(sizeof(char) * (file_size + 1)); + if (!file_contents) { + fclose(fp); + return NULL; + } + if (fread(file_contents, file_size, 1, fp) < 1) { + if (ferror(fp)) { + fclose(fp); + parson_free(file_contents); + return NULL; + } + } + fclose(fp); + file_contents[file_size] = '\0'; + return file_contents; +} + +static void remove_comments(char *string, const char *start_token, const char *end_token) { + int in_string = 0, escaped = 0; + size_t i; + char *ptr = NULL, current_char; + size_t start_token_len = strlen(start_token); + size_t end_token_len = strlen(end_token); + if (start_token_len == 0 || end_token_len == 0) + return; + while ((current_char = *string) != '\0') { + if (current_char == '\\' && !escaped) { + escaped = 1; + string++; + continue; + } else if (current_char == '\"' && !escaped) { + in_string = !in_string; + } else if (!in_string && strncmp(string, start_token, start_token_len) == 0) { + for(i = 0; i < start_token_len; i++) + string[i] = ' '; + string = string + start_token_len; + ptr = strstr(string, end_token); + if (!ptr) + return; + for (i = 0; i < (ptr - string) + end_token_len; i++) + string[i] = ' '; + string = ptr + end_token_len - 1; + } + escaped = 0; + string++; + } +} + +/* JSON Object */ +static JSON_Object * json_object_init(void) { + JSON_Object *new_obj = (JSON_Object*)parson_malloc(sizeof(JSON_Object)); + if (!new_obj) + return NULL; + new_obj->names = (const char**)NULL; + new_obj->values = (JSON_Value**)NULL; + new_obj->capacity = 0; + new_obj->count = 0; + return new_obj; +} + +static int json_object_add(JSON_Object *object, const char *name, JSON_Value *value) { + size_t index; + if (object->count >= object->capacity) { + size_t new_capacity = MAX(object->capacity * 2, STARTING_CAPACITY); + if (new_capacity > OBJECT_MAX_CAPACITY) + return ERROR; + if (json_object_resize(object, new_capacity) == ERROR) + return ERROR; + } + if (json_object_get_value(object, name) != NULL) + return ERROR; + index = object->count; + object->names[index] = parson_strndup(name, strlen(name)); + if (!object->names[index]) + return ERROR; + object->values[index] = value; + object->count++; + return SUCCESS; +} + +static int json_object_resize(JSON_Object *object, size_t capacity) { + if (try_realloc((void**)&object->names, capacity * sizeof(char*)) == ERROR) + return ERROR; + if (try_realloc((void**)&object->values, capacity * sizeof(JSON_Value*)) == ERROR) + return ERROR; + object->capacity = capacity; + return SUCCESS; +} + +static JSON_Value * json_object_nget_value(const JSON_Object *object, const char *name, size_t n) { + size_t i, name_length; + for (i = 0; i < json_object_get_count(object); i++) { + name_length = strlen(object->names[i]); + if (name_length != n) + continue; + if (strncmp(object->names[i], name, n) == 0) + return object->values[i]; + } + return NULL; +} + +static void json_object_free(JSON_Object *object) { + while(object->count--) { + parson_free(object->names[object->count]); + json_value_free(object->values[object->count]); + } + parson_free(object->names); + parson_free(object->values); + parson_free(object); +} + +/* JSON Array */ +static JSON_Array * json_array_init(void) { + JSON_Array *new_array = (JSON_Array*)parson_malloc(sizeof(JSON_Array)); + if (!new_array) + return NULL; + new_array->items = (JSON_Value**)NULL; + new_array->capacity = 0; + new_array->count = 0; + return new_array; +} + +static int json_array_add(JSON_Array *array, JSON_Value *value) { + if (array->count >= array->capacity) { + size_t new_capacity = MAX(array->capacity * 2, STARTING_CAPACITY); + if (new_capacity > ARRAY_MAX_CAPACITY) + return ERROR; + if (!json_array_resize(array, new_capacity)) + return ERROR; + } + array->items[array->count] = value; + array->count++; + return SUCCESS; +} + +static int json_array_resize(JSON_Array *array, size_t capacity) { + if (try_realloc((void**)&array->items, capacity * sizeof(JSON_Value*)) == ERROR) + return ERROR; + array->capacity = capacity; + return SUCCESS; +} + +static void json_array_free(JSON_Array *array) { + while (array->count--) + json_value_free(array->items[array->count]); + parson_free(array->items); + parson_free(array); +} + +/* JSON Value */ +static JSON_Value * json_value_init_object(void) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) + return NULL; + new_value->type = JSONObject; + new_value->value.object = json_object_init(); + if (!new_value->value.object) { + parson_free(new_value); + return NULL; + } + return new_value; +} + +static JSON_Value * json_value_init_array(void) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) + return NULL; + new_value->type = JSONArray; + new_value->value.array = json_array_init(); + if (!new_value->value.array) { + parson_free(new_value); + return NULL; + } + return new_value; +} + +static JSON_Value * json_value_init_string(const char *string) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) + return NULL; + new_value->type = JSONString; + new_value->value.string = string; + return new_value; +} + +static JSON_Value * json_value_init_number(double number) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) + return NULL; + new_value->type = JSONNumber; + new_value->value.number = number; + return new_value; +} + +static JSON_Value * json_value_init_boolean(int boolean) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) + return NULL; + new_value->type = JSONBoolean; + new_value->value.boolean = boolean; + return new_value; +} + +static JSON_Value * json_value_init_null(void) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) + return NULL; + new_value->type = JSONNull; + return new_value; +} + +/* Parser */ +static void skip_quotes(const char **string) { + skip_char(string); + while (**string != '\"') { + if (**string == '\0') |
