diff options
Diffstat (limited to 'src/if_python.c')
-rw-r--r-- | src/if_python.c | 2807 |
1 files changed, 2807 insertions, 0 deletions
diff --git a/src/if_python.c b/src/if_python.c new file mode 100644 index 0000000000..21c4365203 --- /dev/null +++ b/src/if_python.c @@ -0,0 +1,2807 @@ +/* vi:set ts=8 sts=4 sw=4: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ +/* + * Python extensions by Paul Moore. + * Changes for Unix by David Leonard. + * + * This consists of four parts: + * 1. Python interpreter main program + * 2. Python output stream: writes output via [e]msg(). + * 3. Implementation of the Vim module for Python + * 4. Utility functions for handling the interface between Vim and Python. + */ + +#include "vim.h" + +#include <stdio.h> +#include <stdarg.h> +#include <limits.h> + +/* Python.h defines _POSIX_THREADS itself (if needed) */ +#ifdef _POSIX_THREADS +# undef _POSIX_THREADS +#endif + +#if defined(_WIN32) && defined (HAVE_FCNTL_H) +# undef HAVE_FCNTL_H +#endif + +#ifdef _DEBUG +# undef _DEBUG +#endif + +#ifdef HAVE_STDARG_H +# undef HAVE_STDARG_H /* Python's config.h defines it as well. */ +#endif + +#include <Python.h> +#if defined(MACOS) && !defined(MACOS_X_UNIX) +# include "macglue.h" +# include <CodeFragments.h> +#endif +#undef main /* Defined in python.h - aargh */ +#undef HAVE_FCNTL_H /* Clash with os_win32.h */ + +#if !defined(FEAT_PYTHON) && defined(PROTO) +/* Use this to be able to generate prototypes without python being used. */ +# define PyObject int +# define PyThreadState int +# define PyTypeObject int +struct PyMethodDef { int a; }; +# define PySequenceMethods int +#endif + +/* Parser flags */ +#define single_input 256 +#define file_input 257 +#define eval_input 258 + +#if defined(PY_VERSION_HEX) && PY_VERSION_HEX >= 0x020300F0 + /* Python 2.3: can invoke ":python" recursively. */ +# define PY_CAN_RECURSE +#endif + +#if defined(DYNAMIC_PYTHON) || defined(PROTO) +# ifndef DYNAMIC_PYTHON +# define HINSTANCE int /* for generating prototypes */ +# endif + +/* + * Wrapper defines + */ +# define PyArg_Parse dll_PyArg_Parse +# define PyArg_ParseTuple dll_PyArg_ParseTuple +# define PyDict_SetItemString dll_PyDict_SetItemString +# define PyErr_BadArgument dll_PyErr_BadArgument +# define PyErr_Clear dll_PyErr_Clear +# define PyErr_NoMemory dll_PyErr_NoMemory +# define PyErr_Occurred dll_PyErr_Occurred +# define PyErr_SetNone dll_PyErr_SetNone +# define PyErr_SetString dll_PyErr_SetString +# define PyEval_InitThreads dll_PyEval_InitThreads +# define PyEval_RestoreThread dll_PyEval_RestoreThread +# define PyEval_SaveThread dll_PyEval_SaveThread +# ifdef PY_CAN_RECURSE +# define PyGILState_Ensure dll_PyGILState_Ensure +# define PyGILState_Release dll_PyGILState_Release +# endif +# define PyInt_AsLong dll_PyInt_AsLong +# define PyInt_FromLong dll_PyInt_FromLong +# define PyInt_Type (*dll_PyInt_Type) +# define PyList_GetItem dll_PyList_GetItem +# define PyList_New dll_PyList_New +# define PyList_SetItem dll_PyList_SetItem +# define PyList_Size dll_PyList_Size +# define PyList_Type (*dll_PyList_Type) +# define PyImport_ImportModule dll_PyImport_ImportModule +# define PyDict_GetItemString dll_PyDict_GetItemString +# define PyModule_GetDict dll_PyModule_GetDict +# define PyRun_SimpleString dll_PyRun_SimpleString +# define PyString_AsString dll_PyString_AsString +# define PyString_FromString dll_PyString_FromString +# define PyString_FromStringAndSize dll_PyString_FromStringAndSize +# define PyString_Size dll_PyString_Size +# define PyString_Type (*dll_PyString_Type) +# define PySys_SetObject dll_PySys_SetObject +# define PySys_SetArgv dll_PySys_SetArgv +# define PyType_Type (*dll_PyType_Type) +# define Py_BuildValue dll_Py_BuildValue +# define Py_FindMethod dll_Py_FindMethod +# define Py_InitModule4 dll_Py_InitModule4 +# define Py_Initialize dll_Py_Initialize +# define _PyObject_New dll__PyObject_New +# define _Py_NoneStruct (*dll__Py_NoneStruct) +# define PyObject_Init dll__PyObject_Init +# if defined(PY_VERSION_HEX) && PY_VERSION_HEX >= 0x02020000 +# define PyType_IsSubtype dll_PyType_IsSubtype +# endif +# if defined(PY_VERSION_HEX) && PY_VERSION_HEX >= 0x02030000 +# define PyObject_Malloc dll_PyObject_Malloc +# define PyObject_Free dll_PyObject_Free +# endif + +/* + * Pointers for dynamic link + */ +static int(*dll_PyArg_Parse)(PyObject *, char *, ...); +static int(*dll_PyArg_ParseTuple)(PyObject *, char *, ...); +static int(*dll_PyDict_SetItemString)(PyObject *dp, char *key, PyObject *item); +static int(*dll_PyErr_BadArgument)(void); +static void(*dll_PyErr_Clear)(void); +static PyObject*(*dll_PyErr_NoMemory)(void); +static PyObject*(*dll_PyErr_Occurred)(void); +static void(*dll_PyErr_SetNone)(PyObject *); +static void(*dll_PyErr_SetString)(PyObject *, const char *); +static void(*dll_PyEval_InitThreads)(void); +static void(*dll_PyEval_RestoreThread)(PyThreadState *); +static PyThreadState*(*dll_PyEval_SaveThread)(void); +# ifdef PY_CAN_RECURSE +static PyGILState_STATE (*dll_PyGILState_Ensure)(void); +static void (*dll_PyGILState_Release)(PyGILState_STATE); +#endif +static long(*dll_PyInt_AsLong)(PyObject *); +static PyObject*(*dll_PyInt_FromLong)(long); +static PyTypeObject* dll_PyInt_Type; +static PyObject*(*dll_PyList_GetItem)(PyObject *, int); +static PyObject*(*dll_PyList_New)(int size); +static int(*dll_PyList_SetItem)(PyObject *, int, PyObject *); +static int(*dll_PyList_Size)(PyObject *); +static PyTypeObject* dll_PyList_Type; +static PyObject*(*dll_PyImport_ImportModule)(const char *); +static PyObject*(*dll_PyDict_GetItemString)(PyObject *, const char *); +static PyObject*(*dll_PyModule_GetDict)(PyObject *); +static int(*dll_PyRun_SimpleString)(char *); +static char*(*dll_PyString_AsString)(PyObject *); +static PyObject*(*dll_PyString_FromString)(const char *); +static PyObject*(*dll_PyString_FromStringAndSize)(const char *, int); +static int(*dll_PyString_Size)(PyObject *); +static PyTypeObject* dll_PyString_Type; +static int(*dll_PySys_SetObject)(char *, PyObject *); +static int(*dll_PySys_SetArgv)(int, char **); +static PyTypeObject* dll_PyType_Type; +static PyObject*(*dll_Py_BuildValue)(char *, ...); +static PyObject*(*dll_Py_FindMethod)(struct PyMethodDef[], PyObject *, char *); +static PyObject*(*dll_Py_InitModule4)(char *, struct PyMethodDef *, char *, PyObject *, int); +static void(*dll_Py_Initialize)(void); +static PyObject*(*dll__PyObject_New)(PyTypeObject *, PyObject *); +static PyObject*(*dll__PyObject_Init)(PyObject *, PyTypeObject *); +static PyObject* dll__Py_NoneStruct; +# if defined(PY_VERSION_HEX) && PY_VERSION_HEX >= 0x02020000 +static int (*dll_PyType_IsSubtype)(PyTypeObject *, PyTypeObject *); +# endif +# if defined(PY_VERSION_HEX) && PY_VERSION_HEX >= 0x02030000 +static void* (*dll_PyObject_Malloc)(size_t); +static void (*dll_PyObject_Free)(void*); +# endif + +static HINSTANCE hinstPython = 0; /* Instance of python.dll */ + +/* Imported exception objects */ +static PyObject *imp_PyExc_AttributeError; +static PyObject *imp_PyExc_IndexError; +static PyObject *imp_PyExc_KeyboardInterrupt; +static PyObject *imp_PyExc_TypeError; +static PyObject *imp_PyExc_ValueError; + +# define PyExc_AttributeError imp_PyExc_AttributeError +# define PyExc_IndexError imp_PyExc_IndexError +# define PyExc_KeyboardInterrupt imp_PyExc_KeyboardInterrupt +# define PyExc_TypeError imp_PyExc_TypeError +# define PyExc_ValueError imp_PyExc_ValueError + +/* + * Table of name to function pointer of python. + */ +# define PYTHON_PROC FARPROC +static struct +{ + char *name; + PYTHON_PROC *ptr; +} python_funcname_table[] = +{ + {"PyArg_Parse", (PYTHON_PROC*)&dll_PyArg_Parse}, + {"PyArg_ParseTuple", (PYTHON_PROC*)&dll_PyArg_ParseTuple}, + {"PyDict_SetItemString", (PYTHON_PROC*)&dll_PyDict_SetItemString}, + {"PyErr_BadArgument", (PYTHON_PROC*)&dll_PyErr_BadArgument}, + {"PyErr_Clear", (PYTHON_PROC*)&dll_PyErr_Clear}, + {"PyErr_NoMemory", (PYTHON_PROC*)&dll_PyErr_NoMemory}, + {"PyErr_Occurred", (PYTHON_PROC*)&dll_PyErr_Occurred}, + {"PyErr_SetNone", (PYTHON_PROC*)&dll_PyErr_SetNone}, + {"PyErr_SetString", (PYTHON_PROC*)&dll_PyErr_SetString}, + {"PyEval_InitThreads", (PYTHON_PROC*)&dll_PyEval_InitThreads}, + {"PyEval_RestoreThread", (PYTHON_PROC*)&dll_PyEval_RestoreThread}, + {"PyEval_SaveThread", (PYTHON_PROC*)&dll_PyEval_SaveThread}, +# ifdef PY_CAN_RECURSE + {"PyGILState_Ensure", (PYTHON_PROC*)&dll_PyGILState_Ensure}, + {"PyGILState_Release", (PYTHON_PROC*)&dll_PyGILState_Release}, +# endif + {"PyInt_AsLong", (PYTHON_PROC*)&dll_PyInt_AsLong}, + {"PyInt_FromLong", (PYTHON_PROC*)&dll_PyInt_FromLong}, + {"PyInt_Type", (PYTHON_PROC*)&dll_PyInt_Type}, + {"PyList_GetItem", (PYTHON_PROC*)&dll_PyList_GetItem}, + {"PyList_New", (PYTHON_PROC*)&dll_PyList_New}, + {"PyList_SetItem", (PYTHON_PROC*)&dll_PyList_SetItem}, + {"PyList_Size", (PYTHON_PROC*)&dll_PyList_Size}, + {"PyList_Type", (PYTHON_PROC*)&dll_PyList_Type}, + {"PyImport_ImportModule", (PYTHON_PROC*)&dll_PyImport_ImportModule}, + {"PyDict_GetItemString", (PYTHON_PROC*)&dll_PyDict_GetItemString}, + {"PyModule_GetDict", (PYTHON_PROC*)&dll_PyModule_GetDict}, + {"PyRun_SimpleString", (PYTHON_PROC*)&dll_PyRun_SimpleString}, + {"PyString_AsString", (PYTHON_PROC*)&dll_PyString_AsString}, + {"PyString_FromString", (PYTHON_PROC*)&dll_PyString_FromString}, + {"PyString_FromStringAndSize", (PYTHON_PROC*)&dll_PyString_FromStringAndSize}, + {"PyString_Size", (PYTHON_PROC*)&dll_PyString_Size}, + {"PyString_Type", (PYTHON_PROC*)&dll_PyString_Type}, + {"PySys_SetObject", (PYTHON_PROC*)&dll_PySys_SetObject}, + {"PySys_SetArgv", (PYTHON_PROC*)&dll_PySys_SetArgv}, + {"PyType_Type", (PYTHON_PROC*)&dll_PyType_Type}, + {"Py_BuildValue", (PYTHON_PROC*)&dll_Py_BuildValue}, + {"Py_FindMethod", (PYTHON_PROC*)&dll_Py_FindMethod}, + {"Py_InitModule4", (PYTHON_PROC*)&dll_Py_InitModule4}, + {"Py_Initialize", (PYTHON_PROC*)&dll_Py_Initialize}, + {"_PyObject_New", (PYTHON_PROC*)&dll__PyObject_New}, + {"PyObject_Init", (PYTHON_PROC*)&dll__PyObject_Init}, + {"_Py_NoneStruct", (PYTHON_PROC*)&dll__Py_NoneStruct}, +# if defined(PY_VERSION_HEX) && PY_VERSION_HEX >= 0x02020000 + {"PyType_IsSubtype", (PYTHON_PROC*)&dll_PyType_IsSubtype}, +# endif +# if defined(PY_VERSION_HEX) && PY_VERSION_HEX >= 0x02030000 + {"PyObject_Malloc", (PYTHON_PROC*)&dll_PyObject_Malloc}, + {"PyObject_Free", (PYTHON_PROC*)&dll_PyObject_Free}, +# endif + {"", NULL}, +}; + +/* + * Free python.dll + */ + static void +end_dynamic_python(void) +{ + if (hinstPython) + { + FreeLibrary(hinstPython); + hinstPython = 0; + } +} + +/* + * Load library and get all pointers. + * Parameter 'libname' provides name of DLL. + * Return OK or FAIL. + */ + static int +python_runtime_link_init(char *libname, int verbose) +{ + int i; + + if (hinstPython) + return OK; + hinstPython = LoadLibrary(libname); + if (!hinstPython) + { + if (verbose) + EMSG2(_(e_loadlib), libname); + return FAIL; + } + + for (i = 0; python_funcname_table[i].ptr; ++i) + { + if ((*python_funcname_table[i].ptr = GetProcAddress(hinstPython, + python_funcname_table[i].name)) == NULL) + { + FreeLibrary(hinstPython); + hinstPython = 0; + if (verbose) + EMSG2(_(e_loadfunc), python_funcname_table[i].name); + return FAIL; + } + } + return OK; +} + +/* + * If python is enabled (there is installed python on Windows system) return + * TRUE, else FALSE. + */ + int +python_enabled(verbose) + int verbose; +{ + return python_runtime_link_init(DYNAMIC_PYTHON_DLL, verbose) == OK; +} + +/* Load the standard Python exceptions - don't import the symbols from the + * DLL, as this can cause errors (importing data symbols is not reliable). + */ +static void get_exceptions __ARGS((void)); + + static void +get_exceptions() +{ + PyObject *exmod = PyImport_ImportModule("exceptions"); + PyObject *exdict = PyModule_GetDict(exmod); + imp_PyExc_AttributeError = PyDict_GetItemString(exdict, "AttributeError"); + imp_PyExc_IndexError = PyDict_GetItemString(exdict, "IndexError"); + imp_PyExc_KeyboardInterrupt = PyDict_GetItemString(exdict, "KeyboardInterrupt"); + imp_PyExc_TypeError = PyDict_GetItemString(exdict, "TypeError"); + imp_PyExc_ValueError = PyDict_GetItemString(exdict, "ValueError"); + Py_XINCREF(imp_PyExc_AttributeError); + Py_XINCREF(imp_PyExc_IndexError); + Py_XINCREF(imp_PyExc_KeyboardInterrupt); + Py_XINCREF(imp_PyExc_TypeError); + Py_XINCREF(imp_PyExc_ValueError); + Py_XDECREF(exmod); +} +#endif /* DYNAMIC_PYTHON */ + +/****************************************************** + * Internal function prototypes. + */ + +static void DoPythonCommand(exarg_T *, const char *); +static int RangeStart; +static int RangeEnd; + +static void PythonIO_Flush(void); +static int PythonIO_Init(void); +static int PythonMod_Init(void); + +/* Utility functions for the vim/python interface + * ---------------------------------------------- + */ +static PyObject *GetBufferLine(buf_T *, int); +static PyObject *GetBufferLineList(buf_T *, int, int); + +static int SetBufferLine(buf_T *, int, PyObject *, int *); +static int SetBufferLineList(buf_T *, int, int, PyObject *, int *); +static int InsertBufferLines(buf_T *, int, PyObject *, int *); + +static PyObject *LineToString(const char *); +static char *StringToLine(PyObject *); + +static int VimErrorCheck(void); + +#define PyErr_SetVim(str) PyErr_SetString(VimError, str) + +/****************************************************** + * 1. Python interpreter main program. + */ + +static int initialised = 0; + +#if PYTHON_API_VERSION < 1007 /* Python 1.4 */ +typedef PyObject PyThreadState; +#endif /* Python 1.4 */ + +#ifndef PY_CAN_RECURSE +static PyThreadState* saved_python_thread = NULL; + +/* + * Suspend a thread of the Python interpreter, other threads are allowed to + * run. + */ +static void Python_SaveThread(void) +{ + saved_python_thread = PyEval_SaveThread(); +} + +/* + * Restore a thread of the Python interpreter, waits for other threads to + * block. + */ +static void Python_RestoreThread(void) +{ + PyEval_RestoreThread(saved_python_thread); + saved_python_thread = NULL; +} +#endif + +/* + * obtain a lock on the Vim data structures + */ +static void Python_Lock_Vim(void) +{ +} + +/* + * release a lock on the Vim data structures + */ +static void Python_Release_Vim(void) +{ +} + + void +python_end() +{ +#ifdef DYNAMIC_PYTHON + end_dynamic_python(); +#endif +} + + static int +Python_Init(void) +{ + if (!initialised) + { +#ifdef DYNAMIC_PYTHON + if (!python_enabled(TRUE)) + { + EMSG(_("E263: Sorry, this command is disabled, the Python library could not be loaded.")); + goto fail; + } +#endif + +#if !defined(MACOS) || defined(MACOS_X_UNIX) + Py_Initialize(); +#else + PyMac_Initialize(); +#endif + /* initialise threads */ + PyEval_InitThreads(); + +#ifdef DYNAMIC_PYTHON + get_exceptions(); +#endif + + if (PythonIO_Init()) + goto fail; + + if (PythonMod_Init()) + goto fail; + +#ifndef PY_CAN_RECURSE + /* the first python thread is vim's */ + Python_SaveThread(); +#endif + + initialised = 1; + } + + return 0; + +fail: + /* We call PythonIO_Flush() here to print any Python errors. + * This is OK, as it is possible to call this function even + * if PythonIO_Init() has not completed successfully (it will + * not do anything in this case). + */ + PythonIO_Flush(); + return -1; +} + +/* + * External interface + */ + static void +DoPythonCommand(exarg_T *eap, const char *cmd) +{ +#ifdef PY_CAN_RECURSE + PyGILState_STATE pygilstate; +#else + static int recursive = 0; +#endif +#if defined(MACOS) && !defined(MACOS_X_UNIX) + GrafPtr oldPort; +#endif +#if defined(HAVE_LOCALE_H) || defined(X_LOCALE) + char *saved_locale; +#endif + +#ifndef PY_CAN_RECURSE + if (recursive) + { + EMSG(_("E659: Cannot invoke Python recursively")); + return; + } + ++recursive; +#endif + +#if defined(MACOS) && !defined(MACOS_X_UNIX) + GetPort(&oldPort); + /* Check if the Python library is available */ + if ((Ptr)PyMac_Initialize == (Ptr)kUnresolvedCFragSymbolAddress) + goto theend; +#endif + if (Python_Init()) + goto theend; + + RangeStart = eap->line1; + RangeEnd = eap->line2; + Python_Release_Vim(); /* leave vim */ + +#if defined(HAVE_LOCALE_H) || defined(X_LOCALE) + /* Python only works properly when the LC_NUMERIC locale is "C". */ + saved_locale = setlocale(LC_NUMERIC, NULL); + if (saved_locale == NULL || STRCMP(saved_locale, "C") == 0) + saved_locale = NULL; + else + { + /* Need to make a copy, value may change when setting new locale. */ + saved_locale = (char *)vim_strsave((char_u *)saved_locale); + (void)setlocale(LC_NUMERIC, "C"); + } +#endif + +#ifdef PY_CAN_RECURSE + pygilstate = PyGILState_Ensure(); +#else + Python_RestoreThread(); /* enter python */ +#endif + + PyRun_SimpleString((char *)(cmd)); + +#ifdef PY_CAN_RECURSE + PyGILState_Release(pygilstate); +#else + Python_SaveThread(); /* leave python */ +#endif + +#if defined(HAVE_LOCALE_H) || defined(X_LOCALE) + if (saved_locale != NULL) + { + (void)setlocale(LC_NUMERIC, saved_locale); + vim_free(saved_locale); + } +#endif + + Python_Lock_Vim(); /* enter vim */ + PythonIO_Flush(); +#if defined(MACOS) && !defined(MACOS_X_UNIX) + SetPort(oldPort); +#endif + +theend: +#ifndef PY_CAN_RECURSE + --recursive; +#endif + return; /* keeps lint happy */ +} + +/* + * ":python" + */ + void +ex_python(exarg_T *eap) +{ + char_u *script; + + script = script_get(eap, eap->arg); + if (!eap->skip) + { + if (script == NULL) + DoPythonCommand(eap, (char *)eap->arg); + else + DoPythonCommand(eap, (char *)script); + } + vim_free(script); +} + +#define BUFFER_SIZE 1024 + +/* + * ":pyfile" + */ + void +ex_pyfile(exarg_T *eap) +{ + static char buffer[BUFFER_SIZE]; + const char *file = (char *)eap->arg; + char *p; + + /* Have to do it like this. PyRun_SimpleFile requires you to pass a + * stdio file pointer, but Vim and the Python DLL are compiled with + * different options under Windows, meaning that stdio pointers aren't + * compatible between the two. Yuk. + * + * Put the string "execfile('file')" into buffer. But, we need to + * escape any backslashes or single quotes in the file name, so that + * Python won't mangle the file name. + */ + strcpy(buffer, "execfile('"); + p = buffer + 10; /* size of "execfile('" */ + + while (*file && p < buffer + (BUFFER_SIZE - 3)) + { + if (*file == '\\' || *file == '\'') + *p++ = '\\'; + *p++ = *file++; + } + + /* If we didn't finish the file name, we hit a buffer overflow */ + if (*file != '\0') + return; + + /* Put in the terminating "')" and a null */ + *p++ = '\''; + *p++ = ')'; + *p++ = '\0'; + + /* Execute the file */ + DoPythonCommand(eap, buffer); +} + +/****************************************************** + * 2. Python output stream: writes output via [e]msg(). + */ + +/* Implementation functions + */ + +static PyObject *OutputGetattr(PyObject *, char *); +static int OutputSetattr(PyObject *, char *, PyObject *); + +static PyObject *OutputWrite(PyObject *, PyObject *); +static PyObject *OutputWritelines(PyObject *, PyObject *); + +typedef void (*writefn)(char_u *); +static void writer(writefn fn, char_u *str, int n); + +/* Output object definition + */ + +typedef struct +{ + PyObject_HEAD + long softspace; + long error; +} OutputObject; + +static struct PyMethodDef OutputMethods[] = { + /* name, function, calling, documentation */ + {"write", OutputWrite, 1, "" }, + {"writelines", OutputWritelines, 1, "" }, + { NULL, NULL, 0, NULL } +}; + +static PyTypeObject OutputType = { + PyObject_HEAD_INIT(0) + 0, + "message", + sizeof(OutputObject), + 0, + + (destructor) 0, + (printfunc) 0, + (getattrfunc) OutputGetattr, + (setattrfunc) OutputSetattr, + (cmpfunc) 0, + (reprfunc) 0, + + 0, /* as number */ + 0, /* as sequence */ + 0, /* as mapping */ + + (hashfunc) 0, + (ternaryfunc) 0, + (reprfunc) 0 +}; + +/*************/ + + static PyObject * +OutputGetattr(PyObject *self, char *name) +{ + if (strcmp(name, "softspace") == 0) + return PyInt_FromLong(((OutputObject *)(self))->softspace); + + return Py_FindMethod(OutputMethods, self, name); +} + + static int +OutputSetattr(PyObject *self, char *name, PyObject *val) +{ + if (val == NULL) { + PyErr_SetString(PyExc_AttributeError, _("can't delete OutputObject attributes")); + return -1; + } + + if (strcmp(name, "softspace") == 0) + { + if (!PyInt_Check(val)) { + PyErr_SetString(PyExc_TypeError, _("softspace must be an integer")); + return -1; + } + + ((OutputObject *)(self))->softspace = PyInt_AsLong(val); + return 0; + } + + PyErr_SetString(PyExc_AttributeError, _("invalid attribute")); + return -1; +} + +/*************/ + + static PyObject * +OutputWrite(PyObject *self, PyObject *args) +{ + int len; + char *str; + int error = ((OutputObject *)(self))->error; + + if (!PyArg_ParseTuple(args, "s#", &str, &len)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + Python_Lock_Vim(); + writer((writefn)(error ? emsg : msg), (char_u *)str, len); + Python_Release_Vim(); + Py_END_ALLOW_THREADS + + Py_INCREF(Py_None); + return Py_None; +} + + static PyObject * +OutputWritelines(PyObject *self, PyObject *args) +{ + int n; + int i; + PyObject *list; + int error = ((OutputObject *)(self))->error; + + if (!PyArg_ParseTuple(args, "O", &list)) + return NULL; + Py_INCREF(list); + + if (!PyList_Check(list)) { + PyErr_SetString(PyExc_TypeError, _("writelines() requires list of strings")); + Py_DECREF(list); + return NULL; + } + + n = PyList_Size(list); + + for (i = 0; i < n; ++i) + { + PyObject *line = PyList_GetItem(list, i); + char *str; + int len; + + if (!PyArg_Parse(line, "s#", &str, &len)) { + PyErr_SetString(PyExc_TypeError, _("writelines() requires list of strings")); + Py_DECREF(list); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + Python_Lock_Vim(); + writer((writefn)(error ? emsg : msg), (char_u *)str, len); + Python_Release_Vim(); + Py_END_ALLOW_THREADS + } + + Py_DECREF(list); + Py_INCREF(Py_None); + return Py_None; +} + +/* Output buffer management + */ + +static char_u *buffer = NULL; +static int buffer_len = 0; +static int buffer_size = 0; + +static writefn old_fn = NULL; + + static void +buffer_ensure(int n) +{ + int new_size; + char_u *new_buffer; + + if (n < buffer_size) + return; + + new_size = buffer_size; + while (new_size < n) + new_size += 80; + + if (new_size != buffer_size) + { + new_buffer = alloc((unsigned)new_size); + if (new_buffer == NULL) + return; + + if (buffer) + { + memcpy(new_buffer, buffer, buffer_len); + vim_free(buffer); + } + + buffer = new_buffer; + buffer_size = new_size; + } +} + + static void +PythonIO_Flush(void) +{ + if (old_fn && buffer_len) + { + buffer[buffer_len] = 0; + old_fn(buffer); + } + + buffer_len = 0; +} + + static void +writer(writefn fn, char_u *str, int n) +{ + char_u *ptr; + + if (fn != old_fn && old_fn != NULL) + PythonIO_Flush(); + + old_fn = fn; + + while (n > 0 && (ptr = memchr(str, '\n', n)) != NULL) + { + int len = ptr - str; + + buffer_ensure(buffer_len + len + 1); + + memcpy(buffer + buffer_len, str, len); + buffer_len += len; + buffer[buffer_len] = 0; + fn(buffer); + str = ptr + 1; + n -= len + 1; + buffer_len = 0; + } + + /* Put the remaining text into the buffer for later printing */ + buffer_ensure(buffer_len + n + 1); + memcpy(buffer + buffer_len, str, n); + buffer_len += n; +} + +/***************/ + +static OutputObject Output = +{ + PyObject_HEAD_INIT(&OutputType) + 0, + 0 +}; + +static OutputObject Error = +{ + PyObject_HEAD_INIT(&OutputType) + 0, + 1 +}; + + static int +PythonIO_Init(void) +{ + /* Fixups... */ + OutputType.ob_type = &PyType_Type; + + PySys_SetObject("stdout", (PyObject *)(&Output)); + PySys_SetObject("stderr", (PyObject *)(&Error)); + + if (PyErr_Occurred()) + { + EMSG(_("E264: Python: Error initialising I/O objects")); + return -1; + } + + return 0; +} + +/****************************************************** + * 3. Implementation of the Vim module for Python + */ + +/* Vim module - Implementation functions + * ------------------------------------- + */ + +static PyObject *VimError; + +static PyObject *VimCommand(PyObject *, PyObject *); +static PyObject *VimEval(PyObject *, PyObject *); + +/* Window type - Implementation functions + * -------------------------------------- + */ + +typedef struct +{ + PyObject_HEAD + win_T *win; +} +WindowObject; + +#define INVALID_WINDOW_VALUE ((win_T *)(-1)) + +#define WindowType_Check(obj) ((obj)->ob_type == &WindowType) + +static PyObject *WindowNew(win_T *); + +static void WindowDestructor(PyObject *); +static PyObject *WindowGetattr(PyObject *, char *); +static int WindowSetattr(PyObject *, char *, PyObject *); +static PyObject *WindowRepr(PyObject *); + +/* Buffer type - Implementation functions + * -------------------------------------- + */ + +typedef struct +{ + PyObject_HEAD + buf_T *buf; +} +BufferObject; + +#define INVALID_BUFFER_VALUE ((buf_T *)(-1)) + +#define BufferType_Check(obj) ((obj)->ob_type == &BufferType) + +static PyObject *BufferNew (buf_T *); + +static void BufferDestructor(PyObject *); +static PyObject *BufferGetattr(PyObject *, char *); +static PyObject *BufferRepr(PyObject *); + +static int BufferLength(PyObject *); +static PyObject *BufferItem(PyObject *, int); +static PyObject *BufferSlice(PyObject *, int, int); +static int BufferAssItem(PyObject *, int, PyObject *); +static int BufferAssSlice(PyObject *, int, int, PyObject *); + +static PyObject *BufferAppend(PyObject *, PyObject *); +static PyObject *BufferMark(PyObject *, PyObject *); +static PyObject *BufferRange(PyObject *, PyObject *); + +/* Line range type - Implementation functions + * -------------------------------------- + */ + +typedef struct +{ + PyObject_HEAD + BufferObject *buf; + int start; + int end; +} +RangeObject; + +#define RangeType_Check(obj) ((obj)->ob_type == &RangeType) + +static PyObject *RangeNew(buf_T *, int, int); + +static void RangeDestructor(PyObject *); +static PyObject *RangeGetattr(PyObject *, char *); +static PyObject *RangeRepr(PyObject *); + +static int RangeLength(PyObject *); +static PyObject *RangeItem(PyObject *, int); +static PyObject *RangeSlice(PyObject *, int, int); +static int RangeAssItem(PyObject *, int, PyObject *); +static int RangeAssSlice(PyObject *, int, int, PyObject *); + +static PyObject *RangeAppend(PyObject *, PyObject *); + +/* Window list type - Implementation functions + * ------------------------------------------- + */ + +static int WinListLength(PyObject *); +static PyObject *WinListItem(PyObject *, int); + +/* Buffer list type - Implementation functions + * ------------------------------------------- + */ + +static int BufListLength(PyObject *); +static PyObject *BufListItem(PyObject *, int); + +/* Current objects type - Implementation functions + * ----------------------------------------------- + */ + +static PyObject *CurrentGetattr(PyObject *, char *); +static int CurrentSetattr(PyObject *, char *, PyObject *); + +/* Vim module - Definitions + */ + +static struct PyMethodDef VimMethods[] = { + /* name, function, calling, documentation */ + {"command", VimCommand, 1, "" }, + {"eval", VimEval, 1, "" }, + { NULL, NULL, 0, NULL } +}; + +/* Vim module - Implementation + */ +/*ARGSUSED*/ + static PyObject * +VimCommand(PyObject *self, PyObject *args) +{ + char *cmd; + PyObject *result; + + if (!PyArg_ParseTuple(args, "s", &cmd)) + return NULL; + + PyErr_Clear(); + + Py_BEGIN_ALLOW_THREADS + Python_Lock_Vim(); + + do_cmdline_cmd((char_u *)cmd); + update_screen(VALID); + + Python_Release_Vim(); + Py_END_ALLOW_THREADS + + if (VimErrorCheck()) + result = NULL; + else + result = Py_None; + + Py_XINCREF(result); + return result; +} + +/*ARGSUSED*/ + static PyObject * +VimEval(PyObject *self, PyObject *args) +{ +#ifdef FEAT_EVAL + char *expr; + char *str; + PyObject *result; + + if (!PyArg_ParseTuple(args, "s", &expr)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + Python_Lock_Vim(); + str = (char *)eval_to_string((char_u *)expr, NULL); + Python_Release_Vim(); + Py_END_ALLOW_THREADS + + if (str == NULL) + { + PyErr_SetVim(_("invalid expression")); + return NULL; + } + + result = Py_BuildValue("s", str); + + Py_BEGIN_ALLOW_THREADS + Python_Lock_Vim(); + vim_free(str); + Python_Release_Vim(); + Py_END_ALLOW_THREADS + + return result; +#else + PyErr_SetVim(_("expressions disabled at compile time")); + return NULL; +#endif +} + +/* Common routines for buffers and line ranges + * ------------------------------------------- + */ + static int +CheckBuffer(BufferObject *this) +{ + if (this->buf == INVALID_BUFFER_VALUE) + { + PyErr_SetVim(_("attempt to refer to deleted buffer")); + return -1; + } + + return 0; +} + + static PyObject * +RBItem(BufferObject *self, int n, int start, int end) +{ + if (CheckBuffer(self)) + return NULL; + + if (n < 0 || n > end - start) + { + PyErr_SetString(PyExc_IndexError, _("line number out of range")); + return NULL; + } + + return GetBufferLine(self->buf, n+start); +} + + static PyObject * +RBSlice(BufferObject *self, int lo, int hi, int start, int end) +{ + int size; + + if (CheckBuffer(self)) + return NULL; + + size = end - start + 1; + + if (lo < 0) + lo = 0; + else if (lo > size) + lo = size; + if (hi < 0) + hi = 0; + if (hi < lo) + hi = lo; + else if (hi > size) + hi = size; + + return GetBufferLineList(self->buf, lo+start, hi+start); +} + + static int +RBAssItem(BufferObject *self, int n, PyObject *val, int start, int end, int *new_end) +{ + int len_change; + + if (CheckBuffer(self)) + return -1; + + if (n < 0 || n > end - start) + { + PyErr_SetString(PyExc_IndexError, _("line number out of range")); + return -1; + } + + if (SetBufferLine(self->buf, n+start, val, &len_change) == FAIL) + return -1; + + if (new_end) + *new_end = end + len_change; + + return 0; +} + + static int +RBAssSlice(BufferObject *self, int lo, int hi, PyObject *val, int start, int end, int *new_end) +{ + int size; + int len_change; + + /* Self must be a valid buffer */ + if (CheckBuffer(self)) + return -1; + + /* Sort out the slice range */ + size = end - start + 1; + + if (lo < 0) + lo = 0; + else if (lo > size) + lo = size; + if (hi < 0) + hi = 0; + if (hi < lo) + hi = lo; + else if (hi > size) + hi = size; + + if (SetBufferLineList(self->buf, lo+start, hi+start, val, &len_change) == FAIL) + return -1; + + if (new_end) + *new_end = end + len_change; + + return 0; +} + + static PyObject * +RBAppend(BufferObject *self, PyObject *args, int start, int end, int *new_end) +{ + PyObject *lines; + int len_change; + int max; + int n; + + if (CheckBuffer(self)) + return NULL; + + max = n = end - start + 1; + + if (!PyArg_ParseTuple(args, "O|i", &lines, &n)) + return NULL; + + if (n < 0 || n > max) + { + PyErr_SetString(PyExc_ValueError, _("line number out of range")); + return NULL; + } + + if (InsertBufferLines(self->buf, n + start - 1, lines, &len_change) == FAIL) + return NULL; + + if (new_end) + *new_end = end + len_change; + + Py_INCREF(Py_None); + return Py_None; +} + + +/* Buffer object - Definitions + */ + +static struct PyMethodDef BufferMethods[] = { + /* name, fu |