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