#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <cups/string.h>
#include <errno.h>
#ifdef WIN32
# include <io.h>
#else
# include <unistd.h>
# include <fcntl.h>
#endif
#include "file.h"
static int cups_fill(cups_file_t *fp);
static int cups_read(int fd, char *buf, int bytes);
static int cups_write(int fd, const char *buf, int bytes);
int
cupsFileClose(cups_file_t *fp)
{
int fd;
if (!fp)
return (-1);
#ifdef HAVE_LIBZ
if (fp->compressed && fp->mode == 'r')
inflateEnd(&fp->stream);
#endif
if (fp->mode == 'w')
cupsFileFlush(fp);
fd = fp->fd;
free(fp);
return (close(fd));
}
int
cupsFileFlush(cups_file_t *fp)
{
int bytes;
if (!fp || fp->mode != 'w')
return (-1);
bytes = fp->ptr - fp->buf;
if (bytes > 0)
{
if (cups_write(fp->fd, fp->buf, bytes) < bytes)
return (-1);
fp->ptr = fp->buf;
}
return (0);
}
int
cupsFileGetChar(cups_file_t *fp)
{
if (!fp || fp->mode != 'r')
return (-1);
if (fp->ptr >= fp->end)
if (cups_fill(fp) < 0)
return (-1);
return (*(fp->ptr)++ & 255);
}
char *
cupsFileGets(cups_file_t *fp,
char *buf,
int buflen)
{
int ch;
char *ptr,
*end;
if (!fp || fp->mode != 'r' || !buf || buflen < 2)
return (NULL);
for (ptr = buf, end = buf + buflen - 1; ptr < end ;)
{
if (fp->ptr >= fp->end)
if (cups_fill(fp) <= 0)
{
if (ptr == buf)
return (NULL);
else
break;
}
ch = *(fp->ptr)++;
if (ch == '\r')
{
if (fp->ptr >= fp->end)
if (cups_fill(fp) <= 0)
break;
if (*(fp->ptr) == '\n')
fp->ptr ++;
break;
}
else if (ch == '\n')
{
break;
}
else
*ptr++ = ch;
}
*ptr = '\0';
return (buf);
}
cups_file_t *
cupsFileOpen(const char *filename,
const char *mode)
{
cups_file_t *fp;
int o;
if (!filename || !mode || (*mode != 'r' && *mode != 'w' && *mode != 'a'))
return (NULL);
if ((fp = calloc(1, sizeof(cups_file_t))) == NULL)
return (NULL);
switch (*mode)
{
case 'a' :
o = O_RDWR | O_CREAT;
fp->mode = 'w';
break;
case 'r' :
o = O_RDONLY;
fp->mode = 'r';
break;
case 'w' :
o = O_WRONLY | O_TRUNC | O_CREAT;
fp->mode = 'w';
break;
default :
return (NULL);
}
if ((fp->fd = open(filename, o, 0644)) < 0)
{
free(fp);
return (NULL);
}
fcntl(fp->fd, F_SETFD, fcntl(fp->fd, F_GETFD) | FD_CLOEXEC);
if (*mode == 'a')
fp->pos = lseek(fp->fd, 0, SEEK_END);
else
fp->pos = 0;
if (*mode != 'r')
{
fp->ptr = fp->buf;
fp->end = fp->buf + sizeof(fp->buf);
}
return (fp);
}
int
cupsFilePrintf(cups_file_t *fp,
const char *format,
...)
{
va_list ap;
int bytes;
char buf[2048];
if (!fp || !format || fp->mode != 'w')
return (-1);
va_start(ap, format);
bytes = vsnprintf(buf, sizeof(buf), format, ap);
va_end(ap);
if ((fp->ptr + bytes) > fp->end)
if (cupsFileFlush(fp))
return (-1);
fp->pos += bytes;
if (bytes > sizeof(fp->buf))
return (cups_write(fp->fd, buf, bytes));
else
{
memcpy(fp->ptr, buf, bytes);
fp->ptr += bytes;
return (bytes);
}
}
int
cupsFilePutChar(cups_file_t *fp,
int c)
{
if (!fp || fp->mode != 'w')
return (-1);
if (fp->ptr >= fp->end)
if (cupsFileFlush(fp))
return (-1);
*(fp->ptr) ++ = c;
fp->pos ++;
return (0);
}
int
cupsFilePuts(cups_file_t *fp,
const char *s)
{
int bytes;
if (!fp || !s || fp->mode != 'w')
return (-1);
bytes = strlen(s);
if ((fp->ptr + bytes) > fp->end)
if (cupsFileFlush(fp))
return (-1);
fp->pos += bytes;
if (bytes > sizeof(fp->buf))
return (cups_write(fp->fd, s, bytes));
else
{
memcpy(fp->ptr, s, bytes);
fp->ptr += bytes;
return (bytes);
}
}
int
cupsFileRead(cups_file_t *fp,
char *buf,
int bytes)
{
int total,
count;
if (!fp || !buf || bytes < 0 || fp->mode != 'r')
return (-1);
if (bytes == 0)
return (0);
total = 0;
while (bytes > 0)
{
if (fp->ptr >= fp->end)
if (cups_fill(fp) <= 0)
{
if (total > 0)
return (total);
else
return (-1);
}
count = fp->end - fp->ptr;
if (count > bytes)
count = bytes;
memcpy(buf, fp->ptr, count);
fp->ptr += count;
bytes -= count;
total += count;
buf += count;
}
return (total);
}
off_t
cupsFileSeek(cups_file_t *fp,
off_t pos)
{
int bytes;
if (!fp || pos < 0 || fp->mode != 'r')
return (-1);
bytes = fp->end - fp->buf;
if (pos < fp->pos)
{
#ifdef HAVE_LIBZ
if (fp->compressed)
{
inflateEnd(&fp->stream);
lseek(fp->fd, 0, SEEK_SET);
fp->pos = 0;
fp->ptr = NULL;
fp->end = NULL;
fp->eof = 0;
while ((bytes = cups_fill(fp)) > 0)
if (pos >= fp->pos && pos < (fp->pos + bytes))
break;
if (bytes <= 0)
return (-1);
}
else
#endif
{
lseek(fp->fd, pos, SEEK_SET);
fp->pos = pos;
fp->ptr = NULL;
fp->end = NULL;
#ifdef HAVE_LIBZ
fp->eof = 0;
#endif
}
}
else if (pos >= (fp->pos + bytes))
{
#ifdef HAVE_LIBZ
if (fp->compressed)
{
while ((bytes = cups_fill(fp)) > 0)
if (pos >= fp->pos && pos < (fp->pos + bytes))
break;
if (bytes <= 0)
return (-1);
}
else
#endif
{
lseek(fp->fd, pos, SEEK_SET);
fp->pos = pos;
fp->ptr = NULL;
fp->end = NULL;
#ifdef HAVE_LIBZ
fp->eof = 0;
#endif
}
}
else
{
fp->ptr = fp->buf + pos - fp->pos;
#ifdef HAVE_LIBZ
fp->eof = 0;
#endif
}
return (pos);
}
int
cupsFileWrite(cups_file_t *fp,
const char *buf,
int bytes)
{
if (!fp || !buf || bytes < 0 || fp->mode != 'w')
return (-1);
if (bytes == 0)
return (0);
if ((fp->ptr + bytes) > fp->end)
if (cupsFileFlush(fp))
return (-1);
fp->pos += bytes;
if (bytes > sizeof(fp->buf))
return (cups_write(fp->fd, buf, bytes));
else
{
memcpy(fp->ptr, buf, bytes);
fp->ptr += bytes;
return (bytes);
}
}
static int
cups_fill(cups_file_t *fp)
{
int bytes;
#ifdef HAVE_LIBZ
const unsigned char *ptr,
*end;
#endif
if (fp->ptr)
fp->pos += fp->end - fp->buf;
#ifdef HAVE_LIBZ
if (!fp->ptr)
{
fp->compressed = 0;
fp->pos = 0;
if ((bytes = cups_read(fp->fd, (char *)fp->buf, sizeof(fp->buf))) < 0)
{
return (-1);
}
if (bytes < 10 || fp->buf[0] != 0x1f || fp->buf[1] != 0x8b ||
fp->buf[2] != 8 || (fp->buf[3] & 0xe0) != 0)
{
fp->ptr = fp->buf;
fp->end = fp->buf + bytes;
return (bytes);
}
if (bytes > sizeof(fp->cbuf))
bytes = sizeof(fp->cbuf);
memcpy(fp->cbuf, fp->buf, bytes);
lseek(fp->fd, bytes, SEEK_SET);
ptr = fp->cbuf + 10;
end = fp->cbuf + bytes;
if (fp->cbuf[3] & 0x04)
{
if ((ptr + 2) > end)
{
return (-1);
}
bytes = ((unsigned char)ptr[1] << 8) | (unsigned char)ptr[0];
ptr += 2 + bytes;
if (ptr > end)
{
return (-1);
}
}
if (fp->cbuf[3] & 0x08)
{
while (ptr < end && *ptr)
ptr ++;
if (ptr < end)
ptr ++;
else
{
return (-1);
}
}
if (fp->cbuf[3] & 0x10)
{
while (ptr < end && *ptr)
ptr ++;
if (ptr < end)
ptr ++;
else
{
return (-1);
}
}
if (fp->cbuf[3] & 0x02)
{
ptr += 2;
if (ptr > end)
{
return (-1);
}
}
fp->stream.zalloc = (alloc_func)0;
fp->stream.zfree = (free_func)0;
fp->stream.opaque = (voidpf)0;
fp->stream.next_in = (Bytef *)ptr;
fp->stream.next_out = NULL;
fp->stream.avail_in = end - ptr;
fp->stream.avail_out = 0;
if (inflateInit2(&(fp->stream), -15) != Z_OK)
return (-1);
fp->compressed = 1;
}
if (fp->compressed)
{
if (fp->eof)
return (-1);
if (fp->stream.avail_in == 0)
{
if ((bytes = cups_read(fp->fd, (char *)fp->cbuf, sizeof(fp->cbuf))) <= 0)
return (-1);
fp->stream.next_in = fp->cbuf;
fp->stream.avail_in = bytes;
}
fp->stream.next_out = (Bytef *)fp->buf;
fp->stream.avail_out = sizeof(fp->buf);
if (inflate(&(fp->stream), Z_NO_FLUSH) == Z_STREAM_END)
{
fp->eof = 1;
}
bytes = sizeof(fp->buf) - fp->stream.avail_out;
fp->ptr = fp->buf;
fp->end = fp->buf + bytes;
return (bytes);
}
#endif
if ((bytes = cups_read(fp->fd, fp->buf, sizeof(fp->buf))) <= 0)
{
fp->ptr = fp->buf;
fp->end = fp->buf;
return (-1);
}
fp->ptr = fp->buf;
fp->end = fp->buf + bytes;
return (bytes);
}
int
cups_read(int fd,
char *buf,
int bytes)
{
int total;
while ((total = read(fd, buf, bytes)) < 0)
{
if (errno == EAGAIN || errno == EINTR)
continue;
else
return (-1);
}
return (total);
}
int
cups_write(int fd,
const char *buf,
int bytes)
{
int total,
count;
total = 0;
while (bytes > 0)
{
if ((count = write(fd, buf, bytes)) < 0)
{
if (errno == EAGAIN || errno == EINTR)
continue;
else
return (-1);
}
bytes -= count;
total += count;
buf += count;
}
return (total);
}