Skip to content

Commit 08d169f

Browse files
sorciohugovkambv
authored
gh-109617: fix ncurses incompatibility on macOS with Xcode 15 (#111258)
Co-authored-by: Hugo van Kemenade <[email protected]> Co-authored-by: Łukasz Langa <[email protected]>
1 parent 291cfa4 commit 08d169f

File tree

5 files changed

+77
-21
lines changed

5 files changed

+77
-21
lines changed

Include/py_curses.h

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,16 @@
2323
# endif
2424
#endif
2525

26-
#if !defined(HAVE_CURSES_IS_PAD) && defined(WINDOW_HAS_FLAGS)
27-
/* The following definition is necessary for ncurses 5.7; without it,
28-
some of [n]curses.h set NCURSES_OPAQUE to 1, and then Python
29-
can't get at the WINDOW flags field. */
26+
#if defined(WINDOW_HAS_FLAGS) && defined(__APPLE__)
27+
/* gh-109617, gh-115383: we can rely on the default value for NCURSES_OPAQUE on
28+
most platforms, but not on macOS. This is because, starting with Xcode 15,
29+
Apple-provided ncurses.h comes from ncurses 6 (which defaults to opaque
30+
structs) but can still be linked to older versions of ncurses dynamic
31+
libraries which don't provide functions such as is_pad() to deal with opaque
32+
structs. Setting NCURSES_OPAQUE to 0 is harmless in all ncurses releases to
33+
this date (provided that a thread-safe implementation is not required), but
34+
this might change in the future. This fix might become irrelevant once
35+
support for macOS 13 or earlier is dropped. */
3036
#define NCURSES_OPAQUE 0
3137
#endif
3238

@@ -39,7 +45,10 @@
3945
#ifdef HAVE_NCURSES_H
4046
/* configure was checking <curses.h>, but we will
4147
use <ncurses.h>, which has some or all these features. */
42-
#if !defined(WINDOW_HAS_FLAGS) && !(NCURSES_OPAQUE+0)
48+
#if !defined(WINDOW_HAS_FLAGS) && \
49+
(NCURSES_VERSION_PATCH+0 < 20070303 || !(NCURSES_OPAQUE+0))
50+
/* the WINDOW flags field was always accessible in ncurses prior to 20070303;
51+
after that, it depends on the value of NCURSES_OPAQUE. */
4352
#define WINDOW_HAS_FLAGS 1
4453
#endif
4554
#if !defined(HAVE_CURSES_IS_PAD) && NCURSES_VERSION_PATCH+0 >= 20090906
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:mod:`ncurses`: fixed a crash that could occur on macOS 13 or earlier when
2+
Python was built with Apple Xcode 15's SDK.

Modules/_cursesmodule.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,8 +1156,10 @@ int py_mvwdelch(WINDOW *w, int y, int x)
11561156
#endif
11571157

11581158
#if defined(HAVE_CURSES_IS_PAD)
1159+
// is_pad() is defined, either as a macro or as a function
11591160
#define py_is_pad(win) is_pad(win)
11601161
#elif defined(WINDOW_HAS_FLAGS)
1162+
// is_pad() is not defined, but we can inspect WINDOW structure members
11611163
#define py_is_pad(win) ((win) ? ((win)->_flags & _ISPAD) != 0 : FALSE)
11621164
#endif
11631165

@@ -4586,17 +4588,24 @@ make_ncurses_version(PyTypeObject *type)
45864588
if (ncurses_version == NULL) {
45874589
return NULL;
45884590
}
4589-
4591+
const char *str = curses_version();
4592+
unsigned long major = 0, minor = 0, patch = 0;
4593+
if (!str || sscanf(str, "%*[^0-9]%lu.%lu.%lu", &major, &minor, &patch) < 3) {
4594+
// Fallback to header version, which cannot be that wrong
4595+
major = NCURSES_VERSION_MAJOR;
4596+
minor = NCURSES_VERSION_MINOR;
4597+
patch = NCURSES_VERSION_PATCH;
4598+
}
45904599
#define SetIntItem(flag) \
45914600
PyStructSequence_SET_ITEM(ncurses_version, pos++, PyLong_FromLong(flag)); \
45924601
if (PyErr_Occurred()) { \
45934602
Py_CLEAR(ncurses_version); \
45944603
return NULL; \
45954604
}
45964605

4597-
SetIntItem(NCURSES_VERSION_MAJOR)
4598-
SetIntItem(NCURSES_VERSION_MINOR)
4599-
SetIntItem(NCURSES_VERSION_PATCH)
4606+
SetIntItem(major)
4607+
SetIntItem(minor)
4608+
SetIntItem(patch)
46004609
#undef SetIntItem
46014610

46024611
return ncurses_version;

configure

Lines changed: 44 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

configure.ac

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6713,7 +6713,10 @@ AC_DEFUN([PY_CHECK_CURSES_FUNC],
67136713
[py_var],
67146714
[AC_COMPILE_IFELSE(
67156715
[AC_LANG_PROGRAM(
6716-
[@%:@include <curses.h>], [
6716+
[
6717+
#define NCURSES_OPAQUE 0
6718+
#include <curses.h>
6719+
], [
67176720
#ifndef $1
67186721
void *x=$1
67196722
#endif

0 commit comments

Comments
 (0)