summaryrefslogtreecommitdiff
blob: 2a26f647c10e021bd29fbc737dca7fa16e3a07f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
--- a/tclpython.c	2006-03-07 16:28:03.000000000 +0300
+++ b/tclpython.c	2014-05-06 23:33:41.713623943 +0400
@@ -19,13 +19,83 @@
 $ cc -fpic -I/usr/local/include/tcltk/tcl8.3 -c tclthread.c
 $ ld -o tclpython.so -Bshareable -L/usr/X11R6/lib -L/usr/local/lib -L/usr/local/share/python/config tclpython.o tclthread.o -lpython -lutil -lreadline -ltermcap -lcrypt -lgmp -lgdbm -lpq -lz -ltcl83 -ltk83 -lX11
 
+Patched for Python 3 with respect to https://github.com/facebook/fbthrift/blob/master/thrift/lib/py/protocol/fastbinary.c
+
 */
 
 #include <Python.h>
 #include <tcl.h>
-#include <cStringIO.h>
+
+#if PY_MAJOR_VERSION >= 3
+    #define PyInt_FromLong PyLong_FromLong
+    #define PyInt_AsLong PyLong_AsLong
+    #define PyString_FromStringAndSize PyBytes_FromStringAndSize
+#else
+    #include <cStringIO.h>
+#endif
+
 #include "tclpython.h"
 
+// Mostly copied from cStringIO.c
+#if PY_MAJOR_VERSION >= 3
+
+/** io module in python3. */
+static PyObject* Python3IO;
+
+typedef struct {
+  PyObject_HEAD
+  char *buf;
+  Py_ssize_t pos, string_size;
+} IOobject;
+
+#define IOOOBJECT(O) ((IOobject*)(O))
+
+static int
+IO__opencheck(IOobject *self) {
+    if (!self->buf) {
+        PyErr_SetString(PyExc_ValueError,
+                        "I/O operation on closed file");
+        return 0;
+    }
+    return 1;
+}
+
+static PyObject *
+IO_cgetval(PyObject *self) {
+    if (!IO__opencheck(IOOOBJECT(self))) return NULL;
+    assert(IOOOBJECT(self)->pos >= 0);
+    return PyBytes_FromStringAndSize(((IOobject*)self)->buf,
+                                     ((IOobject*)self)->pos);
+}
+#endif
+
+/* -- PYTHON MODULE SETUP STUFF --- */
+
+static PyObject *pythonTclEvaluate(PyObject *self, PyObject *args);
+
+static PyMethodDef tclMethods[] = {
+    {"eval", pythonTclEvaluate, METH_VARARGS, "Evaluate a Tcl script."},
+    {0, 0, 0, 0}                                                                                                      /* sentinel */
+};
+
+#if PY_MAJOR_VERSION >= 3
+struct module_state {
+  PyObject *error;
+};
+
+static struct PyModuleDef TclModuleDef = {
+  PyModuleDef_HEAD_INIT,
+  "tcl",
+  NULL,
+  sizeof(struct module_state),
+  tclMethods,
+  NULL,
+  NULL,
+  NULL,
+  NULL
+};
+#endif
+
 #ifndef WIN32
 /* George Petasis, 21 Feb 2006:
  * The following check cannot be handled correctly
@@ -66,13 +136,13 @@
 
 static int pythonInterpreter(ClientData clientData, Tcl_Interp *interpreter, int numberOfArguments, Tcl_Obj * CONST arguments[])
 {
-    int identifier;
+    intptr_t identifier;
     PyObject *output;
     PyObject *message;
     PyObject *result;
     PyObject *globals;
     char *string = 0;
-    int length;
+    Py_ssize_t length;
     Tcl_Obj *object;
     struct Tcl_HashEntry *entry;
     unsigned evaluate;
@@ -111,12 +181,22 @@
     /* choose start token depending on whether this is an evaluation or an execution: */
     result = PyRun_String(Tcl_GetString(arguments[2]), (evaluate? Py_eval_input: Py_file_input), globals, globals);
     if (result == 0) {                                                                                        /* an error occured */
+#if PY_MAJOR_VERSION >= 3
+        output = PyObject_CallMethod(Python3IO, "BytesIO", "()");
+#else
         output = PycStringIO->NewOutput(1024);               /* use a reasonable initial size but big enough to handle most cases */
-        PySys_SetObject("stderr", output);                                                /* capture all interpreter error output */
+#endif
+        PySys_SetObject("sys.stderr", output);                                                /* capture all interpreter error output */
         PyErr_Print();                                            /* so that error is printed on standard error, redirected above */
+#if PY_MAJOR_VERSION >= 3
+        message = IO_cgetval(output);
+        string = PyBytes_AsString(message);
+        length = (string == NULL) ? 0 : strlen(string);
+#else
         message = PycStringIO->cgetvalue(output);
         string = PyString_AsString(message);
         length = PyString_Size(message);
+#endif
         if ((length > 0) && (string[length - 1] == '\n')) length--;              /* eventually remove trailing new line character */
         object = Tcl_NewObj();
         Tcl_AppendStringsToObj(object, Tcl_GetString(arguments[0]), ": ", 0);                    /* identify interpreter in error */
@@ -124,7 +204,11 @@
         Py_DECREF(output);
     } else {
         if (evaluate) {
+#if PY_MAJOR_VERSION >= 3
+            string = PyUnicode_AsUTF8(PyObject_Str(result));
+#else
             string = PyString_AsString(PyObject_Str(result));
+#endif
             object = Tcl_NewStringObj(string, -1);                                                    /* return evaluation result */
         } else                                                                                                         /* execute */
             object = Tcl_NewObj();                                                   /* always return an empty result or an error */
@@ -139,9 +223,9 @@
 
 Tcl_Interp *tclInterpreter(CONST char *name)                           /* public function for use in extensions to this extension */
 {
-    int identifier;
+    intptr_t identifier;
 
-    if ((sscanf(name, "tcl%u", &identifier) == 0) || (identifier != 0)) {
+    if ((sscanf(name, "tcl%lu", &identifier) == 0) || (identifier != 0)) {
         return 0;                                                                                                 /* invalid name */
     } else {
         return mainInterpreter;                                                                     /* sole available interpreter */
@@ -188,14 +272,9 @@
     return Py_BuildValue("s", result);
 }
 
-static PyMethodDef tclMethods[] = {
-    {"eval", pythonTclEvaluate, METH_VARARGS, "Evaluate a Tcl script."},
-    {0, 0, 0, 0}                                                                                                      /* sentinel */
-};
-
 static int newInterpreter(Tcl_Interp *interpreter)
 {
-    int identifier;
+    intptr_t identifier;
     Tcl_Obj *object;
     int created;
 #ifdef WITH_THREAD
@@ -214,19 +293,31 @@
         return TCL_ERROR;
     } else {
         Py_Initialize();                                                                           /* initialize main interpreter */
+#if PY_MAJOR_VERSION >= 3 
+        Python3IO = PyImport_ImportModule("io");
+#else
         PycString_IMPORT;
+#endif
     }
     Tcl_SetHashValue(Tcl_CreateHashEntry(&threadStates, (ClientData)identifier, &created), 0);
 #else
     if (existingInterpreters == 0) {
         Py_Initialize();                                                                           /* initialize main interpreter */
         PyEval_InitThreads();                                               /* initialize and acquire the global interpreter lock */
+#if PY_MAJOR_VERSION >= 3 
+        Python3IO = PyImport_ImportModule("io");
+#else
         PycString_IMPORT;
+#endif
         globalState = PyThreadState_Swap(0);                                                            /* save the global thread */
     } else {
         PyEval_AcquireLock();                                           /* needed in order to be able to create a new interpreter */
     }
+#if PY_MAJOR_VERSION >= 3
+    if (Python3IO == 0) {                                              /* make sure string input/output is properly initialized */
+#else
     if (PycStringIO == 0) {                                              /* make sure string input/output is properly initialized */
+#endif
         Tcl_SetResult(interpreter, "fatal error: could not initialize Python string input/output module", TCL_STATIC);
         return TCL_ERROR;
     }
@@ -250,7 +341,11 @@
     newIdentifier++;
 #endif
     existingInterpreters++;
+#if PY_MAJOR_VERSION >= 3
+    tcl = PyModule_Create(&TclModuleDef);
+#else
     tcl = Py_InitModule("tcl", tclMethods);                                   /* add a new 'tcl' module to the python interpreter */
+#endif
     Py_INCREF(tcl);
     PyModule_AddObject(PyImport_AddModule("__builtin__"), "tcl", tcl);
     return TCL_OK;
@@ -260,7 +355,7 @@
 {
     int index;
     char *name;
-    int identifier;
+    intptr_t identifier;
     struct Tcl_HashEntry *entry;
     Tcl_Obj *object;
 #ifdef WITH_THREAD
@@ -270,7 +365,7 @@
     for (index = 0; index < numberOfArguments; index++) {
         name = Tcl_GetString(arguments[index]);                                                  /* interpreter name is "pythonN" */
         entry = 0;
-        if (sscanf(name, "python%u", &identifier) == 1) {
+        if (sscanf(name, "python%lu", &identifier) == 1) {
             identifier = atoi(name + 6);
             entry = Tcl_FindHashEntry(&threadStates, (ClientData)identifier);
         }