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