308 lines
7.4 KiB
C
308 lines
7.4 KiB
C
// This is part of the iostream library, providing input/output for C++.
|
|
// Copyright (C) 1991 Per Bothner.
|
|
//
|
|
// This library is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Library General Public
|
|
// License as published by the Free Software Foundation; either
|
|
// version 2 of the License, or (at your option) any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// Library General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Library General Public
|
|
// License along with this library; if not, write to the Free
|
|
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
#ifdef __GNUG__
|
|
#pragma implementation
|
|
#endif
|
|
#include "ioprivate.h"
|
|
#include "parsestream.h"
|
|
|
|
streambuf* parsebuf::setbuf(char*, int)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
int parsebuf::tell_in_line()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int parsebuf::pbackfail(int c)
|
|
{
|
|
if (c == EOF)
|
|
return 0;
|
|
if (seekoff(-1, ios::cur) == EOF)
|
|
return EOF;
|
|
return (unsigned char)c;
|
|
}
|
|
|
|
char* parsebuf::current_line() { return NULL; }
|
|
|
|
streampos parsebuf::seekoff(streamoff offset, _seek_dir dir, int)
|
|
{
|
|
// Make offset relative to line start.
|
|
switch (dir) {
|
|
case ios::beg:
|
|
offset -= pos_at_line_start;
|
|
break;
|
|
case ios::cur:
|
|
offset += tell_in_line();
|
|
break;
|
|
default:
|
|
return EOF;
|
|
}
|
|
if (offset < -1)
|
|
return EOF;
|
|
if (offset > _line_length + 1)
|
|
return EOF;
|
|
return seek_in_line(offset) + pos_at_line_start;
|
|
}
|
|
|
|
// string_parsebuf invariants:
|
|
// The reserve ares (base() .. ebuf()) is always the entire string.
|
|
// The get area (eback() .. egptr()) is the extended current line
|
|
// (i.e. with the '\n' at either end, if these exist).
|
|
|
|
string_parsebuf::string_parsebuf(char *buf, int len,
|
|
int delete_at_close /* = 0*/)
|
|
: parsebuf()
|
|
{
|
|
setb(buf, buf+len, delete_at_close);
|
|
register char *ptr = buf;
|
|
while (ptr < ebuf() && *ptr != '\n') ptr++;
|
|
_line_length = ptr - buf;
|
|
setg(buf, buf, ptr);
|
|
}
|
|
|
|
int string_parsebuf::underflow()
|
|
{
|
|
register char* ptr = egptr(); // Point to end of current_line
|
|
do {
|
|
int i = right() - ptr;
|
|
if (i <= 0)
|
|
return EOF;
|
|
ptr++; i--; // Skip '\n'.
|
|
char *line_start = ptr;
|
|
while (ptr < right() && *ptr == '\n') ptr++;
|
|
setg(line_start-1, line_start, ptr + (ptr < right()));
|
|
pos_at_line_start = line_start - left();
|
|
_line_length = ptr - line_start;
|
|
__line_number++;
|
|
} while (gptr() == ptr);
|
|
return *gptr();
|
|
}
|
|
|
|
char* string_parsebuf::current_line()
|
|
{
|
|
char *ptr = eback();
|
|
if (__line_number > 0)
|
|
ptr++; // Skip '\n' at end of previous line.
|
|
return ptr;
|
|
}
|
|
|
|
int string_parsebuf::tell_in_line()
|
|
{
|
|
int offset = gptr() - eback();
|
|
if (__line_number > 0)
|
|
offset--;
|
|
return offset;
|
|
}
|
|
|
|
int string_parsebuf::seek_in_line(int i)
|
|
{
|
|
int delta = i - tell_in_line();
|
|
gbump(delta); // FIXME: Needs error (bounds) checking!
|
|
return i;
|
|
}
|
|
|
|
static const char NewLine[1] = { '\n' };
|
|
|
|
general_parsebuf::general_parsebuf(streambuf *buf, int delete_arg_buf)
|
|
: parsebuf()
|
|
{
|
|
delete_buf = delete_arg_buf;
|
|
sbuf = buf;
|
|
int buf_size = 128;
|
|
char* buffer = ALLOC_BUF(buf_size);
|
|
setb(buffer, buffer+buf_size, 1);
|
|
// setg(buffer, buffer, buffer);
|
|
}
|
|
|
|
general_parsebuf::~general_parsebuf()
|
|
{
|
|
if (delete_buf)
|
|
delete sbuf;
|
|
}
|
|
|
|
int general_parsebuf::underflow()
|
|
{
|
|
register char *ptr = base();
|
|
int has_newline = eback() < gptr() && gptr()[-1] == '\n';
|
|
if (has_newline)
|
|
*ptr++ = '\n';
|
|
register streambuf *sb = sbuf;
|
|
register int ch;
|
|
for (;;) {
|
|
ch = sb->sbumpc();
|
|
if (ch == EOF)
|
|
break;
|
|
if (ptr == ebuf()) {
|
|
int old_size = ebuf() - base();
|
|
char *new_buffer = new char[old_size * 2];
|
|
memcpy(new_buffer, base(), old_size);
|
|
setb(new_buffer, new_buffer + 2 * old_size, 1);
|
|
ptr = new_buffer + old_size;
|
|
}
|
|
*ptr++ = ch;
|
|
if (ch == '\n')
|
|
break;
|
|
}
|
|
char *cur_pos = base() + has_newline;
|
|
pos_at_line_start += _line_length + 1;
|
|
_line_length = ptr - cur_pos;
|
|
if (ch != EOF || _line_length > 0)
|
|
__line_number++;
|
|
setg(base(), cur_pos, ptr);
|
|
return ptr == cur_pos ? EOF : cur_pos[0];
|
|
}
|
|
|
|
char* general_parsebuf::current_line()
|
|
{
|
|
char* ret = base();
|
|
if (__line_number > 1)
|
|
ret++; // Move past '\n' from end of previous line.
|
|
return ret;
|
|
}
|
|
|
|
int general_parsebuf::tell_in_line()
|
|
{
|
|
int off = gptr() - base();
|
|
if (__line_number > 1)
|
|
off--; // Subtract 1 for '\n' from end of previous line.
|
|
return off;
|
|
}
|
|
|
|
int general_parsebuf::seek_in_line(int i)
|
|
{
|
|
if (__line_number == 0)
|
|
(void)general_parsebuf::underflow();
|
|
if (__line_number > 1)
|
|
i++; // Add 1 for '\n' from end of previous line.
|
|
if (i < 0) i = 0;
|
|
int len = egptr() - eback();
|
|
if (i > len) i = len;
|
|
setg(base(), base() + i, egptr());
|
|
return i;
|
|
}
|
|
|
|
func_parsebuf::func_parsebuf(CharReader func, void *argm) : parsebuf()
|
|
{
|
|
read_func = func;
|
|
arg = argm;
|
|
buf_start = NULL;
|
|
buf_end = NULL;
|
|
setb((char*)NewLine, (char*)NewLine+1, 0);
|
|
setg((char*)NewLine, (char*)NewLine+1, (char*)NewLine+1);
|
|
backed_up_to_newline = 0;
|
|
}
|
|
|
|
int func_parsebuf::tell_in_line()
|
|
{
|
|
if (buf_start == NULL)
|
|
return 0;
|
|
if (egptr() != (char*)NewLine+1)
|
|
// Get buffer was line buffer.
|
|
return gptr() - buf_start;
|
|
if (backed_up_to_newline)
|
|
return -1; // Get buffer is '\n' preceding current line.
|
|
// Get buffer is '\n' following current line.
|
|
return (buf_end - buf_start) + (gptr() - (char*)NewLine);
|
|
}
|
|
|
|
char* func_parsebuf::current_line()
|
|
{
|
|
return buf_start;
|
|
}
|
|
|
|
int func_parsebuf::seek_in_line(int i)
|
|
{
|
|
if (i < 0) {
|
|
// Back up to preceding '\n'.
|
|
if (i < -1) i = -1;
|
|
backed_up_to_newline = 1;
|
|
setg((char*)NewLine, (char*)NewLine+(i+1), (char*)NewLine+1);
|
|
return i;
|
|
}
|
|
backed_up_to_newline = 0;
|
|
int line_length = buf_end-buf_start;
|
|
if (i <= line_length) {
|
|
setg(buf_start, buf_start+i, buf_end);
|
|
return i;
|
|
}
|
|
i -= line_length;
|
|
if (i > 0) i = 1;
|
|
setg((char*)NewLine, (char*)NewLine+i, (char*)NewLine+1);
|
|
return line_length + i;
|
|
}
|
|
|
|
int func_parsebuf::underflow()
|
|
{
|
|
retry:
|
|
if (gptr() < egptr())
|
|
return *gptr();
|
|
if (gptr() != (char*)NewLine+1) {
|
|
// Get buffer was line buffer. Move to following '\n'.
|
|
setg((char*)NewLine, (char*)NewLine, (char*)NewLine+1);
|
|
return *gptr();
|
|
}
|
|
if (backed_up_to_newline)
|
|
// Get buffer was '\n' preceding current line. Move to current line.
|
|
backed_up_to_newline = 0;
|
|
else {
|
|
// Get buffer was '\n' following current line. Read new line.
|
|
if (buf_start) free(buf_start);
|
|
char *str = (*read_func)(arg);
|
|
buf_start = str;
|
|
if (str == NULL)
|
|
return EOF;
|
|
// Initially, _line_length == -1, so pos_at_line_start becomes 0.
|
|
pos_at_line_start += _line_length + 1;
|
|
_line_length = strlen(str);
|
|
buf_end = str + _line_length;
|
|
__line_number++;
|
|
}
|
|
setg(buf_start, buf_start, buf_end);
|
|
goto retry;
|
|
}
|
|
|
|
#if 0
|
|
size_t parsebuf::line_length()
|
|
{
|
|
if (current_line_length == (size_t)(-1)) // Initial value;
|
|
(void)sgetc();
|
|
return current_line_length;
|
|
}
|
|
#endif
|
|
|
|
int parsebuf::seek_in_line(int i)
|
|
{
|
|
#if 1
|
|
abort();
|
|
return 0; // Suppress warning.
|
|
#else
|
|
if (i > 0) {
|
|
size_t len = line_length();
|
|
if ((unsigned)i > len) i = len;
|
|
}
|
|
else if (i < -1) i = -1;
|
|
int new_pos = seekoff(pos_at_line_start + i, ios::beg);
|
|
if (new_pos == EOF)
|
|
return tell_in_line();
|
|
else return new_pos - pos_at_line_start;
|
|
#endif
|
|
}
|