[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]