Bug report
window.instr(), in_wstr(), in_wchstr(), getstr() and get_wstr() pass n to the library and allocate n + 1 elements for the result. That encodes ncurses' reading of n: it stores n characters and adds a terminator, writing n + 1 elements in all.
X/Open does not say that. For innstr() it says the functions "store at most n bytes in the string pointed to by str", for innwstr() "at most n characters in the array", for in_wchnstr() "at most n cchar_t elements" -- and none of the three mentions a terminating null at all. NetBSD curses reads that limit as covering the terminator, so it stores n - 1 characters, and every one of these methods returns one character too few there. Window containing "abcde", n = 5: ncurses gives "abcde", NetBSD gives "abcd".
Both readings are defensible, and NetBSD documents its own (curses_inch.3: read "until either n - 1 characters are read"). The problem is ours: we ask for n and assume ncurses' answer.
Asking a library that counts the terminator for n + 1 gives the n characters ncurses returns:
#if defined(NCURSES_VERSION) || defined(PDCURSES)
# define CURSES_STR_EXTRA 0
#else
# define CURSES_STR_EXTRA 1
#endif
PDCurses follows ncurses (measured), so only the unknown case is bumped, and the buffers become n + 1 + CURSES_STR_EXTRA so that such a library cannot overrun them. Note that the extra element does not always add exactly one character: X/Open requires these functions to store only whole characters ("If the array is not large enough to contain any complete characters, the function fails"), so with multi-byte or combining characters the last cell is admitted as a whole or not at all.
test_curses against NetBSD native curses goes from 34 to 22 failures on main; builds against ncurses are unchanged.
The strlen() side of this is gh-154781: in_wstr() scans for a terminator that winnwstr() need not have written. X/Open supports the fix suggested there -- it promises no terminator for the innstr() family and specifies that these functions return the number of characters actually read, so the return value is the length. The same holds for instr(), which gh-154781 reports as unaffected: it works only because both libraries happen to terminate. getstr() and get_wstr() are the opposite case -- getstr() "is then terminated with a null byte", while getnstr() returns OK, not a count -- so they must keep scanning for the terminator.
Linked PRs
Bug report
window.instr(),in_wstr(),in_wchstr(),getstr()andget_wstr()passnto the library and allocaten + 1elements for the result. That encodes ncurses' reading ofn: it storesncharacters and adds a terminator, writingn + 1elements in all.X/Open does not say that. For
innstr()it says the functions "store at mostnbytes in the string pointed to bystr", forinnwstr()"at mostncharacters in the array", forin_wchnstr()"at mostncchar_t elements" -- and none of the three mentions a terminating null at all. NetBSD curses reads that limit as covering the terminator, so it storesn - 1characters, and every one of these methods returns one character too few there. Window containing"abcde",n = 5: ncurses gives"abcde", NetBSD gives"abcd".Both readings are defensible, and NetBSD documents its own (
curses_inch.3: read "until eithern - 1characters are read"). The problem is ours: we ask fornand assume ncurses' answer.Asking a library that counts the terminator for
n + 1gives thencharacters ncurses returns:PDCurses follows ncurses (measured), so only the unknown case is bumped, and the buffers become
n + 1 + CURSES_STR_EXTRAso that such a library cannot overrun them. Note that the extra element does not always add exactly one character: X/Open requires these functions to store only whole characters ("If the array is not large enough to contain any complete characters, the function fails"), so with multi-byte or combining characters the last cell is admitted as a whole or not at all.test_cursesagainst NetBSD native curses goes from 34 to 22 failures on main; builds against ncurses are unchanged.The
strlen()side of this is gh-154781:in_wstr()scans for a terminator thatwinnwstr()need not have written. X/Open supports the fix suggested there -- it promises no terminator for theinnstr()family and specifies that these functions return the number of characters actually read, so the return value is the length. The same holds forinstr(), which gh-154781 reports as unaffected: it works only because both libraries happen to terminate.getstr()andget_wstr()are the opposite case --getstr()"is then terminated with a null byte", whilegetnstr()returnsOK, not a count -- so they must keep scanning for the terminator.Linked PRs