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