#include // Simple function I wrote to make my life easier while playing with Kore C web server // // Should be easily portable to not use any json libraries at all if you really wanna to // // I know that it uses ?: GCC extension but in this file it can be expanded to standard C ternary operator with simple // sed expression, everything for your closed-source "can't add useful features to my compiler" needs :) // // Usage: // N-th variadic argument starts new context with one of the json types // N+1 sets the name of object if current context is not an array, can be NULL // N+2 sets the object value // N+M marks the end with JSON_END flag if the context was object or array // // Example: // root = json_construct( NULL, // JSON_TYPE_OBJECT, NULL, // JSON_TYPE_STRING, "version", "2.1", // JSON_TYPE_OBJECT, "software", // JSON_TYPE_STRING, "name", "apserv", // JSON_TYPE_STRING, "version", "0.0.1", // JSON_TYPE_STRING, "repository", "https://git.mentality.rip/FWGS/apserv", // JSON_TYPE_STRING, "homepage", "https://git.mentality.rip/FWGS/apserv", // JSON_END, // JSON_TYPE_ARRAY, "protocols", // JSON_TYPE_STRING, "activitypub", // JSON_END, // JSON_TYPE_OBJECT, "services", // JSON_TYPE_ARRAY, "inbound", JSON_END, // JSON_TYPE_ARRAY, "outbound", JSON_END, // JSON_END, // JSON_TYPE_LITERAL, "openRegistrations", APSERV_OPEN_REGISTRATIONS ? JSON_TRUE : JSON_FALSE, // JSON_TYPE_OBJECT, "usage", // JSON_TYPE_OBJECT, "users", // JSON_TYPE_U64, "total", 0, // JSON_TYPE_U64, "activeHalfyear", 0, // JSON_TYPE_U64, "activeMonth", 0, // JSON_END, // JSON_END, // JSON_TYPE_OBJECT, "metadata", // JSON_END, // JSON_END); // // This generates an object below: // { // "version": "2.1", // "software": { // "name": "apserv", // "version": "0.0.1", // "repository": "https://git.mentality.rip/FWGS/apserv", // "homepage": "https://git.mentality.rip/FWGS/apserv" // }, // "protocols": [ // "activitypub" // ], // "services": { // "inbound": [], // "outbound": [] // }, // "openRegistrations": false, // "usage": { // "users": { // "total": 0, // "activeHalfyear": 0, // "activeMonth": 0 // } // }, // "metadata": {} // } // #include #include #include #include #define JSON_END 0 #define JSON_TYPE_OBJECT KORE_JSON_TYPE_OBJECT #define JSON_TYPE_ARRAY KORE_JSON_TYPE_ARRAY #define JSON_TYPE_STRING KORE_JSON_TYPE_STRING #define JSON_TYPE_NUMBER KORE_JSON_TYPE_NUMBER #define JSON_TYPE_LITERAL KORE_JSON_TYPE_LITERAL #define JSON_TYPE_I32 (1U<<31) #define JSON_TYPE_U32 (1U<<30) #define JSON_TYPE_I64 KORE_JSON_TYPE_INTEGER #define JSON_TYPE_U64 KORE_JSON_TYPE_INTEGER_U64 #define JSON_FALSE KORE_JSON_FALSE #define JSON_TRUE KORE_JSON_TRUE #define JSON_NULL KORE_JSON_NULL struct kore_json_item *json_construct( struct kore_json_item *parent, int type, ... ); #define JSON_CONSTRUCT_DEBUG #ifdef JSON_CONSTRUCT_DEBUG #define CONSTRUCT_DEBUG(...) kore_log( LOG_DEBUG, __VA_ARGS__ ) #else #define CONSTRUCT_DEBUG(...) #endif static struct kore_json_item *json_construct_onev( struct kore_json_item *parent, bool skipname, int type, va_list ap ) { struct kore_json_item *ret = NULL; const char *name, *parent_name; if( type == JSON_END ) { fatal( "%s: this shouldn't happen", __func__ ); return NULL; } if( parent ) { if( parent->name ) parent_name = parent->name; else parent_name = "(noname)"; } else parent_name = "(noparent)"; if( !skipname ) name = va_arg( ap, const char * ); else name = NULL; if( type == JSON_TYPE_OBJECT || type == JSON_TYPE_ARRAY ) { const char *strtype = type == JSON_TYPE_OBJECT ? "object" : "array"; struct kore_json_item *f; f = kore_json_create_item( parent, name, type ); if( !ret ) ret = f; CONSTRUCT_DEBUG( "(%s) creating %s \"%s\"", parent_name, strtype, name ?: "" ); for( ;; ) { int nexttype = va_arg( ap, int ); if( nexttype == JSON_END ) { CONSTRUCT_DEBUG( "(%s) closing %s \"%s\"", parent_name, strtype, name ?: "" ); return ret; } json_construct_onev( f, type == JSON_TYPE_ARRAY, nexttype, ap ); } } switch( type ) { case JSON_TYPE_STRING: { const char *szValue = va_arg( ap, const char * ); CONSTRUCT_DEBUG( "(%s) creating string object \"%s\": \"%s\"", parent_name, name ?: "", szValue ); ret = kore_json_create_string( parent, name, szValue ); break; } case JSON_TYPE_NUMBER: { double flValue = va_arg( ap, double ); CONSTRUCT_DEBUG( "(%s) creating number parameter \"%s\": %f", parent_name, name ?: "", flValue ); ret = kore_json_create_number( parent, name, flValue ); break; } case JSON_TYPE_LITERAL: { int32_t iValue = va_arg( ap, int ); const char *szValue; switch( iValue ) { case JSON_FALSE: szValue = "false"; break; case JSON_TRUE: szValue = "true"; break; case JSON_NULL: szValue = "null"; break; default: fatal("illegal value %d for literal type", iValue ); } CONSTRUCT_DEBUG( "(%s) creating literal parameter \"%s\": %s", parent_name, name ?: "", szValue ); ret = kore_json_create_literal( parent, name, iValue ); break; } case JSON_TYPE_I32: { int32_t iValue = va_arg( ap, int32_t ); CONSTRUCT_DEBUG( "(%s) creating int32 parameter \"%s\": %d", parent_name, name ?: "", iValue ); ret = kore_json_create_integer( parent, name, iValue ); break; } case JSON_TYPE_U32: { uint32_t uValue = va_arg( ap, uint32_t ); CONSTRUCT_DEBUG( "(%s) creating uint32 parameter \"%s\": %u", parent_name, name ?: "", uValue ); ret = kore_json_create_integer( parent, name, uValue ); break; } case JSON_TYPE_I64: { int64_t lValue = va_arg( ap, int64_t ); CONSTRUCT_DEBUG( "(%s) creating int64 parameter \"%s\": " PRId64, parent_name, name ?: "", lValue ); ret = kore_json_create_integer( parent, name, lValue ); break; } case JSON_TYPE_U64: { uint64_t ulValue = va_arg( ap, uint64_t ); CONSTRUCT_DEBUG( "(%s) creating uint64 parameter \"%s\": " PRIu64, parent_name, name ?: "", ulValue ); ret = kore_json_create_integer_u64( parent, name, ulValue ); break; } } return ret; } struct kore_json_item *json_construct( struct kore_json_item *parent, int type, ... ) { va_list ap; va_start( ap, type ); parent = json_construct_onev( parent, type == JSON_TYPE_ARRAY, type, ap ); va_end( ap ); return parent; }