[3.14] gh-154855: Ask non-ncurses curses for one more character (GH-154870) (GH-156284)

serhiy-storchaka <[email protected]>
Newsgroups gmane.comp.python.cvs
Message-ID <[email protected]>
https://github.com/python/cpython/commit/f6b1201e20345f3a7a546d458e9f1f2fbdd8fa61
commit: f6b1201e20345f3a7a546d458e9f1f2fbdd8fa61
branch: 3.14
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-08-23T13:10:59Z
summary:

[3.14] gh-154855: Ask non-ncurses curses for one more character (GH-154870) (GH-156284)

Passing n to the library is ncurses' reading of n: it stores n characters
and adds a terminator.  NetBSD curses counts the terminator in n.

Ask a library that is neither ncurses nor PDCurses for n + 1, and read
again if it stored more than asked; truncating could split a multibyte
character.  This is not possible for input, so getstr() is left as it is.

instr() now takes the length from the value returned by winnstr(), as
X/Open specifies, instead of searching for a terminator which it does not.

(cherry picked from commit 43a1869f7eea006f04647d4225d5fda80cb3fbd9)

files:
A Misc/NEWS.d/next/Library/2026-07-29-14-20-40.gh-issue-154855.vfF453.rst
M Modules/_cursesmodule.c

diff --git a/Misc/NEWS.d/next/Library/2026-07-29-14-20-40.gh-issue-154855.vfF453.rst b/Misc/NEWS.d/next/Library/2026-07-29-14-20-40.gh-issue-154855.vfF453.rst
new file mode 100644
index 00000000000000..d8895e32bafe8a
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-07-29-14-20-40.gh-issue-154855.vfF453.rst
@@ -0,0 +1,3 @@
+Fix :meth:`curses.window.instr` returning one character too few when the
+:mod:`curses` module is built against a curses library that counts the
+terminator in the requested length, such as the NetBSD one.
diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c
index 6008802b81c68a..8d8544b4d9c20f 100644
--- a/Modules/_cursesmodule.c
+++ b/Modules/_cursesmodule.c
@@ -215,6 +215,14 @@ static int curses_start_color_called = FALSE;
    while these functions are still in use. */
 static char *curses_screen_encoding = NULL;
 
+/* ncurses and PDCurses store n characters and add a terminator; NetBSD
+   curses counts the terminator in n.  Ask an unknown library for one more. */
+#if defined(NCURSES_VERSION) || defined(PDCURSES)
+#  define CURSES_STR_EXTRA  0
+#else
+#  define CURSES_STR_EXTRA  1
+#endif
+
 /* Utility Checking Procedures */
 
 /*
@@ -2014,25 +2022,33 @@ PyCursesWindow_instr(PyObject *op, PyObject *args)
         return NULL;
     }
 
-    n = Py_MIN(n, max_buf_size - 1);
+    n = Py_MIN(n, max_buf_size - 1 - CURSES_STR_EXTRA);
+    n += CURSES_STR_EXTRA;
     res = PyBytes_FromStringAndSize(NULL, n + 1);
     if (res == NULL) {
         return NULL;
     }
     char *buf = PyBytes_AS_STRING(res);
 
-    if (use_xy) {
-        rtn = mvwinnstr(self->win, y, x, buf, n);
-    }
-    else {
-        rtn = winnstr(self->win, buf, n);
+    /* Read again if the library stored more than asked: truncating could
+       split a multibyte character. */
+    for (unsigned int want = n - CURSES_STR_EXTRA; ; n = want) {
+        if (use_xy) {
+            rtn = mvwinnstr(self->win, y, x, buf, n);
+        }
+        else {
+            rtn = winnstr(self->win, buf, n);
+        }
+        if (rtn == ERR || (unsigned int)rtn <= want) {
+            break;
+        }
     }
 
     if (rtn == ERR) {
         Py_DECREF(res);
         return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES);
     }
-    _PyBytes_Resize(&res, strlen(buf));  // 'res' is set to NULL on failure
+    _PyBytes_Resize(&res, rtn);  // 'res' is set to NULL on failure
     return res;
 }
 

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.