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