1 /*
2 * ruby.c -- Calling RUBY from epic.
3 *
4 * Copyright 2006 EPIC Software Labs.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notices, the above paragraph (the one permitting redistribution),
14 * this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. The names of the author(s) may not be used to endorse or promote
17 * products derived from this software without specific prior written
18 * permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #include "irc.h"
34 #include "ircaux.h"
35 #include "array.h"
36 #include "alias.h"
37 #include "commands.h"
38 #include "functions.h"
39 #include "output.h"
40 #include "ifcmd.h"
41 #include "extlang.h"
42 #include <ruby.h>
43
44 VALUE rubyclass;
45 int is_ruby_running = 0;
46
47 #define RUBY_STARTUP \
48 VALUE x; \
49 char *my_string; \
50 \
51 x = rb_obj_as_string(string); \
52 my_string = StringValuePtr(x);
53
epic_echo(VALUE module,VALUE string)54 static VALUE epic_echo (VALUE module, VALUE string)
55 {
56 RUBY_STARTUP
57 yell("%s", my_string);
58 return Qnil;
59 }
60
epic_say(VALUE module,VALUE string)61 static VALUE epic_say (VALUE module, VALUE string)
62 {
63 RUBY_STARTUP
64 say("%s", my_string);
65 return Qnil;
66 }
67
epic_cmd(VALUE module,VALUE string)68 static VALUE epic_cmd (VALUE module, VALUE string)
69 {
70 RUBY_STARTUP
71 runcmds("$*", my_string);
72 return Qnil;
73 }
74
epic_eval(VALUE module,VALUE string)75 static VALUE epic_eval (VALUE module, VALUE string)
76 {
77 RUBY_STARTUP
78 runcmds(my_string, "");
79 return Qnil;
80 }
81
epic_expr(VALUE module,VALUE string)82 static VALUE epic_expr (VALUE module, VALUE string)
83 {
84 char *exprval;
85 RUBY_STARTUP
86
87 exprval = parse_inline(my_string, "");
88 return rb_str_new(exprval, strlen(exprval));
89 }
90
epic_call(VALUE module,VALUE string)91 static VALUE epic_call (VALUE module, VALUE string)
92 {
93 char *funcval;
94 RUBY_STARTUP
95
96 funcval = call_function(my_string, "");
97 return rb_str_new(funcval, strlen(funcval));
98 }
99
100 /* Called by the epic hooks to activate tcl on-demand. */
ruby_startstop(int value)101 void ruby_startstop (int value)
102 {
103 VALUE rubyval;
104 int state;
105
106 /* If it is already in the state we want, do nothing. */
107 if (is_ruby_running == value)
108 return;
109
110 /* Do a shutdown */
111 if (value == 0)
112 {
113 is_ruby_running = 0;
114 /* Do shutdown stuff */
115 return;
116 }
117
118 /* Do a startup */
119 ++is_ruby_running;
120 ruby_init();
121 ruby_init_loadpath();
122 ruby_script(malloc_strdup(irc_version));
123 rubyclass = rb_define_class("EPIC", rb_cObject);
124 rb_define_singleton_method(rubyclass, "echo", epic_echo, 1);
125 rb_define_singleton_method(rubyclass, "say", epic_say, 1);
126 rb_define_singleton_method(rubyclass, "cmd", epic_cmd, 1);
127 rb_define_singleton_method(rubyclass, "eval", epic_eval, 1);
128 rb_define_singleton_method(rubyclass, "expr", epic_expr, 1);
129 rb_define_singleton_method(rubyclass, "call", epic_call, 1);
130 rb_gc_register_address(&rubyclass);
131
132 /* XXX Is it a hack to do it like this instead of in pure C? */
133 rubyval = rb_eval_string_protect(
134 "EPICstderr = Object.new unless defined? EPICstderr\n"
135 "def EPICstderr.write(string) \n"
136 " str = string.chomp \n"
137 " EPIC.echo(\"RUBY-ERROR: #{str}\") \n"
138 "end \n"
139 "$stderr = EPICstderr", &state);
140 if (rubyval == Qnil)
141 say("stderr assignment returned Qnil");
142 if (state)
143 say("stderr assignment threw exception");
144
145 rubyval = rb_eval_string_protect(
146 "EPICstdout = Object.new unless defined? EPICstdout\n"
147 "def EPICstdout.write(string) \n"
148 " str = string.chomp \n"
149 " EPIC.echo(str) \n"
150 "end \n"
151 "$stdout = EPICstdout", &state);
152 if (rubyval == Qnil)
153 say("stdout assignment returned Qnil");
154 if (state)
155 say("stdout assignment threw exception");
156 }
157
158 /*
159 * Used by the $ruby(...) function: evalulate ... as a RUBY statement, and
160 * return the result of the statement.
161 */
internal_rubyeval(VALUE ruby_input)162 static VALUE internal_rubyeval (VALUE ruby_input)
163 {
164 VALUE rubyval;
165 char * a;
166
167 a = StringValueCStr(ruby_input);
168 rubyval = rb_eval_string(a);
169 if (rubyval == Qnil)
170 return Qnil;
171 else
172 return rubyval;
173 }
174
eval_failed(VALUE args,VALUE error_info)175 static VALUE eval_failed (VALUE args, VALUE error_info)
176 {
177 VALUE err_info_str;
178 char *ick;
179
180 err_info_str = rb_obj_as_string(error_info);
181 ick = StringValuePtr(err_info_str);
182 yell("RUBY-ERROR: %s", ick);
183 return Qnil;
184 }
185
rubyeval(char * input)186 char * rubyeval (char *input)
187 {
188 VALUE rubyval;
189 VALUE ruby_input;
190 char * retval = NULL;
191
192 if (input && *input)
193 {
194 ruby_startstop(1);
195 ruby_input = rb_str_new_cstr(input);
196 rubyval = rb_rescue2(internal_rubyeval, ruby_input,
197 eval_failed, 0,
198 rb_eException, 0);
199 if (rubyval == Qnil)
200 retval = NULL;
201 else
202 {
203 VALUE x;
204 x = rb_obj_as_string(rubyval);
205 retval = StringValuePtr(x);
206 }
207 }
208
209 RETURN_STR(retval); /* XXX Is this malloced or not? */
210 }
211
212 /*
213 * The /RUBY function: Evalulate the args as a RUBY block and ignore the
214 * return value of the statement.
215 */
BUILT_IN_COMMAND(rubycmd)216 BUILT_IN_COMMAND(rubycmd)
217 {
218 char *body, *x;
219
220 if (*args == '{')
221 {
222 if (!(body = next_expr(&args, '{')))
223 {
224 my_error("RUBY: unbalanced {");
225 return;
226 }
227 }
228 else
229 body = args;
230
231 x = rubyeval(body);
232 new_free(&x);
233 }
234
235