@@ -3801,28 +3801,82 @@ _curses_window_inch_impl(PyCursesWindowObject *self, int group_right_1,
38013801 return PyLong_FromUnsignedLongLong (rtn );
38023802}
38033803
3804+ /* Maximum number of characters, and of bytes, that one cell can hold: a
3805+ spacing character optionally followed by CCHARW_MAX -1 combining
3806+ characters, each encoding to at most MB_CUR_MAX bytes. A narrow build
3807+ stores a single byte per call. */
3808+ #ifdef HAVE_NCURSESW
3809+ # define CURSES_CELL_CHARS ((size_t)CCHARW_MAX)
3810+ # define CURSES_CELL_BYTES (CURSES_CELL_CHARS * (size_t)MB_CUR_MAX)
3811+ #else
3812+ # define CURSES_CELL_CHARS ((size_t)1)
3813+ # define CURSES_CELL_BYTES ((size_t)1)
3814+ #endif
3815+
3816+ /* Number of units that can be read from the read position to the end of the
3817+ line, where units_per_cell is what the caller counts per cell: 1 for cells,
3818+ CURSES_CELL_CHARS for wide characters, CURSES_CELL_BYTES for bytes.
3819+ Return 0 if the position lies outside the window: the curses call then fails
3820+ and the caller returns an empty result. */
3821+ static size_t
3822+ curses_window_line_size (PyCursesWindowObject * self , int use_xy , int y , int x ,
3823+ size_t units_per_cell )
3824+ {
3825+ int cury , curx , maxy , maxx ;
3826+
3827+ getmaxyx (self -> win , maxy , maxx );
3828+ if (use_xy ) {
3829+ cury = y ;
3830+ curx = x ;
3831+ }
3832+ else {
3833+ getyx (self -> win , cury , curx );
3834+ }
3835+ if (cury < 0 || cury >= maxy || curx < 0 || curx >= maxx ) {
3836+ return 0 ;
3837+ }
3838+ size_t cells = (size_t )maxx - (size_t )curx ;
3839+ if (cells > (size_t )INT_MAX / units_per_cell ) {
3840+ return (size_t )INT_MAX ;
3841+ }
3842+ return cells * units_per_cell ;
3843+ }
3844+
3845+ /* Clamp the requested count to what is left on the line; a negative count
3846+ means the whole rest of the line. */
3847+ static size_t
3848+ curses_window_read_size (PyCursesWindowObject * self , int use_xy , int y , int x ,
3849+ int n , size_t units_per_cell )
3850+ {
3851+ size_t max_size = curses_window_line_size (self , use_xy , y , x ,
3852+ units_per_cell );
3853+ return n < 0 ? max_size : Py_MIN ((size_t )n , max_size );
3854+ }
3855+
38043856/* Extract characters from the window into a new bytes object (empty on ERR),
38053857 with attributes and color stripped. Shared by instr() and, without the wide
38063858 library, by in_wstr(). */
38073859static PyObject *
38083860curses_window_instr_bytes (PyCursesWindowObject * self , int use_xy ,
3809- int y , int x , unsigned int n )
3861+ int y , int x , int n )
38103862{
38113863 int rtn ;
3812- unsigned int max_buf_size = 2048 ;
3864+ size_t size = curses_window_read_size (self , use_xy , y , x , n ,
3865+ CURSES_CELL_BYTES );
38133866
3814- n = Py_MIN (n , max_buf_size - 1 );
3815- PyBytesWriter * writer = PyBytesWriter_Create (n + 1 );
3867+ PyBytesWriter * writer = PyBytesWriter_Create ((Py_ssize_t )size + 1 );
38163868 if (writer == NULL ) {
38173869 return NULL ;
38183870 }
38193871 char * buf = PyBytesWriter_GetData (writer );
3872+ /* curses does not terminate the buffer when nothing is read. */
3873+ buf [0 ] = '\0' ;
38203874
38213875 if (use_xy ) {
3822- rtn = mvwinnstr (self -> win , y , x , buf , n );
3876+ rtn = mvwinnstr (self -> win , y , x , buf , ( int ) size );
38233877 }
38243878 else {
3825- rtn = winnstr (self -> win , buf , n );
3879+ rtn = winnstr (self -> win , buf , ( int ) size );
38263880 }
38273881
38283882 if (rtn == ERR ) {
@@ -3841,24 +3895,25 @@ _curses.window.instr
38413895 x: int
38423896 X-coordinate.
38433897 ]
3844- n: unsigned_int = 2047
3845- Maximal number of characters.
3898+ n: int = -1
3899+ Maximal number of characters; negative means to the end of the line .
38463900 /
38473901
38483902Return a string of characters, extracted from the window.
38493903
38503904Return a string of characters, extracted from the window starting
38513905at the current cursor position, or at y, x if specified, and
38523906stopping at the end of the line. Attributes and color
3853- information are stripped from the characters. If n is specified,
3854- instr() returns a string at most n characters long (exclusive of
3855- the trailing NUL).
3907+ information are stripped from the characters. If n is specified
3908+ and nonnegative, instr() returns a string at most n characters
3909+ long (exclusive of the trailing NUL); by default, or if n is
3910+ negative, the rest of the line is returned.
38563911[clinic start generated code]*/
38573912
38583913static PyObject *
38593914_curses_window_instr_impl (PyCursesWindowObject * self , int group_left_1 ,
3860- int y , int x , unsigned int n )
3861- /*[clinic end generated code: output=40081f67070132da input=85e62048d2d92642 ]*/
3915+ int y , int x , int n )
3916+ /*[clinic end generated code: output=55246cfe039301bb input=6c052d79ef35ce11 ]*/
38623917{
38633918 return curses_window_instr_bytes (self , group_left_1 , y , x , n );
38643919}
@@ -3956,8 +4011,8 @@ _curses.window.in_wstr
39564011 x: int
39574012 X-coordinate.
39584013 ]
3959- n: unsigned_int = 2047
3960- Maximal number of characters.
4014+ n: int = -1
4015+ Maximal number of characters; negative means to the end of the line .
39614016 /
39624017
39634018Return a string of characters, extracted from the window.
@@ -3967,24 +4022,24 @@ This is the wide-character variant of instr(); it returns a str.
39674022
39684023static PyObject *
39694024_curses_window_in_wstr_impl (PyCursesWindowObject * self , int group_left_1 ,
3970- int y , int x , unsigned int n )
3971- /*[clinic end generated code: output=e3db72a1f10b9875 input=196703989dc57361 ]*/
4025+ int y , int x , int n )
4026+ /*[clinic end generated code: output=5137f0184e742a46 input=5fb03546802762cb ]*/
39724027{
39734028#ifdef HAVE_NCURSESW
39744029 int rtn ;
3975- unsigned int max_buf_size = 2048 ;
4030+ size_t size = curses_window_read_size (self , group_left_1 , y , x , n ,
4031+ CURSES_CELL_CHARS );
39764032
3977- n = Py_MIN (n , max_buf_size - 1 );
3978- wchar_t * buf = PyMem_New (wchar_t , n + 1 );
4033+ wchar_t * buf = PyMem_New (wchar_t , size + 1 );
39794034 if (buf == NULL ) {
39804035 return PyErr_NoMemory ();
39814036 }
39824037
39834038 if (group_left_1 ) {
3984- rtn = mvwinnwstr (self -> win , y , x , buf , n );
4039+ rtn = mvwinnwstr (self -> win , y , x , buf , ( int ) size );
39854040 }
39864041 else {
3987- rtn = winnwstr (self -> win , buf , n );
4042+ rtn = winnwstr (self -> win , buf , ( int ) size );
39884043 }
39894044
39904045 if (rtn == ERR ) {
@@ -4018,8 +4073,8 @@ _curses.window.in_wchstr
40184073 x: int
40194074 X-coordinate.
40204075 ]
4021- n: unsigned_int = 2047
4022- Maximal number of cells.
4076+ n: int = -1
4077+ Maximal number of cells; negative means to the end of the line .
40234078 /
40244079
40254080Return a complexstr of the styled cells extracted from the window.
@@ -4031,28 +4086,27 @@ complexstr.
40314086
40324087static PyObject *
40334088_curses_window_in_wchstr_impl (PyCursesWindowObject * self , int group_left_1 ,
4034- int y , int x , unsigned int n )
4035- /*[clinic end generated code: output=7fb5216f2088835b input=b725c0b8abff62c2 ]*/
4089+ int y , int x , int n )
4090+ /*[clinic end generated code: output=ce23cd8f85982884 input=8a1138d6fae0e389 ]*/
40364091{
40374092 int rtn ;
4038- unsigned int max_buf_size = 2048 ;
4093+ size_t size = curses_window_read_size ( self , group_left_1 , y , x , n , 1 ) ;
40394094
4040- n = Py_MIN (n , max_buf_size - 1 );
40414095 cursesmodule_state * state = get_cursesmodule_state_by_win (self );
40424096 /* Zero the cells: reading a cell back through getcchar() relies on the
40434097 cchar_t text array being NUL-terminated, which some curses libraries
40444098 only guarantee for the characters they actually write. */
4045- curses_cell_t * buf = PyMem_Calloc (n + 1 , sizeof (curses_cell_t ));
4099+ curses_cell_t * buf = PyMem_Calloc (size + 1 , sizeof (curses_cell_t ));
40464100 if (buf == NULL ) {
40474101 return PyErr_NoMemory ();
40484102 }
40494103
40504104#ifdef HAVE_NCURSESW
40514105 if (group_left_1 ) {
4052- rtn = mvwin_wchnstr (self -> win , y , x , buf , n );
4106+ rtn = mvwin_wchnstr (self -> win , y , x , buf , ( int ) size );
40534107 }
40544108 else {
4055- rtn = win_wchnstr (self -> win , buf , n );
4109+ rtn = win_wchnstr (self -> win , buf , ( int ) size );
40564110 }
40574111
40584112 if (rtn == ERR ) {
@@ -4064,7 +4118,7 @@ _curses_window_in_wchstr_impl(PyCursesWindowObject *self, int group_left_1,
40644118 the actual count; every real cell holds at least a space, so the first
40654119 empty cell marks the end of the run. */
40664120 Py_ssize_t count = 0 ;
4067- while (count < (Py_ssize_t )n ) {
4121+ while (count < (Py_ssize_t )size ) {
40684122 wchar_t wstr [CCHARW_MAX + 1 ];
40694123 attr_t attrs ;
40704124 int pair ;
@@ -4079,12 +4133,12 @@ _curses_window_in_wchstr_impl(PyCursesWindowObject *self, int group_left_1,
40794133 /* winchnstr() is not guaranteed (SVr4) to terminate the array, so pre-zero
40804134 it and stop at the first empty cell; a painted cell always holds at least
40814135 a space, never 0. */
4082- memset (buf , 0 , (( size_t ) n + 1 ) * sizeof (curses_cell_t ));
4136+ memset (buf , 0 , (size + 1 ) * sizeof (curses_cell_t ));
40834137 if (group_left_1 ) {
4084- rtn = mvwinchnstr (self -> win , y , x , buf , n );
4138+ rtn = mvwinchnstr (self -> win , y , x , buf , ( int ) size );
40854139 }
40864140 else {
4087- rtn = winchnstr (self -> win , buf , n );
4141+ rtn = winchnstr (self -> win , buf , ( int ) size );
40884142 }
40894143
40904144 if (rtn == ERR ) {
@@ -4093,7 +4147,7 @@ _curses_window_in_wchstr_impl(PyCursesWindowObject *self, int group_left_1,
40934147 }
40944148
40954149 Py_ssize_t count = 0 ;
4096- while (count < (Py_ssize_t )n && buf [count ] != 0 ) {
4150+ while (count < (Py_ssize_t )size && buf [count ] != 0 ) {
40974151 count ++ ;
40984152 }
40994153#endif
0 commit comments