/* * $Id$ * Copyright (C) 2000 ZetaBITS, Inc. * Copyright (C) 2000 Information-technology Promotion Agency, Japan * Copyright (C) 2000 Shugo Maeda * * This file is part of eruby. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA */ #include #include #ifdef HAVE_UNISTD_H #include #endif #include "ruby.h" #include "re.h" #include "regex.h" #include "version.h" #include "eruby.h" #include "eruby_logo.h" EXTERN VALUE ruby_errinfo; EXTERN VALUE rb_stdout; #if RUBY_VERSION_CODE < 180 EXTERN VALUE rb_defout; #endif EXTERN VALUE rb_load_path; EXTERN VALUE ruby_top_self; /* copied from eval.c */ #define TAG_RETURN 0x1 #define TAG_BREAK 0x2 #define TAG_NEXT 0x3 #define TAG_RETRY 0x4 #define TAG_REDO 0x5 #define TAG_RAISE 0x6 #define TAG_THROW 0x7 #define TAG_FATAL 0x8 #define TAG_MASK 0xf static void *eruby_xmalloc(size_t size) { void *ptr; if ((ptr = malloc(size)) == NULL) { fprintf(stderr, "failed to allocate memory\n"); exit(1); } return ptr; } static void write_escaping_html(FILE *out, char *str, int len) { int i; for (i = 0; i < len; i++) { switch (str[i]) { case '&': fputs("&", out); break; case '<': fputs("<", out); break; case '>': fputs(">", out); break; case '"': fputs(""", out); break; default: putc(str[i], out); break; } } } static void error_pos(FILE *out, int cgi) { char buff[BUFSIZ]; #if RUBY_VERSION_CODE >= 190 ID last_func = rb_frame_this_func(); #else ID last_func = rb_frame_last_func(); #endif if (ruby_sourcefile) { if (last_func) { snprintf(buff, BUFSIZ, "%s:%d:in `%s'", ruby_sourcefile, ruby_sourceline, rb_id2name(last_func)); } else { snprintf(buff, BUFSIZ, "%s:%d", ruby_sourcefile, ruby_sourceline); } if (cgi) write_escaping_html(out, buff, strlen(buff)); else fputs(buff, out); } } static void exception_print(FILE *out, int cgi) { VALUE errat; VALUE eclass; VALUE einfo; if (NIL_P(ruby_errinfo)) return; errat = rb_funcall(ruby_errinfo, rb_intern("backtrace"), 0); if (!NIL_P(errat)) { VALUE mesg = RARRAY_PTR(errat)[0]; if (NIL_P(mesg)) { error_pos(out, cgi); } else { if (cgi) write_escaping_html(out, RSTRING_PTR(mesg), RSTRING_LEN(mesg)); else fwrite(RSTRING_PTR(mesg), 1, RSTRING_LEN(mesg), out); } } eclass = CLASS_OF(ruby_errinfo); einfo = rb_obj_as_string(ruby_errinfo); if (eclass == rb_eRuntimeError && RSTRING_LEN(einfo) == 0) { fprintf(out, ": unhandled exception\n"); } else { VALUE epath; epath = rb_class_path(eclass); if (RSTRING_LEN(einfo) == 0) { fprintf(out, ": "); if (cgi) write_escaping_html(out, RSTRING_PTR(epath), RSTRING_LEN(epath)); else fwrite(RSTRING_PTR(epath), 1, RSTRING_LEN(epath), out); if (cgi) fprintf(out, "
\n"); else fprintf(out, "\n"); } else { char *tail = 0; int len = RSTRING_LEN(einfo); if (RSTRING_PTR(epath)[0] == '#') epath = 0; if ((tail = strchr(RSTRING_PTR(einfo), '\n')) != NULL) { len = tail - RSTRING_PTR(einfo); tail++; /* skip newline */ } fprintf(out, ": "); if (cgi) write_escaping_html(out, RSTRING_PTR(einfo), len); else fwrite(RSTRING_PTR(einfo), 1, len, out); if (epath) { fprintf(out, " ("); if (cgi) write_escaping_html(out, RSTRING_PTR(epath), RSTRING_LEN(epath)); else fwrite(RSTRING_PTR(epath), 1, RSTRING_LEN(epath), out); if (cgi) fprintf(out, ")
\n"); else fprintf(out, ")\n"); } if (tail) { if (cgi) write_escaping_html(out, tail, RSTRING_LEN(einfo) - len - 1); else fwrite(tail, 1, RSTRING_LEN(einfo) - len - 1, out); if (cgi) fprintf(out, "
\n"); else fprintf(out, "\n"); } } } if (!NIL_P(errat)) { int i; struct RArray *ep = RARRAY(errat); #define TRACE_MAX (TRACE_HEAD+TRACE_TAIL+5) #define TRACE_HEAD 8 #define TRACE_TAIL 5 rb_ary_pop(errat); ep = RARRAY(errat); for (i=1; ifrom "); write_escaping_html(out, RSTRING_PTR(RARRAY_PTR(ep)[i]), RSTRING_LEN(RARRAY_PTR(ep)[i])); } else { fprintf(out, " from "); fwrite(RSTRING_PTR(RARRAY_PTR(ep)[i]), 1, RSTRING_LEN(RARRAY_PTR(ep)[i]), out); } if (cgi) fprintf(out, "
\n"); else fprintf(out, "\n"); } if (i == TRACE_HEAD && RARRAY_LEN(ep) > TRACE_MAX) { char buff[BUFSIZ]; if (cgi) snprintf(buff, BUFSIZ, "
... %ld levels...\n", RARRAY_LEN(ep) - TRACE_HEAD - TRACE_TAIL); else snprintf(buff, BUFSIZ, " ... %ld levels...
\n", RARRAY_LEN(ep) - TRACE_HEAD - TRACE_TAIL); if (cgi) write_escaping_html(out, buff, strlen(buff)); else fputs(buff, out); i = RARRAY_LEN(ep) - TRACE_TAIL; } } } } static void print_generated_code(FILE *out, VALUE code, int cgi) { if (cgi) { fprintf(out, "\n"); fprintf(out, "GENERATED CODE\n"); fprintf(out, "\n"); fprintf(out, "\n"); fprintf(out, "
\n");
    }
    else {
	fprintf(out, "--- generated code ---\n");
    }

    if (cgi) {
	write_escaping_html(out, RSTRING_PTR(code), RSTRING_LEN(code));
    }
    else {
	fwrite(RSTRING_PTR(code), 1, RSTRING_LEN(code), out);
    }
    if (cgi) {
	fprintf(out, "
\n"); fprintf(out, "\n"); } else { fprintf(out, "----------------------\n"); } } char rfc822_days[][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; char rfc822_months[][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static char *rfc1123_time(time_t t) { static char s[32]; struct tm *tm; tm = gmtime(&t); sprintf(s, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", rfc822_days[tm->tm_wday], tm->tm_mday, rfc822_months[tm->tm_mon], tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec); return s; } static void print_http_headers() { char *tmp; if ((tmp = getenv("SERVER_PROTOCOL")) == NULL) tmp = "HTTP/1.0"; printf("%s 200 OK\r\n", tmp); if ((tmp = getenv("SERVER_SOFTWARE")) == NULL) tmp = "unknown-server/0.0"; printf("Server: %s\r\n", tmp); printf("Date: %s\r\n", rfc1123_time(time(NULL))); printf("Connection: close\r\n"); } static void error_print(FILE *out, int state, int cgi, int mode, VALUE code) { char buff[BUFSIZ]; #if RUBY_VERSION_CODE < 180 rb_defout = rb_stdout; #endif if (cgi) { char *imgdir; if ((imgdir = getenv("SCRIPT_NAME")) == NULL) imgdir = "UNKNOWN_IMG_DIR"; if (mode == MODE_NPHCGI) print_http_headers(); fprintf(out, "Content-Type: text/html\r\n"); fprintf(out, "Content-Style-Type: text/css\r\n"); fprintf(out, "\r\n"); fprintf(out, "\n"); fprintf(out, "\n"); fprintf(out, "\n"); fprintf(out, "eRuby\n"); fprintf(out, "\n"); fprintf(out, "\n"); fprintf(out, "\n"); fprintf(out, "\n"); fprintf(out, "\n"); fprintf(out, "\n"); fprintf(out, "\n"); } if (!NIL_P(code)) print_generated_code(out, code, cgi); if (cgi) { fprintf(out, "
\n"); fprintf(out, "\"eRuby\"\n", imgdir); fprintf(out, "version: %s\n", ERUBY_VERSION); fprintf(out, "
\n"); fprintf(out, "ERROR\n"); fprintf(out, "
\n"); } switch (state) { case TAG_RETURN: error_pos(out, cgi); fprintf(out, ": unexpected return\n"); break; case TAG_NEXT: error_pos(out, cgi); fprintf(out, ": unexpected next\n"); break; case TAG_BREAK: error_pos(out, cgi); fprintf(out, ": unexpected break\n"); break; case TAG_REDO: error_pos(out, cgi); fprintf(out, ": unexpected redo\n"); break; case TAG_RETRY: error_pos(out, cgi); fprintf(out, ": retry outside of rescue clause\n"); break; case TAG_RAISE: case TAG_FATAL: exception_print(out, cgi); break; default: error_pos(out, cgi); snprintf(buff, BUFSIZ, ": unknown longjmp status %d", state); fputs(buff, out); break; } if (cgi) { fprintf(out, "
\n"); fprintf(out, "\n"); fprintf(out, "\n"); } } static VALUE defout_write(VALUE self, VALUE str) { str = rb_obj_as_string(str); rb_str_cat(self, RSTRING_PTR(str), RSTRING_LEN(str)); return Qnil; } static VALUE defout_cancel(VALUE self) { if (RSTRING_LEN(self) == 0) return Qnil; rb_str_resize(self, 0); return Qnil; } static int guess_mode() { if (getenv("GATEWAY_INTERFACE") == NULL) { return MODE_FILTER; } else { char *name = getenv("SCRIPT_FILENAME"); int result; char *buff; if (name == NULL) return MODE_CGI; buff = eruby_xmalloc(strlen(name) + 1); strcpy(buff, name); if ((name = strrchr(buff, '/')) != NULL) *name++ = '\0'; else name = buff; if (strncasecmp(name, "nph-", 4) == 0) result = MODE_NPHCGI; else result = MODE_CGI; free(buff); return result; } } static void give_img_logo(int mode) { if (mode == MODE_NPHCGI) print_http_headers(); printf("Content-Type: image/png\r\n\r\n"); fwrite(eruby_logo_data, eruby_logo_size, 1, stdout); } static void init() { ruby_init(); #if RUBY_VERSION_CODE >= 160 ruby_init_loadpath(); #else #if RUBY_VERSION_CODE >= 145 rb_ary_push(rb_load_path, rb_str_new2(".")); #endif #endif if (eruby_mode == MODE_CGI || eruby_mode == MODE_NPHCGI) rb_set_safe_level(1); eruby_init(); } static void eruby_exit(status) { ruby_finalize(); exit(status); } static void proc_args(int argc, char **argv) { int option_index; ruby_script(argv[0]); switch (eruby_parse_options(argc, argv, &option_index)) { case 1: eruby_exit(0); case 2: eruby_exit(2); } if (eruby_mode == MODE_UNKNOWN) eruby_mode = guess_mode(); if (eruby_mode == MODE_CGI || eruby_mode == MODE_NPHCGI) { char *path; char *tmp_qs; char *query_string; int qs_has_equal; char *path_translated; if ((path = getenv("PATH_INFO")) != NULL && strcmp(path, "/logo.png") == 0) { give_img_logo(eruby_mode); eruby_exit(0); } if ((tmp_qs = getenv("QUERY_STRING")) == NULL) { query_string = ""; } else { query_string = eruby_xmalloc(strlen(tmp_qs) + 1); strcpy(query_string, tmp_qs); } qs_has_equal = (strchr(query_string, '=') != NULL); if ((path_translated = getenv("PATH_TRANSLATED")) == NULL) path_translated = ""; if (path_translated[0] && ((option_index == argc && (!query_string[0] || qs_has_equal)) || (option_index == argc - 1 && !qs_has_equal && strcmp(argv[option_index], query_string) == 0))) { eruby_filename = path_translated; } else if ((option_index == argc - 1 && (!query_string[0] || qs_has_equal)) || (option_index == argc - 2 && !qs_has_equal && strcmp(argv[option_index + 1], query_string) == 0)) { eruby_filename = argv[option_index]; } else { fprintf(stderr, "%s: missing required file to process\n", argv[0]); eruby_exit(1); } if (tmp_qs) free(query_string); } else { if (option_index == argc) { eruby_filename = "-"; } else { eruby_filename = argv[option_index++]; ruby_set_argv(argc - option_index, argv + option_index); } } } static void error(int state, VALUE code) { if (state && !rb_obj_is_kind_of(ruby_errinfo, rb_eSystemExit)) { if (RTEST(ruby_debug) && (eruby_mode == MODE_CGI || eruby_mode == MODE_NPHCGI)) { error_print(stdout, state, 1, eruby_mode, code); eruby_exit(0); } else { error_print(stderr, state, 0, eruby_mode, code); eruby_exit(1); } } } static void print_headers(int length) { if (!eruby_noheader && (eruby_mode == MODE_CGI || eruby_mode == MODE_NPHCGI)) { if (eruby_mode == MODE_NPHCGI) print_http_headers(); printf("Content-Type: text/html; charset=%s\r\n", ERUBY_CHARSET); if (length >= 0) { printf("Content-Length: %d\r\n", length); } printf("\r\n"); } } static void replace_stdout() { #if RUBY_VERSION_CODE >= 180 rb_io_binmode(rb_stdout); /* for mswin32 */ rb_stdout = rb_str_new("", 0); rb_define_singleton_method(rb_stdout, "write", defout_write, 1); rb_define_singleton_method(rb_stdout, "cancel", defout_cancel, 0); #else rb_defout = rb_str_new("", 0); rb_io_binmode(rb_stdout); /* for mswin32 */ rb_define_singleton_method(rb_defout, "write", defout_write, 1); rb_define_singleton_method(rb_defout, "cancel", defout_cancel, 0); #endif } static void flush_buffer() { char *out; int nout; #if RUBY_VERSION_CODE >= 180 out = RSTRING_PTR(rb_stdout); nout = RSTRING_LEN(rb_stdout); #else out = RSTRING(rb_defout)->ptr; nout = RSTRING(rb_defout)->len; #endif print_headers(nout); fwrite(out, nout, 1, stdout); fflush(stdout); } static VALUE file_open(VALUE filename) { return rb_file_open((char *) filename, "r"); } typedef struct compile_arg { VALUE compiler; VALUE input; } compile_arg_t; static VALUE eruby_compile_file(VALUE arg) { return eruby_compiler_compile_file(((compile_arg_t *) arg)->compiler, ((compile_arg_t *) arg)->input); } static VALUE compile(char *filename) { VALUE compiler; VALUE code; VALUE f; VALUE vfilename = rb_str_new2(filename); compile_arg_t carg; int status; if (strcmp(filename, "-") == 0) { f = rb_stdin; } else { f = rb_protect(file_open, (VALUE) filename, &status); if (status) { error(status, Qnil); } } compiler = eruby_compiler_new(); eruby_compiler_set_sourcefile(compiler, vfilename); carg.compiler = compiler; carg.input = f; code = rb_protect(eruby_compile_file, (VALUE) &carg, &status); if (status) { error(status, Qnil); } if (f != rb_stdin) rb_io_close(f); return code; } typedef struct eval_arg { VALUE src; VALUE filename; } eval_arg_t; static VALUE eval_string(VALUE arg) { return rb_funcall(ruby_top_self, rb_intern("eval"), 3, ((eval_arg_t *) arg)->src, Qnil, ((eval_arg_t *) arg)->filename); } static VALUE eval(VALUE code, char *filename) { int status; eval_arg_t earg; earg.src = code; earg.filename = rb_str_new2(filename); rb_protect(eval_string, (VALUE) &earg, &status); if (status) { error(status, code); } return code; } static void run() { VALUE stack_start; VALUE code; void Init_stack _((VALUE*)); Init_stack(&stack_start); code = compile(eruby_filename); if (eruby_sync) { print_headers(-1); } else { replace_stdout(); } code = eval(code, eruby_filename); if (eruby_mode == MODE_FILTER && (RTEST(ruby_debug) || RTEST(ruby_verbose))) { print_generated_code(stderr, code, 0); } rb_exec_end_proc(); if (!eruby_sync) { flush_buffer(); } ruby_finalize(); } int main(int argc, char **argv) { #ifdef _WIN32 NtInitialize(&argc, &argv); #endif init(); proc_args(argc, argv); run(); return 0; } /* * Local variables: * mode: C * tab-width: 8 * End: */