Coverage Report

Created: 2025-07-04 06:49

/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
}