1/* This is part of libio/iostream, providing -*- C++ -*- input/output.
2Copyright (C) 1993 Free Software Foundation
3
4This file is part of the GNU IO Library.  This library is free
5software; you can redistribute it and/or modify it under the
6terms of the GNU General Public License as published by the
7Free Software Foundation; either version 2, or (at your option)
8any later version.
9
10This library is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this library; see the file COPYING.  If not, write to the Free
17Software Foundation, 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19As a special exception, if you link this library with files
20compiled with a GNU compiler to produce an executable, this does not cause
21the resulting executable to be covered by the GNU General Public License.
22This exception does not however invalidate any other reasons why
23the executable file might be covered by the GNU General Public License.
24
25Written by Per Bothner (bothner@cygnus.com). */
26
27#ifdef __GNUG__
28#pragma implementation
29#endif
30#include "libioP.h"
31#include "parsestream.h"
32#include <stdlib.h>
33
34streambuf* parsebuf::setbuf(char*, int)
35{
36    return NULL;
37}
38
39int parsebuf::tell_in_line()
40{
41    return 0;
42}
43
44int parsebuf::pbackfail(int c)
45{
46    if (c == EOF)
47	return 0;
48    if (seekoff(-1, ios::cur) == EOF)
49	return EOF;
50    return (unsigned char)c;
51}
52
53char* parsebuf::current_line() { return NULL; }
54
55streampos parsebuf::seekoff(streamoff offset, _seek_dir dir, int)
56{
57    // Make offset relative to line start.
58    switch (dir) {
59      case ios::beg:
60	offset -= pos_at_line_start;
61	break;
62      case ios::cur:
63	offset += tell_in_line();
64	break;
65      default:
66	return EOF;
67    }
68    if (offset < -1)
69	return EOF;
70    if (offset > _line_length + 1)
71	return EOF;
72    return seek_in_line(offset) + pos_at_line_start;
73}
74
75// string_parsebuf invariants:
76// The reserve ares (base() .. ebuf()) is always the entire string.
77// The get area (eback() .. egptr()) is the extended current line
78// (i.e. with the '\n' at either end, if these exist).
79
80string_parsebuf::string_parsebuf(char *buf, int len,
81				 int delete_at_close /* = 0*/)
82: parsebuf()
83{
84    setb(buf, buf+len, delete_at_close);
85    register char *ptr = buf;
86    while (ptr < ebuf() && *ptr != '\n') ptr++;
87    _line_length = ptr - buf;
88    setg(buf, buf, ptr);
89}
90
91int string_parsebuf::underflow()
92{
93    register char* ptr = egptr(); // Point to end of current_line
94    do {
95	int i = right() - ptr;
96	if (i <= 0)
97	    return EOF;
98	ptr++; i--; // Skip '\n'.
99	char *line_start = ptr;
100	while (ptr < right() && *ptr == '\n') ptr++;
101	setg(line_start-1, line_start, ptr + (ptr < right()));
102	pos_at_line_start = line_start - left();
103	_line_length = ptr - line_start;
104	__line_number++;
105    } while (gptr() == ptr);
106    return *gptr();
107}
108
109char* string_parsebuf::current_line()
110{
111    char *ptr = eback();
112    if (__line_number > 0)
113	ptr++; // Skip '\n' at end of previous line.
114    return ptr;
115}
116
117int string_parsebuf::tell_in_line()
118{
119    int offset = gptr() - eback();
120    if (__line_number > 0)
121	offset--;
122    return offset;
123}
124
125int string_parsebuf::seek_in_line(int i)
126{
127    int delta = i - tell_in_line();
128    gbump(delta); // FIXME: Needs error (bounds) checking!
129    return i;
130}
131
132static const char NewLine[1] = { '\n' };
133
134general_parsebuf::general_parsebuf(streambuf *buf, int delete_arg_buf)
135 : parsebuf()
136{
137    delete_buf = delete_arg_buf;
138    sbuf = buf;
139    int buf_size = 128;
140    char* buffer = (char*)malloc(buf_size);
141    setb(buffer, buffer+buf_size, 1);
142//    setg(buffer, buffer, buffer);
143}
144
145general_parsebuf::~general_parsebuf()
146{
147    if (delete_buf)
148	delete sbuf;
149}
150
151int general_parsebuf::underflow()
152{
153    register char *ptr = base();
154    int has_newline = eback() < gptr() && gptr()[-1] == '\n';
155    if (has_newline)
156	*ptr++ = '\n';
157    register streambuf *sb = sbuf;
158    register int ch;
159    for (;;) {
160	ch = sb->sbumpc();
161	if (ch == EOF)
162	    break;
163	if (ptr == ebuf()) {
164	    int old_size = ebuf() - base();
165	    char *new_buffer = new char[old_size * 2];
166	    memcpy(new_buffer, base(), old_size);
167	    setb(new_buffer, new_buffer + 2 * old_size, 1);
168	    ptr = new_buffer + old_size;
169	}
170	*ptr++ = ch;
171	if (ch == '\n')
172	    break;
173    }
174    char *cur_pos = base() + has_newline;
175    pos_at_line_start += _line_length + 1;
176    _line_length = ptr - cur_pos;
177    if (ch != EOF || _line_length > 0)
178	__line_number++;
179    setg(base(), cur_pos, ptr);
180    return ptr == cur_pos ? EOF : cur_pos[0];
181}
182
183char* general_parsebuf::current_line()
184{
185    char* ret = base();
186    if (__line_number > 1)
187	ret++; // Move past '\n' from end of previous line.
188    return ret;
189}
190
191int general_parsebuf::tell_in_line()
192{
193    int off = gptr() - base();
194    if (__line_number > 1)
195	off--; // Subtract 1 for '\n' from end of previous line.
196    return off;
197}
198
199int general_parsebuf::seek_in_line(int i)
200{
201    if (__line_number == 0)
202	(void)general_parsebuf::underflow();
203    if (__line_number > 1)
204	i++; // Add 1 for '\n' from end of previous line.
205    if (i < 0) i = 0;
206    int len = egptr() - eback();
207    if (i > len) i = len;
208    setg(base(), base() + i, egptr());
209    return i;
210}
211
212func_parsebuf::func_parsebuf(CharReader func, void *argm) : parsebuf()
213{
214    read_func = func;
215    arg = argm;
216    buf_start = NULL;
217    buf_end = NULL;
218    setb((char*)NewLine, (char*)NewLine+1, 0);
219    setg((char*)NewLine, (char*)NewLine+1, (char*)NewLine+1);
220    backed_up_to_newline = 0;
221}
222
223int func_parsebuf::tell_in_line()
224{
225    if (buf_start == NULL)
226	return 0;
227    if (egptr() != (char*)NewLine+1)
228	// Get buffer was line buffer.
229	return gptr() - buf_start;
230    if (backed_up_to_newline)
231	return -1;  // Get buffer is '\n' preceding current line.
232    // Get buffer is '\n' following current line.
233    return (buf_end - buf_start) + (gptr() - (char*)NewLine);
234}
235
236char* func_parsebuf::current_line()
237{
238    return buf_start;
239}
240
241int func_parsebuf::seek_in_line(int i)
242{
243    if (i < 0) {
244	// Back up to preceding '\n'.
245	if (i < -1) i = -1;
246	backed_up_to_newline = 1;
247	setg((char*)NewLine, (char*)NewLine+(i+1), (char*)NewLine+1);
248	return i;
249    }
250    backed_up_to_newline = 0;
251    int line_length = buf_end-buf_start;
252    if (i <= line_length) {
253	setg(buf_start, buf_start+i, buf_end);
254	return i;
255    }
256    i -= line_length;
257    if (i > 0) i = 1;
258    setg((char*)NewLine, (char*)NewLine+i, (char*)NewLine+1);
259    return line_length + i;
260}
261
262int func_parsebuf::underflow()
263{
264  retry:
265    if (gptr() < egptr())
266	return *gptr();
267    if (gptr() != (char*)NewLine+1) {
268	// Get buffer was line buffer.  Move to following '\n'.
269	setg((char*)NewLine, (char*)NewLine, (char*)NewLine+1);
270	return *gptr();
271    }
272    if (backed_up_to_newline)
273	// Get buffer was '\n' preceding current line. Move to current line.
274	backed_up_to_newline = 0;
275    else {
276	// Get buffer was '\n' following current line. Read new line.
277	if (buf_start) free(buf_start);
278	char *str = (*read_func)(arg);
279	buf_start = str;
280	if (str == NULL)
281	    return EOF;
282	// Initially, _line_length == -1, so pos_at_line_start becomes 0.
283	pos_at_line_start += _line_length + 1;
284	_line_length = strlen(str);
285	buf_end = str + _line_length;
286	__line_number++;
287    }
288    setg(buf_start, buf_start, buf_end);
289    goto retry;
290}
291
292#if 0
293size_t parsebuf::line_length()
294{
295    if (current_line_length == (size_t)(-1)) // Initial value;
296	(void)sgetc();
297    return current_line_length;
298}
299#endif
300
301int parsebuf::seek_in_line(int i)
302{
303#if 1
304    abort();
305    return i; // Suppress warnings.
306#else
307    if (i > 0) {
308	size_t len = line_length();
309	if ((unsigned)i > len) i = len;
310    }
311    else if (i < -1) i = -1;
312    int new_pos = seekoff(pos_at_line_start + i, ios::beg);
313    if (new_pos == EOF)
314	return tell_in_line();
315    else return new_pos - pos_at_line_start;
316#endif
317}
318