1 /*
2  * python.c -- Calling Python from epic.
3  *
4  * Copyright 2016 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 /* Python commit #14 */
33 
34 #include <Python.h>
35 #include "irc.h"
36 #include "ircaux.h"
37 #include "array.h"
38 #include "alias.h"
39 #include "commands.h"
40 #include "functions.h"
41 #include "output.h"
42 #include "ifcmd.h"
43 #include "extlang.h"
44 #include "newio.h"
45 #include "server.h"
46 
47 void	output_traceback (void);
48 
49 static	int	p_initialized = 0;
50 static	PyObject *global_vars = NULL;
51 
52 /*
53  * ObRant
54  *
55  * So Python is a language like ircII where there is a distinction made between
56  * "statements" and "expressions".  In both languages, a "statement" is a call
57  * that does not result in a return value, and expression is.  I can't blame
58  * python for this.  But it is different from Perl, Ruby, and TCL, which treat
59  * everything as a "callable" and then you either get a return value (for an
60  * expression) or an empty string (for a statement).
61  *
62  * In order to make python support work, though, we have to honor this
63  * distinction.  I've chosen to do this through the /PYTHON command and the
64  * $python() function
65  *
66  * 	You can only use the /PYTHON command to run statements.
67  *	Using /PYTHON with an expression results in an exception being thrown.
68  *
69  *	You can only use the $python() function to evaluate expressions
70  *	Using $python() with a statement results in an exception being thrown.
71  *
72  * How do you know whether what you're doing is an expression or a statement,
73  * when if you just throw everything into one file you don't have to worry
74  * about it? Good question.  I don't know.  Good luck!
75  */
76 
77 /*
78  * I owe many thanks to
79  *	https://docs.python.org/3.6/extending/embedding.html
80  *	https://docs.python.org/3.6/c-api/veryhigh.html
81  *	https://docs.python.org/3/c-api/init.html
82  *	https://docs.python.org/3/c-api/exceptions.html
83  *	https://www6.software.ibm.com/developerworks/education/l-pythonscript/l-pythonscript-ltr.pdf
84  *	http://boost.cppll.jp/HEAD/libs/python/doc/tutorial/doc/using_the_interpreter.html
85  *	  (for explaining what the "start" flag is to PyRun_String)
86  * for teaching how to embed and extend Python in a C program.
87  */
88 
89 /*
90  * I owe many many thanks to skully for working with me on this
91  * and giving me good advice on how to make this not suck.
92  */
93 
94 /*************************************************************************/
95 /*
96  * Extended Python by creating an "import epic" module.
97  */
98 /*
99  * Psuedo-code of how to create a python module that points at your C funcs
100  *
101  * These functions provide the basic facilities of ircII.
102  * All of these functions take one string argument.
103  *   epic.echo	- yell() -- Like /echo, unconditionally output to screen
104  *   epic.say	- say()	 -- Like /xecho -s, output if not suppressed with ^
105  *   epic.cmd	- runcmds() -- Run a block of code,
106  * 			but don't expand $'s (like from the input line)
107  *   epic.eval	- runcmds() -- Run a block of code,
108  *			expand $'s, but $* is []  (this is lame)
109  *   epic.expr	- parse_inline() -- Evaluate an expression string
110  *			and return the result
111  *   epic.call	- call_function() -- Evaluate a "funcname(argument list)"
112  *			string and return the result
113  *
114  * These functions provide a route-around of ircII, if that's what you want.
115  * All of these functions take a symbol name ("name") and a string containing
116  *	the arguments (if appropriate)
117  * NONE OF THESE FUNCTIONS UNDERGO $-EXPANSION!
118  *	(If you need that, use the stuff ^^^ above)
119  *
120  *   epic.run_command - run an alias (preferentially) or a builtin command.
121  *        Example: epic.run_command("xecho", "-w 0 hi there!  This does not get expanded")
122  *   epic.call_function - call an alias (preferentially) or a builtin function
123  *        Example: epic.call_function("windowctl", "refnums")
124  *   epic.get_set - get a /SET value (only)
125  *        Example: epic.get_set("mail")
126  *   epic.get_assign - get an /ASSIGN value (only)
127  *        Example: epic.get_assign("myvar")
128  *   epic.get_var - get an /ASSIGN value (preferentially) or a /SET
129  *   epic.set_set - set a /SET value (only)
130  *        Example: epic.set_set("mail", "ON")
131  *   epic.set_assign - set a /ASSIGN value
132  *        Example: epic.set_assign("myvar", "5")
133  *
134  * These functions allow you to register file descriptors (like socket)
135  * with epic, which will call back your method when they're interesting.
136  *
137  *   epic.callback_when_readable(python_file, python_function, python_object)
138  *   epic.callback_when_writable(python_file, python_function, python_object)
139  *	When 'python_function' is None, it cancels the callback.
140  *	The 'python_function' needs to be a static function (?)
141  *	The 'python_object' can be anything, i guess.
142  */
143 
144 /********************** Higher level interface to things *********************/
145 
146 /*
147  * epic.echo("hello, world") -- Output something without a banner (like /echo)
148  *
149  * Arguments:
150  *	self - ignored (the "epic" module)
151  *	args - A tuple containing
152  *		1. A string containing stuff to output
153  *
154  * Return value:
155  *	NULL - PyArg_ParseTuple() didn't like your tuple
156  *	   0 - The stuff was displayed successfully.
157  */
epic_echo(PyObject * self,PyObject * args)158 static	PyObject *	epic_echo (PyObject *self, PyObject *args)
159 {
160 	char *	str;
161 
162 	/*
163 	 * https://docs.python.org/3/extending/extending.html
164 	 * tells me that if PyArg_ParseTuple() doesn't like the
165 	 * argument list, it will set an exception and return
166 	 * NULL.  And all i should do is return NULL.
167 	 * So that is why i do that here (and everywhere)
168 	 */
169 	if (!PyArg_ParseTuple(args, "z", &str)) {
170 		return NULL;
171 	}
172 
173 	yell("%s", str);
174 	return PyLong_FromLong(0L);
175 }
176 
177 /*
178  * epic.say("Warning, WIll Robinson") -- Output something with a banner
179  *					(like internal commands do)
180  *
181  * Arguments:
182  *	self - ignored (the "epic" module)
183  *	args - A tuple containing
184  *		1. A string containing stuff to output
185  *
186  * Notes:
187  *	The banner is (of course) /SET BANNER.
188  *
189  * Return value:
190  *	NULL - PyArg_ParseTuple() didn't like your tuple (and threw exception)
191  *	 0 - The stuff was displayed successfully.
192  */
epic_say(PyObject * self,PyObject * args)193 static	PyObject *	epic_say (PyObject *self, PyObject *args)
194 {
195 	char *	str;
196 
197 	if (!PyArg_ParseTuple(args, "z", &str)) {
198 		return NULL;
199 	}
200 
201 	say("%s", str);
202 	return PyLong_FromLong(0L);
203 }
204 
205 /*
206  * epic.cmd("join #epic") -- Run an ircII command like at the input line
207  *
208  * Arguments:
209  *	self - ignored (the "epic" module)
210  *	args - A tuple containing
211  *		1. A string containing an ircII command statement
212  *
213  * Notes:
214  *	'args' is run as a single ircII statement (as if you had typed
215  *	it at the command line).  $'s are not expanded; semicolons are
216  *	not treated as statement separates, and braces have no special
217  *	meaning.
218  *
219  * Return value:
220  *	NULL - PyArg_ParseTuple() didn't like your tuple (and threw exception)
221  *	 0 - The stuff was run successfully.
222  */
epic_cmd(PyObject * self,PyObject * args)223 static	PyObject *	epic_cmd (PyObject *self, PyObject *args)
224 {
225 	char *	str;
226 
227 	if (!PyArg_ParseTuple(args, "z", &str)) {
228 		return NULL;
229 	}
230 
231 	runcmds("$*", str);
232 	return PyLong_FromLong(0L);
233 }
234 
235 /*
236  * epic.eval("echo My nick is $N") -- Run an ircII command line in an alias
237  *
238  * Arguments:
239  *	self - ignored (the "epic" module)
240  *	args - A tuple containing
241  *		1. A string containing an ircII statement
242  *
243  * Notes:
244  * 	$* is treated as the empty string ([]) and so any $0, $1 type
245  *	expansions will expand to the empty string.  Maybe this will
246  *	change in the future, and you'll be permitted to pass in $*.
247  *
248  * Return value:
249  *	NULL - PyArg_ParseTuple() didn't like your tuple (and threw exception)
250  *	 0 - The stuff ran successfully.
251  */
epic_eval(PyObject * self,PyObject * args)252 static	PyObject *	epic_eval (PyObject *self, PyObject *args)
253 {
254 	char *	str;
255 
256 	if (!PyArg_ParseTuple(args, "z", &str)) {
257 		return NULL;
258 	}
259 
260 	runcmds(str, "");
261 	return PyLong_FromLong(0L);
262 }
263 
264 /*
265  * epic.expr("var + 2 * numonchannel()") -- Return the result of an expression
266  *
267  * Arguments:
268  *	self - ignored (the "epic" module)
269  *	args - A tuple containing
270  *		1. A string containing an ircII expression
271  *
272  * Notes:
273  *	$* is treated as the empty string ([]) and so any $0, $1 type
274  *	expansions will expand to the empty string.  Maybe this will
275  *	change in the future, and you'll be permitted to pass in $*.
276  *
277  * Return value:
278  *	NULL - PyArg_ParseTuple() didn't like your tuple (and threw exception)
279  *	NULL - Py_BuildValue() threw an exception converting the expr
280  *		result (a string) into a python string.
281  *	A string - The result of the expression
282  *		- All ircII expressions result in a string, even if that
283  *		  string contains an integer or whatever.
284  *		  ie, "2 + 2" is "4" == a string containing the number 4.
285  */
epic_expr(PyObject * self,PyObject * args)286 static	PyObject *	epic_expr (PyObject *self, PyObject *args)
287 {
288 	char *	str;
289 	char *	exprval;
290 	PyObject *retval;
291 
292 	if (!PyArg_ParseTuple(args, "z", &str)) {
293 		return NULL;
294 	}
295 
296 	exprval = parse_inline(str, "");
297 	if (!(retval = Py_BuildValue("z", exprval))) {
298 		return NULL;
299 	}
300 	new_free(&exprval);
301 	return retval;
302 }
303 
304 /*
305  * epic.expand("text with ${2+2} $vars in it") -- Expand a string and return it
306  *
307  * Arguments:
308  *	self - ignored (the "epic" module)
309  *	args - A tuple containing
310  *		1. A string containing an ircII string
311  *
312  * Notes:
313  *	$* is treated as the empty string ([]) and so any $0, $1 type
314  * 	expansions will expand to the empty string.  Maybe this will
315  *	change in the future, and you'll be permitted to pass in $*.
316  *
317  * Return value:
318  *	NULL - PyArg_ParseTuple() didn't like your tuple (and threw exception)
319  *	NULL - Py_BuildValue() couldn't convert the retval to python string (throws exception)
320  *	A string - The result of the expansion
321  */
epic_expand(PyObject * self,PyObject * args)322 static	PyObject *	epic_expand (PyObject *self, PyObject *args)
323 {
324 	char *	str;
325 	char *	expanded;
326 	PyObject *retval;
327 
328 	if (!PyArg_ParseTuple(args, "z", &str)) {
329 		return NULL;
330 	}
331 
332 	expanded = expand_alias(str, "");
333 	retval = Py_BuildValue("z", expanded);
334 	new_free(&expanded);
335 	return retval;
336 }
337 
338 /*
339  * epic.call("function(arglist)") -- Call a function directly (with $-expansion)
340  *
341  * Arguments:
342  *	self - ignored (the "epic" module)
343  *	args - A tuple containing
344  *		1. A string containing a function call, with parenthesis
345  *
346  * Notes:
347  *	The 'arglist' will be expanded ($-expandos will be honored)
348  *	$* will be treated as the empty string (see the caveats above)
349  *
350  * Return value:
351  *	NULL - PyArg_ParseTuple() didn't like your tuple (and threw exception)
352  *	NULL - Py_BuildValue() couldn't convert the retval to python string
353  *		(throws exception)
354  *	A string - The return value of the function call
355  *		All function calls return exactly one string, even if
356  *		that string contains a number or a list of words.
357  *		You may need to process the string in python.
358  */
epic_call(PyObject * self,PyObject * args)359 static	PyObject *	epic_call (PyObject *self, PyObject *args)
360 {
361 	char *	str;
362 	char *	funcval;
363 	PyObject *retval;
364 
365 	if (!PyArg_ParseTuple(args, "z", &str)) {
366 		return NULL;
367 	}
368 
369 	funcval = call_function(str, "");
370 	retval = Py_BuildValue("z", funcval);
371 	new_free(&funcval);
372 	return retval;
373 }
374 
375 /* Lower level interfaces to things */
376 
377 
378 /*
379  * epic.run_command("COMMAND", "arglist") - Call a command (alias or builtin)
380  *					directly WITHOUT expansion
381  *
382  * Arguments:
383  * 	self - ignored (the "epic" module)
384  *	args - A tuple containing:
385  *		1. A string containing an alias name or a built in command name
386  *		2. A string representing the argument list
387  *
388  * Note: All ircII commands take one string as the argument, even if that string
389  * 	contains some serialization of a collection.  Technically each command
390  *	is permitted to do whatever it wants with its arguments; but the
391  *	convention is to accept a space separated list of words.  Again, this
392  *	is not a requirement, justthe way most things work.
393  *
394  * Note: The argument list is _NOT_ expanded; it is passed literally in to
395  * 		the cmd.
396  * Note: XXX - The $* that is passed in is NULL; I'm not sure if this is
397  *		correct or not.
398  * Note: Aliases are prefered to builtin commands, just like ircII does it.
399  *
400  * Return Value:
401  *	NULL             - PyArg_ParseTuple() didn't like your tuple
402  *					(and threw exception)
403  *	NULL / NameError - The command you tried to run doesn't exist.
404  *	None		 - The command was run successfully
405  */
epic_run_command(PyObject * self,PyObject * args)406 static	PyObject *	epic_run_command (PyObject *self, PyObject *args)
407 {
408 	char *	symbol;
409 	char *	my_args;
410 	PyObject *retval;
411 	void    (*builtin) (const char *, char *, const char *) = NULL;
412 const 	char *	alias = NULL;
413 	void *	arglist = NULL;
414 
415 	if (!PyArg_ParseTuple(args, "zz", &symbol, &my_args)) {
416 		return NULL;
417 	}
418 
419 	upper(symbol);
420 	alias = get_cmd_alias(symbol, &arglist, &builtin);
421 	if (alias)
422 		call_user_command(symbol, alias, my_args, arglist);
423 	else if (builtin)
424 		builtin(symbol, my_args, empty_string);
425 	else
426 	{
427 		PyErr_Format(PyExc_NameError, "%s : Command does not exist", symbol);
428 		return NULL;
429 	}
430 
431 	/* Success! */
432 	Py_INCREF(Py_None);
433 	return Py_None;
434 }
435 
436 
437 /*
438  * epic.call_function("FUNCTION", "arglist") -- Call a function
439  *				(alias or builtin) directly WITHOUT expansion
440  *
441  * Arguments:
442  *	self - ignored (the "epic" module)
443  *	args - A tuple containing:
444  *		1. A string containing an alias name or built in function name
445  *		2. A string representing the argument list
446  *
447  * Note: All ircII functions take one string as their argument, even if that
448  *	string contains some representation of a collection.  Technically each
449  *	function is permitted to do whatever it wants with its arguments; but
450  *	the convention is to accept a space separated list of words.  Again,
451  * 	this is not a requirement, just the way most things work.
452  *
453  * Note: The argument list is _NOT_ expanded; it is passed literally to the fn.
454  * Note: XXX - The $* that is passed in is NULL;
455  *		I'm not sure if this is correct or not.
456  * Note: Aliases are preferred to builtins, just like ircII.
457  *
458  * Return Value:
459  *	NULL             - PyArg_ParseTuple() didn't like your tuple
460  *					(and threw exception)
461  *	NULL / NameError - The function you tried to run doesn't exist.
462  *	A string - the return value of the function (which may be zero-length for its own reasons)
463  */
epic_call_function(PyObject * self,PyObject * args)464 static	PyObject *	epic_call_function (PyObject *self, PyObject *args)
465 {
466 	char *	symbol;
467 	char *	my_args;
468 	PyObject *retval;
469 	const char *alias;
470 	void *	arglist;
471 	char * (*builtin) (char *) = NULL;
472 	char *	funcval = NULL;
473 
474 	if (!PyArg_ParseTuple(args, "zz", &symbol, &my_args)) {
475 		return PyLong_FromLong(-1);
476 	}
477 
478 	upper(symbol);
479         alias = get_func_alias(symbol, &arglist, &builtin);
480         if (alias)
481                 funcval = call_user_function(symbol, alias, my_args, arglist);
482 	else if (builtin)
483                 funcval = builtin(my_args);
484 	else
485 	{
486 		PyErr_Format(PyExc_NameError, "%s : Function does not exist", symbol);
487 		return NULL;
488 	}
489 
490 	retval = Py_BuildValue("z", funcval);
491 	new_free(&funcval);
492 	return retval;
493 }
494 
495 /*
496  * epic.get_set("SETNAME") - Return the value of /SET SETNAME
497  *
498  * Arguments:
499  * 	self - ignored (the "epic" module)
500  *	args - A tuple containing:
501  *		1. A /SET name (ie, "AUTO_REJOIN")
502  *
503  * Note: Although /SETs have types (integer, boolean, string), the
504  *	value of a SET is always a string.
505  *
506  * Return value:
507  *	NULL             - PyArg_ParseTuple() didn't like your tuple
508  *					(and threw exception)
509  *	NULL / NameError - The set you tried to fetch doesn't exist.
510  *	A string - the value of /SET SETNAME
511  */
epic_get_set(PyObject * self,PyObject * args)512 static	PyObject *	epic_get_set (PyObject *self, PyObject *args)
513 {
514 	char *	symbol;
515 	PyObject *retval;
516 	char *	funcval = NULL;
517 
518 	if (!PyArg_ParseTuple(args, "z", &symbol)) {
519 		return NULL;
520 	}
521 
522 	upper(symbol);
523 	if (make_string_var2(symbol, &funcval) < 0)
524 	{
525 		PyErr_Format(PyExc_NameError, "%s : SET variable does not exist", symbol);
526 		return NULL;
527 	}
528 
529 	retval = Py_BuildValue("z", funcval);
530 	new_free(&funcval);
531 	return retval;
532 }
533 
534 /*
535  * epic.get_assign("VARNAME") - Return the value of /ASSIGN VARNAME
536  *
537  * Arguments:
538  *	self - ignored (the "epic" module)
539  *	args - A tuple containing:
540  *		1. A /ASSIGN name (ie, "MYVAR")
541  *
542  * Note: Variables are always strings, even if they contain a number.
543  *
544  * Return value:
545  *	NULL             - PyArg_ParseTuple() didn't like your tuple
546  *					(and threw exception)
547  *	NULL / NameError - The set you tried to fetch doesn't exist.
548  *	A string - the value of /ASSIGN VARNAME (or empty if it does not exist)
549  *		XXX - This is probably wrong, asking for an unknown
550  *			assign should probably return None or throw an
551  *			exception or something.
552  */
epic_get_assign(PyObject * self,PyObject * args)553 static	PyObject *	epic_get_assign (PyObject *self, PyObject *args)
554 {
555 	char *	symbol;
556 	PyObject *retval;
557 	char *	funcval = NULL;
558         char *		(*efunc) (void) = NULL;
559         IrcVariable *	sfunc = NULL;
560 	const char *	assign = NULL;
561 
562 	if (!PyArg_ParseTuple(args, "z", &symbol)) {
563 		return NULL;
564 	}
565 
566 	upper(symbol);
567         if (!(assign = get_var_alias(symbol, &efunc, &sfunc)))
568 	{
569 		PyErr_Format(PyExc_NameError, "%s : ASSIGN variable does not exist", symbol);
570 		return NULL;
571 	}
572 
573 	retval = Py_BuildValue("z", assign);
574 	/* _DO NOT_ FREE 'assign'! */
575 	return retval;
576 }
577 
578 /*
579  * epic.get_var("NAME") - Return the value of a variable [see notes]
580  *
581  * Arguments:
582  *	self - ignored (the "epic" module)
583  *	args - A tuple containing:
584  *		1. A variable symbol name of some sort
585  *
586  * Note:  A variable name can be a local variable, a global variable, or a set
587  *	variable. The return value is what you would get if you did $<NAME>.
588  * Note:  XXX - Because of how this is implemented, this supports ":local" and
589  *	"::global", although I'm not sure it will stay this way forever.
590  *
591  * Return value:
592  *	NULL             - PyArg_ParseTuple() didn't like your tuple
593  *					(and threw exception)
594  *	A string - The value of $<NAME>
595  *		The zero-length string if $<NAME> does not exist.
596  *
597  * Note: Because of how ircII does this, it is not possible to differentiate
598  *	between a variable that does not exist and a variable that is unset.
599  *	If you need that level of granularity (which should be unusual)
600  *	you should use epic.get_var(), epic.get_assign(), etc.
601  */
epic_get_var(PyObject * self,PyObject * args)602 static	PyObject *	epic_get_var (PyObject *self, PyObject *args)
603 {
604 	char *	symbol;
605 	PyObject *retval;
606 	char *	funcval = NULL;
607 
608 	if (!PyArg_ParseTuple(args, "z", &symbol)) {
609 		return NULL;
610 	}
611 
612 	upper(symbol);
613 	funcval = get_variable(symbol);
614 	retval = Py_BuildValue("z", funcval);
615 	new_free(&funcval);
616 	return retval;
617 }
618 
619 /*
620  * epic.set_set("setname", "value") -- do a /SET
621  * XXX - TODO - XXX
622  *
623  * Arguments:
624  *	self - ignored (the "epic" module)
625  *	args - A tuple containing
626  *		1. A string - the SET to be set
627  *		2. A string - the value to be SET
628  * Note:
629  *	SETs have types, which are not currently exposed through this
630  *	interface.  You need to pass in a string that contains a value
631  *	of the correct type (ie, if the SET is an integer, you need to
632  *	pass in a string containing an integer.  If the SET is a boolean,
633  *	you need to pass in a string containing "TRUE" or "FALSE".)
634  * 	Who knows -- this may improve in the future.
635  *
636  * Return value:
637  *	NULL             - PyArg_ParseTuple() didn't like your tuple
638  *					(and threw exception)
639  *	NULL / NotImplelmentedError	- This function is not implemented yet
640  */
epic_set_set(PyObject * self,PyObject * args)641 static	PyObject *	epic_set_set (PyObject *self, PyObject *args)
642 {
643 	char *	symbol;
644 	char *	value;
645 	PyObject *retval;
646 
647 	if (!PyArg_ParseTuple(args, "zz", &symbol, &value)) {
648 		return NULL;
649 	}
650 
651 	/* XXX - TODO - /SET symbol value */
652 	PyErr_Format(PyExc_NotImplementedError, "Not Implemented Yet");
653 	return NULL;
654 
655 	return PyLong_FromLong(0);
656 }
657 
658 /*
659  * epic.set_assign("varname", "value") -- do an /ASSIGN
660  * XXX - TODO - XXX
661  *
662  * Arguments:
663  *	self - ignored (the "epic" module)
664  *	args - A tuple containing
665  *		1. A string -- the variable to be /ASSIGNed
666  *		2. A string -- the value to be /ASSIGNED
667  *
668  * Return value:
669  *	NULL             - PyArg_ParseTuple() didn't like your tuple
670  *					(and threw exception)
671  *	NULL / NotImplelmentedError	- This function is not implemented yet
672  */
epic_set_assign(PyObject * self,PyObject * args)673 static	PyObject *	epic_set_assign (PyObject *self, PyObject *args)
674 {
675 	char *	symbol;
676 	char *	value;
677 	PyObject *retval;
678 
679 	if (!PyArg_ParseTuple(args, "zz", &symbol, &value)) {
680 		return NULL;
681 	}
682 
683 	/* XXX - TODO - /ASSIGN symbol value */
684 	PyErr_Format(PyExc_NotImplementedError, "Not Implemented Yet");
685 	return NULL;
686 
687 }
688 
689 /* * * * * * * * * */
690 typedef struct {
691 	int		vfd;
692 	PyObject *	read_callback;
693 	PyObject *	write_callback;
694 	PyObject *	except_callback;
695 	int		flags;
696 } PythonFDCallback;
697 
698 /* This should be in a header file somewhere... */
699 #if defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX)
700 #  define IO_ARRAYLEN   sysconf(_SC_OPEN_MAX)
701 #else
702 # if defined(FD_SETSIZE)
703 #  define IO_ARRAYLEN   FD_SETSIZE
704 # else
705 #  define IO_ARRAYLEN   NFDBITS
706 # endif
707 #endif
708 
709 PythonFDCallback **python_vfd_callback = NULL;
710 
init_python_fd_callbacks(void)711 void	init_python_fd_callbacks (void)
712 {
713 	int	vfd;
714 	int	max_fd = IO_ARRAYLEN;
715 
716 	if (python_vfd_callback)
717 		return;		/* Panic is so 12008 */
718 
719 	python_vfd_callback = (PythonFDCallback **)new_malloc(sizeof(PythonFDCallback *) * max_fd);
720 	for (vfd = 0; vfd < max_fd; vfd++)
721 		python_vfd_callback[vfd] = NULL;
722 }
723 
get_python_vfd_callback(int vfd)724 PythonFDCallback *	get_python_vfd_callback (int vfd)
725 {
726 	if (vfd < 0 || vfd > IO_ARRAYLEN)
727 		return NULL;
728 
729 	return python_vfd_callback[vfd];
730 }
731 
set_python_vfd_callback(int vfd,PythonFDCallback * callback)732 int	set_python_vfd_callback (int vfd, PythonFDCallback *callback)
733 {
734 	if (callback == NULL)
735 		return -1;
736 
737 	if (python_vfd_callback[vfd] && python_vfd_callback[vfd] != callback)
738 		return -1;
739 
740 	if (callback->vfd == -1)
741 		callback->vfd = vfd;
742 
743 	if (vfd != callback->vfd)
744 		return -1;
745 
746 	python_vfd_callback[vfd] = callback;
747 	return 0;
748 }
749 
new_python_fd_callback(int vfd)750 PythonFDCallback *	new_python_fd_callback (int vfd)
751 {
752 	PythonFDCallback *callback;
753 
754 	callback= (PythonFDCallback *)new_malloc(sizeof(PythonFDCallback));
755 	callback->vfd = vfd;
756 	callback->read_callback = NULL;
757 	callback->write_callback = NULL;
758 	callback->except_callback = NULL;
759 	callback->flags = 0;
760 	set_python_vfd_callback(vfd, callback);
761 	return callback;
762 }
763 
destroy_python_fd_callback(int vfd)764 int	destroy_python_fd_callback (int vfd)
765 {
766 	PythonFDCallback *callback;
767 
768 	if (!(callback = python_vfd_callback[vfd]))
769 		return -1;
770 
771 	callback->vfd = -1;
772 	callback->read_callback = NULL;
773 	callback->write_callback = NULL;
774 	callback->except_callback = NULL;
775 	callback->flags = 0;
776 	python_vfd_callback[vfd] = NULL;
777 	new_free((void **)&callback);
778 	return 0;
779 }
780 
781 
782 
783 /*
784  * call_python_function_1arg - Call a python function to handle IO callbacks
785  *
786  * Python IO callbacks take one argument - the file descriptor.
787  * Python IO callbacks _may not throw exceptions_.
788  * Exceptions are considered a "broken" situation and will result in
789  * a call to the except_callback to repair the situation.
790  * If the except_callback fails, then it's game over for this fd.
791  *
792  * Retval
793  *   -1 	- The callback failed (threw an exception)
794  *    0		- The callback did not fail
795  */
call_python_function_1arg(PyObject * pFunc,int vfd)796 int 	call_python_function_1arg (PyObject *pFunc, int vfd)
797 {
798 	PyObject *args_py = NULL;
799 	PyObject *pArgs = NULL, *pRetVal = NULL;
800 	/* PyObject *retval_repr = NULL; */
801 	char 	*r = NULL, *retvalstr = NULL;
802 
803 	if (!PyCallable_Check(pFunc))
804 	{
805 		my_error("python_fd_callback: The callback was not a function");
806 		goto c_p_f_error;
807 	}
808 
809 	if (!(pArgs = PyTuple_New(1)))
810 		goto c_p_f_error;
811 
812 	if (!(args_py = Py_BuildValue("i", vfd)))
813 		goto c_p_f_error;
814 
815 	if ((PyTuple_SetItem(pArgs, 0, args_py)))
816 		goto c_p_f_error;
817 	args_py = NULL;			/* args_py now belongs to the tuple! */
818 
819 	if (!(pRetVal = PyObject_CallObject(pFunc, pArgs)))
820 		goto c_p_f_error;
821 
822         if (!(/*retval_repr = */PyObject_Repr(pRetVal)))
823 		goto c_p_f_error;
824 
825 	goto c_p_f_cleanup;
826 
827 c_p_f_error:
828 	output_traceback();
829 	return -1;
830 
831 c_p_f_cleanup:
832 	Py_XDECREF(pArgs);
833 	Py_XDECREF(pRetVal);
834 	return 0;
835 }
836 
do_python_fd(int vfd)837 void	do_python_fd (int vfd)
838 {
839 	PythonFDCallback *callback;
840 	char	buffer[BIG_BUFFER_SIZE];
841 	int	n;
842 
843 	if (!((callback = get_python_vfd_callback(vfd))))
844 	{
845 		yell("do_python_fd: FD %d doesn't belong to me - new_close()ing it.", vfd);
846 		new_close(vfd);
847 		return;
848 	}
849 
850 	if ((n = dgets(vfd, buffer, BIG_BUFFER_SIZE, -1)) > 0)
851 	{
852 		if (callback->read_callback)
853 		{
854 			if ((call_python_function_1arg(callback->read_callback, vfd)))
855 			{
856 				if ((call_python_function_1arg(callback->except_callback, vfd)))
857 				{
858 					yell("do_python_fd: FD %d failed: both ordinary and error callback failed.", vfd);
859 					new_close(vfd);
860 				}
861 			}
862 		}
863 
864 		else if (callback->write_callback)
865 		{
866 			if ((call_python_function_1arg(callback->write_callback, vfd)))
867 			{
868 				if ((call_python_function_1arg(callback->except_callback, vfd)))
869 				{
870 					yell("do_python_fd: FD %d failed: both ordinary and error callback failed.", vfd);
871 					new_close(vfd);
872 				}
873 			}
874 		}
875 
876 	}
877 	else if (n < 0)
878 	{
879 		if (callback->except_callback)
880 		{
881 			if ((call_python_function_1arg(callback->except_callback, vfd)))
882 			{
883 				yell("do_python_fd: FD %d failed: both ordinary and error callback failed.", vfd);
884 				new_close(vfd);
885 			}
886 		}
887 	}
888 }
889 
890 
do_python_fd_failure(int vfd,int error)891 void	do_python_fd_failure (int vfd, int error)
892 {
893 	PythonFDCallback *callback;
894 
895 	if (!((callback = get_python_vfd_callback(vfd))))
896 	{
897 		yell("do_python_fd_failure: FD %d doesn't belong to me - new_close()ing it myself.", vfd);
898 		new_close(vfd);
899 		return;
900 	}
901 
902 	if (callback->except_callback)
903 		call_python_function_1arg(callback->except_callback, vfd);
904 }
905 
906 
907 /*
908  * epic.callback_when_readable(fd, read_callback, except_function, flags)
909  *
910  * Arguments:
911  *	self - ignored (the "epic" module)
912  *	args - A tuple containing
913  *		1. fd - An integer - a unix file descriptor (from python)
914  *		2. read_callback - A CallableObject (ie, a method) - A python
915  *				method that takes one integer argument. It will
916  *				be called each time the 'fd' is readable.
917  *				You must "handle" the fd, or it will busy-loop.
918  *				If you block while handling the fd, epic
919  *				will block.
920  *					Arg1 - fd
921  *		3. except_callback - A CallableObject (ie, a method) - A python
922  *				method that takes two integer arguments. It will
923  *				be called if the 'fd' becomes invalid.  This
924  *				would happen if somneone close()d the FD, but
925  *				didn't call _epic.cancel_callback().
926  *				You should call _epic.cancel_callback(), or
927  *				it will busy-loop.  If you block while handling
928  *				the fd, epic will block.
929  *					Arg1 - fd, Arg2 - error code
930  *		4. flags - Reserved for future expansion.  Pass in 0 for now.
931  *
932  * Return value:
933  *	NULL 		- PyArg_ParseTuple() didn't like your tuple
934  *					(and threw exception)
935  *	0		- The command succeeded
936  */
epic_callback_when_readable(PyObject * self,PyObject * args)937 static	PyObject *	epic_callback_when_readable (PyObject *self, PyObject *args)
938 {
939 	long	vfd, flags;
940 	PyObject *read_callback, *except_callback;
941 	PythonFDCallback *callback;
942 
943 	/*
944 	 * https://docs.python.org/3/extending/extending.html
945 	 * tells me that if PyArg_ParseTuple() doesn't like the
946 	 * argument list, it will set an exception and return
947 	 * NULL.  And all i should do is return NULL.
948 	 * So that is why i do that here (and everywhere)
949 	 */
950 	if (!PyArg_ParseTuple(args, "lOOl", &vfd, &read_callback, &except_callback, &flags)) {
951 		return NULL;
952 	}
953 
954 	/*
955 	 * 1. Look up python fd record, create if necessary
956 	 * 2. Set read_callback in the python fd record
957 	 * 3. Call new_open() if necessary
958 	 */
959 	if (!(callback = get_python_vfd_callback(vfd)))
960 		callback = new_python_fd_callback(vfd);
961 	else
962 	{
963 		if (callback->write_callback)
964 			new_close_with_option(vfd, 1);
965 	}
966 
967 	callback->write_callback = NULL;
968 	callback->read_callback = read_callback;
969 	callback->except_callback = except_callback;
970 	callback->flags = flags;
971 
972 	new_open(vfd, do_python_fd, NEWIO_PASSTHROUGH_READ, 0, from_server);
973 	new_open_failure_callback(vfd, do_python_fd_failure);
974 	return PyLong_FromLong(0L);
975 }
976 
977 /*
978  * epic.callback_when_writable(fd, write_callback, except_function, flags)
979  *
980  * Arguments:
981  *	self - ignored (the "epic" module)
982  *	args - A tuple containing
983  *		1. fd - An integer - a unix file descriptor (from python)
984  *		2. read_callback - A CallableObject (ie, a method) - A python
985  *				method that takes one integer argument. It will
986  *				be called each time the 'fd' is writable.
987  *				You must "handle" the fd, or it will busy-loop.
988  *				If you block while handling the fd, epic
989  *				will block.
990  *		3. except_callback - A CallableObject (ie, a method) - A python
991  *				method that takes one integer argument. It will
992  *				be called if the 'fd' becomes invalid.  This
993  *				would happen if somneone close()d the FD, but
994  *				didn't call _epic.cancel_callback().
995  *				You should call _epic.cancel_callbacks(), or
996  *				it will busy-loop.  If you block while handling
997  *				the fd, epic will block.
998  *		4. flags - Reserved for future expansion.  Pass in 0 for now.
999  *
1000  * Return value:
1001  *	NULL 		- PyArg_ParseTuple() didn't like your tuple
1002  *					(and threw exception)
1003  *	0		- The command succeeded
1004  */
epic_callback_when_writable(PyObject * self,PyObject * args)1005 static	PyObject *	epic_callback_when_writable (PyObject *self, PyObject *args)
1006 {
1007 	long	vfd, flags;
1008 	PyObject *write_callback, *except_callback;
1009 	PythonFDCallback *callback;
1010 
1011 	/*
1012 	 * https://docs.python.org/3/extending/extending.html
1013 	 * tells me that if PyArg_ParseTuple() doesn't like the
1014 	 * argument list, it will set an exception and return
1015 	 * NULL.  And all i should do is return NULL.
1016 	 * So that is why i do that here (and everywhere)
1017 	 */
1018 	if (!PyArg_ParseTuple(args, "lOOl", &vfd, &write_callback, &except_callback, &flags)) {
1019 		return NULL;
1020 	}
1021 
1022 	/*
1023 	 * 1. Look up python fd record, create if necessary
1024 	 * 2. Set write_callback in the python fd record
1025 	 * 3. Call new_open() if necessary.
1026 	 */
1027 	if (!(callback = get_python_vfd_callback(vfd)))
1028 		callback = new_python_fd_callback(vfd);
1029 	else
1030 	{
1031 		if (callback->read_callback)
1032 			new_close_with_option(vfd, 1);
1033 	}
1034 
1035 	callback->read_callback = NULL;
1036 	callback->write_callback = write_callback;
1037 	callback->except_callback = except_callback;
1038 	callback->flags = flags;
1039 
1040 	new_open(vfd, do_python_fd, NEWIO_PASSTHROUGH_WRITE, 0, from_server);
1041 	new_open_failure_callback(vfd, do_python_fd_failure);
1042 	return PyLong_FromLong(0L);
1043 }
1044 
1045 /*
1046  * epic.cancel_callback(fd)
1047  *
1048  * Arguments:
1049  *	self - ignored (the "epic" module)
1050  *	args - A tuple containing
1051  *		1. fd - An integer - a unix file descriptor (from python)
1052  *
1053  * Return value:
1054  *	NULL 		- PyArg_ParseTuple() didn't like your tuple
1055  *					(and threw exception)
1056  *	0		- The command succeeded
1057  */
epic_cancel_callback(PyObject * self,PyObject * args)1058 static	PyObject *	epic_cancel_callback (PyObject *self, PyObject *args)
1059 {
1060 	long	vfd;
1061 	PythonFDCallback *callback;
1062 
1063 	/*
1064 	 * https://docs.python.org/3/extending/extending.html
1065 	 * tells me that if PyArg_ParseTuple() doesn't like the
1066 	 * argument list, it will set an exception and return
1067 	 * NULL.  And all i should do is return NULL.
1068 	 * So that is why i do that here (and everywhere)
1069 	 */
1070 	if (!PyArg_ParseTuple(args, "l", &vfd)) {
1071 		return NULL;
1072 	}
1073 
1074 	/*
1075 	 * 1. Look up python fd record (bail if does not exist)
1076 	 * 2. Call new_close_with_option()
1077 	 * 3. Clear the python fd record
1078 	 * (4. The python script has to close the fd...)
1079 	 */
1080 	if ((callback = get_python_vfd_callback(vfd)))
1081 	{
1082 		callback->read_callback = NULL;
1083 		callback->write_callback = NULL;
1084 		callback->except_callback = NULL;
1085 		callback->flags = 0;
1086 		new_close_with_option(vfd, 1);
1087 	}
1088 
1089 	return PyLong_FromLong(0L);
1090 }
1091 
1092 /*
1093  * INTERNAL USE ONLY --
1094  * When you want to register a python module.method as a hardcoded
1095  * builtin ircII command, you need a BUILT_IN_COMMAND() to be registered
1096  * as the callback.  Because of how ircII works, the /COMMAND that you
1097  * run ends up in 'command', so this shim just does the glue:
1098  *
1099  * In python:
1100  *   epic.builtin_cmd("module.method")
1101  * In ircII:
1102  *   /MODULE.METHOD ...args...
1103  *
1104  * XXX - It's not clear if 'subargs' should be passed to the python function.
1105  */
BUILT_IN_COMMAND(pyshim)1106 BUILT_IN_COMMAND(pyshim)
1107 {
1108 	char *	retval = NULL;
1109 
1110 	retval = call_python_directly(command, args);
1111 	new_free(&retval);
1112 }
1113 
1114 /*
1115  * epic.builtin_cmd("module.method") -- Register a python module.method
1116  *					as a builtin ircII cmd
1117  * Arguments:
1118  *	self - ignored (the "epic" object)
1119  *	args - A tuple containing
1120  *		1. A string - the name of "module.method")
1121  *			This will become /MODULE.METHOD in ircII.
1122  *
1123  * Return value:
1124  *	NULL     - PyArg_ParseTuple() didn't like your tuple
1125  *				(and threw exception)
1126  *	 None 	- The command was registered successfully
1127  */
epic_builtin_cmd(PyObject * self,PyObject * args)1128 static	PyObject *	epic_builtin_cmd (PyObject *self, PyObject *args)
1129 {
1130 	char *	symbol;
1131 
1132 	if (!PyArg_ParseTuple(args, "z", &symbol)) {
1133 		return NULL;
1134 	}
1135 
1136 	/* XXX TODO - Test this -- does 'symbol' need to be strdup()d?  XXX TODO */
1137 	add_builtin_cmd_alias(symbol, pyshim);
1138 
1139 	/* Success! */
1140 	Py_INCREF(Py_None);
1141 	return Py_None;
1142 }
1143 
1144 
1145 static	PyMethodDef	epicMethods[] = {
1146       /* Higher level facilities  - $-expansion supported */
1147 	{ "echo", 	   epic_echo, 	METH_VARARGS, 	"Unconditionally output to screen (yell)" },
1148 	{ "say", 	   epic_say, 	METH_VARARGS, 	"Output to screen unless suppressed (say)" },
1149 	{ "cmd", 	   epic_cmd, 	METH_VARARGS, 	"Run a block statement without expansion (runcmds)" },
1150 	{ "eval", 	   epic_eval, 	METH_VARARGS, 	"Run a block statement with expansion (but $* is empty)" },
1151 	{ "expr", 	   epic_expr, 	METH_VARARGS, 	"Return the result of an expression (parse_inline)" },
1152 	{ "call", 	   epic_call, 	METH_VARARGS, 	"Call a function with expansion (but $* is empty) (call_function)" },
1153 	{ "expand",	   epic_expand,	METH_VARARGS,	"Expand some text with $s" },
1154 
1155       /* Lower level facilities - $-expansion NOT supported */
1156 	{ "run_command",   epic_run_command,	METH_VARARGS,	"Run an alias or builtin command" },
1157 	{ "call_function", epic_call_function,	METH_VARARGS,	"Call an alias or builtin function" },
1158 	{ "get_set",       epic_get_set,	METH_VARARGS,	"Get a /SET value (only)" },
1159 	{ "get_assign",    epic_get_assign,	METH_VARARGS,	"Get a /ASSIGN value (only)" },
1160 	{ "get_var",       epic_get_assign,	METH_VARARGS,	"Get a variable (either /ASSIGN or /SET)" },
1161 	{ "set_set",       epic_set_set,	METH_VARARGS,	"Set a /SET value (only)" },
1162 	{ "set_assign",    epic_set_assign,	METH_VARARGS,	"Set a /ASSIGN value (only)" },
1163 	{ "builtin_cmd",   epic_builtin_cmd,	METH_VARARGS,	"Make a Python function an EPIC builtin command" },
1164       /* Lower level IO facilities */
1165 	{ "callback_when_readable",  epic_callback_when_readable, METH_VARARGS,	"Register a python function for FD event callbacks" },
1166 	{ "callback_when_writable",  epic_callback_when_writable, METH_VARARGS,	"Register a python function for FD event callbacks" },
1167 	{ "cancel_callback",         epic_cancel_callback,        METH_VARARGS,	"Unregister FD event callbacks" },
1168 
1169 	{ NULL,		NULL,		0,		NULL }
1170 };
1171 
1172 static	PyModuleDef	epicModule = {
1173 	PyModuleDef_HEAD_INIT,
1174 	"_epic", 	NULL, 		-1,		epicMethods,
1175 	NULL,		NULL,		NULL,		NULL
1176 };
1177 
1178 /*
1179  * This is called via
1180  *	PyImport_AppendInittab("_epic", &PyInit_epic);
1181  * before
1182  *	PyInitialize();
1183  * in main().
1184  */
PyInit_epic(void)1185 static	PyObject *	PyInit_epic (void)
1186 {
1187 	return PyModule_Create(&epicModule);
1188 }
1189 
initialize_python(int start)1190 void	initialize_python (int start)
1191 {
1192 	if (p_initialized == 0)
1193 	{
1194 		PyImport_AppendInittab("_epic", &PyInit_epic);
1195 		Py_Initialize();
1196 		p_initialized = 1;
1197 		global_vars = PyModule_GetDict(PyImport_AddModule("__main__"));
1198 		init_python_fd_callbacks();
1199 	}
1200 }
1201 
1202 /***********************************************************/
1203 /*
1204  * python_eval_expression - Call a Python Expression -- the $python() function
1205  *
1206  * Note: This can only be used to call a Python Expression.
1207  * 	It cannot be used to call a python statement.
1208  *
1209  * See at the top of the file -- Python makes a distinction between
1210  * statements and expressions (like ircII does) and has different
1211  * API calls for each (PyRun_String() and PyRun_SimpleString()).
1212  *
1213  * Arguments:
1214  *	input - A string to pass to the Python interpreter.
1215  *		It must be a python expression, or python will throw exception.
1216  *
1217  * Return Value:
1218  *	The return value of the python expression, as a new_malloc()ed string.
1219  * 	YOU are responsible for new_free()ing the value later (if you call this
1220  *	other than as the $python() function, of course)
1221  */
python_eval_expression(char * input)1222 char *	python_eval_expression (char *input)
1223 {
1224 	PyObject *retval;
1225 	PyObject *retval_repr;
1226 	const char 	*r;
1227 	char *retvalstr = NULL;
1228 
1229 	if (p_initialized == 0)
1230 		initialize_python(1);
1231 
1232 	/*
1233 	 * https://docs.python.org/3/c-api/veryhigh.html
1234  	 * says that this returns NULL if an exception is raised.
1235 	 * So in that case, we try to handle/format the exception.
1236 	 */
1237 	if (!(retval = PyRun_String(input, Py_eval_input, global_vars, global_vars)))
1238 		output_traceback();
1239 
1240 	/* Convert retval to a string */
1241 	retval_repr = PyObject_Repr(retval);
1242 	r = PyUnicode_AsUTF8(retval_repr);
1243 	retvalstr = malloc_strdup(r);
1244 
1245 	Py_XDECREF(retval);
1246 	Py_XDECREF(retval_repr);
1247 	RETURN_MSTR(retvalstr);
1248 }
1249 
1250 /*
1251  * python_eval_statement -- Call a Python Statement -- The /PYTHON function
1252  *
1253  * Note: This can only be used to call a Python Statement.
1254  *	It cannot be used to call a python expression.
1255  *
1256  * See at the top of the file -- Python makes a distinction between
1257  * statements and expressions (like ircII does) and has different
1258  * API calls for each (PyRun_String() and PyRun_SimpleString()).
1259  *
1260  * Arguments:
1261  *	input - A string to pass to the Python interpreter.
1262  *		It must be a python statement, or python will throw exception
1263  *
1264  * Note: It's not clear if the statement throws an exception whether or not
1265  *	we can catch and format it.  We'll see!
1266  */
python_eval_statement(char * input)1267 void	python_eval_statement (char *input)
1268 {
1269 	if (p_initialized == 0)
1270 		initialize_python(1);
1271 
1272 	/*
1273 	 * https://docs.python.org/3/c-api/veryhigh.html
1274 	 * says that "returns 0 on success or -1 if an exception is raised.
1275 	 * If there was an error, there is no way to get the exception
1276 	 * information;"  That's not 100% clear.  So I decided to assume
1277 	 * that if -1 is returned, we should see if there is exception
1278 	 * information and output it if we can.  There is the possibility
1279 	 * that python has just dumped the exception to stdout and that
1280 	 * we are out of luck for reformatting it.
1281 	 */
1282 	if (PyRun_SimpleString(input))
1283 		output_traceback();
1284 }
1285 
1286 
1287 /*
1288  * The /PYTHON command: Evalulate the args as a PYTHON block and ignore the
1289  * return value of the statement.
1290  */
BUILT_IN_COMMAND(pythoncmd)1291 BUILT_IN_COMMAND(pythoncmd)
1292 {
1293         char *body;
1294 
1295         if (*args == '{')
1296         {
1297                 if (!(body = next_expr(&args, '{')))
1298                 {
1299                         my_error("PYTHON: unbalanced {");
1300                         return;
1301                 }
1302         }
1303         else
1304                 body = args;
1305 
1306         python_eval_statement(body);
1307 }
1308 
1309 /*
1310  *
1311  * Call a python module.method directly without quoting hell
1312  *  The return value (if any) is ignored.
1313  *
1314  * Arguments:
1315  *	orig_object - a string containing "module.method", the name of a python method
1316  *	args - a string containing the argument that will be passed to "module.method"
1317  *
1318  * Return value:
1319  *	A new_malloc()ed string containing the repr()d return value of the method (success)
1320  *	A new_malloc()ed zero length string, if something went wrong (failure)
1321  */
call_python_directly(const char * orig_object,char * args)1322 char *	call_python_directly (const char *orig_object, char *args)
1323 {
1324 	char 	*object = NULL, *module = NULL, *method = NULL;
1325 	PyObject *mod_py = NULL, *meth_py = NULL, *args_py = NULL;
1326 	PyObject *pModule = NULL, *pFunc = NULL, *pArgs = NULL, *pRetVal = NULL;
1327 	PyObject *retval_repr = NULL;
1328 	const char 	*r = NULL;
1329 	char *retvalstr = NULL;
1330 
1331 	/* BAH! */
1332 	if (!orig_object)
1333 		RETURN_EMPTY;
1334 
1335 	object = LOCAL_COPY(orig_object);
1336 	module = object;
1337 	if (!(method = strchr(module, '.')))
1338 	{
1339 		my_error("Usage: /PYDIRECT module.method arguments");
1340 		RETURN_EMPTY;
1341 	}
1342 	*method++ = 0;
1343 
1344 	mod_py = Py_BuildValue("z", module);
1345 	pModule = PyImport_Import(mod_py);
1346 	Py_XDECREF(mod_py);
1347 
1348 	if (pModule == NULL)
1349 	{
1350 		my_error("PYDIRECT: Unable to import module %s", module);
1351 		goto c_p_d_error;
1352 	}
1353 
1354 	pFunc = PyObject_GetAttrString(pModule, method);
1355 	if (pFunc == NULL)
1356 	{
1357 		my_error("PYDIRECT: The module %s has nothing named %s", module, method);
1358 		goto c_p_d_error;
1359 	}
1360 
1361 	if (!PyCallable_Check(pFunc))
1362 	{
1363 		my_error("PYDIRECT: The thing named %s.%s is not a function", module, method);
1364 		goto c_p_d_error;
1365 	}
1366 
1367 	if (!(pArgs = PyTuple_New(1)))
1368 		goto c_p_d_error;
1369 
1370 	if (!(args_py = Py_BuildValue("z", args)))
1371 		goto c_p_d_error;
1372 
1373 	if ((PyTuple_SetItem(pArgs, 0, args_py)))
1374 		goto c_p_d_error;
1375 	args_py = NULL;			/* args_py now belongs to the tuple! */
1376 
1377 	if (!(pRetVal = PyObject_CallObject(pFunc, pArgs)))
1378 		goto c_p_d_error;
1379 
1380         if (!(retval_repr = PyObject_Repr(pRetVal)))
1381 		goto c_p_d_error;
1382 
1383         if (!(r = PyUnicode_AsUTF8(retval_repr)))
1384 		goto c_p_d_error;
1385 
1386         retvalstr = malloc_strdup(r);
1387 	goto c_p_d_cleanup;
1388 
1389 c_p_d_error:
1390 	output_traceback();
1391 
1392 c_p_d_cleanup:
1393 	Py_XDECREF(pArgs);
1394 	Py_XDECREF(pFunc);
1395 	Py_XDECREF(pModule);
1396 	Py_XDECREF(pRetVal);
1397 
1398 	RETURN_MSTR(retvalstr);
1399 }
1400 
1401 /*
1402  * /PYDIRECT -- Call a python module.method directly, without using
1403  *		PyRun[Simple]String() [which handles syntax parsing]
1404  *
1405  * Usage:
1406  *	/PYDIRECT module.method arguments
1407  *
1408  * You can only call "module.method" python functions that accept exactly
1409  * one string as its input parameters.  If you need to call anything more
1410  * sophisticated than that, you need to use /PYTHON or $python() to handle
1411  * the parsing.
1412  */
BUILT_IN_COMMAND(pydirect_cmd)1413 BUILT_IN_COMMAND(pydirect_cmd)
1414 {
1415 	char *pyfuncname;
1416 	char *x;
1417 
1418 	if (!args || !*args)
1419 	{
1420 		say("Usage: /PYDIRECT module.method arguments");
1421 		return;
1422 	}
1423 
1424 	pyfuncname = new_next_arg(args, &args);
1425 	x = call_python_directly(pyfuncname, args);
1426 	new_free(&x);
1427 }
1428 
1429 
1430 /*
1431  * output_traceback - output a python exception in an epic-friendly way
1432  *
1433  * Certain things may cause Python Exceptions. When we detect that an
1434  * exception has occurred, we call this function to handle it.
1435  *
1436  * Arguments: None
1437  * Return value : None
1438  */
output_traceback(void)1439 void	output_traceback (void)
1440 {
1441 	PyObject *ptype, *pvalue, *ptraceback;
1442 	PyObject *ptype_repr, *pvalue_repr, *ptraceback_repr;
1443 	char *ptype_str, *pvalue_str, *ptraceback_str;
1444 
1445 	say("The python evaluation threw an exception.");
1446 #if 0
1447 	PyErr_Print();
1448 	PyErr_Fetch(&ptype, &pvalue, &ptraceback);
1449 	if (ptype != NULL)
1450 	{
1451 		ptype_repr = PyObject_Repr(ptype);
1452 		ptype_str = PyUnicode_AsUTF8(ptype_repr);
1453 		say("Exception Type: %s", ptype_str);
1454 	}
1455 	if (pvalue != NULL)
1456 	{
1457 		pvalue_repr = PyObject_Repr(pvalue);
1458 		pvalue_str = PyUnicode_AsUTF8(pvalue_repr);
1459 		say("Value: %s", pvalue_str);
1460 	}
1461 	if (ptraceback != NULL)
1462 	{
1463 #if 0
1464 		ptraceback_repr = PyObject_Repr(ptraceback);
1465 		ptraceback_str = PyUnicode_AsUTF8(ptraceback_repr);
1466 		say("Traceback: %s", ptraceback_str);
1467 #endif
1468 	}
1469 	Py_XDECREF(ptype);
1470 	Py_XDECREF(pvalue);
1471 	Py_XDECREF(ptraceback);
1472 #endif
1473 	return;
1474 }
1475 
1476