Mercurial > hg > freeDiameter
diff extensions/dict_json/json-schema-to-c.cc @ 1355:70b6067f4552
Add tool to generate dict_json_dict_schema.cc from dict_json_dict_schema.json.
author | Thomas Klausner <tk@giga.or.at> |
---|---|
date | Sat, 18 May 2019 11:06:01 +0200 |
parents | |
children | cf411b1dcbbb |
line wrap: on
line diff
--- /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 <tk@giga.or.at> * + * * + * 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 <errno.h> +#include <getopt.h> +#include <inttypes.h> +#include <libgen.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#include <json/SchemaValidator.h> + +[[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<Json::SchemaValidator::Error> errors = validator->errors(); + + for (unsigned int i=0; i<errors.size(); i++) + fprintf(stderr, "%s:%s: %s\n", input, errors[i].path.c_str(), errors[i].message.c_str()); + exit(1); + } + + try { + Json::SchemaValidator v(json); + } + catch (Json::SchemaValidator::Exception e) { + fprintf(stderr, "%s: can't create schema validator: %s\n", argv[0], e.type_message().c_str()); + for (auto error : e.errors) { + fprintf(stderr, "%s:%s: %s\n", input, error.path.c_str(), error.message.c_str()); + } + exit(1); + } + } + + if (!convert) { + exit(0); + } + + FILE *fin = fopen(input, "r"); + if (fin == NULL) { + fprintf(stderr, "%s: can't open schema file [%s]: %s\n", argv[0], input, strerror(errno)); + exit(1); + } + + FILE *fout; + + if (output) { + fout = fopen(output, "w"); + if (fout == NULL) { + fprintf(stderr, "%s: can't create output file [%s]: %s\n", argv[0], output, strerror(errno)); + exit(1); + } + } + else { + fout = stdout; + } + + if (name == NULL) { + char *base = basename(input); + char *end = strrchr(base, '.'); + if (end == NULL) { + end = base + strlen(base); + } + + name = strndup(base, static_cast<size_t>(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 <string>\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); +}