optflags: set _FORTIFY_SOURCE for preproc
[platform/upstream/rpm.git] / python / rpmfd-py.c
1
2 #include "rpmsystem-py.h"
3 #include <rpm/rpmstring.h>
4 #include "header-py.h"  /* XXX for utf8FromPyObject() only */
5 #include "rpmfd-py.h"
6
7 struct rpmfdObject_s {
8     PyObject_HEAD
9     PyObject *md_dict;
10     FD_t fd;
11 };
12
13 FD_t rpmfdGetFd(rpmfdObject *fdo)
14 {
15     return fdo->fd;
16 }
17
18 int rpmfdFromPyObject(PyObject *obj, rpmfdObject **fdop)
19 {
20     rpmfdObject *fdo = NULL;
21
22     if (rpmfdObject_Check(obj)) {
23         Py_INCREF(obj);
24         fdo = (rpmfdObject *) obj;
25     } else {
26         fdo = (rpmfdObject *) PyObject_CallFunctionObjArgs((PyObject *)&rpmfd_Type,
27                                                            obj, NULL);
28     }
29     if (fdo == NULL) return 0;
30
31     if (Ferror(fdo->fd)) {
32         PyErr_SetString(PyExc_IOError, Fstrerror(fdo->fd));
33         Py_DECREF(fdo);
34         return 0;
35     }
36     *fdop = fdo;
37     return 1;
38 }
39
40 static PyObject *err_closed(void)
41 {
42     PyErr_SetString(PyExc_ValueError, "I/O operation on closed file");
43     return NULL;
44 }
45
46 static FD_t openPath(const char *path, const char *mode, const char *flags)
47 {
48     FD_t fd;
49     char *m = rstrscat(NULL, mode, ".", flags, NULL);
50     Py_BEGIN_ALLOW_THREADS
51     fd = Fopen(path, m);
52     Py_END_ALLOW_THREADS;
53     free(m);
54     return fd;
55 }
56
57 static int rpmfd_init(rpmfdObject *s, PyObject *args, PyObject *kwds)
58 {
59     char *kwlist[] = { "obj", "mode", "flags", NULL };
60     const char *mode = "r";
61     const char *flags = "ufdio";
62     PyObject *fo = NULL;
63     FD_t fd = NULL;
64     int fdno;
65
66     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|ss", kwlist, 
67                                      &fo, &mode, &flags))
68         return -1;
69
70     if (PyBytes_Check(fo)) {
71         fd = openPath(PyBytes_AsString(fo), mode, flags);
72     } else if (PyUnicode_Check(fo)) {
73         PyObject *enc = NULL;
74         int rc;
75 #if PY_MAJOR_VERSION >= 3
76         rc = PyUnicode_FSConverter(fo, &enc);
77 #else
78         rc = utf8FromPyObject(fo, &enc);
79 #endif
80         if (rc) {
81             fd = openPath(PyBytes_AsString(enc), mode, flags);
82             Py_DECREF(enc);
83         }
84     } else if ((fdno = PyObject_AsFileDescriptor(fo)) >= 0) {
85         fd = fdDup(fdno);
86     } else {
87         PyErr_SetString(PyExc_TypeError, "path or file object expected");
88     }
89
90     if (fd != NULL) {
91         /* TODO: remember our filename, mode & flags */
92         Fclose(s->fd); /* in case __init__ was called again */
93         s->fd = fd;
94     } else {
95         PyErr_SetString(PyExc_IOError, Fstrerror(fd));
96     }
97
98     return (fd == NULL) ? -1 : 0;
99 }
100
101 static PyObject *do_close(rpmfdObject *s)
102 {
103     /* mimic python fileobject: close on closed file is not an error */
104     if (s->fd) {
105         Py_BEGIN_ALLOW_THREADS
106         Fclose(s->fd);
107         Py_END_ALLOW_THREADS
108         s->fd = NULL;
109     }
110     Py_RETURN_NONE;
111 }
112
113 static PyObject *rpmfd_close(rpmfdObject *s)
114 {
115     return do_close(s);
116 }
117
118 static void rpmfd_dealloc(rpmfdObject *s)
119 {
120     PyObject *res = do_close(s);
121     Py_XDECREF(res);
122     Py_TYPE(s)->tp_free((PyObject *)s);
123 }
124
125 static PyObject *rpmfd_fileno(rpmfdObject *s)
126 {
127     int fno;
128     if (s->fd == NULL) return err_closed();
129
130     Py_BEGIN_ALLOW_THREADS
131     fno = Fileno(s->fd);
132     Py_END_ALLOW_THREADS
133
134     if (Ferror(s->fd)) {
135         PyErr_SetString(PyExc_IOError, Fstrerror(s->fd));
136         return NULL;
137     }
138     return Py_BuildValue("i", fno);
139 }
140
141 static PyObject *rpmfd_flush(rpmfdObject *s)
142 {
143     int rc;
144
145     if (s->fd == NULL) return err_closed();
146
147     Py_BEGIN_ALLOW_THREADS
148     rc = Fflush(s->fd);
149     Py_END_ALLOW_THREADS
150
151     if (rc || Ferror(s->fd)) {
152         PyErr_SetString(PyExc_IOError, Fstrerror(s->fd));
153         return NULL;
154     }
155     Py_RETURN_NONE;
156 }
157
158 static PyObject *rpmfd_isatty(rpmfdObject *s)
159 {
160     int fileno;
161     if (s->fd == NULL) return err_closed();
162
163     Py_BEGIN_ALLOW_THREADS
164     fileno = Fileno(s->fd);
165     Py_END_ALLOW_THREADS
166
167     if (Ferror(s->fd)) {
168         PyErr_SetString(PyExc_IOError, Fstrerror(s->fd));
169         return NULL;
170     }
171     return PyBool_FromLong(isatty(fileno));
172 }
173
174 static PyObject *rpmfd_seek(rpmfdObject *s, PyObject *args, PyObject *kwds)
175 {
176     char *kwlist[] = { "offset", "whence", NULL };
177     off_t offset;
178     int whence = SEEK_SET;
179     int rc = 0;
180
181     if (!PyArg_ParseTupleAndKeywords(args, kwds, "L|i", kwlist, 
182                                      &offset, &whence)) 
183         return NULL;
184
185     if (s->fd == NULL) return err_closed();
186
187     Py_BEGIN_ALLOW_THREADS
188     rc = Fseek(s->fd, offset, whence);
189     Py_END_ALLOW_THREADS
190     if (rc < 0 || Ferror(s->fd)) {
191         PyErr_SetString(PyExc_IOError, Fstrerror(s->fd));
192         return NULL;
193     }
194     Py_RETURN_NONE;
195 }
196
197 static PyObject *rpmfd_tell(rpmfdObject *s)
198 {
199     off_t offset;
200     Py_BEGIN_ALLOW_THREADS
201     offset = Ftell(s->fd);
202     Py_END_ALLOW_THREADS
203     return Py_BuildValue("L", offset);
204     
205 }
206
207 static PyObject *rpmfd_read(rpmfdObject *s, PyObject *args, PyObject *kwds)
208 {
209     char *kwlist[] = { "size", NULL };
210     char buf[BUFSIZ];
211     ssize_t chunksize = sizeof(buf);
212     ssize_t left = -1;
213     ssize_t nb = 0;
214     PyObject *res = NULL;
215     
216     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|l", kwlist, &left))
217         return NULL;
218
219     if (s->fd == NULL) return err_closed();
220
221     /* ConcatAndDel() doesn't work on NULL string, meh */
222     res = PyBytes_FromStringAndSize(NULL, 0);
223     do {
224         if (left >= 0 && left < chunksize)
225             chunksize = left;
226
227         Py_BEGIN_ALLOW_THREADS 
228         nb = Fread(buf, 1, chunksize, s->fd);
229         Py_END_ALLOW_THREADS 
230
231         if (nb > 0) {
232             PyObject *tmp = PyBytes_FromStringAndSize(buf, nb);
233             PyString_ConcatAndDel(&res, tmp);
234             left -= nb;
235         }
236     } while (nb > 0);
237
238     if (Ferror(s->fd)) {
239         PyErr_SetString(PyExc_IOError, Fstrerror(s->fd));
240         Py_XDECREF(res);
241         return NULL;
242     } else {
243         return res;
244     }
245 }
246
247 static PyObject *rpmfd_write(rpmfdObject *s, PyObject *args, PyObject *kwds)
248 {
249
250     const char *buf = NULL;
251     ssize_t size = 0;
252     char *kwlist[] = { "buffer", NULL };
253     ssize_t rc = 0;
254
255     if (!PyArg_ParseTupleAndKeywords(args, kwds, "s#", kwlist, &buf, &size)) {
256         return NULL;
257     }
258
259     if (s->fd == NULL) return err_closed();
260
261     Py_BEGIN_ALLOW_THREADS 
262     rc = Fwrite(buf, 1, size, s->fd);
263     Py_END_ALLOW_THREADS 
264
265     if (Ferror(s->fd)) {
266         PyErr_SetString(PyExc_IOError, Fstrerror(s->fd));
267         return NULL;
268     }
269     return Py_BuildValue("n", rc);
270 }
271
272 static char rpmfd_doc[] = "";
273
274 static struct PyMethodDef rpmfd_methods[] = {
275     { "close",  (PyCFunction) rpmfd_close,      METH_NOARGS,
276         NULL },
277     { "fileno", (PyCFunction) rpmfd_fileno,     METH_NOARGS,
278         NULL },
279     { "flush",  (PyCFunction) rpmfd_flush,      METH_NOARGS,
280         NULL },
281     { "isatty", (PyCFunction) rpmfd_isatty,     METH_NOARGS,
282         NULL },
283     { "read",   (PyCFunction) rpmfd_read,       METH_VARARGS|METH_KEYWORDS,
284         NULL },
285     { "seek",   (PyCFunction) rpmfd_seek,       METH_VARARGS|METH_KEYWORDS,
286         NULL },
287     { "tell",   (PyCFunction) rpmfd_tell,       METH_NOARGS,
288         NULL },
289     { "write",  (PyCFunction) rpmfd_write,      METH_VARARGS|METH_KEYWORDS,
290         NULL },
291     { NULL, NULL }
292 };
293
294 static PyObject *rpmfd_get_closed(rpmfdObject *s)
295 {
296     return PyBool_FromLong((s->fd == NULL));
297 }
298
299 static PyObject *rpmfd_get_name(rpmfdObject *s)
300 {
301     /* XXX: rpm returns non-paths with [mumble], python files use <mumble> */
302     return Py_BuildValue("s", Fdescr(s->fd));
303 }
304
305 static PyGetSetDef rpmfd_getseters[] = {
306     { "closed", (getter)rpmfd_get_closed, NULL, NULL },
307     { "name", (getter)rpmfd_get_name, NULL, NULL },
308     { NULL },
309 };
310
311 PyTypeObject rpmfd_Type = {
312         PyVarObject_HEAD_INIT(&PyType_Type, 0)
313         "rpm.fd",                       /* tp_name */
314         sizeof(rpmfdObject),            /* tp_size */
315         0,                              /* tp_itemsize */
316         /* methods */
317         (destructor) rpmfd_dealloc,     /* tp_dealloc */
318         0,                              /* tp_print */
319         (getattrfunc)0,                 /* tp_getattr */
320         (setattrfunc)0,                 /* tp_setattr */
321         0,                              /* tp_compare */
322         (reprfunc)0,                    /* tp_repr */
323         0,                              /* tp_as_number */
324         0,                              /* tp_as_sequence */
325         0,                              /* tp_as_mapping */
326         (hashfunc)0,                    /* tp_hash */
327         (ternaryfunc)0,                 /* tp_call */
328         (reprfunc)0,                    /* tp_str */
329         PyObject_GenericGetAttr,        /* tp_getattro */
330         PyObject_GenericSetAttr,        /* tp_setattro */
331         0,                              /* tp_as_buffer */
332         Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */
333         rpmfd_doc,                      /* tp_doc */
334         0,                              /* tp_traverse */
335         0,                              /* tp_clear */
336         0,                              /* tp_richcompare */
337         0,                              /* tp_weaklistoffset */
338         0,                              /* tp_iter */
339         0,                              /* tp_iternext */
340         rpmfd_methods,                  /* tp_methods */
341         0,                              /* tp_members */
342         rpmfd_getseters,                /* tp_getset */
343         0,                              /* tp_base */
344         0,                              /* tp_dict */
345         0,                              /* tp_descr_get */
346         0,                              /* tp_descr_set */
347         0,                              /* tp_dictoffset */
348         (initproc)rpmfd_init,           /* tp_init */
349         (allocfunc)0,                   /* tp_alloc */
350         PyType_GenericNew,              /* tp_new */
351         (freefunc)0,                    /* tp_free */
352         0,                              /* tp_is_gc */
353 };