femtolisp/llt/attic/ios.c.old

397 lines
8.2 KiB
C

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
#include <errno.h>
#ifdef WIN32
#include <malloc.h>
#include <io.h>
#include <fcntl.h>
#define fileno _fileno
#else
#include <unistd.h>
#include <sys/time.h>
#include <sys/select.h>
#endif
#include "dtypes.h"
#include "utils.h"
#include "utf8.h"
#include "ios.h"
#include "socket.h"
/* OS-level primitive wrappers */
// return error code, #bytes read in *nread
static int _os_read(long fd, void *buf, size_t n, size_t *nread)
{
ssize_t r = read((int)fd, buf, n);
if (r == -1) {
*nread = 0;
return errno;
}
*nread = (size_t)r;
return 0;
}
static int _os_write(long fd, void *buf, size_t n, size_t *nwritten)
{
ssize_t r = write((int)fd, buf, n);
if (r == -1) {
*nwritten = 0;
return errno;
}
*nread = (size_t)r;
return 0;
}
static int _fd_available(long fd)
{
#ifndef WIN32
fd_set set;
struct timeval tv = {0, 0};
FD_ZERO(&set);
FD_SET(fd, &set);
return (select(fd+1, &set, NULL, NULL, &tv)!=0);
#else
return 0;
#endif
}
/* internal utility functions */
static char *_buf_realloc(ios_t *s, size_t sz)
{
char *temp;
if (sz <= s->maxsize)
return s->buf;
if ((s->buf==NULL || s->buf==&s->local[0]) && (sz <= IOS_INLSIZE)) {
/* TODO: if we want to allow shrinking, see if the buffer shrank
down to this size, in which case we need to copy. */
s->buf = &s->local[0];
s->maxsize = IOS_INLSIZE;
s->ownbuf = 1;
return s->buf;
}
else if (s->ownbuf && s->buf != &s->local[0]) {
// if we own the buffer we're free to resize it
// always allocate 1 bigger in case user wants to add a NUL
// terminator after taking over the buffer
temp = realloc(s->buf, sz+1);
if (temp == NULL)
return NULL;
}
else {
temp = malloc(sz+1);
s->ownbuf = 1;
if (temp == NULL)
return NULL;
}
if (s->buf != temp && s->size > 0)
memcpy(temp, s->buf, s->size);
s->buf = temp;
s->maxsize = sz;
return s->buf;
}
// write a block of data into the buffer at the current position, resizing
// if necessary. returns # written.
static size_t _writebuf_force(ios_t *s, char *data, size_t n)
{
size_t amt;
size_t newsize;
if (n == 0)
return 0;
if (s->bpos + n > s->size) {
if (s->bpos + n > s->maxsize) {
/* TO DO: here you might want to add a mechanism for limiting
the growth of the stream. */
newsize = s->maxsize * 2;
while (s->bpos + n > newsize)
newsize *= 2;
if (_buf_realloc(s, newsize) == NULL) {
/* no more space; write as much as we can */
amt = s->maxsize - s->bpos;
if (amt > 0) {
memcpy(&s->buf[s->bpos], data, amt);
}
s->bpos += amt;
s->size = s->maxsize;
return amt;
}
}
s->size = s->bpos + n;
}
memcpy(&s->buf[s->bpos], data, n);
s->bpos += n;
return n;
}
/* interface functions, low level */
size_t ios_read(ios_t *s, char *dest, size_t n)
{
}
size_t ios_write(ios_t *s, char *data, size_t n)
{
}
off_t ios_seek(ios_t *s, off_t pos)
{
}
off_t ios_seek_end(ios_t *s)
{
}
off_t ios_skip(ios_t *s, off_t offs)
{
}
off_t ios_pos(ios_t *s)
{
if (s->bm == bm_mem)
return (off_t)s->bpos;
off_t fdpos = lseek(s->fd, 0, SEEK_CUR);
if (fdpos == (off_t)-1)
return fdpos;
if (s->state == iost_wr)
fdpos += s->bpos;
else if (s->state == iost_rd)
fdpos -= (s->size - s->bpos);
return fdpos;
}
size_t ios_trunc(ios_t *s, size_t size)
{
}
int ios_eof(ios_t *s)
{
if (s->bm == bm_mem)
return (s->bpos >= s->size);
if (s->fd == -1)
return 1;
// todo
}
static void _discard_partial_buffer(ios_t *s)
{
// this function preserves the invariant that data to write
// begins at the beginning of the buffer, and s->size refers
// to how much valid file data is stored in the buffer.
// this needs to be called when normal operation is interrupted in
// the middle of the buffer. "normal operation" is reading or
// writing to the end of the buffer. this happens e.g. when flushing.
if (s->bpos && s->size > s->bpos) {
memmove(s->buf, s->buf + s->bpos, s->size - s->bpos);
}
s->size -= s->bpos;
s->bpos = 0;
}
int ios_flush(ios_t *s)
{
if (ndirty == 0 || s->bm == bm_mem || s->buf == NULL)
return 0;
if (s->fd == -1)
return -1;
int partialb=0;
if (s->bitpos > 0 && s->ndirty==s->bpos+1) {
// flushing partial byte
partialb=1;
}
size_t nw, ntowrite=s->ndirty;
int err = _os_write(s->fd, s->buf, ntowrite, &nw);
// todo: try recovering from some kinds of errors (e.g. retry)
if (partialb) {
// skip back 1, since we might have to write this byte again
// if more bits in it are changed
if (lseek(s->fd, -1, SEEK_CUR) == (off_t)-1) {
// uhoh. can't write the "rest" of this byte. move to next
// byte instead.
s->bpos++;
s->bitpos = 0;
}
}
_discard_partial_buffer(s);
s->ndirty = 0;
if (err)
return err;
if (nw < ntowrite)
return -1;
return 0;
}
void ios_close(ios_t *s)
{
ios_flush(s);
if (s->fd != -1 && s->ownfd)
close(s->fd);
s->fd = -1;
}
char *ios_takebuf(ios_t *s, size_t *psize)
{
char *buf;
ios_flush(s);
if (s->buf == &s->local[0]) {
buf = malloc(s->size+1);
if (buf == NULL)
return NULL;
if (s->size)
memcpy(buf, s->buf, s->size);
buf[s->size] = '\0';
}
else {
buf = s->buf;
}
*psize = s->size+1; // buffer is actually 1 bigger for terminating NUL
/* empty stream and reinitialize */
if (s->bm == bm_mem || s->bm == bm_none) {
s->buf = &s->local[0];
s->maxsize = IOS_INLSIZE;
}
else {
s->buf = NULL;
_buf_realloc(s, IOS_BUFSIZE);
}
s->size = s->bpos = 0;
s->bitpos = 0;
return buf;
}
int ios_setbuf(ios_t *s, char *buf, size_t size, int own)
{
ios_flush(s);
size_t nvalid=0;
nvalid = s->size;
if (s->size == s->bpos && s->bitpos>0)
nvalid++;
if (size < nvalid)
nvalid = size;
memcpy(buf, s->buf, nvalid);
if (s->bpos > nvalid) {
// truncated
s->bpos = nvalid;
s->bitpos = 0;
}
s->size = nvalid;
if (s->buf!=NULL && s->ownbuf && s->buf!=&s->local[0])
free(s->buf);
s->buf = buf;
s->maxsize = size;
s->ownbuf = own;
return 0;
}
int ios_bufmode(ios_t *s, bufmode_t mode)
{
// no fd; can only do mem-only buffering
if (s->fd == -1 && mode != bm_mem)
return -1;
s->bm = mode;
return 0;
}
void ios_bswap(ios_t *s, int bswap)
{
s->byteswap = !!bswap;
}
int ios_copy(ios_t *to, ios_t *from, size_t nbytes, bool_t all)
{
}
/* stream object initializers. we do no allocation. */
ios_t *ios_file(ios_t *s, char *fname, int create, int rewrite)
{
}
ios_t *ios_mem(ios_t *s, size_t initsize)
{
}
ios_t *ios_fd(ios_t *s, long fd)
{
}
/* higher level interface */
int ios_putbit(ios_t *s, int bit)
{
byte_t mask = 1<<s->bitpos;
if (_ios_setstate(s, iost_wr))
return 0;
if (!s->dirty) {
// haven't written any bits yet
// if stenciling is turned on, the buffer is already full and
// we don't need to do anything
if (s->rereadable && !s->stenciled) {
// fill buffer if we can
}
}
if (bit)
s->buf[s->bpos] |= mask;
else
s->buf[s->bpos] &= ~mask;
s->dirty = 1;
s->bitpos++;
if (s->bitpos > 7) {
s->bitpos = 0;
s->bpos++;
s->tally++;
}
return 1;
}
int ios_getbit(ios_t *s, int *pbit)
{
byte_t mask = 1<<s->bitpos;
if (_ios_setstate(s, iost_rd))
return 0;
*pbit = (s->buf[s->bpos] & mask) ? 1 : 0;
s->bitpos++;
if (s->bitpos > 7) {
s->bitpos = 0;
s->bpos++;
s->tally++;
}
return 1;
}