# HG changeset patch # User Thomas Klausner # Date 1558170361 -7200 # Node ID 70b6067f45525414bc3fe747457b37e39842f043 # Parent 0dff6a604b0a5f1ca86b73ccb8bc744b8f5dda9a Add tool to generate dict_json_dict_schema.cc from dict_json_dict_schema.json. diff -r 0dff6a604b0a -r 70b6067f4552 extensions/dict_json/CMakeLists.txt --- a/extensions/dict_json/CMakeLists.txt Fri May 17 12:59:19 2019 +0200 +++ b/extensions/dict_json/CMakeLists.txt Sat May 18 11:06:01 2019 +0200 @@ -6,11 +6,12 @@ # We use JSONCPP and JSON-Schema to parse and validate JSON files PKG_CHECK_MODULES(JSONCPP REQUIRED jsoncpp) PKG_CHECK_MODULES(JSON_SCHEMA REQUIRED json-schema) +PKG_CHECK_MODULES(PCRECPP REQUIRED libpcrecpp) # List of source files SET(DICT_JSON_SRC dict_json.cc - dict_json_dict_schema.cc + ${CMAKE_CURRENT_BINARY_DIR}/dict_json_dict_schema.cc ) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) @@ -28,6 +29,14 @@ ADD_EXECUTABLE(dict-json-diff dict-json-diff.cc) TARGET_LINK_LIBRARIES(dict-json-diff ${JSONCPP_LIBRARIES} ${JSON_SCHEMA_STATIC_LIBRARIES}) +ADD_EXECUTABLE(json-schema-to-c json-schema-to-c.cc) +TARGET_LINK_LIBRARIES(json-schema-to-c ${JSONCPP_LIBRARIES} ${JSON_SCHEMA_STATIC_LIBRARIES} ${PCRECPP_LIBRARIES}) + +ADD_CUSTOM_COMMAND( + OUTPUT dict_json_dict_schema.cc + COMMAND json-schema-to-c ${CMAKE_CURRENT_SOURCE_DIR}/dict_json_dict_schema.json ${CMAKE_CURRENT_BINARY_DIR}/dict_json_dict_schema.cc + DEPENDS dict_json_dict_schema.json +) #### ## INSTALL section ## @@ -38,6 +47,3 @@ INSTALL(TARGETS dict-json-diff RUNTIME DESTINATION ${INSTALL_DAEMON_SUFFIX} COMPONENT freeDiameter-dictionary-json) - -# dict_json_dict_schema.cc is created from dict_json_dict_schema.json -# the tool for that is not yet open source, but the conversion is straightforward diff -r 0dff6a604b0a -r 70b6067f4552 extensions/dict_json/dict_json_dict_schema.cc --- a/extensions/dict_json/dict_json_dict_schema.cc Fri May 17 12:59:19 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,164 +0,0 @@ -const char * dict_json_dict_schema = "\ -{ \n\ - \"definitions\": { \n\ - \"content\": { \n\ - \"type\": \"array\", \n\ - \"items\": { \n\ - \"type\": \"object\", \n\ - \"additionalProperties\": false, \n\ - \"required\": [ \"AVP\" ], \n\ - \"properties\": { \n\ - \"AVP\": { \"type\": \"string\" }, \n\ - \"Vendor\": { \"$ref\": \"#/definitions/unsigned-integer\" }, \n\ - \"First\": { \"type\": \"boolean\" }, \n\ - \"Min\": { \"$ref\": \"#/definitions/unsigned-integer\" }, \n\ - \"Max\": { \"anyOf\": [ { \"type\": \"integer\" }, { \"enum\": [ \"unbounded\" ] } ] } \n\ - } \n\ - } \n\ - }, \n\ - \n\ - \"identifier\": { \"type\": \"string\", \"pattern\": \"^[[:print:]]+$\" }, \n\ - \"type\": { \n\ - \"enum\": [ \n\ - \"Address\", \n\ - \"DiameterIdentity\", \n\ - \"DiameterURI\", \n\ - \"Enumerated\", \n\ - \"Float32\", \n\ - \"Float64\", \n\ - \"Grouped\", \n\ - \"Integer32\", \n\ - \"Integer64\", \n\ - \"IPFilterRule\", \n\ - \"OctetString\", \n\ - \"Time\", \n\ - \"Unsigned32\", \n\ - \"Unsigned64\", \n\ - \"UTF8String\" \n\ - ] \n\ - }, \n\ - \"unsigned-integer\": { \"type\": \"integer\", \"minimum\": 0 } \n\ - }, \n\ - \n\ - \"type\": \"object\", \n\ - \"additionalProperties\": false, \n\ - \"properties\": { \n\ - \"Vendors\": { \n\ - \"type\": \"array\", \n\ - \"items\": { \n\ - \"type\": \"object\", \n\ - \"additionalProperties\": false, \n\ - \"required\": [ \"Code\", \"Name\" ], \n\ - \"properties\": { \n\ - \"Code\": { \"$ref\": \"#/definitions/unsigned-integer\" }, \n\ - \"Name\": { \"$ref\": \"#/definitions/identifier\" } \n\ - } \n\ - } \n\ - }, \n\ - \"Types\": { \n\ - \"type\": \"array\", \n\ - \"items\": { \n\ - \"type\": \"object\", \n\ - \"additionalProperties\": false, \n\ - \"required\": [ \"Name\", \"Base\" ], \n\ - \"properties\": { \n\ - \"Name\": { \"type\": \"string\" }, \n\ - \"Base\": { \"type\": \"string\" } \n\ - } \n\ - } \n\ - }, \n\ - \"AVPs\": { \n\ - \"type\": \"array\", \n\ - \"items\": { \n\ - \"type\": \"object\", \n\ - \"additionalProperties\": false, \n\ - \"required\": [ \"Code\", \"Name\", \"Type\" ], \n\ - \"properties\": { \n\ - \"Code\": { \"$ref\": \"#/definitions/unsigned-integer\" }, \n\ - \"Vendor\": { \"$ref\": \"#/definitions/unsigned-integer\" }, \n\ - \"Name\": { \"$ref\": \"#/definitions/identifier\" }, \n\ - \"Flags\": { \n\ - \"type\": \"object\", \n\ - \"additionalProperties\": false, \n\ - \"properties\": { \n\ - \"Must\": { \"type\": \"string\", \"pattern\": \"^[VMP]*$\" }, \n\ - \"MustNot\": { \"type\": \"string\", \"pattern\": \"^[VMP]*$\" } \n\ - } \n\ - }, \n\ - \"Type\": { \"$ref\": \"#/definitions/identifier\" }, \n\ - \"EnumValues\": { \n\ - \"type\": \"array\", \n\ - \"items\": { \n\ - \"type\": \"object\", \n\ - \"additionalProperties\": false, \n\ - \"required\": [ \"Code\", \"Name\" ], \n\ - \"properties\": { \n\ - \"Code\": { \"anyOf\": [ { \"type\": \"integer\" }, { \"type\": \"number\" }, { \"type\": \"string\" } ] }, \n\ - \"Name\": { \"type\": \"string\", \"pattern\": \"^[[:print:]]*$\" } \n\ - } \n\ - } \n\ - } \n\ - } \n\ - } \n\ - }, \n\ - \"Applications\": { \n\ - \"type\": \"array\", \n\ - \"items\": { \n\ - \"type\": \"object\", \n\ - \"additionalProperties\": false, \n\ - \"required\": [ \"Code\", \"Name\" ], \n\ - \"properties\": { \n\ - \"Code\": { \"$ref\": \"#/definitions/unsigned-integer\" }, \n\ - \"Name\": { \"$ref\": \"#/definitions/identifier\" } \n\ - } \n\ - } \n\ - }, \n\ - \"Commands\": { \n\ - \"type\": \"array\", \n\ - \"items\": { \n\ - \"type\": \"object\", \n\ - \"additionalProperties\": false, \n\ - \"required\": [ \"Code\", \"Name\" ], \n\ - \"properties\": { \n\ - \"Code\": { \"$ref\": \"#/definitions/unsigned-integer\" }, \n\ - \"Name\": { \"$ref\": \"#/definitions/identifier\" }, \n\ - \"Application\": { \"$ref\": \"#/definitions/identifier\" }, \n\ - \"Flags\": { \n\ - \"type\": \"object\", \n\ - \"additionalProperties\": false, \n\ - \"properties\": { \n\ - \"Must\": { \"type\": \"string\", \"pattern\": \"^[RPE]*$\" }, \n\ - \"MustNot\": { \"type\": \"string\", \"pattern\": \"^[RPET]*$\" } \n\ - } \n\ - } \n\ - } \n\ - } \n\ - }, \n\ - \"CommandRules\": { \n\ - \"type\": \"array\", \n\ - \"items\": { \n\ - \"type\": \"object\", \n\ - \"additionalProperties\": false, \n\ - \"required\": [ \"Command\", \"Content\" ], \n\ - \"properties\": { \n\ - \"Command\": { \"type\": \"string\", \"minimum\": 0 }, \n\ - \"Content\": { \"$ref\": \"#/definitions/content\" } \n\ - } \n\ - } \n\ - }, \n\ - \"AVPRules\": { \n\ - \"type\": \"array\", \n\ - \"items\": { \n\ - \"type\": \"object\", \n\ - \"additionalProperties\": false, \n\ - \"required\": [ \"AVP\", \"Content\" ], \n\ - \"properties\": { \n\ - \"AVP\": { \"type\": \"string\" }, \n\ - \"Vendor\": { \"type\": \"integer\", \"minimum\" : 0 }, \n\ - \"Content\": { \"$ref\": \"#/definitions/content\" } \n\ - } \n\ - } \n\ - } \n\ - } \n\ -} \n\ -"; diff -r 0dff6a604b0a -r 70b6067f4552 extensions/dict_json/json-schema-to-c.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/dict_json/json-schema-to-c.cc Sat May 18 11:06:01 2019 +0200 @@ -0,0 +1,349 @@ +/********************************************************************************************************** + * Software License Agreement (BSD License) * + * Author: Thomas Klausner * + * * + * Copyright (c) 2016, 2017, 2019 Thomas Klausner * + * All rights reserved. * + * * + * Written under contract by nfotex IT GmbH, http://nfotex.com/ * + * * + * Redistribution and use of this software 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. * + * * + * 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. * + **********************************************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +[[noreturn]] +void usage(char *prg, int exit_status) { + FILE *f = (exit_status ? stderr : stdout); + + fprintf(f, "Usage: %s [-hV] [-i include] [-N variable-name] [-n namespace] [-t type] json [output]\n", prg); + fprintf(f, "options:\n\ + -h, --help print this usage message and exit\n\ + -i, --include FILE include FILE in output C source file\n\ + -n, --namespace NAME specify namespace in which to define variable\n\ + -N, --name NAME specify name of variable to define\n\ + --no-validate don't validate against JSON meta schema (default)\n\ + --type TYPE specify type of variable (char * (default) or std::string)\n\ + --validate validate against JSON meta schema\n\ + --validate-only validate against JSON meta schema and exit (don't create C source)\n\ +"); + + exit(exit_status); +} + +const char *OPTIONS = "hi:N:n:t:V"; + +enum { + OPT_NO_VALIDATE = 256, + OPT_VALIDATE_ONLY +}; +struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "include", required_argument, NULL, 'i' }, + { "name", required_argument, NULL, 'N' }, + { "namespace", required_argument, NULL, 'n' }, + { "no-validate", no_argument, NULL, OPT_NO_VALIDATE }, + { "type", required_argument, NULL, 't' }, + { "validate", no_argument, NULL, 'V' }, + { "validate-only", no_argument, NULL, OPT_VALIDATE_ONLY }, + { NULL, 0, NULL, 0 } +}; + +char *read_full_file (const char *filename, off_t max_size, off_t* size_ret, const char *desc) { + FILE *fp; + if ((fp = fopen (filename, "rb")) == NULL) { + fprintf (stderr, "couldn't open %s file [%s]: %d, %s\n", desc, filename, errno, strerror(errno)); + return NULL; + } + struct stat stat_buf; + if (fstat (fileno(fp), &stat_buf) < 0) { + fprintf (stderr, "couldn't stat %s file [%s]: %d, %s\n", desc, filename, errno, strerror(errno)); + fclose (fp); + return NULL; + } + off_t n = stat_buf.st_size; + if (max_size > 0 && n > max_size) { + fprintf (stderr, "%s file [%s] is larger than %" PRIi64 " bytes\n", desc, filename, (int64_t)max_size); + fclose (fp); + return NULL; + } + char *buf; + if ((buf = (char *) malloc ((size_t)n+1)) == NULL) { + fprintf (stderr, "error allocating %" PRIi64 " bytes for read of %s file [%s]\n", (int64_t)n, desc, filename); + fclose (fp); + return NULL; + } + if (fread (buf, 1, (size_t)n, fp) < (size_t) n) { + fprintf (stderr, "error reading %s file [%s]: %d, %s\n", desc, filename, errno, strerror(errno)); + fclose (fp); + free (buf); + return NULL; + } + + fclose (fp); + buf[n] = '\0'; + if (size_ret != NULL) + *size_ret = n; + return buf; +} + +int main (int argc, char **argv) { + char *name_space = NULL; + char *name = NULL; + bool free_name = false; + char default_type[] = "char *"; + char *type = default_type; + bool validate = false; + bool convert = true; + char *include = NULL; + + int c; + while ((c=getopt_long(argc, argv, OPTIONS, options, NULL)) != EOF) { + switch (c) { + case 'i': + include = optarg; + break; + + case 'N': + name = optarg; + break; + + case 'n': + name_space = optarg; + break; + + case 't': + type = optarg; + break; + + case 'V': + validate = true; + break; + + case OPT_NO_VALIDATE: + validate = false; + break; + + case OPT_VALIDATE_ONLY: + validate = true; + convert = false; + break; + + case 'h': + usage(argv[0], 0); + + default: + usage(argv[0], 1); + } + } + + if (optind != argc - 2 && optind != argc - 1) + usage(argv[0], 1); + + char *input = argv[optind]; + char *output = NULL; + if (optind == argc -2) { + output = argv[optind+1]; + } + + char *str = read_full_file(input, 10*1024*1024, NULL, input); + + Json::Reader reader; + + Json::Value json; + if (!reader.parse(str, json)) { + fprintf(stderr, "%s: parse error: %s\n", input, reader.getFormattedErrorMessages()); + exit(1); + } + + if (validate) { + std::string error_message; + Json::SchemaValidator *validator; + + try { + validator = Json::SchemaValidator::create_meta_validator(); + } + catch (Json::SchemaValidator::Exception e) { + fprintf(stderr, "%s: can't create meta schema validator\n", argv[0]); + exit(1); + } + + if (!validator->validate(json)) { + const std::vector errors = validator->errors(); + + for (unsigned int i=0; i(end-base)); + + for (char *p = name; *p; p++) { + if ((*p >= 'A' && *p < 'Z') || (*p >= 'a' && *p < 'z') || *p == '_') { + continue; + } + else if (*p >= '0' && *p <= '9') { + if (p > name) { + continue; + } + } + *p = '_'; + } + } + else { + for (const char *p = name; *p; p++) { + if ((*p >= 'A' && *p < 'Z') || (*p >= 'a' && *p < 'z') || *p == '_') { + continue; + } + else if (*p >= '0' && *p <= '9') { + if (p > name) { + continue; + } + } + else if (p[0] == ':' && p[1] == ':') { + p += 1; + continue; + } + + fprintf(stderr, "%s: name [%s] is not a valid C identifier\n", argv[0], name); + exit(1); + } + } + + if (include) { + fprintf(fout, "#include <%s>\n\n", include); + } + + if (strcmp(type, "std::string") == 0) { + fputs("#include \n\n", fout); + } + + if (name_space) { + fprintf(fout, "namespace %s {\n\n", name_space); + } + + fprintf(fout, "const %s %s = \"\\\n", type, name); + + if (free_name) { + free(name); + name = NULL; + } + + char line[8192]; + + while (fgets(line, sizeof(line), fin)) { + if (line[strlen(line)-1] == '\n') + line[strlen(line)-1] = '\0'; + + char *p = line; + char *end = line+strlen(line); + char *q; + do { + q = p + strcspn(p, "\"\\"); + if (q < end) { + fprintf(fout, "%.*s\\%c", (int)(q-p), p, *q); + p=q+1; + } + else + fprintf(fout, "%s", p); + } while (q < end); + + fputs(" \\n\\\n", fout); + } + + fputs("\";\n", fout); + + if (name_space) { + fputs("\n}\n", fout); + } + + if (ferror(fin)) { + fprintf(stderr, "%s: read error on schema file [%s]: %s\n", argv[0], input, strerror(errno)); + fclose(fout); + unlink(output); + exit(1); + } + + fclose(fin); + + if (ferror(fout)) { + fprintf(stderr, "%s: write error on output file [%s]: %s\n", argv[0], output, strerror(errno)); + fclose(fout); + unlink(output); + exit(1); + } + + fclose(fout); + + exit(0); +}