picrin/extlib/benz/xfile.c

479 lines
9.1 KiB
C

#include "picrin.h"
static int file_read(void *cookie, char *ptr, int size) {
FILE *file = cookie;
int r;
size = 1; /* override size */
r = (int)fread(ptr, 1, (size_t)size, file);
if (r < size && ferror(file)) {
return -1;
}
if (r == 0 && feof(file)) {
clearerr(file);
}
return r;
}
static int file_write(void *cookie, const char *ptr, int size) {
FILE *file = cookie;
int r;
r = (int)fwrite(ptr, 1, (size_t)size, file);
if (r < size) {
return -1;
}
fflush(cookie);
return r;
}
static long file_seek(void *cookie, long pos, int whence) {
switch (whence) {
case XSEEK_CUR:
whence = SEEK_CUR;
break;
case XSEEK_SET:
whence = SEEK_SET;
break;
case XSEEK_END:
whence = SEEK_END;
break;
}
return fseek(cookie, pos, whence);
}
static int file_close(void *cookie) {
return fclose(cookie);
}
xFILE *xfopen(const char *name, const char *mode) {
FILE *fp;
if ((fp = fopen(name, mode)) == NULL) {
return NULL;
}
switch (*mode) {
case 'r':
return xfunopen(fp, file_read, NULL, file_seek, file_close);
default:
return xfunopen(fp, NULL, file_write, file_seek, file_close);
}
}
#define FILE_VTABLE { 0, file_read, file_write, file_seek, file_close }
xFILE x_iob[XOPEN_MAX] = {
{ { 0 }, 0, NULL, NULL, FILE_VTABLE, X_READ },
{ { 0 }, 0, NULL, NULL, FILE_VTABLE, X_WRITE | X_LNBUF },
{ { 0 }, 0, NULL, NULL, FILE_VTABLE, X_WRITE | X_UNBUF }
};
xFILE *xfunopen(void *cookie, int (*read)(void *, char *, int), int (*write)(void *, const char *, int), long (*seek)(void *, long, int), int (*close)(void *)) {
xFILE *fp;
for (fp = x_iob; fp < x_iob + XOPEN_MAX; fp++)
if ((fp->flag & (X_READ | X_WRITE)) == 0)
break; /* found free slot */
if (fp >= x_iob + XOPEN_MAX) /* no free slots */
return NULL;
fp->cnt = 0;
fp->base = NULL;
fp->flag = read? X_READ : X_WRITE;
fp->vtable.cookie = cookie;
fp->vtable.read = read;
fp->vtable.write = write;
fp->vtable.seek = seek;
fp->vtable.close = close;
return fp;
}
int xfclose(xFILE *fp) {
extern void free(void *); /* FIXME */
xfflush(fp);
fp->flag = 0;
if (fp->base != fp->buf)
free(fp->base);
return fp->vtable.close(fp->vtable.cookie);
}
int x_fillbuf(xFILE *fp) {
extern void *malloc(size_t); /* FIXME */
int bufsize;
if ((fp->flag & (X_READ|X_EOF|X_ERR)) != X_READ)
return EOF;
if (fp->base == NULL) {
if ((fp->flag & X_UNBUF) == 0) {
/* no buffer yet */
if ((fp->base = malloc(XBUFSIZ)) == NULL) {
/* can't get buffer, try unbuffered */
fp->flag |= X_UNBUF;
}
}
if (fp->flag & X_UNBUF) {
fp->base = fp->buf;
}
}
bufsize = (fp->flag & X_UNBUF) ? sizeof(fp->buf) : XBUFSIZ;
fp->ptr = fp->base;
fp->cnt = fp->vtable.read(fp->vtable.cookie, fp->ptr, bufsize);
if (--fp->cnt < 0) {
if (fp->cnt == -1)
fp->flag |= X_EOF;
else
fp->flag |= X_ERR;
fp->cnt = 0;
return EOF;
}
return (unsigned char) *fp->ptr++;
}
int x_flushbuf(int x, xFILE *fp) {
extern void *malloc(size_t); /* FIXME */
int num_written=0, bufsize=0;
char c = x;
if ((fp->flag & (X_WRITE|X_EOF|X_ERR)) != X_WRITE)
return EOF;
if (fp->base == NULL && ((fp->flag & X_UNBUF) == 0)) {
/* no buffer yet */
if ((fp->base = malloc(XBUFSIZ)) == NULL) {
/* couldn't allocate a buffer, so try unbuffered */
fp->flag |= X_UNBUF;
} else {
fp->ptr = fp->base;
fp->cnt = XBUFSIZ - 1;
}
}
if (fp->flag & X_UNBUF) {
/* unbuffered write */
fp->ptr = fp->base = NULL;
fp->cnt = 0;
if (x == EOF)
return EOF;
num_written = fp->vtable.write(fp->vtable.cookie, (const char *) &c, 1);
bufsize = 1;
} else {
/* buffered write */
assert(fp->ptr);
if (x != EOF) {
*fp->ptr++ = (unsigned char) c;
}
bufsize = (int)(fp->ptr - fp->base);
while(bufsize - num_written > 0) {
int t;
t = fp->vtable.write(fp->vtable.cookie, fp->base + num_written, bufsize - num_written);
if (t < 0)
break;
num_written += t;
}
fp->ptr = fp->base;
fp->cnt = BUFSIZ - 1;
}
if (num_written == bufsize) {
return x;
} else {
fp->flag |= X_ERR;
return EOF;
}
}
int xfflush(xFILE *f) {
int retval;
int i;
retval = 0;
if (f == NULL) {
/* flush all output streams */
for (i = 0; i < XOPEN_MAX; i++) {
if ((x_iob[i].flag & X_WRITE) && (xfflush(&x_iob[i]) == -1))
retval = -1;
}
} else {
if ((f->flag & X_WRITE) == 0)
return -1;
x_flushbuf(EOF, f);
if (f->flag & X_ERR)
retval = -1;
}
return retval;
}
int xfputc(int x, xFILE *fp) {
return xputc(x, fp);
}
int xfgetc(xFILE *fp) {
return xgetc(fp);
}
int xfputs(const char *s, xFILE *stream) {
const char *ptr = s;
while(*ptr != '\0') {
if (xputc(*ptr, stream) == EOF)
return EOF;
++ptr;
}
return (int)(ptr - s);
}
char *xfgets(char *s, int size, xFILE *stream) {
int c;
char *buf;
xfflush(NULL);
if (size == 0) {
return NULL;
}
buf = s;
while (--size > 0 && (c = xgetc(stream)) != EOF) {
if ((*buf++ = c) == '\n')
break;
}
*buf = '\0';
return (c == EOF && buf == s) ? NULL : s;
}
int xputs(const char *s) {
int i = 1;
while(*s != '\0') {
if (xputchar(*s++) == EOF)
return EOF;
i++;
}
if (xputchar('\n') == EOF) {
return EOF;
}
return i;
}
char *xgets(char *s) {
int c;
char *buf;
xfflush(NULL);
buf = s;
while ((c = xgetchar()) != EOF && c != '\n') {
*buf++ = c;
}
*buf = '\0';
return (c == EOF && buf == s) ? NULL : s;
}
int xungetc(int c, xFILE *fp) {
unsigned char uc = c;
if (c == EOF || fp->base == fp->ptr) {
return EOF;
}
fp->cnt++;
return *--fp->ptr = uc;
}
size_t xfread(void *ptr, size_t size, size_t count, xFILE *fp) {
char *bptr = ptr;
long nbytes;
int c;
nbytes = size * count;
while (nbytes > fp->cnt) {
memcpy(bptr, fp->ptr, fp->cnt);
fp->ptr += fp->cnt;
bptr += fp->cnt;
nbytes -= fp->cnt;
if ((c = x_fillbuf(fp)) == EOF) {
return (size * count - nbytes) / size;
} else {
xungetc(c, fp);
}
}
memcpy(bptr, fp->ptr, nbytes);
fp->ptr += nbytes;
fp->cnt -= nbytes;
return count;
}
size_t xfwrite(const void *ptr, size_t size, size_t count, xFILE *fp) {
const char *bptr = ptr;
long nbytes;
nbytes = size * count;
while (nbytes > fp->cnt) {
memcpy(fp->ptr, bptr, fp->cnt);
fp->ptr += fp->cnt;
bptr += fp->cnt;
nbytes -= fp->cnt;
if (x_flushbuf(EOF, fp) == EOF) {
return (size * count - nbytes) / size;
}
}
memcpy(fp->ptr, bptr, nbytes);
fp->ptr += nbytes;
fp->cnt -= nbytes;
return count;
}
long xfseek(xFILE *fp, long offset, int whence) {
long s;
xfflush(fp);
fp->ptr = fp->base;
fp->cnt = 0;
if ((s = fp->vtable.seek(fp->vtable.cookie, offset, whence)) != 0)
return s;
fp->flag &= ~X_EOF;
return 0;
}
long xftell(xFILE *fp) {
return xfseek(fp, 0, XSEEK_CUR);
}
void xrewind(xFILE *fp) {
xfseek(fp, 0, XSEEK_SET);
xclearerr(fp);
}
int xprintf(const char *fmt, ...) {
va_list ap;
int n;
va_start(ap, fmt);
n = xvfprintf(xstdout, fmt, ap);
va_end(ap);
return n;
}
int xfprintf(xFILE *stream, const char *fmt, ...) {
va_list ap;
int n;
va_start(ap, fmt);
n = xvfprintf(stream, fmt, ap);
va_end(ap);
return n;
}
static int print_int(xFILE *stream, long x, int base) {
static const char digits[] = "0123456789abcdef";
char buf[20];
int i, c, neg;
neg = 0;
if (x < 0) {
neg = 1;
x = -x;
}
i = 0;
do {
buf[i++] = digits[x % base];
} while ((x /= base) != 0);
if (neg) {
buf[i++] = '-';
}
c = i;
while (i-- > 0) {
xputc(buf[i], stream);
}
return c;
}
int xvfprintf(xFILE *stream, const char *fmt, va_list ap) {
const char *p;
char *sval;
int ival;
#if PIC_ENABLE_FLOAT
double dval;
#endif
void *vp;
int cnt = 0;
for (p = fmt; *p; p++) {
if (*p != '%') {
xputc(*p, stream);
cnt++;
continue;
}
switch (*++p) {
case 'd':
case 'i':
ival = va_arg(ap, int);
cnt += print_int(stream, ival, 10);
break;
#if PIC_ENABLE_FLOAT
case 'f':
dval = va_arg(ap, double);
cnt += print_int(stream, dval, 10);
xputc('.', stream);
cnt++;
if ((ival = fabs((dval - floor(dval)) * 1e4) + 0.5) == 0) {
cnt += xfputs("0000", stream);
} else {
int i;
for (i = 0; i < 3 - (int)log10(ival); ++i) {
xputc('0', stream);
cnt++;
}
cnt += print_int(stream, ival, 10);
}
break;
#endif
case 's':
sval = va_arg(ap, char*);
cnt += xfputs(sval, stream);
break;
case 'p':
vp = va_arg(ap, void*);
cnt += xfputs("0x", stream);
cnt += print_int(stream, (long)vp, 16);
break;
case '%':
xputc(*(p-1), stream);
cnt++;
break;
default:
xputc('%', stream);
xputc(*(p-1), stream);
cnt += 2;
break;
}
}
return cnt;
}
#if 0
int main()
{
char buf[256];
xgets(buf);
xprintf("%s\n", buf);
xprintf("hello\n");
xprintf("hello\n");
// xfflush(0);
}
#endif