blob: 9ab27c12b644a0c6932feedf29bef737150356b4 [file] [log] [blame]
Guido van Rossuma9e20242007-03-08 00:43:48 +00001/* Author: Daniel Stutzbach */
2
3#define PY_SSIZE_T_CLEAN
4#include "Python.h"
5#include <sys/types.h>
6#include <sys/stat.h>
7#include <fcntl.h>
8#include <stddef.h> /* For offsetof */
9
10/*
11 * Known likely problems:
12 *
13 * - Files larger then 2**32-1
14 * - Files with unicode filenames
15 * - Passing numbers greater than 2**32-1 when an integer is expected
16 * - Making it work on Windows and other oddball platforms
17 *
18 * To Do:
19 *
20 * - autoconfify header file inclusion
21 * - Make the ABC RawIO type and inherit from it.
22 *
23 * Unanswered questions:
24 *
25 * - Add mode, name, and closed properties a la Python 2 file objects?
26 * - Do we need a (*close)() in the struct like Python 2 file objects,
27 * for not-quite-ordinary-file objects?
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
37typedef struct {
38 PyObject_HEAD
39 int fd;
40 int own_fd; /* 1 means we should close fd */
41 int readable;
42 int writable;
43 int seekable; /* -1 means unknown */
44 PyObject *weakreflist;
45} PyFileIOObject;
46
47#define PyFileIO_Check(op) (PyObject_TypeCheck((op), &PyFileIO_Type))
48
49/* Note: if this function is changed so that it can return a true value,
50 * then we need a separate function for __exit__
51 */
52static PyObject *
53fileio_close(PyFileIOObject *self)
54{
55 if (self->fd >= 0) {
56 Py_BEGIN_ALLOW_THREADS
57 errno = 0;
58 close(self->fd);
59 Py_END_ALLOW_THREADS
60 if (errno < 0) {
61 PyErr_SetFromErrno(PyExc_IOError);
62 return NULL;
63 }
64 self->fd = -1;
65 }
66
67 Py_RETURN_NONE;
68}
69
70static PyObject *
71fileio_new(PyTypeObject *type, PyObject *args, PyObject *kews)
72{
73 PyFileIOObject *self;
74
75 assert(type != NULL && type->tp_alloc != NULL);
76
77 self = (PyFileIOObject *) type->tp_alloc(type, 0);
78 if (self != NULL) {
79 self->fd = -1;
80 self->weakreflist = NULL;
81 self->own_fd = 1;
82 self->seekable = -1;
83 }
84
85 return (PyObject *) self;
86}
87
88/* On Unix, open will succeed for directories.
89 In Python, there should be no file objects referring to
90 directories, so we need a check. */
91
92static int
93dircheck(PyFileIOObject* self)
94{
95#if defined(HAVE_FSTAT) && defined(S_IFDIR) && defined(EISDIR)
96 struct stat buf;
97 if (self->fd < 0)
98 return 0;
99 if (fstat(self->fd, &buf) == 0 && S_ISDIR(buf.st_mode)) {
100#ifdef HAVE_STRERROR
101 char *msg = strerror(EISDIR);
102#else
103 char *msg = "Is a directory";
104#endif
105 PyObject *exc;
106 PyObject *closeresult = fileio_close(self);
107 Py_DECREF(closeresult);
108
109 exc = PyObject_CallFunction(PyExc_IOError, "(is)",
110 EISDIR, msg);
111 PyErr_SetObject(PyExc_IOError, exc);
112 Py_XDECREF(exc);
113 return -1;
114 }
115#endif
116 return 0;
117}
118
119
120static int
121fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
122{
123 PyFileIOObject *self = (PyFileIOObject *) oself;
124 static char *kwlist[] = {"filename", "mode", NULL};
125 char *name = NULL;
126 char *mode = "r";
127 char *s;
128 int wideargument = 0;
129 int ret = 0;
130 int rwa = 0, plus = 0, append = 0;
131 int flags = 0;
132
133 assert(PyFileIO_Check(oself));
134 if (self->fd >= 0)
135 {
136 /* Have to close the existing file first. */
137 PyObject *closeresult = fileio_close(self);
138 if (closeresult == NULL)
139 return -1;
140 Py_DECREF(closeresult);
141 }
142
143#ifdef Py_WIN_WIDE_FILENAMES
144 if (GetVersion() < 0x80000000) { /* On NT, so wide API available */
145 PyObject *po;
146 if (PyArg_ParseTupleAndKeywords(args, kwds, "U|s:fileio",
147 kwlist, &po, &mode)) {
148 wideargument = 1;
149 } else {
150 /* Drop the argument parsing error as narrow
151 strings are also valid. */
152 PyErr_Clear();
153 }
154
155 PyErr_SetString(PyExc_NotImplementedError,
156 "Windows wide filenames are not yet supported");
157 goto error;
158 }
159#endif
160
161 if (!wideargument) {
162 if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|s:fileio",
163 kwlist,
164 Py_FileSystemDefaultEncoding,
165 &name, &mode))
166 goto error;
167 }
168
169 self->readable = self->writable = 0;
170 s = mode;
171 while (*s) {
172 switch (*s++) {
173 case 'r':
174 if (rwa) {
175 bad_mode:
176 PyErr_SetString(PyExc_ValueError,
177 "Must have exactly one of read/write/append mode");
178 goto error;
179 }
180 rwa = 1;
181 self->readable = 1;
182 break;
183 case 'w':
184 if (rwa)
185 goto bad_mode;
186 rwa = 1;
187 self->writable = 1;
188 flags |= O_CREAT | O_TRUNC;
189 break;
190 case 'a':
191 if (rwa)
192 goto bad_mode;
193 rwa = 1;
194 self->writable = 1;
195 flags |= O_CREAT;
196 append = 1;
197 break;
198 case '+':
199 if (plus)
200 goto bad_mode;
201 self->readable = self->writable = 1;
202 plus = 1;
203 break;
204 default:
205 PyErr_Format(PyExc_ValueError,
206 "invalid mode: %.200s", mode);
207 goto error;
208 }
209 }
210
211 if (!rwa)
212 goto bad_mode;
213
214 if (self->readable && self->writable)
215 flags |= O_RDWR;
216 else if (self->readable)
217 flags |= O_RDONLY;
218 else
219 flags |= O_WRONLY;
220
221#ifdef O_BINARY
222 flags |= O_BINARY;
223#endif
224
225 Py_BEGIN_ALLOW_THREADS
226 errno = 0;
227 self->fd = open(name, flags, 0666);
228 Py_END_ALLOW_THREADS
229 if (self->fd < 0 || dircheck(self) < 0) {
230 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
231 goto error;
232 }
233
234 goto done;
235
236 error:
237 ret = -1;
238
239 done:
240 PyMem_Free(name);
241 return ret;
242}
243
244static void
245fileio_dealloc(PyFileIOObject *self)
246{
247 if (self->weakreflist != NULL)
248 PyObject_ClearWeakRefs((PyObject *) self);
249
250 if (self->fd >= 0 && self->own_fd) {
251 PyObject *closeresult = fileio_close(self);
252 if (closeresult == NULL) {
253#ifdef HAVE_STRERROR
254 PySys_WriteStderr("close failed: [Errno %d] %s\n", errno, strerror(errno));
255#else
256 PySys_WriteStderr("close failed: [Errno %d]\n", errno);
257#endif
258 } else
259 Py_DECREF(closeresult);
260 }
261
262 self->ob_type->tp_free((PyObject *)self);
263}
264
265static PyObject *
266err_closed(void)
267{
268 PyErr_SetString(PyExc_ValueError, "I/O operation on closed file");
269 return NULL;
270}
271
272static PyObject *
273fileio_fileno(PyFileIOObject *self)
274{
275 if (self->fd < 0)
276 return err_closed();
277 return PyInt_FromLong((long) self->fd);
278}
279
280static PyObject *
281fileio_readable(PyFileIOObject *self)
282{
283 if (self->fd < 0)
284 return err_closed();
285 return PyInt_FromLong((long) self->readable);
286}
287
288static PyObject *
289fileio_writable(PyFileIOObject *self)
290{
291 if (self->fd < 0)
292 return err_closed();
293 return PyInt_FromLong((long) self->writable);
294}
295
296static PyObject *
297fileio_seekable(PyFileIOObject *self)
298{
299 if (self->fd < 0)
300 return err_closed();
301 if (self->seekable < 0) {
302 int ret;
303 Py_BEGIN_ALLOW_THREADS
304 ret = lseek(self->fd, 0, SEEK_CUR);
305 Py_END_ALLOW_THREADS
306 if (ret < 0)
307 self->seekable = 0;
308 else
309 self->seekable = 1;
310 }
311 return PyInt_FromLong((long) self->seekable);
312}
313
314static PyObject *
315fileio_readinto(PyFileIOObject *self, PyObject *args)
316{
317 char *ptr;
318 Py_ssize_t n;
319
320 if (self->fd < 0)
321 return err_closed();
322 if (!PyArg_ParseTuple(args, "w#", &ptr, &n))
323 return NULL;
324
325 Py_BEGIN_ALLOW_THREADS
326 errno = 0;
327 n = read(self->fd, ptr, n);
328 Py_END_ALLOW_THREADS
329 if (n < 0) {
330 if (errno == EAGAIN)
331 Py_RETURN_NONE;
332 PyErr_SetFromErrno(PyExc_IOError);
333 return NULL;
334 }
335
336 return PyInt_FromLong(n);
337}
338
339static PyObject *
340fileio_read(PyFileIOObject *self, PyObject *args)
341{
342 char *ptr;
343 Py_ssize_t n, size;
344 PyObject *bytes;
345
346 if (self->fd < 0)
347 return err_closed();
348
349 if (!PyArg_ParseTuple(args, "i", &size))
350 return NULL;
351
352 bytes = PyBytes_FromStringAndSize(NULL, size);
353 if (bytes == NULL)
354 return NULL;
355 ptr = PyBytes_AsString(bytes);
356
357 Py_BEGIN_ALLOW_THREADS
358 errno = 0;
359 n = read(self->fd, ptr, size);
360 Py_END_ALLOW_THREADS
361
362 if (n < 0) {
363 if (errno == EAGAIN)
364 Py_RETURN_NONE;
365 PyErr_SetFromErrno(PyExc_IOError);
366 return NULL;
367 }
368
369 if (n != size) {
370 if (PyBytes_Resize(bytes, n) < 0) {
371 Py_DECREF(bytes);
372 return NULL;
373 }
374 }
375
376 return (PyObject *) bytes;
377}
378
379static PyObject *
380fileio_write(PyFileIOObject *self, PyObject *args)
381{
382 Py_ssize_t n;
383 char *ptr;
384
385 if (self->fd < 0)
386 return err_closed();
387 if (!PyArg_ParseTuple(args, "s#", &ptr, &n))
388 return NULL;
389
390 Py_BEGIN_ALLOW_THREADS
391 errno = 0;
392 n = write(self->fd, ptr, n);
393 Py_END_ALLOW_THREADS
394
395 if (n < 0) {
396 if (errno == EAGAIN)
397 Py_RETURN_NONE;
398 PyErr_SetFromErrno(PyExc_IOError);
399 return NULL;
400 }
401
402 return PyInt_FromLong(n);
403}
404
405static PyObject *
406fileio_seek(PyFileIOObject *self, PyObject *args)
407{
408 Py_ssize_t offset;
409 Py_ssize_t whence = 0;
410
411 if (self->fd < 0)
412 return err_closed();
413
414 if (!PyArg_ParseTuple(args, "i|i", &offset, &whence))
415 return NULL;
416
417 Py_BEGIN_ALLOW_THREADS
418 errno = 0;
419 offset = lseek(self->fd, offset, whence);
420 Py_END_ALLOW_THREADS
421
422 if (offset < 0) {
423 PyErr_SetFromErrno(PyExc_IOError);
424 return NULL;
425 }
426
427 Py_RETURN_NONE;
428}
429
430static PyObject *
431fileio_tell(PyFileIOObject *self, PyObject *args)
432{
433 Py_ssize_t offset;
434
435 if (self->fd < 0)
436 return err_closed();
437
438 Py_BEGIN_ALLOW_THREADS
439 errno = 0;
440 offset = lseek(self->fd, 0, SEEK_CUR);
441 Py_END_ALLOW_THREADS
442
443 if (offset < 0) {
444 PyErr_SetFromErrno(PyExc_IOError);
445 return NULL;
446 }
447
448 return PyInt_FromLong(offset);
449}
450
451#ifdef HAVE_FTRUNCATE
452static PyObject *
453fileio_truncate(PyFileIOObject *self, PyObject *args)
454{
455 Py_ssize_t length;
456 int ret;
457
458 if (self->fd < 0)
459 return err_closed();
460
461 /* Setup default value */
462 Py_BEGIN_ALLOW_THREADS
463 errno = 0;
464 length = lseek(self->fd, 0, SEEK_CUR);
465 Py_END_ALLOW_THREADS
466
467 if (length < 0) {
468 PyErr_SetFromErrno(PyExc_IOError);
469 return NULL;
470 }
471
472 if (!PyArg_ParseTuple(args, "|i", &length))
473 return NULL;
474
475#ifdef MS_WINDOWS
476 /* MS _chsize doesn't work if newsize doesn't fit in 32 bits,
477 so don't even try using it. */
478 {
479 HANDLE hFile;
480 Py_ssize_t initialpos;
481
482 /* Have to move current pos to desired endpoint on Windows. */
483 Py_BEGIN_ALLOW_THREADS
484 errno = 0;
485 ret = _portable_fseek(f->f_fp, newsize, SEEK_SET) != 0;
486 Py_END_ALLOW_THREADS
487 if (ret)
488 goto onioerror;
489
490 /* Truncate. Note that this may grow the file! */
491 Py_BEGIN_ALLOW_THREADS
492 errno = 0;
493 hFile = (HANDLE)_get_osfhandle(fileno(f->f_fp));
494 ret = hFile == (HANDLE)-1;
495 if (ret == 0) {
496 ret = SetEndOfFile(hFile) == 0;
497 if (ret)
498 errno = EACCES;
499 }
500 Py_END_ALLOW_THREADS
501 if (ret)
502 goto onioerror;
503 }
504#else
505 Py_BEGIN_ALLOW_THREADS
506 errno = 0;
507 ret = ftruncate(self->fd, length);
508 Py_END_ALLOW_THREADS
509#endif /* !MS_WINDOWS */
510
511 if (ret < 0) {
512 onioerror:
513 PyErr_SetFromErrno(PyExc_IOError);
514 return NULL;
515 }
516
517 /* Return to initial position */
518 Py_BEGIN_ALLOW_THREADS
519 errno = 0;
520 ret = lseek(self->fd, length, SEEK_SET);
521 Py_END_ALLOW_THREADS
522 if (ret < 0)
523 goto onioerror;
524
525 Py_RETURN_NONE;
526}
527#endif
528
529static PyObject *
530fileio_repr(PyFileIOObject *self)
531{
532 PyObject *ret = NULL;
533
534 ret = PyString_FromFormat("<%s file at %p>",
535 self->fd < 0 ? "closed" : "open",
536 self);
537 return ret;
538}
539
540static PyObject *
541fileio_isatty(PyFileIOObject *self)
542{
543 long res;
544
545 if (self->fd < 0)
546 return err_closed();
547 Py_BEGIN_ALLOW_THREADS
548 res = isatty(self->fd);
549 Py_END_ALLOW_THREADS
550 return PyBool_FromLong(res);
551}
552
553static PyObject *
554fileio_self(PyFileIOObject *self)
555{
556 if (self->fd < 0)
557 return err_closed();
558 Py_INCREF(self);
559 return (PyObject *)self;
560}
561
562PyDoc_STRVAR(fileio_doc,
563"file(name: str[, mode: str]) -> file IO object\n"
564"\n"
565"Open a file. The mode can be 'r', 'w' or 'a' for reading (default),\n"
566"writing or appending. The file will be created if it doesn't exist\n"
567"when opened for writing or appending; it will be truncated when\n"
568"opened for writing. Add a '+' to the mode to allow simultaneous\n"
569"reading and writing.");
570
571PyDoc_STRVAR(read_doc,
572"read(size: int) -> bytes. read at most size bytes, returned as bytes.\n"
573"\n"
574"Only makes one system call, so less data may be returned than requested\n"
575"In non-blocking mode, returns None if no data is available. On\n"
576"end-of-file, returns 0.");
577
578PyDoc_STRVAR(write_doc,
579"write(b: bytes) -> int. Write bytes b to file, return number written.\n"
580"\n"
581"Only makes one system call, so not all of the data may be written.\n"
582"The number of bytes actually written is returned.");
583
584PyDoc_STRVAR(fileno_doc,
585"fileno() -> int. \"file descriptor\".\n"
586"\n"
587"This is needed for lower-level file interfaces, such the fcntl module.");
588
589PyDoc_STRVAR(seek_doc,
590"seek(offset: int[, whence: int]) -> None. Move to new file position.\n"
591"\n"
592"Argument offset is a byte count. Optional argument whence defaults to\n"
593"0 (offset from start of file, offset should be >= 0); other values are 1\n"
594"(move relative to current position, positive or negative), and 2 (move\n"
595"relative to end of file, usually negative, although many platforms allow\n"
596"seeking beyond the end of a file)."
597"\n"
598"Note that not all file objects are seekable.");
599
600PyDoc_STRVAR(truncate_doc,
601"truncate([size: int]) -> None. Truncate the file to at most size bytes.\n"
602"\n"
603"Size defaults to the current file position, as returned by tell().");
604
605PyDoc_STRVAR(tell_doc,
606"tell() -> int. Current file position");
607
608PyDoc_STRVAR(readinto_doc,
609"readinto() -> Undocumented. Don't use this; it may go away.");
610
611PyDoc_STRVAR(close_doc,
612"close() -> None. Close the file.\n"
613"\n"
614"A closed file cannot be used for further I/O operations. close() may be\n"
615"called more than once without error. Changes the fileno to -1.");
616
617PyDoc_STRVAR(isatty_doc,
618"isatty() -> bool. True if the file is connected to a tty device.");
619
620PyDoc_STRVAR(enter_doc,
621"__enter__() -> self.");
622
623PyDoc_STRVAR(exit_doc,
624"__exit__(*excinfo) -> None. Closes the file.");
625
626PyDoc_STRVAR(seekable_doc,
627"seekable() -> bool. True if file supports random-access.");
628
629PyDoc_STRVAR(readable_doc,
630"readable() -> bool. True if file was opened in a read mode.");
631
632PyDoc_STRVAR(writable_doc,
633"writable() -> bool. True if file was opened in a write mode.");
634
635static PyMethodDef fileio_methods[] = {
636 {"read", (PyCFunction)fileio_read, METH_VARARGS, read_doc},
637 {"readinto", (PyCFunction)fileio_readinto, METH_VARARGS, readinto_doc},
638 {"write", (PyCFunction)fileio_write, METH_VARARGS, write_doc},
639 {"seek", (PyCFunction)fileio_seek, METH_VARARGS, seek_doc},
640 {"tell", (PyCFunction)fileio_tell, METH_VARARGS, tell_doc},
641 {"truncate", (PyCFunction)fileio_truncate, METH_VARARGS, truncate_doc},
642 {"close", (PyCFunction)fileio_close, METH_NOARGS, close_doc},
643 {"seekable", (PyCFunction)fileio_seekable, METH_NOARGS, seekable_doc},
644 {"readable", (PyCFunction)fileio_readable, METH_NOARGS, readable_doc},
645 {"writable", (PyCFunction)fileio_writable, METH_NOARGS, writable_doc},
646 {"fileno", (PyCFunction)fileio_fileno, METH_NOARGS, fileno_doc},
647 {"isatty", (PyCFunction)fileio_isatty, METH_NOARGS, isatty_doc},
648 {"__enter__",(PyCFunction)fileio_self, METH_NOARGS, enter_doc},
649 {"__exit__", (PyCFunction)fileio_close, METH_VARARGS, exit_doc},
650 {NULL, NULL} /* sentinel */
651};
652
653PyTypeObject PyFileIO_Type = {
654 PyObject_HEAD_INIT(&PyType_Type)
655 0,
656 "FileIO",
657 sizeof(PyFileIOObject),
658 0,
659 (destructor)fileio_dealloc, /* tp_dealloc */
660 0, /* tp_print */
661 0, /* tp_getattr */
662 0, /* tp_setattr */
663 0, /* tp_compare */
664 (reprfunc)fileio_repr, /* tp_repr */
665 0, /* tp_as_number */
666 0, /* tp_as_sequence */
667 0, /* tp_as_mapping */
668 0, /* tp_hash */
669 0, /* tp_call */
670 0, /* tp_str */
671 PyObject_GenericGetAttr, /* tp_getattro */
672 0, /* tp_setattro */
673 0, /* tp_as_buffer */
674 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
675 fileio_doc, /* tp_doc */
676 0, /* tp_traverse */
677 0, /* tp_clear */
678 0, /* tp_richcompare */
679 offsetof(PyFileIOObject, weakreflist), /* tp_weaklistoffset */
680 0, /* tp_iter */
681 0, /* tp_iternext */
682 fileio_methods, /* tp_methods */
683 0, /* tp_members */
684 0, /* tp_getset */
685 0, /* tp_base */
686 0, /* tp_dict */
687 0, /* tp_descr_get */
688 0, /* tp_descr_set */
689 0, /* tp_dictoffset */
690 fileio_init, /* tp_init */
691 PyType_GenericAlloc, /* tp_alloc */
692 fileio_new, /* tp_new */
693 PyObject_Del, /* tp_free */
694};
695
696static PyMethodDef module_methods[] = {
697 {NULL, NULL}
698};
699
700PyMODINIT_FUNC
701init_fileio(void)
702{
703 PyObject *m; /* a module object */
704
705 m = Py_InitModule3("_fileio", module_methods,
706 "Fast implementation of io.FileIO.");
707 if (m == NULL)
708 return;
709 if (PyType_Ready(&PyFileIO_Type) < 0)
710 return;
711 Py_INCREF(&PyFileIO_Type);
712 PyModule_AddObject(m, "_FileIO", (PyObject *) &PyFileIO_Type);
713}