From 3ce06f4ca260d1a66960cb9bf3861e9f5a077ca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 24 Aug 2024 15:02:05 +0200 Subject: [PATCH 01/12] Fix reference leaks in the error-branches. --- Modules/_cursesmodule.c | 347 ++++++++++++++++++++-------------------- 1 file changed, 177 insertions(+), 170 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index b5854e8c33f28a..5722471bf5eb7b 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -199,6 +199,36 @@ static char *screen_encoding = NULL; "must call start_color() first"); \ return 0; } +#define CHECK_NOT_NULL_OR_ERROR(VALUE) \ + do { \ + if ((VALUE) == NULL) { \ + goto error; \ + } \ + } while (0) + +#define CHECK_RET_CODE_OR_ERROR(STATUS) \ + do { \ + if ((STATUS) < 0) { \ + goto error; \ + } \ + } while (0) + +#define CHECK_RET_FLAG_OR_ERROR(STATUS) \ + do { \ + if (!(STATUS)) { \ + goto error; \ + } \ + } while (0) + +#define SET_DICT_INT_VALUE_OR_ERROR(DICT_OBJECT, STRING, VALUE) \ + do { \ + PyObject *value = PyLong_FromLong((long)(VALUE)); \ + CHECK_NOT_NULL_OR_ERROR(value); \ + int rc = PyDict_SetItemString(DICT_OBJECT, STRING, value); \ + Py_DECREF(value); \ + CHECK_RET_CODE_OR_ERROR(rc); \ + } while (0) + /* Utility Functions */ /* @@ -716,9 +746,14 @@ PyCursesWindow_New(WINDOW *win, const char *encoding) static void PyCursesWindow_Dealloc(PyCursesWindowObject *wo) { - if (wo->win != stdscr) delwin(wo->win); - if (wo->encoding != NULL) + if (wo->win != stdscr && wo->win != NULL) { + delwin(wo->win); + wo->win = NULL; + } + if (wo->encoding != NULL) { PyMem_Free(wo->encoding); + wo->encoding = NULL; + } PyObject_Free(wo); } @@ -3251,7 +3286,8 @@ _curses_init_pair_impl(PyObject *module, int pair_number, int fg, int bg) Py_RETURN_NONE; } -static PyObject *ModDict; +// Borrowed reference to this module's internal dictionary. +static PyObject *PRIVATE_MOD_DICT; /*[clinic input] _curses.initscr @@ -3266,7 +3302,6 @@ _curses_initscr_impl(PyObject *module) /*[clinic end generated code: output=619fb68443810b7b input=514f4bce1821f6b5]*/ { WINDOW *win; - PyCursesWindowObject *winobj; if (initialised) { wrefresh(stdscr); @@ -3284,15 +3319,11 @@ _curses_initscr_impl(PyObject *module) /* This was moved from initcurses() because it core dumped on SGI, where they're not defined until you've called initscr() */ -#define SetDictInt(string,ch) \ - do { \ - PyObject *o = PyLong_FromLong((long) (ch)); \ - if (o && PyDict_SetItemString(ModDict, string, o) == 0) { \ - Py_DECREF(o); \ - } \ - } while (0) /* Here are some graphic symbols you can use */ +#define SetDictInt(NAME, VALUE) \ + SET_DICT_INT_VALUE_OR_ERROR(PRIVATE_MOD_DICT, NAME, VALUE) + SetDictInt("ACS_ULCORNER", (ACS_ULCORNER)); SetDictInt("ACS_LLCORNER", (ACS_LLCORNER)); SetDictInt("ACS_URCORNER", (ACS_URCORNER)); @@ -3361,10 +3392,15 @@ _curses_initscr_impl(PyObject *module) SetDictInt("LINES", LINES); SetDictInt("COLS", COLS); +#undef SetDictInt - winobj = (PyCursesWindowObject *)PyCursesWindow_New(win, NULL); + PyCursesWindowObject *winobj = (PyCursesWindowObject *)PyCursesWindow_New(win, NULL); + CHECK_NOT_NULL_OR_ERROR(winobj); screen_encoding = winobj->encoding; return (PyObject *)winobj; + +error: + return NULL; } /*[clinic input] @@ -3970,46 +4006,26 @@ _curses_qiflush_impl(PyObject *module, int flag) static int update_lines_cols(void) { - PyObject *o; - PyObject *m = PyImport_ImportModule("curses"); - - if (!m) - return 0; - + PyObject *exposed_module = NULL, *o = NULL; + exposed_module = PyImport_ImportModule("curses"); + CHECK_NOT_NULL_OR_ERROR(exposed_module); o = PyLong_FromLong(LINES); - if (!o) { - Py_DECREF(m); - return 0; - } - if (PyObject_SetAttrString(m, "LINES", o)) { - Py_DECREF(m); - Py_DECREF(o); - return 0; - } - if (PyDict_SetItemString(ModDict, "LINES", o)) { - Py_DECREF(m); - Py_DECREF(o); - return 0; - } + CHECK_NOT_NULL_OR_ERROR(o); + CHECK_RET_CODE_OR_ERROR(PyObject_SetAttrString(exposed_module, "LINES", o)); + CHECK_RET_CODE_OR_ERROR(PyDict_SetItemString(PRIVATE_MOD_DICT, "LINES", o)); Py_DECREF(o); o = PyLong_FromLong(COLS); - if (!o) { - Py_DECREF(m); - return 0; - } - if (PyObject_SetAttrString(m, "COLS", o)) { - Py_DECREF(m); - Py_DECREF(o); - return 0; - } - if (PyDict_SetItemString(ModDict, "COLS", o)) { - Py_DECREF(m); - Py_DECREF(o); - return 0; - } + CHECK_NOT_NULL_OR_ERROR(o); + CHECK_RET_CODE_OR_ERROR(PyObject_SetAttrString(exposed_module, "COLS", o)); + CHECK_RET_CODE_OR_ERROR(PyDict_SetItemString(PRIVATE_MOD_DICT, "COLS", o)); Py_DECREF(o); - Py_DECREF(m); + Py_DECREF(exposed_module); return 1; + +error: + Py_XDECREF(o); + Py_XDECREF(exposed_module); + return 0; } /*[clinic input] @@ -4101,18 +4117,16 @@ static PyObject * _curses_resizeterm_impl(PyObject *module, int nlines, int ncols) /*[clinic end generated code: output=56d6bcc5194ad055 input=0fca02ebad5ffa82]*/ { - PyObject *result; - PyCursesInitialised; - result = PyCursesCheckERR(resizeterm(nlines, ncols), "resizeterm"); - if (!result) - return NULL; - if (!update_lines_cols()) { - Py_DECREF(result); - return NULL; - } + PyObject *result = PyCursesCheckERR(resizeterm(nlines, ncols), "resizeterm"); + CHECK_NOT_NULL_OR_ERROR(result); + CHECK_RET_FLAG_OR_ERROR(update_lines_cols()); return result; + +error: + Py_XDECREF(result); + return NULL; } #endif @@ -4140,18 +4154,16 @@ static PyObject * _curses_resize_term_impl(PyObject *module, int nlines, int ncols) /*[clinic end generated code: output=9e26d8b9ea311ed2 input=2197edd05b049ed4]*/ { - PyObject *result; - PyCursesInitialised; - result = PyCursesCheckERR(resize_term(nlines, ncols), "resize_term"); - if (!result) - return NULL; - if (!update_lines_cols()) { - Py_DECREF(result); - return NULL; - } + PyObject *result = PyCursesCheckERR(resize_term(nlines, ncols), "resize_term"); + CHECK_NOT_NULL_OR_ERROR(result); + CHECK_RET_FLAG_OR_ERROR(update_lines_cols()); return result; + +error: + Py_XDECREF(result); + return NULL; } #endif /* HAVE_CURSES_RESIZE_TERM */ @@ -4210,35 +4222,21 @@ static PyObject * _curses_start_color_impl(PyObject *module) /*[clinic end generated code: output=8b772b41d8090ede input=0ca0ecb2b77e1a12]*/ { - int code; - PyObject *c, *cp; - PyCursesInitialised; - code = start_color(); - if (code != ERR) { + if (start_color() != ERR) { initialisedcolors = TRUE; - c = PyLong_FromLong((long) COLORS); - if (c == NULL) - return NULL; - if (PyDict_SetItemString(ModDict, "COLORS", c) < 0) { - Py_DECREF(c); - return NULL; - } - Py_DECREF(c); - cp = PyLong_FromLong((long) COLOR_PAIRS); - if (cp == NULL) - return NULL; - if (PyDict_SetItemString(ModDict, "COLOR_PAIRS", cp) < 0) { - Py_DECREF(cp); - return NULL; - } - Py_DECREF(cp); + SET_DICT_INT_VALUE_OR_ERROR(PRIVATE_MOD_DICT, "COLORS", COLORS); + SET_DICT_INT_VALUE_OR_ERROR(PRIVATE_MOD_DICT, "COLOR_PAIRS", COLOR_PAIRS); Py_RETURN_NONE; - } else { + } + else { PyErr_SetString(PyCursesError, "start_color() returned ERR"); return NULL; } + +error: + return NULL; } /*[clinic input] @@ -4595,10 +4593,7 @@ static PyStructSequence_Desc ncurses_version_desc = { static PyObject * make_ncurses_version(PyTypeObject *type) { - PyObject *ncurses_version; - int pos = 0; - - ncurses_version = PyStructSequence_New(type); + PyObject *ncurses_version = PyStructSequence_New(type); if (ncurses_version == NULL) { return NULL; } @@ -4610,19 +4605,22 @@ make_ncurses_version(PyTypeObject *type) minor = NCURSES_VERSION_MINOR; patch = NCURSES_VERSION_PATCH; } -#define SetIntItem(flag) \ - PyStructSequence_SET_ITEM(ncurses_version, pos++, PyLong_FromLong(flag)); \ - if (PyErr_Occurred()) { \ - Py_CLEAR(ncurses_version); \ - return NULL; \ - } - - SetIntItem(major) - SetIntItem(minor) - SetIntItem(patch) -#undef SetIntItem +#define SET_VERSION_COMPONENT(INDEX, VALUE) \ + do { \ + PyObject *o = PyLong_FromLong(VALUE); \ + CHECK_NOT_NULL_OR_ERROR(o); \ + PyStructSequence_SET_ITEM(ncurses_version, INDEX, o); \ + } while (0) + SET_VERSION_COMPONENT(0, major); + SET_VERSION_COMPONENT(1, minor); + SET_VERSION_COMPONENT(2, patch); +#undef SET_VERSION_COMPONENT return ncurses_version; + +error: + Py_DECREF(ncurses_version); + return NULL; } #endif /* NCURSES_VERSION */ @@ -4756,30 +4754,27 @@ curses_destructor(PyObject *op) PyMODINIT_FUNC PyInit__curses(void) { - PyObject *m, *d, *v, *c_api_object; + PyObject *mod = NULL; /* Initialize object type */ - if (PyType_Ready(&PyCursesWindow_Type) < 0) - return NULL; + CHECK_RET_CODE_OR_ERROR(PyType_Ready(&PyCursesWindow_Type)); /* Create the module and add the functions */ - m = PyModule_Create(&_cursesmodule); - if (m == NULL) - return NULL; + mod = PyModule_Create(&_cursesmodule); + CHECK_NOT_NULL_OR_ERROR(mod); #ifdef Py_GIL_DISABLED - PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); + CHECK_RET_CODE_OR_ERROR(PyUnstable_Module_SetGIL(mod, Py_MOD_GIL_NOT_USED)); #endif /* Add some symbolic constants to the module */ - d = PyModule_GetDict(m); - if (d == NULL) - return NULL; - ModDict = d; /* For PyCurses_InitScr to use later */ + PyObject *d = PyModule_GetDict(mod); + CHECK_NOT_NULL_OR_ERROR(d); + PRIVATE_MOD_DICT = d; /* For PyCurses_InitScr to use later */ void **PyCurses_API = PyMem_Calloc(PyCurses_API_pointers, sizeof(void *)); if (PyCurses_API == NULL) { PyErr_NoMemory(); - return NULL; + goto error; } /* Initialize the C API pointer array */ PyCurses_API[0] = (void *)Py_NewRef(&PyCursesWindow_Type); @@ -4788,46 +4783,50 @@ PyInit__curses(void) PyCurses_API[3] = (void *)func_PyCursesInitialisedColor; /* Add a capsule for the C API */ - c_api_object = PyCapsule_New(PyCurses_API, PyCurses_CAPSULE_NAME, - curses_destructor); + PyObject *c_api_object = PyCapsule_New(PyCurses_API, PyCurses_CAPSULE_NAME, + curses_destructor); if (c_api_object == NULL) { Py_DECREF(PyCurses_API[0]); PyMem_Free(PyCurses_API); - return NULL; - } - if (PyDict_SetItemString(d, "_C_API", c_api_object) < 0) { - Py_DECREF(c_api_object); - return NULL; + goto error; } + int rc = PyDict_SetItemString(d, "_C_API", c_api_object); Py_DECREF(c_api_object); + CHECK_RET_CODE_OR_ERROR(rc); /* For exception curses.error */ PyCursesError = PyErr_NewException("_curses.error", NULL, NULL); - PyDict_SetItemString(d, "error", PyCursesError); + CHECK_NOT_NULL_OR_ERROR(PyCursesError); + rc = PyDict_SetItemString(d, "error", PyCursesError); + Py_DECREF(PyCursesError); + CHECK_RET_CODE_OR_ERROR(rc); /* Make the version available */ - v = PyBytes_FromString(PyCursesVersion); - PyDict_SetItemString(d, "version", v); - PyDict_SetItemString(d, "__version__", v); - Py_DECREF(v); + PyObject *curses_version = PyBytes_FromString(PyCursesVersion); + CHECK_NOT_NULL_OR_ERROR(curses_version); + rc = PyDict_SetItemString(d, "version", curses_version); + Py_DECREF(curses_version); + CHECK_RET_CODE_OR_ERROR(rc); + Py_INCREF(curses_version); + rc = PyDict_SetItemString(d, "__version__", curses_version); + Py_CLEAR(curses_version); + CHECK_RET_CODE_OR_ERROR(rc); #ifdef NCURSES_VERSION /* ncurses_version */ PyTypeObject *version_type; version_type = _PyStructSequence_NewType(&ncurses_version_desc, Py_TPFLAGS_DISALLOW_INSTANTIATION); - if (version_type == NULL) { - return NULL; - } - v = make_ncurses_version(version_type); + CHECK_NOT_NULL_OR_ERROR(version_type); + PyObject *ncurses_version = make_ncurses_version(version_type); Py_DECREF(version_type); - if (v == NULL) { - return NULL; - } - PyDict_SetItemString(d, "ncurses_version", v); - Py_DECREF(v); + CHECK_NOT_NULL_OR_ERROR(ncurses_version); + rc = PyDict_SetItemString(d, "ncurses_version", ncurses_version); + Py_CLEAR(ncurses_version); + CHECK_RET_CODE_OR_ERROR(rc); #endif /* NCURSES_VERSION */ +#define SetDictInt(NAME, VALUE) SET_DICT_INT_VALUE_OR_ERROR(d, NAME, VALUE) SetDictInt("ERR", ERR); SetDictInt("OK", OK); @@ -4923,43 +4922,51 @@ PyInit__curses(void) SetDictInt("REPORT_MOUSE_POSITION", REPORT_MOUSE_POSITION); #endif /* Now set everything up for KEY_ variables */ - { - int key; - char *key_n; - char *key_n2; - for (key=KEY_MIN;key < KEY_MAX; key++) { - key_n = (char *)keyname(key); - if (key_n == NULL || strcmp(key_n,"UNKNOWN KEY")==0) - continue; - if (strncmp(key_n,"KEY_F(",6)==0) { - char *p1, *p2; - key_n2 = PyMem_Malloc(strlen(key_n)+1); - if (!key_n2) { - PyErr_NoMemory(); - break; - } - p1 = key_n; - p2 = key_n2; - while (*p1) { - if (*p1 != '(' && *p1 != ')') { - *p2 = *p1; - p2++; - } - p1++; + for (int keycode = KEY_MIN; keycode < KEY_MAX; keycode++) { + const char *key_name = keyname(keycode); + if (key_name == NULL || strcmp(key_name, "UNKNOWN KEY") == 0) { + continue; + } + if (strncmp(key_name, "KEY_F(", 6) == 0) { + char *fn_key_name = PyMem_Malloc(strlen(key_name) + 1); + if (!fn_key_name) { + PyErr_NoMemory(); + goto error; + } + const char *p1 = key_name; + char *p2 = fn_key_name; + while (*p1) { + if (*p1 != '(' && *p1 != ')') { + *p2 = *p1; + p2++; } - *p2 = (char)0; - } else - key_n2 = key_n; - SetDictInt(key_n2,key); - if (key_n2 != key_n) - PyMem_Free(key_n2); + p1++; + } + *p2 = (char)0; + PyObject *p_keycode = PyLong_FromLong((long)keycode); + CHECK_NOT_NULL_OR_ERROR(p_keycode); + int rc = PyDict_SetItemString(d, fn_key_name, p_keycode); + Py_DECREF(p_keycode); + PyMem_Free(fn_key_name); + CHECK_RET_CODE_OR_ERROR(rc); + } + else { + SetDictInt(key_name, keycode); } - SetDictInt("KEY_MIN", KEY_MIN); - SetDictInt("KEY_MAX", KEY_MAX); } + SetDictInt("KEY_MIN", KEY_MIN); + SetDictInt("KEY_MAX", KEY_MAX); +#undef SetDictInt - if (PyModule_AddType(m, &PyCursesWindow_Type) < 0) { - return NULL; - } - return m; + CHECK_RET_CODE_OR_ERROR(PyModule_AddType(mod, &PyCursesWindow_Type)); + return mod; + +error: + Py_XDECREF(mod); + return NULL; } + +#undef SET_DICT_INT_VALUE_OR_ERROR +#undef CHECK_RET_FLAG_OR_ERROR +#undef CHECK_RET_CODE_OR_ERROR +#undef CHECK_NOT_NULL_OR_ERROR From 81bcde89122edb4c0a914ba7306ca51edaabcffd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 10 Sep 2024 15:55:03 +0200 Subject: [PATCH 02/12] protect macros expansions using `do { ... } while (0)` constructions --- Modules/_cursesmodule.c | 47 ++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 5722471bf5eb7b..2b5b7bca53cab8 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -182,22 +182,31 @@ static char *screen_encoding = NULL; /* Utility Macros */ #define PyCursesSetupTermCalled \ - if (initialised_setupterm != TRUE) { \ - PyErr_SetString(PyCursesError, \ - "must call (at least) setupterm() first"); \ - return 0; } + do { \ + if (initialised_setupterm != TRUE) { \ + PyErr_SetString(PyCursesError, \ + "must call (at least) setupterm() first"); \ + return 0; \ + } \ + } while (0) -#define PyCursesInitialised \ - if (initialised != TRUE) { \ - PyErr_SetString(PyCursesError, \ - "must call initscr() first"); \ - return 0; } +#define PyCursesInitialised \ + do { \ + if (initialised != TRUE) { \ + PyErr_SetString(PyCursesError, \ + "must call initscr() first"); \ + return 0; \ + } \ + } while (0) #define PyCursesInitialisedColor \ - if (initialisedcolors != TRUE) { \ - PyErr_SetString(PyCursesError, \ - "must call start_color() first"); \ - return 0; } + do { \ + if (initialisedcolors != TRUE) { \ + PyErr_SetString(PyCursesError, \ + "must call start_color() first"); \ + return 0; \ + } \ + } while (0) #define CHECK_NOT_NULL_OR_ERROR(VALUE) \ do { \ @@ -2664,12 +2673,12 @@ PyTypeObject PyCursesWindow_Type = { #define NoArgNoReturnFunctionBody(X) \ { \ - PyCursesInitialised \ + PyCursesInitialised; \ return PyCursesCheckERR(X(), # X); } #define NoArgOrFlagNoReturnFunctionBody(X, flag) \ { \ - PyCursesInitialised \ + PyCursesInitialised; \ if (flag) \ return PyCursesCheckERR(X(), # X); \ else \ @@ -2678,23 +2687,23 @@ PyTypeObject PyCursesWindow_Type = { #define NoArgReturnIntFunctionBody(X) \ { \ - PyCursesInitialised \ + PyCursesInitialised; \ return PyLong_FromLong((long) X()); } #define NoArgReturnStringFunctionBody(X) \ { \ - PyCursesInitialised \ + PyCursesInitialised; \ return PyBytes_FromString(X()); } #define NoArgTrueFalseFunctionBody(X) \ { \ - PyCursesInitialised \ + PyCursesInitialised; \ return PyBool_FromLong(X()); } #define NoArgNoReturnVoidFunctionBody(X) \ { \ - PyCursesInitialised \ + PyCursesInitialised; \ X(); \ Py_RETURN_NONE; } From 848cc3e8f61a32f68330508a76fa082a9aa50676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 10 Sep 2024 16:06:32 +0200 Subject: [PATCH 03/12] use uppercase global flags --- Modules/_cursesmodule.c | 67 ++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 2b5b7bca53cab8..ff69612d1d3fa1 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -170,43 +170,42 @@ class _curses.window "PyCursesWindowObject *" "&PyCursesWindow_Type" static PyObject *PyCursesError; /* Tells whether setupterm() has been called to initialise terminfo. */ -static int initialised_setupterm = FALSE; +static int CURSES_SETUPTERM_CALLED = FALSE; /* Tells whether initscr() has been called to initialise curses. */ -static int initialised = FALSE; +static int CURSES_INITSCR_CALLED = FALSE; /* Tells whether start_color() has been called to initialise color usage. */ -static int initialisedcolors = FALSE; +static int CURSES_INITIALISED_COLORS = FALSE; -static char *screen_encoding = NULL; +static char *CURSES_SCREEN_ENCODING = NULL; /* Utility Macros */ -#define PyCursesSetupTermCalled \ - do { \ - if (initialised_setupterm != TRUE) { \ - PyErr_SetString(PyCursesError, \ - "must call (at least) setupterm() first"); \ - return 0; \ - } \ - } while (0) -#define PyCursesInitialised \ - do { \ - if (initialised != TRUE) { \ - PyErr_SetString(PyCursesError, \ - "must call initscr() first"); \ - return 0; \ - } \ +/* + * Check that GLOBAL_FLAG is set, or advise to call FUNC_TO_CALL(). + * + * This macro returns 0 on error so that it can be used both in a + * boolean context and in a function that returns a (PyObject *). + */ +#define CHECK_STATE_FLAG(FUNC_TO_CALL, GLOBAL_FLAG) \ + do { \ + if ((GLOBAL_FLAG) != TRUE) { \ + assert(PyCursesError != NULL); \ + PyErr_SetString(PyCursesError, \ + "must call " FUNC_TO_CALL "() first"); \ + return 0; \ + } \ } while (0) -#define PyCursesInitialisedColor \ - do { \ - if (initialisedcolors != TRUE) { \ - PyErr_SetString(PyCursesError, \ - "must call start_color() first"); \ - return 0; \ - } \ - } while (0) +#define PyCursesSetupTermCalled \ + CHECK_STATE_FLAG("setupterm", CURSES_SETUPTERM_CALLED) + +#define PyCursesInitialised \ + CHECK_STATE_FLAG("initscr", CURSES_INITSCR_CALLED) + +#define PyCursesInitialisedColor \ + CHECK_STATE_FLAG("start_color", CURSES_INITIALISED_COLORS) #define CHECK_NOT_NULL_OR_ERROR(VALUE) \ do { \ @@ -297,7 +296,7 @@ PyCurses_ConvertToChtype(PyCursesWindowObject *win, PyObject *obj, chtype *ch) if (win) encoding = win->encoding; else - encoding = screen_encoding; + encoding = CURSES_SCREEN_ENCODING; bytes = PyUnicode_AsEncodedString(obj, encoding, NULL); if (bytes == NULL) return 0; @@ -3312,7 +3311,7 @@ _curses_initscr_impl(PyObject *module) { WINDOW *win; - if (initialised) { + if (CURSES_INITSCR_CALLED) { wrefresh(stdscr); return (PyObject *)PyCursesWindow_New(stdscr, NULL); } @@ -3324,7 +3323,7 @@ _curses_initscr_impl(PyObject *module) return NULL; } - initialised = initialised_setupterm = TRUE; + CURSES_INITSCR_CALLED = CURSES_SETUPTERM_CALLED = TRUE; /* This was moved from initcurses() because it core dumped on SGI, where they're not defined until you've called initscr() */ @@ -3405,7 +3404,7 @@ _curses_initscr_impl(PyObject *module) PyCursesWindowObject *winobj = (PyCursesWindowObject *)PyCursesWindow_New(win, NULL); CHECK_NOT_NULL_OR_ERROR(winobj); - screen_encoding = winobj->encoding; + CURSES_SCREEN_ENCODING = winobj->encoding; return (PyObject *)winobj; error: @@ -3450,7 +3449,7 @@ _curses_setupterm_impl(PyObject *module, const char *term, int fd) } } - if (!initialised_setupterm && setupterm((char *)term, fd, &err) == ERR) { + if (!CURSES_SETUPTERM_CALLED && setupterm((char *)term, fd, &err) == ERR) { const char* s = "setupterm: unknown error"; if (err == 0) { @@ -3463,7 +3462,7 @@ _curses_setupterm_impl(PyObject *module, const char *term, int fd) return NULL; } - initialised_setupterm = TRUE; + CURSES_SETUPTERM_CALLED = TRUE; Py_RETURN_NONE; } @@ -4234,7 +4233,7 @@ _curses_start_color_impl(PyObject *module) PyCursesInitialised; if (start_color() != ERR) { - initialisedcolors = TRUE; + CURSES_INITIALISED_COLORS = TRUE; SET_DICT_INT_VALUE_OR_ERROR(PRIVATE_MOD_DICT, "COLORS", COLORS); SET_DICT_INT_VALUE_OR_ERROR(PRIVATE_MOD_DICT, "COLOR_PAIRS", COLOR_PAIRS); Py_RETURN_NONE; From ea3fd8da7a8b6066b7f9e2289c74d95c19e6923c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 10 Sep 2024 16:31:42 +0200 Subject: [PATCH 04/12] cosmetic changes --- Modules/_cursesmodule.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index ff69612d1d3fa1..aa373e84d5c837 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -3309,14 +3309,12 @@ static PyObject * _curses_initscr_impl(PyObject *module) /*[clinic end generated code: output=619fb68443810b7b input=514f4bce1821f6b5]*/ { - WINDOW *win; - if (CURSES_INITSCR_CALLED) { wrefresh(stdscr); return (PyObject *)PyCursesWindow_New(stdscr, NULL); } - win = initscr(); + WINDOW *win = initscr(); if (win == NULL) { PyErr_SetString(PyCursesError, catchall_NULL); From 557a53c8eba83b4c3a379e2faa39b448dfc45dcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 10 Sep 2024 16:36:34 +0200 Subject: [PATCH 05/12] improve `SetDictInt` macro --- Modules/_cursesmodule.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index aa373e84d5c837..ea082770cbe718 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -237,6 +237,12 @@ static char *CURSES_SCREEN_ENCODING = NULL; CHECK_RET_CODE_OR_ERROR(rc); \ } while (0) +#define SET_MOD_DICT_INT_VALUE_OR_ERROR(NAME, VALUE) \ + do { \ + assert(PRIVATE_MOD_DICT != NULL); \ + SET_DICT_INT_VALUE_OR_ERROR(PRIVATE_MOD_DICT, (NAME), (VALUE)); \ + } while (0) + /* Utility Functions */ /* @@ -3327,8 +3333,7 @@ _curses_initscr_impl(PyObject *module) where they're not defined until you've called initscr() */ /* Here are some graphic symbols you can use */ -#define SetDictInt(NAME, VALUE) \ - SET_DICT_INT_VALUE_OR_ERROR(PRIVATE_MOD_DICT, NAME, VALUE) +#define SetDictInt SET_MOD_DICT_INT_VALUE_OR_ERROR SetDictInt("ACS_ULCORNER", (ACS_ULCORNER)); SetDictInt("ACS_LLCORNER", (ACS_LLCORNER)); @@ -4832,7 +4837,7 @@ PyInit__curses(void) CHECK_RET_CODE_OR_ERROR(rc); #endif /* NCURSES_VERSION */ -#define SetDictInt(NAME, VALUE) SET_DICT_INT_VALUE_OR_ERROR(d, NAME, VALUE) +#define SetDictInt SET_MOD_DICT_INT_VALUE_OR_ERROR SetDictInt("ERR", ERR); SetDictInt("OK", OK); @@ -4972,6 +4977,7 @@ PyInit__curses(void) return NULL; } +#undef SET_MOD_DICT_INT_VALUE_OR_ERROR #undef SET_DICT_INT_VALUE_OR_ERROR #undef CHECK_RET_FLAG_OR_ERROR #undef CHECK_RET_CODE_OR_ERROR From a92fc1fb8094356bdc4c49491cb9eb7c84b14e95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 10 Sep 2024 16:38:04 +0200 Subject: [PATCH 06/12] undef utililty macros --- Modules/_cursesmodule.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index ea082770cbe718..7ced51a3f911ae 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -4982,3 +4982,4 @@ PyInit__curses(void) #undef CHECK_RET_FLAG_OR_ERROR #undef CHECK_RET_CODE_OR_ERROR #undef CHECK_NOT_NULL_OR_ERROR +#undef CHECK_STATE_FLAG From 9ce028946f6e4f03c5ee603a36a0b7b25587ae43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 10 Sep 2024 16:56:15 +0200 Subject: [PATCH 07/12] improve macro usage --- Modules/_cursesmodule.c | 68 +++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 7ced51a3f911ae..d36ea1892e455a 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -228,19 +228,13 @@ static char *CURSES_SCREEN_ENCODING = NULL; } \ } while (0) -#define SET_DICT_INT_VALUE_OR_ERROR(DICT_OBJECT, STRING, VALUE) \ - do { \ - PyObject *value = PyLong_FromLong((long)(VALUE)); \ - CHECK_NOT_NULL_OR_ERROR(value); \ - int rc = PyDict_SetItemString(DICT_OBJECT, STRING, value); \ - Py_DECREF(value); \ - CHECK_RET_CODE_OR_ERROR(rc); \ - } while (0) - -#define SET_MOD_DICT_INT_VALUE_OR_ERROR(NAME, VALUE) \ +#define DICT_ADD_INT_VALUE_OR_ERROR(DICT_OBJECT, STRING, VALUE) \ do { \ - assert(PRIVATE_MOD_DICT != NULL); \ - SET_DICT_INT_VALUE_OR_ERROR(PRIVATE_MOD_DICT, (NAME), (VALUE)); \ + PyObject *value = PyLong_FromLong((long)(VALUE)); \ + CHECK_NOT_NULL_OR_ERROR(value); \ + int rc = PyDict_SetItemString((DICT_OBJECT), (STRING), value); \ + Py_DECREF(value); \ + CHECK_RET_CODE_OR_ERROR(rc); \ } while (0) /* Utility Functions */ @@ -3300,9 +3294,6 @@ _curses_init_pair_impl(PyObject *module, int pair_number, int fg, int bg) Py_RETURN_NONE; } -// Borrowed reference to this module's internal dictionary. -static PyObject *PRIVATE_MOD_DICT; - /*[clinic input] _curses.initscr @@ -3333,7 +3324,10 @@ _curses_initscr_impl(PyObject *module) where they're not defined until you've called initscr() */ /* Here are some graphic symbols you can use */ -#define SetDictInt SET_MOD_DICT_INT_VALUE_OR_ERROR + PyObject *module_dict = PyModule_GetDict(module); + CHECK_NOT_NULL_OR_ERROR(module_dict); +#define SetDictInt(NAME, VALUE) \ + DICT_ADD_INT_VALUE_OR_ERROR(module_dict, (NAME), (VALUE)) SetDictInt("ACS_ULCORNER", (ACS_ULCORNER)); SetDictInt("ACS_LLCORNER", (ACS_LLCORNER)); @@ -4015,7 +4009,7 @@ _curses_qiflush_impl(PyObject *module, int flag) * and _curses.COLS */ #if defined(HAVE_CURSES_RESIZETERM) || defined(HAVE_CURSES_RESIZE_TERM) static int -update_lines_cols(void) +update_lines_cols(PyObject *private_module) { PyObject *exposed_module = NULL, *o = NULL; exposed_module = PyImport_ImportModule("curses"); @@ -4023,12 +4017,12 @@ update_lines_cols(void) o = PyLong_FromLong(LINES); CHECK_NOT_NULL_OR_ERROR(o); CHECK_RET_CODE_OR_ERROR(PyObject_SetAttrString(exposed_module, "LINES", o)); - CHECK_RET_CODE_OR_ERROR(PyDict_SetItemString(PRIVATE_MOD_DICT, "LINES", o)); + CHECK_RET_CODE_OR_ERROR(PyObject_SetAttrString(private_module, "LINES", o)); Py_DECREF(o); o = PyLong_FromLong(COLS); CHECK_NOT_NULL_OR_ERROR(o); CHECK_RET_CODE_OR_ERROR(PyObject_SetAttrString(exposed_module, "COLS", o)); - CHECK_RET_CODE_OR_ERROR(PyDict_SetItemString(PRIVATE_MOD_DICT, "COLS", o)); + CHECK_RET_CODE_OR_ERROR(PyObject_SetAttrString(private_module, "COLS", o)); Py_DECREF(o); Py_DECREF(exposed_module); return 1; @@ -4048,7 +4042,7 @@ static PyObject * _curses_update_lines_cols_impl(PyObject *module) /*[clinic end generated code: output=423f2b1e63ed0f75 input=5f065ab7a28a5d90]*/ { - if (!update_lines_cols()) { + if (!update_lines_cols(module)) { return NULL; } Py_RETURN_NONE; @@ -4132,7 +4126,7 @@ _curses_resizeterm_impl(PyObject *module, int nlines, int ncols) PyObject *result = PyCursesCheckERR(resizeterm(nlines, ncols), "resizeterm"); CHECK_NOT_NULL_OR_ERROR(result); - CHECK_RET_FLAG_OR_ERROR(update_lines_cols()); + CHECK_RET_FLAG_OR_ERROR(update_lines_cols(module)); return result; error: @@ -4169,7 +4163,7 @@ _curses_resize_term_impl(PyObject *module, int nlines, int ncols) PyObject *result = PyCursesCheckERR(resize_term(nlines, ncols), "resize_term"); CHECK_NOT_NULL_OR_ERROR(result); - CHECK_RET_FLAG_OR_ERROR(update_lines_cols()); + CHECK_RET_FLAG_OR_ERROR(update_lines_cols(module)); return result; error: @@ -4237,8 +4231,10 @@ _curses_start_color_impl(PyObject *module) if (start_color() != ERR) { CURSES_INITIALISED_COLORS = TRUE; - SET_DICT_INT_VALUE_OR_ERROR(PRIVATE_MOD_DICT, "COLORS", COLORS); - SET_DICT_INT_VALUE_OR_ERROR(PRIVATE_MOD_DICT, "COLOR_PAIRS", COLOR_PAIRS); + PyObject *module_dict = PyModule_GetDict(module); + CHECK_NOT_NULL_OR_ERROR(module_dict); + DICT_ADD_INT_VALUE_OR_ERROR(module_dict, "COLORS", COLORS); + DICT_ADD_INT_VALUE_OR_ERROR(module_dict, "COLOR_PAIRS", COLOR_PAIRS); Py_RETURN_NONE; } else { @@ -4778,9 +4774,8 @@ PyInit__curses(void) #endif /* Add some symbolic constants to the module */ - PyObject *d = PyModule_GetDict(mod); - CHECK_NOT_NULL_OR_ERROR(d); - PRIVATE_MOD_DICT = d; /* For PyCurses_InitScr to use later */ + PyObject *module_dict = PyModule_GetDict(mod); + CHECK_NOT_NULL_OR_ERROR(module_dict); void **PyCurses_API = PyMem_Calloc(PyCurses_API_pointers, sizeof(void *)); if (PyCurses_API == NULL) { @@ -4801,25 +4796,25 @@ PyInit__curses(void) PyMem_Free(PyCurses_API); goto error; } - int rc = PyDict_SetItemString(d, "_C_API", c_api_object); + int rc = PyDict_SetItemString(module_dict, "_C_API", c_api_object); Py_DECREF(c_api_object); CHECK_RET_CODE_OR_ERROR(rc); /* For exception curses.error */ PyCursesError = PyErr_NewException("_curses.error", NULL, NULL); CHECK_NOT_NULL_OR_ERROR(PyCursesError); - rc = PyDict_SetItemString(d, "error", PyCursesError); + rc = PyDict_SetItemString(module_dict, "error", PyCursesError); Py_DECREF(PyCursesError); CHECK_RET_CODE_OR_ERROR(rc); /* Make the version available */ PyObject *curses_version = PyBytes_FromString(PyCursesVersion); CHECK_NOT_NULL_OR_ERROR(curses_version); - rc = PyDict_SetItemString(d, "version", curses_version); + rc = PyDict_SetItemString(module_dict, "version", curses_version); Py_DECREF(curses_version); CHECK_RET_CODE_OR_ERROR(rc); Py_INCREF(curses_version); - rc = PyDict_SetItemString(d, "__version__", curses_version); + rc = PyDict_SetItemString(module_dict, "__version__", curses_version); Py_CLEAR(curses_version); CHECK_RET_CODE_OR_ERROR(rc); @@ -4832,12 +4827,14 @@ PyInit__curses(void) PyObject *ncurses_version = make_ncurses_version(version_type); Py_DECREF(version_type); CHECK_NOT_NULL_OR_ERROR(ncurses_version); - rc = PyDict_SetItemString(d, "ncurses_version", ncurses_version); + rc = PyDict_SetItemString(module_dict, "ncurses_version", ncurses_version); Py_CLEAR(ncurses_version); CHECK_RET_CODE_OR_ERROR(rc); #endif /* NCURSES_VERSION */ -#define SetDictInt SET_MOD_DICT_INT_VALUE_OR_ERROR +#define SetDictInt(NAME, VALUE) \ + DICT_ADD_INT_VALUE_OR_ERROR(module_dict, (NAME), (VALUE)) + SetDictInt("ERR", ERR); SetDictInt("OK", OK); @@ -4956,7 +4953,7 @@ PyInit__curses(void) *p2 = (char)0; PyObject *p_keycode = PyLong_FromLong((long)keycode); CHECK_NOT_NULL_OR_ERROR(p_keycode); - int rc = PyDict_SetItemString(d, fn_key_name, p_keycode); + int rc = PyDict_SetItemString(module_dict, fn_key_name, p_keycode); Py_DECREF(p_keycode); PyMem_Free(fn_key_name); CHECK_RET_CODE_OR_ERROR(rc); @@ -4977,8 +4974,7 @@ PyInit__curses(void) return NULL; } -#undef SET_MOD_DICT_INT_VALUE_OR_ERROR -#undef SET_DICT_INT_VALUE_OR_ERROR +#undef DICT_ADD_INT_VALUE_OR_ERROR #undef CHECK_RET_FLAG_OR_ERROR #undef CHECK_RET_CODE_OR_ERROR #undef CHECK_NOT_NULL_OR_ERROR From fd7372aaaa58fbf2e299122446c4c6571314b5c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 10 Sep 2024 18:04:59 +0200 Subject: [PATCH 08/12] update globals --- Tools/c-analyzer/cpython/globals-to-fix.tsv | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index cb9750a69a632b..94d1444f7fec2c 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -412,10 +412,10 @@ Modules/_tkinter.c - trbInCmd - Include/datetime.h - PyDateTimeAPI - Modules/_ctypes/cfield.c _ctypes_get_fielddesc initialized - Modules/_ctypes/malloc_closure.c - _pagesize - -Modules/_cursesmodule.c - initialised - -Modules/_cursesmodule.c - initialised_setupterm - -Modules/_cursesmodule.c - initialisedcolors - -Modules/_cursesmodule.c - screen_encoding - +Modules/_cursesmodule.c - CURSES_SETUPTERM_CALLED - +Modules/_cursesmodule.c - CURSES_INITSCR_CALLED - +Modules/_cursesmodule.c - CURSES_INITIALISED_COLORS - +Modules/_cursesmodule.c - CURSES_SCREEN_ENCODING - Modules/_elementtree.c - expat_capi - Modules/readline.c - libedit_append_replace_history_offset - Modules/readline.c - using_libedit_emulation - From 384716d2ca3a8d057d6dcf0fa0a45b15a61e88a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 11 Sep 2024 12:52:00 +0200 Subject: [PATCH 09/12] address Victor's review --- Modules/_cursesmodule.c | 193 +++++++++++++++++++++++----------------- 1 file changed, 113 insertions(+), 80 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index d36ea1892e455a..5b89d503dccf65 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -207,34 +207,49 @@ static char *CURSES_SCREEN_ENCODING = NULL; #define PyCursesInitialisedColor \ CHECK_STATE_FLAG("start_color", CURSES_INITIALISED_COLORS) -#define CHECK_NOT_NULL_OR_ERROR(VALUE) \ - do { \ - if ((VALUE) == NULL) { \ - goto error; \ - } \ - } while (0) - -#define CHECK_RET_CODE_OR_ERROR(STATUS) \ +/* Jump to the 'error' label if STATUS < 0. */ +#define CHECK_RET_CODE(STATUS) \ do { \ if ((STATUS) < 0) { \ + assert(PyErr_Occurred()); \ goto error; \ } \ } while (0) -#define CHECK_RET_FLAG_OR_ERROR(STATUS) \ - do { \ - if (!(STATUS)) { \ - goto error; \ - } \ +/* + * Equivalent to DICT[NAME] = VALUE; on error, jump to the 'error' label. + * + * Parameters + * + * PyObject * DICT The Python dict to alter. + * const char * NAME The constant name. + * int or long VALUE The constant value. + */ +#define DICT_ADD_INT_VALUE(DICT, NAME, VALUE) \ + do { \ + PyObject *value = PyLong_FromLong((long)(VALUE)); \ + if (value == NULL) { \ + goto error; \ + } \ + int rc = PyDict_SetItemString((DICT), (NAME), value); \ + Py_DECREF(value); \ + CHECK_RET_CODE(rc); \ } while (0) -#define DICT_ADD_INT_VALUE_OR_ERROR(DICT_OBJECT, STRING, VALUE) \ - do { \ - PyObject *value = PyLong_FromLong((long)(VALUE)); \ - CHECK_NOT_NULL_OR_ERROR(value); \ - int rc = PyDict_SetItemString((DICT_OBJECT), (STRING), value); \ - Py_DECREF(value); \ - CHECK_RET_CODE_OR_ERROR(rc); \ +/* + * Add an integral constant to a module; on error, jump to the 'error' label. + * + * Parameters + * + * PyObject * MODULE The module object to alter. + * const char * NAME The constant name. + * int or long VALUE The constant value. + */ +#define MODULE_ADD_INT_CONSTANT(MODULE, NAME, VALUE) \ + do { \ + long value = (long)(VALUE); \ + int rc = PyModule_AddIntConstant((MODULE), (NAME), value); \ + CHECK_RET_CODE(rc); \ } while (0) /* Utility Functions */ @@ -754,13 +769,13 @@ PyCursesWindow_New(WINDOW *win, const char *encoding) static void PyCursesWindow_Dealloc(PyCursesWindowObject *wo) { - if (wo->win != stdscr && wo->win != NULL) { - delwin(wo->win); - wo->win = NULL; + if (wo->win != stdscr) { + // Silently ignore errors in delwin(3) (e.g., passing + // a NULL pointer results in delwin() returning ERR). + (void)delwin(wo->win); } if (wo->encoding != NULL) { PyMem_Free(wo->encoding); - wo->encoding = NULL; } PyObject_Free(wo); } @@ -3325,9 +3340,10 @@ _curses_initscr_impl(PyObject *module) /* Here are some graphic symbols you can use */ PyObject *module_dict = PyModule_GetDict(module); - CHECK_NOT_NULL_OR_ERROR(module_dict); -#define SetDictInt(NAME, VALUE) \ - DICT_ADD_INT_VALUE_OR_ERROR(module_dict, (NAME), (VALUE)) + if (module_dict == NULL) { + goto error; + } +#define SetDictInt(NAME, VALUE) DICT_ADD_INT_VALUE(module_dict, (NAME), (VALUE)) SetDictInt("ACS_ULCORNER", (ACS_ULCORNER)); SetDictInt("ACS_LLCORNER", (ACS_LLCORNER)); @@ -3400,7 +3416,9 @@ _curses_initscr_impl(PyObject *module) #undef SetDictInt PyCursesWindowObject *winobj = (PyCursesWindowObject *)PyCursesWindow_New(win, NULL); - CHECK_NOT_NULL_OR_ERROR(winobj); + if (winobj == NULL) { + goto error; + } CURSES_SCREEN_ENCODING = winobj->encoding; return (PyObject *)winobj; @@ -4008,27 +4026,21 @@ _curses_qiflush_impl(PyObject *module, int flag) /* Internal helper used for updating curses.LINES, curses.COLS, _curses.LINES * and _curses.COLS */ #if defined(HAVE_CURSES_RESIZETERM) || defined(HAVE_CURSES_RESIZE_TERM) -static int +static int /* 1 on success, 0 on failure */ update_lines_cols(PyObject *private_module) { - PyObject *exposed_module = NULL, *o = NULL; - exposed_module = PyImport_ImportModule("curses"); - CHECK_NOT_NULL_OR_ERROR(exposed_module); - o = PyLong_FromLong(LINES); - CHECK_NOT_NULL_OR_ERROR(o); - CHECK_RET_CODE_OR_ERROR(PyObject_SetAttrString(exposed_module, "LINES", o)); - CHECK_RET_CODE_OR_ERROR(PyObject_SetAttrString(private_module, "LINES", o)); - Py_DECREF(o); - o = PyLong_FromLong(COLS); - CHECK_NOT_NULL_OR_ERROR(o); - CHECK_RET_CODE_OR_ERROR(PyObject_SetAttrString(exposed_module, "COLS", o)); - CHECK_RET_CODE_OR_ERROR(PyObject_SetAttrString(private_module, "COLS", o)); - Py_DECREF(o); + PyObject *exposed_module = PyImport_ImportModule("curses"); + if (exposed_module == NULL) { + return 0; + } + MODULE_ADD_INT_CONSTANT(exposed_module, "LINES", LINES); + MODULE_ADD_INT_CONSTANT(private_module, "LINES", LINES); + MODULE_ADD_INT_CONSTANT(exposed_module, "COLS", COLS); + MODULE_ADD_INT_CONSTANT(private_module, "COLS", COLS); Py_DECREF(exposed_module); return 1; error: - Py_XDECREF(o); Py_XDECREF(exposed_module); return 0; } @@ -4122,16 +4134,18 @@ static PyObject * _curses_resizeterm_impl(PyObject *module, int nlines, int ncols) /*[clinic end generated code: output=56d6bcc5194ad055 input=0fca02ebad5ffa82]*/ { + PyObject *result; + PyCursesInitialised; - PyObject *result = PyCursesCheckERR(resizeterm(nlines, ncols), "resizeterm"); - CHECK_NOT_NULL_OR_ERROR(result); - CHECK_RET_FLAG_OR_ERROR(update_lines_cols(module)); + result = PyCursesCheckERR(resizeterm(nlines, ncols), "resizeterm"); + if (!result) + return NULL; + if (!update_lines_cols(module)) { + Py_DECREF(result); + return NULL; + } return result; - -error: - Py_XDECREF(result); - return NULL; } #endif @@ -4159,16 +4173,18 @@ static PyObject * _curses_resize_term_impl(PyObject *module, int nlines, int ncols) /*[clinic end generated code: output=9e26d8b9ea311ed2 input=2197edd05b049ed4]*/ { + PyObject *result; + PyCursesInitialised; - PyObject *result = PyCursesCheckERR(resize_term(nlines, ncols), "resize_term"); - CHECK_NOT_NULL_OR_ERROR(result); - CHECK_RET_FLAG_OR_ERROR(update_lines_cols(module)); + result = PyCursesCheckERR(resize_term(nlines, ncols), "resize_term"); + if (!result) + return NULL; + if (!update_lines_cols(module)) { + Py_DECREF(result); + return NULL; + } return result; - -error: - Py_XDECREF(result); - return NULL; } #endif /* HAVE_CURSES_RESIZE_TERM */ @@ -4232,9 +4248,11 @@ _curses_start_color_impl(PyObject *module) if (start_color() != ERR) { CURSES_INITIALISED_COLORS = TRUE; PyObject *module_dict = PyModule_GetDict(module); - CHECK_NOT_NULL_OR_ERROR(module_dict); - DICT_ADD_INT_VALUE_OR_ERROR(module_dict, "COLORS", COLORS); - DICT_ADD_INT_VALUE_OR_ERROR(module_dict, "COLOR_PAIRS", COLOR_PAIRS); + if (module_dict == NULL) { + return NULL; + } + DICT_ADD_INT_VALUE(module_dict, "COLORS", COLORS); + DICT_ADD_INT_VALUE(module_dict, "COLOR_PAIRS", COLOR_PAIRS); Py_RETURN_NONE; } else { @@ -4615,7 +4633,9 @@ make_ncurses_version(PyTypeObject *type) #define SET_VERSION_COMPONENT(INDEX, VALUE) \ do { \ PyObject *o = PyLong_FromLong(VALUE); \ - CHECK_NOT_NULL_OR_ERROR(o); \ + if (o == NULL) { \ + goto error; \ + } \ PyStructSequence_SET_ITEM(ncurses_version, INDEX, o); \ } while (0) @@ -4764,18 +4784,22 @@ PyInit__curses(void) PyObject *mod = NULL; /* Initialize object type */ - CHECK_RET_CODE_OR_ERROR(PyType_Ready(&PyCursesWindow_Type)); + CHECK_RET_CODE(PyType_Ready(&PyCursesWindow_Type)); /* Create the module and add the functions */ mod = PyModule_Create(&_cursesmodule); - CHECK_NOT_NULL_OR_ERROR(mod); + if (mod == NULL) { + goto error; + } #ifdef Py_GIL_DISABLED CHECK_RET_CODE_OR_ERROR(PyUnstable_Module_SetGIL(mod, Py_MOD_GIL_NOT_USED)); #endif /* Add some symbolic constants to the module */ PyObject *module_dict = PyModule_GetDict(mod); - CHECK_NOT_NULL_OR_ERROR(module_dict); + if (module_dict == NULL) { + goto error; + } void **PyCurses_API = PyMem_Calloc(PyCurses_API_pointers, sizeof(void *)); if (PyCurses_API == NULL) { @@ -4798,42 +4822,49 @@ PyInit__curses(void) } int rc = PyDict_SetItemString(module_dict, "_C_API", c_api_object); Py_DECREF(c_api_object); - CHECK_RET_CODE_OR_ERROR(rc); + CHECK_RET_CODE(rc); /* For exception curses.error */ PyCursesError = PyErr_NewException("_curses.error", NULL, NULL); - CHECK_NOT_NULL_OR_ERROR(PyCursesError); + if (PyCursesError == NULL) { + goto error; + } rc = PyDict_SetItemString(module_dict, "error", PyCursesError); Py_DECREF(PyCursesError); - CHECK_RET_CODE_OR_ERROR(rc); + CHECK_RET_CODE(rc); /* Make the version available */ PyObject *curses_version = PyBytes_FromString(PyCursesVersion); - CHECK_NOT_NULL_OR_ERROR(curses_version); + if (curses_version == NULL) { + goto error; + } rc = PyDict_SetItemString(module_dict, "version", curses_version); Py_DECREF(curses_version); - CHECK_RET_CODE_OR_ERROR(rc); + CHECK_RET_CODE(rc); Py_INCREF(curses_version); rc = PyDict_SetItemString(module_dict, "__version__", curses_version); Py_CLEAR(curses_version); - CHECK_RET_CODE_OR_ERROR(rc); + CHECK_RET_CODE(rc); #ifdef NCURSES_VERSION /* ncurses_version */ PyTypeObject *version_type; version_type = _PyStructSequence_NewType(&ncurses_version_desc, Py_TPFLAGS_DISALLOW_INSTANTIATION); - CHECK_NOT_NULL_OR_ERROR(version_type); + if (version_type == NULL) { + goto error; + } PyObject *ncurses_version = make_ncurses_version(version_type); Py_DECREF(version_type); - CHECK_NOT_NULL_OR_ERROR(ncurses_version); + if (ncurses_version == NULL) { + goto error; + } rc = PyDict_SetItemString(module_dict, "ncurses_version", ncurses_version); Py_CLEAR(ncurses_version); - CHECK_RET_CODE_OR_ERROR(rc); + CHECK_RET_CODE(rc); #endif /* NCURSES_VERSION */ -#define SetDictInt(NAME, VALUE) \ - DICT_ADD_INT_VALUE_OR_ERROR(module_dict, (NAME), (VALUE)) +#define SetDictInt(NAME, VALUE) DICT_ADD_INT_VALUE(module_dict, (NAME), (VALUE)) SetDictInt("ERR", ERR); SetDictInt("OK", OK); @@ -4952,11 +4983,13 @@ PyInit__curses(void) } *p2 = (char)0; PyObject *p_keycode = PyLong_FromLong((long)keycode); - CHECK_NOT_NULL_OR_ERROR(p_keycode); + if (p_keycode == NULL) { + goto error; + } int rc = PyDict_SetItemString(module_dict, fn_key_name, p_keycode); Py_DECREF(p_keycode); PyMem_Free(fn_key_name); - CHECK_RET_CODE_OR_ERROR(rc); + CHECK_RET_CODE(rc); } else { SetDictInt(key_name, keycode); @@ -4966,7 +4999,7 @@ PyInit__curses(void) SetDictInt("KEY_MAX", KEY_MAX); #undef SetDictInt - CHECK_RET_CODE_OR_ERROR(PyModule_AddType(mod, &PyCursesWindow_Type)); + CHECK_RET_CODE(PyModule_AddType(mod, &PyCursesWindow_Type)); return mod; error: @@ -4974,8 +5007,8 @@ PyInit__curses(void) return NULL; } -#undef DICT_ADD_INT_VALUE_OR_ERROR +#undef DICT_ADD_INT_VALUE #undef CHECK_RET_FLAG_OR_ERROR -#undef CHECK_RET_CODE_OR_ERROR +#undef CHECK_RET_CODE #undef CHECK_NOT_NULL_OR_ERROR #undef CHECK_STATE_FLAG From 24f47a11622d30055ee9a1de248ff50f6dbe5f30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 11 Sep 2024 12:58:11 +0200 Subject: [PATCH 10/12] fix compilation if `Py_GIL_DISABLED` is defined --- Modules/_cursesmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 5b89d503dccf65..1e23144cbf2390 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -4792,7 +4792,7 @@ PyInit__curses(void) goto error; } #ifdef Py_GIL_DISABLED - CHECK_RET_CODE_OR_ERROR(PyUnstable_Module_SetGIL(mod, Py_MOD_GIL_NOT_USED)); + CHECK_RET_CODE(PyUnstable_Module_SetGIL(mod, Py_MOD_GIL_NOT_USED)); #endif /* Add some symbolic constants to the module */ From 4a1003c745cd8c2fe572c949d940a677fda05661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 11 Sep 2024 13:04:15 +0200 Subject: [PATCH 11/12] use a local macro --- Modules/_cursesmodule.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 1e23144cbf2390..cc6d2eb147a847 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -236,22 +236,6 @@ static char *CURSES_SCREEN_ENCODING = NULL; CHECK_RET_CODE(rc); \ } while (0) -/* - * Add an integral constant to a module; on error, jump to the 'error' label. - * - * Parameters - * - * PyObject * MODULE The module object to alter. - * const char * NAME The constant name. - * int or long VALUE The constant value. - */ -#define MODULE_ADD_INT_CONSTANT(MODULE, NAME, VALUE) \ - do { \ - long value = (long)(VALUE); \ - int rc = PyModule_AddIntConstant((MODULE), (NAME), value); \ - CHECK_RET_CODE(rc); \ - } while (0) - /* Utility Functions */ /* @@ -4033,10 +4017,19 @@ update_lines_cols(PyObject *private_module) if (exposed_module == NULL) { return 0; } +#define MODULE_ADD_INT_CONSTANT(MODULE, NAME, VALUE) \ + do { \ + if (PyModule_AddIntConstant((MODULE), (NAME), (long)(VALUE)) < 0) { \ + goto error; \ + } \ + } while (0) + MODULE_ADD_INT_CONSTANT(exposed_module, "LINES", LINES); MODULE_ADD_INT_CONSTANT(private_module, "LINES", LINES); MODULE_ADD_INT_CONSTANT(exposed_module, "COLS", COLS); MODULE_ADD_INT_CONSTANT(private_module, "COLS", COLS); +#undef MODULE_ADD_INT_CONSTANT + Py_DECREF(exposed_module); return 1; From 356b00c15b27294a7d45b96711f58c88dba5ee0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 11 Sep 2024 13:05:07 +0200 Subject: [PATCH 12/12] remove unused undefs --- Modules/_cursesmodule.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index cc6d2eb147a847..c9900b7c247ae0 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -5001,7 +5001,5 @@ PyInit__curses(void) } #undef DICT_ADD_INT_VALUE -#undef CHECK_RET_FLAG_OR_ERROR #undef CHECK_RET_CODE -#undef CHECK_NOT_NULL_OR_ERROR #undef CHECK_STATE_FLAG