explicitly zero extra buffer space added with realloc to silence valgrind warnings
[project/libubox.git] / jshn.c
1 #include <json/json.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <stdbool.h>
6 #include <ctype.h>
7 #include <getopt.h>
8 #include "list.h"
9
10 #define MAX_VARLEN      256
11
12 static int add_json_element(const char *key, json_object *obj);
13
14 static int add_json_object(json_object *obj)
15 {
16         int ret = 0;
17
18         json_object_object_foreach(obj, key, val) {
19                 ret = add_json_element(key, val);
20                 if (ret)
21                         break;
22         }
23         return ret;
24 }
25
26 static int add_json_array(struct array_list *a)
27 {
28         char seq[12];
29         int i, len;
30         int ret;
31
32         for (i = 0, len = array_list_length(a); i < len; i++) {
33                 sprintf(seq, "%d", i);
34                 ret = add_json_element(seq, array_list_get_idx(a, i));
35                 if (ret)
36                         return ret;
37         }
38
39         return 0;
40 }
41
42 static void add_json_string(const char *str)
43 {
44         char *ptr = (char *) str;
45         int len;
46         char *c;
47
48         while ((c = strchr(ptr, '\'')) != NULL) {
49                 len = c - ptr;
50                 if (len > 0)
51                         fwrite(ptr, len, 1, stdout);
52                 ptr = c + 1;
53                 c = "'\\''";
54                 fwrite(c, strlen(c), 1, stdout);
55         }
56         len = strlen(ptr);
57         if (len > 0)
58                 fwrite(ptr, len, 1, stdout);
59 }
60
61 static void write_key_string(const char *key)
62 {
63         while (*key) {
64                 putc(isalnum(*key) ? *key : '_', stdout);
65                 key++;
66         }
67 }
68
69 static int add_json_element(const char *key, json_object *obj)
70 {
71         char *type;
72
73         if (!obj)
74                 return -1;
75
76         switch (json_object_get_type(obj)) {
77         case json_type_object:
78                 type = "object";
79                 break;
80         case json_type_array:
81                 type = "array";
82                 break;
83         case json_type_string:
84                 type = "string";
85                 break;
86         case json_type_boolean:
87                 type = "boolean";
88                 break;
89         case json_type_int:
90                 type = "int";
91                 break;
92         default:
93                 return -1;
94         }
95
96         fprintf(stdout, "json_add_%s '", type);
97         write_key_string(key);
98
99         switch (json_object_get_type(obj)) {
100         case json_type_object:
101                 fprintf(stdout, "';\n");
102                 add_json_object(obj);
103                 fprintf(stdout, "json_close_object;\n");
104                 break;
105         case json_type_array:
106                 fprintf(stdout, "';\n");
107                 add_json_array(json_object_get_array(obj));
108                 fprintf(stdout, "json_close_array;\n");
109                 break;
110         case json_type_string:
111                 fprintf(stdout, "' '");
112                 add_json_string(json_object_get_string(obj));
113                 fprintf(stdout, "';\n");
114                 break;
115         case json_type_boolean:
116                 fprintf(stdout, "' %d;\n", json_object_get_boolean(obj));
117                 break;
118         case json_type_int:
119                 fprintf(stdout, "' %d;\n", json_object_get_int(obj));
120                 break;
121         default:
122                 return -1;
123         }
124
125         return 0;
126 }
127
128 static int jshn_parse(const char *str)
129 {
130         json_object *obj;
131
132         obj = json_tokener_parse(str);
133         if (is_error(obj) || json_object_get_type(obj) != json_type_object) {
134                 fprintf(stderr, "Failed to parse message data\n");
135                 return 1;
136         }
137         fprintf(stdout, "json_init;\n");
138         add_json_object(obj);
139         fflush(stdout);
140
141         return 0;
142 }
143
144 static char *get_keys(const char *prefix)
145 {
146         char *keys;
147
148         keys = alloca(strlen(prefix) + sizeof("KEYS_") + 1);
149         sprintf(keys, "KEYS_%s", prefix);
150         return getenv(keys);
151 }
152
153 static void get_var(const char *prefix, const char **name, char **var, char **type)
154 {
155         char *tmpname, *varname;
156
157         tmpname = alloca(strlen(prefix) + 1 + strlen(*name) + 1 + sizeof("TYPE_"));
158         sprintf(tmpname, "TYPE_%s_%s", prefix, *name);
159         *var = getenv(tmpname + 5);
160         *type = getenv(tmpname);
161
162         memcpy(tmpname, "NAME", 4);
163         varname = getenv(tmpname);
164         if (varname)
165                 *name = varname;
166 }
167
168 static json_object *jshn_add_objects(json_object *obj, const char *prefix, bool array);
169
170 static void jshn_add_object_var(json_object *obj, bool array, const char *prefix, const char *name)
171 {
172         json_object *new;
173         char *var, *type;
174
175         get_var(prefix, &name, &var, &type);
176         if (!var || !type)
177                 return;
178
179         if (!strcmp(type, "array")) {
180                 new = json_object_new_array();
181                 jshn_add_objects(new, var, true);
182         } else if (!strcmp(type, "object")) {
183                 new = json_object_new_object();
184                 jshn_add_objects(new, var, false);
185         } else if (!strcmp(type, "string")) {
186                 new = json_object_new_string(var);
187         } else if (!strcmp(type, "int")) {
188                 new = json_object_new_int(atoi(var));
189         } else if (!strcmp(type, "boolean")) {
190                 new = json_object_new_boolean(!!atoi(var));
191         } else {
192                 return;
193         }
194
195         if (array)
196                 json_object_array_add(obj, new);
197         else
198                 json_object_object_add(obj, name, new);
199 }
200
201 static json_object *jshn_add_objects(json_object *obj, const char *prefix, bool array)
202 {
203         char *keys, *key, *brk;
204
205         keys = get_keys(prefix);
206         if (!keys || !obj)
207                 goto out;
208
209         for (key = strtok_r(keys, " ", &brk); key;
210              key = strtok_r(NULL, " ", &brk)) {
211                 jshn_add_object_var(obj, array, prefix, key);
212         }
213
214 out:
215         return obj;
216 }
217
218 static int jshn_format(bool no_newline)
219 {
220         json_object *obj;
221
222         obj = json_object_new_object();
223         jshn_add_objects(obj, "JSON_VAR", false);
224         fprintf(stdout, "%s%s", json_object_to_json_string(obj),
225                 no_newline ? "" : "\n");
226         json_object_put(obj);
227         return 0;
228 }
229
230 static int usage(const char *progname)
231 {
232         fprintf(stderr, "Usage: %s [-n] -r <message>|-w\n", progname);
233         return 2;
234 }
235
236 int main(int argc, char **argv)
237 {
238         bool no_newline = false;
239         int ch;
240
241         while ((ch = getopt(argc, argv, "nr:w")) != -1) {
242                 switch(ch) {
243                 case 'r':
244                         return jshn_parse(optarg);
245                 case 'w':
246                         return jshn_format(no_newline);
247                 case 'n':
248                         no_newline = true;
249                         break;
250                 default:
251                         return usage(argv[0]);
252                 }
253         }
254         return usage(argv[0]);
255 }