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