mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
05ffe0c441
changed mrb_open() and mrb_open_core() to return mrb_state with mrb->exc set (instead of NULL) when initialization fails. this allows callers to programmatically inspect error details, which is essential for embedded systems without stderr. return NULL only for true allocation failure. added MRB_OPEN_SUCCESS(mrb) macro to check initialization success, since mrb != NULL no longer guarantees success. updated all binary tools (mruby, mirb, mrdb, mrbtest) to use new pattern: check MRB_OPEN_SUCCESS, print exception details via mrb_print_error if available, then mrb_close. mrb_core_init_protect now preserves exception in mrb->exc instead of printing and clearing it, giving caller control over error handling. breaking change: callers must use MRB_OPEN_SUCCESS(mrb) or check both mrb != NULL && mrb->exc == NULL. old NULL-only checks will miss initialization failures. Co-authored-by: Claude <noreply@anthropic.com>
387 lines
9.4 KiB
C
387 lines
9.4 KiB
C
#include <mruby.h>
|
|
|
|
#ifdef MRB_NO_STDIO
|
|
# error mruby-bin-mruby conflicts 'MRB_NO_STDIO' in your build configuration
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <mruby/array.h>
|
|
#include <mruby/compile.h>
|
|
#include <mruby/dump.h>
|
|
#include <mruby/variable.h>
|
|
#include <mruby/proc.h>
|
|
#include <mruby/error.h>
|
|
#include <mruby/presym.h>
|
|
|
|
#if defined(_WIN32)
|
|
# include <io.h> /* for setmode */
|
|
# include <fcntl.h>
|
|
#endif
|
|
|
|
struct _args {
|
|
FILE *rfp;
|
|
char *cmdline;
|
|
mrb_bool fname : 1;
|
|
mrb_bool mrbfile : 1;
|
|
mrb_bool check_syntax : 1;
|
|
mrb_bool verbose : 1;
|
|
mrb_bool version : 1;
|
|
mrb_bool debug : 1;
|
|
int argc;
|
|
char **argv;
|
|
int libc;
|
|
char **libv;
|
|
};
|
|
|
|
struct options {
|
|
int argc;
|
|
char **argv;
|
|
char *program;
|
|
char *opt;
|
|
char short_opt[2];
|
|
};
|
|
|
|
static void
|
|
usage(const char *name)
|
|
{
|
|
static const char *const usage_msg[] = {
|
|
"switches:",
|
|
"-b load and execute RiteBinary (mrb) file",
|
|
"-c check syntax only",
|
|
"-d set debugging flags (set $DEBUG to true)",
|
|
"-e 'command' one line of script",
|
|
"-r library load the library before executing your script",
|
|
"-v print version number, then run in verbose mode",
|
|
"--verbose run in verbose mode",
|
|
"--version print the version",
|
|
"--copyright print the copyright",
|
|
NULL
|
|
};
|
|
const char *const *p = usage_msg;
|
|
|
|
printf("Usage: %s [switches] [programfile] [arguments]\n", name);
|
|
while (*p)
|
|
printf(" %s\n", *p++);
|
|
}
|
|
|
|
static mrb_bool
|
|
mrb_extension_p(const char *path)
|
|
{
|
|
const char *e = strrchr(path, '.');
|
|
if (e && e[1] == 'm' && e[2] == 'r' && e[3] == 'b' && e[4] == '\0') {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
options_init(struct options *opts, int argc, char **argv)
|
|
{
|
|
opts->argc = argc;
|
|
opts->argv = argv;
|
|
opts->program = *argv;
|
|
*opts->short_opt = 0;
|
|
}
|
|
|
|
static const char *
|
|
options_opt(struct options *opts)
|
|
{
|
|
/* concatenated short options (e.g. `-cv`) */
|
|
if (*opts->short_opt && *++opts->opt) {
|
|
short_opt:
|
|
opts->short_opt[0] = *opts->opt;
|
|
opts->short_opt[1] = 0;
|
|
return opts->short_opt;
|
|
}
|
|
|
|
while (++opts->argv, --opts->argc) {
|
|
opts->opt = *opts->argv;
|
|
|
|
/* not start with `-` || `-` */
|
|
if (opts->opt[0] != '-' || !opts->opt[1]) return NULL;
|
|
|
|
if (opts->opt[1] == '-') {
|
|
/* `--` */
|
|
if (!opts->opt[2]) {
|
|
opts->argv++, opts->argc--;
|
|
return NULL;
|
|
}
|
|
/* long option */
|
|
opts->opt += 2;
|
|
*opts->short_opt = 0;
|
|
return opts->opt;
|
|
}
|
|
else {
|
|
/* short option */
|
|
opts->opt++;
|
|
goto short_opt;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static const char *
|
|
options_arg(struct options *opts)
|
|
{
|
|
if (*opts->short_opt && opts->opt[1]) {
|
|
/* concatenated short option and option argument (e.g. `-rLIBRARY`) */
|
|
*opts->short_opt = 0;
|
|
return opts->opt + 1;
|
|
}
|
|
--opts->argc, ++opts->argv;
|
|
return opts->argc ? *opts->argv : NULL;
|
|
}
|
|
|
|
static char *
|
|
dup_arg_item(mrb_state *mrb, const char *item)
|
|
{
|
|
size_t buflen = strlen(item) + 1;
|
|
char *buf = (char*)mrb_malloc(mrb, buflen);
|
|
memcpy(buf, item, buflen);
|
|
return buf;
|
|
}
|
|
|
|
static int
|
|
parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args)
|
|
{
|
|
static const struct _args args_zero = { 0 };
|
|
struct options opts[1];
|
|
const char *opt, *item;
|
|
|
|
*args = args_zero;
|
|
options_init(opts, argc, argv);
|
|
while ((opt = options_opt(opts))) {
|
|
if (strcmp(opt, "b") == 0) {
|
|
args->mrbfile = TRUE;
|
|
}
|
|
else if (strcmp(opt, "c") == 0) {
|
|
args->check_syntax = TRUE;
|
|
}
|
|
else if (strcmp(opt, "d") == 0) {
|
|
args->debug = TRUE;
|
|
}
|
|
else if (strcmp(opt, "e") == 0) {
|
|
if ((item = options_arg(opts))) {
|
|
if (!args->cmdline) {
|
|
args->cmdline = dup_arg_item(mrb, item);
|
|
}
|
|
else {
|
|
size_t cmdlinelen;
|
|
size_t itemlen;
|
|
|
|
cmdlinelen = strlen(args->cmdline);
|
|
itemlen = strlen(item);
|
|
args->cmdline = (char*)mrb_realloc(mrb, args->cmdline, cmdlinelen + itemlen + 2);
|
|
args->cmdline[cmdlinelen] = '\n';
|
|
memcpy(args->cmdline + cmdlinelen + 1, item, itemlen + 1);
|
|
}
|
|
}
|
|
else {
|
|
fprintf(stderr, "%s: No code specified for -e\n", opts->program);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
else if (strcmp(opt, "h") == 0) {
|
|
usage(opts->program);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
else if (strcmp(opt, "r") == 0) {
|
|
if ((item = options_arg(opts))) {
|
|
if (args->libc == 0) {
|
|
args->libv = (char**)mrb_malloc(mrb, sizeof(char*));
|
|
}
|
|
else {
|
|
args->libv = (char**)mrb_realloc(mrb, args->libv, sizeof(char*) * (args->libc + 1));
|
|
}
|
|
args->libv[args->libc++] = dup_arg_item(mrb, item);
|
|
}
|
|
else {
|
|
fprintf(stderr, "%s: No library specified for -r\n", opts->program);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
else if (strcmp(opt, "v") == 0) {
|
|
if (!args->verbose) {
|
|
mrb_show_version(mrb);
|
|
args->version = TRUE;
|
|
}
|
|
args->verbose = TRUE;
|
|
}
|
|
else if (strcmp(opt, "version") == 0) {
|
|
mrb_show_version(mrb);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
else if (strcmp(opt, "verbose") == 0) {
|
|
args->verbose = TRUE;
|
|
}
|
|
else if (strcmp(opt, "copyright") == 0) {
|
|
mrb_show_copyright(mrb);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
else {
|
|
fprintf(stderr, "%s: invalid option %s%s (-h will show valid options)\n",
|
|
opts->program, opt[1] ? "--" : "-", opt);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
|
|
argc = opts->argc; argv = opts->argv;
|
|
if (args->cmdline == NULL) {
|
|
if (*argv == NULL) {
|
|
if (args->version) exit(EXIT_SUCCESS);
|
|
args->rfp = stdin;
|
|
}
|
|
else {
|
|
args->rfp = strcmp(argv[0], "-") == 0 ?
|
|
stdin : fopen(argv[0], "rb");
|
|
if (args->rfp == NULL) {
|
|
fprintf(stderr, "%s: Cannot open program file: %s\n", opts->program, argv[0]);
|
|
return EXIT_FAILURE;
|
|
}
|
|
args->fname = TRUE;
|
|
args->cmdline = argv[0];
|
|
argc--; argv++;
|
|
}
|
|
}
|
|
#if defined(_WIN32)
|
|
if (args->rfp == stdin) {
|
|
_setmode(_fileno(stdin), O_BINARY);
|
|
}
|
|
#endif
|
|
args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1));
|
|
memcpy(args->argv, argv, (argc+1) * sizeof(char*));
|
|
args->argc = argc;
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
cleanup(mrb_state *mrb, struct _args *args)
|
|
{
|
|
if (args->rfp && args->rfp != stdin)
|
|
fclose(args->rfp);
|
|
if (!args->fname)
|
|
mrb_free(mrb, args->cmdline);
|
|
mrb_free(mrb, args->argv);
|
|
if (args->libc) {
|
|
while (args->libc--) {
|
|
mrb_free(mrb, args->libv[args->libc]);
|
|
}
|
|
mrb_free(mrb, args->libv);
|
|
}
|
|
mrb_close(mrb);
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
mrb_state *mrb = mrb_open();
|
|
int n = -1;
|
|
struct _args args;
|
|
mrb_value ARGV;
|
|
mrb_value v;
|
|
|
|
if (!MRB_OPEN_SUCCESS(mrb)) {
|
|
if (mrb) {
|
|
/* Initialization failed - print exception details */
|
|
mrb_print_error(mrb);
|
|
mrb_close(mrb);
|
|
}
|
|
else {
|
|
/* Allocation failed */
|
|
fprintf(stderr, "%s: Failed to allocate mrb_state, exiting mruby\n", *argv);
|
|
}
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
n = parse_args(mrb, argc, argv, &args);
|
|
if (n == EXIT_FAILURE || (args.cmdline == NULL && args.rfp == NULL)) {
|
|
cleanup(mrb, &args);
|
|
return n;
|
|
}
|
|
else {
|
|
int ai = mrb_gc_arena_save(mrb);
|
|
ARGV = mrb_ary_new_capa(mrb, args.argc);
|
|
for (int i = 0; i < args.argc; i++) {
|
|
char* utf8 = mrb_utf8_from_locale(args.argv[i], -1);
|
|
if (utf8) {
|
|
mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8));
|
|
mrb_utf8_free(utf8);
|
|
}
|
|
}
|
|
mrb_define_global_const(mrb, "ARGV", ARGV);
|
|
mrb_gv_set(mrb, mrb_intern_lit(mrb, "$DEBUG"), mrb_bool_value(args.debug));
|
|
|
|
mrb_ccontext *c = mrb_ccontext_new(mrb);
|
|
if (args.verbose)
|
|
c->dump_result = TRUE;
|
|
if (args.check_syntax)
|
|
c->no_exec = TRUE;
|
|
|
|
/* Set $0 */
|
|
const char *cmdline;
|
|
if (args.rfp) {
|
|
cmdline = args.cmdline ? args.cmdline : "-";
|
|
}
|
|
else {
|
|
cmdline = "-e";
|
|
}
|
|
mrb_gv_set(mrb, mrb_intern_lit(mrb, "$0"), mrb_str_new_cstr(mrb, cmdline));
|
|
|
|
/* Load libraries */
|
|
for (int i = 0; i < args.libc; i++) {
|
|
FILE *lfp = fopen(args.libv[i], "rb");
|
|
if (lfp == NULL) {
|
|
fprintf(stderr, "%s: Cannot open library file: %s\n", *argv, args.libv[i]);
|
|
mrb_ccontext_free(mrb, c);
|
|
cleanup(mrb, &args);
|
|
return EXIT_FAILURE;
|
|
}
|
|
mrb_ccontext_filename(mrb, c, args.libv[i]);
|
|
if (mrb_extension_p(args.libv[i])) {
|
|
mrb_load_irep_file_cxt(mrb, lfp, c);
|
|
}
|
|
else {
|
|
mrb_load_detect_file_cxt(mrb, lfp, c);
|
|
}
|
|
fclose(lfp);
|
|
mrb_vm_ci_env_clear(mrb, mrb->c->cibase);
|
|
mrb_ccontext_cleanup_local_variables(c);
|
|
}
|
|
|
|
/* set program filename */
|
|
mrb_ccontext_filename(mrb, c, cmdline);
|
|
|
|
/* Load program */
|
|
if (args.mrbfile || mrb_extension_p(cmdline)) {
|
|
v = mrb_load_irep_file_cxt(mrb, args.rfp, c);
|
|
}
|
|
else if (args.rfp) {
|
|
v = mrb_load_detect_file_cxt(mrb, args.rfp, c);
|
|
}
|
|
else {
|
|
char* utf8 = mrb_utf8_from_locale(args.cmdline, -1);
|
|
if (!utf8) abort();
|
|
v = mrb_load_string_cxt(mrb, utf8, c);
|
|
mrb_utf8_free(utf8);
|
|
}
|
|
|
|
mrb_gc_arena_restore(mrb, ai);
|
|
mrb_ccontext_free(mrb, c);
|
|
if (mrb->exc) {
|
|
MRB_EXC_CHECK_EXIT(mrb, mrb->exc);
|
|
if (!mrb_undef_p(v)) {
|
|
mrb_print_error(mrb);
|
|
}
|
|
n = EXIT_FAILURE;
|
|
}
|
|
else if (args.check_syntax) {
|
|
puts("Syntax OK");
|
|
}
|
|
}
|
|
cleanup(mrb, &args);
|
|
|
|
return n;
|
|
}
|