Coverage Report

Created: 2022-07-08 09:39

/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