Update copyright dates
[src/app-framework-binder.git] / src / devtools / exprefs.c
1 /*
2  * Copyright (C) 2015-2020 "IoT.bzh"
3  * Author José Bollo <jose.bollo@iot.bzh>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 /*
18  * This simple program expands the object { "$ref": "#/path/to/a/target" }
19  *
20  * For example:
21  *
22  *  {
23  *    "type":{
24  *      "a": "int",
25  *      "b": { "$ref": "#/type/a" }
26  *    }
27  *  }
28  *
29  * will be exapanded to
30  *
31  *  {
32  *    "type":{
33  *      "a": "int",
34  *      "b": "int"
35  *    }
36  *  }
37  *
38  * Invocation:   program  [file|-]...
39  *
40  * without arguments, it reads the input.
41  */
42
43 #define _GNU_SOURCE
44 #include <string.h>
45 #include <stdio.h>
46 #include <unistd.h>
47
48 #include <json-c/json.h>
49
50 /**
51  * records path to the expanded node
52  */
53 struct path
54 {
55         struct json_object *object;     /**< node being expanded */
56         struct path *upper;             /**< link to upper expanded nodes */
57 };
58
59 /**
60  * root of the JSON being parsed
61  */
62 struct json_object *root;
63
64 /**
65  * Search for a reference of type "#/a/b/c" int the
66  * parsed JSON object
67  */
68 struct json_object *search(const char *path)
69 {
70         char *d;
71         struct json_object *i;
72
73         /* does it match #/ at the beginning? */
74         if (path[0] != '#' || (path[0] && path[1] != '/'))
75                 return NULL;
76
77         /* search from root to target */
78         i = root;
79         d = strdupa(path+2);
80         d = strtok(d, "/");
81         while(i && d) {
82                 if (!json_object_object_get_ex(i, d, &i))
83                         return NULL;
84                 d = strtok(NULL, "/");
85         }
86         return i;
87 }
88
89 /**
90  * Expands the node designated by path and returns its expanded form
91  */
92 struct json_object *expand(struct path path)
93 {
94         struct path *p;
95         struct json_object *o, *x;
96         int n, i;
97         struct json_object_iterator ji, jn;
98
99         /* expansion depends of the type of the node */
100         switch (json_object_get_type(path.object)) {
101         case json_type_object:
102                 /* for object, look if it contains a property "$ref" */
103                 if (json_object_object_get_ex(path.object, "$ref", &o)) {
104                         /* yes, reference, try to substitute its target */
105                         if (!json_object_is_type(o, json_type_string)) {
106                                 fprintf(stderr, "found a $ref not being string. Is: %s\n", json_object_get_string(o));
107                                 exit(1);
108                         }
109                         x = search(json_object_get_string(o));
110                         if (!x) {
111                                 fprintf(stderr, "$ref not found. Was: %s\n", json_object_get_string(o));
112                                 exit(1);
113                         }
114                         p = &path;
115                         while(p) {
116                                 if (x == p->object) {
117                                         fprintf(stderr, "$ref recursive. Was: %s\n", json_object_get_string(o));
118                                         exit(1);
119                                 }
120                                 p = p->upper;
121                         }
122                         /* cool found, return a new instance of the target */
123                         return json_object_get(x);
124                 }
125                 /* no, expand the values */
126                 ji = json_object_iter_begin(path.object);
127                 jn = json_object_iter_end(path.object);
128                 while (!json_object_iter_equal(&ji, &jn)) {
129                         o = json_object_iter_peek_value(&ji);
130                         x = expand((struct path){ .object = o, .upper = &path });
131                         if (x != o)
132                                 json_object_object_add(path.object, json_object_iter_peek_name(&ji), x);
133                         json_object_iter_next(&ji);
134                 }
135                 break;
136         case json_type_array:
137                 /* expand the values of arrays */
138                 i = 0;
139                 n = (int)json_object_array_length(path.object);
140                 while (i != n) {
141                         o = json_object_array_get_idx(path.object, i);
142                         x = expand((struct path){ .object = o, .upper = &path });
143                         if (x != o)
144                                 json_object_array_put_idx(path.object, i, x);
145                         i++;
146                 }
147                 break;
148         default:
149                 /* otherwise no expansion */
150                 break;
151         }
152         /* return the given node */
153         return path.object;
154 }
155
156 /**
157  * process a file and prints its expansion on stdout
158  */
159 void process(char *filename)
160 {
161         /* translate - */
162         if (!strcmp(filename, "-"))
163                 filename = "/dev/stdin";
164
165         /* check access */
166         if (access(filename, R_OK)) {
167                 fprintf(stderr, "can't access file %s\n", filename);
168                 exit(1);
169         }
170
171         /* read the file */
172         root = json_object_from_file(filename);
173         if (!root) {
174                 fprintf(stderr, "reading file %s produced null\n", filename);
175                 exit(1);
176         }
177
178         /* expand */
179         root = expand((struct path){ .object = root, .upper = NULL });
180
181         /* print the result */
182         json_object_to_file_ext ("/dev/stdout", root, JSON_C_TO_STRING_PRETTY);
183
184         /* clean up */
185         json_object_put(root);
186 }
187
188 /** process the list of files or stdin if none */
189 int main(int ac, char **av)
190 {
191         if (!*++av)
192                 process("-");
193         else {
194                 do { process(*av); } while(*++av);
195         }
196         return 0;
197 }
198