xref: /third_party/python/Objects/fileobject.c (revision 7db96d56)
1/* File object implementation (what's left of it -- see io.py) */
2
3#define PY_SSIZE_T_CLEAN
4#include "Python.h"
5#include "pycore_call.h"          // _PyObject_CallNoArgs()
6#include "pycore_runtime.h"       // _PyRuntime
7
8#if defined(HAVE_GETC_UNLOCKED) && !defined(_Py_MEMORY_SANITIZER)
9/* clang MemorySanitizer doesn't yet understand getc_unlocked. */
10#define GETC(f) getc_unlocked(f)
11#define FLOCKFILE(f) flockfile(f)
12#define FUNLOCKFILE(f) funlockfile(f)
13#else
14#define GETC(f) getc(f)
15#define FLOCKFILE(f)
16#define FUNLOCKFILE(f)
17#endif
18
19/* Newline flags */
20#define NEWLINE_UNKNOWN 0       /* No newline seen, yet */
21#define NEWLINE_CR 1            /* \r newline seen */
22#define NEWLINE_LF 2            /* \n newline seen */
23#define NEWLINE_CRLF 4          /* \r\n newline seen */
24
25#ifdef __cplusplus
26extern "C" {
27#endif
28
29/* External C interface */
30
31PyObject *
32PyFile_FromFd(int fd, const char *name, const char *mode, int buffering, const char *encoding,
33              const char *errors, const char *newline, int closefd)
34{
35    PyObject *io, *stream;
36
37    /* import _io in case we are being used to open io.py */
38    io = PyImport_ImportModule("_io");
39    if (io == NULL)
40        return NULL;
41    stream = _PyObject_CallMethod(io, &_Py_ID(open), "isisssO", fd, mode,
42                                  buffering, encoding, errors,
43                                  newline, closefd ? Py_True : Py_False);
44    Py_DECREF(io);
45    if (stream == NULL)
46        return NULL;
47    /* ignore name attribute because the name attribute of _BufferedIOMixin
48       and TextIOWrapper is read only */
49    return stream;
50}
51
52PyObject *
53PyFile_GetLine(PyObject *f, int n)
54{
55    PyObject *result;
56
57    if (f == NULL) {
58        PyErr_BadInternalCall();
59        return NULL;
60    }
61
62    if (n <= 0) {
63        result = PyObject_CallMethodNoArgs(f, &_Py_ID(readline));
64    }
65    else {
66        result = _PyObject_CallMethod(f, &_Py_ID(readline), "i", n);
67    }
68    if (result != NULL && !PyBytes_Check(result) &&
69        !PyUnicode_Check(result)) {
70        Py_DECREF(result);
71        result = NULL;
72        PyErr_SetString(PyExc_TypeError,
73                   "object.readline() returned non-string");
74    }
75
76    if (n < 0 && result != NULL && PyBytes_Check(result)) {
77        const char *s = PyBytes_AS_STRING(result);
78        Py_ssize_t len = PyBytes_GET_SIZE(result);
79        if (len == 0) {
80            Py_DECREF(result);
81            result = NULL;
82            PyErr_SetString(PyExc_EOFError,
83                            "EOF when reading a line");
84        }
85        else if (s[len-1] == '\n') {
86            if (Py_REFCNT(result) == 1)
87                _PyBytes_Resize(&result, len-1);
88            else {
89                PyObject *v;
90                v = PyBytes_FromStringAndSize(s, len-1);
91                Py_DECREF(result);
92                result = v;
93            }
94        }
95    }
96    if (n < 0 && result != NULL && PyUnicode_Check(result)) {
97        Py_ssize_t len = PyUnicode_GET_LENGTH(result);
98        if (len == 0) {
99            Py_DECREF(result);
100            result = NULL;
101            PyErr_SetString(PyExc_EOFError,
102                            "EOF when reading a line");
103        }
104        else if (PyUnicode_READ_CHAR(result, len-1) == '\n') {
105            PyObject *v;
106            v = PyUnicode_Substring(result, 0, len-1);
107            Py_DECREF(result);
108            result = v;
109        }
110    }
111    return result;
112}
113
114/* Interfaces to write objects/strings to file-like objects */
115
116int
117PyFile_WriteObject(PyObject *v, PyObject *f, int flags)
118{
119    PyObject *writer, *value, *result;
120
121    if (f == NULL) {
122        PyErr_SetString(PyExc_TypeError, "writeobject with NULL file");
123        return -1;
124    }
125    writer = PyObject_GetAttr(f, &_Py_ID(write));
126    if (writer == NULL)
127        return -1;
128    if (flags & Py_PRINT_RAW) {
129        value = PyObject_Str(v);
130    }
131    else
132        value = PyObject_Repr(v);
133    if (value == NULL) {
134        Py_DECREF(writer);
135        return -1;
136    }
137    result = PyObject_CallOneArg(writer, value);
138    Py_DECREF(value);
139    Py_DECREF(writer);
140    if (result == NULL)
141        return -1;
142    Py_DECREF(result);
143    return 0;
144}
145
146int
147PyFile_WriteString(const char *s, PyObject *f)
148{
149    if (f == NULL) {
150        /* Should be caused by a pre-existing error */
151        if (!PyErr_Occurred())
152            PyErr_SetString(PyExc_SystemError,
153                            "null file for PyFile_WriteString");
154        return -1;
155    }
156    else if (!PyErr_Occurred()) {
157        PyObject *v = PyUnicode_FromString(s);
158        int err;
159        if (v == NULL)
160            return -1;
161        err = PyFile_WriteObject(v, f, Py_PRINT_RAW);
162        Py_DECREF(v);
163        return err;
164    }
165    else
166        return -1;
167}
168
169/* Try to get a file-descriptor from a Python object.  If the object
170   is an integer, its value is returned.  If not, the
171   object's fileno() method is called if it exists; the method must return
172   an integer, which is returned as the file descriptor value.
173   -1 is returned on failure.
174*/
175
176int
177PyObject_AsFileDescriptor(PyObject *o)
178{
179    int fd;
180    PyObject *meth;
181
182    if (PyLong_Check(o)) {
183        fd = _PyLong_AsInt(o);
184    }
185    else if (_PyObject_LookupAttr(o, &_Py_ID(fileno), &meth) < 0) {
186        return -1;
187    }
188    else if (meth != NULL) {
189        PyObject *fno = _PyObject_CallNoArgs(meth);
190        Py_DECREF(meth);
191        if (fno == NULL)
192            return -1;
193
194        if (PyLong_Check(fno)) {
195            fd = _PyLong_AsInt(fno);
196            Py_DECREF(fno);
197        }
198        else {
199            PyErr_SetString(PyExc_TypeError,
200                            "fileno() returned a non-integer");
201            Py_DECREF(fno);
202            return -1;
203        }
204    }
205    else {
206        PyErr_SetString(PyExc_TypeError,
207                        "argument must be an int, or have a fileno() method.");
208        return -1;
209    }
210
211    if (fd == -1 && PyErr_Occurred())
212        return -1;
213    if (fd < 0) {
214        PyErr_Format(PyExc_ValueError,
215                     "file descriptor cannot be a negative integer (%i)",
216                     fd);
217        return -1;
218    }
219    return fd;
220}
221
222int
223_PyLong_FileDescriptor_Converter(PyObject *o, void *ptr)
224{
225    int fd = PyObject_AsFileDescriptor(o);
226    if (fd == -1) {
227        return 0;
228    }
229    *(int *)ptr = fd;
230    return 1;
231}
232
233char *
234_Py_UniversalNewlineFgetsWithSize(char *buf, int n, FILE *stream, PyObject *fobj, size_t* size)
235{
236    char *p = buf;
237    int c;
238
239    if (fobj) {
240        errno = ENXIO;          /* What can you do... */
241        return NULL;
242    }
243    FLOCKFILE(stream);
244    while (--n > 0 && (c = GETC(stream)) != EOF ) {
245        if (c == '\r') {
246            // A \r is translated into a \n, and we skip an adjacent \n, if any.
247            c = GETC(stream);
248            if (c != '\n') {
249                ungetc(c, stream);
250                c = '\n';
251            }
252        }
253        *p++ = c;
254        if (c == '\n') {
255            break;
256        }
257    }
258    FUNLOCKFILE(stream);
259    *p = '\0';
260    if (p == buf) {
261        return NULL;
262    }
263    *size = p - buf;
264    return buf;
265}
266
267/*
268** Py_UniversalNewlineFgets is an fgets variation that understands
269** all of \r, \n and \r\n conventions.
270** The stream should be opened in binary mode.
271** The fobj parameter exists solely for legacy reasons and must be NULL.
272** Note that we need no error handling: fgets() treats error and eof
273** identically.
274*/
275
276char *
277Py_UniversalNewlineFgets(char *buf, int n, FILE *stream, PyObject *fobj) {
278    size_t size;
279    return _Py_UniversalNewlineFgetsWithSize(buf, n, stream, fobj, &size);
280}
281
282/* **************************** std printer ****************************
283 * The stdprinter is used during the boot strapping phase as a preliminary
284 * file like object for sys.stderr.
285 */
286
287typedef struct {
288    PyObject_HEAD
289    int fd;
290} PyStdPrinter_Object;
291
292PyObject *
293PyFile_NewStdPrinter(int fd)
294{
295    PyStdPrinter_Object *self;
296
297    if (fd != fileno(stdout) && fd != fileno(stderr)) {
298        /* not enough infrastructure for PyErr_BadInternalCall() */
299        return NULL;
300    }
301
302    self = PyObject_New(PyStdPrinter_Object,
303                        &PyStdPrinter_Type);
304    if (self != NULL) {
305        self->fd = fd;
306    }
307    return (PyObject*)self;
308}
309
310static PyObject *
311stdprinter_write(PyStdPrinter_Object *self, PyObject *args)
312{
313    PyObject *unicode;
314    PyObject *bytes = NULL;
315    const char *str;
316    Py_ssize_t n;
317    int err;
318
319    /* The function can clear the current exception */
320    assert(!PyErr_Occurred());
321
322    if (self->fd < 0) {
323        /* fd might be invalid on Windows
324         * I can't raise an exception here. It may lead to an
325         * unlimited recursion in the case stderr is invalid.
326         */
327        Py_RETURN_NONE;
328    }
329
330    if (!PyArg_ParseTuple(args, "U", &unicode)) {
331        return NULL;
332    }
333
334    /* Encode Unicode to UTF-8/backslashreplace */
335    str = PyUnicode_AsUTF8AndSize(unicode, &n);
336    if (str == NULL) {
337        PyErr_Clear();
338        bytes = _PyUnicode_AsUTF8String(unicode, "backslashreplace");
339        if (bytes == NULL)
340            return NULL;
341        str = PyBytes_AS_STRING(bytes);
342        n = PyBytes_GET_SIZE(bytes);
343    }
344
345    n = _Py_write(self->fd, str, n);
346    /* save errno, it can be modified indirectly by Py_XDECREF() */
347    err = errno;
348
349    Py_XDECREF(bytes);
350
351    if (n == -1) {
352        if (err == EAGAIN) {
353            PyErr_Clear();
354            Py_RETURN_NONE;
355        }
356        return NULL;
357    }
358
359    return PyLong_FromSsize_t(n);
360}
361
362static PyObject *
363stdprinter_fileno(PyStdPrinter_Object *self, PyObject *Py_UNUSED(ignored))
364{
365    return PyLong_FromLong((long) self->fd);
366}
367
368static PyObject *
369stdprinter_repr(PyStdPrinter_Object *self)
370{
371    return PyUnicode_FromFormat("<stdprinter(fd=%d) object at %p>",
372                                self->fd, self);
373}
374
375static PyObject *
376stdprinter_noop(PyStdPrinter_Object *self, PyObject *Py_UNUSED(ignored))
377{
378    Py_RETURN_NONE;
379}
380
381static PyObject *
382stdprinter_isatty(PyStdPrinter_Object *self, PyObject *Py_UNUSED(ignored))
383{
384    long res;
385    if (self->fd < 0) {
386        Py_RETURN_FALSE;
387    }
388
389    Py_BEGIN_ALLOW_THREADS
390    res = isatty(self->fd);
391    Py_END_ALLOW_THREADS
392
393    return PyBool_FromLong(res);
394}
395
396static PyMethodDef stdprinter_methods[] = {
397    {"close",           (PyCFunction)stdprinter_noop, METH_NOARGS, ""},
398    {"flush",           (PyCFunction)stdprinter_noop, METH_NOARGS, ""},
399    {"fileno",          (PyCFunction)stdprinter_fileno, METH_NOARGS, ""},
400    {"isatty",          (PyCFunction)stdprinter_isatty, METH_NOARGS, ""},
401    {"write",           (PyCFunction)stdprinter_write, METH_VARARGS, ""},
402    {NULL,              NULL}  /*sentinel */
403};
404
405static PyObject *
406get_closed(PyStdPrinter_Object *self, void *closure)
407{
408    Py_RETURN_FALSE;
409}
410
411static PyObject *
412get_mode(PyStdPrinter_Object *self, void *closure)
413{
414    return PyUnicode_FromString("w");
415}
416
417static PyObject *
418get_encoding(PyStdPrinter_Object *self, void *closure)
419{
420    Py_RETURN_NONE;
421}
422
423static PyGetSetDef stdprinter_getsetlist[] = {
424    {"closed", (getter)get_closed, NULL, "True if the file is closed"},
425    {"encoding", (getter)get_encoding, NULL, "Encoding of the file"},
426    {"mode", (getter)get_mode, NULL, "String giving the file mode"},
427    {0},
428};
429
430PyTypeObject PyStdPrinter_Type = {
431    PyVarObject_HEAD_INIT(&PyType_Type, 0)
432    "stderrprinter",                            /* tp_name */
433    sizeof(PyStdPrinter_Object),                /* tp_basicsize */
434    0,                                          /* tp_itemsize */
435    /* methods */
436    0,                                          /* tp_dealloc */
437    0,                                          /* tp_vectorcall_offset */
438    0,                                          /* tp_getattr */
439    0,                                          /* tp_setattr */
440    0,                                          /* tp_as_async */
441    (reprfunc)stdprinter_repr,                  /* tp_repr */
442    0,                                          /* tp_as_number */
443    0,                                          /* tp_as_sequence */
444    0,                                          /* tp_as_mapping */
445    0,                                          /* tp_hash */
446    0,                                          /* tp_call */
447    0,                                          /* tp_str */
448    PyObject_GenericGetAttr,                    /* tp_getattro */
449    0,                                          /* tp_setattro */
450    0,                                          /* tp_as_buffer */
451    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION, /* tp_flags */
452    0,                                          /* tp_doc */
453    0,                                          /* tp_traverse */
454    0,                                          /* tp_clear */
455    0,                                          /* tp_richcompare */
456    0,                                          /* tp_weaklistoffset */
457    0,                                          /* tp_iter */
458    0,                                          /* tp_iternext */
459    stdprinter_methods,                         /* tp_methods */
460    0,                                          /* tp_members */
461    stdprinter_getsetlist,                      /* tp_getset */
462    0,                                          /* tp_base */
463    0,                                          /* tp_dict */
464    0,                                          /* tp_descr_get */
465    0,                                          /* tp_descr_set */
466    0,                                          /* tp_dictoffset */
467    0,                                          /* tp_init */
468    PyType_GenericAlloc,                        /* tp_alloc */
469    0,                                          /* tp_new */
470    PyObject_Del,                               /* tp_free */
471};
472
473
474/* ************************** open_code hook ***************************
475 * The open_code hook allows embedders to override the method used to
476 * open files that are going to be used by the runtime to execute code
477 */
478
479int
480PyFile_SetOpenCodeHook(Py_OpenCodeHookFunction hook, void *userData) {
481    if (Py_IsInitialized() &&
482        PySys_Audit("setopencodehook", NULL) < 0) {
483        return -1;
484    }
485
486    if (_PyRuntime.open_code_hook) {
487        if (Py_IsInitialized()) {
488            PyErr_SetString(PyExc_SystemError,
489                "failed to change existing open_code hook");
490        }
491        return -1;
492    }
493
494    _PyRuntime.open_code_hook = hook;
495    _PyRuntime.open_code_userdata = userData;
496    return 0;
497}
498
499PyObject *
500PyFile_OpenCodeObject(PyObject *path)
501{
502    PyObject *iomod, *f = NULL;
503
504    if (!PyUnicode_Check(path)) {
505        PyErr_Format(PyExc_TypeError, "'path' must be 'str', not '%.200s'",
506                     Py_TYPE(path)->tp_name);
507        return NULL;
508    }
509
510    Py_OpenCodeHookFunction hook = _PyRuntime.open_code_hook;
511    if (hook) {
512        f = hook(path, _PyRuntime.open_code_userdata);
513    } else {
514        iomod = PyImport_ImportModule("_io");
515        if (iomod) {
516            f = _PyObject_CallMethod(iomod, &_Py_ID(open), "Os", path, "rb");
517            Py_DECREF(iomod);
518        }
519    }
520
521    return f;
522}
523
524PyObject *
525PyFile_OpenCode(const char *utf8path)
526{
527    PyObject *pathobj = PyUnicode_FromString(utf8path);
528    PyObject *f;
529    if (!pathobj) {
530        return NULL;
531    }
532    f = PyFile_OpenCodeObject(pathobj);
533    Py_DECREF(pathobj);
534    return f;
535}
536
537
538#ifdef __cplusplus
539}
540#endif
541