Home | History | Annotate | Download | only in _io
      1 /* Author: Daniel Stutzbach */
      2 
      3 #define PY_SSIZE_T_CLEAN
      4 #include "Python.h"
      5 #ifdef HAVE_SYS_TYPES_H
      6 #include <sys/types.h>
      7 #endif
      8 #ifdef HAVE_SYS_STAT_H
      9 #include <sys/stat.h>
     10 #endif
     11 #ifdef HAVE_FCNTL_H
     12 #include <fcntl.h>
     13 #endif
     14 #include <stddef.h> /* For offsetof */
     15 #include "_iomodule.h"
     16 
     17 /*
     18  * Known likely problems:
     19  *
     20  * - Files larger then 2**32-1
     21  * - Files with unicode filenames
     22  * - Passing numbers greater than 2**32-1 when an integer is expected
     23  * - Making it work on Windows and other oddball platforms
     24  *
     25  * To Do:
     26  *
     27  * - autoconfify header file inclusion
     28  */
     29 
     30 #ifdef MS_WINDOWS
     31 /* can simulate truncate with Win32 API functions; see file_truncate */
     32 #define HAVE_FTRUNCATE
     33 #define WIN32_LEAN_AND_MEAN
     34 #include <windows.h>
     35 #endif
     36 
     37 #if BUFSIZ < (8*1024)
     38 #define SMALLCHUNK (8*1024)
     39 #elif (BUFSIZ >= (2 << 25))
     40 #error "unreasonable BUFSIZ > 64MB defined"
     41 #else
     42 #define SMALLCHUNK BUFSIZ
     43 #endif
     44 
     45 #if SIZEOF_INT < 4
     46 #define BIGCHUNK  (512 * 32)
     47 #else
     48 #define BIGCHUNK  (512 * 1024)
     49 #endif
     50 
     51 typedef struct {
     52     PyObject_HEAD
     53     int fd;
     54     unsigned int readable : 1;
     55     unsigned int writable : 1;
     56     signed int seekable : 2; /* -1 means unknown */
     57     unsigned int closefd : 1;
     58     PyObject *weakreflist;
     59     PyObject *dict;
     60 } fileio;
     61 
     62 PyTypeObject PyFileIO_Type;
     63 
     64 #define PyFileIO_Check(op) (PyObject_TypeCheck((op), &PyFileIO_Type))
     65 
     66 int
     67 _PyFileIO_closed(PyObject *self)
     68 {
     69     return ((fileio *)self)->fd < 0;
     70 }
     71 
     72 static PyObject *
     73 portable_lseek(int fd, PyObject *posobj, int whence);
     74 
     75 static PyObject *portable_lseek(int fd, PyObject *posobj, int whence);
     76 
     77 /* Returns 0 on success, -1 with exception set on failure. */
     78 static int
     79 internal_close(fileio *self)
     80 {
     81     int err = 0;
     82     int save_errno = 0;
     83     if (self->fd >= 0) {
     84         int fd = self->fd;
     85         self->fd = -1;
     86         /* fd is accessible and someone else may have closed it */
     87         if (_PyVerify_fd(fd)) {
     88             Py_BEGIN_ALLOW_THREADS
     89             err = close(fd);
     90             if (err < 0)
     91                 save_errno = errno;
     92             Py_END_ALLOW_THREADS
     93         } else {
     94             save_errno = errno;
     95             err = -1;
     96         }
     97     }
     98     if (err < 0) {
     99         errno = save_errno;
    100         PyErr_SetFromErrno(PyExc_IOError);
    101         return -1;
    102     }
    103     return 0;
    104 }
    105 
    106 static PyObject *
    107 fileio_close(fileio *self)
    108 {
    109     if (!self->closefd) {
    110         self->fd = -1;
    111         Py_RETURN_NONE;
    112     }
    113     errno = internal_close(self);
    114     if (errno < 0)
    115         return NULL;
    116 
    117     return PyObject_CallMethod((PyObject*)&PyRawIOBase_Type,
    118                                "close", "O", self);
    119 }
    120 
    121 static PyObject *
    122 fileio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
    123 {
    124     fileio *self;
    125 
    126     assert(type != NULL && type->tp_alloc != NULL);
    127 
    128     self = (fileio *) type->tp_alloc(type, 0);
    129     if (self != NULL) {
    130         self->fd = -1;
    131         self->readable = 0;
    132         self->writable = 0;
    133         self->seekable = -1;
    134         self->closefd = 1;
    135         self->weakreflist = NULL;
    136     }
    137 
    138     return (PyObject *) self;
    139 }
    140 
    141 /* On Unix, open will succeed for directories.
    142    In Python, there should be no file objects referring to
    143    directories, so we need a check.  */
    144 
    145 static int
    146 dircheck(fileio* self, const char *name)
    147 {
    148 #if defined(HAVE_FSTAT) && defined(S_IFDIR) && defined(EISDIR)
    149     struct stat buf;
    150     if (self->fd < 0)
    151         return 0;
    152     if (fstat(self->fd, &buf) == 0 && S_ISDIR(buf.st_mode)) {
    153         char *msg = strerror(EISDIR);
    154         PyObject *exc;
    155         if (internal_close(self))
    156             return -1;
    157 
    158         exc = PyObject_CallFunction(PyExc_IOError, "(iss)",
    159                                     EISDIR, msg, name);
    160         PyErr_SetObject(PyExc_IOError, exc);
    161         Py_XDECREF(exc);
    162         return -1;
    163     }
    164 #endif
    165     return 0;
    166 }
    167 
    168 static int
    169 check_fd(int fd)
    170 {
    171 #if defined(HAVE_FSTAT)
    172     struct stat buf;
    173     if (!_PyVerify_fd(fd) || (fstat(fd, &buf) < 0 && errno == EBADF)) {
    174         PyObject *exc;
    175         char *msg = strerror(EBADF);
    176         exc = PyObject_CallFunction(PyExc_OSError, "(is)",
    177                                     EBADF, msg);
    178         PyErr_SetObject(PyExc_OSError, exc);
    179         Py_XDECREF(exc);
    180         return -1;
    181     }
    182 #endif
    183     return 0;
    184 }
    185 
    186 
    187 static int
    188 fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
    189 {
    190     fileio *self = (fileio *) oself;
    191     static char *kwlist[] = {"file", "mode", "closefd", NULL};
    192     const char *name = NULL;
    193     PyObject *nameobj, *stringobj = NULL;
    194     char *mode = "r";
    195     char *s;
    196 #ifdef MS_WINDOWS
    197     Py_UNICODE *widename = NULL;
    198 #endif
    199     int ret = 0;
    200     int rwa = 0, plus = 0, append = 0;
    201     int flags = 0;
    202     int fd = -1;
    203     int closefd = 1;
    204 
    205     assert(PyFileIO_Check(oself));
    206     if (self->fd >= 0) {
    207         /* Have to close the existing file first. */
    208         if (internal_close(self) < 0)
    209             return -1;
    210     }
    211 
    212     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|si:fileio",
    213                                      kwlist, &nameobj, &mode, &closefd))
    214         return -1;
    215 
    216     if (PyFloat_Check(nameobj)) {
    217         PyErr_SetString(PyExc_TypeError,
    218                         "integer argument expected, got float");
    219         return -1;
    220     }
    221 
    222     fd = PyLong_AsLong(nameobj);
    223     if (fd < 0) {
    224         if (!PyErr_Occurred()) {
    225             PyErr_SetString(PyExc_ValueError,
    226                             "Negative filedescriptor");
    227             return -1;
    228         }
    229         PyErr_Clear();
    230     }
    231 
    232 #ifdef MS_WINDOWS
    233     if (PyUnicode_Check(nameobj))
    234         widename = PyUnicode_AS_UNICODE(nameobj);
    235     if (widename == NULL)
    236 #endif
    237     if (fd < 0)
    238     {
    239         if (PyBytes_Check(nameobj) || PyByteArray_Check(nameobj)) {
    240             Py_ssize_t namelen;
    241             if (PyObject_AsCharBuffer(nameobj, &name, &namelen) < 0)
    242                 return -1;
    243         }
    244         else {
    245             PyObject *u = PyUnicode_FromObject(nameobj);
    246 
    247             if (u == NULL)
    248                 return -1;
    249 
    250             stringobj = PyUnicode_AsEncodedString(
    251                 u, Py_FileSystemDefaultEncoding, NULL);
    252             Py_DECREF(u);
    253             if (stringobj == NULL)
    254                 return -1;
    255             if (!PyBytes_Check(stringobj)) {
    256                 PyErr_SetString(PyExc_TypeError,
    257                                 "encoder failed to return bytes");
    258                 goto error;
    259             }
    260             name = PyBytes_AS_STRING(stringobj);
    261         }
    262     }
    263 
    264     s = mode;
    265     while (*s) {
    266         switch (*s++) {
    267         case 'r':
    268             if (rwa) {
    269             bad_mode:
    270                 PyErr_SetString(PyExc_ValueError,
    271                                 "Must have exactly one of read/write/append "
    272                                 "mode and at most one plus");
    273                 goto error;
    274             }
    275             rwa = 1;
    276             self->readable = 1;
    277             break;
    278         case 'w':
    279             if (rwa)
    280                 goto bad_mode;
    281             rwa = 1;
    282             self->writable = 1;
    283             flags |= O_CREAT | O_TRUNC;
    284             break;
    285         case 'a':
    286             if (rwa)
    287                 goto bad_mode;
    288             rwa = 1;
    289             self->writable = 1;
    290             flags |= O_CREAT;
    291             append = 1;
    292             break;
    293         case 'b':
    294             break;
    295         case '+':
    296             if (plus)
    297                 goto bad_mode;
    298             self->readable = self->writable = 1;
    299             plus = 1;
    300             break;
    301         default:
    302             PyErr_Format(PyExc_ValueError,
    303                          "invalid mode: %.200s", mode);
    304             goto error;
    305         }
    306     }
    307 
    308     if (!rwa)
    309         goto bad_mode;
    310 
    311     if (self->readable && self->writable)
    312         flags |= O_RDWR;
    313     else if (self->readable)
    314         flags |= O_RDONLY;
    315     else
    316         flags |= O_WRONLY;
    317 
    318 #ifdef O_BINARY
    319     flags |= O_BINARY;
    320 #endif
    321 
    322 #ifdef O_APPEND
    323     if (append)
    324         flags |= O_APPEND;
    325 #endif
    326 
    327     if (fd >= 0) {
    328         if (check_fd(fd))
    329             goto error;
    330         self->fd = fd;
    331         self->closefd = closefd;
    332     }
    333     else {
    334         self->closefd = 1;
    335         if (!closefd) {
    336             PyErr_SetString(PyExc_ValueError,
    337                 "Cannot use closefd=False with file name");
    338             goto error;
    339         }
    340 
    341         Py_BEGIN_ALLOW_THREADS
    342         errno = 0;
    343 #ifdef MS_WINDOWS
    344         if (widename != NULL)
    345             self->fd = _wopen(widename, flags, 0666);
    346         else
    347 #endif
    348             self->fd = open(name, flags, 0666);
    349         Py_END_ALLOW_THREADS
    350         if (self->fd < 0) {
    351 #ifdef MS_WINDOWS
    352             if (widename != NULL)
    353                 PyErr_SetFromErrnoWithUnicodeFilename(PyExc_IOError, widename);
    354             else
    355 #endif
    356                 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
    357             goto error;
    358         }
    359         if(dircheck(self, name) < 0)
    360             goto error;
    361     }
    362 
    363     if (PyObject_SetAttrString((PyObject *)self, "name", nameobj) < 0)
    364         goto error;
    365 
    366     if (append) {
    367         /* For consistent behaviour, we explicitly seek to the
    368            end of file (otherwise, it might be done only on the
    369            first write()). */
    370         PyObject *pos = portable_lseek(self->fd, NULL, 2);
    371         if (pos == NULL) {
    372             if (closefd) {
    373                 close(self->fd);
    374                 self->fd = -1;
    375             }
    376             goto error;
    377         }
    378         Py_DECREF(pos);
    379     }
    380 
    381     goto done;
    382 
    383  error:
    384     ret = -1;
    385 
    386  done:
    387     Py_CLEAR(stringobj);
    388     return ret;
    389 }
    390 
    391 static int
    392 fileio_traverse(fileio *self, visitproc visit, void *arg)
    393 {
    394     Py_VISIT(self->dict);
    395     return 0;
    396 }
    397 
    398 static int
    399 fileio_clear(fileio *self)
    400 {
    401     Py_CLEAR(self->dict);
    402     return 0;
    403 }
    404 
    405 static void
    406 fileio_dealloc(fileio *self)
    407 {
    408     if (_PyIOBase_finalize((PyObject *) self) < 0)
    409         return;
    410     _PyObject_GC_UNTRACK(self);
    411     if (self->weakreflist != NULL)
    412         PyObject_ClearWeakRefs((PyObject *) self);
    413     Py_CLEAR(self->dict);
    414     Py_TYPE(self)->tp_free((PyObject *)self);
    415 }
    416 
    417 static PyObject *
    418 err_closed(void)
    419 {
    420     PyErr_SetString(PyExc_ValueError, "I/O operation on closed file");
    421     return NULL;
    422 }
    423 
    424 static PyObject *
    425 err_mode(char *action)
    426 {
    427     PyErr_Format(PyExc_ValueError, "File not open for %s", action);
    428     return NULL;
    429 }
    430 
    431 static PyObject *
    432 fileio_fileno(fileio *self)
    433 {
    434     if (self->fd < 0)
    435         return err_closed();
    436     return PyInt_FromLong((long) self->fd);
    437 }
    438 
    439 static PyObject *
    440 fileio_readable(fileio *self)
    441 {
    442     if (self->fd < 0)
    443         return err_closed();
    444     return PyBool_FromLong((long) self->readable);
    445 }
    446 
    447 static PyObject *
    448 fileio_writable(fileio *self)
    449 {
    450     if (self->fd < 0)
    451         return err_closed();
    452     return PyBool_FromLong((long) self->writable);
    453 }
    454 
    455 static PyObject *
    456 fileio_seekable(fileio *self)
    457 {
    458     if (self->fd < 0)
    459         return err_closed();
    460     if (self->seekable < 0) {
    461         PyObject *pos = portable_lseek(self->fd, NULL, SEEK_CUR);
    462         if (pos == NULL) {
    463             PyErr_Clear();
    464             self->seekable = 0;
    465         } else {
    466             Py_DECREF(pos);
    467             self->seekable = 1;
    468         }
    469     }
    470     return PyBool_FromLong((long) self->seekable);
    471 }
    472 
    473 static PyObject *
    474 fileio_readinto(fileio *self, PyObject *args)
    475 {
    476     Py_buffer pbuf;
    477     Py_ssize_t n;
    478 
    479     if (self->fd < 0)
    480         return err_closed();
    481     if (!self->readable)
    482         return err_mode("reading");
    483 
    484     if (!PyArg_ParseTuple(args, "w*", &pbuf))
    485         return NULL;
    486 
    487     if (_PyVerify_fd(self->fd)) {
    488         Py_BEGIN_ALLOW_THREADS
    489         errno = 0;
    490         n = read(self->fd, pbuf.buf, pbuf.len);
    491         Py_END_ALLOW_THREADS
    492     } else
    493         n = -1;
    494     PyBuffer_Release(&pbuf);
    495     if (n < 0) {
    496         if (errno == EAGAIN)
    497             Py_RETURN_NONE;
    498         PyErr_SetFromErrno(PyExc_IOError);
    499         return NULL;
    500     }
    501 
    502     return PyLong_FromSsize_t(n);
    503 }
    504 
    505 static size_t
    506 new_buffersize(fileio *self, size_t currentsize)
    507 {
    508 #ifdef HAVE_FSTAT
    509     off_t pos, end;
    510     struct stat st;
    511     if (fstat(self->fd, &st) == 0) {
    512         end = st.st_size;
    513         pos = lseek(self->fd, 0L, SEEK_CUR);
    514         /* Files claiming a size smaller than SMALLCHUNK may
    515            actually be streaming pseudo-files. In this case, we
    516            apply the more aggressive algorithm below.
    517         */
    518         if (end >= SMALLCHUNK && end >= pos && pos >= 0) {
    519             /* Add 1 so if the file were to grow we'd notice. */
    520             return currentsize + end - pos + 1;
    521         }
    522     }
    523 #endif
    524     if (currentsize > SMALLCHUNK) {
    525         /* Keep doubling until we reach BIGCHUNK;
    526            then keep adding BIGCHUNK. */
    527         if (currentsize <= BIGCHUNK)
    528             return currentsize + currentsize;
    529         else
    530             return currentsize + BIGCHUNK;
    531     }
    532     return currentsize + SMALLCHUNK;
    533 }
    534 
    535 static PyObject *
    536 fileio_readall(fileio *self)
    537 {
    538     PyObject *result;
    539     Py_ssize_t total = 0;
    540     int n;
    541 
    542     if (self->fd < 0)
    543         return err_closed();
    544     if (!_PyVerify_fd(self->fd))
    545         return PyErr_SetFromErrno(PyExc_IOError);
    546 
    547     result = PyBytes_FromStringAndSize(NULL, SMALLCHUNK);
    548     if (result == NULL)
    549         return NULL;
    550 
    551     while (1) {
    552         size_t newsize = new_buffersize(self, total);
    553         if (newsize > PY_SSIZE_T_MAX || newsize <= 0) {
    554             PyErr_SetString(PyExc_OverflowError,
    555                 "unbounded read returned more bytes "
    556                 "than a Python string can hold ");
    557             Py_DECREF(result);
    558             return NULL;
    559         }
    560 
    561         if (PyBytes_GET_SIZE(result) < (Py_ssize_t)newsize) {
    562             if (_PyBytes_Resize(&result, newsize) < 0) {
    563                 if (total == 0) {
    564                     Py_DECREF(result);
    565                     return NULL;
    566                 }
    567                 PyErr_Clear();
    568                 break;
    569             }
    570         }
    571         Py_BEGIN_ALLOW_THREADS
    572         errno = 0;
    573         n = read(self->fd,
    574                  PyBytes_AS_STRING(result) + total,
    575                  newsize - total);
    576         Py_END_ALLOW_THREADS
    577         if (n == 0)
    578             break;
    579         if (n < 0) {
    580             if (total > 0)
    581                 break;
    582             if (errno == EAGAIN) {
    583                 Py_DECREF(result);
    584                 Py_RETURN_NONE;
    585             }
    586             Py_DECREF(result);
    587             PyErr_SetFromErrno(PyExc_IOError);
    588             return NULL;
    589         }
    590         total += n;
    591     }
    592 
    593     if (PyBytes_GET_SIZE(result) > total) {
    594         if (_PyBytes_Resize(&result, total) < 0) {
    595             /* This should never happen, but just in case */
    596             Py_DECREF(result);
    597             return NULL;
    598         }
    599     }
    600     return result;
    601 }
    602 
    603 static PyObject *
    604 fileio_read(fileio *self, PyObject *args)
    605 {
    606     char *ptr;
    607     Py_ssize_t n;
    608     Py_ssize_t size = -1;
    609     PyObject *bytes;
    610 
    611     if (self->fd < 0)
    612         return err_closed();
    613     if (!self->readable)
    614         return err_mode("reading");
    615 
    616     if (!PyArg_ParseTuple(args, "|O&", &_PyIO_ConvertSsize_t, &size))
    617         return NULL;
    618 
    619     if (size < 0) {
    620         return fileio_readall(self);
    621     }
    622 
    623     bytes = PyBytes_FromStringAndSize(NULL, size);
    624     if (bytes == NULL)
    625         return NULL;
    626     ptr = PyBytes_AS_STRING(bytes);
    627 
    628     if (_PyVerify_fd(self->fd)) {
    629         Py_BEGIN_ALLOW_THREADS
    630         errno = 0;
    631         n = read(self->fd, ptr, size);
    632         Py_END_ALLOW_THREADS
    633     } else
    634         n = -1;
    635 
    636     if (n < 0) {
    637         Py_DECREF(bytes);
    638         if (errno == EAGAIN)
    639             Py_RETURN_NONE;
    640         PyErr_SetFromErrno(PyExc_IOError);
    641         return NULL;
    642     }
    643 
    644     if (n != size) {
    645         if (_PyBytes_Resize(&bytes, n) < 0) {
    646             Py_DECREF(bytes);
    647             return NULL;
    648         }
    649     }
    650 
    651     return (PyObject *) bytes;
    652 }
    653 
    654 static PyObject *
    655 fileio_write(fileio *self, PyObject *args)
    656 {
    657     Py_buffer pbuf;
    658     Py_ssize_t n;
    659 
    660     if (self->fd < 0)
    661         return err_closed();
    662     if (!self->writable)
    663         return err_mode("writing");
    664 
    665     if (!PyArg_ParseTuple(args, "s*", &pbuf))
    666         return NULL;
    667 
    668     if (_PyVerify_fd(self->fd)) {
    669         Py_BEGIN_ALLOW_THREADS
    670         errno = 0;
    671         n = write(self->fd, pbuf.buf, pbuf.len);
    672         Py_END_ALLOW_THREADS
    673     } else
    674         n = -1;
    675 
    676     PyBuffer_Release(&pbuf);
    677 
    678     if (n < 0) {
    679         if (errno == EAGAIN)
    680             Py_RETURN_NONE;
    681         PyErr_SetFromErrno(PyExc_IOError);
    682         return NULL;
    683     }
    684 
    685     return PyLong_FromSsize_t(n);
    686 }
    687 
    688 /* XXX Windows support below is likely incomplete */
    689 
    690 /* Cribbed from posix_lseek() */
    691 static PyObject *
    692 portable_lseek(int fd, PyObject *posobj, int whence)
    693 {
    694     Py_off_t pos, res;
    695 
    696 #ifdef SEEK_SET
    697     /* Turn 0, 1, 2 into SEEK_{SET,CUR,END} */
    698     switch (whence) {
    699 #if SEEK_SET != 0
    700     case 0: whence = SEEK_SET; break;
    701 #endif
    702 #if SEEK_CUR != 1
    703     case 1: whence = SEEK_CUR; break;
    704 #endif
    705 #if SEEK_END != 2
    706     case 2: whence = SEEK_END; break;
    707 #endif
    708     }
    709 #endif /* SEEK_SET */
    710 
    711     if (posobj == NULL)
    712         pos = 0;
    713     else {
    714         if(PyFloat_Check(posobj)) {
    715             PyErr_SetString(PyExc_TypeError, "an integer is required");
    716             return NULL;
    717         }
    718 #if defined(HAVE_LARGEFILE_SUPPORT)
    719         pos = PyLong_AsLongLong(posobj);
    720 #else
    721         pos = PyLong_AsLong(posobj);
    722 #endif
    723         if (PyErr_Occurred())
    724             return NULL;
    725     }
    726 
    727     if (_PyVerify_fd(fd)) {
    728         Py_BEGIN_ALLOW_THREADS
    729 #if defined(MS_WIN64) || defined(MS_WINDOWS)
    730         res = _lseeki64(fd, pos, whence);
    731 #else
    732         res = lseek(fd, pos, whence);
    733 #endif
    734         Py_END_ALLOW_THREADS
    735     } else
    736         res = -1;
    737     if (res < 0)
    738         return PyErr_SetFromErrno(PyExc_IOError);
    739 
    740 #if defined(HAVE_LARGEFILE_SUPPORT)
    741     return PyLong_FromLongLong(res);
    742 #else
    743     return PyLong_FromLong(res);
    744 #endif
    745 }
    746 
    747 static PyObject *
    748 fileio_seek(fileio *self, PyObject *args)
    749 {
    750     PyObject *posobj;
    751     int whence = 0;
    752 
    753     if (self->fd < 0)
    754         return err_closed();
    755 
    756     if (!PyArg_ParseTuple(args, "O|i", &posobj, &whence))
    757         return NULL;
    758 
    759     return portable_lseek(self->fd, posobj, whence);
    760 }
    761 
    762 static PyObject *
    763 fileio_tell(fileio *self, PyObject *args)
    764 {
    765     if (self->fd < 0)
    766         return err_closed();
    767 
    768     return portable_lseek(self->fd, NULL, 1);
    769 }
    770 
    771 #ifdef HAVE_FTRUNCATE
    772 static PyObject *
    773 fileio_truncate(fileio *self, PyObject *args)
    774 {
    775     PyObject *posobj = NULL; /* the new size wanted by the user */
    776 #ifndef MS_WINDOWS
    777     Py_off_t pos;
    778 #endif
    779     int ret;
    780     int fd;
    781 
    782     fd = self->fd;
    783     if (fd < 0)
    784         return err_closed();
    785     if (!self->writable)
    786         return err_mode("writing");
    787 
    788     if (!PyArg_ParseTuple(args, "|O", &posobj))
    789         return NULL;
    790 
    791     if (posobj == Py_None || posobj == NULL) {
    792         /* Get the current position. */
    793         posobj = portable_lseek(fd, NULL, 1);
    794         if (posobj == NULL)
    795             return NULL;
    796     }
    797     else {
    798         Py_INCREF(posobj);
    799     }
    800 
    801 #ifdef MS_WINDOWS
    802     /* MS _chsize doesn't work if newsize doesn't fit in 32 bits,
    803        so don't even try using it. */
    804     {
    805         PyObject *oldposobj, *tempposobj;
    806         HANDLE hFile;
    807 
    808         /* we save the file pointer position */
    809         oldposobj = portable_lseek(fd, NULL, 1);
    810         if (oldposobj == NULL) {
    811             Py_DECREF(posobj);
    812             return NULL;
    813         }
    814 
    815         /* we then move to the truncation position */
    816         tempposobj = portable_lseek(fd, posobj, 0);
    817         if (tempposobj == NULL) {
    818             Py_DECREF(oldposobj);
    819             Py_DECREF(posobj);
    820             return NULL;
    821         }
    822         Py_DECREF(tempposobj);
    823 
    824         /* Truncate.  Note that this may grow the file! */
    825         Py_BEGIN_ALLOW_THREADS
    826         errno = 0;
    827         hFile = (HANDLE)_get_osfhandle(fd);
    828         ret = hFile == (HANDLE)-1; /* testing for INVALID_HANDLE value */
    829         if (ret == 0) {
    830             ret = SetEndOfFile(hFile) == 0;
    831             if (ret)
    832                 errno = EACCES;
    833         }
    834         Py_END_ALLOW_THREADS
    835 
    836         /* we restore the file pointer position in any case */
    837         tempposobj = portable_lseek(fd, oldposobj, 0);
    838         Py_DECREF(oldposobj);
    839         if (tempposobj == NULL) {
    840             Py_DECREF(posobj);
    841             return NULL;
    842         }
    843         Py_DECREF(tempposobj);
    844     }
    845 #else
    846 
    847 #if defined(HAVE_LARGEFILE_SUPPORT)
    848     pos = PyLong_AsLongLong(posobj);
    849 #else
    850     pos = PyLong_AsLong(posobj);
    851 #endif
    852     if (PyErr_Occurred()){
    853         Py_DECREF(posobj);
    854         return NULL;
    855     }
    856 
    857     Py_BEGIN_ALLOW_THREADS
    858     errno = 0;
    859     ret = ftruncate(fd, pos);
    860     Py_END_ALLOW_THREADS
    861 
    862 #endif /* !MS_WINDOWS */
    863 
    864     if (ret != 0) {
    865         Py_DECREF(posobj);
    866         PyErr_SetFromErrno(PyExc_IOError);
    867         return NULL;
    868     }
    869 
    870     return posobj;
    871 }
    872 #endif /* HAVE_FTRUNCATE */
    873 
    874 static char *
    875 mode_string(fileio *self)
    876 {
    877     if (self->readable) {
    878         if (self->writable)
    879             return "rb+";
    880         else
    881             return "rb";
    882     }
    883     else
    884         return "wb";
    885 }
    886 
    887 static PyObject *
    888 fileio_repr(fileio *self)
    889 {
    890     PyObject *nameobj, *res;
    891 
    892     if (self->fd < 0)
    893         return PyString_FromFormat("<_io.FileIO [closed]>");
    894 
    895     nameobj = PyObject_GetAttrString((PyObject *) self, "name");
    896     if (nameobj == NULL) {
    897         if (PyErr_ExceptionMatches(PyExc_AttributeError))
    898             PyErr_Clear();
    899         else
    900             return NULL;
    901         res = PyString_FromFormat("<_io.FileIO fd=%d mode='%s'>",
    902                                    self->fd, mode_string(self));
    903     }
    904     else {
    905         PyObject *repr = PyObject_Repr(nameobj);
    906         Py_DECREF(nameobj);
    907         if (repr == NULL)
    908             return NULL;
    909         res = PyString_FromFormat("<_io.FileIO name=%s mode='%s'>",
    910                                    PyString_AS_STRING(repr),
    911                                    mode_string(self));
    912         Py_DECREF(repr);
    913     }
    914     return res;
    915 }
    916 
    917 static PyObject *
    918 fileio_isatty(fileio *self)
    919 {
    920     long res;
    921 
    922     if (self->fd < 0)
    923         return err_closed();
    924     Py_BEGIN_ALLOW_THREADS
    925     res = isatty(self->fd);
    926     Py_END_ALLOW_THREADS
    927     return PyBool_FromLong(res);
    928 }
    929 
    930 
    931 PyDoc_STRVAR(fileio_doc,
    932 "file(name: str[, mode: str]) -> file IO object\n"
    933 "\n"
    934 "Open a file.  The mode can be 'r', 'w' or 'a' for reading (default),\n"
    935 "writing or appending.  The file will be created if it doesn't exist\n"
    936 "when opened for writing or appending; it will be truncated when\n"
    937 "opened for writing.  Add a '+' to the mode to allow simultaneous\n"
    938 "reading and writing.");
    939 
    940 PyDoc_STRVAR(read_doc,
    941 "read(size: int) -> bytes.  read at most size bytes, returned as bytes.\n"
    942 "\n"
    943 "Only makes one system call, so less data may be returned than requested\n"
    944 "In non-blocking mode, returns None if no data is available.\n"
    945 "On end-of-file, returns ''.");
    946 
    947 PyDoc_STRVAR(readall_doc,
    948 "readall() -> bytes.  read all data from the file, returned as bytes.\n"
    949 "\n"
    950 "In non-blocking mode, returns as much as is immediately available,\n"
    951 "or None if no data is available.  On end-of-file, returns ''.");
    952 
    953 PyDoc_STRVAR(write_doc,
    954 "write(b: bytes) -> int.  Write bytes b to file, return number written.\n"
    955 "\n"
    956 "Only makes one system call, so not all of the data may be written.\n"
    957 "The number of bytes actually written is returned.");
    958 
    959 PyDoc_STRVAR(fileno_doc,
    960 "fileno() -> int. \"file descriptor\".\n"
    961 "\n"
    962 "This is needed for lower-level file interfaces, such the fcntl module.");
    963 
    964 PyDoc_STRVAR(seek_doc,
    965 "seek(offset: int[, whence: int]) -> None.  Move to new file position.\n"
    966 "\n"
    967 "Argument offset is a byte count.  Optional argument whence defaults to\n"
    968 "0 (offset from start of file, offset should be >= 0); other values are 1\n"
    969 "(move relative to current position, positive or negative), and 2 (move\n"
    970 "relative to end of file, usually negative, although many platforms allow\n"
    971 "seeking beyond the end of a file)."
    972 "\n"
    973 "Note that not all file objects are seekable.");
    974 
    975 #ifdef HAVE_FTRUNCATE
    976 PyDoc_STRVAR(truncate_doc,
    977 "truncate([size: int]) -> None.  Truncate the file to at most size bytes.\n"
    978 "\n"
    979 "Size defaults to the current file position, as returned by tell()."
    980 "The current file position is changed to the value of size.");
    981 #endif
    982 
    983 PyDoc_STRVAR(tell_doc,
    984 "tell() -> int.  Current file position");
    985 
    986 PyDoc_STRVAR(readinto_doc,
    987 "readinto() -> Same as RawIOBase.readinto().");
    988 
    989 PyDoc_STRVAR(close_doc,
    990 "close() -> None.  Close the file.\n"
    991 "\n"
    992 "A closed file cannot be used for further I/O operations.  close() may be\n"
    993 "called more than once without error.  Changes the fileno to -1.");
    994 
    995 PyDoc_STRVAR(isatty_doc,
    996 "isatty() -> bool.  True if the file is connected to a tty device.");
    997 
    998 PyDoc_STRVAR(seekable_doc,
    999 "seekable() -> bool.  True if file supports random-access.");
   1000 
   1001 PyDoc_STRVAR(readable_doc,
   1002 "readable() -> bool.  True if file was opened in a read mode.");
   1003 
   1004 PyDoc_STRVAR(writable_doc,
   1005 "writable() -> bool.  True if file was opened in a write mode.");
   1006 
   1007 static PyMethodDef fileio_methods[] = {
   1008     {"read",     (PyCFunction)fileio_read,         METH_VARARGS, read_doc},
   1009     {"readall",  (PyCFunction)fileio_readall,  METH_NOARGS,  readall_doc},
   1010     {"readinto", (PyCFunction)fileio_readinto, METH_VARARGS, readinto_doc},
   1011     {"write",    (PyCFunction)fileio_write,        METH_VARARGS, write_doc},
   1012     {"seek",     (PyCFunction)fileio_seek,         METH_VARARGS, seek_doc},
   1013     {"tell",     (PyCFunction)fileio_tell,         METH_VARARGS, tell_doc},
   1014 #ifdef HAVE_FTRUNCATE
   1015     {"truncate", (PyCFunction)fileio_truncate, METH_VARARGS, truncate_doc},
   1016 #endif
   1017     {"close",    (PyCFunction)fileio_close,        METH_NOARGS,  close_doc},
   1018     {"seekable", (PyCFunction)fileio_seekable, METH_NOARGS,      seekable_doc},
   1019     {"readable", (PyCFunction)fileio_readable, METH_NOARGS,      readable_doc},
   1020     {"writable", (PyCFunction)fileio_writable, METH_NOARGS,      writable_doc},
   1021     {"fileno",   (PyCFunction)fileio_fileno,   METH_NOARGS,      fileno_doc},
   1022     {"isatty",   (PyCFunction)fileio_isatty,   METH_NOARGS,      isatty_doc},
   1023     {NULL,           NULL}             /* sentinel */
   1024 };
   1025 
   1026 /* 'closed' and 'mode' are attributes for backwards compatibility reasons. */
   1027 
   1028 static PyObject *
   1029 get_closed(fileio *self, void *closure)
   1030 {
   1031     return PyBool_FromLong((long)(self->fd < 0));
   1032 }
   1033 
   1034 static PyObject *
   1035 get_closefd(fileio *self, void *closure)
   1036 {
   1037     return PyBool_FromLong((long)(self->closefd));
   1038 }
   1039 
   1040 static PyObject *
   1041 get_mode(fileio *self, void *closure)
   1042 {
   1043     return PyUnicode_FromString(mode_string(self));
   1044 }
   1045 
   1046 static PyGetSetDef fileio_getsetlist[] = {
   1047     {"closed", (getter)get_closed, NULL, "True if the file is closed"},
   1048     {"closefd", (getter)get_closefd, NULL,
   1049         "True if the file descriptor will be closed"},
   1050     {"mode", (getter)get_mode, NULL, "String giving the file mode"},
   1051     {NULL},
   1052 };
   1053 
   1054 PyTypeObject PyFileIO_Type = {
   1055     PyVarObject_HEAD_INIT(NULL, 0)
   1056     "_io.FileIO",
   1057     sizeof(fileio),
   1058     0,
   1059     (destructor)fileio_dealloc,                 /* tp_dealloc */
   1060     0,                                          /* tp_print */
   1061     0,                                          /* tp_getattr */
   1062     0,                                          /* tp_setattr */
   1063     0,                                          /* tp_reserved */
   1064     (reprfunc)fileio_repr,                      /* tp_repr */
   1065     0,                                          /* tp_as_number */
   1066     0,                                          /* tp_as_sequence */
   1067     0,                                          /* tp_as_mapping */
   1068     0,                                          /* tp_hash */
   1069     0,                                          /* tp_call */
   1070     0,                                          /* tp_str */
   1071     PyObject_GenericGetAttr,                    /* tp_getattro */
   1072     0,                                          /* tp_setattro */
   1073     0,                                          /* tp_as_buffer */
   1074     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
   1075                     | Py_TPFLAGS_HAVE_GC,       /* tp_flags */
   1076     fileio_doc,                                 /* tp_doc */
   1077     (traverseproc)fileio_traverse,              /* tp_traverse */
   1078     (inquiry)fileio_clear,                      /* tp_clear */
   1079     0,                                          /* tp_richcompare */
   1080     offsetof(fileio, weakreflist),      /* tp_weaklistoffset */
   1081     0,                                          /* tp_iter */
   1082     0,                                          /* tp_iternext */
   1083     fileio_methods,                             /* tp_methods */
   1084     0,                                          /* tp_members */
   1085     fileio_getsetlist,                          /* tp_getset */
   1086     0,                                          /* tp_base */
   1087     0,                                          /* tp_dict */
   1088     0,                                          /* tp_descr_get */
   1089     0,                                          /* tp_descr_set */
   1090     offsetof(fileio, dict),         /* tp_dictoffset */
   1091     fileio_init,                                /* tp_init */
   1092     PyType_GenericAlloc,                        /* tp_alloc */
   1093     fileio_new,                                 /* tp_new */
   1094     PyObject_GC_Del,                            /* tp_free */
   1095 };
   1096