/*ident "@(#)cls4:lib/stream/filebuf.c 1.7" */ /******************************************************************************* C++ source for the C++ Language System, Release 3.0. This product is a new release of the original cfront developed in the computer science research center of AT&T Bell Laboratories. Copyright (c) 1993 UNIX System Laboratories, Inc. Copyright (c) 1991, 1992 AT&T and UNIX System Laboratories, Inc. Copyright (c) 1984, 1989, 1990 AT&T. All Rights Reserved. THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE of AT&T and UNIX System Laboratories, Inc. The copyright notice above does not evidence any actual or intended publication of such source code. *******************************************************************************/ #include #include #include "streamdefs.h" #ifdef STDIO_ONLY #include static int open(const char* name,int flags, int prot = 0) ; static int creat(const char* name, int prot ) ; static streampos lseek(int fd, long, ios::seek_dir) ; static int read(int fd, char* b, int len) ; static int write(int fd, char* b, int len) ; static int close(int fd) ; #else #include #ifndef O_RDONLY #include #endif #endif /* STDIO_ONLY */ #ifndef O_RDONLY # define O_RDONLY 0 #endif #ifndef O_WRONLY # define O_WRONLY 1 #endif #ifndef O_RDWR # define O_RDWR 2 #endif #ifndef O_CREAT # define O_CREAT 00400 #endif #ifndef O_TRUNC # define O_TRUNC 01000 #endif #ifndef O_EXCL # define O_EXCL 02000 #endif #ifndef O_APPEND static const int NOSYSAPPEND = 1 ; # define O_APPEND 010 #else static const int NOSYSAPPEND = 0 ; #endif #include extern int errno ; const int filebuf::openprot = 0644 ; static const int input=ios::in ; static const int output=ios::out ; static const int append=ios::app ; static const int atend=ios::ate ; static const int tcate=ios::trunc ; static const int nocr=ios::nocreate ; static const int norep=ios::noreplace ; static const int seek_beg=ios::beg ; static const int seek_end=ios::end ; static const int seek_cur=ios::cur ; int filebuf::last_op() { return (pptr()?output:(gptr()?input:0)) ; } inline void save_errno(int& orig) { orig = ::errno ; ::errno = 0 ; } inline int restore_errno(int& orig) { if ( ::errno == 0 ) ::errno = orig ; return EOF ; } /* * Open a file with the given mode. * Return: NULL if failure * this if success */ filebuf* filebuf::open (const char *name, int om, int prot) { int errno_orig ; save_errno(errno_orig) ; if ( om&append ) om |= output ; if ( opened ) { restore_errno(errno_orig) ; return 0 ; } int flags = 0 ; if ( om&append ) flags |= O_APPEND ; /* ios::nocreate and ios::noreplace should be mutually exclusive */ if ( (om&(output|nocr|norep)) == (output|nocr|norep) ) return 0 ; switch (om&(input|output)) { case input : flags |= O_RDONLY ; xfd = ::open(name,flags) ; if ( xfd >= 0 && (om&atend)) lseek(xfd,0,2) ; break ; case output : flags |= O_WRONLY|O_CREAT|O_TRUNC ; if ( om&nocr ) flags &= ~O_CREAT ; else if ( om&norep ) flags |= O_EXCL ; if ( om&append ) flags |= O_APPEND ; if ( om&(append|atend) ) flags &= ~O_TRUNC ; if ( flags == (O_WRONLY|O_CREAT|O_TRUNC) ) { xfd = ::creat(name,prot) ; } else { xfd = ::open(name,flags,prot) ; } if ( xfd >= 0 && (om&(atend|append))) lseek(xfd,0,2) ; break ; case input|output: flags |= O_RDWR|O_CREAT ; if ( om&tcate ) flags |= O_TRUNC ; if ( om&append ) flags |= O_APPEND ; if ( om&nocr ) flags &= ~O_CREAT ; else if ( om&norep ) flags |= O_EXCL; xfd = ::open(name,flags,prot) ; if ( xfd >= 0 && (om&(atend|append))) lseek(xfd,0,2) ; break; } if (xfd < 0) return 0; opened = 1; setp(0,0) ; setg(0,0,0) ; mode = om ; last_seek = EOF ; restore_errno(errno_orig) ; return this; } /* * getmode_from_fd() gets the current mode of the * file descriptor (using fcntl()), then builds up * the mode value that will be needed for a filebuf * object */ static int getmode_from_fd(int fd) { int status_flags, retval; retval = 0; if ((status_flags = fcntl(fd, F_GETFL, 0)) != -1) { switch (status_flags & 03) { case O_RDONLY: retval |= input; break; case O_WRONLY: retval |= output; break; case O_RDWR: retval |= input | output; break; } if (status_flags & O_APPEND) { retval |= append; } } return (retval); } filebuf* filebuf::attach (int f) { if ( opened ) return 0 ; xfd = f; opened = 1; setp(0,0) ; setg(0,0,0) ; mode=0 ; last_seek= EOF; return this; } int filebuf::detach () { int f = xfd; if ( !opened ) return -1 ; if (last_op() == output) overflow(); opened = 0; xfd = -1; setp(0,0) ; setg(0,0,0) ; mode=0 ; last_seek= EOF; return f; } /* * Empty an output buffer. * Returns: EOF on error * 0 on success */ int filebuf::overflow(int c) { int errno_orig ; save_errno(errno_orig) ; if ( !opened ) return restore_errno(errno_orig) ; if ( allocate() == EOF ) return restore_errno(errno_orig) ; if ( last_op() == input ) { if ( sync()==EOF ) return restore_errno(errno_orig) ; } register char* p = base() ; // pptr()==NULL does not imply p < pptr(), so we need separate // test. while ( pptr() && p < pptr() ) { if ( NOSYSAPPEND && (mode&append) ) { // System doesn't have an append mode, so approximate // as best we can. lseek(xfd,0,2) ; } /* Partial writes are sometimes possible in peculiar * circumstances */ register int count = write(xfd,p,pptr()-p) ; if ( count < 0 ) { last_seek = EOF ; return restore_errno(errno_orig) ; } p += count ; if ( SEEK_ARITH_OK && last_seek != EOF && mode && !(mode&append) && count >= 0 ) { last_seek += count ; } else { last_seek = EOF ; } if ( count < 0 ) return restore_errno(errno_orig) ; } setp(base(),ebuf()) ; setg(0,0,0); if ( c == EOF ) /* don't do anything */ ; else if ( unbuffered() ) { char ch = c; last_seek = EOF ; while ( write(xfd,&ch,1)!=1 && ::errno==0 ) ; if ( ::errno != 0 ) return restore_errno(errno_orig) ; } else { sputc(c) ; } restore_errno(errno_orig) ; return zapeof(c) ; } /* * Fill an input buffer. * Returns: EOF on error or end of input * next character on success */ int filebuf::underflow() { int count; if ( !opened ) return EOF ; if ( allocate() == EOF ) return EOF ; if ( last_op() == output ) { if ( sync()==EOF ) return EOF ; } int orig_errno ; save_errno(orig_errno) ; setp(0,0) ; if ( unbuffered() ) { last_seek=EOF ; count = read(xfd,&lahead[0],1) ; setg(&lahead[0],&lahead[0],&lahead[count]) ; if ( count <= 0 ) return EOF ; } else { register int rdsize ; if ( blen() > 2*sizeof(long) ) { /* gptr must be set greater than base to * guarantee at least 1 char of pushback. * putting it farther will tend in many common * cases to keep things aligned. */ in_start = base()+sizeof(long) ; rdsize = blen()-sizeof(long) ; } else { in_start = base()+1 ; rdsize = blen()-1 ; } count = read(xfd,in_start,rdsize) ; while ( count<=0 && ::errno==EINTR ) { /* * Signal caught and returned before any data * transfered. */ ::errno = 0 ; count = read(xfd,in_start,rdsize) ; } if ( SEEK_ARITH_OK && last_seek != EOF && mode && !(mode&append) && count >= 0 ) { last_seek += count ; } else { last_seek = EOF ; } if ( count <= 0 ) { setg(0,0,0) ; return restore_errno(orig_errno) ; } setg(base(),in_start,in_start+count) ; } restore_errno(orig_errno) ; return zapeof(*gptr()); } filebuf* filebuf::close() { int f = xfd ; if ( !opened ) { return 0 ; } if (last_op()==output) overflow(); setg(0,0,0) ; setp(0,0) ; opened = 0 ; xfd = -1 ; last_seek = EOF ; if ( mode != 0 ) { mode = 0 ; int orig_errno ; save_errno(orig_errno) ; int ok = ::close(f); restore_errno(orig_errno) ; return ( ok==EOF ? 0 : this ) ; } else { return this ; } } int filebuf::sync() { int ok = 0 ; int orig_errno ; save_errno(orig_errno) ; switch(last_op()) { case input: last_seek = lseek(xfd,gptr()-egptr(),seek_cur) ; if ( last_seek < 0 ) { ok = EOF ; last_seek = EOF ; } break ; case output: ok = overflow() ; /* This does not result in infinite recursion even though * under some circumstances overflow might call sync. * it explicitly does not when last_op==output */ break; } setp(0,0) ; setg(0,0,0) ; last_seek = EOF ; restore_errno(orig_errno) ; return (ok==EOF ? EOF : 0 ) ; } streampos filebuf::seekoff(streamoff p, ios::seek_dir d, int m) { int orig_errno ; save_errno(orig_errno) ; if ( last_seek == EOF ) { last_seek = lseek(xfd,0,seek_cur) ; } if ( last_seek == EOF ) return EOF ; if( SEEK_ARITH_OK && !unbuffered() ) { char* refptr = 0 ; streampos sneed ; streampos sref, minavail, maxavail ; switch ( last_op() ) { case input : { refptr = gptr() ; sref = last_seek-(egptr()-gptr()) ; minavail = last_seek-(egptr()-in_start) ; maxavail = last_seek-1 ; } break ; case output : { // only allowable seek during output is // to present position. refptr = pptr() ; sref = last_seek+pptr()-pbase() ; minavail = maxavail = sref ; } break ; default: { sref = last_seek; } break ; } switch( d ) { case seek_beg : sneed = p ; break ; case seek_cur : sneed = sref+p ;break ; case seek_end : refptr = 0 ; sneed = 0; break ; /* Can't do seek_end */ } if ( refptr && sneed >= minavail && sneed <= maxavail ) { switch( last_op() ) { case input : { setg(eback(),refptr+(sneed-sref),egptr()); } break ; case output : { // Seeking to current position. Nothing to // do. } break ; default : { // shouldn't get here. Try to recover somehow sync() ; seekoff(p,d,m); } break ; } return sneed ; } #if 0 if ( refptr && sneed < sref && sneed+blen()/2 > sref && last_op() == input ) { // looks like we are stepping backward through // a file. Performance may be improved by // backing up a little extra. streampos toofar = sneed-blen()/2 ; if ( toofar < 0 ) toofar = 0 ; // sync() ; last_seek=lseek(xfd,toofar,seek_beg) ; underflow(); return seekoff(sneed, ios::beg, m); } #endif } restore_errno(orig_errno) ; if ( sync()==EOF ) return EOF ; else { last_seek=lseek(xfd,p,d) ; return last_seek ; } } filebuf::filebuf() : xfd(-1), opened(0), mode(0), last_seek(EOF), in_start(0) { } filebuf::filebuf(int f) : xfd(f), opened(1), mode(getmode_from_fd(f)), last_seek(EOF), in_start(0) { } filebuf::filebuf(int f, char* p, int l) : streambuf(p,l), xfd(f), opened(1), mode(getmode_from_fd(f)), last_seek(EOF), in_start(0) { } filebuf::~filebuf() { close() ; } streambuf* filebuf::setbuf(char* p , int len) { if ( is_open() && base() ) return 0 ; // Note the special case of allowing buffering to be turned // on even for an already opened filebuf. setb(0,0) ; return streambuf::setbuf(p,len) ; } #ifdef STDIO_ONLY /**** The following functions will simulate the UNIX function calls using stdio operations. They are intended for non-unix environments. Simulation is not perfect and there may be problems porting code to such environments. In particular issues of binary files are not dealt with here *****/ #ifndef SEEK_SET # define SEEK_SET 0 #endif #ifndef SEEK_CUR # define SEEK_CUR 1 #endif #ifndef SEEK_END # define SEEK_END 2 #endif typedef FILE* Filep ; static Filep* files = 0 ; static int nfiles = 0 ; static Filep zfp ; static Filep& file(int fd) { if ( fd < nfiles ) return files[fd] ; if ( !files ) { files = new Filep [3] ; if ( !files ) { // Malloc failed. We're in deep trouble return zfp ; } files[0] = stdin ; files[1] = stdout ; files[2] = stderr ; nfiles = 3 ; } if ( fd < nfiles ) return files[fd] ; Filep* newfiles = new Filep [2*nfiles] ; if ( !newfiles ) { // Oh dear malloc failed. There isn't really any // reasonable recovery, so I just kind of boot. return zfp ; } int x ; for ( x = 0 ; x < nfiles ; ++x) newfiles[x] = files[x] ; nfiles = 2*nfiles ; for ( ; x < nfiles ; ++x ) newfiles[x] = 0 ; delete files ; files = newfiles ; return files[fd] ; } static int xfile() { if ( !files ) file(0) ; for ( int x = 0 ; x < nfiles ; ++x ) { if ( !files[x] ) return x ; } return nfiles ; } static int open(const char* name, int flags, int ) { /* not all modes can be simulated using stdio, sorry */ char* type = "" ; switch ( flags & 03 ) { case 0 : /* RD_ONLY */ { type = "r" ; } break ; case 1 : /* WR_ONLY */ { if ( flags & O_TRUNC ) type = "w" ; else type = "a" ; } break ; case 2 : /* RDWR */ { if ( flags & O_TRUNC) type = "w+" ; else if ( flags & O_APPEND ) type = "a+" ; else type = "r+" ; } break ; } int x = xfile() ; if ( x < 0 ) return -1 ; Filep* xfile = &file(x) ; *xfile = fopen(name, type) ; return ( *xfile ? x : -1 ) ; } static int creat(const char* name, int) { return open(name,O_WRONLY|O_CREAT|O_TRUNC,0) ; } static streampos lseek(int fd, long n, ios::seek_dir d ) { int whence ; switch ( d ) { case seek_beg : whence = SEEK_SET ; break ; case seek_cur : whence = SEEK_CUR ; break ; case seek_end : whence = SEEK_END ; break ; } Filep f = file(fd) ; if ( !f ) return EOF ; int fail = fseek(f, n, whence ) ; if ( fail ) return EOF ; return ftell(f) ; } static int read(int fd, char* b, int len) { Filep f = file(fd) ; if ( !f ) return 0 ; return fread(b,1,len,file(fd)) ; } static int write(int fd, char* b, int len) { Filep f = file(fd) ; if ( !f ) return 0 ; int n = fwrite(b,1,len,f) ; fflush(f) ; return n ; } static int close(int fd) { Filep f = file(fd) ; if (!f ) return 0 ; return fclose(f) ; } #endif /* STDIO_ONLY */