#ifdef __GNUG__
#pragma implementation
#endif
#include "libioP.h"
#include "editbuf.h"
#include <stddef.h>
#include <stdlib.h>
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));
}
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();
for (;;)
{
i = new_s1 - pos;
if (i == 0)
break;
#if 0
if (QUITP)
{
pos = new_s1;
break;
}
#endif
if (i > 32000)
i = 32000;
new_s1 -= i;
while (--i >= 0)
*--to = *--from;
}
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;
}
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;
while (1)
{
i = pos - new_s1;
if (i == 0)
break;
#if 0
if (QUITP)
{
pos = new_s1;
break;
}
#endif
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;
}
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;
if (buf_size > 1000) k += 2000;
else k += 20;
p1 = (buf_char *) realloc (data, s1 + size2() + k);
if (p1 == 0)
abort();
k -= gap_size();
data = p1;
p2 = data + buf_size;
p1 = p2 + k;
lim = p2 - size2();
while (lim < p2)
*--p1 = *--p2;
__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 (s1 << 1, (buf_size << 1) + 1, k, old_data);
buf_size += k;
}
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;
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;
}
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()) { 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);
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()) { 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()) {
if (buffer->_writer)
buffer->_writer->flush_to_buffer(); 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()) {
setp(pptr(), str_end);
}
else if (pptr() < buffer->gap_start()) {
setp(pptr(), buffer->gap_start());
goto retry;
}
else if (pptr() == buffer->gap_start()) {
setp(buffer->gap_end(), str_end);
}
else {
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);
}
}
streampos edit_streambuf::seekoff(streamoff offset, _seek_dir dir,
int )
{
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(); }
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;
} else if () {
}
*pos = new_pos;
}
#endif
void edit_buffer::delete_range (buf_index from, buf_index to)
{
register int numdel;
if ((numdel = to - from) <= 0)
return;
if (from > size1())
gap_right (from);
if (to < size1())
gap_left (to);
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;
set_current(buf_ptr, !(mode & ios::out+ios::trunc+ios::app));
if (_mode & ios::trunc)
truncate();
if (_mode & ios::ate)
seekoff(0, ios::end);
}
#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();
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 = 15;
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;
}
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;
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);
}
edit_buffer * edit_mark::buffer()
{
struct edit_mark *mark;
for (mark = this; mark->chain != NULL; mark = mark->chain) ;
return (edit_buffer *)((char*)mark - offsetof(edit_buffer, end_mark));
}
edit_mark::~edit_mark()
{
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; }
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;
}
void edit_string::assign(struct edit_string *src)
{
edit_streambuf dst_file(this, ios::out);
if (buffer == src->buffer ) {
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);
}
}
}