From 46a6f472cd0c21be770069c8dfbd0db442b3cee4 Mon Sep 17 00:00:00 2001 From: tigerding Date: Mon, 19 May 2025 16:43:14 -0400 Subject: [PATCH 01/28] made curses buffer heap allocated instead of stack --- Modules/_cursesmodule.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index ab63fdbe45de61..d4a73aba81c42a 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1695,6 +1695,14 @@ _curses_window_getch_impl(PyCursesWindowObject *self, int group_right_1, } Py_END_ALLOW_THREADS + /* getch() returns ERR in nodelay mode */ + if (PyErr_CheckSignals()) { + cursesmodule_state *state = get_cursesmodule_state_by_win(self); + const char *funcname = group_right_1 ? "mvwgetch" : "wgetch"; + PyErr_Format(state->error, "getch(): %s(): no input", funcname); + return ERR; + } + return rtn; } @@ -2042,12 +2050,16 @@ PyCursesWindow_InStr(PyObject *op, PyObject *args) PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op); int x, y, n; - char rtn[1024]; /* This should be big enough.. I hope */ - int rtn2; + int err_code; + + /* could make the buffer size larger/dynamic */ + const int max_buf_size = 2048; + PyObject *result = PyBytes_FromStringAndSize(NULL, max_buf_size); + char *buf = PyBytes_AS_STRING(result); switch (PyTuple_Size(args)) { case 0: - rtn2 = winnstr(self->win,rtn, 1023); + err_code = winnstr(self->win, buf, max_buf_size - 1); break; case 1: if (!PyArg_ParseTuple(args,"i;n", &n)) @@ -2056,12 +2068,12 @@ PyCursesWindow_InStr(PyObject *op, PyObject *args) PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative"); return NULL; } - rtn2 = winnstr(self->win, rtn, Py_MIN(n, 1023)); + err_code = winnstr(self->win, buf, Py_MIN(n, max_buf_size - 1)); break; case 2: if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) return NULL; - rtn2 = mvwinnstr(self->win,y,x,rtn,1023); + err_code = mvwinnstr(self->win, y, x, buf, max_buf_size - 1); break; case 3: if (!PyArg_ParseTuple(args, "iii;y,x,n", &y, &x, &n)) @@ -2070,15 +2082,19 @@ PyCursesWindow_InStr(PyObject *op, PyObject *args) PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative"); return NULL; } - rtn2 = mvwinnstr(self->win, y, x, rtn, Py_MIN(n,1023)); + err_code = mvwinnstr(self->win, y, x, buf, Py_MIN(n, max_buf_size - 1)); break; default: PyErr_SetString(PyExc_TypeError, "instr requires 0 or 3 arguments"); return NULL; } - if (rtn2 == ERR) - rtn[0] = 0; - return PyBytes_FromString(rtn); + if (err_code == ERR) + buf[0] = '\0'; + + size_t size = strlen(buf); + _PyBytes_Resize(&result, size); + + return result; } /*[clinic input] From 7eeb8f6072137b833d29bbdf01c98fbc4a9a8cee Mon Sep 17 00:00:00 2001 From: tigerding Date: Mon, 19 May 2025 16:49:14 -0400 Subject: [PATCH 02/28] change docs to explicitly mention the max buffer size --- Doc/library/curses.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst index 137504c51b4358..a451c04ed44b0b 100644 --- a/Doc/library/curses.rst +++ b/Doc/library/curses.rst @@ -1078,7 +1078,8 @@ the following methods and attributes: Return a bytes object of characters, extracted from the window starting at the current cursor position, or at *y*, *x* if specified. Attributes are stripped from the characters. If *n* is specified, :meth:`instr` returns a string - at most *n* characters long (exclusive of the trailing NUL). + at most *n* characters long (exclusive of the trailing NUL). The maximum of *n* + for this method is 2047. .. method:: window.is_linetouched(line) From d5aa3dbabb949b18181b665e8b499c86307117b5 Mon Sep 17 00:00:00 2001 From: tigerding Date: Mon, 19 May 2025 16:50:47 -0400 Subject: [PATCH 03/28] remove unintentional changes --- Modules/_cursesmodule.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index d4a73aba81c42a..3f1dbe45e4a036 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1695,14 +1695,6 @@ _curses_window_getch_impl(PyCursesWindowObject *self, int group_right_1, } Py_END_ALLOW_THREADS - /* getch() returns ERR in nodelay mode */ - if (PyErr_CheckSignals()) { - cursesmodule_state *state = get_cursesmodule_state_by_win(self); - const char *funcname = group_right_1 ? "mvwgetch" : "wgetch"; - PyErr_Format(state->error, "getch(): %s(): no input", funcname); - return ERR; - } - return rtn; } From 020d798d7d8cc61554f5b3d108cc0d7bcb8d511a Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 20:59:07 +0000 Subject: [PATCH 04/28] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst diff --git a/Misc/NEWS.d/next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst b/Misc/NEWS.d/next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst new file mode 100644 index 00000000000000..b0b9bd16229ceb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst @@ -0,0 +1 @@ +Made curses `instr` method to allocate buffer on heap instead of on stack. Incremented the max buffer size to 2047 instead of 1023. From 823fa3832aeadc1ab59952f0f8f7f65833580a5b Mon Sep 17 00:00:00 2001 From: tigerding Date: Mon, 19 May 2025 17:03:29 -0400 Subject: [PATCH 05/28] reword news to satify linter --- .../next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst b/Misc/NEWS.d/next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst index b0b9bd16229ceb..f6536511aa6d39 100644 --- a/Misc/NEWS.d/next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst +++ b/Misc/NEWS.d/next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst @@ -1 +1 @@ -Made curses `instr` method to allocate buffer on heap instead of on stack. Incremented the max buffer size to 2047 instead of 1023. +Made curses ``instr`` method to allocate buffer on heap instead of on stack. Incremented the max buffer size to 2047 instead of 1023. From 0cb5fe081ce9e652023fcd0aa35b23fd8b0500b6 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Mon, 19 May 2025 18:00:13 -0700 Subject: [PATCH 06/28] Reword the ReST news entry. --- .../next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst b/Misc/NEWS.d/next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst index f6536511aa6d39..6706d2b22ce9cb 100644 --- a/Misc/NEWS.d/next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst +++ b/Misc/NEWS.d/next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst @@ -1 +1 @@ -Made curses ``instr`` method to allocate buffer on heap instead of on stack. Incremented the max buffer size to 2047 instead of 1023. +The :meth:`curses.window.instr` and :meth:`curses.window.getstr` allocate its internal buffer on the heap instead of the stack. Increased its max buffer size to 2047 from of 1023, documenting the limit. From 63a3ed2db3c7856217201de688b4a97e75107ab2 Mon Sep 17 00:00:00 2001 From: tigerding Date: Mon, 19 May 2025 22:41:53 -0400 Subject: [PATCH 07/28] use rtn value from winnstr instead of strlen --- Modules/_cursesmodule.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 3f1dbe45e4a036..7f9e607dff708d 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -2042,7 +2042,7 @@ PyCursesWindow_InStr(PyObject *op, PyObject *args) PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op); int x, y, n; - int err_code; + int rtn; /* could make the buffer size larger/dynamic */ const int max_buf_size = 2048; @@ -2051,7 +2051,7 @@ PyCursesWindow_InStr(PyObject *op, PyObject *args) switch (PyTuple_Size(args)) { case 0: - err_code = winnstr(self->win, buf, max_buf_size - 1); + rtn = winnstr(self->win, buf, max_buf_size - 1); break; case 1: if (!PyArg_ParseTuple(args,"i;n", &n)) @@ -2060,12 +2060,12 @@ PyCursesWindow_InStr(PyObject *op, PyObject *args) PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative"); return NULL; } - err_code = winnstr(self->win, buf, Py_MIN(n, max_buf_size - 1)); + rtn = winnstr(self->win, buf, Py_MIN(n, max_buf_size - 1)); break; case 2: if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) return NULL; - err_code = mvwinnstr(self->win, y, x, buf, max_buf_size - 1); + rtn = mvwinnstr(self->win, y, x, buf, max_buf_size - 1); break; case 3: if (!PyArg_ParseTuple(args, "iii;y,x,n", &y, &x, &n)) @@ -2074,17 +2074,18 @@ PyCursesWindow_InStr(PyObject *op, PyObject *args) PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative"); return NULL; } - err_code = mvwinnstr(self->win, y, x, buf, Py_MIN(n, max_buf_size - 1)); + rtn = mvwinnstr(self->win, y, x, buf, Py_MIN(n, max_buf_size - 1)); break; default: PyErr_SetString(PyExc_TypeError, "instr requires 0 or 3 arguments"); return NULL; } - if (err_code == ERR) - buf[0] = '\0'; - size_t size = strlen(buf); - _PyBytes_Resize(&result, size); + if (rtn == ERR) { + _PyBytes_Resize(&result, 0); + } else { + _PyBytes_Resize(&result, rtn); + } return result; } From 58d6cde7498e1c0a351cf943151be50e27ed215d Mon Sep 17 00:00:00 2001 From: tigerding Date: Mon, 19 May 2025 22:52:13 -0400 Subject: [PATCH 08/28] changing GetStr() function to behave similarly too --- Modules/_cursesmodule.c | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 7f9e607dff708d..9f48764c15658b 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1818,7 +1818,7 @@ _curses.window.getstr x: int X-coordinate. ] - n: int = 1023 + n: int = 2047 Maximal number of characters. / @@ -1831,24 +1831,28 @@ PyCursesWindow_GetStr(PyObject *op, PyObject *args) PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op); int x, y, n; - char rtn[1024]; /* This should be big enough.. I hope */ - int rtn2; + int rtn; + + /* could make the buffer size larger/dynamic */ + const int max_buf_size = 2048; + PyObject *result = PyBytes_FromStringAndSize(NULL, max_buf_size); + char *buf = PyBytes_AS_STRING(result); switch (PyTuple_Size(args)) { case 0: Py_BEGIN_ALLOW_THREADS - rtn2 = wgetnstr(self->win,rtn, 1023); + rtn = wgetnstr(self->win, buf, max_buf_size - 1); Py_END_ALLOW_THREADS break; case 1: - if (!PyArg_ParseTuple(args,"i;n", &n)) + if (!PyArg_ParseTuple(args, "i;n", &n)) return NULL; if (n < 0) { PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative"); return NULL; } Py_BEGIN_ALLOW_THREADS - rtn2 = wgetnstr(self->win, rtn, Py_MIN(n, 1023)); + rtn = wgetnstr(self->win, buf, Py_MIN(n, max_buf_size - 1)); Py_END_ALLOW_THREADS break; case 2: @@ -1856,14 +1860,14 @@ PyCursesWindow_GetStr(PyObject *op, PyObject *args) return NULL; Py_BEGIN_ALLOW_THREADS #ifdef STRICT_SYSV_CURSES - rtn2 = wmove(self->win,y,x)==ERR ? ERR : wgetnstr(self->win, rtn, 1023); + rtn = wmove(self->win,y,x)==ERR ? ERR : wgetnstr(self->win, rtn, max_buf_size - 1); #else - rtn2 = mvwgetnstr(self->win,y,x,rtn, 1023); + rtn = mvwgetnstr(self->win,y,x,buf, max_buf_size - 1); #endif Py_END_ALLOW_THREADS break; case 3: - if (!PyArg_ParseTuple(args,"iii;y,x,n", &y, &x, &n)) + if (!PyArg_ParseTuple(args, "iii;y,x,n", &y, &x, &n)) return NULL; if (n < 0) { PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative"); @@ -1871,12 +1875,12 @@ PyCursesWindow_GetStr(PyObject *op, PyObject *args) } #ifdef STRICT_SYSV_CURSES Py_BEGIN_ALLOW_THREADS - rtn2 = wmove(self->win,y,x)==ERR ? ERR : - wgetnstr(self->win, rtn, Py_MIN(n, 1023)); + rtn = wmove(self->win,y,x)==ERR ? ERR : + wgetnstr(self->win, rtn, Py_MIN(n, max_buf_size - 1)); Py_END_ALLOW_THREADS #else Py_BEGIN_ALLOW_THREADS - rtn2 = mvwgetnstr(self->win, y, x, rtn, Py_MIN(n, 1023)); + rtn = mvwgetnstr(self->win, y, x, buf, Py_MIN(n, max_buf_size - 1)); Py_END_ALLOW_THREADS #endif break; @@ -1884,9 +1888,14 @@ PyCursesWindow_GetStr(PyObject *op, PyObject *args) PyErr_SetString(PyExc_TypeError, "getstr requires 0 to 3 arguments"); return NULL; } - if (rtn2 == ERR) - rtn[0] = 0; - return PyBytes_FromString(rtn); + + if (rtn == ERR) { + _PyBytes_Resize(&result, 0); + } else { + _PyBytes_Resize(&result, rtn); + } + + return result; } /*[clinic input] @@ -2025,7 +2034,7 @@ _curses.window.instr x: int X-coordinate. ] - n: int = 1023 + n: int = 2047 Maximal number of characters. / From 82c9b1c9180493ab0e1c7daf6082f28b2dcfe15e Mon Sep 17 00:00:00 2001 From: tigerding Date: Mon, 19 May 2025 22:56:24 -0400 Subject: [PATCH 09/28] updated curses doc for getstr function --- Doc/library/curses.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst index a451c04ed44b0b..2f089ed7d948ed 100644 --- a/Doc/library/curses.rst +++ b/Doc/library/curses.rst @@ -988,7 +988,7 @@ the following methods and attributes: window.getstr(y, x, n) Read a bytes object from the user, with primitive line editing capacity. - + The maximum of *n* for this method is 2047. .. method:: window.getyx() From cbce07b9d273e3f2cb0392099d3bc6840792d84e Mon Sep 17 00:00:00 2001 From: tigerding <43339228+zydtiger@users.noreply.github.com> Date: Tue, 20 May 2025 10:40:30 -0400 Subject: [PATCH 10/28] Update Doc/library/curses.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit add version changed message Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/library/curses.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst index 2f089ed7d948ed..e8ede8854fcaff 100644 --- a/Doc/library/curses.rst +++ b/Doc/library/curses.rst @@ -1081,6 +1081,9 @@ the following methods and attributes: at most *n* characters long (exclusive of the trailing NUL). The maximum of *n* for this method is 2047. + .. versionchanged:: next + The maximum value for *n* is set to 2047. + .. method:: window.is_linetouched(line) From dfd210db2c2b362830918952b3068ec89092d532 Mon Sep 17 00:00:00 2001 From: tigerding <43339228+zydtiger@users.noreply.github.com> Date: Tue, 20 May 2025 10:40:48 -0400 Subject: [PATCH 11/28] Update Doc/library/curses.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit add version changed message Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/library/curses.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst index e8ede8854fcaff..1c0747ff6d5ac3 100644 --- a/Doc/library/curses.rst +++ b/Doc/library/curses.rst @@ -990,6 +990,9 @@ the following methods and attributes: Read a bytes object from the user, with primitive line editing capacity. The maximum of *n* for this method is 2047. + .. versionchanged:: next + The maximum value for *n* is set to 2047. + .. method:: window.getyx() Return a tuple ``(y, x)`` of current cursor position relative to the window's From ad832bd61d7301f5dc2c78029d3ec4c7c9401163 Mon Sep 17 00:00:00 2001 From: tigerding <43339228+zydtiger@users.noreply.github.com> Date: Tue, 20 May 2025 10:41:53 -0400 Subject: [PATCH 12/28] Update Misc/NEWS.d/next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- .../Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst b/Misc/NEWS.d/next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst index 6706d2b22ce9cb..f985872f3c975e 100644 --- a/Misc/NEWS.d/next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst +++ b/Misc/NEWS.d/next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst @@ -1 +1,3 @@ -The :meth:`curses.window.instr` and :meth:`curses.window.getstr` allocate its internal buffer on the heap instead of the stack. Increased its max buffer size to 2047 from of 1023, documenting the limit. +:mod:`curses`: The :meth:`curses.window.instr` and :meth:`curses.window.getstr` +methods now allocate their internal buffer on the heap instead of the stack; +in addition, the max buffer size is increased from 1023 to 2047. From f7a51f9a4ba3ac04e7a2b1c0219366428ecdff67 Mon Sep 17 00:00:00 2001 From: tigerding <43339228+zydtiger@users.noreply.github.com> Date: Tue, 20 May 2025 10:42:09 -0400 Subject: [PATCH 13/28] Update Modules/_cursesmodule.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Modules/_cursesmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 9f48764c15658b..3893e6b6549bc4 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1834,7 +1834,7 @@ PyCursesWindow_GetStr(PyObject *op, PyObject *args) int rtn; /* could make the buffer size larger/dynamic */ - const int max_buf_size = 2048; + Py_ssize_t max_buf_size = 2048; PyObject *result = PyBytes_FromStringAndSize(NULL, max_buf_size); char *buf = PyBytes_AS_STRING(result); From e5597a2c0c1f5eb1e409aa1124ddb25440129621 Mon Sep 17 00:00:00 2001 From: tigerding <43339228+zydtiger@users.noreply.github.com> Date: Tue, 20 May 2025 10:42:48 -0400 Subject: [PATCH 14/28] Update Modules/_cursesmodule.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Modules/_cursesmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 3893e6b6549bc4..aac54f078d05c8 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1867,7 +1867,7 @@ PyCursesWindow_GetStr(PyObject *op, PyObject *args) Py_END_ALLOW_THREADS break; case 3: - if (!PyArg_ParseTuple(args, "iii;y,x,n", &y, &x, &n)) + if (!PyArg_ParseTuple(args,"iii;y,x,n", &y, &x, &n)) return NULL; if (n < 0) { PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative"); From 5dd430438b4aa2b7c665b02492b670db3fe2657e Mon Sep 17 00:00:00 2001 From: tigerding <43339228+zydtiger@users.noreply.github.com> Date: Tue, 20 May 2025 10:43:46 -0400 Subject: [PATCH 15/28] Update Modules/_cursesmodule.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Modules/_cursesmodule.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index aac54f078d05c8..aaa33e45126565 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1890,9 +1890,12 @@ PyCursesWindow_GetStr(PyObject *op, PyObject *args) } if (rtn == ERR) { - _PyBytes_Resize(&result, 0); - } else { - _PyBytes_Resize(&result, rtn); + Py_DECREF(result); + return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); + } + + if (_PyBytes_Resize(&result, rtn) < 0) { + return NULL; } return result; From d868088f4126d9767c4789ba57e2dfd397eb8aa1 Mon Sep 17 00:00:00 2001 From: tigerding Date: Tue, 20 May 2025 10:45:31 -0400 Subject: [PATCH 16/28] Update instr with proper return error handling --- Modules/_cursesmodule.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index aaa33e45126565..c2ca5df1993081 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -2094,9 +2094,12 @@ PyCursesWindow_InStr(PyObject *op, PyObject *args) } if (rtn == ERR) { - _PyBytes_Resize(&result, 0); - } else { - _PyBytes_Resize(&result, rtn); + Py_DECREF(result); + return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); + } + + if (_PyBytes_Resize(&result, rtn) < 0) { + return NULL; } return result; From 1a519cbe3f4edc7293ff281c4cbfb89f1d403f88 Mon Sep 17 00:00:00 2001 From: tigerding <43339228+zydtiger@users.noreply.github.com> Date: Tue, 20 May 2025 10:46:12 -0400 Subject: [PATCH 17/28] Update Modules/_cursesmodule.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Modules/_cursesmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index c2ca5df1993081..ceba8b552147da 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1845,7 +1845,7 @@ PyCursesWindow_GetStr(PyObject *op, PyObject *args) Py_END_ALLOW_THREADS break; case 1: - if (!PyArg_ParseTuple(args, "i;n", &n)) + if (!PyArg_ParseTuple(args,"i;n", &n)) return NULL; if (n < 0) { PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative"); From 083f855245275fcacb1015de62d70b9f4a60f387 Mon Sep 17 00:00:00 2001 From: tigerding Date: Tue, 20 May 2025 11:13:39 -0400 Subject: [PATCH 18/28] change to strlen and better memory safety --- Modules/_cursesmodule.c | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index b5fcccb432b77f..0a5de1d9ec1aa4 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1844,10 +1844,10 @@ PyCursesWindow_GetStr(PyObject *op, PyObject *args) break; case 1: if (!PyArg_ParseTuple(args,"i;n", &n)) - return NULL; + goto error; if (n < 0) { PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative"); - return NULL; + goto error; } Py_BEGIN_ALLOW_THREADS rtn = wgetnstr(self->win, buf, Py_MIN(n, max_buf_size - 1)); @@ -1855,7 +1855,7 @@ PyCursesWindow_GetStr(PyObject *op, PyObject *args) break; case 2: if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) - return NULL; + goto error; Py_BEGIN_ALLOW_THREADS #ifdef STRICT_SYSV_CURSES rtn = wmove(self->win,y,x)==ERR ? ERR : wgetnstr(self->win, rtn, max_buf_size - 1); @@ -1866,10 +1866,10 @@ PyCursesWindow_GetStr(PyObject *op, PyObject *args) break; case 3: if (!PyArg_ParseTuple(args,"iii;y,x,n", &y, &x, &n)) - return NULL; + goto error; if (n < 0) { PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative"); - return NULL; + goto error; } #ifdef STRICT_SYSV_CURSES Py_BEGIN_ALLOW_THREADS @@ -1884,7 +1884,7 @@ PyCursesWindow_GetStr(PyObject *op, PyObject *args) break; default: PyErr_SetString(PyExc_TypeError, "getstr requires 0 to 3 arguments"); - return NULL; + goto error; } if (rtn == ERR) { @@ -1892,11 +1892,15 @@ PyCursesWindow_GetStr(PyObject *op, PyObject *args) return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); } - if (_PyBytes_Resize(&result, rtn) < 0) { + if (_PyBytes_Resize(&result, strlen(buf)) < 0) { return NULL; } return result; + +error: + Py_DECREF(result); + return NULL; } /*[clinic input] @@ -2065,30 +2069,30 @@ PyCursesWindow_InStr(PyObject *op, PyObject *args) break; case 1: if (!PyArg_ParseTuple(args,"i;n", &n)) - return NULL; + goto error; if (n < 0) { PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative"); - return NULL; + goto error; } rtn = winnstr(self->win, buf, Py_MIN(n, max_buf_size - 1)); break; case 2: if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) - return NULL; + goto error; rtn = mvwinnstr(self->win, y, x, buf, max_buf_size - 1); break; case 3: if (!PyArg_ParseTuple(args, "iii;y,x,n", &y, &x, &n)) - return NULL; + goto error; if (n < 0) { PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative"); - return NULL; + goto error; } rtn = mvwinnstr(self->win, y, x, buf, Py_MIN(n, max_buf_size - 1)); break; default: PyErr_SetString(PyExc_TypeError, "instr requires 0 or 3 arguments"); - return NULL; + goto error; } if (rtn == ERR) { @@ -2096,11 +2100,15 @@ PyCursesWindow_InStr(PyObject *op, PyObject *args) return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); } - if (_PyBytes_Resize(&result, rtn) < 0) { - return NULL; + if (_PyBytes_Resize(&result, strlen(buf)) < 0) { + return NULL; } return result; + +error: + Py_DECREF(result); + return NULL; } /*[clinic input] From 20046cb4681a23666f6e9c4fe1d5f925872c39e2 Mon Sep 17 00:00:00 2001 From: tigerding <43339228+zydtiger@users.noreply.github.com> Date: Tue, 20 May 2025 11:14:56 -0400 Subject: [PATCH 19/28] Update Doc/library/curses.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/library/curses.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst index 1c0747ff6d5ac3..1e8580199b1181 100644 --- a/Doc/library/curses.rst +++ b/Doc/library/curses.rst @@ -988,7 +988,7 @@ the following methods and attributes: window.getstr(y, x, n) Read a bytes object from the user, with primitive line editing capacity. - The maximum of *n* for this method is 2047. + The maximum value for *n* is 2047. .. versionchanged:: next The maximum value for *n* is set to 2047. From fd5c3f4ab0562ebd51c843445fb78e2c7e23aaec Mon Sep 17 00:00:00 2001 From: tigerding <43339228+zydtiger@users.noreply.github.com> Date: Tue, 20 May 2025 11:15:03 -0400 Subject: [PATCH 20/28] Update Doc/library/curses.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/library/curses.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst index 1e8580199b1181..b02cb95b68a8f9 100644 --- a/Doc/library/curses.rst +++ b/Doc/library/curses.rst @@ -1081,8 +1081,8 @@ the following methods and attributes: Return a bytes object of characters, extracted from the window starting at the current cursor position, or at *y*, *x* if specified. Attributes are stripped from the characters. If *n* is specified, :meth:`instr` returns a string - at most *n* characters long (exclusive of the trailing NUL). The maximum of *n* - for this method is 2047. + at most *n* characters long (exclusive of the trailing NUL). + The maximum value for *n* is 2047. .. versionchanged:: next The maximum value for *n* is set to 2047. From c061f893bc2f2f02910994729a220d21778895d7 Mon Sep 17 00:00:00 2001 From: tigerding Date: Tue, 20 May 2025 11:15:57 -0400 Subject: [PATCH 21/28] change from const int to Py_ssize_t --- Modules/_cursesmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 0a5de1d9ec1aa4..3cdb8cf0334d8c 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -2059,7 +2059,7 @@ PyCursesWindow_InStr(PyObject *op, PyObject *args) int rtn; /* could make the buffer size larger/dynamic */ - const int max_buf_size = 2048; + Py_ssize_t max_buf_size = 2048; PyObject *result = PyBytes_FromStringAndSize(NULL, max_buf_size); char *buf = PyBytes_AS_STRING(result); From c3323a3a346473e172fe35938cd65572bc3b6c10 Mon Sep 17 00:00:00 2001 From: tigerding Date: Tue, 20 May 2025 11:18:24 -0400 Subject: [PATCH 22/28] fix indent --- Modules/_cursesmodule.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 3cdb8cf0334d8c..69ef18c4c8c8d2 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -2096,12 +2096,12 @@ PyCursesWindow_InStr(PyObject *op, PyObject *args) } if (rtn == ERR) { - Py_DECREF(result); - return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); + Py_DECREF(result); + return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); } if (_PyBytes_Resize(&result, strlen(buf)) < 0) { - return NULL; + return NULL; } return result; From 12d4a5f8af54e54145721d4fad0a74445771ffbc Mon Sep 17 00:00:00 2001 From: tigerding <43339228+zydtiger@users.noreply.github.com> Date: Tue, 20 May 2025 11:24:14 -0400 Subject: [PATCH 23/28] Update Doc/library/curses.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/library/curses.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst index b02cb95b68a8f9..709ef2895cd847 100644 --- a/Doc/library/curses.rst +++ b/Doc/library/curses.rst @@ -993,6 +993,7 @@ the following methods and attributes: .. versionchanged:: next The maximum value for *n* is set to 2047. + .. method:: window.getyx() Return a tuple ``(y, x)`` of current cursor position relative to the window's From 769ba15fa73272c937ea64008ccb8597b8c77ef8 Mon Sep 17 00:00:00 2001 From: tigerding Date: Tue, 20 May 2025 11:24:44 -0400 Subject: [PATCH 24/28] add mem allocation guard --- Modules/_cursesmodule.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 69ef18c4c8c8d2..294c06f6decd62 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1834,6 +1834,8 @@ PyCursesWindow_GetStr(PyObject *op, PyObject *args) /* could make the buffer size larger/dynamic */ Py_ssize_t max_buf_size = 2048; PyObject *result = PyBytes_FromStringAndSize(NULL, max_buf_size); + if (result == NULL) + return NULL; char *buf = PyBytes_AS_STRING(result); switch (PyTuple_Size(args)) { @@ -2061,6 +2063,8 @@ PyCursesWindow_InStr(PyObject *op, PyObject *args) /* could make the buffer size larger/dynamic */ Py_ssize_t max_buf_size = 2048; PyObject *result = PyBytes_FromStringAndSize(NULL, max_buf_size); + if (result == NULL) + return NULL; char *buf = PyBytes_AS_STRING(result); switch (PyTuple_Size(args)) { From 59b49b40a871f69640b23aa316ed20aba8d19900 Mon Sep 17 00:00:00 2001 From: tigerding Date: Tue, 20 May 2025 11:27:44 -0400 Subject: [PATCH 25/28] remove trailing spaces in line --- Modules/_cursesmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 294c06f6decd62..1e75b6f287ccc8 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -2109,7 +2109,7 @@ PyCursesWindow_InStr(PyObject *op, PyObject *args) } return result; - + error: Py_DECREF(result); return NULL; From 6510d092bb59c0f5fbd8caabac4996e19b2dfa10 Mon Sep 17 00:00:00 2001 From: tigerding Date: Tue, 20 May 2025 11:43:48 -0400 Subject: [PATCH 26/28] fix indent --- Modules/_cursesmodule.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 1e75b6f287ccc8..5e1eccee3e4a89 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1890,8 +1890,8 @@ PyCursesWindow_GetStr(PyObject *op, PyObject *args) } if (rtn == ERR) { - Py_DECREF(result); - return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); + Py_DECREF(result); + return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); } if (_PyBytes_Resize(&result, strlen(buf)) < 0) { From 6eccdc91f1312a37c77dba9de8b0ef8fd17bd442 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Tue, 20 May 2025 16:02:59 -0400 Subject: [PATCH 27/28] update versionchanged to mention it was an increase. --- Doc/library/curses.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst index 709ef2895cd847..a8a472c5654145 100644 --- a/Doc/library/curses.rst +++ b/Doc/library/curses.rst @@ -991,7 +991,7 @@ the following methods and attributes: The maximum value for *n* is 2047. .. versionchanged:: next - The maximum value for *n* is set to 2047. + The maximum value for *n* was increased from 1023 to 2047. .. method:: window.getyx() @@ -1086,7 +1086,7 @@ the following methods and attributes: The maximum value for *n* is 2047. .. versionchanged:: next - The maximum value for *n* is set to 2047. + The maximum value for *n* was increased from 1023 to 2047. .. method:: window.is_linetouched(line) From 769bb873e56cfbccb6986add533e28dd30e112aa Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Tue, 20 May 2025 16:08:28 -0400 Subject: [PATCH 28/28] explicitly use versionchanged 3.14 as that is its own branch now. --- Doc/library/curses.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst index a8a472c5654145..5ec23b61396773 100644 --- a/Doc/library/curses.rst +++ b/Doc/library/curses.rst @@ -990,7 +990,7 @@ the following methods and attributes: Read a bytes object from the user, with primitive line editing capacity. The maximum value for *n* is 2047. - .. versionchanged:: next + .. versionchanged:: 3.14 The maximum value for *n* was increased from 1023 to 2047. @@ -1085,7 +1085,7 @@ the following methods and attributes: at most *n* characters long (exclusive of the trailing NUL). The maximum value for *n* is 2047. - .. versionchanged:: next + .. versionchanged:: 3.14 The maximum value for *n* was increased from 1023 to 2047.