Mercurial > hg > freeDiameter
comparison 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 |
comparison
equal
deleted
inserted
replaced
1354:0dff6a604b0a | 1355:70b6067f4552 |
---|---|
1 /********************************************************************************************************** | |
2 * Software License Agreement (BSD License) * | |
3 * Author: Thomas Klausner <tk@giga.or.at> * | |
4 * * | |
5 * Copyright (c) 2016, 2017, 2019 Thomas Klausner * | |
6 * All rights reserved. * | |
7 * * | |
8 * Written under contract by nfotex IT GmbH, http://nfotex.com/ * | |
9 * * | |
10 * Redistribution and use of this software in source and binary forms, with or without modification, are * | |
11 * permitted provided that the following conditions are met: * | |
12 * * | |
13 * * Redistributions of source code must retain the above * | |
14 * copyright notice, this list of conditions and the * | |
15 * following disclaimer. * | |
16 * * | |
17 * * Redistributions in binary form must reproduce the above * | |
18 * copyright notice, this list of conditions and the * | |
19 * following disclaimer in the documentation and/or other * | |
20 * materials provided with the distribution. * | |
21 * * | |
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * | |
23 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * | |
24 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * | |
25 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * | |
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * | |
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * | |
28 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * | |
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * | |
30 **********************************************************************************************************/ | |
31 | |
32 #include <errno.h> | |
33 #include <getopt.h> | |
34 #include <inttypes.h> | |
35 #include <libgen.h> | |
36 #include <stdio.h> | |
37 #include <stdlib.h> | |
38 #include <string.h> | |
39 #include <sys/stat.h> | |
40 | |
41 #include <json/SchemaValidator.h> | |
42 | |
43 [[noreturn]] | |
44 void usage(char *prg, int exit_status) { | |
45 FILE *f = (exit_status ? stderr : stdout); | |
46 | |
47 fprintf(f, "Usage: %s [-hV] [-i include] [-N variable-name] [-n namespace] [-t type] json [output]\n", prg); | |
48 fprintf(f, "options:\n\ | |
49 -h, --help print this usage message and exit\n\ | |
50 -i, --include FILE include FILE in output C source file\n\ | |
51 -n, --namespace NAME specify namespace in which to define variable\n\ | |
52 -N, --name NAME specify name of variable to define\n\ | |
53 --no-validate don't validate against JSON meta schema (default)\n\ | |
54 --type TYPE specify type of variable (char * (default) or std::string)\n\ | |
55 --validate validate against JSON meta schema\n\ | |
56 --validate-only validate against JSON meta schema and exit (don't create C source)\n\ | |
57 "); | |
58 | |
59 exit(exit_status); | |
60 } | |
61 | |
62 const char *OPTIONS = "hi:N:n:t:V"; | |
63 | |
64 enum { | |
65 OPT_NO_VALIDATE = 256, | |
66 OPT_VALIDATE_ONLY | |
67 }; | |
68 struct option options[] = { | |
69 { "help", no_argument, NULL, 'h' }, | |
70 { "include", required_argument, NULL, 'i' }, | |
71 { "name", required_argument, NULL, 'N' }, | |
72 { "namespace", required_argument, NULL, 'n' }, | |
73 { "no-validate", no_argument, NULL, OPT_NO_VALIDATE }, | |
74 { "type", required_argument, NULL, 't' }, | |
75 { "validate", no_argument, NULL, 'V' }, | |
76 { "validate-only", no_argument, NULL, OPT_VALIDATE_ONLY }, | |
77 { NULL, 0, NULL, 0 } | |
78 }; | |
79 | |
80 char *read_full_file (const char *filename, off_t max_size, off_t* size_ret, const char *desc) { | |
81 FILE *fp; | |
82 if ((fp = fopen (filename, "rb")) == NULL) { | |
83 fprintf (stderr, "couldn't open %s file [%s]: %d, %s\n", desc, filename, errno, strerror(errno)); | |
84 return NULL; | |
85 } | |
86 struct stat stat_buf; | |
87 if (fstat (fileno(fp), &stat_buf) < 0) { | |
88 fprintf (stderr, "couldn't stat %s file [%s]: %d, %s\n", desc, filename, errno, strerror(errno)); | |
89 fclose (fp); | |
90 return NULL; | |
91 } | |
92 off_t n = stat_buf.st_size; | |
93 if (max_size > 0 && n > max_size) { | |
94 fprintf (stderr, "%s file [%s] is larger than %" PRIi64 " bytes\n", desc, filename, (int64_t)max_size); | |
95 fclose (fp); | |
96 return NULL; | |
97 } | |
98 char *buf; | |
99 if ((buf = (char *) malloc ((size_t)n+1)) == NULL) { | |
100 fprintf (stderr, "error allocating %" PRIi64 " bytes for read of %s file [%s]\n", (int64_t)n, desc, filename); | |
101 fclose (fp); | |
102 return NULL; | |
103 } | |
104 if (fread (buf, 1, (size_t)n, fp) < (size_t) n) { | |
105 fprintf (stderr, "error reading %s file [%s]: %d, %s\n", desc, filename, errno, strerror(errno)); | |
106 fclose (fp); | |
107 free (buf); | |
108 return NULL; | |
109 } | |
110 | |
111 fclose (fp); | |
112 buf[n] = '\0'; | |
113 if (size_ret != NULL) | |
114 *size_ret = n; | |
115 return buf; | |
116 } | |
117 | |
118 int main (int argc, char **argv) { | |
119 char *name_space = NULL; | |
120 char *name = NULL; | |
121 bool free_name = false; | |
122 char default_type[] = "char *"; | |
123 char *type = default_type; | |
124 bool validate = false; | |
125 bool convert = true; | |
126 char *include = NULL; | |
127 | |
128 int c; | |
129 while ((c=getopt_long(argc, argv, OPTIONS, options, NULL)) != EOF) { | |
130 switch (c) { | |
131 case 'i': | |
132 include = optarg; | |
133 break; | |
134 | |
135 case 'N': | |
136 name = optarg; | |
137 break; | |
138 | |
139 case 'n': | |
140 name_space = optarg; | |
141 break; | |
142 | |
143 case 't': | |
144 type = optarg; | |
145 break; | |
146 | |
147 case 'V': | |
148 validate = true; | |
149 break; | |
150 | |
151 case OPT_NO_VALIDATE: | |
152 validate = false; | |
153 break; | |
154 | |
155 case OPT_VALIDATE_ONLY: | |
156 validate = true; | |
157 convert = false; | |
158 break; | |
159 | |
160 case 'h': | |
161 usage(argv[0], 0); | |
162 | |
163 default: | |
164 usage(argv[0], 1); | |
165 } | |
166 } | |
167 | |
168 if (optind != argc - 2 && optind != argc - 1) | |
169 usage(argv[0], 1); | |
170 | |
171 char *input = argv[optind]; | |
172 char *output = NULL; | |
173 if (optind == argc -2) { | |
174 output = argv[optind+1]; | |
175 } | |
176 | |
177 char *str = read_full_file(input, 10*1024*1024, NULL, input); | |
178 | |
179 Json::Reader reader; | |
180 | |
181 Json::Value json; | |
182 if (!reader.parse(str, json)) { | |
183 fprintf(stderr, "%s: parse error: %s\n", input, reader.getFormattedErrorMessages()); | |
184 exit(1); | |
185 } | |
186 | |
187 if (validate) { | |
188 std::string error_message; | |
189 Json::SchemaValidator *validator; | |
190 | |
191 try { | |
192 validator = Json::SchemaValidator::create_meta_validator(); | |
193 } | |
194 catch (Json::SchemaValidator::Exception e) { | |
195 fprintf(stderr, "%s: can't create meta schema validator\n", argv[0]); | |
196 exit(1); | |
197 } | |
198 | |
199 if (!validator->validate(json)) { | |
200 const std::vector<Json::SchemaValidator::Error> errors = validator->errors(); | |
201 | |
202 for (unsigned int i=0; i<errors.size(); i++) | |
203 fprintf(stderr, "%s:%s: %s\n", input, errors[i].path.c_str(), errors[i].message.c_str()); | |
204 exit(1); | |
205 } | |
206 | |
207 try { | |
208 Json::SchemaValidator v(json); | |
209 } | |
210 catch (Json::SchemaValidator::Exception e) { | |
211 fprintf(stderr, "%s: can't create schema validator: %s\n", argv[0], e.type_message().c_str()); | |
212 for (auto error : e.errors) { | |
213 fprintf(stderr, "%s:%s: %s\n", input, error.path.c_str(), error.message.c_str()); | |
214 } | |
215 exit(1); | |
216 } | |
217 } | |
218 | |
219 if (!convert) { | |
220 exit(0); | |
221 } | |
222 | |
223 FILE *fin = fopen(input, "r"); | |
224 if (fin == NULL) { | |
225 fprintf(stderr, "%s: can't open schema file [%s]: %s\n", argv[0], input, strerror(errno)); | |
226 exit(1); | |
227 } | |
228 | |
229 FILE *fout; | |
230 | |
231 if (output) { | |
232 fout = fopen(output, "w"); | |
233 if (fout == NULL) { | |
234 fprintf(stderr, "%s: can't create output file [%s]: %s\n", argv[0], output, strerror(errno)); | |
235 exit(1); | |
236 } | |
237 } | |
238 else { | |
239 fout = stdout; | |
240 } | |
241 | |
242 if (name == NULL) { | |
243 char *base = basename(input); | |
244 char *end = strrchr(base, '.'); | |
245 if (end == NULL) { | |
246 end = base + strlen(base); | |
247 } | |
248 | |
249 name = strndup(base, static_cast<size_t>(end-base)); | |
250 | |
251 for (char *p = name; *p; p++) { | |
252 if ((*p >= 'A' && *p < 'Z') || (*p >= 'a' && *p < 'z') || *p == '_') { | |
253 continue; | |
254 } | |
255 else if (*p >= '0' && *p <= '9') { | |
256 if (p > name) { | |
257 continue; | |
258 } | |
259 } | |
260 *p = '_'; | |
261 } | |
262 } | |
263 else { | |
264 for (const char *p = name; *p; p++) { | |
265 if ((*p >= 'A' && *p < 'Z') || (*p >= 'a' && *p < 'z') || *p == '_') { | |
266 continue; | |
267 } | |
268 else if (*p >= '0' && *p <= '9') { | |
269 if (p > name) { | |
270 continue; | |
271 } | |
272 } | |
273 else if (p[0] == ':' && p[1] == ':') { | |
274 p += 1; | |
275 continue; | |
276 } | |
277 | |
278 fprintf(stderr, "%s: name [%s] is not a valid C identifier\n", argv[0], name); | |
279 exit(1); | |
280 } | |
281 } | |
282 | |
283 if (include) { | |
284 fprintf(fout, "#include <%s>\n\n", include); | |
285 } | |
286 | |
287 if (strcmp(type, "std::string") == 0) { | |
288 fputs("#include <string>\n\n", fout); | |
289 } | |
290 | |
291 if (name_space) { | |
292 fprintf(fout, "namespace %s {\n\n", name_space); | |
293 } | |
294 | |
295 fprintf(fout, "const %s %s = \"\\\n", type, name); | |
296 | |
297 if (free_name) { | |
298 free(name); | |
299 name = NULL; | |
300 } | |
301 | |
302 char line[8192]; | |
303 | |
304 while (fgets(line, sizeof(line), fin)) { | |
305 if (line[strlen(line)-1] == '\n') | |
306 line[strlen(line)-1] = '\0'; | |
307 | |
308 char *p = line; | |
309 char *end = line+strlen(line); | |
310 char *q; | |
311 do { | |
312 q = p + strcspn(p, "\"\\"); | |
313 if (q < end) { | |
314 fprintf(fout, "%.*s\\%c", (int)(q-p), p, *q); | |
315 p=q+1; | |
316 } | |
317 else | |
318 fprintf(fout, "%s", p); | |
319 } while (q < end); | |
320 | |
321 fputs(" \\n\\\n", fout); | |
322 } | |
323 | |
324 fputs("\";\n", fout); | |
325 | |
326 if (name_space) { | |
327 fputs("\n}\n", fout); | |
328 } | |
329 | |
330 if (ferror(fin)) { | |
331 fprintf(stderr, "%s: read error on schema file [%s]: %s\n", argv[0], input, strerror(errno)); | |
332 fclose(fout); | |
333 unlink(output); | |
334 exit(1); | |
335 } | |
336 | |
337 fclose(fin); | |
338 | |
339 if (ferror(fout)) { | |
340 fprintf(stderr, "%s: write error on output file [%s]: %s\n", argv[0], output, strerror(errno)); | |
341 fclose(fout); | |
342 unlink(output); | |
343 exit(1); | |
344 } | |
345 | |
346 fclose(fout); | |
347 | |
348 exit(0); | |
349 } |