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