diff options
Diffstat (limited to 'gnu/lib/libg++/iostream/streambuf.C')
| -rw-r--r-- | gnu/lib/libg++/iostream/streambuf.C | 666 |
1 files changed, 666 insertions, 0 deletions
diff --git a/gnu/lib/libg++/iostream/streambuf.C b/gnu/lib/libg++/iostream/streambuf.C new file mode 100644 index 000000000000..0038f39190e8 --- /dev/null +++ b/gnu/lib/libg++/iostream/streambuf.C @@ -0,0 +1,666 @@ +// This is part of the iostream library, providing input/output for C++. +// Copyright (C) 1991, 1992 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. + +#define _STREAM_COMPAT +#ifdef __GNUG__ +#pragma implementation +#endif +#include "ioprivate.h" +#include <string.h> + +void streambuf::_un_link() +{ + if (_flags & _S_LINKED) { + streambuf **f; + for (f = &_list_all; *f != NULL; f = &(*f)->xchain()) { + if (*f == this) { + *f = xchain(); + break; + } + } + _flags &= ~_S_LINKED; + } +} + +void streambuf::_link_in() +{ + if ((_flags & _S_LINKED) == 0) { + _flags |= _S_LINKED; + xchain() = _list_all; + _list_all = this; + } +} + +// Return minimum _pos markers +// Assumes the current get area is the main get area. +int streambuf::_least_marker() +{ + int least_so_far = _egptr - _eback; + for (register streammarker *mark = _markers; + mark != NULL; mark = mark->_next) + if (mark->_pos < least_so_far) + least_so_far = mark->_pos; + return least_so_far; +} + +// Switch current get area from backup buffer to (start of) main get area. + +void streambuf::switch_to_main_get_area() +{ + char *tmp; + _flags &= ~_S_IN_BACKUP; + // Swap _egptr and _other_egptr. + tmp= _egptr; _egptr= _other_egptr; _other_egptr= tmp; + // Swap _eback and _other_gbase. + tmp= _eback; _eback = _other_gbase; _other_gbase = tmp; + _gptr = _eback; +} + +// Switch current get area from main get area to (end of) backup area. + +void streambuf::switch_to_backup_area() +{ + char *tmp; + _flags |= _S_IN_BACKUP; + // Swap _egptr and _other_egptr. + tmp = _egptr; _egptr = _other_egptr; _other_egptr = tmp; + // Swap _gbase and _other_gbase. + tmp = _eback; _eback = _other_gbase; _other_gbase = tmp; + _gptr = _egptr; +} + +int streambuf::switch_to_get_mode() +{ + if (_pptr > _pbase) + if (overflow(EOF) == EOF) + return EOF; + if (in_backup()) { + _eback = _aux_limit; + } + else { + _eback = _base; + if (_pptr > _egptr) + _egptr = _pptr; + } + _gptr = _pptr; + + setp(_gptr, _gptr); + + _flags &= ~_S_CURRENTLY_PUTTING; + return 0; +} + +void streambuf::free_backup_area() +{ + if (in_backup()) + switch_to_main_get_area(); // Just in case. + delete [] _other_gbase; + _other_gbase = NULL; + _other_egptr = NULL; + _aux_limit = NULL; +} + +#if 0 +int streambuf::switch_to_put_mode() +{ + _pbase = _gptr; + _pptr = _gptr; + _epptr = in_backup() ? _egptr : _ebuf; // wrong if line- or un-buffered? + + _gptr = _egptr; + _eback = _egptr; + + _flags |= _S_CURRENTLY_PUTTING; + return 0; +} +#endif + +#ifdef _G_FRIEND_BUG +int __underflow(register streambuf *sb) { return __UNDERFLOW(sb); } +int __UNDERFLOW(register streambuf *sb) +#else +int __underflow(register streambuf *sb) +#endif +{ + if (sb->put_mode()) + if (sb->switch_to_get_mode() == EOF) return EOF; + if (sb->_gptr < sb->_egptr) + return *(unsigned char*)sb->_gptr; + if (sb->in_backup()) { + sb->switch_to_main_get_area(); + if (sb->_gptr < sb->_egptr) + return *sb->_gptr; + } + if (sb->have_markers()) { + // Append [_gbase.._egptr] to backup area. + int least_mark = sb->_least_marker(); + // needed_size is how much space we need in the backup area. + int needed_size = (sb->_egptr - sb->_eback) - least_mark; + int current_Bsize = sb->_other_egptr - sb->_other_gbase; + int avail; // Extra space available for future expansion. + if (needed_size > current_Bsize) { + avail = 0; // 100 ?? FIXME + char *new_buffer = new char[avail+needed_size]; + if (least_mark < 0) { + memcpy(new_buffer + avail, + sb->_other_egptr + least_mark, + -least_mark); + memcpy(new_buffer +avail - least_mark, + sb->_eback, + sb->_egptr - sb->_eback); + } + else + memcpy(new_buffer + avail, + sb->_eback + least_mark, + needed_size); + delete [] sb->_other_gbase; + sb->_other_gbase = new_buffer; + sb->_other_egptr = new_buffer + avail + needed_size; + } + else { + avail = current_Bsize - needed_size; + if (least_mark < 0) { + memmove(sb->_other_gbase + avail, + sb->_other_egptr + least_mark, + -least_mark); + memcpy(sb->_other_gbase + avail - least_mark, + sb->_eback, + sb->_egptr - sb->_eback); + } + else if (needed_size > 0) + memcpy(sb->_other_gbase + avail, + sb->_eback + least_mark, + needed_size); + } + // FIXME: Dubious arithmetic if pointers are NULL + sb->_aux_limit = sb->_other_gbase + avail; + // Adjust all the streammarkers. + int delta = sb->_egptr - sb->_eback; + for (register streammarker *mark = sb->_markers; + mark != NULL; mark = mark->_next) + mark->_pos -= delta; + } + else if (sb->have_backup()) + sb->free_backup_area(); + return sb->underflow(); +} + +#ifdef _G_FRIEND_BUG +int __overflow(register streambuf *sb, int c) { return __OVERFLOW(sb, c); } +int __OVERFLOW(register streambuf *sb, int c) +#else +int __overflow(streambuf* sb, int c) +#endif +{ + return sb->overflow(c); +} + +int streambuf::xsputn(register const char* s, int n) +{ + if (n <= 0) + return 0; + register int more = n; + for (;;) { + int count = _epptr - _pptr; // Space available. + if (count > 0) { + if (count > more) + count = more; + if (count > 20) { + memcpy(_pptr, s, count); + s += count; + _pptr += count; + } + else if (count <= 0) + count = 0; + else { + register char *p = _pptr; + for (register int i = count; --i >= 0; ) *p++ = *s++; + _pptr = p; + } + more -= count; + } + if (more == 0 || __overflow(this, (unsigned char)*s++) == EOF) + break; + more--; + } + return n - more; +} + +int streambuf::padn(char pad, int count) +{ +#define PADSIZE 16 + static char const blanks[PADSIZE] = + {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; + static char const zeroes[PADSIZE] = + {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; + char padbuf[PADSIZE]; + const char *padptr; + register int i; + + if (pad == ' ') + padptr = blanks; + else if (pad == '0') + padptr = zeroes; + else { + for (i = PADSIZE; --i >= 0; ) padbuf[i] = pad; + padptr = padbuf; + } + for (i = count; i >= PADSIZE; i -= PADSIZE) + if (sputn(padptr, PADSIZE) != PADSIZE) + return EOF; + if (i > 0 && sputn(padptr, i) != i) + return EOF; + return pad; +} + +int streambuf::xsgetn(char* s, int n) +{ + register int more = n; + for (;;) { + int count = _egptr - _gptr; // Data available. + if (count > 0) { + if (count > more) + count = more; + if (count > 20) { + memcpy(s, _gptr, count); + s += count; + _gptr += count; + } + else if (count <= 0) + count = 0; + else { + register char *p = _gptr; + for (register int i = count; --i >= 0; ) *s++ = *p++; + _gptr = p; + } + more -= count; + } + if (more == 0 || __underflow(this) == EOF) + break; + } + return n - more; +} + +int streambuf::ignore(int n) +{ + register int more = n; + for (;;) { + int count = _egptr - _gptr; // Data available. + if (count > 0) { + if (count > more) + count = more; + _gptr += count; + more -= count; + } + if (more == 0 || __underflow(this) == EOF) + break; + } + return n - more; +} + +int streambuf::sync() +{ + if (gptr() == egptr() && pptr() == pbase()) + return 0; + return EOF; +} + +int streambuf::pbackfail(int c) +{ + if (_gptr > _eback) + _gptr--; + else if (seekoff(-1, ios::cur, ios::in) == EOF) + return EOF; + if (c != EOF && *_gptr != c) + *_gptr = c; + return (unsigned char)c; +} + +streambuf* streambuf::setbuf(char* p, int len) +{ + if (sync() == EOF) + return NULL; + if (p == NULL || len == 0) { + unbuffered(1); + setb(_shortbuf, _shortbuf+1, 0); + } + else { + unbuffered(0); + setb(p, p+len, 0); + } + setp(0, 0); + setg(0, 0, 0); + return this; +} + +streampos streambuf::seekpos(streampos pos, int mode) +{ + return seekoff(pos, ios::beg, mode); +} + +void streambuf::setb(char* b, char* eb, int a) +{ + if (_base && !(_flags & _S_USER_BUF)) + FREE_BUF(_base); + _base = b; + _ebuf = eb; + if (a) + _flags &= ~_S_USER_BUF; + else + _flags |= _S_USER_BUF; +} + +int streambuf::doallocate() +{ + char *buf = ALLOC_BUF(_G_BUFSIZ); + if (buf == NULL) + return EOF; + setb(buf, buf+_G_BUFSIZ, 1); + return 1; +} + +void streambuf::doallocbuf() +{ + if (base() || (!unbuffered() && doallocate() != EOF)) return; + setb(_shortbuf, _shortbuf+1, 0); +} + +streambuf::streambuf(int flags) +{ + _flags = _IO_MAGIC|flags; + _base = NULL; + _ebuf = NULL; + _eback = NULL; + _gptr = NULL; + _egptr = NULL; + _pbase = NULL; + _pptr = NULL; + _epptr = NULL; + _chain = NULL; // Not necessary. + + _other_gbase = NULL; + _aux_limit = NULL; + _other_egptr = NULL; + _markers = NULL; + _cur_column = 0; +} + +streambuf::~streambuf() +{ + if (_base && !(_flags & _S_USER_BUF)) + FREE_BUF(_base); + + for (register streammarker *mark = _markers; + mark != NULL; mark = mark->_next) + mark->_sbuf = NULL; + +} + +streampos +streambuf::seekoff(streamoff, _seek_dir, int mode /*=ios::in|ios::out*/) +{ + return EOF; +} + +int streambuf::sputbackc(char c) +{ + if (_gptr > _eback && (unsigned char)_gptr[-1] == (unsigned char)c) { + _gptr--; + return (unsigned char)c; + } + return pbackfail(c); +} + +int streambuf::sungetc() +{ + if (_gptr > _eback) { + _gptr--; + return (unsigned char)*_gptr; + } + else + return pbackfail(EOF); +} + +#if 0 /* Work in progress */ +void streambuf::collumn(int c) +{ + if (c == -1) + _collumn = -1; + else + _collumn = c - (_pptr - _pbase); +} +#endif + + +int streambuf::get_column() +{ + if (_cur_column) + return __adjust_column(_cur_column - 1, pbase(), pptr() - pbase()); + return -1; +} + +int streambuf::set_column(int i) +{ + _cur_column = i+1; + return 0; +} + +int streambuf::flush_all() +{ + int result = 0; + for (streambuf *sb = _list_all; sb != NULL; sb = sb->xchain()) + if (sb->overflow(EOF) == EOF) + result = EOF; + return result; +} + +void streambuf::flush_all_linebuffered() +{ + for (streambuf *sb = _list_all; sb != NULL; sb = sb->xchain()) + if (sb->linebuffered()) + sb->overflow(EOF); +} + +int backupbuf::underflow() +{ + return EOF; +} + +int backupbuf::overflow(int c) +{ + return EOF; +} + +streammarker::streammarker(streambuf *sb) +{ + _sbuf = sb; + if (!(sb->xflags() & _S_IS_BACKUPBUF)) { + set_streampos(sb->seekoff(0, ios::cur, ios::in)); + _next = 0; + } + else { + if (sb->put_mode()) + sb->switch_to_get_mode(); + if (((backupbuf*)sb)->in_backup()) + set_offset(sb->_gptr - sb->_egptr); + else + set_offset(sb->_gptr - sb->_eback); + + // Should perhaps sort the chain? + _next = ((backupbuf*)sb)->_markers; + ((backupbuf*)sb)->_markers = this; + } +} + +streammarker::~streammarker() +{ + if (saving()) { + // Unlink from sb's chain. + register streammarker **ptr = &((backupbuf*)_sbuf)->_markers; + for (; ; ptr = &(*ptr)->_next) + if (*ptr == NULL) + break; + else if (*ptr == this) { + *ptr = _next; + return; + } + } +#if 0 + if _sbuf has a backup area that is no longer needed, should we delete + it now, or wait until underflow()? +#endif +} + +#define BAD_DELTA EOF + +int streammarker::delta(streammarker& other_mark) +{ + if (_sbuf != other_mark._sbuf) + return BAD_DELTA; + if (saving() && other_mark.saving()) + return _pos - other_mark._pos; + else if (!saving() && !other_mark.saving()) + return _spos - other_mark._spos; + else + return BAD_DELTA; +} + +int streammarker::delta() +{ + if (_sbuf == NULL) + return BAD_DELTA; + if (saving()) { + int cur_pos; + if (_sbuf->in_backup()) + cur_pos = _sbuf->_gptr - _sbuf->_egptr; + else + cur_pos = _sbuf->_gptr - _sbuf->_eback; + return _pos - cur_pos; + } + else { + if (_spos == EOF) + return BAD_DELTA; + int cur_pos = _sbuf->seekoff(0, ios::cur); + if (cur_pos == EOF) + return BAD_DELTA; + return _pos - cur_pos; + } +} + +int streambuf::seekmark(streammarker& mark, int delta /* = 0 */) +{ + if (mark._sbuf != this) + return EOF; + if (!mark.saving()) { + return seekpos(mark._spos, ios::in); + } + else if (mark._pos >= 0) { + if (in_backup()) + switch_to_main_get_area(); + _gptr = _eback + mark._pos; + } + else { + if (!in_backup()) + switch_to_backup_area(); + _gptr = _egptr + mark._pos; + } + return 0; +} + +void streambuf::unsave_markers() +{ + register streammarker *mark =_markers; + if (_markers) { + streampos offset = seekoff(0, ios::cur, ios::in); + if (offset != EOF) { + offset += eGptr() - Gbase(); + for ( ; mark != NULL; mark = mark->_next) + mark->set_streampos(mark->_pos + offset); + } + else { + for ( ; mark != NULL; mark = mark->_next) + mark->set_streampos(EOF); + } + _markers = 0; + } + + free_backup_area(); +} + +int backupbuf::pbackfail(int c) +{ + if (_gptr <= _eback) { + // Need to handle a filebuf in write mode (switch to read mode). FIXME! + + if (have_backup() && !in_backup()) { + switch_to_backup_area(); + } + if (!have_backup()) { + // No backup buffer: allocate one. + // Use short buffer, if unused? (probably not) FIXME + int backup_size = 128; + _other_gbase = new char [backup_size]; + _other_egptr = _other_gbase + backup_size; + _aux_limit = _other_egptr; + switch_to_backup_area(); + } + else if (gptr() <= eback()) { + // Increase size of existing backup buffer. + size_t new_size; + size_t old_size = egptr() - eback(); + new_size = 2 * old_size; + char* new_buf = new char [new_size]; + memcpy(new_buf+(new_size-old_size), eback(), old_size); + delete [] eback(); + setg(new_buf, new_buf+(new_size-old_size), new_buf+new_size); + _aux_limit = _gptr; + } + } + _gptr--; + if (c != EOF && *_gptr != c) + *_gptr = c; + return (unsigned char)*_gptr; +} + +unsigned __adjust_column(unsigned start, const char *line, int count) +{ + register const char *ptr = line + count; + while (ptr > line) + if (*--ptr == '\n') + return line + count - ptr - 1; + return start + count; +} + +int ios::readable() { return !(rdbuf()->_flags & _S_NO_READS); } +int ios::writable() { return !(rdbuf()->_flags & _S_NO_WRITES); } +int ios::is_open() { return rdbuf() + && (rdbuf()->_flags & _S_NO_READS+_S_NO_WRITES) + != _S_NO_READS+_S_NO_WRITES; } + +#if defined(linux) +#define IO_CLEANUP ; +#endif + +#ifdef IO_CLEANUP + IO_CLEANUP +#else +struct __io_defs { + __io_defs() { } + ~__io_defs() { streambuf::flush_all(); } +}; +__io_defs io_defs__; +#endif |
