#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "sh.h"
#define EB_READSW 0x01
#define EB_GROW 0x02
static int shf_fillbuf(struct shf *);
static int shf_emptybuf(struct shf *, int);
struct shf *
shf_open(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;
shf = alloc(sizeof(struct shf) + bsize, ATEMP);
shf->areap = ATEMP;
shf->buf = (unsigned char *) &shf[1];
shf->bsize = bsize;
shf->flags = SHF_ALLOCS;
fd = open(name, oflags, mode);
if (fd == -1) {
afree(shf, shf->areap);
return NULL;
}
if ((sflags & SHF_MAPHI) && fd < FDBASE) {
int nfd;
nfd = fcntl(fd, F_DUPFD, FDBASE);
close(fd);
if (nfd == -1) {
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);
}
struct shf *
shf_fdopen(int fd, int sflags, struct shf *shf)
{
int bsize = sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE;
if (sflags & SHF_GETFL) {
int flags = fcntl(fd, F_GETFL);
if (flags == -1)
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("%s: missing read/write", __func__);
if (shf) {
if (bsize) {
shf->buf = alloc(bsize, ATEMP);
sflags |= SHF_ALLOCB;
} else
shf->buf = NULL;
} else {
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;
shf->wbsize = sflags & SHF_UNBUF ? 0 : bsize;
shf->flags = sflags;
shf->errno_ = 0;
shf->bsize = bsize;
if (sflags & SHF_CLEXEC)
fcntl(fd, F_SETFD, FD_CLOEXEC);
return shf;
}
struct shf *
shf_reopen(int fd, int sflags, struct shf *shf)
{
int bsize = sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE;
if (sflags & SHF_GETFL) {
int flags = fcntl(fd, F_GETFL);
if (flags == -1)
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("%s: missing read/write", __func__);
if (!shf || !shf->buf || shf->bsize < bsize)
internal_errorf("%s: bad shf/buf/bsize", __func__);
shf->fd = fd;
shf->rp = shf->wp = shf->buf;
shf->rnleft = 0;
shf->rbsize = bsize;
shf->wnleft = 0;
shf->wbsize = sflags & SHF_UNBUF ? 0 : bsize;
shf->flags = (shf->flags & (SHF_ALLOCS | SHF_ALLOCB)) | sflags;
shf->errno_ = 0;
if (sflags & SHF_CLEXEC)
fcntl(fd, F_SETFD, FD_CLOEXEC);
return shf;
}
struct shf *
shf_sopen(char *buf, int bsize, int sflags, struct shf *shf)
{
if (!(sflags & (SHF_RD | SHF_WR)) ||
(sflags & (SHF_RD | SHF_WR)) == (SHF_RD | SHF_WR))
internal_errorf("%s: flags 0x%x", __func__, sflags);
if (!shf) {
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;
shf->wbsize = bsize;
shf->flags = sflags | SHF_STRING;
shf->errno_ = 0;
shf->bsize = bsize;
return shf;
}
int
shf_close(struct shf *shf)
{
int ret = 0;
if (shf->fd >= 0) {
ret = shf_flush(shf);
if (close(shf->fd) == -1)
ret = EOF;
}
if (shf->flags & SHF_ALLOCS)
afree(shf, shf->areap);
else if (shf->flags & SHF_ALLOCB)
afree(shf->buf, shf->areap);
return ret;
}
int
shf_fdclose(struct shf *shf)
{
int ret = 0;
if (shf->fd >= 0) {
ret = shf_flush(shf);
if (close(shf->fd) == -1)
ret = EOF;
shf->rnleft = 0;
shf->rp = shf->buf;
shf->wnleft = 0;
shf->fd = -1;
}
return ret;
}
char *
shf_sclose(struct shf *shf)
{
unsigned char *s = shf->buf;
if (shf->flags & SHF_WR) {
shf->wnleft++;
shf_putc('\0', shf);
}
if (shf->flags & SHF_ALLOCS)
afree(shf, shf->areap);
return (char *) s;
}
int
shf_flush(struct shf *shf)
{
if (shf->flags & SHF_STRING)
return (shf->flags & SHF_WR) ? EOF : 0;
if (shf->fd < 0)
internal_errorf("%s: no fd", __func__);
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, SEEK_CUR);
shf->rnleft = 0;
shf->rp = shf->buf;
}
return 0;
} else if (shf->flags & SHF_WRITING)
return shf_emptybuf(shf, 0);
return 0;
}
static int
shf_emptybuf(struct shf *shf, int flags)
{
int ret = 0;
if (!(shf->flags & SHF_STRING) && shf->fd < 0)
internal_errorf("%s: no fd", __func__);
if (shf->flags & SHF_ERROR) {
errno = shf->errno_;
return EOF;
}
if (shf->flags & SHF_READING) {
if (flags & EB_READSW)
return 0;
ret = shf_flush(shf);
shf->flags &= ~SHF_READING;
}
if (shf->flags & SHF_STRING) {
unsigned char *nbuf;
if (!(flags & EB_GROW) || !(shf->flags & SHF_DYNAMIC) ||
!(shf->flags & SHF_ALLOCB))
return EOF;
nbuf = areallocarray(shf->buf, 2, shf->wbsize, shf->areap);
shf->rp = nbuf + (shf->rp - shf->buf);
shf->wp = nbuf + (shf->wp - shf->buf);
shf->rbsize += 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 == -1) {
if (errno == EINTR &&
!(shf->flags & SHF_INTERRUPT))
continue;
shf->flags |= SHF_ERROR;
shf->errno_ = errno;
shf->wnleft = 0;
if (buf != shf->buf) {
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;
}
static int
shf_fillbuf(struct shf *shf)
{
if (shf->flags & SHF_STRING)
return 0;
if (shf->fd < 0)
internal_errorf("%s: no fd", __func__);
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;
}
int
shf_read(char *buf, int bsize, struct shf *shf)
{
int orig_bsize = bsize;
int ncopy;
if (!(shf->flags & SHF_RD))
internal_errorf("%s: flags %x", __func__, shf->flags);
if (bsize <= 0)
internal_errorf("%s: bsize %d", __func__, bsize);
while (bsize > 0) {
if (shf->rnleft == 0 &&
(shf_fillbuf(shf) == EOF || shf->rnleft == 0))
break;
ncopy = shf->rnleft;
if (ncopy > bsize)
ncopy = bsize;
if (memchr((char *)shf->rp, '\0', ncopy) != NULL) {
errorf("syntax error: NUL byte unexpected");
return EOF;
}
memcpy(buf, shf->rp, ncopy);
buf += ncopy;
bsize -= ncopy;
shf->rp += ncopy;
shf->rnleft -= ncopy;
}
return orig_bsize == bsize ? (shf_error(shf) ? EOF : 0) :
orig_bsize - bsize;
}
char *
shf_getse(char *buf, int bsize, struct shf *shf)
{
unsigned char *end;
int ncopy;
char *orig_buf = buf;
if (!(shf->flags & SHF_RD))
internal_errorf("%s: flags %x", __func__, shf->flags);
if (bsize <= 0)
return NULL;
--bsize;
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;
if (memchr((char *)shf->rp, '\0', ncopy) != NULL) {
errorf("syntax error: NUL byte unexpected");
return NULL;
}
memcpy(buf, (char *) shf->rp, ncopy);
shf->rp += ncopy;
shf->rnleft -= ncopy;
buf += ncopy;
bsize -= ncopy;
} while (!end && bsize);
*buf = '\0';
return buf;
}
int
shf_getchar(struct shf *shf)
{
if (!(shf->flags & SHF_RD))
internal_errorf("%s: flags %x", __func__, shf->flags);
if (shf->rnleft == 0 && (shf_fillbuf(shf) == EOF || shf->rnleft == 0))
return EOF;
--shf->rnleft;
return *shf->rp++;
}
int
shf_ungetc(int c, struct shf *shf)
{
if (!(shf->flags & SHF_RD))
internal_errorf("%s: flags %x", __func__, 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) {
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;
}
int
shf_putchar(int c, struct shf *shf)
{
if (!(shf->flags & SHF_WR))
internal_errorf("%s: flags %x", __func__, shf->flags);
if (c == EOF)
return EOF;
if (shf->flags & SHF_UNBUF) {
char cc = c;
int n;
if (shf->fd < 0)
internal_errorf("%s: no fd", __func__);
if (shf->flags & SHF_ERROR) {
errno = shf->errno_;
return EOF;
}
while ((n = write(shf->fd, &cc, 1)) != 1)
if (n == -1) {
if (errno == EINTR &&
!(shf->flags & SHF_INTERRUPT))
continue;
shf->flags |= SHF_ERROR;
shf->errno_ = errno;
return EOF;
}
} else {
if (shf->wnleft == 0 && shf_emptybuf(shf, EB_GROW) == EOF)
return EOF;
shf->wnleft--;
*shf->wp++ = c;
}
return c;
}
int
shf_puts(const char *s, struct shf *shf)
{
if (!s)
return EOF;
return shf_write(s, strlen(s), shf);
}
int
shf_write(const char *buf, int nbytes, struct shf *shf)
{
int orig_nbytes = nbytes;
int n;
int ncopy;
if (!(shf->flags & SHF_WR))
internal_errorf("%s: flags %x", __func__, shf->flags);
if (nbytes < 0)
internal_errorf("%s: nbytes %d", __func__, nbytes);
if ((ncopy = shf->wnleft) &&
(shf->wp != shf->buf || nbytes < shf->wnleft)) {
if (ncopy > nbytes)
ncopy = nbytes;
memcpy(shf->wp, buf, ncopy);
nbytes -= ncopy;
buf += ncopy;
shf->wp += ncopy;
shf->wnleft -= ncopy;
}
if (nbytes > 0) {
if (shf_emptybuf(shf, EB_GROW) == EOF)
return EOF;
if (nbytes > shf->wbsize) {
ncopy = nbytes;
if (shf->wbsize)
ncopy -= nbytes % shf->wbsize;
nbytes -= ncopy;
while (ncopy > 0) {
n = write(shf->fd, buf, ncopy);
if (n == -1) {
if (errno == EINTR &&
!(shf->flags & SHF_INTERRUPT))
continue;
shf->flags |= SHF_ERROR;
shf->errno_ = errno;
shf->wnleft = 0;
return EOF;
}
buf += n;
ncopy -= n;
}
}
if (nbytes > 0) {
memcpy(shf->wp, buf, nbytes);
shf->wp += nbytes;
shf->wnleft -= nbytes;
}
}
return orig_nbytes;
}
int
shf_fprintf(struct shf *shf, const char *fmt, ...)
{
va_list args;
int n;
va_start(args, fmt);
n = shf_vfprintf(shf, fmt, args);
va_end(args);
return n;
}
int
shf_snprintf(char *buf, int bsize, const char *fmt, ...)
{
struct shf shf;
va_list args;
int n;
if (!buf || bsize <= 0)
internal_errorf("%s: buf %lx, bsize %d",
__func__, (long) buf, bsize);
shf_sopen(buf, bsize, SHF_WR, &shf);
va_start(args, fmt);
n = shf_vfprintf(&shf, fmt, args);
va_end(args);
shf_sclose(&shf);
return n;
}
char *
shf_smprintf(const char *fmt, ...)
{
struct shf shf;
va_list args;
shf_sopen(NULL, 0, SHF_WR|SHF_DYNAMIC, &shf);
va_start(args, fmt);
shf_vfprintf(&shf, fmt, args);
va_end(args);
return shf_sclose(&shf);
}
#define FL_HASH 0x001
#define FL_PLUS 0x002
#define FL_RIGHT 0x004
#define FL_BLANK 0x008
#define FL_SHORT 0x010
#define FL_LONG 0x020
#define FL_LLONG 0x040
#define FL_ZERO 0x080
#define FL_DOT 0x100
#define FL_UPPER 0x200
#define FL_NUMBER 0x400
int
shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
{
char c, *s;
int tmp = 0;
int field, precision;
int len;
int flags;
unsigned long long llnum;
char numbuf[(BITS(long long) + 2) / 3 + 1];
int nwritten = 0;
if (!fmt)
return 0;
while ((c = *fmt++)) {
if (c != '%') {
shf_putc(c, shf);
nwritten++;
continue;
}
flags = field = precision = 0;
for ( ; (c = *fmt++) ; ) {
switch (c) {
case '#':
flags |= FL_HASH;
continue;
case '+':
flags |= FL_PLUS;
continue;
case '-':
flags |= FL_RIGHT;
continue;
case ' ':
flags |= FL_BLANK;
continue;
case '0':
if (!(flags & FL_DOT))
flags |= FL_ZERO;
continue;
case '.':
flags |= FL_DOT;
precision = 0;
continue;
case '*':
tmp = va_arg(args, int);
if (flags & FL_DOT)
precision = tmp;
else if ((field = tmp) < 0) {
field = -field;
flags |= FL_RIGHT;
}
continue;
case 'l':
if (*fmt == 'l') {
fmt++;
flags |= FL_LLONG;
} else
flags |= FL_LONG;
continue;
case 'h':
flags |= FL_SHORT;
continue;
}
if (digit(c)) {
tmp = c - '0';
while (c = *fmt++, digit(c))
tmp = tmp * 10 + c - '0';
--fmt;
if (tmp < 0)
tmp = 0;
if (flags & FL_DOT)
precision = tmp;
else
field = tmp;
continue;
}
break;
}
if (precision < 0)
precision = 0;
if (!c)
break;
if (c >= 'A' && c <= 'Z') {
flags |= FL_UPPER;
c = c - 'A' + 'a';
}
switch (c) {
case 'p':
flags &= ~(FL_LLONG | FL_SHORT);
flags |= FL_LONG;
case 'd':
case 'i':
case 'o':
case 'u':
case 'x':
flags |= FL_NUMBER;
s = &numbuf[sizeof(numbuf)];
if (flags & FL_LLONG)
llnum = va_arg(args, unsigned long long);
else if (flags & FL_LONG) {
if (c == 'd' || c == 'i')
llnum = va_arg(args, long);
else
llnum = va_arg(args, unsigned long);
} else {
if (c == 'd' || c == 'i')
llnum = va_arg(args, int);
else
llnum = va_arg(args, unsigned int);
}
switch (c) {
case 'd':
case 'i':
if (0 > (long long) llnum)
llnum = - (long long) llnum, tmp = 1;
else
tmp = 0;
case 'u':
do {
*--s = llnum % 10 + '0';
llnum /= 10;
} while (llnum);
if (c != 'u') {
if (tmp)
*--s = '-';
else if (flags & FL_PLUS)
*--s = '+';
else if (flags & FL_BLANK)
*--s = ' ';
}
break;
case 'o':
do {
*--s = (llnum & 0x7) + '0';
llnum >>= 3;
} while (llnum);
if ((flags & FL_HASH) && *s != '0')
*--s = '0';
break;
case 'p':
case 'x':
{
const char *digits = (flags & FL_UPPER) ?
"0123456789ABCDEF" :
"0123456789abcdef";
do {
*--s = digits[llnum & 0xf];
llnum >>= 4;
} while (llnum);
if (flags & FL_HASH) {
*--s = (flags & FL_UPPER) ? 'X' : 'x';
*--s = '0';
}
}
}
len = &numbuf[sizeof(numbuf)] - s;
if (flags & FL_DOT) {
if (precision > len) {
field = precision;
flags |= FL_ZERO;
} else
precision = len;
}
break;
case 's':
if (!(s = va_arg(args, char *)))
s = "(null %s)";
len = strlen(s);
break;
case 'c':
flags &= ~FL_DOT;
numbuf[0] = va_arg(args, int);
s = numbuf;
len = 1;
break;
case '%':
default:
numbuf[0] = c;
s = numbuf;
len = 1;
break;
}
if (!(flags & FL_DOT) || len < precision)
precision = len;
if (field > precision) {
field -= precision;
if (!(flags & FL_RIGHT)) {
field = -field;
if ((flags & FL_ZERO) && (flags & FL_NUMBER)) {
if (*s == '+' || *s == '-' || *s ==' ') {
shf_putc(*s, shf);
s++;
precision--;
nwritten++;
} else if (*s == '0') {
shf_putc(*s, shf);
s++;
nwritten++;
if (--precision > 0 &&
(*s | 0x20) == 'x') {
shf_putc(*s, shf);
s++;
precision--;
nwritten++;
}
}
c = '0';
} else
c = flags & FL_ZERO ? '0' : ' ';
if (field < 0) {
nwritten += -field;
for ( ; field < 0 ; field++)
shf_putc(c, shf);
}
} else
c = ' ';
} else
field = 0;
if (precision > 0) {
nwritten += precision;
for ( ; precision-- > 0 ; s++)
shf_putc(*s, shf);
}
if (field > 0) {
nwritten += field;
for ( ; field > 0 ; --field)
shf_putc(c, shf);
}
}
return shf_error(shf) ? EOF : nwritten;
}