Cast to (const char *) in strrchr calls
[elinks.git] / src / scripting / ruby / core.c
blob24b0b3dd081cbaa9b1f3e1077b73ab10ced5d108
1 /* Ruby interface (scripting engine) */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <ruby.h>
9 #undef _
11 #include "elinks.h"
13 #include "bfu/dialog.h"
14 #include "config/home.h"
15 #include "intl/gettext/libintl.h"
16 #include "main/module.h"
17 #include "osdep/osdep.h"
18 #include "scripting/scripting.h"
19 #include "scripting/ruby/core.h"
20 #include "scripting/ruby/ruby.h"
21 #include "util/error.h"
22 #include "util/file.h"
23 #include "util/string.h"
25 #define RUBY_HOOKS_FILENAME "hooks.rb"
28 /* I've decided to use `erb' to refer to this ELinks/ruby thingy. */
30 VALUE erb_module;
33 /* Error reporting. */
35 void
36 alert_ruby_error(struct session *ses, unsigned char *msg)
38 report_scripting_error(&ruby_scripting_module, ses, msg);
41 /* Another Vim treat. */
42 void
43 erb_report_error(struct session *ses, int error)
45 VALUE eclass;
46 VALUE einfo;
47 unsigned char buff[MAX_STR_LEN];
48 unsigned char *msg;
50 /* XXX: Ew. These are from the Ruby internals. */
51 #define TAG_RETURN 0x1
52 #define TAG_BREAK 0x2
53 #define TAG_NEXT 0x3
54 #define TAG_RETRY 0x4
55 #define TAG_REDO 0x5
56 #define TAG_RAISE 0x6
57 #define TAG_THROW 0x7
58 #define TAG_FATAL 0x8
59 #define TAG_MASK 0xf
61 switch (error) {
62 case TAG_RETURN:
63 msg = "unexpected return";
64 break;
65 case TAG_NEXT:
66 msg = "unexpected next";
67 break;
68 case TAG_BREAK:
69 msg = "unexpected break";
70 break;
71 case TAG_REDO:
72 msg = "unexpected redo";
73 break;
74 case TAG_RETRY:
75 msg = "retry outside of rescue clause";
76 break;
77 case TAG_RAISE:
78 case TAG_FATAL:
79 eclass = CLASS_OF(ruby_errinfo);
80 einfo = rb_obj_as_string(ruby_errinfo);
82 if (eclass == rb_eRuntimeError && RSTRING(einfo)->len == 0) {
83 msg = "unhandled exception";
85 } else {
86 VALUE epath;
87 unsigned char *p;
89 epath = rb_class_path(eclass);
90 snprintf(buff, MAX_STR_LEN, "%s: %s",
91 RSTRING(epath)->ptr, RSTRING(einfo)->ptr);
93 p = strchr((const char *)buff, '\n');
94 if (p) *p = '\0';
95 msg = buff;
97 break;
98 default:
99 snprintf(buff, MAX_STR_LEN, "unknown longjmp status %d", error);
100 msg = buff;
101 break;
104 alert_ruby_error(ses, msg);
108 /* The ELinks module: */
110 /* Inspired by Vim this is used to hook into the stdout write method so written
111 * data is displayed in a nice message box. */
112 static VALUE
113 erb_module_message(VALUE self, VALUE str)
115 unsigned char *message, *line_end;
116 struct terminal *term;
118 str = rb_obj_as_string(str);
119 message = memacpy(RSTRING(str)->ptr, RSTRING(str)->len);
120 if (!message) return Qnil;
122 line_end = strchr((const char *)message, '\n');
123 if (line_end) *line_end = '\0';
125 term = get_default_terminal();
126 if (!term) {
127 usrerror("[Ruby] %s", message);
128 mem_free(message);
129 return Qnil;
132 info_box(term, MSGBOX_NO_TEXT_INTL | MSGBOX_FREE_TEXT,
133 N_("Ruby Message"), ALIGN_LEFT, message);
135 return Qnil;
138 /* The global Kernel::p method will for each object, directly write
139 * object.inspect() followed by the current output record separator to the
140 * program's standard output and will bypass the Ruby I/O libraries.
142 * Inspired by Vim we hook into the method and pop up a nice message box so it
143 * can be used to easily debug scripts without dirtying the screen. */
144 static VALUE
145 erb_stdout_p(int argc, VALUE *argv, VALUE self)
147 int i;
148 struct string string;
149 struct terminal *term;
151 if (!init_string(&string))
152 return Qnil;
154 for (i = 0; i < argc; i++) {
155 VALUE substr;
156 unsigned char *ptr;
157 int len;
159 if (i > 0)
160 add_to_string(&string, ", ");
162 substr = rb_inspect(argv[i]);
164 /* The Ruby p() function writes variable number of objects using
165 * the inspect() method, which adds quotes to the strings, so
166 * gently ignore them. */
168 ptr = RSTRING(substr)->ptr;
169 len = RSTRING(substr)->len;
171 if (*ptr == '"')
172 ptr++, len--;
174 if (ptr[len - 1] == '"')
175 len--;
177 add_bytes_to_string(&string, ptr, len);
180 term = get_default_terminal();
181 if (!term) {
182 usrerror("[Ruby] %s", string.source);
183 done_string(&string);
184 return Qnil;
187 info_box(term, MSGBOX_NO_TEXT_INTL | MSGBOX_FREE_TEXT,
188 N_("Ruby Message"), ALIGN_LEFT, string.source);
190 return Qnil;
193 /* ELinks::method_missing() is a catch all method that will be called when a
194 * hook is not defined. It might not be so elegant but it removes NoMethodErrors
195 * from popping up. */
196 /* FIXME: It might be useful for user to actually display them to debug scripts,
197 * so maybe it should be optional. --jonas */
198 static VALUE
199 erb_module_method_missing(VALUE self, VALUE arg)
201 return Qnil;
204 static void
205 init_erb_module(void)
207 unsigned char *home;
209 erb_module = rb_define_module("ELinks");
210 rb_define_const(erb_module, "VERSION", rb_str_new2(VERSION_STRING));
212 home = elinks_home ? elinks_home : (unsigned char *) CONFDIR;
213 rb_define_const(erb_module, "HOME", rb_str_new2(home));
215 rb_define_module_function(erb_module, "message", erb_module_message, 1);
216 rb_define_module_function(erb_module, "method_missing", erb_module_method_missing, -1);
217 rb_define_module_function(erb_module, "p", erb_stdout_p, -1);
221 void
222 init_ruby(struct module *module)
224 unsigned char *path;
226 /* Set up and initialize the interpreter. This function should be called
227 * before any other Ruby-related functions. */
228 ruby_init();
229 ruby_script("ELinks-ruby");
230 ruby_init_loadpath();
232 /* ``Trap'' debug prints from scripts. */
233 rb_define_singleton_method(rb_stdout, "write", erb_module_message, 1);
234 rb_define_global_function("p", erb_stdout_p, -1);
236 /* Set up the ELinks module interface. */
237 init_erb_module();
239 if (elinks_home) {
240 path = straconcat(elinks_home, RUBY_HOOKS_FILENAME,
241 (unsigned char *) NULL);
243 } else {
244 path = stracpy(CONFDIR STRING_DIR_SEP RUBY_HOOKS_FILENAME);
247 if (!path) return;
249 if (file_can_read(path)) {
250 int error;
252 /* Load ~/.elinks/hooks.rb into the interpreter. */
253 //rb_load_file(path);
254 rb_load_protect(rb_str_new2(path), 0, &error);
255 if (error)
256 erb_report_error(NULL, error);
259 mem_free(path);