1 #ifndef __FILE_COMPAT_H__
2 #define __FILE_COMPAT_H__
3
4 #include <Python.h>
5 #include <stdio.h>
6 #include "numpy/npy_common.h"
7 #include "numpy/ndarrayobject.h"
8 #include "mplutils.h"
9
10 #ifdef __cplusplus
11 extern "C" {
12 #endif
13 #if defined(_MSC_VER) && defined(_WIN64) && (_MSC_VER > 1400)
14 #include <io.h>
15 #define mpl_fseek _fseeki64
16 #define mpl_ftell _ftelli64
17 #define mpl_lseek _lseeki64
18 #define mpl_off_t npy_int64
19
20 #if NPY_SIZEOF_INT == 8
21 #define MPL_OFF_T_PYFMT "i"
22 #elif NPY_SIZEOF_LONG == 8
23 #define MPL_OFF_T_PYFMT "l"
24 #elif NPY_SIZEOF_LONGLONG == 8
25 #define MPL_OFF_T_PYFMT "L"
26 #else
27 #error Unsupported size for type off_t
28 #endif
29 #else
30 #define mpl_fseek fseek
31 #define mpl_ftell ftell
32 #define mpl_lseek lseek
33 #define mpl_off_t off_t
34
35 #if NPY_SIZEOF_INT == NPY_SIZEOF_SHORT
36 #define MPL_OFF_T_PYFMT "h"
37 #elif NPY_SIZEOF_INT == NPY_SIZEOF_INT
38 #define MPL_OFF_T_PYFMT "i"
39 #elif NPY_SIZEOF_INT == NPY_SIZEOF_LONG
40 #define MPL_OFF_T_PYFMT "l"
41 #elif NPY_SIZEOF_INT == NPY_SIZEOF_LONGLONG
42 #define MPL_OFF_T_PYFMT "L"
43 #else
44 #error Unsupported size for type off_t
45 #endif
46 #endif
47
48 /*
49 * PyFile_* compatibility
50 */
51 #if PY3K | defined(PYPY_VERSION)
52
53 /*
54 * Get a FILE* handle to the file represented by the Python object
55 */
mpl_PyFile_Dup(PyObject * file,char * mode,mpl_off_t * orig_pos)56 static NPY_INLINE FILE *mpl_PyFile_Dup(PyObject *file, char *mode, mpl_off_t *orig_pos)
57 {
58 int fd, fd2;
59 PyObject *ret, *os;
60 mpl_off_t pos;
61 FILE *handle;
62
63 if (mode[0] != 'r') {
64 /* Flush first to ensure things end up in the file in the correct order */
65 ret = PyObject_CallMethod(file, (char *)"flush", (char *)"");
66 if (ret == NULL) {
67 return NULL;
68 }
69 Py_DECREF(ret);
70 }
71
72 fd = PyObject_AsFileDescriptor(file);
73 if (fd == -1) {
74 return NULL;
75 }
76
77 /* The handle needs to be dup'd because we have to call fclose
78 at the end */
79 os = PyImport_ImportModule("os");
80 if (os == NULL) {
81 return NULL;
82 }
83 ret = PyObject_CallMethod(os, (char *)"dup", (char *)"i", fd);
84 Py_DECREF(os);
85 if (ret == NULL) {
86 return NULL;
87 }
88 fd2 = PyNumber_AsSsize_t(ret, NULL);
89 Py_DECREF(ret);
90
91 /* Convert to FILE* handle */
92 #ifdef _WIN32
93 handle = _fdopen(fd2, mode);
94 #else
95 handle = fdopen(fd2, mode);
96 #endif
97 if (handle == NULL) {
98 PyErr_SetString(PyExc_IOError, "Getting a FILE* from a Python file object failed");
99 }
100
101 /* Record the original raw file handle position */
102 *orig_pos = mpl_ftell(handle);
103 if (*orig_pos == -1) {
104 // handle is a stream, so we don't have to worry about this
105 return handle;
106 }
107
108 /* Seek raw handle to the Python-side position */
109 ret = PyObject_CallMethod(file, (char *)"tell", (char *)"");
110 if (ret == NULL) {
111 fclose(handle);
112 return NULL;
113 }
114 pos = PyNumber_AsSsize_t(ret, PyExc_OverflowError);
115 Py_DECREF(ret);
116 if (PyErr_Occurred()) {
117 fclose(handle);
118 return NULL;
119 }
120 if (mpl_fseek(handle, pos, SEEK_SET) == -1) {
121 PyErr_SetString(PyExc_IOError, "seeking file failed");
122 return NULL;
123 }
124 return handle;
125 }
126
127 /*
128 * Close the dup-ed file handle, and seek the Python one to the current position
129 */
mpl_PyFile_DupClose(PyObject * file,FILE * handle,mpl_off_t orig_pos)130 static NPY_INLINE int mpl_PyFile_DupClose(PyObject *file, FILE *handle, mpl_off_t orig_pos)
131 {
132 PyObject *exc_type = NULL, *exc_value = NULL, *exc_tb = NULL;
133 PyErr_Fetch(&exc_type, &exc_value, &exc_tb);
134
135 int fd;
136 PyObject *ret;
137 mpl_off_t position;
138
139 position = mpl_ftell(handle);
140
141 /* Close the FILE* handle */
142 fclose(handle);
143
144 /* Restore original file handle position, in order to not confuse
145 Python-side data structures. Note that this would fail if an exception
146 is currently set, which can happen as this function is called in cleanup
147 code, so we need to carefully fetch and restore the exception state. */
148 fd = PyObject_AsFileDescriptor(file);
149 if (fd == -1) {
150 goto fail;
151 }
152 if (mpl_lseek(fd, orig_pos, SEEK_SET) != -1) {
153 if (position == -1) {
154 PyErr_SetString(PyExc_IOError, "obtaining file position failed");
155 goto fail;
156 }
157
158 /* Seek Python-side handle to the FILE* handle position */
159 ret = PyObject_CallMethod(file, (char *)"seek", (char *)(MPL_OFF_T_PYFMT "i"), position, 0);
160 if (ret == NULL) {
161 goto fail;
162 }
163 Py_DECREF(ret);
164 }
165 PyErr_Restore(exc_type, exc_value, exc_tb);
166 return 0;
167 fail:
168 Py_XDECREF(exc_type);
169 Py_XDECREF(exc_value);
170 Py_XDECREF(exc_tb);
171 return -1;
172 }
173
mpl_PyFile_Check(PyObject * file)174 static NPY_INLINE int mpl_PyFile_Check(PyObject *file)
175 {
176 int fd;
177 fd = PyObject_AsFileDescriptor(file);
178 if (fd == -1) {
179 PyErr_Clear();
180 return 0;
181 }
182 return 1;
183 }
184
185 #else
186
187 static NPY_INLINE FILE *mpl_PyFile_Dup(PyObject *file, const char *mode, mpl_off_t *orig_pos)
188 {
189 return PyFile_AsFile(file);
190 }
191
192 static NPY_INLINE int mpl_PyFile_DupClose(PyObject *file, FILE *handle, mpl_off_t orig_pos)
193 {
194 // deliberately nothing
195 return 0;
196 }
197
198 static NPY_INLINE int mpl_PyFile_Check(PyObject *file)
199 {
200 return PyFile_Check(file);
201 }
202
203 #endif
204
mpl_PyFile_OpenFile(PyObject * filename,const char * mode)205 static NPY_INLINE PyObject *mpl_PyFile_OpenFile(PyObject *filename, const char *mode)
206 {
207 PyObject *open;
208 open = PyDict_GetItemString(PyEval_GetBuiltins(), "open");
209 if (open == NULL) {
210 return NULL;
211 }
212 return PyObject_CallFunction(open, (char *)"Os", filename, mode);
213 }
214
mpl_PyFile_CloseFile(PyObject * file)215 static NPY_INLINE int mpl_PyFile_CloseFile(PyObject *file)
216 {
217 PyObject *type, *value, *tb;
218 PyErr_Fetch(&type, &value, &tb);
219
220 PyObject *ret;
221
222 ret = PyObject_CallMethod(file, (char *)"close", NULL);
223 if (ret == NULL) {
224 goto fail;
225 }
226 Py_DECREF(ret);
227 PyErr_Restore(type, value, tb);
228 return 0;
229 fail:
230 Py_XDECREF(type);
231 Py_XDECREF(value);
232 Py_XDECREF(tb);
233 return -1;
234 }
235
236 #ifdef __cplusplus
237 }
238 #endif
239
240 #endif /* ifndef __FILE_COMPAT_H__ */
241