Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
720 lines (650 sloc) 16.7 KB
/*
* $Id$
* Copyright (C) 2000 ZetaBITS, Inc.
* Copyright (C) 2000 Information-technology Promotion Agency, Japan
* Copyright (C) 2000 Shugo Maeda <shugo@modruby.net>
*
* 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 <ctype.h>
#include <time.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#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("&amp;", out);
break;
case '<':
fputs("&lt;", out);
break;
case '>':
fputs("&gt;", out);
break;
case '"':
fputs("&quot;", 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, "<br>\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, ")<br>\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, "<br>\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; i<RARRAY_LEN(ep); i++) {
if (TYPE(RARRAY_PTR(ep)[i]) == T_STRING) {
if (cgi) {
fprintf(out, "<div class=\"backtrace\">from ");
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, "<br></div>\n");
else
fprintf(out, "\n");
}
if (i == TRACE_HEAD && RARRAY_LEN(ep) > TRACE_MAX) {
char buff[BUFSIZ];
if (cgi)
snprintf(buff, BUFSIZ,
"<div class=\"backtrace\">... %ld levels...\n",
RARRAY_LEN(ep) - TRACE_HEAD - TRACE_TAIL);
else
snprintf(buff, BUFSIZ, " ... %ld levels...<br></div>\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, "<tr><th id=\"code\">\n");
fprintf(out, "GENERATED CODE\n");
fprintf(out, "</th></tr>\n");
fprintf(out, "<tr><td headers=\"code\">\n");
fprintf(out, "<pre><code>\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, "</code></pre>\n");
fprintf(out, "</td></tr>\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, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\">\n");
fprintf(out, "<html>\n");
fprintf(out, "<head>\n");
fprintf(out, "<title>eRuby</title>\n");
fprintf(out, "<style type=\"text/css\">\n");
fprintf(out, "<!--\n");
fprintf(out, "body { background-color: #ffffff }\n");
fprintf(out, "table { width: 100%%; padding: 5pt; border-style: none }\n");
fprintf(out, "th { color: #6666ff; background-color: #b0d0d0; text-align: left }\n");
fprintf(out, "td { color: #336666; background-color: #d0ffff }\n");
fprintf(out, "strong { color: #ff0000; font-weight: bold }\n");
fprintf(out, "div.backtrace { text-indent: 15%% }\n");
fprintf(out, "#version { color: #ff9900 }\n");
fprintf(out, "-->\n");
fprintf(out, "</style>\n");
fprintf(out, "</head>\n");
fprintf(out, "<body>\n");
fprintf(out, "<table summary=\"eRuby error information\">\n");
fprintf(out, "<caption>\n");
fprintf(out, "<img src=\"%s/logo.png\" alt=\"eRuby\">\n", imgdir);
fprintf(out, "<span id=version>version: %s</span>\n", ERUBY_VERSION);
fprintf(out, "</caption>\n");
fprintf(out, "<tr><th id=\"error\">\n");
fprintf(out, "ERROR\n");
fprintf(out, "</th></tr>\n");
fprintf(out, "<tr><td headers=\"error\">\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, "</td></tr>\n");
}
if (!NIL_P(code))
print_generated_code(out, code, cgi);
if (cgi) {
fprintf(out, "</table>\n");
fprintf(out, "</body>\n");
fprintf(out, "</html>\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:
*/