Line data Source code
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
62 0 : _PyFileIO_closed(PyObject *self)
63 : {
64 0 : 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
74 0 : internal_close(fileio *self)
75 : {
76 0 : int err = 0;
77 0 : int save_errno = 0;
78 0 : if (self->fd >= 0) {
79 0 : int fd = self->fd;
80 0 : 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 0 : err = close(fd);
85 0 : if (err < 0)
86 0 : save_errno = errno;
87 : Py_END_ALLOW_THREADS
88 : } else {
89 : save_errno = errno;
90 : err = -1;
91 : }
92 : }
93 0 : if (err < 0) {
94 0 : errno = save_errno;
95 0 : PyErr_SetFromErrno(PyExc_IOError);
96 0 : return -1;
97 : }
98 0 : return 0;
99 : }
100 :
101 : static PyObject *
102 0 : fileio_close(fileio *self)
103 : {
104 : PyObject *res;
105 0 : res = PyObject_CallMethod((PyObject*)&PyRawIOBase_Type,
106 : "close", "O", self);
107 0 : if (!self->closefd) {
108 0 : self->fd = -1;
109 0 : return res;
110 : }
111 0 : if (internal_close(self) < 0)
112 0 : Py_CLEAR(res);
113 0 : return res;
114 : }
115 :
116 : static PyObject *
117 0 : fileio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
118 : {
119 : fileio *self;
120 :
121 : assert(type != NULL && type->tp_alloc != NULL);
122 :
123 0 : self = (fileio *) type->tp_alloc(type, 0);
124 0 : if (self != NULL) {
125 0 : self->fd = -1;
126 0 : self->readable = 0;
127 0 : self->writable = 0;
128 0 : self->appending = 0;
129 0 : self->seekable = -1;
130 0 : self->closefd = 1;
131 0 : self->weakreflist = NULL;
132 : }
133 :
134 0 : 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
142 0 : dircheck(fileio* self, PyObject *nameobj)
143 : {
144 : #if defined(HAVE_FSTAT) && defined(S_IFDIR) && defined(EISDIR)
145 : struct stat buf;
146 0 : if (self->fd < 0)
147 0 : return 0;
148 0 : if (fstat(self->fd, &buf) == 0 && S_ISDIR(buf.st_mode)) {
149 0 : errno = EISDIR;
150 0 : PyErr_SetFromErrnoWithFilenameObject(PyExc_IOError, nameobj);
151 0 : return -1;
152 : }
153 : #endif
154 0 : return 0;
155 : }
156 :
157 : static int
158 0 : check_fd(int fd)
159 : {
160 : #if defined(HAVE_FSTAT)
161 : struct stat buf;
162 0 : if (!_PyVerify_fd(fd) || (fstat(fd, &buf) < 0 && errno == EBADF)) {
163 : PyObject *exc;
164 0 : char *msg = strerror(EBADF);
165 0 : exc = PyObject_CallFunction(PyExc_OSError, "(is)",
166 : EBADF, msg);
167 0 : PyErr_SetObject(PyExc_OSError, exc);
168 0 : Py_XDECREF(exc);
169 0 : return -1;
170 : }
171 : #endif
172 0 : return 0;
173 : }
174 :
175 :
176 : static int
177 0 : fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
178 : {
179 0 : fileio *self = (fileio *) oself;
180 : static char *kwlist[] = {"file", "mode", "closefd", NULL};
181 0 : const char *name = NULL;
182 0 : PyObject *nameobj, *stringobj = NULL;
183 0 : char *mode = "r";
184 : char *s;
185 : #ifdef MS_WINDOWS
186 : Py_UNICODE *widename = NULL;
187 : #endif
188 0 : int ret = 0;
189 0 : int rwa = 0, plus = 0;
190 0 : int flags = 0;
191 0 : int fd = -1;
192 0 : int closefd = 1;
193 0 : int fd_is_own = 0;
194 :
195 : assert(PyFileIO_Check(oself));
196 0 : if (self->fd >= 0) {
197 0 : if (self->closefd) {
198 : /* Have to close the existing file first. */
199 0 : if (internal_close(self) < 0)
200 0 : return -1;
201 : }
202 : else
203 0 : self->fd = -1;
204 : }
205 :
206 0 : if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|si:fileio",
207 : kwlist, &nameobj, &mode, &closefd))
208 0 : return -1;
209 :
210 0 : if (PyFloat_Check(nameobj)) {
211 0 : PyErr_SetString(PyExc_TypeError,
212 : "integer argument expected, got float");
213 0 : return -1;
214 : }
215 :
216 0 : fd = _PyLong_AsInt(nameobj);
217 0 : if (fd < 0) {
218 0 : if (!PyErr_Occurred()) {
219 0 : PyErr_SetString(PyExc_ValueError,
220 : "negative file descriptor");
221 0 : return -1;
222 : }
223 0 : 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 0 : if (fd < 0)
237 : {
238 0 : if (PyBytes_Check(nameobj) || PyByteArray_Check(nameobj)) {
239 : Py_ssize_t namelen;
240 0 : if (PyObject_AsCharBuffer(nameobj, &name, &namelen) < 0)
241 0 : return -1;
242 0 : if (strlen(name) != (size_t)namelen) {
243 0 : PyErr_SetString(PyExc_TypeError, "embedded NUL character");
244 0 : return -1;
245 : }
246 : }
247 : else {
248 0 : PyObject *u = PyUnicode_FromObject(nameobj);
249 :
250 0 : if (u == NULL)
251 0 : return -1;
252 :
253 0 : stringobj = PyUnicode_AsEncodedString(
254 : u, Py_FileSystemDefaultEncoding, NULL);
255 0 : Py_DECREF(u);
256 0 : if (stringobj == NULL)
257 0 : return -1;
258 0 : if (!PyBytes_Check(stringobj)) {
259 0 : PyErr_SetString(PyExc_TypeError,
260 : "encoder failed to return bytes");
261 0 : goto error;
262 : }
263 0 : name = PyBytes_AS_STRING(stringobj);
264 0 : if (strlen(name) != (size_t)PyBytes_GET_SIZE(stringobj)) {
265 0 : PyErr_SetString(PyExc_TypeError, "embedded NUL character");
266 0 : goto error;
267 : }
268 : }
269 : }
270 :
271 0 : s = mode;
272 0 : while (*s) {
273 0 : switch (*s++) {
274 : case 'r':
275 0 : if (rwa) {
276 : bad_mode:
277 0 : PyErr_SetString(PyExc_ValueError,
278 : "Must have exactly one of read/write/append "
279 : "mode and at most one plus");
280 0 : goto error;
281 : }
282 0 : rwa = 1;
283 0 : self->readable = 1;
284 0 : break;
285 : case 'w':
286 0 : if (rwa)
287 0 : goto bad_mode;
288 0 : rwa = 1;
289 0 : self->writable = 1;
290 0 : flags |= O_CREAT | O_TRUNC;
291 0 : break;
292 : case 'a':
293 0 : if (rwa)
294 0 : goto bad_mode;
295 0 : rwa = 1;
296 0 : self->writable = 1;
297 0 : self->appending = 1;
298 0 : flags |= O_APPEND | O_CREAT;
299 0 : break;
300 : case 'b':
301 0 : break;
302 : case '+':
303 0 : if (plus)
304 0 : goto bad_mode;
305 0 : self->readable = self->writable = 1;
306 0 : plus = 1;
307 0 : break;
308 : default:
309 0 : PyErr_Format(PyExc_ValueError,
310 : "invalid mode: %.200s", mode);
311 0 : goto error;
312 : }
313 : }
314 :
315 0 : if (!rwa)
316 0 : goto bad_mode;
317 :
318 0 : if (self->readable && self->writable)
319 0 : flags |= O_RDWR;
320 0 : else if (self->readable)
321 0 : flags |= O_RDONLY;
322 : else
323 0 : flags |= O_WRONLY;
324 :
325 : #ifdef O_BINARY
326 : flags |= O_BINARY;
327 : #endif
328 :
329 0 : if (fd >= 0) {
330 0 : if (check_fd(fd))
331 0 : goto error;
332 0 : self->fd = fd;
333 0 : self->closefd = closefd;
334 : }
335 : else {
336 0 : self->closefd = 1;
337 0 : if (!closefd) {
338 0 : PyErr_SetString(PyExc_ValueError,
339 : "Cannot use closefd=False with file name");
340 0 : goto error;
341 : }
342 :
343 : Py_BEGIN_ALLOW_THREADS
344 0 : errno = 0;
345 : #ifdef MS_WINDOWS
346 : if (widename != NULL)
347 : self->fd = _wopen(widename, flags, 0666);
348 : else
349 : #endif
350 0 : self->fd = open(name, flags, 0666);
351 : Py_END_ALLOW_THREADS
352 0 : fd_is_own = 1;
353 0 : if (self->fd < 0) {
354 : #ifdef MS_WINDOWS
355 : if (widename != NULL)
356 : PyErr_SetFromErrnoWithUnicodeFilename(PyExc_IOError, widename);
357 : else
358 : #endif
359 0 : PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
360 0 : goto error;
361 : }
362 : }
363 0 : if (dircheck(self, nameobj) < 0)
364 0 : goto error;
365 :
366 0 : if (PyObject_SetAttrString((PyObject *)self, "name", nameobj) < 0)
367 0 : goto error;
368 :
369 0 : 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 0 : PyObject *pos = portable_lseek(self->fd, NULL, 2);
374 0 : if (pos == NULL)
375 0 : goto error;
376 0 : Py_DECREF(pos);
377 : }
378 :
379 0 : goto done;
380 :
381 : error:
382 0 : if (!fd_is_own)
383 0 : self->fd = -1;
384 :
385 0 : ret = -1;
386 :
387 : done:
388 0 : Py_CLEAR(stringobj);
389 0 : return ret;
390 : }
391 :
392 : static int
393 0 : fileio_traverse(fileio *self, visitproc visit, void *arg)
394 : {
395 0 : Py_VISIT(self->dict);
396 0 : return 0;
397 : }
398 :
399 : static int
400 0 : fileio_clear(fileio *self)
401 : {
402 0 : Py_CLEAR(self->dict);
403 0 : return 0;
404 : }
405 :
406 : static void
407 0 : fileio_dealloc(fileio *self)
408 : {
409 0 : if (_PyIOBase_finalize((PyObject *) self) < 0)
410 0 : return;
411 0 : _PyObject_GC_UNTRACK(self);
412 0 : if (self->weakreflist != NULL)
413 0 : PyObject_ClearWeakRefs((PyObject *) self);
414 0 : Py_CLEAR(self->dict);
415 0 : Py_TYPE(self)->tp_free((PyObject *)self);
416 : }
417 :
418 : static PyObject *
419 0 : err_closed(void)
420 : {
421 0 : PyErr_SetString(PyExc_ValueError, "I/O operation on closed file");
422 0 : return NULL;
423 : }
424 :
425 : static PyObject *
426 0 : err_mode(char *action)
427 : {
428 0 : PyErr_Format(PyExc_ValueError, "File not open for %s", action);
429 0 : return NULL;
430 : }
431 :
432 : static PyObject *
433 0 : fileio_fileno(fileio *self)
434 : {
435 0 : if (self->fd < 0)
436 0 : return err_closed();
437 0 : return PyInt_FromLong((long) self->fd);
438 : }
439 :
440 : static PyObject *
441 0 : fileio_readable(fileio *self)
442 : {
443 0 : if (self->fd < 0)
444 0 : return err_closed();
445 0 : return PyBool_FromLong((long) self->readable);
446 : }
447 :
448 : static PyObject *
449 0 : fileio_writable(fileio *self)
450 : {
451 0 : if (self->fd < 0)
452 0 : return err_closed();
453 0 : return PyBool_FromLong((long) self->writable);
454 : }
455 :
456 : static PyObject *
457 0 : fileio_seekable(fileio *self)
458 : {
459 0 : if (self->fd < 0)
460 0 : return err_closed();
461 0 : if (self->seekable < 0) {
462 0 : PyObject *pos = portable_lseek(self->fd, NULL, SEEK_CUR);
463 0 : if (pos == NULL) {
464 0 : PyErr_Clear();
465 0 : self->seekable = 0;
466 : } else {
467 0 : Py_DECREF(pos);
468 0 : self->seekable = 1;
469 : }
470 : }
471 0 : return PyBool_FromLong((long) self->seekable);
472 : }
473 :
474 : static PyObject *
475 0 : fileio_readinto(fileio *self, PyObject *args)
476 : {
477 : Py_buffer pbuf;
478 : Py_ssize_t n, len;
479 :
480 0 : if (self->fd < 0)
481 0 : return err_closed();
482 0 : if (!self->readable)
483 0 : return err_mode("reading");
484 :
485 0 : if (!PyArg_ParseTuple(args, "w*", &pbuf))
486 0 : return NULL;
487 :
488 : if (_PyVerify_fd(self->fd)) {
489 0 : len = pbuf.len;
490 : Py_BEGIN_ALLOW_THREADS
491 0 : 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 0 : n = read(self->fd, pbuf.buf, len);
498 : #endif
499 : Py_END_ALLOW_THREADS
500 : } else
501 : n = -1;
502 0 : PyBuffer_Release(&pbuf);
503 0 : if (n < 0) {
504 0 : if (errno == EAGAIN)
505 0 : Py_RETURN_NONE;
506 0 : PyErr_SetFromErrno(PyExc_IOError);
507 0 : return NULL;
508 : }
509 :
510 0 : return PyLong_FromSsize_t(n);
511 : }
512 :
513 : static size_t
514 0 : new_buffersize(fileio *self, size_t currentsize)
515 : {
516 : #ifdef HAVE_FSTAT
517 : off_t pos, end;
518 : struct stat st;
519 0 : if (fstat(self->fd, &st) == 0) {
520 0 : end = st.st_size;
521 0 : 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 0 : if (end >= SMALLCHUNK && end >= pos && pos >= 0) {
527 : /* Add 1 so if the file were to grow we'd notice. */
528 0 : 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 0 : return currentsize + (currentsize >> 3) + 6;
536 : }
537 :
538 : static PyObject *
539 0 : fileio_readall(fileio *self)
540 : {
541 : PyObject *result;
542 0 : Py_ssize_t total = 0;
543 : Py_ssize_t n;
544 :
545 0 : if (self->fd < 0)
546 0 : return err_closed();
547 : if (!_PyVerify_fd(self->fd))
548 : return PyErr_SetFromErrno(PyExc_IOError);
549 :
550 0 : result = PyBytes_FromStringAndSize(NULL, SMALLCHUNK);
551 0 : if (result == NULL)
552 0 : return NULL;
553 :
554 : while (1) {
555 0 : size_t newsize = new_buffersize(self, total);
556 0 : if (newsize > PY_SSIZE_T_MAX || newsize <= 0) {
557 0 : PyErr_SetString(PyExc_OverflowError,
558 : "unbounded read returned more bytes "
559 : "than a Python string can hold ");
560 0 : Py_DECREF(result);
561 0 : return NULL;
562 : }
563 :
564 0 : if (PyBytes_GET_SIZE(result) < (Py_ssize_t)newsize) {
565 0 : if (_PyBytes_Resize(&result, newsize) < 0)
566 0 : return NULL; /* result has been freed */
567 : }
568 : Py_BEGIN_ALLOW_THREADS
569 0 : errno = 0;
570 0 : 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 0 : n = read(self->fd,
579 0 : PyBytes_AS_STRING(result) + total,
580 : n);
581 : #endif
582 : Py_END_ALLOW_THREADS
583 0 : if (n == 0)
584 0 : break;
585 0 : if (n < 0) {
586 0 : if (errno == EINTR) {
587 0 : if (PyErr_CheckSignals()) {
588 0 : Py_DECREF(result);
589 0 : return NULL;
590 : }
591 0 : continue;
592 : }
593 0 : if (errno == EAGAIN) {
594 0 : if (total > 0)
595 0 : break;
596 0 : Py_DECREF(result);
597 0 : Py_RETURN_NONE;
598 : }
599 0 : Py_DECREF(result);
600 0 : PyErr_SetFromErrno(PyExc_IOError);
601 0 : return NULL;
602 : }
603 0 : total += n;
604 0 : }
605 :
606 0 : if (PyBytes_GET_SIZE(result) > total) {
607 0 : if (_PyBytes_Resize(&result, total) < 0) {
608 : /* This should never happen, but just in case */
609 0 : return NULL;
610 : }
611 : }
612 0 : return result;
613 : }
614 :
615 : static PyObject *
616 0 : fileio_read(fileio *self, PyObject *args)
617 : {
618 : char *ptr;
619 : Py_ssize_t n;
620 0 : Py_ssize_t size = -1;
621 : PyObject *bytes;
622 :
623 0 : if (self->fd < 0)
624 0 : return err_closed();
625 0 : if (!self->readable)
626 0 : return err_mode("reading");
627 :
628 0 : if (!PyArg_ParseTuple(args, "|O&", &_PyIO_ConvertSsize_t, &size))
629 0 : return NULL;
630 :
631 0 : if (size < 0) {
632 0 : 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 0 : bytes = PyBytes_FromStringAndSize(NULL, size);
640 0 : if (bytes == NULL)
641 0 : return NULL;
642 0 : ptr = PyBytes_AS_STRING(bytes);
643 :
644 : if (_PyVerify_fd(self->fd)) {
645 : Py_BEGIN_ALLOW_THREADS
646 0 : errno = 0;
647 : #if defined(MS_WIN64) || defined(MS_WINDOWS)
648 : n = read(self->fd, ptr, (int)size);
649 : #else
650 0 : n = read(self->fd, ptr, size);
651 : #endif
652 : Py_END_ALLOW_THREADS
653 : } else
654 : n = -1;
655 :
656 0 : if (n < 0) {
657 0 : Py_DECREF(bytes);
658 0 : if (errno == EAGAIN)
659 0 : Py_RETURN_NONE;
660 0 : PyErr_SetFromErrno(PyExc_IOError);
661 0 : return NULL;
662 : }
663 :
664 0 : if (n != size) {
665 0 : if (_PyBytes_Resize(&bytes, n) < 0)
666 0 : return NULL;
667 : }
668 :
669 0 : return (PyObject *) bytes;
670 : }
671 :
672 : static PyObject *
673 0 : fileio_write(fileio *self, PyObject *args)
674 : {
675 : Py_buffer pbuf;
676 : Py_ssize_t n, len;
677 :
678 0 : if (self->fd < 0)
679 0 : return err_closed();
680 0 : if (!self->writable)
681 0 : return err_mode("writing");
682 :
683 0 : if (!PyArg_ParseTuple(args, "s*", &pbuf))
684 0 : return NULL;
685 :
686 : if (_PyVerify_fd(self->fd)) {
687 : Py_BEGIN_ALLOW_THREADS
688 0 : errno = 0;
689 0 : 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 0 : n = write(self->fd, pbuf.buf, len);
696 : #endif
697 : Py_END_ALLOW_THREADS
698 : } else
699 : n = -1;
700 :
701 0 : PyBuffer_Release(&pbuf);
702 :
703 0 : if (n < 0) {
704 0 : if (errno == EAGAIN)
705 0 : Py_RETURN_NONE;
706 0 : PyErr_SetFromErrno(PyExc_IOError);
707 0 : return NULL;
708 : }
709 :
710 0 : return PyLong_FromSsize_t(n);
711 : }
712 :
713 : /* XXX Windows support below is likely incomplete */
714 :
715 : /* Cribbed from posix_lseek() */
716 : static PyObject *
717 0 : 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 0 : if (posobj == NULL)
737 0 : pos = 0;
738 : else {
739 0 : if(PyFloat_Check(posobj)) {
740 0 : PyErr_SetString(PyExc_TypeError, "an integer is required");
741 0 : return NULL;
742 : }
743 : #if defined(HAVE_LARGEFILE_SUPPORT)
744 : pos = PyLong_AsLongLong(posobj);
745 : #else
746 0 : pos = PyLong_AsLong(posobj);
747 : #endif
748 0 : if (PyErr_Occurred())
749 0 : 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 0 : res = lseek(fd, pos, whence);
758 : #endif
759 : Py_END_ALLOW_THREADS
760 : } else
761 : res = -1;
762 0 : if (res < 0)
763 0 : return PyErr_SetFromErrno(PyExc_IOError);
764 :
765 : #if defined(HAVE_LARGEFILE_SUPPORT)
766 : return PyLong_FromLongLong(res);
767 : #else
768 0 : return PyLong_FromLong(res);
769 : #endif
770 : }
771 :
772 : static PyObject *
773 0 : fileio_seek(fileio *self, PyObject *args)
774 : {
775 : PyObject *posobj;
776 0 : int whence = 0;
777 :
778 0 : if (self->fd < 0)
779 0 : return err_closed();
780 :
781 0 : if (!PyArg_ParseTuple(args, "O|i", &posobj, &whence))
782 0 : return NULL;
783 :
784 0 : return portable_lseek(self->fd, posobj, whence);
785 : }
786 :
787 : static PyObject *
788 0 : fileio_tell(fileio *self, PyObject *args)
789 : {
790 0 : if (self->fd < 0)
791 0 : return err_closed();
792 :
793 0 : return portable_lseek(self->fd, NULL, 1);
794 : }
795 :
796 : #ifdef HAVE_FTRUNCATE
797 : static PyObject *
798 0 : fileio_truncate(fileio *self, PyObject *args)
799 : {
800 0 : 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 0 : fd = self->fd;
808 0 : if (fd < 0)
809 0 : return err_closed();
810 0 : if (!self->writable)
811 0 : return err_mode("writing");
812 :
813 0 : if (!PyArg_ParseTuple(args, "|O", &posobj))
814 0 : return NULL;
815 :
816 0 : if (posobj == Py_None || posobj == NULL) {
817 : /* Get the current position. */
818 0 : posobj = portable_lseek(fd, NULL, 1);
819 0 : if (posobj == NULL)
820 0 : return NULL;
821 : }
822 : else {
823 0 : 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 0 : pos = PyLong_AsLong(posobj);
876 : #endif
877 0 : if (PyErr_Occurred()){
878 0 : Py_DECREF(posobj);
879 0 : return NULL;
880 : }
881 :
882 : Py_BEGIN_ALLOW_THREADS
883 0 : errno = 0;
884 0 : ret = ftruncate(fd, pos);
885 : Py_END_ALLOW_THREADS
886 :
887 : #endif /* !MS_WINDOWS */
888 :
889 0 : if (ret != 0) {
890 0 : Py_DECREF(posobj);
891 0 : PyErr_SetFromErrno(PyExc_IOError);
892 0 : return NULL;
893 : }
894 :
895 0 : return posobj;
896 : }
897 : #endif /* HAVE_FTRUNCATE */
898 :
899 : static char *
900 0 : mode_string(fileio *self)
901 : {
902 0 : if (self->appending) {
903 0 : if (self->readable)
904 0 : return "ab+";
905 : else
906 0 : return "ab";
907 : }
908 0 : else if (self->readable) {
909 0 : if (self->writable)
910 0 : return "rb+";
911 : else
912 0 : return "rb";
913 : }
914 : else
915 0 : return "wb";
916 : }
917 :
918 : static PyObject *
919 0 : fileio_repr(fileio *self)
920 : {
921 : PyObject *nameobj, *res;
922 :
923 0 : if (self->fd < 0)
924 0 : return PyString_FromFormat("<_io.FileIO [closed]>");
925 :
926 0 : nameobj = PyObject_GetAttrString((PyObject *) self, "name");
927 0 : if (nameobj == NULL) {
928 0 : if (PyErr_ExceptionMatches(PyExc_AttributeError))
929 0 : PyErr_Clear();
930 : else
931 0 : return NULL;
932 0 : res = PyString_FromFormat("<_io.FileIO fd=%d mode='%s'>",
933 : self->fd, mode_string(self));
934 : }
935 : else {
936 0 : PyObject *repr = PyObject_Repr(nameobj);
937 0 : Py_DECREF(nameobj);
938 0 : if (repr == NULL)
939 0 : return NULL;
940 0 : res = PyString_FromFormat("<_io.FileIO name=%s mode='%s'>",
941 0 : PyString_AS_STRING(repr),
942 : mode_string(self));
943 0 : Py_DECREF(repr);
944 : }
945 0 : return res;
946 : }
947 :
948 : static PyObject *
949 0 : fileio_isatty(fileio *self)
950 : {
951 : long res;
952 :
953 0 : if (self->fd < 0)
954 0 : return err_closed();
955 : Py_BEGIN_ALLOW_THREADS
956 0 : res = isatty(self->fd);
957 : Py_END_ALLOW_THREADS
958 0 : 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 *
1065 0 : get_closed(fileio *self, void *closure)
1066 : {
1067 0 : return PyBool_FromLong((long)(self->fd < 0));
1068 : }
1069 :
1070 : static PyObject *
1071 0 : get_closefd(fileio *self, void *closure)
1072 : {
1073 0 : return PyBool_FromLong((long)(self->closefd));
1074 : }
1075 :
1076 : static PyObject *
1077 0 : get_mode(fileio *self, void *closure)
1078 : {
1079 0 : 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 : };
|