/* This module implements an interface to posix system calls */ /* file stdio intrinsics for S-Lang */ /* Copyright (C) 2004, 2005, 2006, 2007 John E. Davis This file is part of the S-Lang Library. The S-Lang Library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The S-Lang Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "slinclud.h" #if defined(__unix__) || (defined (__os2__) && defined (__EMX__)) # include #endif #ifdef HAVE_FCNTL_H # include #endif #ifdef HAVE_SYS_FCNTL_H # include #endif #ifdef __unix__ # include #endif #ifdef HAVE_IO_H # include #endif #if defined(__BORLANDC__) # include #endif #if defined(__DECC) && defined(VMS) # include # include #endif #ifdef VMS # include #else # include #endif #include #include "slang.h" #include "_slang.h" struct _pSLFile_FD_Type { char *name; unsigned int num_refs; /* reference counting */ int fd; /* used if get_fd method is NULL */ SLang_MMT_Type *stdio_mmt; /* fdopen'd stdio object */ int is_closed; /* non-zero if closed */ /* methods */ int clientdata_id; VOID_STAR clientdata; void (*free_client_data)(VOID_STAR); int (*get_fd) (VOID_STAR, int *); /* If non-NULL, get_fd will be used to obtain the descriptor when needed */ int (*close) (VOID_STAR); int (*read)(VOID_STAR, char *, unsigned int); int (*write)(VOID_STAR, char *, unsigned int); SLFile_FD_Type *(*dup)(VOID_STAR); }; /* Returns 0 the system call should not be restarted, 1 otherwise */ static int is_interrupt (int e) { SLerrno_set_errno (e); #ifdef EINTR if (e == EINTR) { if (0 == SLang_handle_interrupt ()) return 1; } #endif #ifdef EAGAIN if (e == EAGAIN) { if (0 == SLang_handle_interrupt ()) return 1; } #endif return 0; } static int get_fd (SLFile_FD_Type *f, int *fdp) { if (f->is_closed == 0) { if (f->get_fd == NULL) { *fdp = f->fd; return 0; } if (0 == (*f->get_fd)(f->clientdata, fdp)) return 0; } *fdp = -1; #ifdef EBADF SLerrno_set_errno (EBADF); #endif return -1; } static int do_close (SLFile_FD_Type *f) { int fd; if (-1 == get_fd (f, &fd)) return -1; while (1) { int status; errno = 0; if (f->close != NULL) status = (*f->close)(f->clientdata); else status = close (fd); if (status == 0) { f->fd = -1; f->is_closed = 1; if ((f->clientdata != NULL) && (f->free_client_data != NULL)) (*f->free_client_data) (f->clientdata); f->clientdata = NULL; return status; } if (0 == is_interrupt (errno)) return -1; } } static int do_write (SLFile_FD_Type *f, char *buf, unsigned int *nump) { int fd; if (-1 == get_fd (f, &fd)) { *nump = 0; return -1; } while (1) { int num; errno = 0; if (f->write != NULL) num = (*f->write)(f->clientdata, buf, *nump); else num = write (fd, buf, *nump); if (num != -1) { *nump = (unsigned int) num; return 0; } if (is_interrupt (errno)) continue; *nump = 0; return -1; } } static int do_read (SLFile_FD_Type *f, char *buf, unsigned int *nump) { int fd; if (-1 == get_fd (f, &fd)) { *nump = 0; return -1; } while (1) { int num; errno = 0; if (f->read != NULL) num = (*f->read)(f->clientdata, buf, *nump); else num = read (fd, buf, *nump); if (num != -1) { *nump = (unsigned int) num; return 0; } if (is_interrupt (errno)) continue; *nump = 0; return -1; } } static int posix_close (SLFile_FD_Type *f) { if (-1 == do_close (f)) return -1; if (f->stdio_mmt != NULL) { SLang_free_mmt (f->stdio_mmt); f->stdio_mmt = NULL; } return 0; } /* Usage: Uint write (f, buf); */ static void posix_write (SLFile_FD_Type *f, SLang_BString_Type *bstr) { unsigned int len; char *p; if ((NULL == (p = (char *)SLbstring_get_pointer (bstr, &len))) || (-1 == do_write (f, p, &len))) { SLang_push_integer (-1); return; } (void) SLang_push_uinteger (len); } /* Usage: nn = read (f, &buf, n); */ static void posix_read (SLFile_FD_Type *f, SLang_Ref_Type *ref, unsigned int *nbytes) { unsigned int len; char *b; SLang_BString_Type *bstr; b = NULL; len = *nbytes; if ((NULL == (b = SLmalloc (len + 1))) || (-1 == do_read (f, b, &len))) goto return_error; if (len != *nbytes) { char *b1 = SLrealloc (b, len + 1); if (b1 == NULL) goto return_error; b = b1; } bstr = SLbstring_create_malloced ((unsigned char *) b, len, 0); if (bstr != NULL) { if (-1 == SLang_assign_to_ref (ref, SLANG_BSTRING_TYPE, (VOID_STAR)&bstr)) { SLbstring_free (bstr); return; } SLbstring_free (bstr); (void) SLang_push_uinteger (len); return; } return_error: if (b != NULL) SLfree ((char *)b); (void) SLang_assign_to_ref (ref, SLANG_NULL_TYPE, NULL); (void) SLang_push_integer (-1); } SLFile_FD_Type *SLfile_create_fd (char *name, int fd) { SLFile_FD_Type *f; if (NULL == (f = (SLFile_FD_Type *) SLmalloc (sizeof (SLFile_FD_Type)))) return NULL; memset ((char *) f, 0, sizeof (SLFile_FD_Type)); if (NULL == (f->name = SLang_create_slstring (name))) { SLfree ((char *)f); return NULL; } f->fd = fd; f->num_refs = 1; f->clientdata_id = 0; f->clientdata = NULL; /* If NULL, use the standard routines on a file descriptor */ f->close = NULL; f->read = NULL; f->write = NULL; return f; } int SLfile_set_getfd_method (SLFile_FD_Type *f, int (*func)(VOID_STAR, int *)) { if (f == NULL) return -1; f->get_fd = func; return 0; } int Last_Client_Data_ID = 0; int SLfile_create_clientdata_id (int *idp) { if (Last_Client_Data_ID != -1) Last_Client_Data_ID++; if (Last_Client_Data_ID == -1) { *idp = -1; return -1; } *idp = Last_Client_Data_ID; return 0; } int SLfile_get_clientdata (SLFile_FD_Type *f, int id, VOID_STAR *cdp) { if ((f == NULL) || (f->clientdata_id != id)) { *cdp = NULL; return -1; } *cdp = f->clientdata; return 0; } int SLfile_set_clientdata (SLFile_FD_Type *f, void (*func)(VOID_STAR), VOID_STAR cd, int id) { if (f == NULL) return -1; if (id == -1) { SLang_verror (SL_Application_Error, "SLfile_set_client_data: invalid id"); return -1; } f->free_client_data = func; f->clientdata = cd; f->clientdata_id = id; return 0; } int SLfile_set_close_method (SLFile_FD_Type *f, int (*func)(VOID_STAR)) { if (f == NULL) return -1; f->close = func; return 0; } int SLfile_set_read_method (SLFile_FD_Type *f, int (*func)(VOID_STAR, char*, unsigned int)) { if (f == NULL) return -1; f->read = func; return 0; } int SLfile_set_write_method (SLFile_FD_Type *f, int (*func)(VOID_STAR, char*, unsigned int)) { if (f == NULL) return -1; f->write = func; return 0; } int SLfile_set_dup_method (SLFile_FD_Type *f, SLFile_FD_Type *(*func)(VOID_STAR)) { if (f == NULL) return -1; f->dup = func; return 0; } SLFile_FD_Type *SLfile_dup_fd (SLFile_FD_Type *f0) { SLFile_FD_Type *f; int fd0, fd; if (f0 == NULL) return NULL; if (-1 == get_fd (f0, &fd0)) return NULL; if (f0->dup != NULL) return (*f0->dup)(f0->clientdata); while (-1 == (fd = dup (fd0))) { if (is_interrupt (errno)) continue; return NULL; } if (NULL == (f = SLfile_create_fd (f0->name, fd))) { while ((-1 == close (fd)) && is_interrupt (errno)) ; return NULL; } return f; } int SLfile_get_fd (SLFile_FD_Type *f, int *fd) { if (f == NULL) return -1; return get_fd (f, fd); } void SLfile_free_fd (SLFile_FD_Type *f) { if (f == NULL) return; if (f->num_refs > 1) { f->num_refs -= 1; return; } (void) do_close (f); if ((f->clientdata != NULL) && (f->free_client_data != NULL)) (*f->free_client_data) (f->clientdata); if (f->stdio_mmt != NULL) SLang_free_mmt (f->stdio_mmt); SLfree ((char *) f); } static int pop_string_int (char **s, int *i) { *s = NULL; if ((-1 == SLang_pop_integer (i)) || (-1 == SLang_pop_slstring (s))) return -1; return 0; } static int pop_string_int_int (char **s, int *a, int *b) { *s = NULL; if ((-1 == SLang_pop_integer (b)) || (-1 == pop_string_int (s, a))) return -1; return 0; } static void posix_open (void) { char *file; int mode, flags; SLFile_FD_Type *f; switch (SLang_Num_Function_Args) { case 3: if (-1 == pop_string_int_int (&file, &flags, &mode)) { SLang_push_null (); return; } break; case 2: default: if (-1 == pop_string_int (&file, &flags)) return; mode = 0777; break; } f = SLfile_create_fd (file, -1); if (f == NULL) { SLang_free_slstring (file); SLang_push_null (); return; } SLang_free_slstring (file); while (-1 == (f->fd = open (f->name, flags, mode))) { if (is_interrupt (errno)) continue; SLfile_free_fd (f); SLang_push_null (); return; } if (-1 == SLfile_push_fd (f)) SLang_push_null (); SLfile_free_fd (f); } static int dummy_close (VOID_STAR cd) { (void) cd; return 0; } static void posix_fileno (void) { FILE *fp; SLang_MMT_Type *mmt; int fd; SLFile_FD_Type *f; char *name; if (-1 == SLang_pop_fileptr (&mmt, &fp)) { SLang_push_null (); return; } name = SLang_get_name_from_fileptr (mmt); fd = fileno (fp); f = SLfile_create_fd (name, fd); if (f != NULL) { f->close = dummy_close; /* prevent fd from being closed * when it goes out of scope */ } SLang_free_mmt (mmt); if (-1 == SLfile_push_fd (f)) SLang_push_null (); SLfile_free_fd (f); } static void posix_fdopen (SLFile_FD_Type *f, char *mode) { if (f->stdio_mmt == NULL) { if (-1 == _pSLstdio_fdopen (f->name, f->fd, mode)) return; if (NULL == (f->stdio_mmt = SLang_pop_mmt (SLANG_FILE_PTR_TYPE))) return; } (void) SLang_push_mmt (f->stdio_mmt); } static _pSLc_off_t_Type posix_lseek (SLFile_FD_Type *f, _pSLc_off_t_Type *ofs, int *whence) { _pSLc_off_t_Type status; int fd; if (-1 == get_fd (f, &fd)) return -1; while (-1 == (status = lseek (fd, *ofs, *whence))) { if (is_interrupt (errno)) continue; return -1; } return status; } static int posix_isatty (void) { int ret; SLFile_FD_Type *f; int fd; if (SLang_peek_at_stack () == SLANG_FILE_PTR_TYPE) { SLang_MMT_Type *mmt; FILE *fp; if (-1 == SLang_pop_fileptr (&mmt, &fp)) return 0; /* invalid descriptor */ ret = isatty (fileno (fp)); SLang_free_mmt (mmt); return ret; } if (-1 == SLfile_pop_fd (&f)) return 0; if (-1 == get_fd (f, &fd)) ret = -1; else ret = isatty (f->fd); SLfile_free_fd (f); return ret; } static void posix_dup (SLFile_FD_Type *f) { if ((NULL == (f = SLfile_dup_fd (f))) || (-1 == SLfile_push_fd (f))) SLang_push_null (); SLfile_free_fd (f); } #define I SLANG_INT_TYPE #define V SLANG_VOID_TYPE #define F SLANG_FILE_FD_TYPE #define B SLANG_BSTRING_TYPE #define R SLANG_REF_TYPE #define U SLANG_UINT_TYPE #define S SLANG_STRING_TYPE #define L SLANG_LONG_TYPE static SLang_Intrin_Fun_Type Fd_Name_Table [] = { MAKE_INTRINSIC_0("fileno", posix_fileno, V), MAKE_INTRINSIC_0("isatty", posix_isatty, I), MAKE_INTRINSIC_0("open", posix_open, V), MAKE_INTRINSIC_3("read", posix_read, V, F, R, U), MAKE_INTRINSIC_3("lseek", posix_lseek, SLANG_C_OFF_T_TYPE, F, SLANG_C_OFF_T_TYPE, I), MAKE_INTRINSIC_2("fdopen", posix_fdopen, V, F, S), MAKE_INTRINSIC_2("write", posix_write, V, F, B), MAKE_INTRINSIC_1("dup_fd", posix_dup, V, F), MAKE_INTRINSIC_1("close", posix_close, I, F), SLANG_END_INTRIN_FUN_TABLE }; #undef I #undef V #undef F #undef B #undef R #undef S #undef L #undef U static SLang_IConstant_Type PosixIO_Consts [] = { #ifdef O_RDONLY MAKE_ICONSTANT("O_RDONLY", O_RDONLY), #endif #ifdef O_WRONLY MAKE_ICONSTANT("O_WRONLY", O_WRONLY), #endif #ifdef O_RDWR MAKE_ICONSTANT("O_RDWR", O_RDWR), #endif #ifdef O_APPEND MAKE_ICONSTANT("O_APPEND", O_APPEND), #endif #ifdef O_CREAT MAKE_ICONSTANT("O_CREAT", O_CREAT), #endif #ifdef O_EXCL MAKE_ICONSTANT("O_EXCL", O_EXCL), #endif #ifdef O_NOCTTY MAKE_ICONSTANT("O_NOCTTY", O_NOCTTY), #endif #ifdef O_NONBLOCK MAKE_ICONSTANT("O_NONBLOCK", O_NONBLOCK), #endif #ifdef O_TRUNC MAKE_ICONSTANT("O_TRUNC", O_TRUNC), #endif #ifndef O_BINARY # define O_BINARY 0 #endif MAKE_ICONSTANT("O_BINARY", O_BINARY), #ifndef O_TEXT # define O_TEXT 0 #endif MAKE_ICONSTANT("O_TEXT", O_TEXT), SLANG_END_ICONST_TABLE }; int SLfile_push_fd (SLFile_FD_Type *f) { if (f == NULL) return SLang_push_null (); f->num_refs += 1; if (0 == SLclass_push_ptr_obj (SLANG_FILE_FD_TYPE, (VOID_STAR) f)) return 0; f->num_refs -= 1; return -1; } int SLfile_pop_fd (SLFile_FD_Type **f) { return SLclass_pop_ptr_obj (SLANG_FILE_FD_TYPE, (VOID_STAR *) f); } static void destroy_fd_type (SLtype type, VOID_STAR ptr) { (void) type; SLfile_free_fd (*(SLFile_FD_Type **) ptr); } static int fd_push (SLtype type, VOID_STAR v) { (void) type; return SLfile_push_fd (*(SLFile_FD_Type **)v); } static int fd_fd_bin_op_result (int op, SLtype a, SLtype b, SLtype *c) { (void) a; (void) b; switch (op) { default: return 0; #if 0 case SLANG_GT: case SLANG_GE: case SLANG_LT: case SLANG_LE: #endif case SLANG_EQ: case SLANG_NE: *c = SLANG_CHAR_TYPE; break; } return 1; } static int fd_fd_bin_op (int op, SLtype a_type, VOID_STAR ap, unsigned int na, SLtype b_type, VOID_STAR bp, unsigned int nb, VOID_STAR cp) { char *ic; SLFile_FD_Type **a, **b; unsigned int n, n_max; unsigned int da, db; (void) a_type; (void) b_type; if (na == 1) da = 0; else da = 1; if (nb == 1) db = 0; else db = 1; if (na > nb) n_max = na; else n_max = nb; a = (SLFile_FD_Type **) ap; b = (SLFile_FD_Type **) bp; ic = (char *) cp; switch (op) { case SLANG_NE: for (n = 0; n < n_max; n++) { if ((*a == NULL) || (*b == NULL)) ic [n] = (*a != *b); else ic [n] = (*a)->fd != (*b)->fd; a += da; b += db; } break; case SLANG_EQ: for (n = 0; n < n_max; n++) { if ((*a == NULL) || (*b == NULL)) ic [n] = (*a == *b); else ic [n] = (*a)->fd == (*b)->fd; a += da; b += db; } break; default: return 0; } return 1; } int SLang_init_posix_io (void) { SLang_Class_Type *cl; if (NULL == (cl = SLclass_allocate_class ("FD_Type"))) return -1; cl->cl_destroy = destroy_fd_type; (void) SLclass_set_push_function (cl, fd_push); if ((-1 == SLclass_register_class (cl, SLANG_FILE_FD_TYPE, sizeof (SLFile_FD_Type), SLANG_CLASS_TYPE_PTR)) || (-1 == SLclass_add_binary_op (SLANG_FILE_FD_TYPE, SLANG_FILE_FD_TYPE, fd_fd_bin_op, fd_fd_bin_op_result))) return -1; if ((-1 == SLadd_intrin_fun_table(Fd_Name_Table, "__POSIXIO__")) || (-1 == SLadd_iconstant_table (PosixIO_Consts, NULL)) || (-1 == _pSLerrno_init ())) return -1; return 0; }