1 #include <mruby.h>
2 
3 #ifdef MRB_DISABLE_STDIO
4 # error mruby-bin-mruby conflicts 'MRB_DISABLE_STDIO' configuration in your 'build_config.rb'
5 #endif
6 
7 #include <stdlib.h>
8 #include <string.h>
9 #include <mruby/array.h>
10 #include <mruby/compile.h>
11 #include <mruby/dump.h>
12 #include <mruby/variable.h>
13 
14 struct _args {
15   FILE *rfp;
16   char *cmdline;
17   mrb_bool fname        : 1;
18   mrb_bool mrbfile      : 1;
19   mrb_bool check_syntax : 1;
20   mrb_bool verbose      : 1;
21   mrb_bool version      : 1;
22   mrb_bool debug        : 1;
23   int argc;
24   char **argv;
25   int libc;
26   char **libv;
27 };
28 
29 struct options {
30   int argc;
31   char **argv;
32   char *program;
33   char *opt;
34   char short_opt[2];
35 };
36 
37 static void
usage(const char * name)38 usage(const char *name)
39 {
40   static const char *const usage_msg[] = {
41   "switches:",
42   "-b           load and execute RiteBinary (mrb) file",
43   "-c           check syntax only",
44   "-d           set debugging flags (set $DEBUG to true)",
45   "-e 'command' one line of script",
46   "-r library   load the library before executing your script",
47   "-v           print version number, then run in verbose mode",
48   "--verbose    run in verbose mode",
49   "--version    print the version",
50   "--copyright  print the copyright",
51   NULL
52   };
53   const char *const *p = usage_msg;
54 
55   printf("Usage: %s [switches] [programfile] [arguments]\n", name);
56   while (*p)
57     printf("  %s\n", *p++);
58 }
59 
60 static void
options_init(struct options * opts,int argc,char ** argv)61 options_init(struct options *opts, int argc, char **argv)
62 {
63   opts->argc = argc;
64   opts->argv = argv;
65   opts->program = *argv;
66   *opts->short_opt = 0;
67 }
68 
69 static const char *
options_opt(struct options * opts)70 options_opt(struct options *opts)
71 {
72   /* concatenated short options (e.g. `-cv`) */
73   if (*opts->short_opt && *++opts->opt) {
74    short_opt:
75     opts->short_opt[0] = *opts->opt;
76     opts->short_opt[1] = 0;
77     return opts->short_opt;
78   }
79 
80   while (++opts->argv, --opts->argc) {
81     opts->opt = *opts->argv;
82 
83     /*  empty         || not start with `-`  || `-` */
84     if (!opts->opt[0] || opts->opt[0] != '-' || !opts->opt[1]) return NULL;
85 
86     if (opts->opt[1] == '-') {
87       /* `--` */
88       if (!opts->opt[2]) {
89         ++opts->argv, --opts->argc;
90         return NULL;
91       }
92       /* long option */
93       opts->opt += 2;
94       *opts->short_opt = 0;
95       return opts->opt;
96     }
97     else {
98       /* short option */
99       ++opts->opt;
100       goto short_opt;
101     }
102   }
103   return NULL;
104 }
105 
106 static const char *
options_arg(struct options * opts)107 options_arg(struct options *opts)
108 {
109   if (*opts->short_opt && opts->opt[1]) {
110     /* concatenated short option and option argument (e.g. `-rLIBRARY`) */
111     *opts->short_opt = 0;
112     return opts->opt + 1;
113   }
114   --opts->argc, ++opts->argv;
115   return opts->argc ? *opts->argv : NULL;
116 }
117 
118 static char *
dup_arg_item(mrb_state * mrb,const char * item)119 dup_arg_item(mrb_state *mrb, const char *item)
120 {
121   size_t buflen = strlen(item) + 1;
122   char *buf = (char*)mrb_malloc(mrb, buflen);
123   memcpy(buf, item, buflen);
124   return buf;
125 }
126 
127 static int
parse_args(mrb_state * mrb,int argc,char ** argv,struct _args * args)128 parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args)
129 {
130   static const struct _args args_zero = { 0 };
131   struct options opts[1];
132   const char *opt, *item;
133 
134   *args = args_zero;
135   options_init(opts, argc, argv);
136   while ((opt = options_opt(opts))) {
137     if (strcmp(opt, "b") == 0) {
138       args->mrbfile = TRUE;
139     }
140     else if (strcmp(opt, "c") == 0) {
141       args->check_syntax = TRUE;
142     }
143     else if (strcmp(opt, "d") == 0) {
144       args->debug = TRUE;
145     }
146     else if (strcmp(opt, "e") == 0) {
147       if ((item = options_arg(opts))) {
148         if (!args->cmdline) {
149           args->cmdline = dup_arg_item(mrb, item);
150         }
151         else {
152           size_t cmdlinelen;
153           size_t itemlen;
154 
155           cmdlinelen = strlen(args->cmdline);
156           itemlen = strlen(item);
157           args->cmdline =
158             (char *)mrb_realloc(mrb, args->cmdline, cmdlinelen + itemlen + 2);
159           args->cmdline[cmdlinelen] = '\n';
160           memcpy(args->cmdline + cmdlinelen + 1, item, itemlen + 1);
161         }
162       }
163       else {
164         fprintf(stderr, "%s: No code specified for -e\n", opts->program);
165         return EXIT_FAILURE;
166       }
167     }
168     else if (strcmp(opt, "h") == 0) {
169       usage(opts->program);
170       exit(EXIT_SUCCESS);
171     }
172     else if (strcmp(opt, "r") == 0) {
173       if ((item = options_arg(opts))) {
174         if (args->libc == 0) {
175           args->libv = (char**)mrb_malloc(mrb, sizeof(char*));
176         }
177         else {
178           args->libv = (char**)mrb_realloc(mrb, args->libv, sizeof(char*) * (args->libc + 1));
179         }
180         args->libv[args->libc++] = dup_arg_item(mrb, item);
181       }
182       else {
183         fprintf(stderr, "%s: No library specified for -r\n", opts->program);
184         return EXIT_FAILURE;
185       }
186     }
187     else if (strcmp(opt, "v") == 0) {
188       if (!args->verbose) {
189         mrb_show_version(mrb);
190         args->version = TRUE;
191       }
192       args->verbose = TRUE;
193     }
194     else if (strcmp(opt, "version") == 0) {
195       mrb_show_version(mrb);
196       exit(EXIT_SUCCESS);
197     }
198     else if (strcmp(opt, "verbose") == 0) {
199       args->verbose = TRUE;
200     }
201     else if (strcmp(opt, "copyright") == 0) {
202       mrb_show_copyright(mrb);
203       exit(EXIT_SUCCESS);
204     }
205     else {
206       fprintf(stderr, "%s: invalid option %s%s (-h will show valid options)\n",
207               opts->program, opt[1] ? "--" : "-", opt);
208       return EXIT_FAILURE;
209     }
210   }
211 
212   argc = opts->argc; argv = opts->argv;
213   if (args->cmdline == NULL) {
214     if (*argv == NULL) {
215       if (args->version) exit(EXIT_SUCCESS);
216       args->rfp = stdin;
217     }
218     else {
219       args->rfp = strcmp(argv[0], "-") == 0 ?
220         stdin : fopen(argv[0], args->mrbfile ? "rb" : "r");
221       if (args->rfp == NULL) {
222         fprintf(stderr, "%s: Cannot open program file: %s\n", opts->program, argv[0]);
223         return EXIT_FAILURE;
224       }
225       args->fname = TRUE;
226       args->cmdline = argv[0];
227       argc--; argv++;
228     }
229   }
230   args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1));
231   memcpy(args->argv, argv, (argc+1) * sizeof(char*));
232   args->argc = argc;
233 
234   return EXIT_SUCCESS;
235 }
236 
237 static void
cleanup(mrb_state * mrb,struct _args * args)238 cleanup(mrb_state *mrb, struct _args *args)
239 {
240   if (args->rfp && args->rfp != stdin)
241     fclose(args->rfp);
242   if (!args->fname)
243     mrb_free(mrb, args->cmdline);
244   mrb_free(mrb, args->argv);
245   if (args->libc) {
246     while (args->libc--) {
247       mrb_free(mrb, args->libv[args->libc]);
248     }
249     mrb_free(mrb, args->libv);
250   }
251   mrb_close(mrb);
252 }
253 
254 int
main(int argc,char ** argv)255 main(int argc, char **argv)
256 {
257   mrb_state *mrb = mrb_open();
258   int n = -1;
259   int i;
260   struct _args args;
261   mrb_value ARGV;
262   mrbc_context *c;
263   mrb_value v;
264   mrb_sym zero_sym;
265 
266   if (mrb == NULL) {
267     fprintf(stderr, "%s: Invalid mrb_state, exiting mruby\n", *argv);
268     return EXIT_FAILURE;
269   }
270 
271   n = parse_args(mrb, argc, argv, &args);
272   if (n == EXIT_FAILURE || (args.cmdline == NULL && args.rfp == NULL)) {
273     cleanup(mrb, &args);
274     return n;
275   }
276   else {
277     int ai = mrb_gc_arena_save(mrb);
278     ARGV = mrb_ary_new_capa(mrb, args.argc);
279     for (i = 0; i < args.argc; i++) {
280       char* utf8 = mrb_utf8_from_locale(args.argv[i], -1);
281       if (utf8) {
282         mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8));
283         mrb_utf8_free(utf8);
284       }
285     }
286     mrb_define_global_const(mrb, "ARGV", ARGV);
287     mrb_gv_set(mrb, mrb_intern_lit(mrb, "$DEBUG"), mrb_bool_value(args.debug));
288 
289     c = mrbc_context_new(mrb);
290     if (args.verbose)
291       c->dump_result = TRUE;
292     if (args.check_syntax)
293       c->no_exec = TRUE;
294 
295     /* Set $0 */
296     zero_sym = mrb_intern_lit(mrb, "$0");
297     if (args.rfp) {
298       const char *cmdline;
299       cmdline = args.cmdline ? args.cmdline : "-";
300       mrbc_filename(mrb, c, cmdline);
301       mrb_gv_set(mrb, zero_sym, mrb_str_new_cstr(mrb, cmdline));
302     }
303     else {
304       mrbc_filename(mrb, c, "-e");
305       mrb_gv_set(mrb, zero_sym, mrb_str_new_lit(mrb, "-e"));
306     }
307 
308     /* Load libraries */
309     for (i = 0; i < args.libc; i++) {
310       FILE *lfp = fopen(args.libv[i], args.mrbfile ? "rb" : "r");
311       if (lfp == NULL) {
312         fprintf(stderr, "%s: Cannot open library file: %s\n", *argv, args.libv[i]);
313         mrbc_context_free(mrb, c);
314         cleanup(mrb, &args);
315         return EXIT_FAILURE;
316       }
317       if (args.mrbfile) {
318         v = mrb_load_irep_file_cxt(mrb, lfp, c);
319       }
320       else {
321         v = mrb_load_file_cxt(mrb, lfp, c);
322       }
323       fclose(lfp);
324     }
325 
326     /* Load program */
327     if (args.mrbfile) {
328       v = mrb_load_irep_file_cxt(mrb, args.rfp, c);
329     }
330     else if (args.rfp) {
331       v = mrb_load_file_cxt(mrb, args.rfp, c);
332     }
333     else {
334       char* utf8 = mrb_utf8_from_locale(args.cmdline, -1);
335       if (!utf8) abort();
336       v = mrb_load_string_cxt(mrb, utf8, c);
337       mrb_utf8_free(utf8);
338     }
339 
340     mrb_gc_arena_restore(mrb, ai);
341     mrbc_context_free(mrb, c);
342     if (mrb->exc) {
343       if (!mrb_undef_p(v)) {
344         mrb_print_error(mrb);
345       }
346       n = EXIT_FAILURE;
347     }
348     else if (args.check_syntax) {
349       puts("Syntax OK");
350     }
351   }
352   cleanup(mrb, &args);
353 
354   return n;
355 }
356