This is if_python.c in view mode; [Download] [Up]
/* vi:set ts=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.
*/
/*
* Python extensions by Paul Moore.
*
* 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 <stdio.h>
#include <stdarg.h>
#include <limits.h>
#include "python.h"
#undef main /* Defined in python.h - aargh */
#undef HAVE_FCNTL_H /* Clash with os_win32.h */
/* Parser flags */
#define single_input 256
#define file_input 257
#define eval_input 258
#include "vim.h"
#include "globals.h"
#include "proto.h"
/*************************************************************************
* Internal function prototypes.
*************************************************************************/
static int DoPythonCmd (EXARG *, 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 *, int);
static PyObject *GetBufferLineList (BUF *, int, int);
static int SetBufferLine (BUF *, int, PyObject *, int *);
static int SetBufferLineList (BUF *, int, int, PyObject *, int *);
static int InsertBufferLines (BUF *, int, PyObject *, int *);
static char *VimArrayToString (char **, int, int *);
static int MemToVimArray (char *, int);
static void PyErr_SetVim (const char *, ...);
static char *VimStringAlloc (PyObject *);
/*************************************************************************
* 1. Python interpreter main program.
*************************************************************************/
static int initialised = 0;
static int Python_Init (void)
{
if (!initialised)
{
Py_Initialize();
if (PythonIO_Init())
goto fail;
if (PythonMod_Init())
goto fail;
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 int DoPythonCommand (EXARG *eap, const char *cmd)
{
if (Python_Init())
return FAIL;
RangeStart = eap->line1;
RangeEnd = eap->line2;
PyRun_SimpleString ((char *)(cmd));
PythonIO_Flush ();
return OK;
}
int do_python (EXARG *eap)
{
return DoPythonCommand (eap, eap->arg);
}
#define BUFFER_SIZE 1024
int do_pyfile (EXARG *eap)
{
static char buffer[BUFFER_SIZE];
const char *file = 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 filename, so that
* Python won't mangle the filename.
*/
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 filename, we hit a buffer overflow */
if (*file != '\0')
return FAIL;
/* Put in the terminating "')" and a null */
*p++ = '\'';
*p++ = ')';
*p++ = '\0';
/* Execute the file */
return 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,
"OutputObject",
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,
(binaryfunc) 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;
writer ((error ? emsg : msg), str, len);
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;
}
writer ((error ? emsg : msg), str, len);
}
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 = malloc (new_size);
if (new_buffer == NULL)
{
emsg("Out of memory!");
return;
}
if (buffer)
{
memcpy (new_buffer, buffer, buffer_len);
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("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 int VimInit (void);
static PyObject *VimCommand (PyObject *, PyObject *);
static PyObject *VimRegister (PyObject *, PyObject *);
static PyObject *VimSetRegister (PyObject *, PyObject *);
/* Window type - Implementation functions
* --------------------------------------
*/
typedef struct
{
PyObject_HEAD
WIN *win;
}
WindowObject;
#define INVALID_WINDOW_VALUE ((WIN*)(-1))
static PyTypeObject WindowType;
#define WindowType_Check(obj) ((obj)->ob_type == &WindowType)
static PyObject *WindowNew (WIN *);
static void WindowDestructor (PyObject *);
static PyObject *WindowGetattr (PyObject *, char *);
static int WindowSetattr (PyObject *, char *, PyObject *);
/* Buffer type - Implementation functions
* --------------------------------------
*/
typedef struct
{
PyObject_HEAD
BUF *buf;
}
BufferObject;
#define INVALID_BUFFER_VALUE ((BUF*)(-1))
static PyTypeObject BufferType;
#define BufferType_Check(obj) ((obj)->ob_type == &BufferType)
static PyObject *BufferNew (BUF *);
static void BufferDestructor (PyObject *);
static PyObject *BufferGetattr (PyObject *, char *);
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;
static PyTypeObject RangeType;
#define RangeType_Check(obj) ((obj)->ob_type == &RangeType)
static PyObject *RangeNew (BUF *, int, int);
static void RangeDestructor (PyObject *);
static PyObject *RangeGetattr (PyObject *, char *);
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, "" },
{"register", VimRegister, 1, "" },
{"set_register", VimSetRegister, 1, "" },
{ NULL, NULL, 0, NULL }
};
/* Vim module - Implementation
*/
static PyObject *VimCommand (PyObject *self, PyObject *args)
{
char *cmd;
if (!PyArg_ParseTuple(args, "s", &cmd))
return NULL;
do_cmdline (cmd, NULL, NULL, DOCMD_NOWAIT|DOCMD_VERBOSE);
update_screen(NOT_VALID);
if (got_int)
{
PyErr_SetNone (PyExc_KeyboardInterrupt);
return NULL;
}
else if (did_emsg)
{
PyErr_SetVim ("command failed");
return NULL;
}
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *VimRegister (PyObject *self, PyObject *args)
{
int n;
int size;
char reg;
char *str;
int linewise;
char **array;
PyObject *result;
if (!PyArg_ParseTuple(args, "c", ®))
return NULL;
n = get_register (reg, &array, &linewise);
if (n == -1)
{
PyErr_SetVim ("invalid register '%c'", reg);
return NULL;
}
/* Empty register - return None */
if (n == 0)
{
Py_INCREF(Py_None);
return Py_None;
}
str = VimArrayToString (array, n, &size);
if (str == NULL)
return PyErr_NoMemory();
/* Omit the terminating newline if the register is not linewise */
if (!linewise)
--size;
result = PyString_FromStringAndSize (str, size);
free (str);
return result;
}
static PyObject *VimSetRegister (PyObject *self, PyObject *args)
{
char reg;
char *str = NULL;
int len = 0;
int linewise = -1;
int i;
int n;
char *p;
int result;
char *copy;
char **array;
if (!PyArg_ParseTuple(args, "cs#|i", ®, &str, &len, &linewise))
return NULL;
/* If linewise was not specified, make the register linewise only if
* the string had a final newline.
*/
if (linewise == -1)
{
linewise = (str[len-1] == '\n' ? 1 : 0);
}
/* Copy the string, as we are going to mess with it.
* Add a terminating newline, to normalise what goes on below.
*/
copy = alloc(len+1);
if (copy == NULL)
return PyErr_NoMemory();
memcpy (copy, str, len);
copy[len] = '\n';
++len;
/* Convert the string to a Vim array (lines separated by nulls, and
* embedded nulls replaced with \n characters
*/
n = MemToVimArray(copy, len);
/* Allocate an array of pointers, and fill it */
array = (char_u **) alloc (n * sizeof(char_u *));
if (array == NULL)
{
vim_free (copy);
return PyErr_NoMemory();
}
p = copy;
for (i = 0; i < n; ++i)
{
array[i] = p;
p = p + strlen(p) + 1;
}
result = set_register (reg, array, n, linewise);
vim_free (array);
vim_free (copy);
if (result == FAIL)
{
PyErr_SetVim ("cannot set register '%c'", reg);
return NULL;
}
Py_INCREF(Py_None);
return Py_None;
}
/* 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 ret = 0;
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, function, calling, documentation */
{"append", BufferAppend, 1, "" },
{"mark", BufferMark, 1, "" },
{"range", BufferRange, 1, "" },
{ NULL, NULL, 0, NULL }
};
static PySequenceMethods BufferAsSeq = {
(inquiry) BufferLength, /* sq_length, len(x) */
(binaryfunc) 0, /* BufferConcat, */ /* sq_concat, x+y */
(intargfunc) 0, /* BufferRepeat, */ /* sq_repeat, x*n */
(intargfunc) BufferItem, /* sq_item, x[i] */
(intintargfunc) BufferSlice, /* sq_slice, x[i:j] */
(intobjargproc) BufferAssItem, /* sq_ass_item, x[i]=v */
(intintobjargproc) BufferAssSlice, /* sq_ass_slice, x[i:j]=v */
};
static PyTypeObject BufferType = {
PyObject_HEAD_INIT(0)
0,
"BufferObject",
sizeof(BufferObject),
0,
(destructor) BufferDestructor, /* tp_dealloc, refcount==0 */
(printfunc) 0, /* tp_print, print x */
(getattrfunc) BufferGetattr, /* tp_getattr, x.attr */
(setattrfunc) 0, /* tp_setattr, x.attr=v */
(cmpfunc) 0, /* tp_compare, x>y */
(reprfunc) 0, /* tp_repr, `x`, print x */
0, /* as number */
&BufferAsSeq, /* as sequence */
0, /* as mapping */
(hashfunc) 0, /* tp_hash, dict(x) */
(binaryfunc) 0, /* tp_call, x() */
(reprfunc) 0, /* tp_str, str(x) */
};
/* Buffer object - Implementation
*/
static PyObject *BufferNew (BUF *buf)
{
/* We need to handle deletion of buffers underneath us.
* If we add a "python_ref" field to the BUF structure,
* then we can get at it in buf_freeall() in vim. We then
* need to create only ONE Python object per buffer - if
* we try to create a second, just INCREF the existing one
* and return it. The (single) Python object referring to
* the buffer is stored in "python_ref".
* Question: what to do on a buf_freeall(). We'll probably
* have to either delete the Python object (DECREF it to
* zero - a bad idea, as it leaves dangling refs!) or
* set the BUF* value to an invalid value (-1?), which
* means we need checks in all access functions... Bah.
*/
BufferObject *self;
if (buf->python_ref)
self = buf->python_ref;
else
{
self = PyObject_NEW (BufferObject, &BufferType);
if (self == NULL)
return NULL;
self->buf = buf;
buf->python_ref = self;
}
return (PyObject *)(self);
}
static void BufferDestructor (PyObject *self)
{
BufferObject *this = (BufferObject *)(self);
if (this->buf && this->buf != INVALID_BUFFER_VALUE)
this->buf->python_ref = NULL;
PyMem_DEL (self);
}
static PyObject *BufferGetattr (PyObject *self, char *name)
{
BufferObject *this = (BufferObject *)(self);
if (CheckBuffer(this))
return NULL;
if (strcmp(name, "name") == 0)
return Py_BuildValue("s",this->buf->b_ffname);
else if (strcmp(name,"__members__") == 0)
return Py_BuildValue("[s]", "name");
else
return Py_FindMethod (BufferMethods, self, name);
}
/**********************************************************************/
static int BufferLength (PyObject *self)
{
/* HOW DO WE SIGNAL AN ERROR FROM THIS FUNCTION? */
if (CheckBuffer((BufferObject *)(self)))
return -1; /* ??? */
return (((BufferObject *)(self))->buf->b_ml.ml_line_count);
}
static PyObject *BufferItem (PyObject *self, int n)
{
return RBItem((BufferObject *)(self), n, 1,
((BufferObject *)(self))->buf->b_ml.ml_line_count);
}
static PyObject *BufferSlice (PyObject *self, int lo, int hi)
{
return RBSlice((BufferObject *)(self), lo, hi, 1,
((BufferObject *)(self))->buf->b_ml.ml_line_count);
}
static int BufferAssItem (PyObject *self, int n, PyObject *val)
{
return RBAssItem((BufferObject *)(self), n, val, 1,
((BufferObject *)(self))->buf->b_ml.ml_line_count,
NULL);
}
static int BufferAssSlice (PyObject *self, int lo, int hi, PyObject *val)
{
return RBAssSlice((BufferObject *)(self), lo, hi, val, 1,
((BufferObject *)(self))->buf->b_ml.ml_line_count,
NULL);
}
static PyObject *BufferAppend (PyObject *self, PyObject *args)
{
return RBAppend((BufferObject *)(self), args, 1,
((BufferObject *)(self))->buf->b_ml.ml_line_count,
NULL);
}
static PyObject *BufferMark (PyObject *self, PyObject *args)
{
FPOS pos;
char mark;
if (CheckBuffer((BufferObject *)(self)))
return NULL;
if (!PyArg_ParseTuple(args, "c", &mark))
return NULL;
if (islower(mark))
pos = ((BufferObject *)(self))->buf->b_namedm[mark-'a'];
else if (mark == '[')
pos = ((BufferObject *)(self))->buf->b_op_start;
else if (mark == ']')
pos = ((BufferObject *)(self))->buf->b_op_end;
else if (mark == '"')
pos = ((BufferObject *)(self))->buf->b_last_cursor;
else if (mark == '<' || mark == '>')
{
FPOS *start, *end;
if (VIsual_active)
start = &VIsual_save;
else
start = &VIsual;
end = &VIsual_end;
pos = ((mark == '<') == lt(*start, *end) ? *start : *end);
}
else
{
PyErr_SetVim ("invalid mark name '%c'", mark);
return NULL;
}
if (pos.lnum == 0)
{
/* Or raise an error? */
Py_INCREF(Py_None);
return Py_None;
}
return Py_BuildValue("(ll)", (long)(pos.lnum), (long)(pos.col));
}
static PyObject *BufferRange (PyObject *self, PyObject *args)
{
int start;
int end;
if (CheckBuffer((BufferObject *)(self)))
return NULL;
if (!PyArg_ParseTuple(args, "ii", &start, &end))
return NULL;
return RangeNew(((BufferObject *)(self))->buf, start, end);
}
/* Line range object - Definitions
*/
static struct PyMethodDef RangeMethods[] = {
/* name, function, calling, documentation */
{"append", RangeAppend, 1, "" },
{ NULL, NULL, 0, NULL }
};
static PySequenceMethods RangeAsSeq = {
(inquiry) RangeLength, /* sq_length, len(x) */
(binaryfunc) 0, /* BufferConcat, */ /* sq_concat, x+y */
(intargfunc) 0, /* BufferRepeat, */ /* sq_repeat, x*n */
(intargfunc) RangeItem, /* sq_item, x[i] */
(intintargfunc) RangeSlice, /* sq_slice, x[i:j] */
(intobjargproc) RangeAssItem, /* sq_ass_item, x[i]=v */
(intintobjargproc) RangeAssSlice, /* sq_ass_slice, x[i:j]=v */
};
static PyTypeObject RangeType = {
PyObject_HEAD_INIT(0)
0,
"RangeObject",
sizeof(RangeObject),
0,
(destructor) RangeDestructor, /* tp_dealloc, refcount==0 */
(printfunc) 0, /* tp_print, print x */
(getattrfunc) RangeGetattr, /* tp_getattr, x.attr */
(setattrfunc) 0, /* tp_setattr, x.attr=v */
(cmpfunc) 0, /* tp_compare, x>y */
(reprfunc) 0, /* tp_repr, `x`, print x */
0, /* as number */
&RangeAsSeq, /* as sequence */
0, /* as mapping */
(hashfunc) 0, /* tp_hash, dict(x) */
(binaryfunc) 0, /* tp_call, x() */
(reprfunc) 0, /* tp_str, str(x) */
};
/* Line range object - Implementation
*/
static PyObject *RangeNew (BUF *buf, int start, int end)
{
BufferObject *buffer;
RangeObject *self;
self = PyObject_NEW (RangeObject, &RangeType);
if (self == NULL)
return NULL;
buffer = (BufferObject *)BufferNew(buf);
if (buffer == NULL)
{
PyMem_DEL(self);
return NULL;
}
Py_INCREF(buffer);
self->buf = buffer;
self->start = start;
self->end = end;
return (PyObject *)(self);
}
static void RangeDestructor (PyObject *self)
{
Py_DECREF(((RangeObject *)(self))->buf);
PyMem_DEL (self);
}
static PyObject *RangeGetattr (PyObject *self, char *name)
{
return Py_FindMethod (RangeMethods, self, name);
}
/**********************************************************************/
static int RangeLength (PyObject *self)
{
/* HOW DO WE SIGNAL AN ERROR FROM THIS FUNCTION? */
if (CheckBuffer(((RangeObject *)(self))->buf))
return -1; /* ??? */
return (((RangeObject *)(self))->end - ((RangeObject *)(self))->start + 1);
}
static PyObject *RangeItem (PyObject *self, int n)
{
return RBItem(((RangeObject *)(self))->buf, n,
((RangeObject *)(self))->start,
((RangeObject *)(self))->end);
}
static PyObject *RangeSlice (PyObject *self, int lo, int hi)
{
return RBSlice(((RangeObject *)(self))->buf, lo, hi,
((RangeObject *)(self))->start,
((RangeObject *)(self))->end);
}
static int RangeAssItem (PyObject *self, int n, PyObject *val)
{
return RBAssItem(((RangeObject *)(self))->buf, n, val,
((RangeObject *)(self))->start,
((RangeObject *)(self))->end,
&((RangeObject *)(self))->end);
}
static int RangeAssSlice (PyObject *self, int lo, int hi, PyObject *val)
{
return RBAssSlice(((RangeObject *)(self))->buf, lo, hi, val,
((RangeObject *)(self))->start,
((RangeObject *)(self))->end,
&((RangeObject *)(self))->end);
}
static PyObject *RangeAppend (PyObject *self, PyObject *args)
{
return RBAppend(((RangeObject *)(self))->buf, args,
((RangeObject *)(self))->start,
((RangeObject *)(self))->end,
&((RangeObject *)(self))->end);
}
/* Buffer list object - Definitions
*/
typedef struct
{
PyObject_HEAD
}
BufListObject;
static PySequenceMethods BufListAsSeq = {
(inquiry) BufListLength, /* sq_length, len(x) */
(binaryfunc) 0, /* sq_concat, x+y */
(intargfunc) 0, /* sq_repeat, x*n */
(intargfunc) BufListItem, /* sq_item, x[i] */
(intintargfunc) 0, /* sq_slice, x[i:j] */
(intobjargproc) 0, /* sq_ass_item, x[i]=v */
(intintobjargproc) 0, /* sq_ass_slice, x[i:j]=v */
};
static PyTypeObject BufListType = {
PyObject_HEAD_INIT(0)
0,
"BufListObject",
sizeof(BufListObject),
0,
(destructor) 0, /* tp_dealloc, refcount==0 */
(printfunc) 0, /* tp_print, print x */
(getattrfunc) 0, /* tp_getattr, x.attr */
(setattrfunc) 0, /* tp_setattr, x.attr=v */
(cmpfunc) 0, /* tp_compare, x>y */
(reprfunc) 0, /* tp_repr, `x`, print x */
0, /* as number */
&BufListAsSeq, /* as sequence */
0, /* as mapping */
(hashfunc) 0, /* tp_hash, dict(x) */
(binaryfunc) 0, /* tp_call, x() */
(reprfunc) 0, /* tp_str, str(x) */
};
/* Buffer list object - Implementation
*/
static int BufListLength (PyObject *self)
{
BUF *b = firstbuf;
int n = 0;
while (b)
{
++n;
b = b->b_next;
}
return n;
}
static PyObject *BufListItem (PyObject *self, int n)
{
BUF *b;
for (b = firstbuf; b; b = b->b_next, --n)
{
if (n == 0)
return BufferNew(b);
}
PyErr_SetString(PyExc_IndexError, "no such buffer");
return NULL;
}
/* Window object - Definitions
*/
static struct PyMethodDef WindowMethods[] = {
/* name, function, calling, documentation */
{ NULL, NULL, 0, NULL }
};
static PyTypeObject WindowType = {
PyObject_HEAD_INIT(0)
0,
"WindowObject",
sizeof(WindowObject),
0,
(destructor) WindowDestructor, /* tp_dealloc, refcount==0 */
(printfunc) 0, /* tp_print, print x */
(getattrfunc) WindowGetattr, /* tp_getattr, x.attr */
(setattrfunc) WindowSetattr, /* tp_setattr, x.attr=v */
(cmpfunc) 0, /* tp_compare, x>y */
(reprfunc) 0, /* tp_repr, `x`, print x */
0, /* as number */
0, /* as sequence */
0, /* as mapping */
(hashfunc) 0, /* tp_hash, dict(x) */
(binaryfunc) 0, /* tp_call, x() */
(reprfunc) 0, /* tp_str, str(x) */
};
/* Window object - Implementation
*/
static PyObject *WindowNew (WIN *win)
{
/* We need to handle deletion of windows underneath us.
* If we add a "python_ref" field to the WIN structure,
* then we can get at it in win_free() in vim. We then
* need to create only ONE Python object per window - if
* we try to create a second, just INCREF the existing one
* and return it. The (single) Python object referring to
* the window is stored in "python_ref".
* On a win_free() we set the Python object's WIN* field
* to an invalid value. We trap all uses of a window
* object, and reject them if the WIN* field is invalid.
*/
WindowObject *self;
if (win->python_ref)
self = win->python_ref;
else
{
self = PyObject_NEW (WindowObject, &WindowType);
if (self == NULL)
return NULL;
self->win = win;
win->python_ref = self;
}
return (PyObject *)(self);
}
static void WindowDestructor (PyObject *self)
{
WindowObject *this = (WindowObject *)(self);
if (this->win && this->win != INVALID_WINDOW_VALUE)
this->win->python_ref = NULL;
PyMem_DEL (self);
}
static int CheckWindow (WindowObject *this)
{
if (this->win == INVALID_WINDOW_VALUE)
{
PyErr_SetVim ("attempt to refer to deleted window");
return -1;
}
return 0;
}
static PyObject *WindowGetattr (PyObject *self, char *name)
{
WindowObject *this = (WindowObject *)(self);
if (CheckWindow(this))
return NULL;
if (strcmp(name, "buffer") == 0)
return (PyObject *)BufferNew(this->win->w_buffer);
else if (strcmp (name, "cursor") == 0)
{
FPOS *pos = &this->win->w_cursor;
return Py_BuildValue("(ll)", (long)(pos->lnum), (long)(pos->col));
}
else if (strcmp (name, "height") == 0)
return Py_BuildValue("l", (long)(this->win->w_height));
else if (strcmp(name,"__members__") == 0)
return Py_BuildValue("[sss]", "buffer", "cursor", "height");
else
return Py_FindMethod (WindowMethods, self, name);
}
static int WindowSetattr (PyObject *self, char *name, PyObject *val)
{
WindowObject *this = (WindowObject *)(self);
if (CheckWindow(this))
return -1;
if (strcmp(name, "buffer") == 0)
{
PyErr_SetString(PyExc_TypeError, "readonly attribute");
return -1;
}
else if (strcmp (name, "cursor") == 0)
{
long lnum;
long col;
if (!PyArg_Parse(val, "(ll)", &lnum, &col))
return -1;
if (lnum <= 0 || lnum > this->win->w_buffer->b_ml.ml_line_count)
{
PyErr_SetVim ("cursor position outside buffer");
return -1;
}
/* NO CHECK ON COLUMN - SEEMS NOT TO MATTER */
this->win->w_cursor.lnum = lnum;
this->win->w_cursor.col = col;
update_screen(NOT_VALID);
return 0;
}
else if (strcmp (name, "height") == 0)
{
int height;
WIN *savewin;
if (!PyArg_Parse(val, "i", &height))
return -1;
savewin = curwin;
curwin = this->win;
win_setheight (height);
curwin = savewin;
return 0;
}
else
{
PyErr_SetString(PyExc_AttributeError, name);
return -1;
}
}
/* Window list object - Definitions
*/
typedef struct
{
PyObject_HEAD
}
WinListObject;
static PySequenceMethods WinListAsSeq = {
(inquiry) WinListLength, /* sq_length, len(x) */
(binaryfunc) 0, /* sq_concat, x+y */
(intargfunc) 0, /* sq_repeat, x*n */
(intargfunc) WinListItem, /* sq_item, x[i] */
(intintargfunc) 0, /* sq_slice, x[i:j] */
(intobjargproc) 0, /* sq_ass_item, x[i]=v */
(intintobjargproc) 0, /* sq_ass_slice, x[i:j]=v */
};
static PyTypeObject WinListType = {
PyObject_HEAD_INIT(0)
0,
"WinListObject",
sizeof(WinListObject),
0,
(destructor) 0, /* tp_dealloc, refcount==0 */
(printfunc) 0, /* tp_print, print x */
(getattrfunc) 0, /* tp_getattr, x.attr */
(setattrfunc) 0, /* tp_setattr, x.attr=v */
(cmpfunc) 0, /* tp_compare, x>y */
(reprfunc) 0, /* tp_repr, `x`, print x */
0, /* as number */
&WinListAsSeq, /* as sequence */
0, /* as mapping */
(hashfunc) 0, /* tp_hash, dict(x) */
(binaryfunc) 0, /* tp_call, x() */
(reprfunc) 0, /* tp_str, str(x) */
};
/* Window list object - Implementation
*/
static int WinListLength (PyObject *self)
{
WIN *w = firstwin;
int n = 0;
while (w)
{
++n;
w = w->w_next;
}
return n;
}
static PyObject *WinListItem (PyObject *self, int n)
{
WIN *w;
for (w = firstwin; w; w = w->w_next, --n)
{
if (n == 0)
return WindowNew(w);
}
PyErr_SetString(PyExc_IndexError, "no such window");
return NULL;
}
/* Current items object - Definitions
*/
typedef struct
{
PyObject_HEAD
}
CurrentObject;
static PyTypeObject CurrentType = {
PyObject_HEAD_INIT(0)
0,
"CurrentObject",
sizeof(CurrentObject),
0,
(destructor) 0, /* tp_dealloc, refcount==0 */
(printfunc) 0, /* tp_print, print x */
(getattrfunc) CurrentGetattr, /* tp_getattr, x.attr */
(setattrfunc) CurrentSetattr, /* tp_setattr, x.attr=v */
(cmpfunc) 0, /* tp_compare, x>y */
(reprfunc) 0, /* tp_repr, `x`, print x */
0, /* as number */
0, /* as sequence */
0, /* as mapping */
(hashfunc) 0, /* tp_hash, dict(x) */
(binaryfunc) 0, /* tp_call, x() */
(reprfunc) 0, /* tp_str, str(x) */
};
/* Current items object - Implementation
*/
static PyObject *CurrentGetattr (PyObject *self, char *name)
{
if (strcmp(name, "buffer") == 0)
return (PyObject *)BufferNew(curbuf);
else if (strcmp (name, "window") == 0)
return (PyObject *)WindowNew(curwin);
else if (strcmp (name, "line") == 0)
return GetBufferLine (curbuf, curwin->w_cursor.lnum);
else if (strcmp (name, "range") == 0)
return RangeNew (curbuf, RangeStart, RangeEnd);
else if (strcmp(name,"__members__") == 0)
return Py_BuildValue("[ssss]", "buffer", "window", "line", "range");
else
{
PyErr_SetString(PyExc_AttributeError, name);
return NULL;
}
}
static int CurrentSetattr (PyObject *self, char *name, PyObject *value)
{
if (strcmp(name, "buffer") == 0)
{
PyErr_SetString(PyExc_TypeError, "readonly attribute");
return -1;
}
else if (strcmp (name, "window") == 0)
{
PyErr_SetString(PyExc_TypeError, "readonly attribute");
return -1;
}
else if (strcmp (name, "line") == 0)
{
if (SetBufferLine(curbuf, curwin->w_cursor.lnum, value, NULL) == FAIL)
return -1;
return 0;
}
else if (strcmp (name, "range") == 0)
{
PyErr_SetString (PyExc_AttributeError, "not yet implemented");
return -1;
}
else
{
PyErr_SetString(PyExc_AttributeError, name);
return -1;
}
}
/* External interface
*/
void python_buffer_free (BUF *buf)
{
if (buf->python_ref)
{
BufferObject *bp = buf->python_ref;
bp->buf = INVALID_BUFFER_VALUE;
buf->python_ref = NULL;
}
}
void python_window_free (WIN *win)
{
if (win->python_ref)
{
WindowObject *wp = win->python_ref;
wp->win = INVALID_WINDOW_VALUE;
win->python_ref = NULL;
}
}
static BufListObject TheBufferList =
{
PyObject_HEAD_INIT(&BufListType)
};
static WinListObject TheWindowList =
{
PyObject_HEAD_INIT(&WinListType)
};
static CurrentObject TheCurrent =
{
PyObject_HEAD_INIT(&CurrentType)
};
static int VimInit (void)
{
PyObject *mod;
PyObject *dict;
mod = Py_InitModule ("vim", VimMethods);
dict = PyModule_GetDict (mod);
VimError = Py_BuildValue ("s", "vim.error");
PyDict_SetItemString (dict, "error", VimError);
PyDict_SetItemString (dict, "buffers", (PyObject *)(&TheBufferList));
PyDict_SetItemString (dict, "current", (PyObject *)(&TheCurrent));
PyDict_SetItemString (dict, "windows", (PyObject *)(&TheWindowList));
if (PyErr_Occurred())
return -1;
return 0;
}
static int PythonMod_Init (void)
{
// Fixups...
BufferType.ob_type = &PyType_Type;
RangeType.ob_type = &PyType_Type;
WindowType.ob_type = &PyType_Type;
BufListType.ob_type = &PyType_Type;
WinListType.ob_type = &PyType_Type;
CurrentType.ob_type = &PyType_Type;
return VimInit();
}
/*************************************************************************
* 4. Utility functions for handling the interface between Vim and Python.
*************************************************************************/
/* Get a line from the specified buffer. The line number is
* in Vim format (1-based). The line is returned as a Python
* string object.
*/
static PyObject *GetBufferLine (BUF *buf, int n)
{
char_u *line = vim_strsave(ml_get_buf (buf, n, FALSE));
int len = strlen(line);
PyObject *obj;
char_u *p;
/* Convert embedded '\n' characters to nulls */
for (p = memchr(line, '\n', len); p; p = memchr(p, '\n', len-(p-line)))
*p++ = '\0';
/* Create a Python string object */
obj = PyString_FromStringAndSize (line, len);
/* Free the temporary memory */
vim_free (line);
/* Return the object. If a Python error occurred, this will be a
* NULL pointer. It is the caller's responsibility to check for
* this and react appropriately.
*
* The ownership of the Python object is passed to the caller (ie,
* the caller should Py_DECREF() the object when it is finished
* with it).
*/
return obj;
}
/* Get a list of lines from the specified buffer. The line numbers
* are in Vim format (1-based). The range is from lo up to, but not
* including, hi. The list is returned as a Python list of string objects.
*/
static PyObject *GetBufferLineList (BUF *buf, int lo, int hi)
{
int i;
int n = hi - lo;
PyObject *list = PyList_New (n);
if (list == NULL)
return NULL;
for (i = 0; i < n; ++i)
{
char_u *line = vim_strsave(ml_get_buf (buf, lo + i, FALSE));
int len = strlen(line);
PyObject *str;
char_u *p;
/* Convert embedded '\n' characters to nulls */
for (p = memchr(line, '\n', len); p; p = memchr(p, '\n', len-(p-line)))
*p++ = '\0';
/* Create a Python string object */
str = PyString_FromStringAndSize (line, len);
/* Free the temporary memory */
vim_free (line);
/* Error check - was the Python string creation OK? */
if (str == NULL)
{
Py_DECREF(list);
return NULL;
}
/* Set the list item */
if (PyList_SetItem (list, i, str))
{
Py_DECREF(str);
Py_DECREF(list);
return NULL;
}
}
/* The ownership of the Python list is passed to the caller (ie,
* the caller should Py_DECREF() the object when it is finished
* with it).
*/
return list;
}
/* Replace a line in the specified buffer. The line number is
* in Vim format (1-based). The replacement line is given as
* a Python string object. The object is checked for validity
* and correct format. Errors are returned as a value of FAIL.
* The return value is OK on success.
* If OK is returned and len_change is not NULL, *len_change
* is set to the change in the buffer length.
*/
static int SetBufferLine (BUF *buf, int n, PyObject *line, int *len_change)
{
/* First of all, we check the thpe of the supplied Python object.
* There are three cases:
* 1. NULL, or None - this is a deletion.
* 2. A string - this is a replacement.
* 3. Anything else - this is an error.
*/
if (line == Py_None || line == NULL)
{
/* TODO: Add error checking */
BUF *savebuf = curbuf;
curbuf = buf;
u_savedel (n, 1);
ml_delete (n, FALSE);
mark_adjust (n, n, MAXLNUM, -1);
curbuf = savebuf;
update_screen(NOT_VALID);
if (len_change)
*len_change = -1;
return OK;
}
else if (PyString_Check(line))
{
const char *str = PyString_AsString (line);
char *save = VimStringAlloc(line);
BUF *savebuf;
if (save == NULL)
return FAIL;
/* We do not need to free save, as we pass responsibility for
* it to vim, via the final parameter of ml_replace().
*/
/* TODO: Add error checking */
savebuf = curbuf;
curbuf = buf;
u_savesub(n);
ml_replace (n, save, TRUE);
curbuf = savebuf;
update_screen(NOT_VALID);
if (len_change)
*len_change = 0;
return OK;
}
else
{
PyErr_BadArgument();
return FAIL;
}
}
/* Replace a range of lines in the specified buffer. The line numbers are in
* Vim format (1-based). The range is from lo up to, but not including, hi.
* The replacement lines are given as a Python list of string objects. The
* list is checked for validity and correct format. Errors are returned as a
* value of FAIL. The return value is OK on success.
* If OK is returned and len_change is not NULL, *len_change
* is set to the change in the buffer length.
*/
static int SetBufferLineList (BUF *buf, int lo, int hi, PyObject *list, int *len_change)
{
/* First of all, we check the thpe of the supplied Python object.
* There are three cases:
* 1. NULL, or None - this is a deletion.
* 2. A list - this is a replacement.
* 3. Anything else - this is an error.
*/
if (list == Py_None || list == NULL)
{
/* TODO: Add error checking */
int i;
int n = hi - lo;
BUF *savebuf = curbuf;
curbuf = buf;
u_savedel (lo, n);
for (i = 0; i < n; ++i)
ml_delete (lo, FALSE);
mark_adjust (lo, hi-1, MAXLNUM, -n);
curbuf = savebuf;
update_screen(NOT_VALID);
if (len_change)
*len_change = -n;
return OK;
}
else if (PyList_Check(list))
{
int i;
int n = PyList_Size(list);
int lines = hi - lo;
char **array;
BUF *savebuf;
array = (char **) alloc (n * sizeof(char *));
if (array == NULL)
{
PyErr_NoMemory();
return FAIL;
}
for (i = 0; i < n; ++i)
{
PyObject *line = PyList_GetItem (list, i);
array[i] = VimStringAlloc(line);
if (array[i] == NULL)
{
while (i)
vim_free (array[--i]);
vim_free (array);
return FAIL;
}
}
/* TODO: Add error checking */
savebuf = curbuf;
curbuf = buf;
u_save(lo-1, hi);
/* If the size of the range is reducing (ie, n < lines) we
* need to delete some lines. We do this at the start, by
* repeatedly deleting line "lo".
*/
for (i = 0; i < lines - n; ++i)
ml_delete(lo, FALSE);
/* For as long as possible, replace the existing lines with the
* new lines. This is a more efficient operation, as it requires
* less memory allocation and freeing.
*/
for (i = 0; i < lines && i < n; ++i)
{
ml_replace (lo+i, array[i], TRUE);
}
/* Now we may need to insert the remaining new lines. If we do, we
* must free the strings as we finish with them (we can't pass the
* responsibility to vim in this case).
*/
while (i < n)
{
ml_append (lo+i-1, array[i], 0, FALSE);
vim_free (array[i]);
++i;
}
/* Adjust marks. Invalidate any which lie in the
* changed range, and move any in the remainder of the buffer.
*/
mark_adjust (lo, hi-1, MAXLNUM, n - lines);
/* Free the array of lines. All of its contents have now
* been dealt with (either freed, or the responsibility passed
* to vim.
*/
vim_free (array);
curbuf = savebuf;
update_screen(NOT_VALID);
if (len_change)
*len_change = n - lines;
return OK;
}
else
{
PyErr_BadArgument();
return FAIL;
}
}
/* Insert a number of lines into the specified buffer after the specifed line.
* The line number is in Vim format (1-based). The lines to be inserted are
* given as a Python list of string objects or as a single string. The lines
* to be added are checked for validity and correct format. Errors are
* returned as a value of FAIL. The return value is OK on success.
* If OK is returned and len_change is not NULL, *len_change
* is set to the change in the buffer length.
*/
static int InsertBufferLines (BUF *buf, int n, PyObject *lines, int *len_change)
{
/* First of all, we check the type of the supplied Python object.
* It must be a string or a list, or the call is in error.
*/
if (PyString_Check(lines))
{
char *str = VimStringAlloc(lines);
BUF *savebuf;
if (str == NULL)
return FAIL;
/* TODO: Add error checking */
savebuf = curbuf;
curbuf = buf;
u_save(n, n+1);
ml_append (n, str, 0, FALSE);
mark_adjust (n+1, MAXLNUM, 1, 0);
vim_free(str);
curbuf = savebuf;
update_screen(NOT_VALID);
if (len_change)
*len_change = 1;
return OK;
}
else if (PyList_Check(lines))
{
int i;
int size = PyList_Size(lines);
char **array;
BUF *savebuf;
array = (char **) alloc (size * sizeof(char *));
if (array == NULL)
{
PyErr_NoMemory();
return FAIL;
}
for (i = 0; i < size; ++i)
{
PyObject *line = PyList_GetItem (lines, i);
array[i] = VimStringAlloc(line);
if (array[i] == NULL)
{
while (i)
vim_free (array[--i]);
vim_free (array);
return FAIL;
}
}
/* TODO: Add error checking */
savebuf = curbuf;
curbuf = buf;
u_save(n, n+1); /* TODO: Check this! How to save for an insert? */
for (i = 0; i < size; ++i)
{
ml_append (n+i, array[i], 0, FALSE);
vim_free (array[i]);
}
mark_adjust (n+1, MAXLNUM, size, 0);
/* Free the array of lines. All of its contents have now
* been freed.
*/
vim_free (array);
curbuf = savebuf;
update_screen(NOT_VALID);
if (len_change)
*len_change = size;
return OK;
}
else
{
PyErr_BadArgument();
return FAIL;
}
}
/* Convert an array of lines in Vim format (with null bytes replaced
* by \n) into a single string, with embedded nulls and with the
* individual lines separated by \n characters.
* A newline is added at the end.
* The size of the resulting string is returned in *size.
* The returned string has been allocated on the heap.
* NULL is returned on errors (no data, memory exhaustion, etc).
*/
static char *VimArrayToString (char **array, int n, int *size)
{
int i, j;
char *p;
int len = 0;
char *result;
if (size == NULL)
return NULL;
for (i = 0; i < n; ++i)
{
len += strlen(array[i]) + 1;
}
if (len == 0)
return NULL;
result = malloc(len);
if (result == NULL)
return NULL;
p = result;
for (i = 0; i < n; ++i)
{
char *str = array[i];
int max = strlen(str);
for (j = 0; j < max; ++j)
{
char ch = str[j];
*p++ = (ch == '\n' ? '\0' : ch);
}
*p++ = '\n';
}
*size = len;
return result;
}
/* Swap newline and null characters in a block of memory.
* Converts a normal string into a Vim array of lines.
* Returns the number of lines in the array.
*/
static int MemToVimArray (char *mem, int len)
{
int lines = 0;
while (len)
{
if (*mem == '\n')
{
++lines;
*mem = '\0';
}
else if (*mem == '\0')
*mem = '\n';
++mem;
--len;
}
return lines;
}
/* Set the Python error information to indicate a Vim error.
* There is a fixed length buffer of 256 bytes. This should be enough.
*/
void PyErr_SetVim (const char *fmt, ...)
{
static char buf[256];
va_list ap;
va_start(ap, fmt);
vsprintf(buf, fmt, ap);
va_end(ap);
PyErr_SetString(VimError, buf);
}
static char *VimStringAlloc (PyObject *obj)
{
const char *str;
char *save;
int len;
int i;
if (obj == NULL || !PyString_Check(obj))
{
PyErr_BadArgument();
return NULL;
}
str = PyString_AsString (obj);
len = PyString_Size (obj);
/* Error checking: String must not contain newlines, as we
* are replacing a single line, and we must replace it with
* a single line.
*/
if (memchr(str, '\n', len))
{
PyErr_SetVim ("string cannot contain newlines");
return NULL;
}
/* Create a copy of the string, with internal nulls replaced by
* newline characters, as is the vim convention.
*/
save = alloc (len+1);
if (save == NULL)
{
PyErr_NoMemory();
return NULL;
}
for (i = 0; i < len; ++i)
{
if (str[i] == '\0')
save[i] = '\n';
else
save[i] = str[i];
}
save[i] = '\0';
return save;
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.