Merge from vendor branch GCC:
[dragonfly.git] / contrib / libio / parsestream.cc
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 }