/home/mdboom/Work/builds/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 | #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 * |
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 *open, *stream; |
36 | |
37 | /* import _io in case we are being used to open io.py */ |
38 | open = _PyImport_GetModuleAttrString("_io", "open"); |
39 | if (open == NULL) Branch (39:9): [True: 0, False: 0]
|
40 | return NULL; |
41 | stream = PyObject_CallFunction(open, "isisssO", fd, mode, |
42 | buffering, encoding, errors, |
43 | newline, closefd ? Py_True : Py_False); Branch (43:44): [True: 0, False: 0]
|
44 | Py_DECREF(open); |
45 | if (stream == NULL) Branch (45:9): [True: 0, False: 0]
|
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 * |
53 | PyFile_GetLine(PyObject *f, int n) |
54 | { |
55 | PyObject *result; |
56 | |
57 | if (f == NULL) { Branch (57:9): [True: 0, False: 321k]
|
58 | PyErr_BadInternalCall(); |
59 | return NULL; |
60 | } |
61 | |
62 | if (n <= 0) { Branch (62:9): [True: 321k, False: 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 && !321k PyBytes_Check(result) && Branch (68:9): [True: 321k, False: 1]
Branch (68:27): [True: 321k, False: 0]
|
69 | !321k PyUnicode_Check321k (result)) { Branch (69:9): [True: 0, False: 321k]
|
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_Check321k (result)) { Branch (76:9): [True: 321k, False: 0]
Branch (76:18): [True: 321k, False: 1]
|
77 | const char *s = PyBytes_AS_STRING(result); |
78 | Py_ssize_t len = PyBytes_GET_SIZE(result); |
79 | if (len == 0) { Branch (79:13): [True: 0, False: 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') { Branch (85:18): [True: 0, False: 0]
|
86 | if (Py_REFCNT(result) == 1) Branch (86:17): [True: 0, False: 0]
|
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_Check321k (result)) { Branch (96:9): [True: 321k, False: 0]
Branch (96:18): [True: 321k, False: 1]
|
97 | Py_ssize_t len = PyUnicode_GET_LENGTH(result); |
98 | if (len == 0) { Branch (98:13): [True: 1, False: 321k]
|
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') { Branch (104:18): [True: 321k, False: 2]
|
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 |
117 | PyFile_WriteObject(PyObject *v, PyObject *f, int flags) |
118 | { |
119 | PyObject *writer, *value, *result; |
120 | |
121 | if (f == NULL) { Branch (121:9): [True: 0, False: 136k]
|
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) Branch (126:9): [True: 2, False: 136k]
|
127 | return -1; |
128 | if (flags & Py_PRINT_RAW) { Branch (128:9): [True: 135k, False: 1.61k]
|
129 | value = PyObject_Str(v); |
130 | } |
131 | else |
132 | value = PyObject_Repr(v); |
133 | if (value == NULL) { Branch (133:9): [True: 1, False: 136k]
|
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) Branch (140:9): [True: 1, False: 136k]
|
141 | return -1; |
142 | Py_DECREF(result); |
143 | return 0; |
144 | } |
145 | |
146 | int |
147 | PyFile_WriteString(const char *s, PyObject *f) |
148 | { |
149 | if (f == NULL) { Branch (149:9): [True: 0, False: 55.5k]
|
150 | /* Should be caused by a pre-existing error */ |
151 | if (!PyErr_Occurred()) Branch (151:13): [True: 0, False: 0]
|
152 | PyErr_SetString(PyExc_SystemError, |
153 | "null file for PyFile_WriteString"); |
154 | return -1; |
155 | } |
156 | else if (!PyErr_Occurred()) { Branch (156:14): [True: 55.5k, False: 0]
|
157 | PyObject *v = PyUnicode_FromString(s); |
158 | int err; |
159 | if (v == NULL) Branch (159:13): [True: 0, False: 55.5k]
|
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 |
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) { Branch (185:14): [True: 0, False: 635]
|
186 | return -1; |
187 | } |
188 | else if (meth != NULL) { Branch (188:14): [True: 627, False: 8]
|
189 | PyObject *fno = _PyObject_CallNoArgs(meth); |
190 | Py_DECREF(meth); |
191 | if (fno == NULL) Branch (191:13): [True: 0, False: 627]
|
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()10 ) Branch (211:9): [True: 10, False: 305k]
Branch (211:21): [True: 5, False: 5]
|
212 | return -1; |
213 | if (fd < 0) { Branch (213:9): [True: 5, False: 305k]
|
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 |
223 | _PyLong_FileDescriptor_Converter(PyObject *o, void *ptr) |
224 | { |
225 | int fd = PyObject_AsFileDescriptor(o); |
226 | if (fd == -1) { Branch (226:9): [True: 19, False: 219k]
|
227 | return 0; |
228 | } |
229 | *(int *)ptr = fd; |
230 | return 1; |
231 | } |
232 | |
233 | /* |
234 | ** Py_UniversalNewlineFgets is an fgets variation that understands |
235 | ** all of \r, \n and \r\n conventions. |
236 | ** The stream should be opened in binary mode. |
237 | ** The fobj parameter exists solely for legacy reasons and must be NULL. |
238 | ** Note that we need no error handling: fgets() treats error and eof |
239 | ** identically. |
240 | */ |
241 | char * |
242 | Py_UniversalNewlineFgets(char *buf, int n, FILE *stream, PyObject *fobj) |
243 | { |
244 | char *p = buf; |
245 | int c; |
246 | |
247 | if (fobj) { Branch (247:9): [True: 0, False: 906]
|
248 | errno = ENXIO; /* What can you do... */ |
249 | return NULL; |
250 | } |
251 | FLOCKFILE(stream); |
252 | while (--n > 0 && (c = GETC(stream)) != EOF ) { Branch (252:12): [True: 20.3k, False: 0]
Branch (252:23): [True: 20.3k, False: 16]
|
253 | if (c == '\r') { Branch (253:13): [True: 0, False: 20.3k]
|
254 | // A \r is translated into a \n, and we skip an adjacent \n, if any. |
255 | c = GETC(stream); |
256 | if (c != '\n') { Branch (256:17): [True: 0, False: 0]
|
257 | ungetc(c, stream); |
258 | c = '\n'; |
259 | } |
260 | } |
261 | *p++ = c; |
262 | if (c == '\n') { Branch (262:13): [True: 890, False: 19.4k]
|
263 | break; |
264 | } |
265 | } |
266 | FUNLOCKFILE(stream); |
267 | *p = '\0'; |
268 | if (p == buf) Branch (268:9): [True: 4, False: 902]
|
269 | return NULL; |
270 | return buf; |
271 | } |
272 | |
273 | /* **************************** std printer **************************** |
274 | * The stdprinter is used during the boot strapping phase as a preliminary |
275 | * file like object for sys.stderr. |
276 | */ |
277 | |
278 | typedef struct { |
279 | PyObject_HEAD |
280 | int fd; |
281 | } PyStdPrinter_Object; |
282 | |
283 | PyObject * |
284 | PyFile_NewStdPrinter(int fd) |
285 | { |
286 | PyStdPrinter_Object *self; |
287 | |
288 | if (fd != fileno(stdout) && fd != fileno(stderr)278 ) { Branch (288:9): [True: 278, False: 3]
Branch (288:33): [True: 0, False: 278]
|
289 | /* not enough infrastructure for PyErr_BadInternalCall() */ |
290 | return NULL; |
291 | } |
292 | |
293 | self = PyObject_New(PyStdPrinter_Object, |
294 | &PyStdPrinter_Type); |
295 | if (self != NULL) { Branch (295:9): [True: 281, False: 0]
|
296 | self->fd = fd; |
297 | } |
298 | return (PyObject*)self; |
299 | } |
300 | |
301 | static PyObject * |
302 | stdprinter_write(PyStdPrinter_Object *self, PyObject *args) |
303 | { |
304 | PyObject *unicode; |
305 | PyObject *bytes = NULL; |
306 | const char *str; |
307 | Py_ssize_t n; |
308 | int err; |
309 | |
310 | /* The function can clear the current exception */ |
311 | assert(!PyErr_Occurred()); |
312 | |
313 | if (self->fd < 0) { Branch (313:9): [True: 0, False: 209]
|
314 | /* fd might be invalid on Windows |
315 | * I can't raise an exception here. It may lead to an |
316 | * unlimited recursion in the case stderr is invalid. |
317 | */ |
318 | Py_RETURN_NONE; |
319 | } |
320 | |
321 | if (!PyArg_ParseTuple(args, "U", &unicode)) { Branch (321:9): [True: 0, False: 209]
|
322 | return NULL; |
323 | } |
324 | |
325 | /* Encode Unicode to UTF-8/backslashreplace */ |
326 | str = PyUnicode_AsUTF8AndSize(unicode, &n); |
327 | if (str == NULL) { Branch (327:9): [True: 1, False: 208]
|
328 | PyErr_Clear(); |
329 | bytes = _PyUnicode_AsUTF8String(unicode, "backslashreplace"); |
330 | if (bytes == NULL) Branch (330:13): [True: 0, False: 1]
|
331 | return NULL; |
332 | str = PyBytes_AS_STRING(bytes); |
333 | n = PyBytes_GET_SIZE(bytes); |
334 | } |
335 | |
336 | n = _Py_write(self->fd, str, n); |
337 | /* save errno, it can be modified indirectly by Py_XDECREF() */ |
338 | err = errno; |
339 | |
340 | Py_XDECREF(bytes); |
341 | |
342 | if (n == -1) { Branch (342:9): [True: 0, False: 209]
|
343 | if (err == EAGAIN) { Branch (343:13): [True: 0, False: 0]
|
344 | PyErr_Clear(); |
345 | Py_RETURN_NONE; |
346 | } |
347 | return NULL; |
348 | } |
349 | |
350 | return PyLong_FromSsize_t(n); |
351 | } |
352 | |
353 | static PyObject * |
354 | stdprinter_fileno(PyStdPrinter_Object *self, PyObject *Py_UNUSED(ignored)) |
355 | { |
356 | return PyLong_FromLong((long) self->fd); |
357 | } |
358 | |
359 | static PyObject * |
360 | stdprinter_repr(PyStdPrinter_Object *self) |
361 | { |
362 | return PyUnicode_FromFormat("<stdprinter(fd=%d) object at %p>", |
363 | self->fd, self); |
364 | } |
365 | |
366 | static PyObject * |
367 | stdprinter_noop(PyStdPrinter_Object *self, PyObject *Py_UNUSED(ignored)) |
368 | { |
369 | Py_RETURN_NONE; |
370 | } |
371 | |
372 | static PyObject * |
373 | stdprinter_isatty(PyStdPrinter_Object *self, PyObject *Py_UNUSED(ignored)) |
374 | { |
375 | long res; |
376 | if (self->fd < 0) { Branch (376:9): [True: 0, False: 1]
|
377 | Py_RETURN_FALSE; |
378 | } |
379 | |
380 | Py_BEGIN_ALLOW_THREADS |
381 | res = isatty(self->fd); |
382 | Py_END_ALLOW_THREADS |
383 | |
384 | return PyBool_FromLong(res); |
385 | } |
386 | |
387 | static PyMethodDef stdprinter_methods[] = { |
388 | {"close", (PyCFunction)stdprinter_noop, METH_NOARGS, ""}, |
389 | {"flush", (PyCFunction)stdprinter_noop, METH_NOARGS, ""}, |
390 | {"fileno", (PyCFunction)stdprinter_fileno, METH_NOARGS, ""}, |
391 | {"isatty", (PyCFunction)stdprinter_isatty, METH_NOARGS, ""}, |
392 | {"write", (PyCFunction)stdprinter_write, METH_VARARGS, ""}, |
393 | {NULL, NULL} /*sentinel */ |
394 | }; |
395 | |
396 | static PyObject * |
397 | get_closed(PyStdPrinter_Object *self, void *closure) |
398 | { |
399 | Py_RETURN_FALSE; |
400 | } |
401 | |
402 | static PyObject * |
403 | get_mode(PyStdPrinter_Object *self, void *closure) |
404 | { |
405 | return PyUnicode_FromString("w"); |
406 | } |
407 | |
408 | static PyObject * |
409 | get_encoding(PyStdPrinter_Object *self, void *closure) |
410 | { |
411 | Py_RETURN_NONE; |
412 | } |
413 | |
414 | static PyGetSetDef stdprinter_getsetlist[] = { |
415 | {"closed", (getter)get_closed, NULL, "True if the file is closed"}, |
416 | {"encoding", (getter)get_encoding, NULL, "Encoding of the file"}, |
417 | {"mode", (getter)get_mode, NULL, "String giving the file mode"}, |
418 | {0}, |
419 | }; |
420 | |
421 | PyTypeObject PyStdPrinter_Type = { |
422 | PyVarObject_HEAD_INIT(&PyType_Type, 0) |
423 | "stderrprinter", /* tp_name */ |
424 | sizeof(PyStdPrinter_Object), /* tp_basicsize */ |
425 | 0, /* tp_itemsize */ |
426 | /* methods */ |
427 | 0, /* tp_dealloc */ |
428 | 0, /* tp_vectorcall_offset */ |
429 | 0, /* tp_getattr */ |
430 | 0, /* tp_setattr */ |
431 | 0, /* tp_as_async */ |
432 | (reprfunc)stdprinter_repr, /* tp_repr */ |
433 | 0, /* tp_as_number */ |
434 | 0, /* tp_as_sequence */ |
435 | 0, /* tp_as_mapping */ |
436 | 0, /* tp_hash */ |
437 | 0, /* tp_call */ |
438 | 0, /* tp_str */ |
439 | PyObject_GenericGetAttr, /* tp_getattro */ |
440 | 0, /* tp_setattro */ |
441 | 0, /* tp_as_buffer */ |
442 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION, /* tp_flags */ |
443 | 0, /* tp_doc */ |
444 | 0, /* tp_traverse */ |
445 | 0, /* tp_clear */ |
446 | 0, /* tp_richcompare */ |
447 | 0, /* tp_weaklistoffset */ |
448 | 0, /* tp_iter */ |
449 | 0, /* tp_iternext */ |
450 | stdprinter_methods, /* tp_methods */ |
451 | 0, /* tp_members */ |
452 | stdprinter_getsetlist, /* tp_getset */ |
453 | 0, /* tp_base */ |
454 | 0, /* tp_dict */ |
455 | 0, /* tp_descr_get */ |
456 | 0, /* tp_descr_set */ |
457 | 0, /* tp_dictoffset */ |
458 | 0, /* tp_init */ |
459 | PyType_GenericAlloc, /* tp_alloc */ |
460 | 0, /* tp_new */ |
461 | PyObject_Del, /* tp_free */ |
462 | }; |
463 | |
464 | |
465 | /* ************************** open_code hook *************************** |
466 | * The open_code hook allows embedders to override the method used to |
467 | * open files that are going to be used by the runtime to execute code |
468 | */ |
469 | |
470 | int |
471 | PyFile_SetOpenCodeHook(Py_OpenCodeHookFunction hook, void *userData) { |
472 | if (Py_IsInitialized() && Branch (472:9): [True: 0, False: 2]
|
473 | PySys_Audit("setopencodehook", NULL) < 00 ) { Branch (473:9): [True: 0, False: 0]
|
474 | return -1; |
475 | } |
476 | |
477 | if (_PyRuntime.open_code_hook) { Branch (477:9): [True: 1, False: 1]
|
478 | if (Py_IsInitialized()) { Branch (478:13): [True: 0, False: 1]
|
479 | PyErr_SetString(PyExc_SystemError, |
480 | "failed to change existing open_code hook"); |
481 | } |
482 | return -1; |
483 | } |
484 | |
485 | _PyRuntime.open_code_hook = hook; |
486 | _PyRuntime.open_code_userdata = userData; |
487 | return 0; |
488 | } |
489 | |
490 | PyObject * |
491 | PyFile_OpenCodeObject(PyObject *path) |
492 | { |
493 | PyObject *f = NULL; |
494 | |
495 | if (!PyUnicode_Check(path)) { Branch (495:9): [True: 0, False: 8.59k]
|
496 | PyErr_Format(PyExc_TypeError, "'path' must be 'str', not '%.200s'", |
497 | Py_TYPE(path)->tp_name); |
498 | return NULL; |
499 | } |
500 | |
501 | Py_OpenCodeHookFunction hook = _PyRuntime.open_code_hook; |
502 | if (hook) { Branch (502:9): [True: 5, False: 8.59k]
|
503 | f = hook(path, _PyRuntime.open_code_userdata); |
504 | } else { |
505 | PyObject *open = _PyImport_GetModuleAttrString("_io", "open"); |
506 | if (open) { Branch (506:13): [True: 8.59k, False: 0]
|
507 | f = PyObject_CallFunction(open, "Os", path, "rb"); |
508 | Py_DECREF(open); |
509 | } |
510 | } |
511 | |
512 | return f; |
513 | } |
514 | |
515 | PyObject * |
516 | PyFile_OpenCode(const char *utf8path) |
517 | { |
518 | PyObject *pathobj = PyUnicode_FromString(utf8path); |
519 | PyObject *f; |
520 | if (!pathobj) { Branch (520:9): [True: 0, False: 1]
|
521 | return NULL; |
522 | } |
523 | f = PyFile_OpenCodeObject(pathobj); |
524 | Py_DECREF(pathobj); |
525 | return f; |
526 | } |
527 | |
528 | |
529 | #ifdef __cplusplus |
530 | } |
531 | #endif |