diff options
author | yugui <yugui@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2008-08-25 15:02:05 +0000 |
---|---|---|
committer | yugui <yugui@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2008-08-25 15:02:05 +0000 |
commit | 0dc342de848a642ecce8db697b8fecd83a63e117 (patch) | |
tree | 2b7ed4724aff1f86073e4740134bda9c4aac1a39 /trunk/ruby.c | |
parent | ef70cf7138ab8034b5b806f466e4b484b24f0f88 (diff) |
added tag v1_9_0_4
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/tags/v1_9_0_4@18845 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'trunk/ruby.c')
-rw-r--r-- | trunk/ruby.c | 1584 |
1 files changed, 1584 insertions, 0 deletions
diff --git a/trunk/ruby.c b/trunk/ruby.c new file mode 100644 index 0000000000..5a51bbb52a --- /dev/null +++ b/trunk/ruby.c @@ -0,0 +1,1584 @@ +/********************************************************************** + + ruby.c - + + $Author$ + created at: Tue Aug 10 12:47:31 JST 1993 + + Copyright (C) 1993-2007 Yukihiro Matsumoto + Copyright (C) 2000 Network Applied Communication Laboratory, Inc. + Copyright (C) 2000 Information-technology Promotion Agency, Japan + +**********************************************************************/ + +#ifdef __CYGWIN__ +#include <windows.h> +#include <sys/cygwin.h> +#endif +#ifdef _WIN32_WCE +#include <winsock.h> +#include "ruby/wince.h" +#endif +#include "ruby/ruby.h" +#include "ruby/node.h" +#include "ruby/encoding.h" +#include "eval_intern.h" +#include "dln.h" +#include <stdio.h> +#include <sys/types.h> +#include <ctype.h> + +#ifdef __hpux +#include <sys/pstat.h> +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#if defined(HAVE_FCNTL_H) +#include <fcntl.h> +#elif defined(HAVE_SYS_FCNTL_H) +#include <sys/fcntl.h> +#endif +#ifdef HAVE_SYS_PARAM_H +# include <sys/param.h> +#endif +#ifndef MAXPATHLEN +# define MAXPATHLEN 1024 +#endif + +#if defined(__MACOS__) && defined(__MWERKS__) +#include <console.h> +#endif + +#include "ruby/util.h" + +#ifndef HAVE_STDLIB_H +char *getenv(); +#endif + +VALUE rb_parser_get_yydebug(VALUE); +VALUE rb_parser_set_yydebug(VALUE, VALUE); + +const char *ruby_get_inplace_mode(void); +void ruby_set_inplace_mode(const char *); + +#define DISABLE_BIT(bit) (1U << disable_##bit) +enum disable_flag_bits { + disable_gems, + disable_rubyopt +}; + +#define DUMP_BIT(bit) (1U << dump_##bit) +enum dump_flag_bits { + dump_insns +}; + +struct cmdline_options { + int sflag, xflag; + int do_loop, do_print; + int do_check, do_line; + int do_split, do_search; + int usage; + int version; + int copyright; + unsigned int disable; + int verbose; + int yydebug; + unsigned int setids; + unsigned int dump; + const char *script; + VALUE script_name; + VALUE e_script; + struct { + struct { + VALUE name; + int index; + } enc; + } src, ext; + VALUE req_list; +}; + +static void init_ids(struct cmdline_options *); + +#define src_encoding_index GET_VM()->src_encoding_index + +static struct cmdline_options * +cmdline_options_init(struct cmdline_options *opt) +{ + MEMZERO(opt, *opt, 1); + init_ids(opt); + opt->src.enc.index = src_encoding_index; + return opt; +} + +struct cmdline_arguments { + int argc; + char **argv; + struct cmdline_options *opt; +}; + +static NODE *load_file(VALUE, const char *, int, struct cmdline_options *); +static void forbid_setid(const char *, struct cmdline_options *); +#define forbid_setid(s) forbid_setid(s, opt) + +static struct { + int argc; + char **argv; +#if !defined(PSTAT_SETCMD) && !defined(HAVE_SETPROCTITLE) + int len; +#endif +} origarg; + +static void +usage(const char *name) +{ + /* This message really ought to be max 23 lines. + * Removed -h because the user already knows that option. Others? */ + + static const char *const usage_msg[] = { + "-0[octal] specify record separator (\\0, if no argument)", + "-a autosplit mode with -n or -p (splits $_ into $F)", + "-c check syntax only", + "-Cdirectory cd to directory, before executing your script", + "-d set debugging flags (set $DEBUG to true)", + "-e 'command' one line of script. Several -e's allowed. Omit [programfile]", + "-Eencoding specifies the character encoding for the program codes", + "-Fpattern split() pattern for autosplit (-a)", + "-i[extension] edit ARGV files in place (make backup if extension supplied)", + "-Idirectory specify $LOAD_PATH directory (may be used more than once)", + "-l enable line ending processing", + "-n assume 'while gets(); ... end' loop around your script", + "-p assume loop like -n but print line also like sed", + "-rlibrary require the library, before executing your script", + "-s enable some switch parsing for switches after script name", + "-S look for the script using PATH environment variable", + "-T[level] turn on tainting checks", + "-v print version number, then turn on verbose mode", + "-w turn warnings on for your script", + "-W[level] set warning level; 0=silence, 1=medium, 2=verbose (default)", + "-x[directory] strip off text before #!ruby line and perhaps cd to directory", + "--copyright print the copyright", + "--version print the version", + NULL + }; + const char *const *p = usage_msg; + + printf("Usage: %s [switches] [--] [programfile] [arguments]\n", name); + while (*p) + printf(" %s\n", *p++); +} + +VALUE rb_get_load_path(void); + +#ifndef CharNext /* defined as CharNext[AW] on Windows. */ +#define CharNext(p) ((p) + mblen(p, RUBY_MBCHAR_MAXSIZE)) +#endif + +#if defined DOSISH || defined __CYGWIN__ +static inline void +translate_char(char *p, int from, int to) +{ + while (*p) { + if ((unsigned char)*p == from) + *p = to; + p = CharNext(p); + } +} +#endif + +#if defined _WIN32 || defined __CYGWIN__ || defined __DJGPP__ +static VALUE +rubylib_mangled_path(const char *s, unsigned int l) +{ + static char *newp, *oldp; + static int newl, oldl, notfound; + char *ptr; + VALUE ret; + + if (!newp && !notfound) { + newp = getenv("RUBYLIB_PREFIX"); + if (newp) { + oldp = newp = strdup(newp); + while (*newp && !ISSPACE(*newp) && *newp != ';') { + newp = CharNext(newp); /* Skip digits. */ + } + oldl = newp - oldp; + while (*newp && (ISSPACE(*newp) || *newp == ';')) { + newp = CharNext(newp); /* Skip whitespace. */ + } + newl = strlen(newp); + if (newl == 0 || oldl == 0) { + rb_fatal("malformed RUBYLIB_PREFIX"); + } + translate_char(newp, '\\', '/'); + } + else { + notfound = 1; + } + } + if (!newp || l < oldl || STRNCASECMP(oldp, s, oldl) != 0) { + return rb_str_new(s, l); + } + ret = rb_str_new(0, l + newl - oldl); + ptr = RSTRING_PTR(ret); + memcpy(ptr, newp, newl); + memcpy(ptr + newl, s + oldl, l - oldl); + ptr[l + newl - oldl] = 0; + return ret; +} + +static VALUE +rubylib_mangled_path2(const char *s) +{ + return rubylib_mangled_path(s, strlen(s)); +} +#else +#define rubylib_mangled_path rb_str_new +#define rubylib_mangled_path2 rb_str_new2 +#endif + +static void +push_include(const char *path, VALUE (*filter)(VALUE)) +{ + const char sep = PATH_SEP_CHAR; + const char *p, *s; + VALUE load_path = GET_VM()->load_path; + + p = path; + while (*p) { + while (*p == sep) + p++; + if (!*p) break; + for (s = p; *s && *s != sep; s = CharNext(s)); + rb_ary_push(load_path, (*filter)(rubylib_mangled_path(p, s - p))); + p = s; + } +} + +#ifdef __CYGWIN__ +static void +push_include_cygwin(const char *path, VALUE (*filter)(VALUE)) +{ + const char *p, *s; + char rubylib[FILENAME_MAX]; + VALUE buf = 0; + + p = path; + while (*p) { + unsigned int len; + while (*p == ';') + p++; + if (!*p) break; + for (s = p; *s && *s != ';'; s = CharNext(s)); + len = s - p; + if (*s) { + if (!buf) { + buf = rb_str_new(p, len); + p = RSTRING_PTR(buf); + } + else { + rb_str_resize(buf, len); + p = strncpy(RSTRING_PTR(buf), p, len); + } + } + if (cygwin_conv_to_posix_path(p, rubylib) == 0) + p = rubylib; + push_include(p, filter); + if (!*s) break; + p = s + 1; + } +} + +#define push_include push_include_cygwin +#endif + +void +ruby_push_include(const char *path, VALUE (*filter)(VALUE)) +{ + if (path == 0) + return; + push_include(path, filter); +} + +static VALUE +identical_path(VALUE path) +{ + return path; +} + +void +ruby_incpush(const char *path) +{ + ruby_push_include(path, identical_path); +} + +static VALUE +expand_include_path(VALUE path) +{ + char *p = RSTRING_PTR(path); + if (!p) + return path; + if (*p == '.' && p[1] == '/') + return path; + return rb_file_expand_path(path, Qnil); +} + +void +ruby_incpush_expand(const char *path) +{ + ruby_push_include(path, expand_include_path); +} + +#if defined DOSISH || defined __CYGWIN__ +#define LOAD_RELATIVE 1 +#endif + +#if defined _WIN32 || defined __CYGWIN__ +static HMODULE libruby; + +BOOL WINAPI +DllMain(HINSTANCE dll, DWORD reason, LPVOID reserved) +{ + if (reason == DLL_PROCESS_ATTACH) + libruby = dll; + return TRUE; +} +#endif + +void +ruby_init_loadpath(void) +{ + VALUE load_path; +#if defined LOAD_RELATIVE + char libpath[MAXPATHLEN + 1]; + char *p; + int rest; + +#if defined _WIN32 || defined __CYGWIN__ + GetModuleFileName(libruby, libpath, sizeof libpath); +#elif defined(DJGPP) + extern char *__dos_argv0; + strncpy(libpath, __dos_argv0, sizeof(libpath) - 1); +#elif defined(__human68k__) + extern char **_argv; + strncpy(libpath, _argv[0], sizeof(libpath) - 1); +#elif defined(__EMX__) + _execname(libpath, sizeof(libpath) - 1); +#endif + + libpath[sizeof(libpath) - 1] = '\0'; +#if defined DOSISH + translate_char(libpath, '\\', '/'); +#elif defined __CYGWIN__ + { + char rubylib[FILENAME_MAX]; + cygwin_conv_to_posix_path(libpath, rubylib); + strncpy(libpath, rubylib, sizeof(libpath)); + } +#endif + p = strrchr(libpath, '/'); + if (p) { + *p = 0; + if (p - libpath > 3 && !STRCASECMP(p - 4, "/bin")) { + p -= 4; + *p = 0; + } + } + else { + strcpy(libpath, "."); + p = libpath + 1; + } + + rest = sizeof(libpath) - 1 - (p - libpath); + +#define RUBY_RELATIVE(path) (strncpy(p, (path), rest), libpath) +#else +#define RUBY_RELATIVE(path) (path) +#endif +#define incpush(path) rb_ary_push(load_path, rubylib_mangled_path2(path)) + load_path = GET_VM()->load_path; + + if (rb_safe_level() == 0) { + ruby_incpush(getenv("RUBYLIB")); + } + +#ifdef RUBY_SEARCH_PATH + incpush(RUBY_RELATIVE(RUBY_SEARCH_PATH)); +#endif + + incpush(RUBY_RELATIVE(RUBY_SITE_LIB2)); +#ifdef RUBY_SITE_THIN_ARCHLIB + incpush(RUBY_RELATIVE(RUBY_SITE_THIN_ARCHLIB)); +#endif + incpush(RUBY_RELATIVE(RUBY_SITE_ARCHLIB)); + incpush(RUBY_RELATIVE(RUBY_SITE_LIB)); + + incpush(RUBY_RELATIVE(RUBY_VENDOR_LIB2)); +#ifdef RUBY_VENDOR_THIN_ARCHLIB + incpush(RUBY_RELATIVE(RUBY_VENDOR_THIN_ARCHLIB)); +#endif + incpush(RUBY_RELATIVE(RUBY_VENDOR_ARCHLIB)); + incpush(RUBY_RELATIVE(RUBY_VENDOR_LIB)); + + incpush(RUBY_RELATIVE(RUBY_LIB)); +#ifdef RUBY_THIN_ARCHLIB + incpush(RUBY_RELATIVE(RUBY_THIN_ARCHLIB)); +#endif + incpush(RUBY_RELATIVE(RUBY_ARCHLIB)); + + if (rb_safe_level() == 0) { + incpush("."); + } +} + + +static void +add_modules(struct cmdline_options *opt, const char *mod) +{ + VALUE list = opt->req_list; + + if (!list) { + opt->req_list = list = rb_ary_new(); + RBASIC(list)->klass = 0; + } + rb_ary_push(list, rb_obj_freeze(rb_str_new2(mod))); +} + +extern void Init_ext(void); +extern VALUE rb_vm_top_self(void); + +static void +require_libraries(struct cmdline_options *opt) +{ + VALUE list = opt->req_list; + ID require; + + Init_ext(); /* should be called here for some reason :-( */ + CONST_ID(require, "require"); + while (list && RARRAY_LEN(list) > 0) { + VALUE feature = rb_ary_shift(list); + rb_funcall2(rb_vm_top_self(), require, 1, &feature); + } + opt->req_list = 0; +} + +static void +process_sflag(struct cmdline_options *opt) +{ + if (opt->sflag) { + long n; + VALUE *args; + VALUE argv = rb_argv; + + n = RARRAY_LEN(argv); + args = RARRAY_PTR(argv); + while (n > 0) { + VALUE v = *args++; + char *s = StringValuePtr(v); + char *p; + int hyphen = Qfalse; + + if (s[0] != '-') + break; + n--; + if (s[1] == '-' && s[2] == '\0') + break; + + v = Qtrue; + /* check if valid name before replacing - with _ */ + for (p = s + 1; *p; p++) { + if (*p == '=') { + *p++ = '\0'; + v = rb_str_new2(p); + break; + } + if (*p == '-') { + hyphen = Qtrue; + } + else if (*p != '_' && !ISALNUM(*p)) { + VALUE name_error[2]; + name_error[0] = + rb_str_new2("invalid name for global variable - "); + if (!(p = strchr(p, '='))) { + rb_str_cat2(name_error[0], s); + } + else { + rb_str_cat(name_error[0], s, p - s); + } + name_error[1] = args[-1]; + rb_exc_raise(rb_class_new_instance(2, name_error, rb_eNameError)); + } + } + s[0] = '$'; + if (hyphen) { + for (p = s + 1; *p; ++p) { + if (*p == '-') + *p = '_'; + } + } + rb_gv_set(s, v); + } + n = RARRAY_LEN(argv) - n; + while (n--) { + rb_ary_shift(argv); + } + } + opt->sflag = 0; +} + +NODE *rb_parser_append_print(VALUE, NODE *); +NODE *rb_parser_while_loop(VALUE, NODE *, int, int); +static int proc_options(int argc, char **argv, struct cmdline_options *opt); + +static char * +moreswitches(const char *s, struct cmdline_options *opt) +{ + int argc; + char *argv[3]; + const char *p = s; + + argc = 2; + argv[0] = argv[2] = 0; + while (*s && !ISSPACE(*s)) + s++; + argv[1] = ALLOCA_N(char, s - p + 2); + argv[1][0] = '-'; + strncpy(argv[1] + 1, p, s - p); + argv[1][s - p + 1] = '\0'; + proc_options(argc, argv, opt); + while (*s && ISSPACE(*s)) + s++; + return (char *)s; +} + +#define NAME_MATCH_P(name, str, len) \ + ((len) < sizeof(name) && strncmp((str), name, (len)) == 0) + +#define UNSET_WHEN(name, bit, str, len) \ + if (NAME_MATCH_P(name, str, len)) { \ + *(unsigned int *)arg &= ~(bit); \ + return; \ + } + +#define SET_WHEN(name, bit, str, len) \ + if (NAME_MATCH_P(name, str, len)) { \ + *(unsigned int *)arg |= (bit); \ + return; \ + } + +static void +enable_option(const char *str, int len, void *arg) +{ +#define UNSET_WHEN_DISABLE(bit) UNSET_WHEN(#bit, DISABLE_BIT(bit), str, len) + UNSET_WHEN_DISABLE(gems); + UNSET_WHEN_DISABLE(rubyopt); + if (NAME_MATCH_P("all", str, len)) { + *(unsigned int *)arg = 0U; + return; + } + rb_warn("unknown argument for --enable: `%.*s'", len, str); +} + +static void +disable_option(const char *str, int len, void *arg) +{ +#define SET_WHEN_DISABLE(bit) SET_WHEN(#bit, DISABLE_BIT(bit), str, len) + SET_WHEN_DISABLE(gems); + SET_WHEN_DISABLE(rubyopt); + if (NAME_MATCH_P("all", str, len)) { + *(unsigned int *)arg = ~0U; + return; + } + rb_warn("unknown argument for --disable: `%.*s'", len, str); +} + +static void +dump_option(const char *str, int len, void *arg) +{ +#define SET_WHEN_DUMP(bit) SET_WHEN(#bit, DUMP_BIT(bit), str, len) + SET_WHEN_DUMP(insns); + rb_warn("don't know how to dump `%.*s', (insns)", len, str); +} + +static int +proc_options(int argc, char **argv, struct cmdline_options *opt) +{ + int n, argc0 = argc; + const char *s; + + if (argc == 0) + return 0; + + for (argc--, argv++; argc > 0; argc--, argv++) { + if (argv[0][0] != '-' || !argv[0][1]) + break; + + s = argv[0] + 1; + reswitch: + switch (*s) { + case 'a': + opt->do_split = Qtrue; + s++; + goto reswitch; + + case 'p': + opt->do_print = Qtrue; + /* through */ + case 'n': + opt->do_loop = Qtrue; + s++; + goto reswitch; + + case 'd': + ruby_debug = Qtrue; + ruby_verbose = Qtrue; + s++; + goto reswitch; + + case 'y': + opt->yydebug = 1; + s++; + goto reswitch; + + case 'v': + if (opt->verbose) { + s++; + goto reswitch; + } + ruby_show_version(); + opt->verbose = 1; + case 'w': + ruby_verbose = Qtrue; + s++; + goto reswitch; + + case 'W': + { + int numlen; + int v = 2; /* -W as -W2 */ + + if (*++s) { + v = scan_oct(s, 1, &numlen); + if (numlen == 0) + v = 1; + s += numlen; + } + switch (v) { + case 0: + ruby_verbose = Qnil; + break; + case 1: + ruby_verbose = Qfalse; + break; + default: + ruby_verbose = Qtrue; + break; + } + } + goto reswitch; + + case 'c': + opt->do_check = Qtrue; + s++; + goto reswitch; + + case 's': + forbid_setid("-s"); + opt->sflag = 1; + s++; + goto reswitch; + + case 'h': + usage(origarg.argv[0]); + rb_exit(EXIT_SUCCESS); + break; + + case 'l': + opt->do_line = Qtrue; + rb_output_rs = rb_rs; + s++; + goto reswitch; + + case 'S': + forbid_setid("-S"); + opt->do_search = Qtrue; + s++; + goto reswitch; + + case 'e': + forbid_setid("-e"); + if (!*++s) { + s = argv[1]; + argc--, argv++; + } + if (!s) { + rb_raise(rb_eRuntimeError, "no code specified for -e"); + } + if (!opt->e_script) { + opt->e_script = rb_str_new(0, 0); + if (opt->script == 0) + opt->script = "-e"; + } + rb_str_cat2(opt->e_script, s); + rb_str_cat2(opt->e_script, "\n"); + break; + + case 'r': + forbid_setid("-r"); + if (*++s) { + add_modules(opt, s); + } + else if (argv[1]) { + add_modules(opt, argv[1]); + argc--, argv++; + } + break; + + case 'i': + forbid_setid("-i"); + ruby_set_inplace_mode(s + 1); + break; + + case 'x': + opt->xflag = Qtrue; + s++; + if (*s && chdir(s) < 0) { + rb_fatal("Can't chdir to %s", s); + } + break; + + case 'C': + case 'X': + s++; + if (!*s) { + s = argv[1]; + argc--, argv++; + } + if (!s || !*s) { + rb_fatal("Can't chdir"); + } + if (chdir(s) < 0) { + rb_fatal("Can't chdir to %s", s); + } + break; + + case 'F': + if (*++s) { + rb_fs = rb_reg_new(s, strlen(s), 0); + } + break; + + case 'E': + if (!*++s) goto next_encoding; + goto encoding; + + case 'K': + if (*++s) { + const char *enc_name = 0; + switch (*s) { + case 'E': case 'e': + enc_name = "EUC-JP"; + break; + case 'S': case 's': + enc_name = "Windows-31J"; + break; + case 'U': case 'u': + enc_name = "UTF-8"; + break; + case 'N': case 'n': case 'A': case 'a': + enc_name = "ASCII-8BIT"; + break; + } + if (enc_name) { + opt->src.enc.name = rb_str_new2(enc_name); + opt->ext.enc.name = opt->src.enc.name; + } + s++; + } + goto reswitch; + + case 'T': + { + int numlen; + int v = 1; + + if (*++s) { + v = scan_oct(s, 2, &numlen); + if (numlen == 0) + v = 1; + s += numlen; + } + rb_set_safe_level(v); + } + goto reswitch; + + case 'I': + forbid_setid("-I"); + if (*++s) + ruby_incpush_expand(s); + else if (argv[1]) { + ruby_incpush_expand(argv[1]); + argc--, argv++; + } + break; + + case '0': + { + int numlen; + int v; + char c; + + v = scan_oct(s, 4, &numlen); + s += numlen; + if (v > 0377) + rb_rs = Qnil; + else if (v == 0 && numlen >= 2) { + rb_rs = rb_str_new2("\n\n"); + } + else { + c = v & 0xff; + rb_rs = rb_str_new(&c, 1); + } + } + goto reswitch; + + case '-': + if (!s[1] || (s[1] == '\r' && !s[2])) { + argc--, argv++; + goto switch_end; + } + s++; + if (strcmp("copyright", s) == 0) + opt->copyright = 1; + else if (strcmp("debug", s) == 0) { + ruby_debug = Qtrue; + ruby_verbose = Qtrue; + } + else if (strncmp("enable", s, n = 6) == 0 && + (!s[n] || s[n] == '-' || s[n] == '=')) { + if ((s += n + 1)[-1] ? !*s : (!--argc || !(s = *++argv))) { + rb_raise(rb_eRuntimeError, "missing argument for --enable"); + } + ruby_each_words(s, enable_option, &opt->disable); + } + else if (strncmp("disable", s, n = 7) == 0 && + (!s[n] || s[n] == '-' || s[n] == '=')) { + if ((s += n + 1)[-1] ? !*s : (!--argc || !(s = *++argv))) { + rb_raise(rb_eRuntimeError, "missing argument for --disable"); + } + ruby_each_words(s, disable_option, &opt->disable); + } + else if (strncmp("encoding", s, n = 8) == 0 && (!s[n] || s[n] == '=')) { + s += n; + if (!*s++) { + next_encoding: + if (!--argc || !(s = *++argv)) { + rb_raise(rb_eRuntimeError, "missing argument for --encoding"); + } + } + encoding: + opt->ext.enc.name = rb_str_new2(s); + } + else if (strcmp("version", s) == 0) + opt->version = 1; + else if (strcmp("verbose", s) == 0) { + opt->verbose = 1; + ruby_verbose = Qtrue; + } + else if (strcmp("yydebug", s) == 0) + opt->yydebug = 1; + else if (strncmp("dump", s, n = 4) == 0 && (!s[n] || s[n] == '=')) { + if (!(s += n + 1)[-1] && (!--argc || !(s = *++argv)) && *s != '-') break; + ruby_each_words(s, dump_option, &opt->dump); + } + else if (strcmp("help", s) == 0) { + usage(origarg.argv[0]); + rb_exit(EXIT_SUCCESS); + } + else { + rb_raise(rb_eRuntimeError, + "invalid option --%s (-h will show valid options)", s); + } + break; + + case '\r': + if (!s[1]) + break; + + default: + { + if (ISPRINT(*s)) { + rb_raise(rb_eRuntimeError, + "invalid option -%c (-h will show valid options)", + (int)(unsigned char)*s); + } + else { + rb_raise(rb_eRuntimeError, + "invalid option -\\x%02X (-h will show valid options)", + (int)(unsigned char)*s); + } + } + goto switch_end; + + case 0: + break; + } + } + + switch_end: + return argc0 - argc; +} + +void Init_prelude(void); + +static void +ruby_init_gems(int enable) +{ + if (enable) rb_define_module("Gem"); + Init_prelude(); +} + +static int +opt_enc_index(VALUE enc_name) +{ + const char *s = RSTRING_PTR(enc_name); + int i = rb_enc_find_index(s); + + if (i < 0) { + rb_raise(rb_eRuntimeError, "unknown encoding name - %s", s); + } + else if (rb_enc_dummy_p(rb_enc_from_index(i))) { + rb_raise(rb_eRuntimeError, "dummy encoding is not acceptable - %s ", s); + } + return i; +} + +#define rb_progname (GET_VM()->progname) +VALUE rb_argv0; + +static VALUE +process_options(VALUE arg) +{ + struct cmdline_arguments *argp = (struct cmdline_arguments *)arg; + struct cmdline_options *opt = argp->opt; + int argc = argp->argc; + char **argv = argp->argv; + NODE *tree = 0; + VALUE parser; + VALUE iseq; + rb_encoding *enc, *lenc; + const char *s; + char fbuf[MAXPATHLEN]; + int i = proc_options(argc, argv, opt); + int safe; + + argc -= i; + argv += i; + + if (!(opt->disable & DISABLE_BIT(rubyopt)) && + rb_safe_level() == 0 && (s = getenv("RUBYOPT"))) { + VALUE src_enc_name = opt->src.enc.name; + VALUE ext_enc_name = opt->ext.enc.name; + + while (ISSPACE(*s)) + s++; + if (*s == 'T' || (*s == '-' && *(s + 1) == 'T')) { + int numlen; + int v = 1; + + if (*s != 'T') + ++s; + if (*++s) { + v = scan_oct(s, 2, &numlen); + if (numlen == 0) + v = 1; + } + rb_set_safe_level(v); + } + else { + while (s && *s) { + if (*s == '-') { + s++; + if (ISSPACE(*s)) { + do { + s++; + } while (ISSPACE(*s)); + continue; + } + } + if (!*s) + break; + if (!strchr("EIdvwWrK", *s)) + rb_raise(rb_eRuntimeError, + "invalid switch in RUBYOPT: -%c", *s); + s = moreswitches(s, opt); + } + } + if (src_enc_name) + opt->src.enc.name = src_enc_name; + if (ext_enc_name) + opt->ext.enc.name = ext_enc_name; + } + + if (opt->version) { + ruby_show_version(); + return Qtrue; + } + if (opt->copyright) { + ruby_show_copyright(); + } + + if (rb_safe_level() >= 4) { + OBJ_TAINT(rb_argv); + OBJ_TAINT(GET_VM()->load_path); + } + + if (!opt->e_script) { + if (argc == 0) { /* no more args */ + if (opt->verbose) + return Qtrue; + opt->script = "-"; + } + else { + opt->script = argv[0]; + if (opt->script[0] == '\0') { + opt->script = "-"; + } + else if (opt->do_search) { + char *path = getenv("RUBYPATH"); + + opt->script = 0; + if (path) { + opt->script = dln_find_file_r(argv[0], path, fbuf, sizeof(fbuf)); + } + if (!opt->script) { + opt->script = dln_find_file_r(argv[0], getenv(PATH_ENV), fbuf, sizeof(fbuf)); + } + if (!opt->script) + opt->script = argv[0]; + } + argc--; + argv++; + } + } + + ruby_script(opt->script); +#if defined DOSISH || defined __CYGWIN__ + translate_char(RSTRING_PTR(rb_progname), '\\', '/'); +#endif + opt->script_name = rb_str_new4(rb_progname); + opt->script = RSTRING_PTR(opt->script_name); + safe = rb_safe_level(); + rb_set_safe_level_force(0); + ruby_set_argv(argc, argv); + process_sflag(opt); + + ruby_init_loadpath(); + ruby_init_gems(!(opt->disable & DISABLE_BIT(gems))); + lenc = rb_locale_encoding(); + for (i = 0; i < RARRAY_LEN(rb_argv); i++) { + rb_enc_associate(RARRAY_PTR(rb_argv)[i], lenc); + } + parser = rb_parser_new(); + if (opt->yydebug) rb_parser_set_yydebug(parser, Qtrue); + if (opt->ext.enc.name != 0) { + opt->ext.enc.index = opt_enc_index(opt->ext.enc.name); + } + if (opt->src.enc.name != 0) { + opt->src.enc.index = opt_enc_index(opt->src.enc.name); + src_encoding_index = opt->src.enc.index; + } + if (opt->ext.enc.index >= 0) { + enc = rb_enc_from_index(opt->ext.enc.index); + } + else { + enc = lenc; + } + rb_enc_set_default_external(rb_enc_from_encoding(enc)); + + rb_set_safe_level_force(safe); + if (opt->e_script) { + rb_encoding *eenc; + if (opt->src.enc.index >= 0) { + eenc = rb_enc_from_index(opt->src.enc.index); + } + else { + eenc = lenc; + } + rb_enc_associate(opt->e_script, eenc); + require_libraries(opt); + tree = rb_parser_compile_string(parser, opt->script, opt->e_script, 1); + } + else { + if (opt->script[0] == '-' && !opt->script[1]) { + forbid_setid("program input from stdin"); + } + tree = load_file(parser, opt->script, 1, opt); + } + + if (!tree) return Qfalse; + + process_sflag(opt); + opt->xflag = 0; + + if (rb_safe_level() >= 4) { + FL_UNSET(rb_argv, FL_TAINT); + FL_UNSET(GET_VM()->load_path, FL_TAINT); + } + + if (opt->do_check) { + printf("Syntax OK\n"); + return Qtrue; + } + + if (opt->do_print) { + tree = rb_parser_append_print(parser, tree); + } + if (opt->do_loop) { + tree = rb_parser_while_loop(parser, tree, opt->do_line, opt->do_split); + } + + iseq = rb_iseq_new(tree, rb_str_new2("<main>"), + opt->script_name, Qfalse, ISEQ_TYPE_TOP); + + if (opt->dump & DUMP_BIT(insns)) { + rb_io_write(rb_stdout, ruby_iseq_disasm(iseq)); + rb_io_flush(rb_stdout); + return Qtrue; + } + + return iseq; +} + +static NODE * +load_file(VALUE parser, const char *fname, int script, struct cmdline_options *opt) +{ + extern VALUE rb_stdin; + VALUE f; + int line_start = 1; + NODE *tree = 0; + rb_encoding *enc; + + if (!fname) + rb_load_fail(fname); + if (strcmp(fname, "-") == 0) { + f = rb_stdin; + } + else { + int fd, mode = O_RDONLY; +#if defined DOSISH || defined __CYGWIN__ + { + const char *ext = strrchr(fname, '.'); + if (ext && STRCASECMP(ext, ".exe") == 0) + mode |= O_BINARY; + } +#endif + if ((fd = open(fname, mode)) < 0) { + rb_load_fail(fname); + } + + f = rb_io_fdopen(fd, mode, fname); + } + + if (script) { + VALUE c = 1; /* something not nil */ + VALUE line; + char *p; + int no_src_enc = !opt->src.enc.name; + int no_ext_enc = !opt->ext.enc.name; + + enc = rb_usascii_encoding(); + rb_funcall(f, rb_intern("set_encoding"), 1, rb_enc_from_encoding(enc)); + + if (opt->xflag) { + forbid_setid("-x"); + opt->xflag = Qfalse; + while (!NIL_P(line = rb_io_gets(f))) { + line_start++; + if (RSTRING_LEN(line) > 2 + && RSTRING_PTR(line)[0] == '#' + && RSTRING_PTR(line)[1] == '!') { + if ((p = strstr(RSTRING_PTR(line), "ruby")) != 0) { + goto start_read; + } + } + } + rb_raise(rb_eLoadError, "no Ruby script found in input"); + } + + c = rb_io_getbyte(f); + if (c == INT2FIX('#')) { + c = rb_io_getbyte(f); + if (c == INT2FIX('!')) { + line = rb_io_gets(f); + if (NIL_P(line)) + return 0; + + if ((p = strstr(RSTRING_PTR(line), "ruby")) == 0) { + /* not ruby script, kick the program */ + char **argv; + char *path; + char *pend = RSTRING_PTR(line) + RSTRING_LEN(line); + + p = RSTRING_PTR(line); /* skip `#!' */ + if (pend[-1] == '\n') + pend--; /* chomp line */ + if (pend[-1] == '\r') + pend--; + *pend = '\0'; + while (p < pend && ISSPACE(*p)) + p++; + path = p; /* interpreter path */ + while (p < pend && !ISSPACE(*p)) + p++; + *p++ = '\0'; + if (p < pend) { + argv = ALLOCA_N(char *, origarg.argc + 3); + argv[1] = p; + MEMCPY(argv + 2, origarg.argv + 1, char *, origarg.argc); + } + else { + argv = origarg.argv; + } + argv[0] = path; + execv(path, argv); + + rb_fatal("Can't exec %s", path); + } + + start_read: + p += 4; + RSTRING_PTR(line)[RSTRING_LEN(line) - 1] = '\0'; + if (RSTRING_PTR(line)[RSTRING_LEN(line) - 2] == '\r') + RSTRING_PTR(line)[RSTRING_LEN(line) - 2] = '\0'; + if ((p = strstr(p, " -")) != 0) { + p++; /* skip space before `-' */ + while (*p == '-') { + p = moreswitches(p + 1, opt); + } + } + + /* push back shebang for pragma may exist in next line */ + rb_io_ungetbyte(f, rb_str_new2("!\n")); + } + else if (!NIL_P(c)) { + rb_io_ungetbyte(f, c); + } + rb_io_ungetbyte(f, INT2FIX('#')); + if (no_src_enc && opt->src.enc.name) { + opt->src.enc.index = opt_enc_index(opt->src.enc.name); + src_encoding_index = opt->src.enc.index; + } + if (no_ext_enc && opt->ext.enc.name) { + opt->ext.enc.index = opt_enc_index(opt->ext.enc.name); + } + } + else if (!NIL_P(c)) { + rb_io_ungetbyte(f, c); + } + require_libraries(opt); /* Why here? unnatural */ + } + if (opt->src.enc.index >= 0) { + enc = rb_enc_from_index(opt->src.enc.index); + } + else if (f == rb_stdin) { + enc = rb_locale_encoding(); + } + else { + enc = rb_usascii_encoding(); + } + rb_funcall(f, rb_intern("set_encoding"), 1, rb_enc_from_encoding(enc)); + tree = (NODE *)rb_parser_compile_file(parser, fname, f, line_start); + rb_funcall(f, rb_intern("set_encoding"), 1, rb_parser_encoding(parser)); + if (script && rb_parser_end_seen_p(parser)) { + rb_define_global_const("DATA", f); + } + else if (f != rb_stdin) { + rb_io_close(f); + } + return tree; +} + +void * +rb_load_file(const char *fname) +{ + struct cmdline_options opt; + + return load_file(rb_parser_new(), fname, 0, cmdline_options_init(&opt)); +} + +#if !defined(PSTAT_SETCMD) && !defined(HAVE_SETPROCTITLE) +#if !defined(_WIN32) && !(defined(HAVE_SETENV) && defined(HAVE_UNSETENV)) +#define USE_ENVSPACE_FOR_ARG0 +#endif + +#ifdef USE_ENVSPACE_FOR_ARG0 +extern char **environ; +#endif + +static int +get_arglen(int argc, char **argv) +{ + char *s = argv[0]; + int i; + + if (!argc) return 0; + s += strlen(s); + /* See if all the arguments are contiguous in memory */ + for (i = 1; i < argc; i++) { + if (argv[i] == s + 1) { + s++; + s += strlen(s); /* this one is ok too */ + } + else { + break; + } + } +#if defined(USE_ENVSPACE_FOR_ARG0) + if (environ && (s == environ[0])) { + s += strlen(s); + for (i = 1; environ[i]; i++) { + if (environ[i] == s + 1) { + s++; + s += strlen(s); /* this one is ok too */ + } + } + ruby_setenv("", NULL); /* duplicate environ vars */ + } +#endif + return s - argv[0]; +} +#endif + +static void +set_arg0(VALUE val, ID id) +{ + char *s; + long i; + + if (origarg.argv == 0) + rb_raise(rb_eRuntimeError, "$0 not initialized"); + StringValue(val); + s = RSTRING_PTR(val); + i = RSTRING_LEN(val); +#if defined(PSTAT_SETCMD) + if (i > PST_CLEN) { + union pstun un; + char buf[PST_CLEN + 1]; /* PST_CLEN is 64 (HP-UX 11.23) */ + strncpy(buf, s, PST_CLEN); + buf[PST_CLEN] = '\0'; + un.pst_command = buf; + pstat(PSTAT_SETCMD, un, PST_CLEN, 0, 0); + } + else { + union pstun un; + un.pst_command = s; + pstat(PSTAT_SETCMD, un, i, 0, 0); + } +#elif defined(HAVE_SETPROCTITLE) + setproctitle("%.*s", (int)i, s); +#else + + if (i >= origarg.len) { + i = origarg.len; + } + + memcpy(origarg.argv[0], s, i); + + { + int j; + char *t = origarg.argv[0] + i; + *t = '\0'; + + if (i + 1 < origarg.len) memset(t + 1, ' ', origarg.len - i - 1); + for (j = 1; j < origarg.argc; j++) { + origarg.argv[j] = t; + } + } +#endif + rb_progname = rb_obj_freeze(rb_tainted_str_new(s, i)); +} + +void +ruby_script(const char *name) +{ + if (name) { + rb_progname = rb_obj_freeze(rb_tainted_str_new2(name)); + } +} + +static void +init_ids(struct cmdline_options *opt) +{ + rb_uid_t uid = getuid(); + rb_uid_t euid = geteuid(); + rb_gid_t gid = getgid(); + rb_gid_t egid = getegid(); + +#ifdef VMS + uid |= gid << 16; + euid |= egid << 16; +#endif + if (uid != euid) opt->setids |= 1; + if (egid != gid) opt->setids |= 2; + if (uid && opt->setids) { + rb_set_safe_level(1); + } +} + +#undef forbid_setid +static void +forbid_setid(const char *s, struct cmdline_options *opt) +{ + if (opt->setids & 1) + rb_raise(rb_eSecurityError, "no %s allowed while running setuid", s); + if (opt->setids & 2) + rb_raise(rb_eSecurityError, "no %s allowed while running setgid", s); + if (rb_safe_level() > 0) + rb_raise(rb_eSecurityError, "no %s allowed in tainted mode", s); +} + +static void +verbose_setter(VALUE val, ID id, void *data) +{ + VALUE *variable = data; + *variable = RTEST(val) ? Qtrue : val; +} + +static VALUE +opt_W_getter(ID id, void *data) +{ + VALUE *variable = data; + switch (*variable) { + case Qnil: + return INT2FIX(0); + case Qfalse: + return INT2FIX(1); + case Qtrue: + return INT2FIX(2); + } + return Qnil; /* not reached */ +} + +void +ruby_prog_init(void) +{ + rb_define_hooked_variable("$VERBOSE", &ruby_verbose, 0, verbose_setter); + rb_define_hooked_variable("$-v", &ruby_verbose, 0, verbose_setter); + rb_define_hooked_variable("$-w", &ruby_verbose, 0, verbose_setter); + rb_define_hooked_variable("$-W", &ruby_verbose, opt_W_getter, 0); + rb_define_variable("$DEBUG", &ruby_debug); + rb_define_variable("$-d", &ruby_debug); + + rb_define_hooked_variable("$0", &rb_progname, 0, set_arg0); + rb_define_hooked_variable("$PROGRAM_NAME", &rb_progname, 0, set_arg0); + + rb_define_global_const("ARGV", rb_argv); + rb_global_variable(&rb_argv0); + +#ifdef MSDOS + /* + * There is no way we can refer to them from ruby, so close them to save + * space. + */ + (void)fclose(stdaux); + (void)fclose(stdprn); +#endif +} + +void +ruby_set_argv(int argc, char **argv) +{ + int i; + VALUE av = rb_argv; + +#if defined(USE_DLN_A_OUT) + if (origarg.argv) + dln_argv0 = origarg.argv[0]; + else + dln_argv0 = argv[0]; +#endif + rb_ary_clear(av); + for (i = 0; i < argc; i++) { + VALUE arg = rb_tainted_str_new2(argv[i]); + + OBJ_FREEZE(arg); + rb_ary_push(av, arg); + } +} + +static VALUE +false_value(void) +{ + return Qfalse; +} + +static VALUE +true_value(void) +{ + return Qtrue; +} + +#define rb_define_readonly_boolean(name, val) \ + rb_define_virtual_variable((name), (val) ? true_value : false_value, 0) + +void * +ruby_process_options(int argc, char **argv) +{ + struct cmdline_arguments args; + struct cmdline_options opt; + NODE *tree; + + ruby_script(argv[0]); /* for the time being */ + rb_argv0 = rb_str_new4(rb_progname); + args.argc = argc; + args.argv = argv; + args.opt = cmdline_options_init(&opt); + opt.ext.enc.index = -1; + tree = (NODE *)rb_vm_call_cfunc(rb_vm_top_self(), + process_options, (VALUE)&args, + 0, rb_progname); + + rb_define_readonly_boolean("$-p", opt.do_print); + rb_define_readonly_boolean("$-l", opt.do_line); + rb_define_readonly_boolean("$-a", opt.do_split); + + return tree; +} + +void +ruby_sysinit(int *argc, char ***argv) +{ +#if defined(__APPLE__) && (defined(__MACH__) || defined(__DARWIN__)) + int i, n = *argc, len = 0; + char **v1 = *argv, **v2, *p; + + for (i = 0; i < n; ++i) { + len += strlen(v1[i]) + 1; + } + v2 = malloc((n + 1)* sizeof(char*) + len); + p = (char *)&v2[n + 1]; + for (i = 0; i < n; ++i) { + int l = strlen(v1[i]); + memcpy(p, v1[i], l + 1); + v2[i] = p; + p += l + 1; + } + v2[n] = 0; + *argv = v2; +#elif defined(__MACOS__) && defined(__MWERKS__) + *argc = ccommand(argv); +#elif defined(_WIN32) + void rb_w32_sysinit(int *argc, char ***argv); + rb_w32_sysinit(argc, argv); +#endif + origarg.argc = *argc; + origarg.argv = *argv; +#if !defined(PSTAT_SETCMD) && !defined(HAVE_SETPROCTITLE) + origarg.len = get_arglen(origarg.argc, origarg.argv); +#endif +#if defined(USE_DLN_A_OUT) + dln_argv0 = origarg.argv[0]; +#endif +} |