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
26 extern "C" {
27 #endif
28 
29 /* External C interface */
30 
31 PyObject *
PyFile_FromFd(int fd,const char * name,const char * mode,int buffering,const char * encoding,const char * errors,const char * newline,int closefd)32 PyFile_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 
52 PyObject *
PyFile_GetLine(PyObject * f,int n)53 PyFile_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 
116 int
PyFile_WriteObject(PyObject * v,PyObject * f,int flags)117 PyFile_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 
146 int
PyFile_WriteString(const char * s,PyObject * f)147 PyFile_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 
176 int
PyObject_AsFileDescriptor(PyObject * o)177 PyObject_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 
222 int
_PyLong_FileDescriptor_Converter(PyObject * o,void * ptr)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 
233 char *
_Py_UniversalNewlineFgetsWithSize(char * buf,int n,FILE * stream,PyObject * fobj,size_t * size)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 
276 char *
Py_UniversalNewlineFgets(char * buf,int n,FILE * stream,PyObject * fobj)277 Py_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 
287 typedef struct {
288     PyObject_HEAD
289     int fd;
290 } PyStdPrinter_Object;
291 
292 PyObject *
PyFile_NewStdPrinter(int fd)293 PyFile_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 
310 static PyObject *
stdprinter_write(PyStdPrinter_Object * self,PyObject * args)311 stdprinter_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 
362 static PyObject *
stdprinter_fileno(PyStdPrinter_Object * self,PyObject * Py_UNUSED (ignored))363 stdprinter_fileno(PyStdPrinter_Object *self, PyObject *Py_UNUSED(ignored))
364 {
365     return PyLong_FromLong((long) self->fd);
366 }
367 
368 static PyObject *
stdprinter_repr(PyStdPrinter_Object * self)369 stdprinter_repr(PyStdPrinter_Object *self)
370 {
371     return PyUnicode_FromFormat("<stdprinter(fd=%d) object at %p>",
372                                 self->fd, self);
373 }
374 
375 static PyObject *
stdprinter_noop(PyStdPrinter_Object * self,PyObject * Py_UNUSED (ignored))376 stdprinter_noop(PyStdPrinter_Object *self, PyObject *Py_UNUSED(ignored))
377 {
378     Py_RETURN_NONE;
379 }
380 
381 static PyObject *
stdprinter_isatty(PyStdPrinter_Object * self,PyObject * Py_UNUSED (ignored))382 stdprinter_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 
396 static 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 
405 static PyObject *
get_closed(PyStdPrinter_Object * self,void * closure)406 get_closed(PyStdPrinter_Object *self, void *closure)
407 {
408     Py_RETURN_FALSE;
409 }
410 
411 static PyObject *
get_mode(PyStdPrinter_Object * self,void * closure)412 get_mode(PyStdPrinter_Object *self, void *closure)
413 {
414     return PyUnicode_FromString("w");
415 }
416 
417 static PyObject *
get_encoding(PyStdPrinter_Object * self,void * closure)418 get_encoding(PyStdPrinter_Object *self, void *closure)
419 {
420     Py_RETURN_NONE;
421 }
422 
423 static 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 
430 PyTypeObject 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 
479 int
PyFile_SetOpenCodeHook(Py_OpenCodeHookFunction hook,void * userData)480 PyFile_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 
499 PyObject *
PyFile_OpenCodeObject(PyObject * path)500 PyFile_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 
524 PyObject *
PyFile_OpenCode(const char * utf8path)525 PyFile_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