Coverage Report

Created: 2025-07-11 06:59

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