1
/* functions for managing a stack of file and buffer input streams. */
5
Copyright (c) 2001, Richard Krehbiel
8
Redistribution and use in source and binary forms, with or without
9
modification, are permitted provided that the following conditions are
12
o Redistributions of source code must retain the above copyright
13
notice, this list of conditions and the following disclaimer.
15
o Redistributions in binary form must reproduce the above copyright
16
notice, this list of conditions and the following disclaimer in the
17
documentation and/or other materials provided with the distribution.
19
o Neither the name of the copyright holder nor the names of its
20
contributors may be used to endorse or promote products derived from
21
this software without specific prior written permission.
23
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27
HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
30
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
32
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
33
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
49
/* BUFFER functions */
51
/* new_buffer allocates a new buffer */
53
BUFFER *new_buffer(void)
55
BUFFER *buf = memcheck(malloc(sizeof(BUFFER)));
63
/* buffer_resize makes the buffer at least the requested size. */
64
/* If the buffer is already larger, then it will attempt */
67
void buffer_resize(BUFFER *buff, int size)
79
if(buff->buffer == NULL)
80
buff->buffer = memcheck(malloc(buff->size));
82
buff->buffer = memcheck(realloc(buff->buffer, buff->size));
86
/* buffer_clone makes a copy of a buffer */
87
/* Basically it increases the use count */
89
BUFFER *buffer_clone(BUFFER *from)
96
/* buffer_free frees a buffer */
97
/* It decreases the use count, and if zero, */
98
/* frees the memory. */
100
void buffer_free(BUFFER *buf)
104
if(--(buf->use) == 0)
112
/* Append characters to the buffer. */
114
void buffer_appendn(BUFFER *buf, char *str, int len)
116
int needed = buf->length + len + 1;
118
if(needed >= buf->size)
120
buf->size = needed + GROWBUF_INCR;
122
if(buf->buffer == NULL)
123
buf->buffer = memcheck(malloc(buf->size));
125
buf->buffer = memcheck(realloc(buf->buffer, buf->size));
128
memcpy(buf->buffer + buf->length, str, len);
130
buf->buffer[buf->length] = 0;
133
/* append a text line (zero or newline-delimited) */
135
void buffer_append_line(BUFFER *buf, char *str)
138
if((nl = strchr(str, '\n')) != NULL)
139
buffer_appendn(buf, str, nl - str + 1);
141
buffer_appendn(buf, str, strlen(str));
144
/* Base STREAM class methods */
146
/* stream_construct initializes a newly allocated STREAM */
148
void stream_construct(STREAM *str, char *name)
151
str->name = memcheck(strdup(name));
156
/* stream_delete destroys and deletes (frees) a STREAM */
158
void stream_delete(STREAM *str)
164
/* *** class BUFFER_STREAM implementation */
166
/* STREAM::gets for a buffer stream */
168
char *buffer_stream_gets(STREAM *str)
172
BUFFER_STREAM *bstr = (BUFFER_STREAM *)str;
173
BUFFER *buf = bstr->buffer;
176
return NULL; /* No buffer */
178
if(bstr->offset >= buf->length)
181
cp = buf->buffer + bstr->offset;
183
/* Find the next line in preparation for the next call */
185
nl = memchr(cp, '\n', buf->length - bstr->offset);
190
bstr->offset = nl - buf->buffer;
196
/* STREAM::close for a buffer stream */
198
void buffer_stream_delete(STREAM *str)
200
BUFFER_STREAM *bstr = (BUFFER_STREAM *)str;
201
buffer_free(bstr->buffer);
205
/* STREAM::rewind for a buffer stream */
207
void buffer_stream_rewind(STREAM *str)
209
BUFFER_STREAM *bstr = (BUFFER_STREAM *)str;
214
/* BUFFER_STREAM vtbl */
216
STREAM_VTBL buffer_stream_vtbl = { buffer_stream_delete,
218
buffer_stream_rewind };
220
void buffer_stream_construct(BUFFER_STREAM *bstr, BUFFER *buf, char *name)
222
bstr->stream.vtbl = &buffer_stream_vtbl;
224
bstr->stream.name = memcheck(strdup(name));
226
bstr->buffer = buffer_clone(buf);
228
bstr->stream.line = 0;
231
void buffer_stream_set_buffer(BUFFER_STREAM *bstr, BUFFER *buf)
234
buffer_free(bstr->buffer);
235
bstr->buffer = buffer_clone(buf);
239
/* new_buffer_stream clones the given buffer, gives it the name, */
240
/* and creates a BUFFER_STREAM to reference it */
242
STREAM *new_buffer_stream(BUFFER *buf, char *name)
244
BUFFER_STREAM *bstr = memcheck(malloc(sizeof(BUFFER_STREAM)));
246
buffer_stream_construct(bstr, buf, name);
247
return &bstr->stream;
250
/* *** FILE_STREAM implementation */
252
/* Implement STREAM::gets for a file stream */
254
static char *file_gets(STREAM *str)
257
FILE_STREAM *fstr = (FILE_STREAM *)str;
265
/* Read single characters, end of line when '\n' or '\f' hit */
268
while(c = fgetc(fstr->fp),
269
c != '\n' && c != '\f' && c != EOF)
272
continue; /* Don't buffer zeros */
274
continue; /* Don't buffer carriage returns either */
275
if(i < STREAM_BUFFER_SIZE - 2)
276
fstr->buffer[i++] = c;
279
fstr->buffer[i++] = '\n'; /* Silently transform formfeeds
284
fstr->stream.line++; /* Count a line */
289
/* Implement STREAM::destroy for a file stream */
291
void file_destroy(STREAM *str)
293
FILE_STREAM *fstr = (FILE_STREAM *)str;
299
/* Implement STREAM::rewind for a file stream */
301
void file_rewind(STREAM *str)
303
FILE_STREAM *fstr = (FILE_STREAM *)str;
308
static STREAM_VTBL file_stream_vtbl = { file_destroy, file_gets,
311
/* Prepare and open a stream from a file. */
313
STREAM *new_file_stream(char *filename)
318
fp = fopen(filename, "r");
322
str = memcheck(malloc(sizeof(FILE_STREAM)));
324
str->stream.vtbl = &file_stream_vtbl;
325
str->stream.name = memcheck(strdup(filename));
326
str->buffer = memcheck(malloc(STREAM_BUFFER_SIZE));
328
str->stream.line = 0;
333
/* STACK functions */
335
/* stack_init prepares a stack */
337
void stack_init(STACK *stack)
339
stack->top = NULL; /* Too simple */
342
/* stack_pop removes and deletes the topmost STRAM on the stack */
344
void stack_pop(STACK *stack)
346
STREAM *top = stack->top;
347
STREAM *next = top->next;
349
top->vtbl->delete(top);
353
/* stack_push pushes a STREAM onto the top of the stack */
355
void stack_push(STACK *stack, STREAM *str)
357
str->next = stack->top;
361
/* stack_gets calls vtbl->gets for the topmost stack entry. When
362
topmost streams indicate they're exhausted, they are popped and
363
deleted, until the stack is exhausted. */
365
char *stack_gets(STACK *stack)
369
if(stack->top == NULL)
372
while((line = stack->top->vtbl->gets(stack->top)) == NULL)
375
if(stack->top == NULL)