xref: /haiku/src/libs/stdc++/legacy/parsestream.cc (revision b671e9bbdbd10268a042b4f4cc4317ccd03d105e)
1 /* This is part of libio/iostream, providing -*- C++ -*- input/output.
2 Copyright (C) 1993 Free Software Foundation
3 
4 This file is part of the GNU IO Library.  This library is free
5 software; you can redistribute it and/or modify it under the
6 terms of the GNU General Public License as published by the
7 Free Software Foundation; either version 2, or (at your option)
8 any later version.
9 
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this library; see the file COPYING.  If not, write to the Free
17 Software Foundation, 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 As a special exception, if you link this library with files
20 compiled with a GNU compiler to produce an executable, this does not cause
21 the resulting executable to be covered by the GNU General Public License.
22 This exception does not however invalidate any other reasons why
23 the executable file might be covered by the GNU General Public License.
24 
25 Written 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 
34 streambuf* parsebuf::setbuf(char*, int)
35 {
36     return NULL;
37 }
38 
39 int parsebuf::tell_in_line()
40 {
41     return 0;
42 }
43 
44 int 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 
53 char* parsebuf::current_line() { return NULL; }
54 
55 streampos 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 
80 string_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 
91 int 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 
109 char* 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 
117 int string_parsebuf::tell_in_line()
118 {
119     int offset = gptr() - eback();
120     if (__line_number > 0)
121 	offset--;
122     return offset;
123 }
124 
125 int 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 
132 static const char NewLine[1] = { '\n' };
133 
134 general_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 
145 general_parsebuf::~general_parsebuf()
146 {
147     if (delete_buf)
148 	delete sbuf;
149 }
150 
151 int 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 
183 char* 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 
191 int 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 
199 int 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 
212 func_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 
223 int 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 
236 char* func_parsebuf::current_line()
237 {
238     return buf_start;
239 }
240 
241 int 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 
262 int 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
293 size_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 
301 int 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