| /*********************************************************** |
| Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam, |
| The Netherlands. |
| |
| All Rights Reserved |
| |
| Permission to use, copy, modify, and distribute this software and its |
| documentation for any purpose and without fee is hereby granted, |
| provided that the above copyright notice appear in all copies and that |
| both that copyright notice and this permission notice appear in |
| supporting documentation, and that the names of Stichting Mathematisch |
| Centrum or CWI or Corporation for National Research Initiatives or |
| CNRI not be used in advertising or publicity pertaining to |
| distribution of the software without specific, written prior |
| permission. |
| |
| While CWI is the initial source for this software, a modified version |
| is made available by the Corporation for National Research Initiatives |
| (CNRI) at the Internet address ftp://ftp.python.org. |
| |
| STICHTING MATHEMATISCH CENTRUM AND CNRI DISCLAIM ALL WARRANTIES WITH |
| REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF |
| MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH |
| CENTRUM OR CNRI BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL |
| DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| PERFORMANCE OF THIS SOFTWARE. |
| |
| ******************************************************************/ |
| |
| /* File object implementation */ |
| |
| #include "allobjects.h" |
| #include "modsupport.h" |
| #include "structmember.h" |
| #include "ceval.h" |
| |
| #ifdef HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| |
| #ifdef THINK_C |
| #define HAVE_FOPENRF |
| #endif |
| #ifdef __MWERKS__ |
| /* Mwerks fopen() doesn't always set errno */ |
| #define NO_FOPEN_ERRNO |
| #endif |
| |
| #define BUF(v) GETSTRINGVALUE((stringobject *)v) |
| |
| #include <errno.h> |
| |
| typedef struct { |
| OB_HEAD |
| FILE *f_fp; |
| object *f_name; |
| object *f_mode; |
| int (*f_close) PROTO((FILE *)); |
| int f_softspace; /* Flag used by 'print' command */ |
| } fileobject; |
| |
| FILE * |
| getfilefile(f) |
| object *f; |
| { |
| if (f == NULL || !is_fileobject(f)) |
| return NULL; |
| else |
| return ((fileobject *)f)->f_fp; |
| } |
| |
| object * |
| getfilename(f) |
| object *f; |
| { |
| if (f == NULL || !is_fileobject(f)) |
| return NULL; |
| else |
| return ((fileobject *)f)->f_name; |
| } |
| |
| object * |
| newopenfileobject(fp, name, mode, close) |
| FILE *fp; |
| char *name; |
| char *mode; |
| int (*close) FPROTO((FILE *)); |
| { |
| fileobject *f = NEWOBJ(fileobject, &Filetype); |
| if (f == NULL) |
| return NULL; |
| f->f_fp = NULL; |
| f->f_name = newstringobject(name); |
| f->f_mode = newstringobject(mode); |
| f->f_close = close; |
| f->f_softspace = 0; |
| if (f->f_name == NULL || f->f_mode == NULL) { |
| DECREF(f); |
| return NULL; |
| } |
| f->f_fp = fp; |
| return (object *) f; |
| } |
| |
| object * |
| newfileobject(name, mode) |
| char *name, *mode; |
| { |
| extern int fclose PROTO((FILE *)); |
| fileobject *f; |
| f = (fileobject *) newopenfileobject((FILE *)NULL, name, mode, fclose); |
| if (f == NULL) |
| return NULL; |
| #ifdef HAVE_FOPENRF |
| if (*mode == '*') { |
| FILE *fopenRF(); |
| f->f_fp = fopenRF(name, mode+1); |
| } |
| else |
| #endif |
| { |
| BGN_SAVE |
| f->f_fp = fopen(name, mode); |
| END_SAVE |
| } |
| if (f->f_fp == NULL) { |
| #ifdef NO_FOPEN_ERRNO |
| if ( errno == 0 ) { |
| err_setstr(IOError, "Cannot open file"); |
| DECREF(f); |
| return NULL; |
| } |
| #endif |
| err_errno(IOError); |
| DECREF(f); |
| return NULL; |
| } |
| return (object *)f; |
| } |
| |
| void |
| setfilebufsize(f, bufsize) |
| object *f; |
| int bufsize; |
| { |
| if (bufsize >= 0) { |
| #ifdef HAVE_SETVBUF |
| int type; |
| switch (bufsize) { |
| case 0: |
| type = _IONBF; |
| break; |
| case 1: |
| type = _IOLBF; |
| bufsize = BUFSIZ; |
| break; |
| default: |
| type = _IOFBF; |
| } |
| setvbuf(((fileobject *)f)->f_fp, (char *)NULL, type, bufsize); |
| #endif /* HAVE_SETVBUF */ |
| } |
| } |
| |
| static object * |
| err_closed() |
| { |
| err_setstr(ValueError, "I/O operation on closed file"); |
| return NULL; |
| } |
| |
| /* Methods */ |
| |
| static void |
| file_dealloc(f) |
| fileobject *f; |
| { |
| if (f->f_fp != NULL && f->f_close != NULL) { |
| BGN_SAVE |
| (*f->f_close)(f->f_fp); |
| END_SAVE |
| } |
| if (f->f_name != NULL) |
| DECREF(f->f_name); |
| if (f->f_mode != NULL) |
| DECREF(f->f_mode); |
| free((char *)f); |
| } |
| |
| static object * |
| file_repr(f) |
| fileobject *f; |
| { |
| char buf[300]; |
| sprintf(buf, "<%s file '%.256s', mode '%.10s' at %lx>", |
| f->f_fp == NULL ? "closed" : "open", |
| getstringvalue(f->f_name), |
| getstringvalue(f->f_mode), |
| (long)f); |
| return newstringobject(buf); |
| } |
| |
| static object * |
| file_close(f, args) |
| fileobject *f; |
| object *args; |
| { |
| int sts = 0; |
| if (!getnoarg(args)) |
| return NULL; |
| if (f->f_fp != NULL) { |
| if (f->f_close != NULL) { |
| BGN_SAVE |
| errno = 0; |
| sts = (*f->f_close)(f->f_fp); |
| END_SAVE |
| } |
| f->f_fp = NULL; |
| } |
| if (sts == EOF) |
| return err_errno(IOError); |
| if (sts != 0) |
| return newintobject((long)sts); |
| INCREF(None); |
| return None; |
| } |
| |
| static object * |
| file_seek(f, args) |
| fileobject *f; |
| object *args; |
| { |
| long offset; |
| int whence; |
| int ret; |
| |
| if (f->f_fp == NULL) |
| return err_closed(); |
| whence = 0; |
| if (!getargs(args, "l", &offset)) { |
| err_clear(); |
| if (!getargs(args, "(li)", &offset, &whence)) |
| return NULL; |
| } |
| BGN_SAVE |
| errno = 0; |
| ret = fseek(f->f_fp, offset, whence); |
| END_SAVE |
| if (ret != 0) { |
| err_errno(IOError); |
| clearerr(f->f_fp); |
| return NULL; |
| } |
| INCREF(None); |
| return None; |
| } |
| |
| #ifdef HAVE_FTRUNCATE |
| static object * |
| file_truncate(f, args) |
| fileobject *f; |
| object *args; |
| { |
| long newsize; |
| int ret; |
| |
| if (f->f_fp == NULL) |
| return err_closed(); |
| if (!getargs(args, "l", &newsize)) { |
| err_clear(); |
| if (!getnoarg(args)) |
| return NULL; |
| BGN_SAVE |
| errno = 0; |
| newsize = ftell(f->f_fp); /* default to current position*/ |
| END_SAVE |
| if (newsize == -1L) { |
| err_errno(IOError); |
| clearerr(f->f_fp); |
| return NULL; |
| } |
| } |
| BGN_SAVE |
| errno = 0; |
| ret = fflush(f->f_fp); |
| END_SAVE |
| if (ret == 0) { |
| BGN_SAVE |
| errno = 0; |
| ret = ftruncate(fileno(f->f_fp), newsize); |
| END_SAVE |
| } |
| if (ret != 0) { |
| err_errno(IOError); |
| clearerr(f->f_fp); |
| return NULL; |
| } |
| INCREF(None); |
| return None; |
| } |
| #endif /* HAVE_FTRUNCATE */ |
| |
| static object * |
| file_tell(f, args) |
| fileobject *f; |
| object *args; |
| { |
| long offset; |
| if (f->f_fp == NULL) |
| return err_closed(); |
| if (!getnoarg(args)) |
| return NULL; |
| BGN_SAVE |
| errno = 0; |
| offset = ftell(f->f_fp); |
| END_SAVE |
| if (offset == -1L) { |
| err_errno(IOError); |
| clearerr(f->f_fp); |
| return NULL; |
| } |
| return newintobject(offset); |
| } |
| |
| static object * |
| file_fileno(f, args) |
| fileobject *f; |
| object *args; |
| { |
| if (f->f_fp == NULL) |
| return err_closed(); |
| if (!getnoarg(args)) |
| return NULL; |
| return newintobject((long) fileno(f->f_fp)); |
| } |
| |
| static object * |
| file_flush(f, args) |
| fileobject *f; |
| object *args; |
| { |
| int res; |
| |
| if (f->f_fp == NULL) |
| return err_closed(); |
| if (!getnoarg(args)) |
| return NULL; |
| BGN_SAVE |
| errno = 0; |
| res = fflush(f->f_fp); |
| END_SAVE |
| if (res != 0) { |
| err_errno(IOError); |
| clearerr(f->f_fp); |
| return NULL; |
| } |
| INCREF(None); |
| return None; |
| } |
| |
| static object * |
| file_isatty(f, args) |
| fileobject *f; |
| object *args; |
| { |
| long res; |
| if (f->f_fp == NULL) |
| return err_closed(); |
| if (!getnoarg(args)) |
| return NULL; |
| BGN_SAVE |
| res = isatty((int)fileno(f->f_fp)); |
| END_SAVE |
| return newintobject(res); |
| } |
| |
| static object * |
| file_read(f, args) |
| fileobject *f; |
| object *args; |
| { |
| int n, n1, n2, n3; |
| object *v; |
| |
| if (f->f_fp == NULL) |
| return err_closed(); |
| if (args == NULL) |
| n = -1; |
| else { |
| if (!getargs(args, "i", &n)) |
| return NULL; |
| } |
| n2 = n >= 0 ? n : BUFSIZ; |
| v = newsizedstringobject((char *)NULL, n2); |
| if (v == NULL) |
| return NULL; |
| n1 = 0; |
| BGN_SAVE |
| for (;;) { |
| n3 = fread(BUF(v)+n1, 1, n2-n1, f->f_fp); |
| /* XXX Error check? */ |
| if (n3 == 0) |
| break; |
| n1 += n3; |
| if (n1 == n) |
| break; |
| if (n < 0) { |
| n2 = n1 + BUFSIZ; |
| RET_SAVE |
| if (resizestring(&v, n2) < 0) |
| return NULL; |
| RES_SAVE |
| } |
| } |
| END_SAVE |
| if (n1 != n2) |
| resizestring(&v, n1); |
| return v; |
| } |
| |
| /* Internal routine to get a line. |
| Size argument interpretation: |
| > 0: max length; |
| = 0: read arbitrary line; |
| < 0: strip trailing '\n', raise EOFError if EOF reached immediately |
| */ |
| |
| static object * |
| getline(f, n) |
| fileobject *f; |
| int n; |
| { |
| register FILE *fp; |
| register int c; |
| register char *buf, *end; |
| int n1, n2; |
| object *v; |
| |
| fp = f->f_fp; |
| n2 = n > 0 ? n : 100; |
| v = newsizedstringobject((char *)NULL, n2); |
| if (v == NULL) |
| return NULL; |
| buf = BUF(v); |
| end = buf + n2; |
| |
| BGN_SAVE |
| for (;;) { |
| if ((c = getc(fp)) == EOF) { |
| clearerr(fp); |
| if (sigcheck()) { |
| RET_SAVE |
| DECREF(v); |
| return NULL; |
| } |
| if (n < 0 && buf == BUF(v)) { |
| RET_SAVE |
| DECREF(v); |
| err_setstr(EOFError, |
| "EOF when reading a line"); |
| return NULL; |
| } |
| break; |
| } |
| if ((*buf++ = c) == '\n') { |
| if (n < 0) |
| buf--; |
| break; |
| } |
| if (buf == end) { |
| if (n > 0) |
| break; |
| n1 = n2; |
| n2 += 1000; |
| RET_SAVE |
| if (resizestring(&v, n2) < 0) |
| return NULL; |
| RES_SAVE |
| buf = BUF(v) + n1; |
| end = BUF(v) + n2; |
| } |
| } |
| END_SAVE |
| |
| n1 = buf - BUF(v); |
| if (n1 != n2) |
| resizestring(&v, n1); |
| return v; |
| } |
| |
| /* External C interface */ |
| |
| object * |
| filegetline(f, n) |
| object *f; |
| int n; |
| { |
| if (f == NULL) { |
| err_badcall(); |
| return NULL; |
| } |
| if (!is_fileobject(f)) { |
| object *reader; |
| object *args; |
| object *result; |
| reader = getattr(f, "readline"); |
| if (reader == NULL) |
| return NULL; |
| if (n <= 0) |
| args = mkvalue("()"); |
| else |
| args = mkvalue("(i)", n); |
| if (args == NULL) { |
| DECREF(reader); |
| return NULL; |
| } |
| result = call_object(reader, args); |
| DECREF(reader); |
| DECREF(args); |
| if (result != NULL && !is_stringobject(result)) { |
| DECREF(result); |
| result = NULL; |
| err_setstr(TypeError, |
| "object.readline() returned non-string"); |
| } |
| if (n < 0 && result != NULL) { |
| char *s = getstringvalue(result); |
| int len = getstringsize(result); |
| if (len == 0) { |
| DECREF(result); |
| result = NULL; |
| err_setstr(EOFError, |
| "EOF when reading a line"); |
| } |
| else if (s[len-1] == '\n') { |
| if (result->ob_refcnt == 1) |
| resizestring(&result, len-1); |
| else { |
| object *v; |
| v = newsizedstringobject(s, len-1); |
| DECREF(result); |
| result = v; |
| } |
| } |
| } |
| return result; |
| } |
| if (((fileobject*)f)->f_fp == NULL) |
| return err_closed(); |
| return getline((fileobject *)f, n); |
| } |
| |
| /* Python method */ |
| |
| static object * |
| file_readline(f, args) |
| fileobject *f; |
| object *args; |
| { |
| int n; |
| |
| if (f->f_fp == NULL) |
| return err_closed(); |
| if (args == NULL) |
| n = 0; /* Unlimited */ |
| else { |
| if (!getintarg(args, &n)) |
| return NULL; |
| if (n == 0) |
| return newstringobject(""); |
| if (n < 0) |
| n = 0; |
| } |
| |
| return getline(f, n); |
| } |
| |
| static object * |
| file_readlines(f, args) |
| fileobject *f; |
| object *args; |
| { |
| object *list; |
| object *line; |
| |
| if (f->f_fp == NULL) |
| return err_closed(); |
| if (!getnoarg(args)) |
| return NULL; |
| if ((list = newlistobject(0)) == NULL) |
| return NULL; |
| for (;;) { |
| line = getline(f, 0); |
| if (line != NULL && getstringsize(line) == 0) { |
| DECREF(line); |
| break; |
| } |
| if (line == NULL || addlistitem(list, line) != 0) { |
| DECREF(list); |
| XDECREF(line); |
| return NULL; |
| } |
| DECREF(line); |
| } |
| return list; |
| } |
| |
| static object * |
| file_write(f, args) |
| fileobject *f; |
| object *args; |
| { |
| char *s; |
| int n, n2; |
| if (f->f_fp == NULL) |
| return err_closed(); |
| if (!getargs(args, "s#", &s, &n)) |
| return NULL; |
| f->f_softspace = 0; |
| BGN_SAVE |
| errno = 0; |
| n2 = fwrite(s, 1, n, f->f_fp); |
| END_SAVE |
| if (n2 != n) { |
| err_errno(IOError); |
| clearerr(f->f_fp); |
| return NULL; |
| } |
| INCREF(None); |
| return None; |
| } |
| |
| static object * |
| file_writelines(f, args) |
| fileobject *f; |
| object *args; |
| { |
| int i, n; |
| if (f->f_fp == NULL) |
| return err_closed(); |
| if (args == NULL || !is_listobject(args)) { |
| err_setstr(TypeError, |
| "writelines() requires list of strings"); |
| return NULL; |
| } |
| n = getlistsize(args); |
| f->f_softspace = 0; |
| BGN_SAVE |
| errno = 0; |
| for (i = 0; i < n; i++) { |
| object *line = getlistitem(args, i); |
| int len; |
| int nwritten; |
| if (!is_stringobject(line)) { |
| RET_SAVE |
| err_setstr(TypeError, |
| "writelines() requires list of strings"); |
| return NULL; |
| } |
| len = getstringsize(line); |
| nwritten = fwrite(getstringvalue(line), 1, len, f->f_fp); |
| if (nwritten != len) { |
| RET_SAVE |
| err_errno(IOError); |
| clearerr(f->f_fp); |
| return NULL; |
| } |
| } |
| END_SAVE |
| INCREF(None); |
| return None; |
| } |
| |
| static struct methodlist file_methods[] = { |
| {"close", (method)file_close, 0}, |
| {"flush", (method)file_flush, 0}, |
| {"fileno", (method)file_fileno, 0}, |
| {"isatty", (method)file_isatty, 0}, |
| {"read", (method)file_read, 0}, |
| {"readline", (method)file_readline, 0}, |
| {"readlines", (method)file_readlines, 0}, |
| {"seek", (method)file_seek, 0}, |
| #ifdef HAVE_FTRUNCATE |
| {"truncate", (method)file_truncate, 0}, |
| #endif |
| {"tell", (method)file_tell, 0}, |
| {"write", (method)file_write, 0}, |
| {"writelines", (method)file_writelines, 0}, |
| {NULL, NULL} /* sentinel */ |
| }; |
| |
| #define OFF(x) offsetof(fileobject, x) |
| |
| static struct memberlist file_memberlist[] = { |
| {"softspace", T_INT, OFF(f_softspace)}, |
| {"mode", T_OBJECT, OFF(f_mode), RO}, |
| {"name", T_OBJECT, OFF(f_name), RO}, |
| /* getattr(f, "closed") is implemented without this table */ |
| {"closed", T_INT, 0, RO}, |
| {NULL} /* Sentinel */ |
| }; |
| |
| static object * |
| file_getattr(f, name) |
| fileobject *f; |
| char *name; |
| { |
| object *res; |
| |
| res = findmethod(file_methods, (object *)f, name); |
| if (res != NULL) |
| return res; |
| err_clear(); |
| if (strcmp(name, "closed") == 0) |
| return newintobject((long)(f->f_fp == 0)); |
| return getmember((char *)f, file_memberlist, name); |
| } |
| |
| static int |
| file_setattr(f, name, v) |
| fileobject *f; |
| char *name; |
| object *v; |
| { |
| if (v == NULL) { |
| err_setstr(AttributeError, "can't delete file attributes"); |
| return -1; |
| } |
| return setmember((char *)f, file_memberlist, name, v); |
| } |
| |
| typeobject Filetype = { |
| OB_HEAD_INIT(&Typetype) |
| 0, |
| "file", |
| sizeof(fileobject), |
| 0, |
| (destructor)file_dealloc, /*tp_dealloc*/ |
| 0, /*tp_print*/ |
| (getattrfunc)file_getattr, /*tp_getattr*/ |
| (setattrfunc)file_setattr, /*tp_setattr*/ |
| 0, /*tp_compare*/ |
| (reprfunc)file_repr, /*tp_repr*/ |
| }; |
| |
| /* Interface for the 'soft space' between print items. */ |
| |
| int |
| softspace(f, newflag) |
| object *f; |
| int newflag; |
| { |
| int oldflag = 0; |
| if (f == NULL) { |
| /* Do nothing */ |
| } |
| else if (is_fileobject(f)) { |
| oldflag = ((fileobject *)f)->f_softspace; |
| ((fileobject *)f)->f_softspace = newflag; |
| } |
| else { |
| object *v; |
| v = getattr(f, "softspace"); |
| if (v == NULL) |
| err_clear(); |
| else { |
| if (is_intobject(v)) |
| oldflag = getintvalue(v); |
| DECREF(v); |
| } |
| v = newintobject((long)newflag); |
| if (v == NULL) |
| err_clear(); |
| else { |
| if (setattr(f, "softspace", v) != 0) |
| err_clear(); |
| DECREF(v); |
| } |
| } |
| return oldflag; |
| } |
| |
| /* Interfaces to write objects/strings to file-like objects */ |
| |
| int |
| writeobject(v, f, flags) |
| object *v; |
| object *f; |
| int flags; |
| { |
| object *writer, *value, *args, *result; |
| if (f == NULL) { |
| err_setstr(TypeError, "writeobject with NULL file"); |
| return -1; |
| } |
| else if (is_fileobject(f)) { |
| FILE *fp = getfilefile(f); |
| if (fp == NULL) { |
| err_closed(); |
| return -1; |
| } |
| return printobject(v, fp, flags); |
| } |
| writer = getattr(f, "write"); |
| if (writer == NULL) |
| return -1; |
| if (flags & PRINT_RAW) |
| value = strobject(v); |
| else |
| value = reprobject(v); |
| if (value == NULL) { |
| DECREF(writer); |
| return -1; |
| } |
| args = mkvalue("(O)", value); |
| if (value == NULL) { |
| DECREF(value); |
| DECREF(writer); |
| return -1; |
| } |
| result = call_object(writer, args); |
| DECREF(args); |
| DECREF(value); |
| DECREF(writer); |
| if (result == NULL) |
| return -1; |
| DECREF(result); |
| return 0; |
| } |
| |
| void |
| writestring(s, f) |
| char *s; |
| object *f; |
| { |
| if (f == NULL) { |
| /* Do nothing */ |
| } |
| else if (is_fileobject(f)) { |
| FILE *fp = getfilefile(f); |
| if (fp != NULL) |
| fputs(s, fp); |
| } |
| else if (!err_occurred()) { |
| object *v = newstringobject(s); |
| if (v == NULL) { |
| err_clear(); |
| } |
| else { |
| if (writeobject(v, f, PRINT_RAW) != 0) |
| err_clear(); |
| DECREF(v); |
| } |
| } |
| } |