diff src/if_py_both.h @ 2894:fe9c7da98b5e v7.3.220

updated for version 7.3.220 Problem: Python 3: vim.error is a 'str' instead of an 'Exception' object, so 'except' or 'raise' it causes a 'SystemError' exception. Buffer objects do not support slice assignment. When exchanging text between Vim and Python, multibyte texts become gabage or cause Unicode Expceptions, etc. 'py3file' tries to read in the file as Unicode, sometimes causes UnicodeDecodeException Solution: Fix the problems. (lilydjwg)
author Bram Moolenaar <bram@vim.org>
date Sun, 19 Jun 2011 00:27:51 +0200
parents 62e8d93241cd
children b67d3a44262a
line wrap: on
line diff
--- a/src/if_py_both.h
+++ b/src/if_py_both.h
@@ -65,10 +65,10 @@ static struct PyMethodDef OutputMethods[
 OutputWrite(PyObject *self, PyObject *args)
 {
     int len;
-    char *str;
+    char *str = NULL;
     int error = ((OutputObject *)(self))->error;
 
-    if (!PyArg_ParseTuple(args, "s#", &str, &len))
+    if (!PyArg_ParseTuple(args, "es#", p_enc, &str, &len))
 	return NULL;
 
     Py_BEGIN_ALLOW_THREADS
@@ -76,6 +76,7 @@ OutputWrite(PyObject *self, PyObject *ar
     writer((writefn)(error ? emsg : msg), (char_u *)str, len);
     Python_Release_Vim();
     Py_END_ALLOW_THREADS
+    PyMem_Free(str);
 
     Py_INCREF(Py_None);
     return Py_None;
@@ -104,10 +105,10 @@ OutputWritelines(PyObject *self, PyObjec
     for (i = 0; i < n; ++i)
     {
 	PyObject *line = PyList_GetItem(list, i);
-	char *str;
+	char *str = NULL;
 	PyInt len;
 
-	if (!PyArg_Parse(line, "s#", &str, &len)) {
+	if (!PyArg_Parse(line, "es#", p_enc, &str, &len)) {
 	    PyErr_SetString(PyExc_TypeError, _("writelines() requires list of strings"));
 	    Py_DECREF(list);
 	    return NULL;
@@ -118,6 +119,7 @@ OutputWritelines(PyObject *self, PyObjec
 	writer((writefn)(error ? emsg : msg), (char_u *)str, len);
 	Python_Release_Vim();
 	Py_END_ALLOW_THREADS
+	PyMem_Free(str);
     }
 
     Py_DECREF(list);
@@ -681,6 +683,7 @@ StringToLine(PyObject *obj)
 {
     const char *str;
     char *save;
+    PyObject *bytes;
     PyInt len;
     PyInt i;
     char *p;
@@ -691,8 +694,9 @@ StringToLine(PyObject *obj)
 	return NULL;
     }
 
-    str = PyString_AsString(obj);
-    len = PyString_Size(obj);
+    bytes = PyString_AsBytes(obj);  /* for Python 2 this does nothing */
+    str = PyString_AsString(bytes);
+    len = PyString_Size(bytes);
 
     /*
      * Error checking: String must not contain newlines, as we
@@ -731,6 +735,7 @@ StringToLine(PyObject *obj)
     }
 
     save[i] = '\0';
+    PyString_FreeBytes(bytes);  /* Python 2 does nothing here */
 
     return save;
 }
@@ -817,7 +822,8 @@ py_fix_cursor(linenr_T lo, linenr_T hi, 
     invalidate_botline();
 }
 
-/* Replace a line in the specified buffer. The line number is
+/*
+ * 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.
@@ -908,6 +914,193 @@ SetBufferLine(buf_T *buf, PyInt n, PyObj
     }
 }
 
+/* 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_T *buf, PyInt lo, PyInt hi, PyObject *list, PyInt *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)
+    {
+	PyInt	i;
+	PyInt	n = (int)(hi - lo);
+	buf_T	*savebuf = curbuf;
+
+	PyErr_Clear();
+	curbuf = buf;
+
+	if (u_savedel((linenr_T)lo, (long)n) == FAIL)
+	    PyErr_SetVim(_("cannot save undo information"));
+	else
+	{
+	    for (i = 0; i < n; ++i)
+	    {
+		if (ml_delete((linenr_T)lo, FALSE) == FAIL)
+		{
+		    PyErr_SetVim(_("cannot delete line"));
+		    break;
+		}
+	    }
+	    if (buf == curwin->w_buffer)
+		py_fix_cursor((linenr_T)lo, (linenr_T)hi, (linenr_T)-n);
+	    deleted_lines_mark((linenr_T)lo, (long)i);
+	}
+
+	curbuf = savebuf;
+
+	if (PyErr_Occurred() || VimErrorCheck())
+	    return FAIL;
+
+	if (len_change)
+	    *len_change = -n;
+
+	return OK;
+    }
+    else if (PyList_Check(list))
+    {
+	PyInt	i;
+	PyInt	new_len = PyList_Size(list);
+	PyInt	old_len = hi - lo;
+	PyInt	extra = 0;	/* lines added to text, can be negative */
+	char	**array;
+	buf_T	*savebuf;
+
+	if (new_len == 0)	/* avoid allocating zero bytes */
+	    array = NULL;
+	else
+	{
+	    array = (char **)alloc((unsigned)(new_len * sizeof(char *)));
+	    if (array == NULL)
+	    {
+		PyErr_NoMemory();
+		return FAIL;
+	    }
+	}
+
+	for (i = 0; i < new_len; ++i)
+	{
+	    PyObject *line = PyList_GetItem(list, i);
+
+	    array[i] = StringToLine(line);
+	    if (array[i] == NULL)
+	    {
+		while (i)
+		    vim_free(array[--i]);
+		vim_free(array);
+		return FAIL;
+	    }
+	}
+
+	savebuf = curbuf;
+
+	PyErr_Clear();
+	curbuf = buf;
+
+	if (u_save((linenr_T)(lo-1), (linenr_T)hi) == FAIL)
+	    PyErr_SetVim(_("cannot save undo information"));
+
+	/* If the size of the range is reducing (ie, new_len < old_len) we
+	 * need to delete some old_len. We do this at the start, by
+	 * repeatedly deleting line "lo".
+	 */
+	if (!PyErr_Occurred())
+	{
+	    for (i = 0; i < old_len - new_len; ++i)
+		if (ml_delete((linenr_T)lo, FALSE) == FAIL)
+		{
+		    PyErr_SetVim(_("cannot delete line"));
+		    break;
+		}
+	    extra -= i;
+	}
+
+	/* For as long as possible, replace the existing old_len with the
+	 * new old_len. This is a more efficient operation, as it requires
+	 * less memory allocation and freeing.
+	 */
+	if (!PyErr_Occurred())
+	{
+	    for (i = 0; i < old_len && i < new_len; ++i)
+		if (ml_replace((linenr_T)(lo+i), (char_u *)array[i], FALSE)
+								      == FAIL)
+		{
+		    PyErr_SetVim(_("cannot replace line"));
+		    break;
+		}
+	}
+	else
+	    i = 0;
+
+	/* Now we may need to insert the remaining new old_len. If we do, we
+	 * must free the strings as we finish with them (we can't pass the
+	 * responsibility to vim in this case).
+	 */
+	if (!PyErr_Occurred())
+	{
+	    while (i < new_len)
+	    {
+		if (ml_append((linenr_T)(lo + i - 1),
+					(char_u *)array[i], 0, FALSE) == FAIL)
+		{
+		    PyErr_SetVim(_("cannot insert line"));
+		    break;
+		}
+		vim_free(array[i]);
+		++i;
+		++extra;
+	    }
+	}
+
+	/* Free any left-over old_len, as a result of an error */
+	while (i < new_len)
+	{
+	    vim_free(array[i]);
+	    ++i;
+	}
+
+	/* Free the array of old_len. All of its contents have now
+	 * been dealt with (either freed, or the responsibility passed
+	 * to vim.
+	 */
+	vim_free(array);
+
+	/* Adjust marks. Invalidate any which lie in the
+	 * changed range, and move any in the remainder of the buffer.
+	 */
+	mark_adjust((linenr_T)lo, (linenr_T)(hi - 1),
+						  (long)MAXLNUM, (long)extra);
+	changed_lines((linenr_T)lo, 0, (linenr_T)hi, (long)extra);
+
+	if (buf == curwin->w_buffer)
+	    py_fix_cursor((linenr_T)lo, (linenr_T)hi, (linenr_T)extra);
+
+	curbuf = savebuf;
+
+	if (PyErr_Occurred() || VimErrorCheck())
+	    return FAIL;
+
+	if (len_change)
+	    *len_change = new_len - old_len;
+
+	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
@@ -1113,6 +1306,40 @@ RBAsItem(BufferObject *self, PyInt n, Py
     return 0;
 }
 
+    static PyInt
+RBAsSlice(BufferObject *self, PyInt lo, PyInt hi, PyObject *val, PyInt start, PyInt end, PyInt *new_end)
+{
+    PyInt size;
+    PyInt 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, PyInt start, PyInt end, PyInt *new_end)