mirror of
https://github.com/open-simh/simtools.git
synced 2026-01-13 15:27:18 +00:00
414 lines
9.1 KiB
C
414 lines
9.1 KiB
C
/* functions for managing a stack of file and buffer input streams. */
|
|
|
|
/*
|
|
|
|
Copyright (c) 2001, Richard Krehbiel
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are
|
|
met:
|
|
|
|
o Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
o Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
o Neither the name of the copyright holder nor the names of its
|
|
contributors may be used to endorse or promote products derived from
|
|
this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
|
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
|
DAMAGE.
|
|
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <stdarg.h>
|
|
|
|
#include "util.h"
|
|
|
|
#include "stream2.h"
|
|
|
|
/* BUFFER functions */
|
|
|
|
/* new_buffer allocates a new buffer */
|
|
|
|
BUFFER *new_buffer(
|
|
void)
|
|
{
|
|
BUFFER *buf = memcheck(malloc(sizeof(BUFFER)));
|
|
|
|
buf->length = 0;
|
|
buf->size = 0;
|
|
buf->use = 1;
|
|
buf->buffer = NULL;
|
|
return buf;
|
|
}
|
|
|
|
/* buffer_resize makes the buffer at least the requested size. */
|
|
/* If the buffer is already larger, then it will attempt */
|
|
/* to shrink it. */
|
|
|
|
void buffer_resize(
|
|
BUFFER *buff,
|
|
int size)
|
|
{
|
|
buff->size = size;
|
|
buff->length = size;
|
|
|
|
if (size == 0) {
|
|
free(buff->buffer);
|
|
buff->buffer = NULL;
|
|
} else {
|
|
if (buff->buffer == NULL)
|
|
buff->buffer = memcheck(malloc(buff->size));
|
|
else
|
|
buff->buffer = memcheck(realloc(buff->buffer, buff->size));
|
|
}
|
|
}
|
|
|
|
/* buffer_clone makes a copy of a buffer */
|
|
/* Basically it increases the use count */
|
|
|
|
BUFFER *buffer_clone(
|
|
BUFFER *from)
|
|
{
|
|
if (from)
|
|
from->use++;
|
|
return from;
|
|
}
|
|
|
|
/* buffer_free frees a buffer */
|
|
/* It decreases the use count, and if zero, */
|
|
/* frees the memory. */
|
|
|
|
void buffer_free(
|
|
BUFFER *buf)
|
|
{
|
|
if (buf) {
|
|
if (--(buf->use) == 0) {
|
|
free(buf->buffer);
|
|
free(buf);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Append characters to the buffer. */
|
|
|
|
void buffer_appendn(
|
|
BUFFER *buf,
|
|
char *str,
|
|
int len)
|
|
{
|
|
int needed = buf->length + len + 1;
|
|
|
|
if (needed >= buf->size) {
|
|
buf->size = needed + GROWBUF_INCR;
|
|
|
|
if (buf->buffer == NULL)
|
|
buf->buffer = memcheck(malloc(buf->size));
|
|
else
|
|
buf->buffer = memcheck(realloc(buf->buffer, buf->size));
|
|
}
|
|
|
|
memcpy(buf->buffer + buf->length, str, len);
|
|
buf->length += len;
|
|
buf->buffer[buf->length] = 0;
|
|
}
|
|
|
|
/* append a text line (zero or newline-delimited) */
|
|
|
|
void buffer_append_line(
|
|
BUFFER *buf,
|
|
char *str)
|
|
{
|
|
char *nl;
|
|
|
|
if ((nl = strchr(str, '\n')) != NULL)
|
|
buffer_appendn(buf, str, (int) (nl - str + 1));
|
|
else
|
|
buffer_appendn(buf, str, strlen(str));
|
|
}
|
|
|
|
/* Base STREAM class methods */
|
|
|
|
/* stream_construct initializes a newly allocated STREAM */
|
|
|
|
void stream_construct(
|
|
STREAM *str,
|
|
char *name)
|
|
{
|
|
str->line = 0;
|
|
str->name = memcheck(strdup(name));
|
|
str->next = NULL;
|
|
str->vtbl = NULL;
|
|
}
|
|
|
|
/* stream_delete destroys and deletes (frees) a STREAM */
|
|
|
|
void stream_delete(
|
|
STREAM *str)
|
|
{
|
|
free(str->name);
|
|
free(str);
|
|
}
|
|
|
|
/* *** class BUFFER_STREAM implementation */
|
|
|
|
/* STREAM::getline for a buffer stream */
|
|
|
|
char *buffer_stream_getline(
|
|
STREAM *str)
|
|
{
|
|
char *nl;
|
|
char *cp;
|
|
BUFFER_STREAM *bstr = (BUFFER_STREAM *) str;
|
|
BUFFER *buf = bstr->buffer;
|
|
|
|
if (buf == NULL)
|
|
return NULL; /* No buffer */
|
|
|
|
if (bstr->offset >= buf->length)
|
|
return NULL;
|
|
|
|
cp = buf->buffer + bstr->offset;
|
|
|
|
/* Find the next line in preparation for the next call */
|
|
|
|
nl = memchr(cp, '\n', buf->length - bstr->offset);
|
|
|
|
if (nl)
|
|
nl++;
|
|
|
|
bstr->offset = (int) (nl - buf->buffer);
|
|
str->line++;
|
|
|
|
return cp;
|
|
}
|
|
|
|
/* STREAM::close for a buffer stream */
|
|
|
|
void buffer_stream_delete(
|
|
STREAM *str)
|
|
{
|
|
BUFFER_STREAM *bstr = (BUFFER_STREAM *) str;
|
|
|
|
buffer_free(bstr->buffer);
|
|
stream_delete(str);
|
|
}
|
|
|
|
/* STREAM::rewind for a buffer stream */
|
|
|
|
void buffer_stream_rewind(
|
|
STREAM *str)
|
|
{
|
|
BUFFER_STREAM *bstr = (BUFFER_STREAM *) str;
|
|
|
|
bstr->offset = 0;
|
|
str->line = 0;
|
|
}
|
|
|
|
/* BUFFER_STREAM vtbl */
|
|
|
|
STREAM_VTBL buffer_stream_vtbl = {
|
|
buffer_stream_delete, buffer_stream_getline, buffer_stream_rewind
|
|
};
|
|
|
|
void buffer_stream_construct(
|
|
BUFFER_STREAM * bstr,
|
|
BUFFER *buf,
|
|
char *name)
|
|
{
|
|
bstr->stream.vtbl = &buffer_stream_vtbl;
|
|
|
|
bstr->stream.name = memcheck(strdup(name));
|
|
bstr->stream.line = 0;
|
|
bstr->stream.next = NULL;
|
|
|
|
bstr->buffer = buffer_clone(buf);
|
|
bstr->offset = 0;
|
|
}
|
|
|
|
void buffer_stream_set_buffer(
|
|
BUFFER_STREAM * bstr,
|
|
BUFFER *buf)
|
|
{
|
|
if (bstr->buffer)
|
|
buffer_free(bstr->buffer);
|
|
bstr->buffer = buffer_clone(buf);
|
|
bstr->offset = 0;
|
|
}
|
|
|
|
/* new_buffer_stream clones the given buffer, gives it the name, */
|
|
/* and creates a BUFFER_STREAM to reference it */
|
|
|
|
STREAM *new_buffer_stream(
|
|
BUFFER *buf,
|
|
char *name)
|
|
{
|
|
BUFFER_STREAM *bstr = memcheck(malloc(sizeof(BUFFER_STREAM)));
|
|
|
|
buffer_stream_construct(bstr, buf, name);
|
|
return &bstr->stream;
|
|
}
|
|
|
|
/* *** FILE_STREAM implementation */
|
|
|
|
/* Implement STREAM::getline for a file stream */
|
|
|
|
static char *file_getline(
|
|
STREAM *str)
|
|
{
|
|
int i,
|
|
c;
|
|
FILE_STREAM *fstr = (FILE_STREAM *) str;
|
|
|
|
if (fstr->fp == NULL)
|
|
return NULL;
|
|
|
|
if (feof(fstr->fp))
|
|
return NULL;
|
|
|
|
/* Read single characters, end of line when '\n' or '\f' hit */
|
|
|
|
i = 0;
|
|
while (c = fgetc(fstr->fp), c != '\n' && c != '\f' && c != EOF) {
|
|
if (c == 0)
|
|
continue; /* Don't buffer zeros */
|
|
if (c == '\r')
|
|
continue; /* Don't buffer carriage returns either */
|
|
if (i < STREAM_BUFFER_SIZE - 2)
|
|
fstr->buffer[i++] = c;
|
|
}
|
|
|
|
fstr->buffer[i++] = '\n'; /* Silently transform formfeeds
|
|
into newlines */
|
|
fstr->buffer[i] = 0;
|
|
|
|
if (c == '\n')
|
|
fstr->stream.line++; /* Count a line */
|
|
|
|
return fstr->buffer;
|
|
}
|
|
|
|
/* Implement STREAM::destroy for a file stream */
|
|
|
|
void file_destroy(
|
|
STREAM *str)
|
|
{
|
|
FILE_STREAM *fstr = (FILE_STREAM *) str;
|
|
|
|
fclose(fstr->fp);
|
|
free(fstr->buffer);
|
|
stream_delete(str);
|
|
}
|
|
|
|
/* Implement STREAM::rewind for a file stream */
|
|
|
|
void file_rewind(
|
|
STREAM *str)
|
|
{
|
|
FILE_STREAM *fstr = (FILE_STREAM *) str;
|
|
|
|
rewind(fstr->fp);
|
|
str->line = 0;
|
|
}
|
|
|
|
static STREAM_VTBL file_stream_vtbl = {
|
|
file_destroy, file_getline, file_rewind
|
|
};
|
|
|
|
/* Prepare and open a stream from a file. */
|
|
|
|
STREAM *new_file_stream(
|
|
char *filename)
|
|
{
|
|
FILE *fp;
|
|
FILE_STREAM *str;
|
|
|
|
fp = fopen(filename, "r");
|
|
if (fp == NULL)
|
|
return NULL;
|
|
|
|
str = memcheck(malloc(sizeof(FILE_STREAM)));
|
|
|
|
str->stream.vtbl = &file_stream_vtbl;
|
|
str->stream.name = memcheck(strdup(filename));
|
|
str->stream.line = 0;
|
|
str->stream.next = NULL;
|
|
str->buffer = memcheck(malloc(STREAM_BUFFER_SIZE));
|
|
str->fp = fp;
|
|
|
|
return &str->stream;
|
|
}
|
|
|
|
/* STACK functions */
|
|
|
|
/* stack_init prepares a stack */
|
|
|
|
void stack_init(
|
|
STACK *stack)
|
|
{
|
|
stack->top = NULL; /* Too simple */
|
|
}
|
|
|
|
/* stack_pop removes and deletes the topmost STRAM on the stack */
|
|
|
|
void stack_pop(
|
|
STACK *stack)
|
|
{
|
|
STREAM *top = stack->top;
|
|
STREAM *next = top->next;
|
|
|
|
top->vtbl->delete(top);
|
|
stack->top = next;
|
|
}
|
|
|
|
/* stack_push pushes a STREAM onto the top of the stack */
|
|
|
|
void stack_push(
|
|
STACK *stack,
|
|
STREAM *str)
|
|
{
|
|
str->next = stack->top;
|
|
stack->top = str;
|
|
}
|
|
|
|
/* stack_getline calls vtbl->getline for the topmost stack entry. When
|
|
topmost streams indicate they're exhausted, they are popped and
|
|
deleted, until the stack is exhausted. */
|
|
|
|
char *stack_getline(
|
|
STACK *stack)
|
|
{
|
|
char *line;
|
|
|
|
if (stack->top == NULL)
|
|
return NULL;
|
|
|
|
while ((line = stack->top->vtbl->getline(stack->top)) == NULL) {
|
|
stack_pop(stack);
|
|
if (stack->top == NULL)
|
|
return NULL;
|
|
}
|
|
|
|
return line;
|
|
}
|