/* * $Id: eruby_main.c,v 1.37 2003/12/23 15:10:54 shugo Exp $ * 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; /* 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]; ID last_func = rb_frame_last_func(); 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(errat)->ptr[0]; if (NIL_P(mesg)) { error_pos(out, cgi); } else { if (cgi) write_escaping_html(out, RSTRING(mesg)->ptr, RSTRING(mesg)->len); else fwrite(RSTRING(mesg)->ptr, 1, RSTRING(mesg)->len, out); } } eclass = CLASS_OF(ruby_errinfo); einfo = rb_obj_as_string(ruby_errinfo); if (eclass == rb_eRuntimeError && RSTRING(einfo)->len == 0) { fprintf(out, ": unhandled exception\n"); } else { VALUE epath; epath = rb_class_path(eclass); if (RSTRING(einfo)->len == 0) { fprintf(out, ": "); if (cgi) write_escaping_html(out, RSTRING(epath)->ptr, RSTRING(epath)->len); else fwrite(RSTRING(epath)->ptr, 1, RSTRING(epath)->len, out); if (cgi) fprintf(out, "
\n"); else fprintf(out, "\n"); } else { char *tail = 0; int len = RSTRING(einfo)->len; if (RSTRING(epath)->ptr[0] == '#') epath = 0; if ((tail = strchr(RSTRING(einfo)->ptr, '\n')) != NULL) { len = tail - RSTRING(einfo)->ptr; tail++; /* skip newline */ } fprintf(out, ": "); if (cgi) write_escaping_html(out, RSTRING(einfo)->ptr, len); else fwrite(RSTRING(einfo)->ptr, 1, len, out); if (epath) { fprintf(out, " ("); if (cgi) write_escaping_html(out, RSTRING(epath)->ptr, RSTRING(epath)->len); else fwrite(RSTRING(epath)->ptr, 1, RSTRING(epath)->len, out); if (cgi) fprintf(out, ")
\n"); else fprintf(out, ")\n"); } if (tail) { if (cgi) write_escaping_html(out, tail, RSTRING(einfo)->len - len - 1); else fwrite(tail, 1, RSTRING(einfo)->len - 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; ilen; i++) { if (TYPE(ep->ptr[i]) == T_STRING) { if (cgi) { fprintf(out, "
from "); write_escaping_html(out, RSTRING(ep->ptr[i])->ptr, RSTRING(ep->ptr[i])->len); } else { fprintf(out, " from "); fwrite(RSTRING(ep->ptr[i])->ptr, 1, RSTRING(ep->ptr[i])->len, out); } if (cgi) fprintf(out, "
\n"); else fprintf(out, "\n"); } if (i == TRACE_HEAD && ep->len > TRACE_MAX) { char buff[BUFSIZ]; if (cgi) snprintf(buff, BUFSIZ, "
... %ld levels...\n", ep->len - TRACE_HEAD - TRACE_TAIL); else snprintf(buff, BUFSIZ, " ... %ld levels...
\n", ep->len - TRACE_HEAD - TRACE_TAIL); if (cgi) write_escaping_html(out, buff, strlen(buff)); else fputs(buff, out); i = ep->len - 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(code)->ptr, RSTRING(code)->len);
    }
    else {
	fwrite(RSTRING(code)->ptr, 1, RSTRING(code)->len, 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(str)->ptr, RSTRING(str)->len); return Qnil; } static VALUE defout_cancel(VALUE self) { if (RSTRING(self)->len == 0) return Qnil; RSTRING(self)->len = 0; RSTRING(self)->ptr[0] = '\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); #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 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]; } } } static void run() { VALUE stack_start; VALUE code; int state; char *out; int nout; void Init_stack _((VALUE*)); Init_stack(&stack_start); code = eruby_load(eruby_filename, 0, &state); 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); } } if (eruby_mode == MODE_FILTER && (RTEST(ruby_debug) || RTEST(ruby_verbose))) { print_generated_code(stderr, code, 0); } #if RUBY_VERSION_CODE >= 180 out = RSTRING(rb_stdout)->ptr; nout = RSTRING(rb_stdout)->len; #else out = RSTRING(rb_defout)->ptr; nout = RSTRING(rb_defout)->len; #endif 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); printf("Content-Length: %d\r\n", nout); printf("\r\n"); } fwrite(out, nout, 1, stdout); fflush(stdout); ruby_finalize(); } int main(int argc, char **argv) { init(); proc_args(argc, argv); run(); return 0; } /* * Local variables: * mode: C * tab-width: 8 * End: */