ftp.nice.ch/pub/next/unix/editor/vim-5.0f.s.tar.gz#/vim-5.0f/src/if_python.c

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", &reg))
        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", &reg, &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.