📄 shf.c
字号:
/* * 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; } return 0;}/* Read a buffer from shf. Returns the number of bytes read into buf, * if no bytes were read, returns 0 if end of file was seen, EOF if * a read error occurred. */intshf_read(buf, bsize, shf) char *buf; int bsize; struct shf *shf;{ int orig_bsize = bsize; int ncopy; if (!(shf->flags & SHF_RD)) internal_errorf(1, "shf_read: flags %x", shf->flags); if (bsize <= 0) internal_errorf(1, "shf_read: bsize %d", bsize); while (bsize > 0) { if (shf->rnleft == 0 && (shf_fillbuf(shf) == EOF || shf->rnleft == 0)) break; ncopy = shf->rnleft; if (ncopy > bsize) ncopy = bsize; memcpy(buf, shf->rp, ncopy); buf += ncopy; bsize -= ncopy; shf->rp += ncopy; shf->rnleft -= ncopy; } /* Note: fread(3S) returns 0 for errors - this doesn't */ return orig_bsize == bsize ? (shf_error(shf) ? EOF : 0) : orig_bsize - bsize;}/* Read up to a newline or EOF. The newline is put in buf; buf is always * null terminated. Returns NULL on read error or if nothing was read before * end of file, returns a pointer to the null byte in buf otherwise. */char *shf_getse(buf, bsize, shf) char *buf; int bsize; struct shf *shf;{ unsigned char *end; int ncopy; char *orig_buf = buf; if (!(shf->flags & SHF_RD)) internal_errorf(1, "shf_getse: flags %x", shf->flags); if (bsize <= 0) return (char *) 0; --bsize; /* save room for null */ do { if (shf->rnleft == 0) { if (shf_fillbuf(shf) == EOF) return NULL; if (shf->rnleft == 0) { *buf = '\0'; return buf == orig_buf ? NULL : buf; } } end = (unsigned char *) memchr((char *) shf->rp, '\n', shf->rnleft); ncopy = end ? end - shf->rp + 1 : shf->rnleft; if (ncopy > bsize) ncopy = bsize; memcpy(buf, (char *) shf->rp, ncopy); shf->rp += ncopy; shf->rnleft -= ncopy; buf += ncopy; bsize -= ncopy;#ifdef OS2 if (end && buf > orig_buf + 1 && buf[-2] == '\r') { buf--; bsize++; buf[-1] = '\n'; }#endif } while (!end && bsize); *buf = '\0'; return buf;}/* Returns the char read. Returns EOF for error and end of file. */intshf_getchar(shf) struct shf *shf;{ if (!(shf->flags & SHF_RD)) internal_errorf(1, "shf_getchar: flags %x", shf->flags); if (shf->rnleft == 0 && (shf_fillbuf(shf) == EOF || shf->rnleft == 0)) return EOF; --shf->rnleft; return *shf->rp++;}/* Put a character back in the input stream. Returns the character if * successful, EOF if there is no room. */intshf_ungetc(c, shf) int c; struct shf *shf;{ if (!(shf->flags & SHF_RD)) internal_errorf(1, "shf_ungetc: flags %x", shf->flags); if ((shf->flags & SHF_ERROR) || c == EOF || (shf->rp == shf->buf && shf->rnleft)) return EOF; if ((shf->flags & SHF_WRITING) && shf_emptybuf(shf, EB_READSW) == EOF) return EOF; if (shf->rp == shf->buf) shf->rp = shf->buf + shf->rbsize; if (shf->flags & SHF_STRING) { /* Can unget what was read, but not something different - we * don't want to modify a string. */ if (shf->rp[-1] != c) return EOF; shf->flags &= ~SHF_EOF; shf->rp--; shf->rnleft++; return c; } shf->flags &= ~SHF_EOF; *--(shf->rp) = c; shf->rnleft++; return c;}/* Write a character. Returns the character if successful, EOF if * the char could not be written. */intshf_putchar(c, shf) int c; struct shf *shf;{ if (!(shf->flags & SHF_WR)) internal_errorf(1, "shf_putchar: flags %x", shf->flags); if (c == EOF)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -