#include "includes.h"
#define XBUFSIZE BUFSIZ
static XFILE _x_stdin = { 0, NULL, NULL, XBUFSIZE, 0, O_RDONLY, X_IOFBF, 0 };
static XFILE _x_stdout = { 1, NULL, NULL, XBUFSIZE, 0, O_WRONLY, X_IOLBF, 0 };
static XFILE _x_stderr = { 2, NULL, NULL, 0, 0, O_WRONLY, X_IONBF, 0 };
XFILE *x_stdin = &_x_stdin;
XFILE *x_stdout = &_x_stdout;
XFILE *x_stderr = &_x_stderr;
#define X_FLAG_EOF 1
#define X_FLAG_ERROR 2
#define X_FLAG_EINVAL 3
int x_setvbuf(XFILE *f, char *buf, int mode, size_t size)
{
x_fflush(f);
if (f->bufused) return -1;
if ((f->open_flags & O_ACCMODE) == O_RDONLY) {
mode = X_IOFBF;
}
SAFE_FREE(f->buf);
f->buf = 0;
f->bufsize = 0;
f->next = NULL;
f->bufused = 0;
f->buftype = mode;
if (f->buftype == X_IONBF) return 0;
if (size == 0) size = XBUFSIZE;
f->bufsize = size;
f->bufused = 0;
return 0;
}
static int x_allocate_buffer(XFILE *f)
{
if (f->buf) return 1;
if (f->bufsize == 0) return 0;
f->buf = SMB_MALLOC(f->bufsize);
if (!f->buf) return 0;
f->next = f->buf;
return 1;
}
XFILE *x_fopen(const char *fname, int flags, mode_t mode)
{
XFILE *ret;
ret = SMB_MALLOC_P(XFILE);
if (!ret) return NULL;
memset(ret, 0, sizeof(XFILE));
if ((flags & O_ACCMODE) == O_RDWR) {
errno = EINVAL;
return NULL;
}
ret->open_flags = flags;
ret->fd = sys_open(fname, flags, mode);
if (ret->fd == -1) {
SAFE_FREE(ret);
return NULL;
}
x_setvbuf(ret, NULL, X_IOFBF, XBUFSIZE);
return ret;
}
int x_fclose(XFILE *f)
{
int ret;
x_fflush(f);
ret = close(f->fd);
f->fd = -1;
if (f->buf) {
memset(f->buf, 0, f->bufsize);
SAFE_FREE(f->buf);
}
if (f != x_stdin && f != x_stdout && f != x_stderr) {
SAFE_FREE(f);
}
return ret;
}
size_t x_fwrite(const void *p, size_t size, size_t nmemb, XFILE *f)
{
ssize_t ret;
size_t total=0;
if (f->buftype == X_IONBF ||
(!f->buf && !x_allocate_buffer(f))) {
ret = write(f->fd, p, size*nmemb);
if (ret == -1) return -1;
return ret/size;
}
while (total < size*nmemb) {
size_t n = f->bufsize - f->bufused;
n = MIN(n, (size*nmemb)-total);
if (n == 0) {
x_fflush(f);
continue;
}
memcpy(f->buf + f->bufused, total+(const char *)p, n);
f->bufused += n;
total += n;
}
if (f->buftype == X_IOLBF && f->bufused) {
int i;
for (i=(size*nmemb)-1; i>=0; i--) {
if (*(i+(const char *)p) == '\n') {
x_fflush(f);
break;
}
}
}
return total/size;
}
int x_vfprintf(XFILE *f, const char *format, va_list ap)
{
char *p;
int len, ret;
va_list ap2;
VA_COPY(ap2, ap);
len = vasprintf(&p, format, ap2);
if (len <= 0) return len;
ret = x_fwrite(p, 1, len, f);
SAFE_FREE(p);
return ret;
}
int x_fprintf(XFILE *f, const char *format, ...)
{
va_list ap;
int ret;
va_start(ap, format);
ret = x_vfprintf(f, format, ap);
va_end(ap);
return ret;
}
int x_fileno(XFILE *f)
{
return f->fd;
}
int x_fflush(XFILE *f)
{
int ret;
if (f->flags & X_FLAG_ERROR) return -1;
if ((f->open_flags & O_ACCMODE) != O_WRONLY) {
errno = EINVAL;
return -1;
}
if (f->bufused == 0) return 0;
ret = write(f->fd, f->buf, f->bufused);
if (ret == -1) return -1;
f->bufused -= ret;
if (f->bufused > 0) {
f->flags |= X_FLAG_ERROR;
memmove(f->buf, ret + (char *)f->buf, f->bufused);
return -1;
}
return 0;
}
void x_setbuffer(XFILE *f, char *buf, size_t size)
{
x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, size);
}
void x_setbuf(XFILE *f, char *buf)
{
x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, XBUFSIZE);
}
void x_setlinebuf(XFILE *f)
{
x_setvbuf(f, NULL, X_IOLBF, 0);
}
int x_feof(XFILE *f)
{
if (f->flags & X_FLAG_EOF) return 1;
return 0;
}
int x_ferror(XFILE *f)
{
if (f->flags & X_FLAG_ERROR) return 1;
return 0;
}
static void x_fillbuf(XFILE *f)
{
int n;
if (f->bufused) return;
if (!f->buf && !x_allocate_buffer(f)) return;
n = read(f->fd, f->buf, f->bufsize);
if (n <= 0) return;
f->bufused = n;
f->next = f->buf;
}
int x_fgetc(XFILE *f)
{
int ret;
if (f->flags & (X_FLAG_EOF | X_FLAG_ERROR)) return EOF;
if (f->bufused == 0) x_fillbuf(f);
if (f->bufused == 0) {
f->flags |= X_FLAG_EOF;
return EOF;
}
ret = *(unsigned char *)(f->next);
f->next++;
f->bufused--;
return ret;
}
size_t x_fread(void *p, size_t size, size_t nmemb, XFILE *f)
{
size_t total = 0;
while (total < size*nmemb) {
int c = x_fgetc(f);
if (c == EOF) break;
(total+(char *)p)[0] = (char)c;
total++;
}
return total/size;
}
char *x_fgets(char *s, int size, XFILE *stream)
{
char *s0 = s;
int l = size;
while (l>1) {
int c = x_fgetc(stream);
if (c == EOF) break;
*s++ = (char)c;
l--;
if (c == '\n') break;
}
if (l==size || x_ferror(stream)) {
return 0;
}
*s = 0;
return s0;
}
off_t x_tseek(XFILE *f, off_t offset, int whence)
{
if (f->flags & X_FLAG_ERROR)
return -1;
if (whence != SEEK_SET && whence != SEEK_END) {
f->flags |= X_FLAG_EINVAL;
errno = EINVAL;
return -1;
}
switch (f->open_flags & O_ACCMODE) {
case O_RDONLY:
f->bufused = 0;
break;
case O_WRONLY:
if (x_fflush(f) != 0)
return -1;
break;
default:
errno = EINVAL;
return -1;
}
f->flags &= ~X_FLAG_EOF;
return (off_t)sys_lseek(f->fd, offset, whence);
}