gh-85283: Build termios extension with the limited C API (#116928)

diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst
index 78b5868..3e08d80 100644
--- a/Doc/whatsnew/3.13.rst
+++ b/Doc/whatsnew/3.13.rst
@@ -1479,7 +1479,8 @@
 * Building CPython now requires a compiler with support for the C11 atomic
   library, GCC built-in atomic functions, or MSVC interlocked intrinsics.
 
-* The ``errno``, ``fcntl``, ``grp``, ``md5``, ``pwd``, ``resource``, ``winsound``,
+* The ``errno``, ``fcntl``, ``grp``, ``md5``, ``pwd``, ``resource``,
+  ``termios``, ``winsound``,
   ``_ctypes_test``, ``_multiprocessing.posixshmem``, ``_scproxy``, ``_stat``,
   ``_testimportmultiple`` and ``_uuid`` C extensions are now built with the
   :ref:`limited C API <limited-c-api>`.
diff --git a/Misc/NEWS.d/next/C API/2024-03-14-10-33-58.gh-issue-85283.LOgmdU.rst b/Misc/NEWS.d/next/C API/2024-03-14-10-33-58.gh-issue-85283.LOgmdU.rst
index 8a3185a..bfb72db 100644
--- a/Misc/NEWS.d/next/C API/2024-03-14-10-33-58.gh-issue-85283.LOgmdU.rst
+++ b/Misc/NEWS.d/next/C API/2024-03-14-10-33-58.gh-issue-85283.LOgmdU.rst
@@ -1,2 +1,2 @@
-The ``fcntl``, ``grp`` and ``pwd`` C extensions are now built with the :ref:`limited
-C API <limited-c-api>`. (Contributed by Victor Stinner in :gh:`85283`.)
+The ``fcntl``, ``grp``, ``pwd`` and ``termios`` C extensions are now
+built with the :ref:`limited C API <limited-c-api>`. Patch by Victor Stinner.
diff --git a/Modules/clinic/termios.c.h b/Modules/clinic/termios.c.h
index 2813e5e..83f5a4f 100644
--- a/Modules/clinic/termios.c.h
+++ b/Modules/clinic/termios.c.h
@@ -2,8 +2,6 @@
 preserve
 [clinic start generated code]*/
 
-#include "pycore_modsupport.h"    // _PyArg_CheckPositional()
-
 PyDoc_STRVAR(termios_tcgetattr__doc__,
 "tcgetattr($module, fd, /)\n"
 "--\n"
@@ -53,7 +51,7 @@
 "queued output and discarding all queued input.");
 
 #define TERMIOS_TCSETATTR_METHODDEF    \
-    {"tcsetattr", _PyCFunction_CAST(termios_tcsetattr), METH_FASTCALL, termios_tcsetattr__doc__},
+    {"tcsetattr", (PyCFunction)(void(*)(void))termios_tcsetattr, METH_FASTCALL, termios_tcsetattr__doc__},
 
 static PyObject *
 termios_tcsetattr_impl(PyObject *module, int fd, int when, PyObject *term);
@@ -66,7 +64,8 @@
     int when;
     PyObject *term;
 
-    if (!_PyArg_CheckPositional("tcsetattr", nargs, 3, 3)) {
+    if (nargs != 3) {
+        PyErr_Format(PyExc_TypeError, "tcsetattr expected 3 arguments, got %zd", nargs);
         goto exit;
     }
     fd = PyObject_AsFileDescriptor(args[0]);
@@ -94,7 +93,7 @@
 "has a system dependent meaning.");
 
 #define TERMIOS_TCSENDBREAK_METHODDEF    \
-    {"tcsendbreak", _PyCFunction_CAST(termios_tcsendbreak), METH_FASTCALL, termios_tcsendbreak__doc__},
+    {"tcsendbreak", (PyCFunction)(void(*)(void))termios_tcsendbreak, METH_FASTCALL, termios_tcsendbreak__doc__},
 
 static PyObject *
 termios_tcsendbreak_impl(PyObject *module, int fd, int duration);
@@ -106,7 +105,8 @@
     int fd;
     int duration;
 
-    if (!_PyArg_CheckPositional("tcsendbreak", nargs, 2, 2)) {
+    if (nargs != 2) {
+        PyErr_Format(PyExc_TypeError, "tcsendbreak expected 2 arguments, got %zd", nargs);
         goto exit;
     }
     fd = PyObject_AsFileDescriptor(args[0]);
@@ -162,7 +162,7 @@
 "both queues.");
 
 #define TERMIOS_TCFLUSH_METHODDEF    \
-    {"tcflush", _PyCFunction_CAST(termios_tcflush), METH_FASTCALL, termios_tcflush__doc__},
+    {"tcflush", (PyCFunction)(void(*)(void))termios_tcflush, METH_FASTCALL, termios_tcflush__doc__},
 
 static PyObject *
 termios_tcflush_impl(PyObject *module, int fd, int queue);
@@ -174,7 +174,8 @@
     int fd;
     int queue;
 
-    if (!_PyArg_CheckPositional("tcflush", nargs, 2, 2)) {
+    if (nargs != 2) {
+        PyErr_Format(PyExc_TypeError, "tcflush expected 2 arguments, got %zd", nargs);
         goto exit;
     }
     fd = PyObject_AsFileDescriptor(args[0]);
@@ -202,7 +203,7 @@
 "or termios.TCION to restart input.");
 
 #define TERMIOS_TCFLOW_METHODDEF    \
-    {"tcflow", _PyCFunction_CAST(termios_tcflow), METH_FASTCALL, termios_tcflow__doc__},
+    {"tcflow", (PyCFunction)(void(*)(void))termios_tcflow, METH_FASTCALL, termios_tcflow__doc__},
 
 static PyObject *
 termios_tcflow_impl(PyObject *module, int fd, int action);
@@ -214,7 +215,8 @@
     int fd;
     int action;
 
-    if (!_PyArg_CheckPositional("tcflow", nargs, 2, 2)) {
+    if (nargs != 2) {
+        PyErr_Format(PyExc_TypeError, "tcflow expected 2 arguments, got %zd", nargs);
         goto exit;
     }
     fd = PyObject_AsFileDescriptor(args[0]);
@@ -271,7 +273,7 @@
 "is a two-item tuple (ws_row, ws_col) like the one returned by tcgetwinsize().");
 
 #define TERMIOS_TCSETWINSIZE_METHODDEF    \
-    {"tcsetwinsize", _PyCFunction_CAST(termios_tcsetwinsize), METH_FASTCALL, termios_tcsetwinsize__doc__},
+    {"tcsetwinsize", (PyCFunction)(void(*)(void))termios_tcsetwinsize, METH_FASTCALL, termios_tcsetwinsize__doc__},
 
 static PyObject *
 termios_tcsetwinsize_impl(PyObject *module, int fd, PyObject *winsz);
@@ -283,7 +285,8 @@
     int fd;
     PyObject *winsz;
 
-    if (!_PyArg_CheckPositional("tcsetwinsize", nargs, 2, 2)) {
+    if (nargs != 2) {
+        PyErr_Format(PyExc_TypeError, "tcsetwinsize expected 2 arguments, got %zd", nargs);
         goto exit;
     }
     fd = PyObject_AsFileDescriptor(args[0]);
@@ -296,4 +299,4 @@
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=7327a2085972bf59 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=c6c6192583b0da36 input=a9049054013a1b77]*/
diff --git a/Modules/termios.c b/Modules/termios.c
index 4635fef..a29474d 100644
--- a/Modules/termios.c
+++ b/Modules/termios.c
@@ -1,11 +1,19 @@
 /* termios.c -- POSIX terminal I/O module implementation.  */
 
-#ifndef Py_BUILD_CORE_BUILTIN
-#  define Py_BUILD_CORE_MODULE 1
+// Need limited C API version 3.13 for PyLong_AsInt()
+// in code generated by Argument Clinic.
+#include "pyconfig.h"   // Py_GIL_DISABLED
+#ifndef Py_GIL_DISABLED
+#  define Py_LIMITED_API 0x030d0000
 #endif
 
 #include "Python.h"
 
+#include <string.h>               // memset()
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <unistd.h>               // _POSIX_VDISABLE
+
 // On QNX 6, struct termio must be declared by including sys/termio.h
 // if TCGETA, TCSETA, TCSETAW, or TCSETAF are used. sys/termio.h must
 // be included before termios.h or it will generate an error.
@@ -19,28 +27,26 @@
 #  define CTRL(c) ((c)&037)
 #endif
 
+// We could do better. Check bpo-32660
 #if defined(__sun)
-/* We could do better. Check issue-32660 */
-#include <sys/filio.h>
-#include <sys/sockio.h>
+#  include <sys/filio.h>
+#  include <sys/sockio.h>
 #endif
 
-#include <termios.h>
-#include <sys/ioctl.h>
-#include <unistd.h> // _POSIX_VDISABLE
-
 /* HP-UX requires that this be included to pick up MDCD, MCTS, MDSR,
  * MDTR, MRI, and MRTS (apparently used internally by some things
  * defined as macros; these are not used here directly).
  */
 #ifdef HAVE_SYS_MODEM_H
-#include <sys/modem.h>
+#  include <sys/modem.h>
 #endif
+
 /* HP-UX requires that this be included to pick up TIOCGPGRP and friends */
 #ifdef HAVE_SYS_BSDTTY_H
-#include <sys/bsdtty.h>
+#  include <sys/bsdtty.h>
 #endif
 
+
 /*[clinic input]
 module termios
 [clinic start generated code]*/
@@ -120,7 +126,7 @@ termios_tcgetattr_impl(PyObject *module, int fd)
         v = PyBytes_FromStringAndSize(&ch, 1);
         if (v == NULL)
             goto err;
-        PyList_SET_ITEM(cc, i, v);
+        PyList_SetItem(cc, i, v);
     }
 
     /* Convert the MIN and TIME slots to integer.  On some systems, the
@@ -154,7 +160,7 @@ termios_tcgetattr_impl(PyObject *module, int fd)
             Py_DECREF(v); \
             goto err; \
         } \
-        PyList_SET_ITEM(v, index, l); \
+        PyList_SetItem(v, index, l); \
     } while (0)
 
     ADD_LONG_ITEM(0, mode.c_iflag);
@@ -165,7 +171,7 @@ termios_tcgetattr_impl(PyObject *module, int fd)
     ADD_LONG_ITEM(5, ospeed);
 #undef ADD_LONG_ITEM
 
-    PyList_SET_ITEM(v, 6, cc);
+    PyList_SetItem(v, 6, cc);
     return v;
   err:
     Py_DECREF(cc);
@@ -214,7 +220,7 @@ termios_tcsetattr_impl(PyObject *module, int fd, int when, PyObject *term)
 
     speed_t ispeed, ospeed;
 #define SET_FROM_LIST(TYPE, VAR, LIST, N) do {  \
-    PyObject *item = PyList_GET_ITEM(LIST, N);  \
+    PyObject *item = PyList_GetItem(LIST, N);  \
     long num = PyLong_AsLong(item);             \
     if (num == -1 && PyErr_Occurred()) {        \
         return NULL;                            \
@@ -230,7 +236,7 @@ termios_tcsetattr_impl(PyObject *module, int fd, int when, PyObject *term)
     SET_FROM_LIST(speed_t, ospeed, term, 5);
 #undef SET_FROM_LIST
 
-    PyObject *cc = PyList_GET_ITEM(term, 6);
+    PyObject *cc = PyList_GetItem(term, 6);
     if (!PyList_Check(cc) || PyList_Size(cc) != NCCS) {
         PyErr_Format(PyExc_TypeError,
             "tcsetattr: attributes[6] must be %d element list",