• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ucode/main.c

  1 /*
  2  * Copyright (C) 2020-2021 Jo-Philipp Wich <jo@mein.io>
  3  *
  4  * Permission to use, copy, modify, and/or distribute this software for any
  5  * purpose with or without fee is hereby granted, provided that the above
  6  * copyright notice and this permission notice appear in all copies.
  7  *
  8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 15  */
 16 
 17 #include <stdio.h>
 18 #include <stdbool.h>
 19 #include <stdint.h>
 20 #include <unistd.h>
 21 #include <errno.h>
 22 #include <ctype.h>
 23 #include <sys/stat.h>
 24 
 25 #ifdef JSONC
 26         #include <json.h>
 27 #else
 28         #include <json-c/json.h>
 29 #endif
 30 
 31 #include "ucode/compiler.h"
 32 #include "ucode/lexer.h"
 33 #include "ucode/lib.h"
 34 #include "ucode/vm.h"
 35 #include "ucode/source.h"
 36 #include "ucode/program.h"
 37 
 38 
 39 static void
 40 print_usage(const char *app)
 41 {
 42         printf(
 43         "Usage\n\n"
 44         "  # %s [-t] [-l] [-r] [-S] [-R] [-x function [-x ...]] [-e '[prefix=]{\"var\": ...}'] [-E [prefix=]env.json] {-i <file> | -s \"ucode script...\"}\n"
 45         "  -h, --help   Print this help\n"
 46         "  -i file      Execute the given ucode script file\n"
 47         "  -s \"ucode script...\"       Execute the given string as ucode script\n"
 48         "  -t Enable VM execution tracing\n"
 49         "  -l Do not strip leading block whitespace\n"
 50         "  -r Do not trim trailing block newlines\n"
 51         "  -S Enable strict mode\n"
 52         "  -R Enable raw code mode\n"
 53         "  -e Set global variables from given JSON object\n"
 54         "  -E Set global variables from given JSON file\n"
 55         "  -x Disable given function\n"
 56         "  -m Preload given module\n"
 57         "  -o Write precompiled byte code to given file\n"
 58         "  -O Write precompiled byte code to given file and strip debug information\n",
 59                 basename(app));
 60 }
 61 
 62 static void
 63 register_variable(uc_value_t *scope, const char *key, uc_value_t *val)
 64 {
 65         char *name = strdup(key);
 66         char *p;
 67 
 68         if (!name)
 69                 return;
 70 
 71         for (p = name; *p; p++)
 72                 if (!isalnum(*p) && *p != '_')
 73                         *p = '_';
 74 
 75         ucv_object_add(scope, name, val);
 76         free(name);
 77 }
 78 
 79 
 80 static int
 81 compile(uc_vm_t *vm, uc_source_t *src, FILE *precompile, bool strip)
 82 {
 83         uc_value_t *res = NULL;
 84         uc_function_t *entry;
 85         int rc = 0;
 86         char *err;
 87 
 88         entry = uc_compile(vm->config, src, &err);
 89 
 90         if (!entry) {
 91                 fprintf(stderr, "%s", err);
 92                 free(err);
 93                 rc = -1;
 94                 goto out;
 95         }
 96 
 97         if (precompile) {
 98                 uc_program_to_file(entry->program, precompile, !strip);
 99                 uc_program_free(entry->program);
100                 fclose(precompile);
101                 goto out;
102         }
103 
104         rc = uc_vm_execute(vm, entry, &res);
105 
106         switch (rc) {
107         case STATUS_OK:
108                 rc = 0;
109                 break;
110 
111         case STATUS_EXIT:
112                 rc = (int)ucv_int64_get(res);
113                 break;
114 
115         case ERROR_COMPILE:
116                 rc = -1;
117                 break;
118 
119         case ERROR_RUNTIME:
120                 rc = -2;
121                 break;
122         }
123 
124 out:
125         ucv_put(res);
126 
127         return rc;
128 }
129 
130 static uc_source_t *
131 read_stdin(char **ptr)
132 {
133         size_t rlen = 0, tlen = 0;
134         char buf[128];
135 
136         if (*ptr) {
137                 fprintf(stderr, "Can read from stdin only once\n");
138                 errno = EINVAL;
139 
140                 return NULL;
141         }
142 
143         while (true) {
144                 rlen = fread(buf, 1, sizeof(buf), stdin);
145 
146                 if (rlen == 0)
147                         break;
148 
149                 *ptr = xrealloc(*ptr, tlen + rlen);
150                 memcpy(*ptr + tlen, buf, rlen);
151                 tlen += rlen;
152         }
153 
154         return uc_source_new_buffer("[stdin]", *ptr, tlen);
155 }
156 
157 static uc_value_t *
158 parse_envfile(FILE *fp)
159 {
160         enum json_tokener_error err = json_tokener_continue;
161         struct json_tokener *tok;
162         json_object *jso = NULL;
163         uc_value_t *rv;
164         char buf[128];
165         size_t rlen;
166 
167         tok = xjs_new_tokener();
168 
169         while (true) {
170                 rlen = fread(buf, 1, sizeof(buf), fp);
171 
172                 if (rlen == 0)
173                         break;
174 
175                 jso = json_tokener_parse_ex(tok, buf, rlen);
176                 err = json_tokener_get_error(tok);
177 
178                 if (err != json_tokener_continue)
179                         break;
180         }
181 
182         if (err != json_tokener_success || !json_object_is_type(jso, json_type_object)) {
183                 json_object_put(jso);
184 
185                 return NULL;
186         }
187 
188         json_tokener_free(tok);
189 
190         rv = ucv_from_json(NULL, jso);
191 
192         json_object_put(jso);
193 
194         return rv;
195 }
196 
197 int
198 main(int argc, char **argv)
199 {
200         uc_source_t *source = NULL, *envfile = NULL;
201         FILE *precompile = NULL;
202         char *stdin = NULL, *c;
203         bool strip = false;
204         uc_vm_t vm = { 0 };
205         uc_value_t *o, *p;
206         int opt, rv = 0;
207 
208         uc_parse_config_t config = {
209                 .strict_declarations = false,
210                 .lstrip_blocks = true,
211                 .trim_blocks = true
212         };
213 
214         if (argc == 1)
215         {
216                 print_usage(argv[0]);
217                 goto out;
218         }
219 
220         uc_vm_init(&vm, &config);
221 
222         /* load std functions into global scope */
223         uc_stdlib_load(uc_vm_scope_get(&vm));
224 
225         /* register ARGV array */
226         o = ucv_array_new_length(&vm, argc);
227 
228         for (opt = 0; opt < argc; opt++)
229                 ucv_array_push(o, ucv_string_new(argv[opt]));
230 
231         ucv_object_add(uc_vm_scope_get(&vm), "ARGV", o);
232 
233         /* parse options */
234         while ((opt = getopt(argc, argv, "hlrtSRe:E:i:s:m:x:o:O:")) != -1)
235         {
236                 switch (opt) {
237                 case 'h':
238                         print_usage(argv[0]);
239                         goto out;
240 
241                 case 'i':
242                         if (source)
243                                 fprintf(stderr, "Options -i and -s are exclusive\n");
244 
245                         if (!strcmp(optarg, "-"))
246                                 source = read_stdin(&stdin);
247                         else
248                                 source = uc_source_new_file(optarg);
249 
250                         if (!source) {
251                                 fprintf(stderr, "Failed to open %s: %s\n", optarg, strerror(errno));
252                                 rv = 1;
253                                 goto out;
254                         }
255 
256                         break;
257 
258                 case 'l':
259                         config.lstrip_blocks = false;
260                         break;
261 
262                 case 'r':
263                         config.trim_blocks = false;
264                         break;
265 
266                 case 's':
267                         if (source)
268                                 fprintf(stderr, "Options -i and -s are exclusive\n");
269 
270                         c = xstrdup(optarg);
271                         source = uc_source_new_buffer("[-s argument]", c, strlen(c));
272 
273                         if (!source)
274                                 free(c);
275 
276                         break;
277 
278                 case 'S':
279                         config.strict_declarations = true;
280                         break;
281 
282                 case 'R':
283                         config.raw_mode = true;
284                         break;
285 
286                 case 'e':
287                         c = strchr(optarg, '=');
288 
289                         if (c)
290                                 *c++ = 0;
291                         else
292                                 c = optarg;
293 
294                         envfile = uc_source_new_buffer("[-e argument]", xstrdup(c), strlen(c));
295                         /* fallthrough */
296 
297                 case 'E':
298                         if (!envfile) {
299                                 c = strchr(optarg, '=');
300 
301                                 if (c)
302                                         *c++ = 0;
303                                 else
304                                         c = optarg;
305 
306                                 if (!strcmp(c, "-"))
307                                         envfile = read_stdin(&stdin);
308                                 else
309                                         envfile = uc_source_new_file(c);
310 
311                                 if (!envfile) {
312                                         fprintf(stderr, "Failed to open %s: %s\n", c, strerror(errno));
313                                         rv = 1;
314                                         goto out;
315                                 }
316                         }
317 
318                         o = parse_envfile(envfile->fp);
319 
320                         uc_source_put(envfile);
321 
322                         envfile = NULL;
323 
324                         if (!o) {
325                                 fprintf(stderr, "Option -%c must point to a valid JSON object\n", opt);
326                                 rv = 1;
327                                 goto out;
328                         }
329 
330                         if (c > optarg && optarg[0]) {
331                                 p = ucv_object_new(&vm);
332                                 ucv_object_add(uc_vm_scope_get(&vm), optarg, p);
333                         }
334                         else {
335                                 p = uc_vm_scope_get(&vm);
336                         }
337 
338                         ucv_object_foreach(o, key, val)
339                                 register_variable(p, key, ucv_get(val));
340 
341                         ucv_put(o);
342 
343                         break;
344 
345                 case 'm':
346                         p = ucv_string_new(optarg);
347                         o = uc_vm_invoke(&vm, "require", 1, p);
348 
349                         if (o)
350                                 register_variable(uc_vm_scope_get(&vm), optarg, o);
351 
352                         ucv_put(p);
353 
354                         break;
355 
356                 case 't':
357                         uc_vm_trace_set(&vm, 1);
358                         break;
359 
360                 case 'x':
361                         o = ucv_object_get(uc_vm_scope_get(&vm), optarg, NULL);
362 
363                         if (ucv_is_callable(o))
364                                 ucv_object_delete(uc_vm_scope_get(&vm), optarg);
365                         else
366                                 fprintf(stderr, "Unknown function %s specified\n", optarg);
367 
368                         break;
369 
370                 case 'o':
371                 case 'O':
372                         strip = (opt == 'O');
373 
374                         if (!strcmp(optarg, "-")) {
375                                 precompile = stdout;
376                         }
377                         else {
378                                 precompile = fopen(optarg, "wb");
379 
380                                 if (!precompile) {
381                                         fprintf(stderr, "Unable to open output file %s: %s\n",
382                                                 optarg, strerror(errno));
383 
384                                         goto out;
385                                 }
386                         }
387 
388                         break;
389                 }
390         }
391 
392         if (!source && argv[optind] != NULL) {
393                 source = uc_source_new_file(argv[optind]);
394 
395                 if (!source) {
396                         fprintf(stderr, "Failed to open %s: %s\n", argv[optind], strerror(errno));
397                         rv = 1;
398                         goto out;
399                 }
400         }
401 
402         if (!source) {
403                 fprintf(stderr, "One of -i or -s is required\n");
404                 rv = 1;
405                 goto out;
406         }
407 
408         rv = compile(&vm, source, precompile, strip);
409 
410 out:
411         uc_source_put(source);
412 
413         uc_vm_free(&vm);
414 
415         return rv;
416 }
417 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt