forked from Telodendria/Telodendria
Jordan Bancino
92da3542a6
The Stream API now provides the buffered I/O functionality analogous to the C standard library.
319 lines
5.1 KiB
C
319 lines
5.1 KiB
C
#include <Stream.h>
|
|
|
|
#ifndef STREAM_BUFFER
|
|
#define STREAM_BUFFER 4096
|
|
#endif
|
|
|
|
#define STREAM_EOF (1 << 0)
|
|
#define STREAM_ERR (1 << 1)
|
|
|
|
#include <Io.h>
|
|
#include <Memory.h>
|
|
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
|
|
struct Stream
|
|
{
|
|
Io *io;
|
|
|
|
int *rBuf;
|
|
size_t rLen;
|
|
size_t rOff;
|
|
|
|
int *wBuf;
|
|
size_t wLen;
|
|
|
|
int *ugBuf;
|
|
size_t ugSize;
|
|
size_t ugLen;
|
|
|
|
int flags : 2;
|
|
};
|
|
|
|
Stream *
|
|
StreamOpen(Io * io)
|
|
{
|
|
Stream *stream;
|
|
|
|
if (!io)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
stream = Malloc(sizeof(Stream));
|
|
if (!stream)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
memset(stream, 0, sizeof(Stream));
|
|
stream->io = io;
|
|
|
|
return stream;
|
|
}
|
|
|
|
int
|
|
StreamClose(Stream *stream)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (!stream)
|
|
{
|
|
errno = EBADF;
|
|
return EOF;
|
|
}
|
|
|
|
if (stream->rBuf)
|
|
{
|
|
Free(stream->rBuf);
|
|
}
|
|
|
|
if (stream->wBuf)
|
|
{
|
|
ssize_t writeRes = IoWrite(stream->io, stream->wBuf, stream->wLen);
|
|
Free(stream->wBuf);
|
|
|
|
if (writeRes == -1)
|
|
{
|
|
ret = EOF;
|
|
}
|
|
}
|
|
|
|
if (stream->ugBuf)
|
|
{
|
|
Free(stream->ugBuf);
|
|
}
|
|
|
|
ret = IoClose(stream->io);
|
|
Free(stream);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
StreamVprintf(Stream * stream, const char *fmt, va_list ap)
|
|
{
|
|
if (!stream)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
StreamFlush(stream); /* Flush the buffer out before doing the printf */
|
|
|
|
/* Defer printf to underlying Io. We probably should buffer the
|
|
* printf operation just like StreamPutc() so we don't have to
|
|
* flush the buffer.
|
|
*/
|
|
return IoVprintf(stream->io, fmt, ap);
|
|
}
|
|
|
|
int
|
|
StreamPrintf(Stream * stream, const char *fmt,...)
|
|
{
|
|
int ret;
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
ret = StreamVprintf(stream, fmt, ap);
|
|
va_end(ap);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
StreamGetc(Stream * stream)
|
|
{
|
|
int c;
|
|
|
|
if (!stream)
|
|
{
|
|
errno = EBADF;
|
|
return EOF;
|
|
}
|
|
|
|
/* Empty the ungetc stack first */
|
|
if (stream->ugLen)
|
|
{
|
|
c = stream->ugBuf[stream->ugLen - 1];
|
|
stream->ugLen--;
|
|
return c;
|
|
}
|
|
|
|
if (stream->flags & EOF)
|
|
{
|
|
return EOF;
|
|
}
|
|
|
|
if (!stream->rBuf)
|
|
{
|
|
/* No buffer allocated yet */
|
|
stream->rBuf = Malloc(STREAM_BUFFER * sizeof(int));
|
|
if (!stream->rBuf)
|
|
{
|
|
stream->flags |= STREAM_ERR;
|
|
return EOF;
|
|
}
|
|
|
|
stream->rOff = 0;
|
|
stream->rLen = 0;
|
|
}
|
|
|
|
if (stream->rOff >= stream->rLen)
|
|
{
|
|
/* We read through the entire buffer; get a new one */
|
|
ssize_t readRes = IoRead(stream->io, stream->rBuf, STREAM_BUFFER);
|
|
|
|
if (readRes == 0)
|
|
{
|
|
stream->flags |= STREAM_EOF;
|
|
return EOF;
|
|
}
|
|
|
|
if (readRes == -1)
|
|
{
|
|
stream->flags |= STREAM_ERR;
|
|
return EOF;
|
|
}
|
|
|
|
stream->rOff = 0;
|
|
stream->rLen = readRes;
|
|
}
|
|
|
|
/* Read the character in the buffer and advance the offset */
|
|
c = stream->rBuf[stream->rOff];
|
|
stream->rOff++;
|
|
|
|
return c;
|
|
}
|
|
|
|
int
|
|
StreamUngetc(Stream * stream, int c)
|
|
{
|
|
if (!stream)
|
|
{
|
|
errno = EBADF;
|
|
return EOF;
|
|
}
|
|
|
|
if (!stream->ugBuf)
|
|
{
|
|
stream->ugSize = STREAM_BUFFER;
|
|
stream->ugBuf = Malloc(stream->ugSize);
|
|
|
|
if (!stream->ugBuf)
|
|
{
|
|
stream->flags |= STREAM_ERR;
|
|
return EOF;
|
|
}
|
|
}
|
|
|
|
if (stream->ugLen >= stream->ugSize)
|
|
{
|
|
int *new;
|
|
|
|
stream->ugSize += STREAM_BUFFER;
|
|
new = Realloc(stream->ugBuf, stream->ugSize);
|
|
if (!new)
|
|
{
|
|
stream->flags |= STREAM_ERR;
|
|
Free(stream->ugBuf);
|
|
stream->ugBuf = NULL;
|
|
return EOF;
|
|
}
|
|
|
|
Free(stream->ugBuf);
|
|
stream->ugBuf = new;
|
|
}
|
|
|
|
stream->ugBuf[stream->ugLen - 1] = c;
|
|
stream->ugLen++;
|
|
|
|
return c;
|
|
}
|
|
|
|
int
|
|
StreamPutc(Stream * stream, int c)
|
|
{
|
|
if (!stream)
|
|
{
|
|
errno = EBADF;
|
|
return EOF;
|
|
}
|
|
|
|
if (!stream->wBuf)
|
|
{
|
|
stream->wBuf = Malloc(STREAM_BUFFER * sizeof(int));
|
|
if (!stream->wBuf)
|
|
{
|
|
stream->flags |= STREAM_ERR;
|
|
return EOF;
|
|
}
|
|
}
|
|
|
|
if (stream->wLen == STREAM_BUFFER)
|
|
{
|
|
/* Buffer full; write it */
|
|
ssize_t writeRes = IoWrite(stream->io, stream->wBuf, stream->wLen);
|
|
|
|
if (writeRes == -1)
|
|
{
|
|
stream->flags |= STREAM_ERR;
|
|
return EOF;
|
|
}
|
|
|
|
stream->wLen = 0;
|
|
}
|
|
|
|
stream->wBuf[stream->wLen] = c;
|
|
stream->wLen++;
|
|
|
|
return c;
|
|
}
|
|
|
|
int
|
|
StreamEof(Stream * stream)
|
|
{
|
|
return stream && (stream->flags & STREAM_EOF);
|
|
}
|
|
|
|
int
|
|
StreamError(Stream * stream)
|
|
{
|
|
return stream && (stream->flags & STREAM_ERR);
|
|
}
|
|
|
|
void
|
|
StreamClearError(Stream * stream)
|
|
{
|
|
if (stream)
|
|
{
|
|
stream->flags &= ~STREAM_ERR;
|
|
}
|
|
}
|
|
|
|
int
|
|
StreamFlush(Stream * stream)
|
|
{
|
|
if (!stream)
|
|
{
|
|
errno = EBADF;
|
|
return EOF;
|
|
}
|
|
|
|
if (stream->wLen)
|
|
{
|
|
ssize_t writeRes = IoWrite(stream->io, stream->wBuf, stream->wLen);
|
|
|
|
if (writeRes == -1)
|
|
{
|
|
stream->flags |= STREAM_ERR;
|
|
return EOF;
|
|
}
|
|
|
|
stream->wLen = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|