diff --git a/Doc/library/dbm.rst b/Doc/library/dbm.rst index 39e287b15214e4..140ca5c1e3405a 100644 --- a/Doc/library/dbm.rst +++ b/Doc/library/dbm.rst @@ -90,10 +90,13 @@ the Oracle Berkeley DB. .. versionchanged:: 3.11 *file* accepts a :term:`path-like object`. -The object returned by :func:`~dbm.open` supports the same basic functionality as a -:class:`dict`; keys and their corresponding values can be stored, retrieved, and -deleted, and the :keyword:`in` operator and the :meth:`!keys` method are -available, as well as :meth:`!get` and :meth:`!setdefault` methods. +The object returned by :func:`~dbm.open` supports the basic +functionality of mutable :term:`mappings `; +keys and their corresponding values can be stored, retrieved, and +deleted, and iteration, the :keyword:`in` operator and methods :meth:`!keys`, +:meth:`!get`, :meth:`!setdefault` and :meth:`!clear` are available. +The :meth:`!keys` method returns a list instead of a view object. +The :meth:`!setdefault` method requires two arguments. Key and values are always stored as :class:`bytes`. This means that when strings are used they are implicitly converted to the default encoding before @@ -114,6 +117,10 @@ will automatically close them when done. Deleting a key from a read-only database raises a database module specific exception instead of :exc:`KeyError`. +.. versionchanged:: 3.13 + :meth:`!clear` methods are now available for all :mod:`dbm` backends. + + The following example records some hostnames and a corresponding title, and then prints out the contents of the database:: @@ -173,9 +180,6 @@ or any other SQLite browser, including the SQLite CLI. .. function:: open(filename, /, flag="r", mode=0o666) Open an SQLite database. - The returned object behaves like a :term:`mapping`, - implements a :meth:`!close` method, - and supports a "closing" context manager via the :keyword:`with` keyword. :param filename: The path to the database to be opened. @@ -192,6 +196,17 @@ or any other SQLite browser, including the SQLite CLI. The Unix file access mode of the file (default: octal ``0o666``), used only when the database has to be created. + The returned database object behaves similar to a mutable :term:`mapping`, + but the :meth:`!keys` method returns a list, and + the :meth:`!setdefault` method requires two arguments. + It also supports a "closing" context manager via the :keyword:`with` keyword. + + The following methods are also provided: + + .. method:: sqlite3.close() + + Close the SQLite database. + .. method:: sqlite3.reorganize() If you have carried out a lot of deletions and would like to shrink the space @@ -204,6 +219,7 @@ or any other SQLite browser, including the SQLite CLI. .. versionadded:: next + :mod:`dbm.gnu` --- GNU database manager --------------------------------------- @@ -232,6 +248,11 @@ functionality like crash tolerance. raised for general mapping errors like specifying an incorrect key. +.. data:: open_flags + + A string of characters the *flag* parameter of :meth:`~dbm.gnu.open` supports. + + .. function:: open(filename, flag="r", mode=0o666, /) Open a GDBM database and return a :class:`!gdbm` object. @@ -270,14 +291,25 @@ functionality like crash tolerance. .. versionchanged:: 3.11 *filename* accepts a :term:`path-like object`. - .. data:: open_flags + :class:`!gdbm` objects behave similar to mutable :term:`mappings `, + but methods :meth:`!items`, :meth:`!values`, :meth:`!pop`, :meth:`!popitem`, + and :meth:`!update` are not supported, + the :meth:`!keys` method returns a list, and + the :meth:`!setdefault` method requires two arguments. + It also supports a "closing" context manager via the :keyword:`with` keyword. + + .. versionchanged:: 3.2 + Added the :meth:`!get` and :meth:`!setdefault` methods. - A string of characters the *flag* parameter of :meth:`~dbm.gnu.open` supports. + .. versionchanged:: 3.13 + Added the :meth:`!clear` method. - :class:`!gdbm` objects behave similar to :term:`mappings `, - but :meth:`!items` and :meth:`!values` methods are not supported. The following methods are also provided: + .. method:: gdbm.close() + + Close the GDBM database. + .. method:: gdbm.firstkey() It's possible to loop over every key in the database using this method and the @@ -313,16 +345,6 @@ functionality like crash tolerance. When the database has been opened in fast mode, this method forces any unwritten data to be written to the disk. - .. method:: gdbm.close() - - Close the GDBM database. - - .. method:: gdbm.clear() - - Remove all items from the GDBM database. - - .. versionadded:: 3.13 - :mod:`dbm.ndbm` --- New Database Manager ---------------------------------------- @@ -383,22 +405,27 @@ This module can be used with the "classic" NDBM interface or the :param int mode: |mode_param_doc| - :class:`!ndbm` objects behave similar to :term:`mappings `, - but :meth:`!items` and :meth:`!values` methods are not supported. - The following methods are also provided: - .. versionchanged:: 3.11 Accepts :term:`path-like object` for filename. - .. method:: ndbm.close() + :class:`!ndbm` objects behave similar to mutable :term:`mappings `, + but methods :meth:`!items`, :meth:`!values`, :meth:`!pop`, :meth:`!popitem`, + and :meth:`!update` are not supported, + the :meth:`!keys` method returns a list, and + the :meth:`!setdefault` method requires two arguments. + It also supports a "closing" context manager via the :keyword:`with` keyword. - Close the NDBM database. + .. versionchanged:: 3.2 + Added the :meth:`!get` and :meth:`!setdefault` methods. - .. method:: ndbm.clear() + .. versionchanged:: 3.13 + Added the :meth:`!clear` method. - Remove all items from the NDBM database. + The following method is also provided: - .. versionadded:: 3.13 + .. method:: ndbm.close() + + Close the NDBM database. :mod:`dbm.dumb` --- Portable DBM implementation @@ -436,9 +463,6 @@ The :mod:`!dbm.dumb` module defines the following: .. function:: open(filename, flag="c", mode=0o666) Open a :mod:`!dbm.dumb` database. - The returned database object behaves similar to a :term:`mapping`, - in addition to providing :meth:`~dumbdbm.sync` and :meth:`~dumbdbm.close` - methods. :param filename: The basename of the database file (without extensions). @@ -477,14 +501,12 @@ The :mod:`!dbm.dumb` module defines the following: .. versionchanged:: 3.11 *filename* accepts a :term:`path-like object`. - In addition to the methods provided by the - :class:`collections.abc.MutableMapping` class, - the following methods are provided: + The returned database object behaves similar to a mutable :term:`mapping`, + but the :meth:`!keys` and :meth:`!items` methods return lists, and + the :meth:`!setdefault` method requires two arguments. + It also supports a "closing" context manager via the :keyword:`with` keyword. - .. method:: dumbdbm.sync() - - Synchronize the on-disk directory and data files. This method is called - by the :meth:`shelve.Shelf.sync` method. + The following methods are also provided: .. method:: dumbdbm.close() @@ -501,3 +523,8 @@ The :mod:`!dbm.dumb` module defines the following: that this factor changes for each :mod:`dbm` submodule. .. versionadded:: next + + .. method:: dumbdbm.sync() + + Synchronize the on-disk directory and data files. This method is called + by the :meth:`shelve.Shelf.sync` method. diff --git a/Doc/library/resource.rst b/Doc/library/resource.rst index ea75c8b2b98924..230f11474f767d 100644 --- a/Doc/library/resource.rst +++ b/Doc/library/resource.rst @@ -78,12 +78,12 @@ this module for those platforms. Sets new limits of consumption of *resource*. The *limits* argument must be a tuple ``(soft, hard)`` of two integers describing the new limits. A value of - :data:`~resource.RLIM_INFINITY` can be used to request a limit that is + :const:`~resource.RLIM_INFINITY` can be used to request a limit that is unlimited. Raises :exc:`ValueError` if an invalid resource is specified, if the new soft limit exceeds the hard limit, or if a process tries to raise its hard limit. - Specifying a limit of :data:`~resource.RLIM_INFINITY` when the hard or + Specifying a limit of :const:`~resource.RLIM_INFINITY` when the hard or system limit for that resource is not unlimited will result in a :exc:`ValueError`. A process with the effective UID of super-user can request any valid limit value, including unlimited, but :exc:`ValueError` @@ -93,7 +93,7 @@ this module for those platforms. ``setrlimit`` may also raise :exc:`error` if the underlying system call fails. - VxWorks only supports setting :data:`RLIMIT_NOFILE`. + VxWorks only supports setting :const:`RLIMIT_NOFILE`. .. audit-event:: resource.setrlimit resource,limits resource.setrlimit diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index ff6053cb7e94d9..18985d42349885 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -1898,8 +1898,9 @@ to speed up repeated connections from the same clients. .. attribute:: SSLContext.sslsocket_class The return type of :meth:`SSLContext.wrap_socket`, defaults to - :class:`SSLSocket`. The attribute can be overridden on instance of class - in order to return a custom subclass of :class:`SSLSocket`. + :class:`SSLSocket`. The attribute can be assigned to on instances of + :class:`SSLContext` in order to return a custom subclass of + :class:`SSLSocket`. .. versionadded:: 3.7 diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 126329b538db31..61f458d24e93a4 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -834,7 +834,7 @@ dbm (Contributed by Raymond Hettinger and Erlend E. Aasland in :gh:`100414`.) * Allow removing all items from the database through - the new :meth:`.gdbm.clear` and :meth:`.ndbm.clear` methods. + the new :meth:`!clear` methods of the GDBM and NDBM database objects. (Contributed by Donghee Na in :gh:`107122`.) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index 0955005df5ccee..ab1b1722b7c254 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -213,17 +213,6 @@ def _need_normalize_century(): _normalize_century = True return _normalize_century -_supports_c99 = None -def _can_support_c99(): - global _supports_c99 - if _supports_c99 is None: - try: - _supports_c99 = ( - _time.strftime("%F", (1900, 1, 1, 0, 0, 0, 0, 1, 0)) == "1900-01-01") - except ValueError: - _supports_c99 = False - return _supports_c99 - # Correctly substitute for %z and %Z escapes in strftime formats. def _wrap_strftime(object, format, timetuple): # Don't call utcoffset() or tzname() unless actually needed. @@ -283,7 +272,7 @@ def _wrap_strftime(object, format, timetuple): newformat.append(Zreplace) # Note that datetime(1000, 1, 1).strftime('%G') == '1000' so # year 1000 for %G can go on the fast path. - elif ((ch in 'YG' or ch in 'FC' and _can_support_c99()) and + elif ((ch in 'YG' or ch in 'FC') and object.year < 1000 and _need_normalize_century()): if ch == 'G': year = int(_time.strftime("%G", timetuple)) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 4e42ae52765d0f..2299d1fab2e73d 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -1807,7 +1807,7 @@ def test_bool(self): self.assertTrue(self.theclass.min) self.assertTrue(self.theclass.max) - def test_strftime_y2k(self): + def check_strftime_y2k(self, specifier): # Test that years less than 1000 are 0-padded; note that the beginning # of an ISO 8601 year may fall in an ISO week of the year before, and # therefore needs an offset of -1 when formatting with '%G'. @@ -1821,22 +1821,28 @@ def test_strftime_y2k(self): (1000, 0), (1970, 0), ) - specifiers = 'YG' - if _time.strftime('%F', (1900, 1, 1, 0, 0, 0, 0, 1, 0)) == '1900-01-01': - specifiers += 'FC' for year, g_offset in dataset: - for specifier in specifiers: - with self.subTest(year=year, specifier=specifier): - d = self.theclass(year, 1, 1) - if specifier == 'G': - year += g_offset - if specifier == 'C': - expected = f"{year // 100:02d}" - else: - expected = f"{year:04d}" - if specifier == 'F': - expected += f"-01-01" - self.assertEqual(d.strftime(f"%{specifier}"), expected) + with self.subTest(year=year, specifier=specifier): + d = self.theclass(year, 1, 1) + if specifier == 'G': + year += g_offset + if specifier == 'C': + expected = f"{year // 100:02d}" + else: + expected = f"{year:04d}" + if specifier == 'F': + expected += f"-01-01" + self.assertEqual(d.strftime(f"%{specifier}"), expected) + + def test_strftime_y2k(self): + self.check_strftime_y2k('Y') + self.check_strftime_y2k('G') + + def test_strftime_y2k_c99(self): + # CPython requires C11; specifiers new in C99 must work. + # (Other implementations may want to disable this test.) + self.check_strftime_y2k('F') + self.check_strftime_y2k('C') def test_replace(self): cls = self.theclass diff --git a/Lib/test/test_resource.py b/Lib/test/test_resource.py index f73914b9b92c83..5fd076bee38e79 100644 --- a/Lib/test/test_resource.py +++ b/Lib/test/test_resource.py @@ -116,33 +116,40 @@ def test_fsize_not_too_big(self): self.addCleanup(resource.setrlimit, resource.RLIMIT_FSIZE, (cur, max)) def expected(cur): - return (min(cur, resource.RLIM_INFINITY), max) + # The glibc wrapper functions use a 64-bit rlim_t data type, + # even on 32-bit platforms. If a program tried to set a resource + # limit to a value larger than can be represented in a 32-bit + # unsigned long, then the glibc setrlimit() wrapper function + # silently converted the limit value to RLIM_INFINITY. + if sys.maxsize < 2**32 <= cur <= resource.RLIM_INFINITY: + return [(resource.RLIM_INFINITY, max), (cur, max)] + return [(min(cur, resource.RLIM_INFINITY), max)] resource.setrlimit(resource.RLIMIT_FSIZE, (2**31-5, max)) self.assertEqual(resource.getrlimit(resource.RLIMIT_FSIZE), (2**31-5, max)) resource.setrlimit(resource.RLIMIT_FSIZE, (2**31, max)) - self.assertEqual(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**31)) + self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**31)) resource.setrlimit(resource.RLIMIT_FSIZE, (2**32-5, max)) - self.assertEqual(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**32-5)) + self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**32-5)) try: resource.setrlimit(resource.RLIMIT_FSIZE, (2**32, max)) except OverflowError: pass else: - self.assertEqual(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**32)) + self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**32)) resource.setrlimit(resource.RLIMIT_FSIZE, (2**63-5, max)) - self.assertEqual(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**63-5)) + self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**63-5)) try: resource.setrlimit(resource.RLIMIT_FSIZE, (2**63, max)) except ValueError: # There is a hard limit on macOS. pass else: - self.assertEqual(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**63)) + self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**63)) resource.setrlimit(resource.RLIMIT_FSIZE, (2**64-5, max)) - self.assertEqual(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**64-5)) + self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**64-5)) @unittest.skipIf(sys.platform == "vxworks", "setting RLIMIT_FSIZE is not supported on VxWorks") diff --git a/Misc/NEWS.d/next/Build/2025-01-03-13-02-06.gh-issue-123681.gQ67nK.rst b/Misc/NEWS.d/next/Build/2025-01-03-13-02-06.gh-issue-123681.gQ67nK.rst new file mode 100644 index 00000000000000..a60b4607456096 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2025-01-03-13-02-06.gh-issue-123681.gQ67nK.rst @@ -0,0 +1,3 @@ +Check the ``strftime()`` behavior at runtime instead of at the compile time +to support cross-compiling. +Remove the internal macro ``_Py_NORMALIZE_CENTURY``. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 60d6fe3efdccf3..eb52eb72614fa5 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1760,6 +1760,24 @@ format_utcoffset(char *buf, size_t buflen, const char *sep, return 0; } +/* Check whether year with century should be normalized for strftime. */ +inline static int +normalize_century(void) +{ + static int cache = -1; + if (cache < 0) { + char year[5]; + struct tm date = { + .tm_year = -1801, + .tm_mon = 0, + .tm_mday = 1 + }; + cache = (strftime(year, sizeof(year), "%Y", &date) && + strcmp(year, "0099") != 0); + } + return cache; +} + static PyObject * make_somezreplacement(PyObject *object, char *sep, PyObject *tzinfoarg) { @@ -1931,10 +1949,9 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, } replacement = freplacement; } -#ifdef _Py_NORMALIZE_CENTURY - else if (ch == 'Y' || ch == 'G' - || ch == 'F' || ch == 'C' - ) { + else if (normalize_century() + && (ch == 'Y' || ch == 'G' || ch == 'F' || ch == 'C')) + { /* 0-pad year with century as necessary */ PyObject *item = PySequence_GetItem(timetuple, 0); if (item == NULL) { @@ -1985,7 +2002,6 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, } continue; } -#endif else { /* percent followed by something else */ continue; diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index dc626e4bea0f59..4fdb7b3cd1abf2 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -228,6 +228,7 @@ Modules/_datetimemodule.c datetime_isoformat specs - Modules/_datetimemodule.c parse_hh_mm_ss_ff correction - Modules/_datetimemodule.c time_isoformat specs - Modules/_datetimemodule.c - capi_types - +Modules/_datetimemodule.c normalize_century cache - Modules/_decimal/_decimal.c - cond_map_template - Modules/_decimal/_decimal.c - dec_signal_string - Modules/_decimal/_decimal.c - dflt_ctx - diff --git a/configure b/configure index 451f72fdfd4ec3..568db406dbc67b 100755 --- a/configure +++ b/configure @@ -28190,110 +28190,6 @@ printf "%s\n" "#define HAVE_STAT_TV_NSEC2 1" >>confdefs.h fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether year with century should be normalized for strftime" >&5 -printf %s "checking whether year with century should be normalized for strftime... " >&6; } -if test ${ac_cv_normalize_century+y} -then : - printf %s "(cached) " >&6 -else case e in #( - e) -if test "$cross_compiling" = yes -then : - ac_cv_normalize_century=yes -else case e in #( - e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -#include -#include - -int main(void) -{ - char year[5]; - struct tm date = { - .tm_year = -1801, - .tm_mon = 0, - .tm_mday = 1 - }; - if (strftime(year, sizeof(year), "%Y", &date) && !strcmp(year, "0099")) { - return 1; - } - return 0; -} - -_ACEOF -if ac_fn_c_try_run "$LINENO" -then : - ac_cv_normalize_century=yes -else case e in #( - e) ac_cv_normalize_century=no ;; -esac -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext ;; -esac -fi - ;; -esac -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_normalize_century" >&5 -printf "%s\n" "$ac_cv_normalize_century" >&6; } -if test "$ac_cv_normalize_century" = yes -then - -printf "%s\n" "#define _Py_NORMALIZE_CENTURY 1" >>confdefs.h - -fi - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C99-compatible strftime specifiers are supported" >&5 -printf %s "checking whether C99-compatible strftime specifiers are supported... " >&6; } -if test ${ac_cv_strftime_c99_support+y} -then : - printf %s "(cached) " >&6 -else case e in #( - e) -if test "$cross_compiling" = yes -then : - ac_cv_strftime_c99_support= -else case e in #( - e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -#include -#include - -int main(void) -{ - char full_date[11]; - struct tm date = { - .tm_year = 0, - .tm_mon = 0, - .tm_mday = 1 - }; - if (strftime(full_date, sizeof(full_date), "%F", &date) && !strcmp(full_date, "1900-01-01")) { - return 0; - } - return 1; -} - -_ACEOF -if ac_fn_c_try_run "$LINENO" -then : - ac_cv_strftime_c99_support=yes -else case e in #( - e) as_fn_error $? "Python requires C99-compatible strftime specifiers" "$LINENO" 5 ;; -esac -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext ;; -esac -fi - ;; -esac -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_strftime_c99_support" >&5 -printf "%s\n" "$ac_cv_strftime_c99_support" >&6; } - have_curses=no have_panel=no diff --git a/configure.ac b/configure.ac index f0e9eb2ee88c03..bbaff7f80352b8 100644 --- a/configure.ac +++ b/configure.ac @@ -6800,57 +6800,6 @@ then [Define if you have struct stat.st_mtimensec]) fi -AC_CACHE_CHECK([whether year with century should be normalized for strftime], [ac_cv_normalize_century], [ -AC_RUN_IFELSE([AC_LANG_SOURCE([[ -#include -#include - -int main(void) -{ - char year[5]; - struct tm date = { - .tm_year = -1801, - .tm_mon = 0, - .tm_mday = 1 - }; - if (strftime(year, sizeof(year), "%Y", &date) && !strcmp(year, "0099")) { - return 1; - } - return 0; -} -]])], -[ac_cv_normalize_century=yes], -[ac_cv_normalize_century=no], -[ac_cv_normalize_century=yes])]) -if test "$ac_cv_normalize_century" = yes -then - AC_DEFINE([_Py_NORMALIZE_CENTURY], [1], - [Define if year with century should be normalized for strftime.]) -fi - -AC_CACHE_CHECK([whether C99-compatible strftime specifiers are supported], [ac_cv_strftime_c99_support], [ -AC_RUN_IFELSE([AC_LANG_SOURCE([[ -#include -#include - -int main(void) -{ - char full_date[11]; - struct tm date = { - .tm_year = 0, - .tm_mon = 0, - .tm_mday = 1 - }; - if (strftime(full_date, sizeof(full_date), "%F", &date) && !strcmp(full_date, "1900-01-01")) { - return 0; - } - return 1; -} -]])], -[ac_cv_strftime_c99_support=yes], -[AC_MSG_ERROR([Python requires C99-compatible strftime specifiers])], -[ac_cv_strftime_c99_support=])]) - dnl check for ncursesw/ncurses and panelw/panel dnl NOTE: old curses is not detected. dnl have_curses=[no, yes] diff --git a/pyconfig.h.in b/pyconfig.h.in index 1c533b2bfb7fb4..0d6ad4465c0e93 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -2023,9 +2023,6 @@ /* HACL* library can compile SIMD256 implementations */ #undef _Py_HACL_CAN_COMPILE_VEC256 -/* Define if year with century should be normalized for strftime. */ -#undef _Py_NORMALIZE_CENTURY - /* Define to force use of thread-safe errno, h_errno, and other functions */ #undef _REENTRANT