Skip to content

Commit 110b9cc

Browse files
[3.15] gh-154855: Ask non-ncurses curses for one more character (GH-154870) (GH-156283)
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 43a1869)
1 parent 74358f2 commit 110b9cc

2 files changed

Lines changed: 26 additions & 7 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix :meth:`curses.window.instr` returning one character too few when the
2+
:mod:`curses` module is built against a curses library that counts the
3+
terminator in the requested length, such as the NetBSD one.

‎Modules/_cursesmodule.c‎

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,14 @@ curses_window_set_null_error(PyCursesWindowObject *win,
310310
_curses_set_null_error(state, curses_funcname, python_funcname);
311311
}
312312

313+
/* ncurses and PDCurses store n characters and add a terminator; NetBSD
314+
curses counts the terminator in n. Ask an unknown library for one more. */
315+
#if defined(NCURSES_VERSION) || defined(PDCURSES)
316+
# define CURSES_STR_EXTRA 0
317+
#else
318+
# define CURSES_STR_EXTRA 1
319+
#endif
320+
313321
/* Utility Checking Procedures */
314322

315323
/*
@@ -2217,25 +2225,33 @@ PyCursesWindow_instr(PyObject *op, PyObject *args)
22172225
return NULL;
22182226
}
22192227

2220-
n = Py_MIN(n, max_buf_size - 1);
2228+
n = Py_MIN(n, max_buf_size - 1 - CURSES_STR_EXTRA);
2229+
n += CURSES_STR_EXTRA;
22212230
PyBytesWriter *writer = PyBytesWriter_Create(n + 1);
22222231
if (writer == NULL) {
22232232
return NULL;
22242233
}
22252234
char *buf = PyBytesWriter_GetData(writer);
22262235

2227-
if (use_xy) {
2228-
rtn = mvwinnstr(self->win, y, x, buf, n);
2229-
}
2230-
else {
2231-
rtn = winnstr(self->win, buf, n);
2236+
/* Read again if the library stored more than asked: truncating could
2237+
split a multibyte character. */
2238+
for (unsigned int want = n - CURSES_STR_EXTRA; ; n = want) {
2239+
if (use_xy) {
2240+
rtn = mvwinnstr(self->win, y, x, buf, n);
2241+
}
2242+
else {
2243+
rtn = winnstr(self->win, buf, n);
2244+
}
2245+
if (rtn == ERR || (unsigned int)rtn <= want) {
2246+
break;
2247+
}
22322248
}
22332249

22342250
if (rtn == ERR) {
22352251
PyBytesWriter_Discard(writer);
22362252
return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES);
22372253
}
2238-
return PyBytesWriter_FinishWithSize(writer, strlen(buf));
2254+
return PyBytesWriter_FinishWithSize(writer, rtn);
22392255
}
22402256

22412257
/*[clinic input]

0 commit comments

Comments
 (0)