Issue #8407: Add pthread_kill(), sigpending() and sigwait() functions to the

signal module.
This commit is contained in:
Victor Stinner 2011-05-08 01:46:11 +02:00
parent 2c736bb38e
commit b3e7219abf
9 changed files with 287 additions and 42 deletions

View File

@ -2284,6 +2284,8 @@ written in Python, such as a mail server's external command delivery program.
will be set to *sig*. The Windows version of :func:`kill` additionally takes will be set to *sig*. The Windows version of :func:`kill` additionally takes
process handles to be killed. process handles to be killed.
See also :func:`signal.pthread_kill`.
.. versionadded:: 3.2 .. versionadded:: 3.2
Windows support. Windows support.

View File

@ -179,6 +179,29 @@ The :mod:`signal` module defines the following functions:
will then be called. Returns nothing. Not on Windows. (See the Unix man page will then be called. Returns nothing. Not on Windows. (See the Unix man page
:manpage:`signal(2)`.) :manpage:`signal(2)`.)
See also :func:`sigwait` and :func:`sigpending`.
.. function:: pthread_kill(thread_id, signum)
Send the signal *signum* to the thread *thread_id*, another thread in the same
process as the caller. The signal is asynchronously directed to thread.
*thread_id* can be read from the :attr:`~threading.Thread.ident` attribute
of :attr:`threading.Thread`. For example,
``threading.current_thread().ident`` gives the identifier of the current
thread.
If *signum* is 0, then no signal is sent, but error checking is still
performed; this can be used to check if a thread is still running.
Availability: Unix (see the man page :manpage:`pthread_kill(3)` for further
information).
See also :func:`os.kill`.
.. versionadded:: 3.3
.. function:: pthread_sigmask(how, mask) .. function:: pthread_sigmask(how, mask)
@ -206,6 +229,8 @@ The :mod:`signal` module defines the following functions:
Availability: Unix. See the man page :manpage:`sigprocmask(3)` and Availability: Unix. See the man page :manpage:`sigprocmask(3)` and
:manpage:`pthread_sigmask(3)` for further information. :manpage:`pthread_sigmask(3)` for further information.
See also :func:`pause`, :func:`sigpending` and :func:`sigwait`.
.. versionadded:: 3.3 .. versionadded:: 3.3
@ -283,6 +308,34 @@ The :mod:`signal` module defines the following functions:
:const:`SIGTERM`. A :exc:`ValueError` will be raised in any other case. :const:`SIGTERM`. A :exc:`ValueError` will be raised in any other case.
.. function:: sigpending()
Examine the set of signals that are pending for delivery to the calling
thread (i.e., the signals which have been raised while blocked). Return the
set of the pending signals.
Availability: Unix (see the man page :manpage:`sigpending(2)` for further
information).
See also :func:`pause`, :func:`pthread_sigmask` and :func:`sigwait`.
.. versionadded:: 3.3
.. function:: sigwait(sigset)
Suspend execution of the calling thread until the delivery of one of the
signals specified in the signal set *sigset*. The function accepts the signal
(removes it from the pending list of signals), and returns the signal number.
Availability: Unix (see the man page :manpage:`sigwait(3)` for further
information).
See also :func:`pause`, :func:`pthread_sigmask` and :func:`sigpending`.
.. versionadded:: 3.3
.. _signal-example: .. _signal-example:
Example Example

View File

@ -123,10 +123,13 @@ sys
signal signal
------ ------
* The :mod:`signal` module has a new :func:`~signal.pthread_sigmask` function * The :mod:`signal` module has a new functions:
to fetch and/or change the signal mask of the calling thread.
(Contributed by Jean-Paul Calderone in :issue:`8407`) * :func:`~signal.pthread_sigmask`: fetch and/or change the signal mask of the
calling thread (Contributed by Jean-Paul Calderone in :issue:`8407`) ;
* :func:`~signal.pthread_kill`: send a signal to a thread ;
* :func:`~signal.sigpending`: examine pending functions ;
* :func:`~signal.sigwait`: wait a signal.
Optimizations Optimizations

View File

@ -8,6 +8,10 @@ import signal
import subprocess import subprocess
import traceback import traceback
import sys, os, time, errno import sys, os, time, errno
try:
import threading
except ImportError:
threading = None
if sys.platform in ('os2', 'riscos'): if sys.platform in ('os2', 'riscos'):
raise unittest.SkipTest("Can't test signal on %s" % sys.platform) raise unittest.SkipTest("Can't test signal on %s" % sys.platform)
@ -187,7 +191,7 @@ class InterProcessSignalTests(unittest.TestCase):
@unittest.skipIf(sys.platform == "win32", "Not valid on Windows") @unittest.skipIf(sys.platform == "win32", "Not valid on Windows")
class BasicSignalTests(unittest.TestCase): class PosixTests(unittest.TestCase):
def trivial_signal_handler(self, *args): def trivial_signal_handler(self, *args):
pass pass
@ -484,50 +488,121 @@ class ItimerTest(unittest.TestCase):
self.assertEqual(self.hndl_called, True) self.assertEqual(self.hndl_called, True)
@unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
'need signal.pthread_sigmask()')
class PendingSignalsTests(unittest.TestCase): class PendingSignalsTests(unittest.TestCase):
""" """
Tests for the pthread_sigmask() function. Test pthread_sigmask(), pthread_kill(), sigpending() and sigwait()
functions.
""" """
def setUp(self):
self.has_pthread_kill = hasattr(signal, 'pthread_kill')
def handler(self, signum, frame): def handler(self, signum, frame):
1/0 1/0
def read_sigmask(self): def read_sigmask(self):
return signal.pthread_sigmask(signal.SIG_BLOCK, []) return signal.pthread_sigmask(signal.SIG_BLOCK, [])
def test_pthread_sigmask_arguments(self): def can_test_blocked_signals(self, skip):
self.assertRaises(TypeError, signal.pthread_sigmask) """
self.assertRaises(TypeError, signal.pthread_sigmask, 1) Check if a blocked signal can be raised to the main thread without
self.assertRaises(TypeError, signal.pthread_sigmask, 1, 2, 3) calling its signal handler. We need pthread_kill() or exactly one
self.assertRaises(RuntimeError, signal.pthread_sigmask, 1700, []) thread (the main thread).
def test_pthread_sigmask(self): Return True if it's possible. Otherwise, return False and print a
import faulthandler warning if skip is False, or raise a SkipTest exception if skip is
pid = os.getpid() True.
signum = signal.SIGUSR1 """
if self.has_pthread_kill:
return True
# The fault handler timeout thread masks all signals. If the main # The fault handler timeout thread masks all signals. If the main
# thread masks also SIGUSR1, all threads mask this signal. In this # thread masks also SIGUSR1, all threads mask this signal. In this
# case, if we send SIGUSR1 to the process, the signal is pending in the # case, if we send SIGUSR1 to the process, the signal is pending in the
# main or the faulthandler timeout thread. Unblock SIGUSR1 in the main # main or the faulthandler timeout thread. Unblock SIGUSR1 in the main
# thread calls the signal handler only if the signal is pending for the # thread calls the signal handler only if the signal is pending for the
# main thread. # main thread. Stop the faulthandler timeout thread to workaround this
# # problem.
# Stop the faulthandler timeout thread to workaround this problem. import faulthandler
# Another solution would be to send the signal directly to the main
# thread using pthread_kill(), but Python doesn't expose this
# function.
faulthandler.cancel_dump_tracebacks_later() faulthandler.cancel_dump_tracebacks_later()
# Issue #11998: The _tkinter module loads the Tcl library which creates # Issue #11998: The _tkinter module loads the Tcl library which
# a thread waiting events in select(). This thread receives signals # creates a thread waiting events in select(). This thread receives
# blocked by all other threads. We cannot test blocked signals if the # signals blocked by all other threads. We cannot test blocked
# _tkinter module is loaded. # signals
can_test_blocked_signals = ('_tkinter' not in sys.modules) if '_tkinter' in sys.modules:
if not can_test_blocked_signals: message = ("_tkinter is loaded and pthread_kill() is missing, "
print("WARNING: _tkinter is loaded, cannot test signals " "cannot test blocked signals (issue #11998)")
"blocked by pthread_sigmask() (issue #11998)") if skip:
self.skipTest(message)
else:
print("WARNING: %s" % message)
return False
return True
def kill(self, signum):
if self.has_pthread_kill:
tid = threading.current_thread().ident
signal.pthread_kill(tid, signum)
else:
pid = os.getpid()
os.kill(pid, signum)
@unittest.skipUnless(hasattr(signal, 'sigpending'),
'need signal.sigpending()')
def test_sigpending_empty(self):
self.assertEqual(signal.sigpending(), set())
@unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
'need signal.pthread_sigmask()')
@unittest.skipUnless(hasattr(signal, 'sigpending'),
'need signal.sigpending()')
def test_sigpending(self):
self.can_test_blocked_signals(True)
signum = signal.SIGUSR1
old_handler = signal.signal(signum, self.handler)
self.addCleanup(signal.signal, signum, old_handler)
signal.pthread_sigmask(signal.SIG_BLOCK, [signum])
self.kill(signum)
self.assertEqual(signal.sigpending(), {signum})
with self.assertRaises(ZeroDivisionError):
signal.pthread_sigmask(signal.SIG_UNBLOCK, [signum])
@unittest.skipUnless(hasattr(signal, 'pthread_kill'),
'need signal.pthread_kill()')
def test_pthread_kill(self):
signum = signal.SIGUSR1
current = threading.current_thread().ident
old_handler = signal.signal(signum, self.handler)
self.addCleanup(signal.signal, signum, old_handler)
with self.assertRaises(ZeroDivisionError):
signal.pthread_kill(current, signum)
@unittest.skipUnless(hasattr(signal, 'sigwait'),
'need signal.sigwait()')
def test_sigwait(self):
old_handler = signal.signal(signal.SIGALRM, self.handler)
self.addCleanup(signal.signal, signal.SIGALRM, old_handler)
signal.alarm(1)
self.assertEqual(signal.sigwait([signal.SIGALRM]), signal.SIGALRM)
@unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
'need signal.pthread_sigmask()')
def test_pthread_sigmask_arguments(self):
self.assertRaises(TypeError, signal.pthread_sigmask)
self.assertRaises(TypeError, signal.pthread_sigmask, 1)
self.assertRaises(TypeError, signal.pthread_sigmask, 1, 2, 3)
self.assertRaises(OSError, signal.pthread_sigmask, 1700, [])
@unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
'need signal.pthread_sigmask()')
def test_pthread_sigmask(self):
test_blocked_signals = self.can_test_blocked_signals(False)
signum = signal.SIGUSR1
# Install our signal handler # Install our signal handler
old_handler = signal.signal(signum, self.handler) old_handler = signal.signal(signum, self.handler)
@ -537,13 +612,13 @@ class PendingSignalsTests(unittest.TestCase):
old_mask = signal.pthread_sigmask(signal.SIG_UNBLOCK, [signum]) old_mask = signal.pthread_sigmask(signal.SIG_UNBLOCK, [signum])
self.addCleanup(signal.pthread_sigmask, signal.SIG_SETMASK, old_mask) self.addCleanup(signal.pthread_sigmask, signal.SIG_SETMASK, old_mask)
with self.assertRaises(ZeroDivisionError): with self.assertRaises(ZeroDivisionError):
os.kill(pid, signum) self.kill(signum)
# Block and then raise SIGUSR1. The signal is blocked: the signal # Block and then raise SIGUSR1. The signal is blocked: the signal
# handler is not called, and the signal is now pending # handler is not called, and the signal is now pending
signal.pthread_sigmask(signal.SIG_BLOCK, [signum]) signal.pthread_sigmask(signal.SIG_BLOCK, [signum])
if can_test_blocked_signals: if test_blocked_signals:
os.kill(pid, signum) self.kill(signum)
# Check the new mask # Check the new mask
blocked = self.read_sigmask() blocked = self.read_sigmask()
@ -551,14 +626,14 @@ class PendingSignalsTests(unittest.TestCase):
self.assertEqual(old_mask ^ blocked, {signum}) self.assertEqual(old_mask ^ blocked, {signum})
# Unblock SIGUSR1 # Unblock SIGUSR1
if can_test_blocked_signals: if test_blocked_signals:
with self.assertRaises(ZeroDivisionError): with self.assertRaises(ZeroDivisionError):
# unblock the pending signal calls immediatly the signal handler # unblock the pending signal calls immediatly the signal handler
signal.pthread_sigmask(signal.SIG_UNBLOCK, [signum]) signal.pthread_sigmask(signal.SIG_UNBLOCK, [signum])
else: else:
signal.pthread_sigmask(signal.SIG_UNBLOCK, [signum]) signal.pthread_sigmask(signal.SIG_UNBLOCK, [signum])
with self.assertRaises(ZeroDivisionError): with self.assertRaises(ZeroDivisionError):
os.kill(pid, signum) self.kill(signum)
# Check the new mask # Check the new mask
unblocked = self.read_sigmask() unblocked = self.read_sigmask()
@ -570,7 +645,7 @@ class PendingSignalsTests(unittest.TestCase):
def test_main(): def test_main():
try: try:
support.run_unittest(BasicSignalTests, InterProcessSignalTests, support.run_unittest(PosixTests, InterProcessSignalTests,
WakeupSignalTests, SiginterruptTest, WakeupSignalTests, SiginterruptTest,
ItimerTest, WindowsSignalTests, ItimerTest, WindowsSignalTests,
PendingSignalsTests) PendingSignalsTests)

View File

@ -140,6 +140,9 @@ Core and Builtins
Library Library
------- -------
- Issue #8407: Add pthread_kill(), sigpending() and sigwait() functions to the
signal module.
- Issue #11927: SMTP_SSL now uses port 465 by default as documented. Patch - Issue #11927: SMTP_SSL now uses port 465 by default as documented. Patch
by Kasun Herath. by Kasun Herath.

View File

@ -503,7 +503,7 @@ PyDoc_STRVAR(getitimer_doc,
Returns current value of given itimer."); Returns current value of given itimer.");
#endif #endif
#ifdef PYPTHREAD_SIGMASK #if defined(PYPTHREAD_SIGMASK) || defined(HAVE_SIGWAIT)
/* Convert an iterable to a sigset. /* Convert an iterable to a sigset.
Return 0 on success, return -1 and raise an exception on error. */ Return 0 on success, return -1 and raise an exception on error. */
@ -551,7 +551,9 @@ error:
Py_XDECREF(iterator); Py_XDECREF(iterator);
return result; return result;
} }
#endif
#if defined(PYPTHREAD_SIGMASK) || defined(HAVE_SIGPENDING)
static PyObject* static PyObject*
sigset_to_set(sigset_t mask) sigset_to_set(sigset_t mask)
{ {
@ -585,7 +587,9 @@ sigset_to_set(sigset_t mask)
} }
return result; return result;
} }
#endif
#ifdef PYPTHREAD_SIGMASK
static PyObject * static PyObject *
signal_pthread_sigmask(PyObject *self, PyObject *args) signal_pthread_sigmask(PyObject *self, PyObject *args)
{ {
@ -603,7 +607,7 @@ signal_pthread_sigmask(PyObject *self, PyObject *args)
err = pthread_sigmask(how, &mask, &previous); err = pthread_sigmask(how, &mask, &previous);
if (err != 0) { if (err != 0) {
errno = err; errno = err;
PyErr_SetFromErrno(PyExc_RuntimeError); PyErr_SetFromErrno(PyExc_OSError);
return NULL; return NULL;
} }
@ -621,6 +625,88 @@ Fetch and/or change the signal mask of the calling thread.");
#endif /* #ifdef PYPTHREAD_SIGMASK */ #endif /* #ifdef PYPTHREAD_SIGMASK */
#ifdef HAVE_SIGPENDING
static PyObject *
signal_sigpending(PyObject *self)
{
int err;
sigset_t mask;
err = sigpending(&mask);
if (err)
return PyErr_SetFromErrno(PyExc_OSError);
return sigset_to_set(mask);
}
PyDoc_STRVAR(signal_sigpending_doc,
"sigpending() -> list\n\
\n\
Examine pending signals.");
#endif /* #ifdef HAVE_SIGPENDING */
#ifdef HAVE_SIGWAIT
static PyObject *
signal_sigwait(PyObject *self, PyObject *args)
{
PyObject *signals;
sigset_t set;
int err, signum;
if (!PyArg_ParseTuple(args, "O:sigwait", &signals))
return NULL;
if (iterable_to_sigset(signals, &set))
return NULL;
err = sigwait(&set, &signum);
if (err) {
errno = err;
return PyErr_SetFromErrno(PyExc_OSError);
}
return PyLong_FromLong(signum);
}
PyDoc_STRVAR(signal_sigwait_doc,
"sigwait(sigset) -> signum\n\
\n\
Wait a signal.");
#endif /* #ifdef HAVE_SIGPENDING */
#if defined(HAVE_PTHREAD_KILL) && defined(WITH_THREAD)
static PyObject *
signal_pthread_kill(PyObject *self, PyObject *args)
{
long tid;
int signum;
int err;
if (!PyArg_ParseTuple(args, "li:pthread_kill", &tid, &signum))
return NULL;
err = pthread_kill(tid, signum);
if (err != 0) {
errno = err;
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
/* the signal may have been send to the current thread */
if (PyErr_CheckSignals())
return NULL;
Py_RETURN_NONE;
}
PyDoc_STRVAR(signal_pthread_kill_doc,
"pthread_kill(thread_id, signum)\n\
\n\
Send a signal to a thread.");
#endif /* #if defined(HAVE_PTHREAD_KILL) && defined(WITH_THREAD) */
/* List of functions defined in the module */ /* List of functions defined in the module */
static PyMethodDef signal_methods[] = { static PyMethodDef signal_methods[] = {
#ifdef HAVE_ALARM #ifdef HAVE_ALARM
@ -644,9 +730,21 @@ static PyMethodDef signal_methods[] = {
#endif #endif
{"default_int_handler", signal_default_int_handler, {"default_int_handler", signal_default_int_handler,
METH_VARARGS, default_int_handler_doc}, METH_VARARGS, default_int_handler_doc},
#if defined(HAVE_PTHREAD_KILL) && defined(WITH_THREAD)
{"pthread_kill", (PyCFunction)signal_pthread_kill,
METH_VARARGS, signal_pthread_kill_doc},
#endif
#ifdef PYPTHREAD_SIGMASK #ifdef PYPTHREAD_SIGMASK
{"pthread_sigmask", (PyCFunction)signal_pthread_sigmask, {"pthread_sigmask", (PyCFunction)signal_pthread_sigmask,
METH_VARARGS, signal_pthread_sigmask_doc}, METH_VARARGS, signal_pthread_sigmask_doc},
#endif
#ifdef HAVE_SIGPENDING
{"sigpending", (PyCFunction)signal_sigpending,
METH_NOARGS, signal_sigpending_doc},
#endif
#ifdef HAVE_SIGWAIT
{"sigwait", (PyCFunction)signal_sigwait,
METH_VARARGS, signal_sigwait_doc},
#endif #endif
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };

5
configure vendored
View File

@ -9258,11 +9258,12 @@ for ac_func in alarm accept4 setitimer getitimer bind_textdomain_codeset chown \
initgroups kill killpg lchmod lchown lockf linkat lstat lutimes mbrtowc mkdirat mkfifo \ initgroups kill killpg lchmod lchown lockf linkat lstat lutimes mbrtowc mkdirat mkfifo \
mkfifoat mknod mknodat mktime mremap nice openat pathconf pause plock poll \ mkfifoat mknod mknodat mktime mremap nice openat pathconf pause plock poll \
posix_fallocate posix_fadvise pread \ posix_fallocate posix_fadvise pread \
pthread_init putenv pwrite readlink readlinkat readv realpath renameat \ pthread_init pthread_kill putenv pwrite readlink readlinkat readv realpath renameat \
select sem_open sem_timedwait sem_getvalue sem_unlink sendfile setegid seteuid \ select sem_open sem_timedwait sem_getvalue sem_unlink sendfile setegid seteuid \
setgid sethostname \ setgid sethostname \
setlocale setregid setreuid setresuid setresgid setsid setpgid setpgrp setpriority setuid setvbuf \ setlocale setregid setreuid setresuid setresgid setsid setpgid setpgrp setpriority setuid setvbuf \
sigaction sigaltstack siginterrupt sigrelse snprintf strftime strlcpy symlinkat sync \ sigaction sigaltstack siginterrupt sigpending \
sigrelse sigwait snprintf strftime strlcpy symlinkat sync \
sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile tmpnam tmpnam_r \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile tmpnam tmpnam_r \
truncate uname unlinkat unsetenv utimensat utimes waitid waitpid wait3 wait4 \ truncate uname unlinkat unsetenv utimensat utimes waitid waitpid wait3 wait4 \
wcscoll wcsftime wcsxfrm writev _getpty wcscoll wcsftime wcsxfrm writev _getpty

View File

@ -2503,11 +2503,12 @@ AC_CHECK_FUNCS(alarm accept4 setitimer getitimer bind_textdomain_codeset chown \
initgroups kill killpg lchmod lchown lockf linkat lstat lutimes mbrtowc mkdirat mkfifo \ initgroups kill killpg lchmod lchown lockf linkat lstat lutimes mbrtowc mkdirat mkfifo \
mkfifoat mknod mknodat mktime mremap nice openat pathconf pause plock poll \ mkfifoat mknod mknodat mktime mremap nice openat pathconf pause plock poll \
posix_fallocate posix_fadvise pread \ posix_fallocate posix_fadvise pread \
pthread_init putenv pwrite readlink readlinkat readv realpath renameat \ pthread_init pthread_kill putenv pwrite readlink readlinkat readv realpath renameat \
select sem_open sem_timedwait sem_getvalue sem_unlink sendfile setegid seteuid \ select sem_open sem_timedwait sem_getvalue sem_unlink sendfile setegid seteuid \
setgid sethostname \ setgid sethostname \
setlocale setregid setreuid setresuid setresgid setsid setpgid setpgrp setpriority setuid setvbuf \ setlocale setregid setreuid setresuid setresgid setsid setpgid setpgrp setpriority setuid setvbuf \
sigaction sigaltstack siginterrupt sigrelse snprintf strftime strlcpy symlinkat sync \ sigaction sigaltstack siginterrupt sigpending \
sigrelse sigwait snprintf strftime strlcpy symlinkat sync \
sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile tmpnam tmpnam_r \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile tmpnam tmpnam_r \
truncate uname unlinkat unsetenv utimensat utimes waitid waitpid wait3 wait4 \ truncate uname unlinkat unsetenv utimensat utimes waitid waitpid wait3 wait4 \
wcscoll wcsftime wcsxfrm writev _getpty) wcscoll wcsftime wcsxfrm writev _getpty)

View File

@ -590,6 +590,9 @@
/* Define to 1 if you have the `pthread_sigmask' function. */ /* Define to 1 if you have the `pthread_sigmask' function. */
#undef HAVE_PTHREAD_SIGMASK #undef HAVE_PTHREAD_SIGMASK
/* Define to 1 if you have the `pthread_kill' function. */
#undef HAVE_PTHREAD_KILL
/* Define to 1 if you have the <pty.h> header file. */ /* Define to 1 if you have the <pty.h> header file. */
#undef HAVE_PTY_H #undef HAVE_PTY_H
@ -719,12 +722,18 @@
/* Define to 1 if you have the `siginterrupt' function. */ /* Define to 1 if you have the `siginterrupt' function. */
#undef HAVE_SIGINTERRUPT #undef HAVE_SIGINTERRUPT
/* Define to 1 if you have the `sigpending' function. */
#undef HAVE_SIGPENDING
/* Define to 1 if you have the <signal.h> header file. */ /* Define to 1 if you have the <signal.h> header file. */
#undef HAVE_SIGNAL_H #undef HAVE_SIGNAL_H
/* Define to 1 if you have the `sigrelse' function. */ /* Define to 1 if you have the `sigrelse' function. */
#undef HAVE_SIGRELSE #undef HAVE_SIGRELSE
/* Define to 1 if you have the `sigwait' function. */
#undef HAVE_SIGWAIT
/* Define to 1 if you have the `snprintf' function. */ /* Define to 1 if you have the `snprintf' function. */
#undef HAVE_SNPRINTF #undef HAVE_SNPRINTF