summaryrefslogtreecommitdiff
path: root/Modules/posixmodule.c
diff options
context:
space:
mode:
authorThomas Wouters <thomas@python.org>2006-04-21 10:40:58 +0000
committerThomas Wouters <thomas@python.org>2006-04-21 10:40:58 +0000
commit49fd7fa4431da299196d74087df4a04f99f9c46f (patch)
tree35ace5fe78d3d52c7a9ab356ab9f6dbf8d4b71f4 /Modules/posixmodule.c
parent9ada3d6e29d5165dadacbe6be07bcd35cfbef59d (diff)
downloadcpython-git-49fd7fa4431da299196d74087df4a04f99f9c46f.tar.gz
Merge p3yk branch with the trunk up to revision 45595. This breaks a fair
number of tests, all because of the codecs/_multibytecodecs issue described here (it's not a Py3K issue, just something Py3K discovers): http://mail.python.org/pipermail/python-dev/2006-April/064051.html Hye-Shik Chang promised to look for a fix, so no need to fix it here. The tests that are expected to break are: test_codecencodings_cn test_codecencodings_hk test_codecencodings_jp test_codecencodings_kr test_codecencodings_tw test_codecs test_multibytecodec This merge fixes an actual test failure (test_weakref) in this branch, though, so I believe merging is the right thing to do anyway.
Diffstat (limited to 'Modules/posixmodule.c')
-rw-r--r--Modules/posixmodule.c424
1 files changed, 268 insertions, 156 deletions
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 1fbc353008..b51ba5dc54 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -22,6 +22,10 @@
# include <unixio.h>
#endif /* defined(__VMS) */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
PyDoc_STRVAR(posix__doc__,
"This module provides access to operating system functionality that is\n\
standardized by the C Standard and the POSIX standard (a thinly\n\
@@ -264,6 +268,12 @@ extern int lstat(const char *, struct stat *);
#define WTERMSIG(u_wait) ((u_wait).w_termsig)
#endif
+#define WAIT_TYPE union wait
+#define WAIT_STATUS_INT(s) (s.w_status)
+
+#else /* !UNION_WAIT */
+#define WAIT_TYPE int
+#define WAIT_STATUS_INT(s) (s)
#endif /* UNION_WAIT */
/* Don't use the "_r" form if we don't need it (also, won't have a
@@ -971,6 +981,7 @@ static PyStructSequence_Desc statvfs_result_desc = {
10
};
+static int initialized;
static PyTypeObject StatResultType;
static PyTypeObject StatVFSResultType;
static newfunc structseq_new;
@@ -1839,6 +1850,7 @@ posix_listdir(PyObject *self, PyObject *args)
struct dirent *ep;
int arg_is_unicode = 1;
+ errno = 0;
if (!PyArg_ParseTuple(args, "U:listdir", &v)) {
arg_is_unicode = 0;
PyErr_Clear();
@@ -1895,6 +1907,12 @@ posix_listdir(PyObject *self, PyObject *args)
}
Py_DECREF(v);
}
+ if (errno != 0 && d != NULL) {
+ /* readdir() returned NULL and set errno */
+ closedir(dirp);
+ Py_DECREF(d);
+ return posix_error_with_allocated_filename(name);
+ }
closedir(dirp);
PyMem_Free(name);
@@ -1995,13 +2013,13 @@ posix_mkdir(PyObject *self, PyObject *args)
}
-#ifdef HAVE_NICE
-#if defined(HAVE_BROKEN_NICE) && defined(HAVE_SYS_RESOURCE_H)
-#if defined(HAVE_GETPRIORITY) && !defined(PRIO_PROCESS)
+/* sys/resource.h is needed for at least: wait3(), wait4(), broken nice. */
+#if defined(HAVE_SYS_RESOURCE_H)
#include <sys/resource.h>
#endif
-#endif
+
+#ifdef HAVE_NICE
PyDoc_STRVAR(posix_nice__doc__,
"nice(inc) -> new_priority\n\n\
Decrease the priority of process by inc and return the new priority.");
@@ -3088,7 +3106,7 @@ posix_openpty(PyObject *self, PyObject *noargs)
#if defined(HAVE_DEV_PTMX) && !defined(HAVE_OPENPTY) && !defined(HAVE__GETPTY)
PyOS_sighandler_t sig_saved;
#ifdef sun
- extern char *ptsname();
+ extern char *ptsname(int fildes);
#endif
#endif
@@ -5091,6 +5109,114 @@ posix_setgroups(PyObject *self, PyObject *args)
}
#endif /* HAVE_SETGROUPS */
+#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4)
+static PyObject *
+wait_helper(int pid, int status, struct rusage *ru)
+{
+ PyObject *result;
+ static PyObject *struct_rusage;
+
+ if (pid == -1)
+ return posix_error();
+
+ if (struct_rusage == NULL) {
+ PyObject *m = PyImport_ImportModule("resource");
+ if (m == NULL)
+ return NULL;
+ struct_rusage = PyObject_GetAttrString(m, "struct_rusage");
+ Py_DECREF(m);
+ if (struct_rusage == NULL)
+ return NULL;
+ }
+
+ /* XXX(nnorwitz): Copied (w/mods) from resource.c, there should be only one. */
+ result = PyStructSequence_New((PyTypeObject*) struct_rusage);
+ if (!result)
+ return NULL;
+
+#ifndef doubletime
+#define doubletime(TV) ((double)(TV).tv_sec + (TV).tv_usec * 0.000001)
+#endif
+
+ PyStructSequence_SET_ITEM(result, 0,
+ PyFloat_FromDouble(doubletime(ru->ru_utime)));
+ PyStructSequence_SET_ITEM(result, 1,
+ PyFloat_FromDouble(doubletime(ru->ru_stime)));
+#define SET_INT(result, index, value)\
+ PyStructSequence_SET_ITEM(result, index, PyInt_FromLong(value))
+ SET_INT(result, 2, ru->ru_maxrss);
+ SET_INT(result, 3, ru->ru_ixrss);
+ SET_INT(result, 4, ru->ru_idrss);
+ SET_INT(result, 5, ru->ru_isrss);
+ SET_INT(result, 6, ru->ru_minflt);
+ SET_INT(result, 7, ru->ru_majflt);
+ SET_INT(result, 8, ru->ru_nswap);
+ SET_INT(result, 9, ru->ru_inblock);
+ SET_INT(result, 10, ru->ru_oublock);
+ SET_INT(result, 11, ru->ru_msgsnd);
+ SET_INT(result, 12, ru->ru_msgrcv);
+ SET_INT(result, 13, ru->ru_nsignals);
+ SET_INT(result, 14, ru->ru_nvcsw);
+ SET_INT(result, 15, ru->ru_nivcsw);
+#undef SET_INT
+
+ if (PyErr_Occurred()) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ return Py_BuildValue("iiN", pid, status, result);
+}
+#endif /* HAVE_WAIT3 || HAVE_WAIT4 */
+
+#ifdef HAVE_WAIT3
+PyDoc_STRVAR(posix_wait3__doc__,
+"wait3(options) -> (pid, status, rusage)\n\n\
+Wait for completion of a child process.");
+
+static PyObject *
+posix_wait3(PyObject *self, PyObject *args)
+{
+ int pid, options;
+ struct rusage ru;
+ WAIT_TYPE status;
+ WAIT_STATUS_INT(status) = 0;
+
+ if (!PyArg_ParseTuple(args, "i:wait3", &options))
+ return NULL;
+
+ Py_BEGIN_ALLOW_THREADS
+ pid = wait3(&status, options, &ru);
+ Py_END_ALLOW_THREADS
+
+ return wait_helper(pid, WAIT_STATUS_INT(status), &ru);
+}
+#endif /* HAVE_WAIT3 */
+
+#ifdef HAVE_WAIT4
+PyDoc_STRVAR(posix_wait4__doc__,
+"wait4(pid, options) -> (pid, status, rusage)\n\n\
+Wait for completion of a given child process.");
+
+static PyObject *
+posix_wait4(PyObject *self, PyObject *args)
+{
+ int pid, options;
+ struct rusage ru;
+ WAIT_TYPE status;
+ WAIT_STATUS_INT(status) = 0;
+
+ if (!PyArg_ParseTuple(args, "ii:wait4", &pid, &options))
+ return NULL;
+
+ Py_BEGIN_ALLOW_THREADS
+ pid = wait4(pid, &status, options, &ru);
+ Py_END_ALLOW_THREADS
+
+ return wait_helper(pid, WAIT_STATUS_INT(status), &ru);
+}
+#endif /* HAVE_WAIT4 */
+
#ifdef HAVE_WAITPID
PyDoc_STRVAR(posix_waitpid__doc__,
"waitpid(pid, options) -> (pid, status)\n\n\
@@ -5100,14 +5226,8 @@ static PyObject *
posix_waitpid(PyObject *self, PyObject *args)
{
int pid, options;
-#ifdef UNION_WAIT
- union wait status;
-#define status_i (status.w_status)
-#else
- int status;
-#define status_i status
-#endif
- status_i = 0;
+ WAIT_TYPE status;
+ WAIT_STATUS_INT(status) = 0;
if (!PyArg_ParseTuple(args, "ii:waitpid", &pid, &options))
return NULL;
@@ -5116,8 +5236,8 @@ posix_waitpid(PyObject *self, PyObject *args)
Py_END_ALLOW_THREADS
if (pid == -1)
return posix_error();
- else
- return Py_BuildValue("ii", pid, status_i);
+
+ return Py_BuildValue("ii", pid, WAIT_STATUS_INT(status));
}
#elif defined(HAVE_CWAIT)
@@ -5140,10 +5260,9 @@ posix_waitpid(PyObject *self, PyObject *args)
Py_END_ALLOW_THREADS
if (pid == -1)
return posix_error();
- else
- /* shift the status left a byte so this is more like the
- POSIX waitpid */
- return Py_BuildValue("ii", pid, status << 8);
+
+ /* shift the status left a byte so this is more like the POSIX waitpid */
+ return Py_BuildValue("ii", pid, status << 8);
}
#endif /* HAVE_WAITPID || HAVE_CWAIT */
@@ -5156,23 +5275,16 @@ static PyObject *
posix_wait(PyObject *self, PyObject *noargs)
{
int pid;
-#ifdef UNION_WAIT
- union wait status;
-#define status_i (status.w_status)
-#else
- int status;
-#define status_i status
-#endif
+ WAIT_TYPE status;
+ WAIT_STATUS_INT(status) = 0;
- status_i = 0;
Py_BEGIN_ALLOW_THREADS
pid = wait(&status);
Py_END_ALLOW_THREADS
if (pid == -1)
return posix_error();
- else
- return Py_BuildValue("ii", pid, status_i);
-#undef status_i
+
+ return Py_BuildValue("ii", pid, WAIT_STATUS_INT(status));
}
#endif
@@ -5668,9 +5780,24 @@ posix_fdopen(PyObject *self, PyObject *args)
"invalid file mode '%s'", mode);
return NULL;
}
-
Py_BEGIN_ALLOW_THREADS
+#if !defined(MS_WINDOWS) && defined(HAVE_FCNTL_H)
+ if (mode[0] == 'a') {
+ /* try to make sure the O_APPEND flag is set */
+ int flags;
+ flags = fcntl(fd, F_GETFL);
+ if (flags != -1)
+ fcntl(fd, F_SETFL, flags | O_APPEND);
+ fp = fdopen(fd, mode);
+ if (fp == NULL && flags != -1)
+ /* restore old mode if fdopen failed */
+ fcntl(fd, F_SETFL, flags);
+ } else {
+ fp = fdopen(fd, mode);
+ }
+#else
fp = fdopen(fd, mode);
+#endif
Py_END_ALLOW_THREADS
if (fp == NULL)
return posix_error();
@@ -5887,7 +6014,7 @@ static PyObject *
posix_putenv(PyObject *self, PyObject *args)
{
char *s1, *s2;
- char *new;
+ char *newenv;
PyObject *newstr;
size_t len;
@@ -5918,9 +6045,9 @@ posix_putenv(PyObject *self, PyObject *args)
newstr = PyString_FromStringAndSize(NULL, (int)len - 1);
if (newstr == NULL)
return PyErr_NoMemory();
- new = PyString_AS_STRING(newstr);
- PyOS_snprintf(new, len, "%s=%s", s1, s2);
- if (putenv(new)) {
+ newenv = PyString_AS_STRING(newstr);
+ PyOS_snprintf(newenv, len, "%s=%s", s1, s2);
+ if (putenv(newenv)) {
Py_DECREF(newstr);
posix_error();
return NULL;
@@ -6010,22 +6137,13 @@ Return True if the process returning 'status' was dumped to a core file.");
static PyObject *
posix_WCOREDUMP(PyObject *self, PyObject *args)
{
-#ifdef UNION_WAIT
- union wait status;
-#define status_i (status.w_status)
-#else
- int status;
-#define status_i status
-#endif
- status_i = 0;
+ WAIT_TYPE status;
+ WAIT_STATUS_INT(status) = 0;
- if (!PyArg_ParseTuple(args, "i:WCOREDUMP", &status_i))
- {
+ if (!PyArg_ParseTuple(args, "i:WCOREDUMP", &WAIT_STATUS_INT(status)))
return NULL;
- }
return PyBool_FromLong(WCOREDUMP(status));
-#undef status_i
}
#endif /* WCOREDUMP */
@@ -6038,22 +6156,13 @@ job control stop.");
static PyObject *
posix_WIFCONTINUED(PyObject *self, PyObject *args)
{
-#ifdef UNION_WAIT
- union wait status;
-#define status_i (status.w_status)
-#else
- int status;
-#define status_i status
-#endif
- status_i = 0;
+ WAIT_TYPE status;
+ WAIT_STATUS_INT(status) = 0;
- if (!PyArg_ParseTuple(args, "i:WCONTINUED", &status_i))
- {
+ if (!PyArg_ParseTuple(args, "i:WCONTINUED", &WAIT_STATUS_INT(status)))
return NULL;
- }
return PyBool_FromLong(WIFCONTINUED(status));
-#undef status_i
}
#endif /* WIFCONTINUED */
@@ -6065,22 +6174,13 @@ Return True if the process returning 'status' was stopped.");
static PyObject *
posix_WIFSTOPPED(PyObject *self, PyObject *args)
{
-#ifdef UNION_WAIT
- union wait status;
-#define status_i (status.w_status)
-#else
- int status;
-#define status_i status
-#endif
- status_i = 0;
+ WAIT_TYPE status;
+ WAIT_STATUS_INT(status) = 0;
- if (!PyArg_ParseTuple(args, "i:WIFSTOPPED", &status_i))
- {
+ if (!PyArg_ParseTuple(args, "i:WIFSTOPPED", &WAIT_STATUS_INT(status)))
return NULL;
- }
return PyBool_FromLong(WIFSTOPPED(status));
-#undef status_i
}
#endif /* WIFSTOPPED */
@@ -6092,22 +6192,13 @@ Return True if the process returning 'status' was terminated by a signal.");
static PyObject *
posix_WIFSIGNALED(PyObject *self, PyObject *args)
{
-#ifdef UNION_WAIT
- union wait status;
-#define status_i (status.w_status)
-#else
- int status;
-#define status_i status
-#endif
- status_i = 0;
+ WAIT_TYPE status;
+ WAIT_STATUS_INT(status) = 0;
- if (!PyArg_ParseTuple(args, "i:WIFSIGNALED", &status_i))
- {
+ if (!PyArg_ParseTuple(args, "i:WIFSIGNALED", &WAIT_STATUS_INT(status)))
return NULL;
- }
return PyBool_FromLong(WIFSIGNALED(status));
-#undef status_i
}
#endif /* WIFSIGNALED */
@@ -6120,22 +6211,13 @@ system call.");
static PyObject *
posix_WIFEXITED(PyObject *self, PyObject *args)
{
-#ifdef UNION_WAIT
- union wait status;
-#define status_i (status.w_status)
-#else
- int status;
-#define status_i status
-#endif
- status_i = 0;
+ WAIT_TYPE status;
+ WAIT_STATUS_INT(status) = 0;
- if (!PyArg_ParseTuple(args, "i:WIFEXITED", &status_i))
- {
+ if (!PyArg_ParseTuple(args, "i:WIFEXITED", &WAIT_STATUS_INT(status)))
return NULL;
- }
return PyBool_FromLong(WIFEXITED(status));
-#undef status_i
}
#endif /* WIFEXITED */
@@ -6147,22 +6229,13 @@ Return the process return code from 'status'.");
static PyObject *
posix_WEXITSTATUS(PyObject *self, PyObject *args)
{
-#ifdef UNION_WAIT
- union wait status;
-#define status_i (status.w_status)
-#else
- int status;
-#define status_i status
-#endif
- status_i = 0;
+ WAIT_TYPE status;
+ WAIT_STATUS_INT(status) = 0;
- if (!PyArg_ParseTuple(args, "i:WEXITSTATUS", &status_i))
- {
+ if (!PyArg_ParseTuple(args, "i:WEXITSTATUS", &WAIT_STATUS_INT(status)))
return NULL;
- }
return Py_BuildValue("i", WEXITSTATUS(status));
-#undef status_i
}
#endif /* WEXITSTATUS */
@@ -6175,22 +6248,13 @@ value.");
static PyObject *
posix_WTERMSIG(PyObject *self, PyObject *args)
{
-#ifdef UNION_WAIT
- union wait status;
-#define status_i (status.w_status)
-#else
- int status;
-#define status_i status
-#endif
- status_i = 0;
+ WAIT_TYPE status;
+ WAIT_STATUS_INT(status) = 0;
- if (!PyArg_ParseTuple(args, "i:WTERMSIG", &status_i))
- {
+ if (!PyArg_ParseTuple(args, "i:WTERMSIG", &WAIT_STATUS_INT(status)))
return NULL;
- }
return Py_BuildValue("i", WTERMSIG(status));
-#undef status_i
}
#endif /* WTERMSIG */
@@ -6203,22 +6267,13 @@ the 'status' value.");
static PyObject *
posix_WSTOPSIG(PyObject *self, PyObject *args)
{
-#ifdef UNION_WAIT
- union wait status;
-#define status_i (status.w_status)
-#else
- int status;
-#define status_i status
-#endif
- status_i = 0;
+ WAIT_TYPE status;
+ WAIT_STATUS_INT(status) = 0;
- if (!PyArg_ParseTuple(args, "i:WSTOPSIG", &status_i))
- {
+ if (!PyArg_ParseTuple(args, "i:WSTOPSIG", &WAIT_STATUS_INT(status)))
return NULL;
- }
return Py_BuildValue("i", WSTOPSIG(status));
-#undef status_i
}
#endif /* WSTOPSIG */
@@ -6396,15 +6451,16 @@ posix_tmpnam(PyObject *self, PyObject *noargs)
name = tmpnam(buffer);
#endif
if (name == NULL) {
- PyErr_SetObject(PyExc_OSError,
- Py_BuildValue("is", 0,
+ PyObject *err = Py_BuildValue("is", 0,
#ifdef USE_TMPNAM_R
"unexpected NULL from tmpnam_r"
#else
"unexpected NULL from tmpnam"
#endif
- ));
- return NULL;
+ );
+ PyErr_SetObject(PyExc_OSError, err);
+ Py_XDECREF(err);
+ return NULL;
}
return PyString_FromString(buffer);
}
@@ -6753,26 +6809,30 @@ posix_confstr(PyObject *self, PyObject *args)
{
PyObject *result = NULL;
int name;
- char buffer[64];
+ char buffer[256];
if (PyArg_ParseTuple(args, "O&:confstr", conv_confstr_confname, &name)) {
- int len = confstr(name, buffer, sizeof(buffer));
+ int len;
errno = 0;
- if (len == 0) {
- if (errno != 0)
- posix_error();
- else
- result = PyString_FromString("");
+ len = confstr(name, buffer, sizeof(buffer));
+ if (len == 0) {
+ if (errno) {
+ posix_error();
+ }
+ else {
+ result = Py_None;
+ Py_INCREF(Py_None);
+ }
}
else {
- if (len >= sizeof(buffer)) {
- result = PyString_FromStringAndSize(NULL, len);
+ if ((unsigned int)len >= sizeof(buffer)) {
+ result = PyString_FromStringAndSize(NULL, len-1);
if (result != NULL)
- confstr(name, PyString_AS_STRING(result), len+1);
+ confstr(name, PyString_AS_STRING(result), len);
}
else
- result = PyString_FromString(buffer);
+ result = PyString_FromStringAndSize(buffer, len-1);
}
}
return result;
@@ -7423,6 +7483,44 @@ win32_startfile(PyObject *self, PyObject *args)
char *filepath;
char *operation = NULL;
HINSTANCE rc;
+#ifdef Py_WIN_WIDE_FILENAMES
+ if (unicode_file_names()) {
+ PyObject *unipath, *woperation = NULL;
+ if (!PyArg_ParseTuple(args, "U|s:startfile",
+ &unipath, &operation)) {
+ PyErr_Clear();
+ goto normal;
+ }
+
+
+ if (operation) {
+ woperation = PyUnicode_DecodeASCII(operation,
+ strlen(operation), NULL);
+ if (!woperation) {
+ PyErr_Clear();
+ operation = NULL;
+ goto normal;
+ }
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ rc = ShellExecuteW((HWND)0, woperation ? PyUnicode_AS_UNICODE(woperation) : 0,
+ PyUnicode_AS_UNICODE(unipath),
+ NULL, NULL, SW_SHOWNORMAL);
+ Py_END_ALLOW_THREADS
+
+ Py_XDECREF(woperation);
+ if (rc <= (HINSTANCE)32) {
+ PyObject *errval = win32_error_unicode("startfile",
+ PyUnicode_AS_UNICODE(unipath));
+ return errval;
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+#endif
+
+normal:
if (!PyArg_ParseTuple(args, "et|s:startfile",
Py_FileSystemDefaultEncoding, &filepath,
&operation))
@@ -7695,6 +7793,12 @@ static PyMethodDef posix_methods[] = {
#ifdef HAVE_WAIT
{"wait", posix_wait, METH_NOARGS, posix_wait__doc__},
#endif /* HAVE_WAIT */
+#ifdef HAVE_WAIT3
+ {"wait3", posix_wait3, METH_VARARGS, posix_wait3__doc__},
+#endif /* HAVE_WAIT3 */
+#ifdef HAVE_WAIT4
+ {"wait4", posix_wait4, METH_VARARGS, posix_wait4__doc__},
+#endif /* HAVE_WAIT4 */
#if defined(HAVE_WAITPID) || defined(HAVE_CWAIT)
{"waitpid", posix_waitpid, METH_VARARGS, posix_waitpid__doc__},
#endif /* HAVE_WAITPID */
@@ -8142,19 +8246,27 @@ INITFUNC(void)
posix_putenv_garbage = PyDict_New();
#endif
- stat_result_desc.name = MODNAME ".stat_result";
- stat_result_desc.fields[7].name = PyStructSequence_UnnamedField;
- stat_result_desc.fields[8].name = PyStructSequence_UnnamedField;
- stat_result_desc.fields[9].name = PyStructSequence_UnnamedField;
- PyStructSequence_InitType(&StatResultType, &stat_result_desc);
- structseq_new = StatResultType.tp_new;
- StatResultType.tp_new = statresult_new;
+ if (!initialized) {
+ stat_result_desc.name = MODNAME ".stat_result";
+ stat_result_desc.fields[7].name = PyStructSequence_UnnamedField;
+ stat_result_desc.fields[8].name = PyStructSequence_UnnamedField;
+ stat_result_desc.fields[9].name = PyStructSequence_UnnamedField;
+ PyStructSequence_InitType(&StatResultType, &stat_result_desc);
+ structseq_new = StatResultType.tp_new;
+ StatResultType.tp_new = statresult_new;
+
+ statvfs_result_desc.name = MODNAME ".statvfs_result";
+ PyStructSequence_InitType(&StatVFSResultType, &statvfs_result_desc);
+ }
Py_INCREF((PyObject*) &StatResultType);
PyModule_AddObject(m, "stat_result", (PyObject*) &StatResultType);
-
- statvfs_result_desc.name = MODNAME ".statvfs_result";
- PyStructSequence_InitType(&StatVFSResultType, &statvfs_result_desc);
Py_INCREF((PyObject*) &StatVFSResultType);
PyModule_AddObject(m, "statvfs_result",
(PyObject*) &StatVFSResultType);
+ initialized = 1;
}
+
+#ifdef __cplusplus
+}
+#endif
+