diff options
Diffstat (limited to 'src/shim/mkjson')
-rw-r--r-- | src/shim/mkjson/LICENSE | 21 | ||||
-rw-r--r-- | src/shim/mkjson/makefile | 30 | ||||
-rw-r--r-- | src/shim/mkjson/mkjson.c | 307 | ||||
-rw-r--r-- | src/shim/mkjson/mkjson.h | 50 |
4 files changed, 408 insertions, 0 deletions
diff --git a/src/shim/mkjson/LICENSE b/src/shim/mkjson/LICENSE new file mode 100644 index 000000000..8c4284c91 --- /dev/null +++ b/src/shim/mkjson/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Jacek Wieczorek + +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. diff --git a/src/shim/mkjson/makefile b/src/shim/mkjson/makefile new file mode 100644 index 000000000..ba75399d2 --- /dev/null +++ b/src/shim/mkjson/makefile @@ -0,0 +1,30 @@ +CFLAGS = -Wall -Os -I. +CC = gcc +AR = ar + +#USE_ASPRINTF make flag can be used in order to encourage asprintf use inside the library +ifeq ($(USE_ASPRINTF),1) +CFLAGS += -D_GNU_SOURCE +endif + +#Builds object and a static library file +all: clean force + $(CC) $(CFLAGS) -c mkjson.c -o obj/mkjson.o + $(AR) -cvq lib/libmkjson.a obj/mkjson.o + $(AR) -t lib/libmkjson.a + +#Normal cleanup +clean: + -rm -rf obj + -rm -rf lib + +#Environment init +force: + -mkdir obj + -mkdir lib + +#Build the example snippet +example: all + gcc -o example examples/example.c -I. -Llib -lmkjson + + diff --git a/src/shim/mkjson/mkjson.c b/src/shim/mkjson/mkjson.c new file mode 100644 index 000000000..1172664fb --- /dev/null +++ b/src/shim/mkjson/mkjson.c @@ -0,0 +1,307 @@ +/* mkjson.c - a part of mkjson library + * + * Copyright (C) 2018 Jacek Wieczorek + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +#include <mkjson.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> + +// Works like asprintf, but it's always there +// I don't want the name to collide with anything +static int allsprintf( char **strp, const char *fmt, ... ) +{ + int len; + va_list ap; + va_start( ap, fmt ); + + #ifdef _GNU_SOURCE + // Just hand everything to vasprintf, if it's available + len = vasprintf( strp, fmt, ap ); + #else + // Or do it the manual way + char *buf; + len = vsnprintf( NULL, 0, fmt, ap ); + if ( len >= 0 ) + { + buf = malloc( ++len ); + if ( buf != NULL ) + { + // Hopefully, that's the right way to do it + va_end( ap ); + va_start( ap, fmt ); + + // Write and return the data + len = vsnprintf( buf, len, fmt, ap ); + if ( len >= 0 ) + { + *strp = buf; + } + else + { + free( buf ); + } + } + } + #endif + + va_end( ap ); + return len; +} + +// Return JSON string built from va_arg arguments +// If no longer needed, should be passed to free() by user +char *mkjson( enum mkjson_container_type otype, int count, ... ) +{ + int i, len, goodchunks = 0, failure = 0; + char *json, *prefix, **chunks, ign; + + // Value - type and data + enum mkjson_value_type vtype; + const char *key; + long long int intval; + long double dblval; + const char *strval; + + // Since v0.9 count cannot be a negative value and datatype is indicated by a separate argument + // Since I'm not sure whether it's right to put assertions in libraries, the next line is commented out + // assert( count >= 0 && "After v0.9 negative count is prohibited; please use otype argument instead" ); + if ( count < 0 || ( otype != MKJSON_OBJ && otype != MKJSON_ARR ) ) return NULL; + + // Allocate chunk pointer array - on standard platforms each one should be NULL + chunks = calloc( count, sizeof( char* ) ); + if ( chunks == NULL ) return NULL; + + // This should rather be at the point of no return + va_list ap; + va_start( ap, count ); + + // Create chunks + for ( i = 0; i < count && !failure; i++ ) + { + // Get value type + vtype = va_arg( ap, enum mkjson_value_type ); + + // Get key + if ( otype == MKJSON_OBJ ) + { + key = va_arg( ap, char* ); + if ( key == NULL ) + { + failure = 1; + break; + } + } + else key = ""; + + // Generate prefix + if ( allsprintf( &prefix, "%s%s%s", + otype == MKJSON_OBJ ? "\"" : "", // Quote before key + key, // Key + otype == MKJSON_OBJ ? "\": " : "" ) == -1 ) // Quote and colon after key + { + failure = 1; + break; + } + + // Depending on value type + ign = 0; + switch ( vtype ) + { + // Ignore string / JSON data + case MKJSON_IGN_STRING: + case MKJSON_IGN_JSON: + (void) va_arg( ap, const char* ); + ign = 1; + break; + + // Ignore string / JSON data and pass the pointer to free + case MKJSON_IGN_STRING_FREE: + case MKJSON_IGN_JSON_FREE: + free( va_arg( ap, char* ) ); + ign = 1; + break; + + // Ignore int / long long int + case MKJSON_IGN_INT: + case MKJSON_IGN_LLINT: + if ( vtype == MKJSON_IGN_INT ) + (void) va_arg( ap, int ); + else + (void) va_arg( ap, long long int ); + ign = 1; + break; + + // Ignore double / long double + case MKJSON_IGN_DOUBLE: + case MKJSON_IGN_LDOUBLE: + if ( vtype == MKJSON_IGN_DOUBLE ) + (void) va_arg( ap, double ); + else + (void) va_arg( ap, long double ); + ign = 1; + break; + + // Ignore boolean + case MKJSON_IGN_BOOL: + (void) va_arg( ap, int ); + ign = 1; + break; + + // Ignore null value + case MKJSON_IGN_NULL: + ign = 1; + break; + + // A null-terminated string + case MKJSON_STRING: + case MKJSON_STRING_FREE: + strval = va_arg( ap, const char* ); + + // If the pointer points to NULL, the string will be replaced with JSON null value + if ( strval == NULL ) + { + if ( allsprintf( chunks + i, "%snull", prefix ) == -1 ) + chunks[i] = NULL; + } + else + { + if ( allsprintf( chunks + i, "%s\"%s\"", prefix, strval ) == -1 ) + chunks[i] = NULL; + } + + // Optional free + if ( vtype == MKJSON_STRING_FREE ) + free( (char*) strval ); + break; + + // Embed JSON data + case MKJSON_JSON: + case MKJSON_JSON_FREE: + strval = va_arg( ap, const char* ); + + // If the pointer points to NULL, the JSON data is replaced with null value + if ( allsprintf( chunks + i, "%s%s", prefix, strval == NULL ? "null" : strval ) == -1 ) + chunks[i] = NULL; + + // Optional free + if ( vtype == MKJSON_JSON_FREE ) + free( (char*) strval ); + break; + + // int / long long int + case MKJSON_INT: + case MKJSON_LLINT: + if ( vtype == MKJSON_INT ) + intval = va_arg( ap, int ); + else + intval = va_arg( ap, long long int ); + + if ( allsprintf( chunks + i, "%s%Ld", prefix, intval ) == -1 ) chunks[i] = NULL; + break; + + // double / long double + case MKJSON_DOUBLE: + case MKJSON_LDOUBLE: + if ( vtype == MKJSON_DOUBLE ) + dblval = va_arg( ap, double ); + else + dblval = va_arg( ap, long double ); + + if ( allsprintf( chunks + i, "%s%Lf", prefix, dblval ) == -1 ) chunks[i] = NULL; + break; + + // double / long double + case MKJSON_SCI_DOUBLE: + case MKJSON_SCI_LDOUBLE: + if ( vtype == MKJSON_SCI_DOUBLE ) + dblval = va_arg( ap, double ); + else + dblval = va_arg( ap, long double ); + + if ( allsprintf( chunks + i, "%s%Le", prefix, dblval ) == -1 ) chunks[i] = NULL; + break; + + // Boolean + case MKJSON_BOOL: + intval = va_arg( ap, int ); + if ( allsprintf( chunks + i, "%s%s", prefix, intval ? "true" : "false" ) == -1 ) chunks[i] = NULL; + break; + + // JSON null + case MKJSON_NULL: + if ( allsprintf( chunks + i, "%snull", prefix ) == -1 ) chunks[i] = NULL; + break; + + // Bad type specifier + default: + chunks[i] = NULL; + break; + } + + // Free prefix memory + free( prefix ); + + // NULL chunk without ignore flag indicates failure + if ( !ign && chunks[i] == NULL ) failure = 1; + + // NULL chunk now indicates ignore flag + if ( ign ) chunks[i] = NULL; + else goodchunks++; + } + + // We won't use ap anymore + va_end( ap ); + + // If everything is fine, merge chunks and create full JSON table + if ( !failure ) + { + // Get total length (this is without NUL byte) + len = 0; + for ( i = 0; i < count; i++ ) + if ( chunks[i] != NULL ) + len += strlen( chunks[i] ); + + // Total length = Chunks length + 2 brackets + separators + if ( goodchunks == 0 ) goodchunks = 1; + len = len + 2 + ( goodchunks - 1 ) * 2; + + // Allocate memory for the whole thing + json = calloc( len + 1, sizeof( char ) ); + if ( json != NULL ) + { + // Merge chunks (and do not overwrite the first bracket) + for ( i = 0; i < count; i++ ) + { + // Add separators: + // - not on the begining + // - always after valid chunk + // - between two valid chunks + // - between valid and ignored chunk if the latter isn't the last one + if ( i != 0 && chunks[i - 1] != NULL && ( chunks[i] != NULL || ( chunks[i] == NULL && i != count - 1 ) ) ) + strcat( json + 1, ", "); + + if ( chunks[i] != NULL ) + strcat( json + 1, chunks[i] ); + } + + // Add proper brackets + json[0] = otype == MKJSON_OBJ ? '{' : '['; + json[len - 1] = otype == MKJSON_OBJ ? '}' : ']'; + } + } + else json = NULL; + + // Free chunks + for ( i = 0; i < count; i++ ) + free( chunks[i] ); + free( chunks ); + + return json; +} + diff --git a/src/shim/mkjson/mkjson.h b/src/shim/mkjson/mkjson.h new file mode 100644 index 000000000..38cc07b26 --- /dev/null +++ b/src/shim/mkjson/mkjson.h @@ -0,0 +1,50 @@ +/* mkjson.h - a part of mkjson library + * + * Copyright (C) 2018 Jacek Wieczorek + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +#ifndef MKJSON_H +#define MKJSON_H + +// JSON container types +enum mkjson_container_type +{ + MKJSON_ARR = 0, // An array + MKJSON_OBJ = 1 // An object (hash or whatever you call it) +}; + +// JSON data types +enum mkjson_value_type +{ + MKJSON_STRING = (int)('s'), // const char* - String data + MKJSON_STRING_FREE = (int)('f'), // char* - String data, but pointer is freed + MKJSON_JSON = (int)('r'), // const char* - JSON data (like string, but no quotes) + MKJSON_JSON_FREE = (int)('j'), // char* - JSON data, but pointer is freed + MKJSON_INT = (int)('i'), // int - An integer + MKJSON_LLINT = (int)('I'), // long long int - A long integer + MKJSON_DOUBLE = (int)('d'), // double - A double + MKJSON_LDOUBLE = (int)('D'), // long double - A long double + MKJSON_SCI_DOUBLE = (int)('e'), // double - A double with scientific notation + MKJSON_SCI_LDOUBLE = (int)('E'), // long double - A long double with scientific notation + MKJSON_BOOL = (int)('b'), // int - A boolean value + MKJSON_NULL = (int)('n'), // -- - JSON null value + + // These cause one argument of certain type to be ignored + MKJSON_IGN_STRING = (-MKJSON_STRING), + MKJSON_IGN_STRING_FREE = (-MKJSON_STRING_FREE), + MKJSON_IGN_JSON = (-MKJSON_JSON), + MKJSON_IGN_JSON_FREE = (-MKJSON_JSON_FREE), + MKJSON_IGN_INT = (-MKJSON_INT), + MKJSON_IGN_LLINT = (-MKJSON_LLINT), + MKJSON_IGN_DOUBLE = (-MKJSON_DOUBLE), + MKJSON_IGN_LDOUBLE = (-MKJSON_LDOUBLE), + MKJSON_IGN_BOOL = (-MKJSON_BOOL), + MKJSON_IGN_NULL = (-MKJSON_NULL) +}; + +extern char *mkjson( enum mkjson_container_type otype, int count, ... ); + +#endif |