718 lines
18 KiB
C++
718 lines
18 KiB
C++
/* This is part of libio/iostream, providing -*- C++ -*- input/output.
|
|
Copyright (C) 1993 Free Software Foundation
|
|
|
|
This file is part of the GNU IO Library. This library is free
|
|
software; you can redistribute it and/or modify it under the
|
|
terms of the GNU General Public License as published by the
|
|
Free Software Foundation; either version 2, 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this library; see the file COPYING. If not, write to the Free
|
|
Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
As a special exception, if you link this library with files
|
|
compiled with a GNU compiler to produce an executable, this does not cause
|
|
the resulting executable to be covered by the GNU General Public License.
|
|
This exception does not however invalidate any other reasons why
|
|
the executable file might be covered by the GNU General Public License.
|
|
|
|
Written by Per Bothner (bothner@cygnus.com). */
|
|
|
|
#ifdef __GNUG__
|
|
#pragma implementation
|
|
#endif
|
|
#include "libioP.h"
|
|
#include "editbuf.h"
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
|
|
/* NOTE: Some of the code here is taken from GNU emacs */
|
|
/* Hence this file falls under the GNU License! */
|
|
|
|
// Invariants for edit_streambuf:
|
|
// An edit_streambuf is associated with a specific edit_string,
|
|
// which again is a sub-string of a specific edit_buffer.
|
|
// An edit_streambuf is always in either get mode or put mode, never both.
|
|
// In get mode, gptr() is the current position,
|
|
// and pbase(), pptr(), and epptr() are all NULL.
|
|
// In put mode, pptr() is the current position,
|
|
// and eback(), gptr(), and egptr() are all NULL.
|
|
// Any edit_streambuf that is actively doing insertion (as opposed to
|
|
// replacing) // must have its pptr() pointing to the start of the gap.
|
|
// Only one edit_streambuf can be actively inserting into a specific
|
|
// edit_buffer; the edit_buffer's _writer field points to that edit_streambuf.
|
|
// That edit_streambuf "owns" the gap, and the actual start of the
|
|
// gap is the pptr() of the edit_streambuf; the edit_buffer::_gap_start pointer
|
|
// will only be updated on an edit_streambuf::overflow().
|
|
|
|
int edit_streambuf::truncate()
|
|
{
|
|
str->buffer->delete_range(str->buffer->tell((buf_char*)pptr()),
|
|
str->buffer->tell(str->end));
|
|
return 0;
|
|
}
|
|
|
|
#ifdef OLD_STDIO
|
|
inline void disconnect_gap_from_file(edit_buffer* buffer, FILE* fp)
|
|
{
|
|
if (buffer->gap_start_ptr != &fp->__bufp)
|
|
return;
|
|
buffer->gap_start_normal = fp->__bufp;
|
|
buffer->gap_start_ptr = &buffer->gap_start_normal;
|
|
}
|
|
#endif
|
|
|
|
void edit_streambuf::flush_to_buffer(edit_buffer* buffer)
|
|
{
|
|
if (pptr() > buffer->_gap_start && pptr() < buffer->gap_end())
|
|
buffer->_gap_start = pptr();
|
|
}
|
|
|
|
void edit_streambuf::disconnect_gap_from_file(edit_buffer* buffer)
|
|
{
|
|
if (buffer->_writer != this) return;
|
|
flush_to_buffer(buffer);
|
|
setp(pptr(),pptr());
|
|
buffer->_writer = NULL;
|
|
}
|
|
|
|
buf_index edit_buffer::tell(buf_char *ptr)
|
|
{
|
|
if (ptr <= gap_start())
|
|
return ptr - data;
|
|
else
|
|
return ptr - gap_end() + size1();
|
|
}
|
|
|
|
#if 0
|
|
buf_index buf_cookie::tell()
|
|
{
|
|
return str->buffer->tell(file->__bufp);
|
|
}
|
|
#endif
|
|
|
|
buf_index edit_buffer::tell(edit_mark*mark)
|
|
{
|
|
return tell(data + mark->index_in_buffer(this));
|
|
}
|
|
|
|
// adjust the position of the gap
|
|
|
|
void edit_buffer::move_gap(buf_offset pos)
|
|
{
|
|
if (pos < size1())
|
|
gap_left (pos);
|
|
else if (pos > size1())
|
|
gap_right (pos);
|
|
}
|
|
|
|
void edit_buffer::gap_left (int pos)
|
|
{
|
|
register buf_char *to, *from;
|
|
register int i;
|
|
int new_s1;
|
|
|
|
i = size1();
|
|
from = gap_start();
|
|
to = from + gap_size();
|
|
new_s1 = size1();
|
|
|
|
/* Now copy the characters. To move the gap down,
|
|
copy characters up. */
|
|
|
|
for (;;)
|
|
{
|
|
/* I gets number of characters left to copy. */
|
|
i = new_s1 - pos;
|
|
if (i == 0)
|
|
break;
|
|
#if 0
|
|
/* If a quit is requested, stop copying now.
|
|
Change POS to be where we have actually moved the gap to. */
|
|
if (QUITP)
|
|
{
|
|
pos = new_s1;
|
|
break;
|
|
}
|
|
#endif
|
|
/* Move at most 32000 chars before checking again for a quit. */
|
|
if (i > 32000)
|
|
i = 32000;
|
|
new_s1 -= i;
|
|
while (--i >= 0)
|
|
*--to = *--from;
|
|
}
|
|
|
|
/* Adjust markers, and buffer data structure, to put the gap at POS.
|
|
POS is where the loop above stopped, which may be what was specified
|
|
or may be where a quit was detected. */
|
|
adjust_markers (pos << 1, size1() << 1, gap_size(), data);
|
|
#ifndef OLD_STDIO
|
|
_gap_start = data + pos;
|
|
#else
|
|
if (gap_start_ptr == &gap_start_normal)
|
|
gap_start_normal = data + pos;
|
|
#endif
|
|
__gap_end_pos = to - data;
|
|
/* QUIT;*/
|
|
}
|
|
|
|
void edit_buffer::gap_right (int pos)
|
|
{
|
|
register buf_char *to, *from;
|
|
register int i;
|
|
int new_s1;
|
|
|
|
i = size1();
|
|
to = gap_start();
|
|
from = i + gap_end();
|
|
new_s1 = i;
|
|
|
|
/* Now copy the characters. To move the gap up,
|
|
copy characters down. */
|
|
|
|
while (1)
|
|
{
|
|
/* I gets number of characters left to copy. */
|
|
i = pos - new_s1;
|
|
if (i == 0)
|
|
break;
|
|
#if 0
|
|
/* If a quit is requested, stop copying now.
|
|
Change POS to be where we have actually moved the gap to. */
|
|
if (QUITP)
|
|
{
|
|
pos = new_s1;
|
|
break;
|
|
}
|
|
#endif
|
|
/* Move at most 32000 chars before checking again for a quit. */
|
|
if (i > 32000)
|
|
i = 32000;
|
|
new_s1 += i;
|
|
while (--i >= 0)
|
|
*to++ = *from++;
|
|
}
|
|
|
|
adjust_markers ((size1() + gap_size()) << 1, (pos + gap_size()) << 1,
|
|
- gap_size(), data);
|
|
#ifndef OLD_STDIO
|
|
_gap_start = data+pos;
|
|
#else
|
|
if (gap_start_ptr == &gap_start_normal)
|
|
gap_start_normal = data + pos;
|
|
#endif
|
|
__gap_end_pos = from - data;
|
|
/* QUIT;*/
|
|
}
|
|
|
|
/* make sure that the gap in the current buffer is at least k
|
|
characters wide */
|
|
|
|
void edit_buffer::make_gap(buf_offset k)
|
|
{
|
|
register buf_char *p1, *p2, *lim;
|
|
buf_char *old_data = data;
|
|
int s1 = size1();
|
|
|
|
if (gap_size() >= k)
|
|
return;
|
|
|
|
/* Get more than just enough */
|
|
if (buf_size > 1000) k += 2000;
|
|
else k += /*200;*/ 20; // for testing!
|
|
|
|
p1 = (buf_char *) realloc (data, s1 + size2() + k);
|
|
if (p1 == 0)
|
|
abort(); /*memory_full ();*/
|
|
|
|
k -= gap_size(); /* Amount of increase. */
|
|
|
|
/* Record new location of text */
|
|
data = p1;
|
|
|
|
/* Transfer the new free space from the end to the gap
|
|
by shifting the second segment upward */
|
|
p2 = data + buf_size;
|
|
p1 = p2 + k;
|
|
lim = p2 - size2();
|
|
while (lim < p2)
|
|
*--p1 = *--p2;
|
|
|
|
/* Finish updating text location data */
|
|
__gap_end_pos += k;
|
|
|
|
#ifndef OLD_STDIO
|
|
_gap_start = data + s1;
|
|
#else
|
|
if (gap_start_ptr == &gap_start_normal)
|
|
gap_start_normal = data + s1;
|
|
#endif
|
|
|
|
/* adjust markers */
|
|
adjust_markers (s1 << 1, (buf_size << 1) + 1, k, old_data);
|
|
buf_size += k;
|
|
}
|
|
|
|
/* Add `amount' to the position of every marker in the current buffer
|
|
whose current position is between `from' (exclusive) and `to' (inclusive).
|
|
Also, any markers past the outside of that interval, in the direction
|
|
of adjustment, are first moved back to the near end of the interval
|
|
and then adjusted by `amount'. */
|
|
|
|
void edit_buffer::adjust_markers(register mark_pointer low,
|
|
register mark_pointer high,
|
|
int amount, buf_char *old_data)
|
|
{
|
|
register struct edit_mark *m;
|
|
register mark_pointer mpos;
|
|
/* convert to mark_pointer */
|
|
amount <<= 1;
|
|
|
|
if (_writer)
|
|
_writer->disconnect_gap_from_file(this);
|
|
|
|
for (m = mark_list(); m != NULL; m = m->chain)
|
|
{
|
|
mpos = m->_pos;
|
|
if (amount > 0)
|
|
{
|
|
if (mpos > high && mpos < high + amount)
|
|
mpos = high + amount;
|
|
}
|
|
else
|
|
{
|
|
if (mpos > low + amount && mpos <= low)
|
|
mpos = low + amount;
|
|
}
|
|
if (mpos > low && mpos <= high)
|
|
mpos += amount;
|
|
m->_pos = mpos;
|
|
}
|
|
|
|
// Now adjust files
|
|
edit_streambuf *file;
|
|
|
|
for (file = files; file != NULL; file = file->next) {
|
|
mpos = file->current() - old_data;
|
|
if (amount > 0)
|
|
{
|
|
if (mpos > high && mpos < high + amount)
|
|
mpos = high + amount;
|
|
}
|
|
else
|
|
{
|
|
if (mpos > low + amount && mpos <= low)
|
|
mpos = low + amount;
|
|
}
|
|
if (mpos > low && mpos <= high)
|
|
mpos += amount;
|
|
char* new_pos = data + mpos;
|
|
file->set_current(new_pos, file->is_reading());
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
stdio_
|
|
__off == index at start of buffer (need only be valid after seek ? )
|
|
__buf ==
|
|
|
|
if read/read_delete/overwrite mode:
|
|
__endp <= min(*gap_start_ptr, edit_string->end->ptr(buffer))
|
|
|
|
if inserting:
|
|
must have *gap_start_ptr == __bufp && *gap_start_ptr+gap == __endp
|
|
file->edit_string->end->ptr(buffer) == *gap_start_ptr+end
|
|
if write_mode:
|
|
if before gap
|
|
#endif
|
|
|
|
int edit_streambuf::underflow()
|
|
{
|
|
if (!(_mode & ios::in))
|
|
return EOF;
|
|
struct edit_buffer *buffer = str->buffer;
|
|
if (!is_reading()) { // Must switch from put to get mode.
|
|
disconnect_gap_from_file(buffer);
|
|
set_current(pptr(), 1);
|
|
}
|
|
buf_char *str_end = str->end->ptr(buffer);
|
|
retry:
|
|
if (gptr() < egptr()) {
|
|
return *gptr();
|
|
}
|
|
if ((buf_char*)gptr() == str_end)
|
|
return EOF;
|
|
if (str_end <= buffer->gap_start()) {
|
|
setg(eback(), gptr(), str_end);
|
|
goto retry;
|
|
}
|
|
if (gptr() < buffer->gap_start()) {
|
|
setg(eback(), gptr(), buffer->gap_start());
|
|
goto retry;
|
|
}
|
|
if (gptr() == buffer->gap_start()) {
|
|
disconnect_gap_from_file(buffer);
|
|
// fp->__offset += fp->__bufp - fp->__buffer;
|
|
setg(buffer->gap_end(), buffer->gap_end(), str_end);
|
|
}
|
|
else
|
|
setg(eback(), gptr(), str_end);
|
|
goto retry;
|
|
}
|
|
|
|
int edit_streambuf::overflow(int ch)
|
|
{
|
|
if (_mode == ios::in)
|
|
return EOF;
|
|
struct edit_buffer *buffer = str->buffer;
|
|
flush_to_buffer(buffer);
|
|
if (ch == EOF)
|
|
return 0;
|
|
if (is_reading()) { // Must switch from get to put mode.
|
|
set_current(gptr(), 0);
|
|
}
|
|
buf_char *str_end = str->end->ptr(buffer);
|
|
retry:
|
|
if (pptr() < epptr()) {
|
|
*pptr() = ch;
|
|
pbump(1);
|
|
return (unsigned char)ch;
|
|
}
|
|
if ((buf_char*)pptr() == str_end || inserting()) {
|
|
/* insert instead */
|
|
if (buffer->_writer)
|
|
buffer->_writer->flush_to_buffer(); // Redundant?
|
|
buffer->_writer = NULL;
|
|
if (pptr() >= buffer->gap_end())
|
|
buffer->move_gap(pptr() - buffer->gap_size());
|
|
else
|
|
buffer->move_gap(pptr());
|
|
buffer->make_gap(1);
|
|
setp(buffer->gap_start(), buffer->gap_end());
|
|
buffer->_writer = this;
|
|
*pptr() = ch;
|
|
pbump(1);
|
|
return (unsigned char)ch;
|
|
}
|
|
if (str_end <= buffer->gap_start()) {
|
|
// Entire string is left of gap.
|
|
setp(pptr(), str_end);
|
|
}
|
|
else if (pptr() < buffer->gap_start()) {
|
|
// Current pos is left of gap.
|
|
setp(pptr(), buffer->gap_start());
|
|
goto retry;
|
|
}
|
|
else if (pptr() == buffer->gap_start()) {
|
|
// Current pos is at start of gap; move to end of gap.
|
|
// disconnect_gap_from_file(buffer);
|
|
setp(buffer->gap_end(), str_end);
|
|
// __offset += __bufp - __buffer;
|
|
}
|
|
else {
|
|
// Otherwise, current pos is right of gap.
|
|
setp(pptr(), str_end);
|
|
}
|
|
goto retry;
|
|
}
|
|
|
|
void edit_streambuf::set_current(char *new_pos, int reading)
|
|
{
|
|
if (reading) {
|
|
setg(new_pos, new_pos, new_pos);
|
|
setp(NULL, NULL);
|
|
}
|
|
else {
|
|
setg(NULL, NULL, NULL);
|
|
setp(new_pos, new_pos);
|
|
}
|
|
}
|
|
|
|
// Called by fseek(fp, pos, whence) if fp is bound to a edit_buffer.
|
|
|
|
streampos edit_streambuf::seekoff(streamoff offset, _seek_dir dir,
|
|
int /* =ios::in|ios::out*/)
|
|
{
|
|
struct edit_buffer *buffer = str->buffer;
|
|
disconnect_gap_from_file(buffer);
|
|
buf_index cur_pos = buffer->tell((buf_char*)current());;
|
|
buf_index start_pos = buffer->tell(str->start);
|
|
buf_index end_pos = buffer->tell(str->end);
|
|
switch (dir) {
|
|
case ios::beg:
|
|
offset += start_pos;
|
|
break;
|
|
case ios::cur:
|
|
offset += cur_pos;
|
|
break;
|
|
case ios::end:
|
|
offset += end_pos;
|
|
break;
|
|
}
|
|
if (offset < start_pos || offset > end_pos)
|
|
return EOF;
|
|
buf_char *new_pos = buffer->data + offset;
|
|
buf_char* gap_start = buffer->gap_start();
|
|
if (new_pos > gap_start) {
|
|
buf_char* gap_end = buffer->gap_end();
|
|
new_pos += gap_end - gap_start;
|
|
if (new_pos >= buffer->data + buffer->buf_size) abort(); // Paranoia.
|
|
}
|
|
set_current(new_pos, is_reading());
|
|
return EOF;
|
|
}
|
|
|
|
#if 0
|
|
int buf_seek(void *arg_cookie, fpos_t * pos, int whence)
|
|
{
|
|
struct buf_cookie *cookie = arg_cookie;
|
|
FILE *file = cookie->file;
|
|
struct edit_buffer *buffer = cookie->str->buffer;
|
|
buf_char *str_start = cookie->str->start->ptr(buffer);
|
|
disconnect_gap_from_file(buffer, cookie->file);
|
|
fpos_t cur_pos, new_pos;
|
|
if (file->__bufp <= *buffer->gap_start_ptr
|
|
|| str_start >= buffer->__gap_end)
|
|
cur_pos = str_start - file->__bufp;
|
|
else
|
|
cur_pos =
|
|
(*buffer->gap_start_ptr - str_start) + (file->__bufp - __gap_end);
|
|
end_pos = ...;
|
|
switch (whence) {
|
|
case SEEK_SET:
|
|
new_pos = *pos;
|
|
break;
|
|
case SEEK_CUR:
|
|
new_pos = cur_pos + *pos;
|
|
break;
|
|
case SEEK_END:
|
|
new_pos = end_pos + *pos;
|
|
break;
|
|
}
|
|
if (new_pos > end_pos) {
|
|
seek to end_pos;
|
|
insert_nulls(new_pos - end_pos);
|
|
return;
|
|
}
|
|
if (str_start + new_pos <= *gap_start_ptr &* *gap_start_ptr < end) {
|
|
__buffer = str_start;
|
|
__off = 0;
|
|
__bufp = str_start + new_pos;
|
|
file->__get_limit =
|
|
*buffer->gap_start_ptr; /* what if gap_start_ptr == &bufp ??? */
|
|
} else if () {
|
|
|
|
}
|
|
*pos = new_pos;
|
|
}
|
|
#endif
|
|
|
|
/* Delete characters from `from' up to (but not incl) `to' */
|
|
|
|
void edit_buffer::delete_range (buf_index from, buf_index to)
|
|
{
|
|
register int numdel;
|
|
|
|
if ((numdel = to - from) <= 0)
|
|
return;
|
|
|
|
/* Make sure the gap is somewhere in or next to what we are deleting */
|
|
if (from > size1())
|
|
gap_right (from);
|
|
if (to < size1())
|
|
gap_left (to);
|
|
|
|
/* Relocate all markers pointing into the new, larger gap
|
|
to point at the end of the text before the gap. */
|
|
adjust_markers ((to + gap_size()) << 1, (to + gap_size()) << 1,
|
|
- numdel - gap_size(), data);
|
|
|
|
__gap_end_pos = to + gap_size();
|
|
_gap_start = data + from;
|
|
}
|
|
|
|
void edit_buffer::delete_range(struct edit_mark *start, struct edit_mark *end)
|
|
{
|
|
delete_range(tell(start), tell(end));
|
|
}
|
|
|
|
void buf_delete_chars(struct edit_buffer *, struct edit_mark *, size_t)
|
|
{
|
|
abort();
|
|
}
|
|
|
|
edit_streambuf::edit_streambuf(edit_string* bstr, int mode)
|
|
{
|
|
_mode = mode;
|
|
str = bstr;
|
|
edit_buffer* buffer = bstr->buffer;
|
|
next = buffer->files;
|
|
buffer->files = this;
|
|
char* buf_ptr = bstr->start->ptr(buffer);
|
|
_inserting = 0;
|
|
// setb(buf_ptr, buf_ptr, 0);
|
|
set_current(buf_ptr, !(mode & ios::out+ios::trunc+ios::app));
|
|
if (_mode & ios::trunc)
|
|
truncate();
|
|
if (_mode & ios::ate)
|
|
seekoff(0, ios::end);
|
|
}
|
|
|
|
// Called by fclose(fp) if fp is bound to a edit_buffer.
|
|
|
|
#if 0
|
|
static int buf_close(void *arg)
|
|
{
|
|
register struct buf_cookie *cookie = arg;
|
|
struct edit_buffer *buffer = cookie->str->buffer;
|
|
struct buf_cookie **ptr;
|
|
for (ptr = &buffer->files; *ptr != cookie; ptr = &(*ptr)->next) ;
|
|
*ptr = cookie->next;
|
|
disconnect_gap_from_file(buffer, cookie->file);
|
|
free (cookie);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
edit_streambuf::~edit_streambuf()
|
|
{
|
|
if (_mode == ios::out)
|
|
truncate();
|
|
// Unlink this from list of files associated with bstr->buffer.
|
|
edit_streambuf **ptr = &str->buffer->files;
|
|
for (; *ptr != this; ptr = &(*ptr)->next) { }
|
|
*ptr = next;
|
|
|
|
disconnect_gap_from_file(str->buffer);
|
|
}
|
|
|
|
edit_buffer::edit_buffer()
|
|
{
|
|
buf_size = /*200;*/ 15; /* for testing! */
|
|
data = (buf_char*)malloc(buf_size);
|
|
files = NULL;
|
|
#ifndef OLD_STDIO
|
|
_gap_start = data;
|
|
_writer = NULL;
|
|
#else
|
|
gap_start_normal = data;
|
|
gap_start_ptr = &gap_start_normal;
|
|
#endif
|
|
__gap_end_pos = buf_size;
|
|
start_mark.chain = &end_mark;
|
|
start_mark._pos = 0;
|
|
end_mark.chain = NULL;
|
|
end_mark._pos = 2 * buf_size + 1;
|
|
}
|
|
|
|
// Allocate a new mark, which is adjusted by 'delta' bytes from 'this'.
|
|
// Restrict new mark to lie within 'str'.
|
|
|
|
edit_mark::edit_mark(struct edit_string *str, long delta)
|
|
{
|
|
struct edit_buffer *buf = str->buffer;
|
|
chain = buf->start_mark.chain;
|
|
buf->start_mark.chain = this;
|
|
mark_pointer size1 = buf->size1() << 1;
|
|
int gap_size = buf->gap_size() << 1;
|
|
delta <<= 1;
|
|
|
|
// check if new and old marks are opposite sides of gap
|
|
if (_pos <= size1 && _pos + delta > size1)
|
|
delta += gap_size;
|
|
else if (_pos >= size1 + gap_size && _pos + delta < size1 + gap_size)
|
|
delta -= gap_size;
|
|
|
|
_pos = _pos + delta;
|
|
if (_pos < str->start->_pos & ~1)
|
|
_pos = (str->start->_pos & ~ 1) + (_pos & 1);
|
|
else if (_pos >= str->end->_pos)
|
|
_pos = (str->end->_pos & ~ 1) + (_pos & 1);
|
|
}
|
|
|
|
// A (slow) way to find the buffer a mark belongs to.
|
|
|
|
edit_buffer * edit_mark::buffer()
|
|
{
|
|
struct edit_mark *mark;
|
|
for (mark = this; mark->chain != NULL; mark = mark->chain) ;
|
|
// Assume that the last mark on the chain is the end_mark.
|
|
return (edit_buffer *)((char*)mark - offsetof(edit_buffer, end_mark));
|
|
}
|
|
|
|
edit_mark::~edit_mark()
|
|
{
|
|
// Must unlink mark from chain of owning buffer
|
|
struct edit_buffer *buf = buffer();
|
|
if (this == &buf->start_mark || this == &buf->end_mark) abort();
|
|
edit_mark **ptr;
|
|
for (ptr = &buf->start_mark.chain; *ptr != this; ptr = &(*ptr)->chain) ;
|
|
*ptr = this->chain;
|
|
}
|
|
|
|
int edit_string::length() const
|
|
{
|
|
ptrdiff_t delta = end->ptr(buffer) - start->ptr(buffer);
|
|
if (end->ptr(buffer) <= buffer->gap_start() ||
|
|
start->ptr(buffer) >= buffer->gap_end())
|
|
return delta;
|
|
return delta - buffer->gap_size();
|
|
}
|
|
|
|
buf_char * edit_string::copy_bytes(int *lenp) const
|
|
{
|
|
char *new_str;
|
|
int len1, len2;
|
|
buf_char *start1, *start2;
|
|
start1 = start->ptr(buffer);
|
|
if (end->ptr(buffer) <= buffer->gap_start()
|
|
|| start->ptr(buffer) >= buffer->gap_end()) {
|
|
len1 = end->ptr(buffer) - start1;
|
|
len2 = 0;
|
|
start2 = NULL; // To avoid a warning from g++.
|
|
}
|
|
else {
|
|
len1 = buffer->gap_start() - start1;
|
|
start2 = buffer->gap_end();
|
|
len2 = end->ptr(buffer) - start2;
|
|
}
|
|
new_str = (char*)malloc(len1 + len2 + 1);
|
|
memcpy(new_str, start1, len1);
|
|
if (len2 > 0) memcpy(new_str + len1, start2, len2);
|
|
new_str[len1+len2] = '\0';
|
|
*lenp = len1+len2;
|
|
return new_str;
|
|
}
|
|
|
|
// Replace the buf_chars in 'this' with ones from 'src'.
|
|
// Equivalent to deleting this, then inserting src, except tries
|
|
// to leave marks in place: Marks whose offset from the start
|
|
// of 'this' is less than 'src->length()' will still have the
|
|
// same offset in 'this' when done.
|
|
|
|
void edit_string::assign(struct edit_string *src)
|
|
{
|
|
edit_streambuf dst_file(this, ios::out);
|
|
if (buffer == src->buffer /*&& ???*/) { /* overly conservative */
|
|
int src_len;
|
|
buf_char *new_str;
|
|
new_str = src->copy_bytes(&src_len);
|
|
dst_file.sputn(new_str, src_len);
|
|
free (new_str);
|
|
} else {
|
|
edit_streambuf src_file(src, ios::in);
|
|
for ( ; ; ) {
|
|
int ch = src_file.sbumpc();
|
|
if (ch == EOF) break;
|
|
dst_file.sputc(ch);
|
|
}
|
|
}
|
|
}
|