1303 lines · 27.4 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* *  Shell file I/O routines */ #include "sh.h"#include "ksh_stat.h"#include "ksh_limval.h"  /* flags to shf_emptybuf() */#define EB_READSW	0x01	/* about to switch to reading */#define EB_GROW		0x02	/* grow buffer if necessary (STRING+DYNAMIC) */ /* * Replacement stdio routines.  Stdio is too flakey on too many machines * to be useful when you have multiple processes using the same underlying * file descriptors. */ static int	shf_fillbuf	ARGS((struct shf *shf));static int	shf_emptybuf	ARGS((struct shf *shf, int flags)); /* Open a file.  First three args are for open(), last arg is flags for * this package.  Returns NULL if file could not be opened, or if a dup * fails. */struct shf *shf_open(name, oflags, mode, sflags)	const char *name;	int oflags;	int mode;	int sflags;{	struct shf *shf;	int bsize = sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE;	int fd; 	/* Done before open so if alloca fails, fd won't be lost. */	shf = (struct shf *) alloc(sizeof(struct shf) + bsize, ATEMP);	shf->areap = ATEMP;	shf->buf = (unsigned char *) &shf[1];	shf->bsize = bsize;	shf->flags = SHF_ALLOCS;	/* Rest filled in by reopen. */ 	fd = open(name, oflags, mode);	if (fd < 0) {		afree(shf, shf->areap);		return NULL;	}	if ((sflags & SHF_MAPHI) && fd < FDBASE) {		int nfd; 		nfd = ksh_dupbase(fd, FDBASE);		close(fd);		if (nfd < 0) {			afree(shf, shf->areap);			return NULL;		}		fd = nfd;	}	sflags &= ~SHF_ACCMODE;	sflags |= (oflags & O_ACCMODE) == O_RDONLY ? SHF_RD		  : ((oflags & O_ACCMODE) == O_WRONLY ? SHF_WR		     : SHF_RDWR); 	return shf_reopen(fd, sflags, shf);} /* Set up the shf structure for a file descriptor.  Doesn't fail. */struct shf *shf_fdopen(fd, sflags, shf)	int fd;	int sflags;	struct shf *shf;{	int bsize = sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE; 	/* use fcntl() to figure out correct read/write flags */	if (sflags & SHF_GETFL) {		int flags = fcntl(fd, F_GETFL, 0); 		if (flags < 0)			/* will get an error on first read/write */			sflags |= SHF_RDWR;		else			switch (flags & O_ACCMODE) {			case O_RDONLY: sflags |= SHF_RD; break;			case O_WRONLY: sflags |= SHF_WR; break;			case O_RDWR: sflags |= SHF_RDWR; break;			}	} 	if (!(sflags & (SHF_RD | SHF_WR)))		internal_errorf(1, "shf_fdopen: missing read/write"); 	if (shf) {		if (bsize) {			shf->buf = (unsigned char *) alloc(bsize, ATEMP);			sflags |= SHF_ALLOCB;		} else			shf->buf = (unsigned char *) 0;	} else {		shf = (struct shf *) alloc(sizeof(struct shf) + bsize, ATEMP);		shf->buf = (unsigned char *) &shf[1];		sflags |= SHF_ALLOCS;	}	shf->areap = ATEMP;	shf->fd = fd;	shf->rp = shf->wp = shf->buf;	shf->rnleft = 0;	shf->rbsize = bsize;	shf->wnleft = 0; /* force call to shf_emptybuf() */	shf->wbsize = sflags & SHF_UNBUF ? 0 : bsize;	shf->flags = sflags;	shf->errno_ = 0;	shf->bsize = bsize;	if (sflags & SHF_CLEXEC)		fd_clexec(fd);	return shf;} /* Set up an existing shf (and buffer) to use the given fd */struct shf *shf_reopen(fd, sflags, shf)	int fd;	int sflags;	struct shf *shf;{	int bsize = sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE; 	/* use fcntl() to figure out correct read/write flags */	if (sflags & SHF_GETFL) {		int flags = fcntl(fd, F_GETFL, 0); 		if (flags < 0)			/* will get an error on first read/write */			sflags |= SHF_RDWR;		else			switch (flags & O_ACCMODE) {			case O_RDONLY: sflags |= SHF_RD; break;			case O_WRONLY: sflags |= SHF_WR; break;			case O_RDWR: sflags |= SHF_RDWR; break;			}	} 	if (!(sflags & (SHF_RD | SHF_WR)))		internal_errorf(1, "shf_reopen: missing read/write");	if (!shf || !shf->buf || shf->bsize < bsize)		internal_errorf(1, "shf_reopen: bad shf/buf/bsize"); 	/* assumes shf->buf and shf->bsize already set up */	shf->fd = fd;	shf->rp = shf->wp = shf->buf;	shf->rnleft = 0;	shf->rbsize = bsize;	shf->wnleft = 0; /* force call to shf_emptybuf() */	shf->wbsize = sflags & SHF_UNBUF ? 0 : bsize;	shf->flags = (shf->flags & (SHF_ALLOCS | SHF_ALLOCB)) | sflags;	shf->errno_ = 0;	if (sflags & SHF_CLEXEC)		fd_clexec(fd);	return shf;} /* Open a string for reading or writing.  If reading, bsize is the number * of bytes that can be read.  If writing, bsize is the maximum number of * bytes that can be written.  If shf is not null, it is filled in and * returned, if it is null, shf is allocated.  If writing and buf is null * and SHF_DYNAMIC is set, the buffer is allocated (if bsize > 0, it is * used for the initial size).  Doesn't fail. * When writing, a byte is reserved for a trailing null - see shf_sclose(). */struct shf *shf_sopen(buf, bsize, sflags, shf)	char *buf;	int bsize;	int sflags;	struct shf *shf;{	/* can't have a read+write string */	if (!(sflags & (SHF_RD | SHF_WR))	    || (sflags & (SHF_RD | SHF_WR)) == (SHF_RD | SHF_WR))		internal_errorf(1, "shf_sopen: flags 0x%x", sflags); 	if (!shf) {		shf = (struct shf *) alloc(sizeof(struct shf), ATEMP);		sflags |= SHF_ALLOCS;	}	shf->areap = ATEMP;	if (!buf && (sflags & SHF_WR) && (sflags & SHF_DYNAMIC)) {		if (bsize <= 0)			bsize = 64;		sflags |= SHF_ALLOCB;		buf = alloc(bsize, shf->areap);	}	shf->fd = -1;	shf->buf = shf->rp = shf->wp = (unsigned char *) buf;	shf->rnleft = bsize;	shf->rbsize = bsize;	shf->wnleft = bsize - 1;	/* space for a '\0' */	shf->wbsize = bsize;	shf->flags = sflags | SHF_STRING;	shf->errno_ = 0;	shf->bsize = bsize; 	return shf;} /* Flush and close file descriptor, free the shf structure */intshf_close(shf)	struct shf *shf;{	int ret = 0; 	if (shf->fd >= 0) {		ret = shf_flush(shf);		if (close(shf->fd) < 0)			ret = EOF;	}	if (shf->flags & SHF_ALLOCS)		afree(shf, shf->areap);	else if (shf->flags & SHF_ALLOCB)		afree(shf->buf, shf->areap); 	return ret;} /* Flush and close file descriptor, don't free file structure */intshf_fdclose(shf)	struct shf *shf;{	int ret = 0; 	if (shf->fd >= 0) {		ret = shf_flush(shf);		if (close(shf->fd) < 0)			ret = EOF;		shf->rnleft = 0;		shf->rp = shf->buf;		shf->wnleft = 0;		shf->fd = -1;	} 	return ret;} /* Close a string - if it was opened for writing, it is null terminated; * returns a pointer to the string and frees shf if it was allocated * (does not free string if it was allocated). */char *shf_sclose(shf)	struct shf *shf;{	unsigned char *s = shf->buf; 	/* null terminate */	if (shf->flags & SHF_WR) {		shf->wnleft++;		shf_putc('\0', shf);	}	if (shf->flags & SHF_ALLOCS)		afree(shf, shf->areap);	return (char *) s;} /* Flush and free file structure, don't close file descriptor */intshf_finish(shf)	struct shf *shf;{	int ret = 0; 	if (shf->fd >= 0)		ret = shf_flush(shf);	if (shf->flags & SHF_ALLOCS)		afree(shf, shf->areap);	else if (shf->flags & SHF_ALLOCB)		afree(shf->buf, shf->areap); 	return ret;} /* Un-read what has been read but not examined, or write what has been * buffered.  Returns 0 for success, EOF for (write) error. */intshf_flush(shf)	struct shf *shf;{	if (shf->flags & SHF_STRING)		return (shf->flags & SHF_WR) ? EOF : 0; 	if (shf->fd < 0)		internal_errorf(1, "shf_flush: no fd"); 	if (shf->flags & SHF_ERROR) {		errno = shf->errno_;		return EOF;	} 	if (shf->flags & SHF_READING) {		shf->flags &= ~(SHF_EOF | SHF_READING);		if (shf->rnleft > 0) {			lseek(shf->fd, (off_t) -shf->rnleft, 1);			shf->rnleft = 0;			shf->rp = shf->buf;		}		return 0;	} else if (shf->flags & SHF_WRITING)		return shf_emptybuf(shf, 0); 	return 0;} /* Write out any buffered data.  If currently reading, flushes the read * buffer.  Returns 0 for success, EOF for (write) error. */static intshf_emptybuf(shf, flags)	struct shf *shf;	int flags;{	int ret = 0; 	if (!(shf->flags & SHF_STRING) && shf->fd < 0)		internal_errorf(1, "shf_emptybuf: no fd"); 	if (shf->flags & SHF_ERROR) {		errno = shf->errno_;		return EOF;	} 	if (shf->flags & SHF_READING) {		if (flags & EB_READSW) /* doesn't happen */			return 0;		ret = shf_flush(shf);		shf->flags &= ~SHF_READING;	}	if (shf->flags & SHF_STRING) {		unsigned char	*nbuf; 		/* Note that we assume SHF_ALLOCS is not set if SHF_ALLOCB		 * is set... (changing the shf pointer could cause problems)		 */		if (!(flags & EB_GROW) || !(shf->flags & SHF_DYNAMIC)		    || !(shf->flags & SHF_ALLOCB))			return EOF;		/* allocate more space for buffer */		nbuf = (unsigned char *) aresize(shf->buf, shf->wbsize * 2,						shf->areap);		shf->rp = nbuf + (shf->rp - shf->buf);		shf->wp = nbuf + (shf->wp - shf->buf);		shf->rbsize += shf->wbsize;		shf->wbsize += shf->wbsize;		shf->wnleft += shf->wbsize;		shf->wbsize *= 2;		shf->buf = nbuf;	} else {		if (shf->flags & SHF_WRITING) {			int ntowrite = shf->wp - shf->buf;			unsigned char *buf = shf->buf;			int n; 			while (ntowrite > 0) {				n = write(shf->fd, buf, ntowrite);				if (n < 0) {					if (errno == EINTR					    && !(shf->flags & SHF_INTERRUPT))						continue;					shf->flags |= SHF_ERROR;					shf->errno_ = errno;					shf->wnleft = 0;					if (buf != shf->buf) {						/* allow a second flush						 * to work */						memmove(shf->buf, buf,							ntowrite);						shf->wp = shf->buf + ntowrite;					}					return EOF;				}				buf += n;				ntowrite -= n;			}			if (flags & EB_READSW) {				shf->wp = shf->buf;				shf->wnleft = 0;				shf->flags &= ~SHF_WRITING;				return 0;			}		}		shf->wp = shf->buf;		shf->wnleft = shf->wbsize;	}	shf->flags |= SHF_WRITING; 	return ret;} /* Fill up a read buffer.  Returns EOF for a read error, 0 otherwise. */static intshf_fillbuf(shf)	struct shf *shf;{	if (shf->flags & SHF_STRING)		return 0; 	if (shf->fd < 0)		internal_errorf(1, "shf_fillbuf: no fd"); 	if (shf->flags & (SHF_EOF | SHF_ERROR)) {		if (shf->flags & SHF_ERROR)			errno = shf->errno_;		return EOF;	} 	if ((shf->flags & SHF_WRITING) && shf_emptybuf(shf, EB_READSW) == EOF)		return EOF; 	shf->flags |= SHF_READING; 	shf->rp = shf->buf;	while (1) {		shf->rnleft = blocking_read(shf->fd, (char *) shf->buf,					    shf->rbsize);		if (shf->rnleft < 0 && errno == EINTR		    && !(shf->flags & SHF_INTERRUPT))			continue;		break;	}	if (shf->rnleft <= 0) {		if (shf->rnleft < 0) {			shf->flags |= SHF_ERROR;			shf->errno_ = errno;			shf->rnleft = 0;			shf->rp = shf->buf;			return EOF;		}		shf->flags |= SHF_EOF;	}	return 0;} /* Seek to a new position in the file.  If writing, flushes the buffer * first.  If reading, optimizes small relative seeks that stay inside the * buffer.  Returns 0 for success, EOF otherwise. */intshf_seek(shf, where, from)	struct shf *shf;	off_t where;	int from;{	if (shf->fd < 0) {		errno = EINVAL;		return EOF;	} 	if (shf->flags & SHF_ERROR) {		errno = shf->errno_;		return EOF;	} 	if ((shf->flags & SHF_WRITING) && shf_emptybuf(shf, EB_READSW) == EOF)		return EOF; 	if (shf->flags & SHF_READING) {		if (from == SEEK_CUR &&				(where < 0 ?					-where >= shf->rbsize - shf->rnleft :					where < shf->rnleft)) {			shf->rnleft -= where;			shf->rp += where;			return 0;		}		shf->rnleft = 0;		shf->rp = shf->buf;	} 	shf->flags &= ~(SHF_EOF | SHF_READING | SHF_WRITING); 	if (lseek(shf->fd, where, from) < 0) {		shf->errno_ = errno;		shf->flags |= SHF_ERROR;		return EOF;	} 

Lines 1–500 of 1303.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t