home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (c) 1996 by Lele Gaifax. All Rights Reserved
- *
- * This file is part of Nothing (yet).
- *
- * $RCSfile: pyrl.c,v $
- * $Revision: 1.1.1.1 $
- * $Date: 1997/01/18 03:52:15 $
- *
- * Created Tue Oct 1 12:04:57 1996.
- */
-
- /* Lele Gaifax <lele@nautilus.eclipse.it> on 28 September 1996:
-
- I added a completer function for the readline library, bound on
- M-Tab (aka ESC-Tab). By doing a simple parse of the current line
- it's able to expand names of builtins, locals, members, methods... */
-
- #include "Python.h"
-
- #ifdef WITH_READLINE
-
- #include <stdio.h>
- #include <string.h>
- #include <errno.h>
-
- #include "myproto.h"
- #include "mymalloc.h"
- #include "intrcheck.h"
-
- #include <readline/readline.h>
- #include <readline/history.h>
- #include <setjmp.h>
- #include <signal.h>
-
-
- /* AIX requires this to be the first thing in the file. */
- #ifdef __GNUC__
- #ifndef alloca /* predefined by NeXT's header */
- # define alloca __builtin_alloca
- #endif
- #else
- # if HAVE_ALLOCA_H
- # include <alloca.h>
- # else
- # ifdef _AIX
- #pragma alloca
- # else
- # ifndef alloca /* predefined by HP cc +Olibcalls */
- char *alloca ();
- # endif
- # endif
- # endif
- #endif
-
- static inline char *
- strdup (const char *s)
- {
- char *dup = malloc (strlen (s) + 1);
-
-
- return strcpy (dup, s);
- }
-
- static jmp_buf jbuf;
-
- /* ARGSUSED */
- static RETSIGTYPE
- onintr(sig)
- int sig;
- {
- longjmp(jbuf, sig);
- }
-
- /* Appends to LIST the ``papabile'' symbols in DICT, that may actually
- be a dictionary, a list, or an object responding to .keys().
- Returns the number of added symbols, or 0 on errors. */
- static int
- append_papabile_symbols (PyObject *list,
- PyObject *dict,
- const char *text,
- int len)
- {
- PyObject *keys;
- int count;
-
- if (PyDict_Check (dict))
- {
- keys = PyDict_Keys (dict);
- if (!keys)
- return 0;
-
- if (PyList_Sort (keys) != 0)
- {
- Py_DECREF (keys);
- return 0;
- }
- }
- else if (PyList_Check (dict))
- {
- keys = dict;
- Py_INCREF (keys);
- }
- else
- keys = PyObject_CallMethod (dict, "keys", NULL);
-
- count = 0;
- if (keys)
- {
- unsigned int idx = PyList_Size (keys);
-
- while (idx--)
- {
- PyObject *localsym = PyList_GetItem (keys, idx);
-
- if (strncmp (text, PyString_AsString (localsym), len) == 0)
- {
- PyList_Append (list, localsym);
- count++;
- }
- }
-
- Py_DECREF (keys);
- }
- else
- PyErr_Clear();
-
- return count;
- }
-
- /* If STATUS==0, collects the symbols from __main__.__dict__ and from
- __builtins__.__dict__, then build a list of those beginning with
- TEXT.
- Returns the next symbol's name if there is one, NULL otherwise.
-
- This is called from the readline's completer function. */
- static char *
- try_complete_local_symbol (char *text, int state)
- {
- static PyObject *symbols_list = NULL;
- static int symbols_list_idx;
-
- if (!state)
- {
- PyObject *symbols;
- int len;
-
- Py_XDECREF (symbols_list);
- symbols_list = NULL;
- symbols_list_idx = 0;
-
- symbols_list = PyList_New(0);
- if (!symbols_list)
- return NULL;
-
- len = strlen (text);
-
- symbols = PyObject_GetAttrString (PyImport_AddModule ("__main__"), "__dict__");
- if (symbols)
- {
- symbols_list_idx += append_papabile_symbols (symbols_list,
- symbols,
- text,
- len);
- Py_DECREF (symbols);
- }
- else
- PyErr_Clear();
-
- symbols = PyObject_GetAttrString (PyEval_GetBuiltins(), "__dict__");
- if (symbols)
- {
- symbols_list_idx += append_papabile_symbols (symbols_list,
- symbols,
- text,
- len);
- Py_DECREF (symbols);
- }
- else
- PyErr_Clear();
- }
-
- if (symbols_list_idx > 0)
- {
- PyObject *localsym = PyList_GetItem (symbols_list, --symbols_list_idx);
-
- return strdup (PyString_AsString (localsym));
- }
-
- return NULL;
- }
-
- /* If STATUS==0, tries to understand from the entered TEXT the object
- we are writing the name, and then collects in a list the name of the
- symbols in both ``__dict__'' and ``__members__'' slots of it.
-
- Returns the next symbol's name if there is one, NULL otherwise.
-
- This is called from the readline's completer function. */
- static char *
- try_complete_member_symbol (char *text, int state)
- {
- static PyObject *symbols_list = NULL;
- static int symbols_list_idx;
- static int prefix_len;
-
- if (!state)
- {
- int len;
- PyObject *symbols = PyImport_AddModule ("__main__");
- PyObject *list;
- const char *point, *start;
-
- Py_XDECREF (symbols_list);
- symbols_list = NULL;
- symbols_list_idx = 0;
-
- symbols_list = PyList_New(0);
- if (!symbols_list)
- return NULL;
-
- /* PyImport_AddModule() does not return a new reference! */
- Py_INCREF (symbols);
-
- start = text;
- while ((point = strchr (start, '.')))
- {
- int symlen = point-start;
- char *symname;
-
- if (!symlen)
- return NULL;
-
- symname = alloca (symlen+1);
- strncpy (symname, start, symlen);
- symname[symlen] = '\0';
-
- /* This is safe: if this is the first time, we INCREFed the
- module before the loop; if not, the previous GetAttrString()
- returned a new reference to a surely-existing-somewhere object. */
- Py_DECREF (symbols);
-
- symbols = PyObject_GetAttrString (symbols, symname);
- if (!symbols)
- {
- PyErr_Clear();
- return NULL;
- }
-
- start = point+1;
- }
-
- prefix_len = start-text;
- len = strlen (start);
-
- list = PyObject_GetAttrString (symbols, "__dict__");
- if (list)
- {
- symbols_list_idx += append_papabile_symbols (symbols_list,
- list,
- start,
- len);
- Py_DECREF (list);
- }
- else
- PyErr_Clear();
-
- list = PyObject_GetAttrString (symbols, "__members__");
- if (list)
- {
- symbols_list_idx += append_papabile_symbols (symbols_list,
- list,
- start,
- len);
- Py_DECREF (list);
- }
- else
- PyErr_Clear();
-
- list = PyObject_GetAttrString (symbols, "__methods__");
- if (list)
- {
- symbols_list_idx += append_papabile_symbols (symbols_list,
- list,
- start,
- len);
- Py_DECREF (list);
- }
- else
- PyErr_Clear();
- }
-
- if (symbols_list_idx > 0)
- {
- PyObject *localsym = PyList_GetItem (symbols_list, --symbols_list_idx);
- extern char *strdup (const char *);
- const char *ls = PyString_AsString (localsym);
- char *complete;
-
- complete = alloca (prefix_len + PyString_Size (localsym) + 1);
- strncpy (complete, text, prefix_len);
- strcpy (complete+prefix_len, ls);
- return strdup (complete);
- }
-
- return NULL;
- }
-
- static inline char **
- builtin_completion (char *text, int start, int end)
- {
- if (end > start && strchr (text, '.'))
- return completion_matches (text, try_complete_member_symbol);
- else
- return completion_matches (text, try_complete_local_symbol);
- }
-
- static PyObject *python_completion_in_python;
-
- /* If the current TEXT contains a point '.', assume we are accessing a
- member of an object and attempt to complete that; otherwise try to
- complete a local symbol. */
- static char **
- python_completion (char *text, int start, int end)
- {
- /* Do not perform standard filename completion, even if we can't complete
- the symbol. */
- rl_attempted_completion_over = 1;
-
- if (python_completion_in_python)
- {
- PyTupleObject *args;
- PyObject *result;
-
- args = (PyTupleObject *) PyTuple_New (3);
- if (!args)
- return NULL;
-
- PyTuple_SET_ITEM (args, 0, PyString_FromString (text));
- PyTuple_SET_ITEM (args, 1, PyInt_FromLong (start));
- PyTuple_SET_ITEM (args, 2, PyInt_FromLong (end));
-
- result = PyEval_CallObject (python_completion_in_python, (PyObject *) args);
-
- Py_DECREF (args);
-
- if (result && PyList_Check (result))
- {
- int midx = PyList_Size (result);
- char **matches = malloc ((midx+1) * sizeof (char *));
-
- if (!matches)
- {
- PyErr_NoMemory();
- return NULL;
- }
-
- matches[midx] = NULL;
- while (midx--)
- matches[midx] = strdup (PyString_AsString (PyList_GetItem (result, midx)));
-
- Py_DECREF (result);
-
- return matches;
- }
- else
- {
- if (! result)
- {
- PyErr_Print();
-
- Py_DECREF (python_completion_in_python);
- python_completion_in_python = NULL;
- }
- else
- Py_DECREF (result);
- return NULL;
- }
- }
- else
- return builtin_completion (text, start, end);
- }
-
- static char *
- py_readline (prompt)
- char *prompt;
- {
- int n;
- char *p;
- RETSIGTYPE (*old_inthandler)();
- int sig;
-
- #ifdef NeXT
- RETSIGTYPE (*old_conthandler)();
-
- sigcont_received:
- old_conthandler = signal (SIGCONT, onintr);
- #endif
- old_inthandler = signal (SIGINT, onintr);
- if ((sig = setjmp (jbuf)))
- {
- #ifdef HAVE_SIGRELSE
- /* This seems necessary on SunOS 4.1 (Rasmus Hahn) */
- sigrelse(SIGINT);
- #endif
- signal(SIGINT, old_inthandler);
- #ifdef NeXT
- signal (SIGCONT, old_conthandler);
- if (sig == SIGCONT)
- goto sigcont_received;
- #endif
- return NULL;
- }
-
- p = readline (prompt);
- signal (SIGINT, old_inthandler);
- #ifdef NeXT
- signal (SIGCONT, old_conthandler);
- #endif
-
- if (p == NULL)
- {
- p = malloc(1);
- if (p != NULL)
- *p = '\0';
- return p;
- }
- n = strlen (p);
- if (n > 0)
- add_history (p);
- if ((p = realloc (p, n+2)) != NULL)
- {
- p[n] = '\n';
- p[n+1] = '\0';
- }
- return p;
- }
-
- static char pyrl_set_completer_doc[] =
- "Set a new completer function. The COMPLETER argument must be a callable\n\
- object, and will be called with three arguments: the TEXT entered so far,\n\
- START and END show the region of TEXT that contains the word to complete.\n\
- Returns the previous completer object, or None if it was the builtin one.";
-
- static PyObject *
- pyrl_set_completer (PyObject *self, PyObject *args)
- {
- PyObject *new;
-
- if (PyArg_ParseTuple (args, "O;completer", &new))
- {
- if (new == Py_None || PyCallable_Check (new))
- {
- PyObject *old = python_completion_in_python;
-
- if (new != Py_None)
- {
- python_completion_in_python = new;
- Py_INCREF (python_completion_in_python);
- }
- else
- python_completion_in_python = NULL;
-
- if (!old)
- {
- old = Py_None;
- Py_INCREF (old);
- }
-
- return old;
- }
- else
- PyErr_BadArgument();
- }
-
- return NULL;
- }
-
- static char pyrl_readline_doc[] =
- "Read a line of input from the user with the GNU readline facility. If the\n\
- optional PROMPT is not given, sys.ps1 is used instead.";
-
- static PyObject *
- pyrl_readline (PyObject *self, PyObject *args)
- {
- char *prompt = NULL;
-
- if (PyArg_ParseTuple (args, "|s;prompt", &prompt))
- {
- char *line;
-
- if (prompt == NULL)
- {
- PyObject *ps1 = PySys_GetObject ("ps1");
-
- prompt = PyString_AsString (ps1);
- }
-
- line = py_readline (prompt);
- if (line)
- {
- PyObject *ret = PyString_FromString (line);
-
- free (line);
- return ret;
- }
- else
- {
- PyErr_NoMemory();
- return NULL;
- }
- }
-
- return NULL;
- }
-
- static char pyrl_builtin_completer_doc[] = "The builtin completer function";
- static PyObject *
- pyrl_builtin_completer (PyObject *self, PyObject *args)
- {
- char *text;
- int start, end;
-
- if (PyArg_ParseTuple (args, "sii", &text, &start, &end))
- {
- char **result = builtin_completion (text, start, end);
-
- if (result)
- {
- PyObject *list;
- int ncompl;
-
- for (ncompl=0; result[ncompl]; ncompl++)
- /* do nothing */;
-
- list = PyList_New (ncompl);
- if (!list)
- return NULL;
-
- while (ncompl--)
- {
- PyObject *str = PyString_FromString (result[ncompl]);
-
- PyList_SetItem (list, ncompl, str);
- free (result[ncompl]);
- }
- free (result);
-
- return list;
- }
- else
- {
- Py_INCREF (Py_None);
- return Py_None;
- }
- }
-
- return NULL;
- }
-
- extern char * (*PyOS_Readline)();
-
- #endif /* WITH_READLINE */
-
- static PyMethodDef pyrl_methods[] =
- {
- #ifdef WITH_READLINE
- { "set_completer", (PyCFunction) pyrl_set_completer, METH_VARARGS, pyrl_set_completer_doc },
- { "builtin_completer",(PyCFunction) pyrl_builtin_completer, METH_VARARGS, pyrl_builtin_completer_doc },
- { "readline", (PyCFunction) pyrl_readline, METH_VARARGS, pyrl_readline_doc },
- #endif
- { 0, 0, 0, 0 }
- };
-
- static char pyrl_doc[] =
- #ifdef WITH_READLINE
- "A replacement for the builtin readline implementation that uses GNU readline.";
- #else
- "A placehold empty module";
- #endif
-
- void initpyrl()
- {
- static int been_here = 0;
-
- if (!been_here)
- {
- Py_InitModule4 ("pyrl", pyrl_methods, pyrl_doc, NULL, PYTHON_API_VERSION);
-
- been_here++;
-
- #ifdef WITH_READLINE
- /* Force rebind of TAB to insert-tab */
- rl_bind_key ('\t', rl_insert);
- rl_bind_key_in_map ('\t', rl_complete, emacs_meta_keymap);
- rl_readline_name = "Python";
- rl_attempted_completion_function = (CPPFunction *) python_completion;
- rl_basic_word_break_characters = " \t\"'`<>=()[]{}";
- PyOS_Readline = py_readline;
- #endif
- }
- }
-