evil-qemacs/buffer.c

1752 lines
43 KiB
C
Raw Normal View History

2004-05-29 10:17:37 +00:00
/*
* Buffer handling for QEmacs
2007-12-03 16:10:38 +00:00
*
2004-05-29 10:17:37 +00:00
* Copyright (c) 2000 Fabrice Bellard.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
2007-12-03 16:10:38 +00:00
2004-05-29 10:17:37 +00:00
#include "qe.h"
#ifndef WIN32
#include <sys/mman.h>
#endif
static void eb_addlog(EditBuffer *b, enum LogOperation op,
int offset, int size);
static EditBufferDataType *first_buffer_data_type;
2004-05-29 10:17:37 +00:00
/************************************************************/
/* basic access to the edit buffer */
/* find a page at a given offset */
static Page *find_page(EditBuffer *b, int *offset_ptr)
{
Page *p;
int offset;
offset = *offset_ptr;
if (b->cur_page && offset >= b->cur_offset &&
offset < b->cur_offset + b->cur_page->size) {
/* use the cache */
*offset_ptr -= b->cur_offset;
return b->cur_page;
} else {
p = b->page_table;
while (offset >= p->size) {
offset -= p->size;
p++;
}
b->cur_page = p;
b->cur_offset = *offset_ptr - offset;
*offset_ptr = offset;
return p;
}
}
/* prepare a page to be written */
static void update_page(Page *p)
{
2005-05-11 14:35:54 +00:00
u8 *buf;
2004-05-29 10:17:37 +00:00
/* if the page is read only, copy it */
if (p->flags & PG_READ_ONLY) {
buf = qe_malloc_dup(p->data, p->size);
2004-05-29 10:17:37 +00:00
/* XXX: should return an error */
if (!buf)
return;
p->data = buf;
p->flags &= ~PG_READ_ONLY;
}
p->flags &= ~(PG_VALID_POS | PG_VALID_CHAR | PG_VALID_COLORS);
}
/* Read or write in the buffer. We must have 0 <= offset < b->total_size */
static int eb_rw(EditBuffer *b, int offset, u8 *buf, int size1, int do_write)
{
Page *p;
int len, size;
if (offset < 0)
return 0;
2004-05-29 10:17:37 +00:00
if ((offset + size1) > b->total_size)
size1 = b->total_size - offset;
if (size1 <= 0)
return 0;
size = size1;
if (do_write)
2005-05-11 14:35:54 +00:00
eb_addlog(b, LOGOP_WRITE, offset, size);
2004-05-29 10:17:37 +00:00
p = find_page(b, &offset);
while (size > 0) {
len = p->size - offset;
if (len > size)
len = size;
if (do_write) {
update_page(p);
memcpy(p->data + offset, buf, len);
} else {
memcpy(buf, p->data + offset, len);
}
buf += len;
size -= len;
offset += len;
if (offset >= p->size) {
p++;
offset = 0;
}
}
return size1;
}
/* We must have: 0 <= offset < b->total_size */
/* Safety: request will be clipped */
2005-07-08 09:30:43 +00:00
int eb_read(EditBuffer *b, int offset, void *buf, int size)
2004-05-29 10:17:37 +00:00
{
return eb_rw(b, offset, buf, size, 0);
}
/* Note: eb_write can be used to insert after the end of the buffer */
void eb_write(EditBuffer *b, int offset, const void *buf_arg, int size)
2004-05-29 10:17:37 +00:00
{
int len, left;
const u8 *buf = buf_arg;
2007-12-20 20:39:18 +00:00
if (b->flags & BF_READONLY)
2007-12-20 20:39:18 +00:00
return;
len = eb_rw(b, offset, (void *)buf, size, 1);
2004-05-29 10:17:37 +00:00
left = size - len;
if (left > 0) {
offset += len;
buf += len;
eb_insert(b, offset, buf, left);
}
}
/* internal function for insertion : 'buf' of size 'size' at the
beginning of the page at page_index */
static void eb_insert1(EditBuffer *b, int page_index, const u8 *buf, int size)
2004-05-29 10:17:37 +00:00
{
int len, n;
Page *p;
if (page_index < b->nb_pages) {
p = &b->page_table[page_index];
2004-05-29 10:17:37 +00:00
len = MAX_PAGE_SIZE - p->size;
if (len > size)
len = size;
if (len > 0) {
update_page(p);
/* CG: probably faster with qe_malloc + qe_free */
qe_realloc(&p->data, p->size + len);
2005-05-11 14:35:54 +00:00
memmove(p->data + len, p->data, p->size);
2004-05-29 10:17:37 +00:00
memcpy(p->data, buf + size - len, len);
size -= len;
p->size += len;
}
}
/* now add new pages if necessary */
n = (size + MAX_PAGE_SIZE - 1) / MAX_PAGE_SIZE;
if (n > 0) {
b->nb_pages += n;
qe_realloc(&b->page_table, b->nb_pages * sizeof(Page));
p = &b->page_table[page_index];
memmove(p + n, p, sizeof(Page) * (b->nb_pages - n - page_index));
2004-05-29 10:17:37 +00:00
while (size > 0) {
len = size;
if (len > MAX_PAGE_SIZE)
len = MAX_PAGE_SIZE;
p->size = len;
p->data = qe_malloc_dup(buf, len);
2004-05-29 10:17:37 +00:00
p->flags = 0;
buf += len;
size -= len;
p++;
}
}
}
/* We must have : 0 <= offset <= b->total_size */
static void eb_insert_lowlevel(EditBuffer *b, int offset,
2005-05-11 14:35:54 +00:00
const u8 *buf, int size)
2004-05-29 10:17:37 +00:00
{
int len, len_out, page_index;
Page *p;
b->total_size += size;
/* find the correct page */
p = b->page_table;
if (offset > 0) {
offset--;
p = find_page(b, &offset);
offset++;
/* compute what we can insert in current page */
len = MAX_PAGE_SIZE - offset;
if (len > size)
len = size;
/* number of bytes to put in next pages */
len_out = p->size + len - MAX_PAGE_SIZE;
page_index = p - b->page_table;
if (len_out > 0)
eb_insert1(b, page_index + 1,
p->data + p->size - len_out, len_out);
else
len_out = 0;
/* now we can insert in current page */
if (len > 0) {
p = b->page_table + page_index;
update_page(p);
p->size += len - len_out;
qe_realloc(&p->data, p->size);
2004-05-29 10:17:37 +00:00
memmove(p->data + offset + len,
p->data + offset, p->size - (offset + len));
memcpy(p->data + offset, buf, len);
buf += len;
size -= len;
}
} else {
page_index = -1;
}
/* insert the remaining data in the next pages */
if (size > 0)
eb_insert1(b, page_index + 1, buf, size);
/* the page cache is no longer valid */
b->cur_page = NULL;
}
/* Insert 'size bytes of 'src' buffer from position 'src_offset' into
buffer 'dest' at offset 'dest_offset'. 'src' MUST BE DIFFERENT from
'dest' */
void eb_insert_buffer(EditBuffer *dest, int dest_offset,
EditBuffer *src, int src_offset,
int size)
{
Page *p, *p_start, *q;
int size_start, len, n, page_index;
if (dest->flags & BF_READONLY)
2007-12-20 20:39:18 +00:00
return;
2004-05-29 10:17:37 +00:00
if (size == 0)
return;
/* CG: should assert parameter consistency */
2004-05-29 10:17:37 +00:00
eb_addlog(dest, LOGOP_INSERT, dest_offset, size);
/* insert the data from the first page if it is not completely
selected */
p = find_page(src, &src_offset);
if (src_offset > 0) {
len = p->size - src_offset;
if (len > size)
len = size;
eb_insert_lowlevel(dest, dest_offset, p->data + src_offset, len);
dest_offset += len;
size -= len;
p++;
}
if (size == 0)
return;
/* cut the page at dest offset if needed */
if (dest_offset < dest->total_size) {
q = find_page(dest, &dest_offset);
page_index = q - dest->page_table;
if (dest_offset > 0) {
page_index++;
eb_insert1(dest, page_index, q->data + dest_offset,
q->size - dest_offset);
/* must reload q because page_table may have been
realloced */
q = dest->page_table + page_index - 1;
update_page(q);
qe_realloc(&q->data, dest_offset);
2004-05-29 10:17:37 +00:00
q->size = dest_offset;
}
} else {
page_index = dest->nb_pages;
}
/* update total_size */
dest->total_size += size;
/* compute the number of complete pages to insert */
p_start = p;
size_start = size;
while (size > 0 && p->size <= size) {
size -= p->size;
p++;
}
n = p - p_start; /* number of pages to insert */
p = p_start;
if (n > 0) {
/* add the pages */
dest->nb_pages += n;
qe_realloc(&dest->page_table, dest->nb_pages * sizeof(Page));
2004-05-29 10:17:37 +00:00
q = dest->page_table + page_index;
memmove(q + n, q, sizeof(Page) * (dest->nb_pages - n - page_index));
2004-05-29 10:17:37 +00:00
p = p_start;
while (n > 0) {
len = p->size;
q->size = len;
if (p->flags & PG_READ_ONLY) {
/* simply copy the reference */
q->flags = PG_READ_ONLY;
q->data = p->data;
} else {
/* allocate a new page */
q->flags = 0;
q->data = qe_malloc_dup(p->data, len);
2004-05-29 10:17:37 +00:00
}
n--;
p++;
q++;
}
page_index = q - dest->page_table;
}
/* insert the remaning bytes */
if (size > 0) {
eb_insert1(dest, page_index, p->data, size);
}
/* the page cache is no longer valid */
dest->cur_page = NULL;
}
/* Insert 'size' bytes from 'buf' into 'b' at offset 'offset'. We must
have : 0 <= offset <= b->total_size */
2005-07-08 09:30:43 +00:00
void eb_insert(EditBuffer *b, int offset, const void *buf, int size)
2004-05-29 10:17:37 +00:00
{
if (b->flags & BF_READONLY)
2007-12-20 20:39:18 +00:00
return;
/* sanity checks */
if (offset > b->total_size)
2007-12-20 20:39:18 +00:00
offset = b->total_size;
if (offset < 0 || size <= 0)
return;
2004-05-29 10:17:37 +00:00
eb_addlog(b, LOGOP_INSERT, offset, size);
eb_insert_lowlevel(b, offset, buf, size);
/* the page cache is no longer valid */
b->cur_page = NULL;
}
/* We must have : 0 <= offset <= b->total_size */
void eb_delete(EditBuffer *b, int offset, int size)
{
int n, len;
Page *del_start, *p;
if (b->flags & BF_READONLY)
2007-12-20 20:39:18 +00:00
return;
if (offset + size > b->total_size)
2007-12-20 20:39:18 +00:00
size = b->total_size - offset;
if (offset < 0 || size <= 0)
2004-05-29 10:17:37 +00:00
return;
b->total_size -= size;
eb_addlog(b, LOGOP_DELETE, offset, size);
/* find the correct page */
p = find_page(b, &offset);
n = 0;
del_start = NULL;
while (size > 0) {
len = p->size - offset;
if (len > size)
len = size;
if (len == p->size) {
if (!del_start)
del_start = p;
/* we cannot free if read only */
if (!(p->flags & PG_READ_ONLY))
qe_free(&p->data);
2004-05-29 10:17:37 +00:00
p++;
offset = 0;
n++;
} else {
update_page(p);
memmove(p->data + offset, p->data + offset + len,
p->size - offset - len);
p->size -= len;
qe_realloc(&p->data, p->size);
2004-05-29 10:17:37 +00:00
offset += len;
if (offset >= p->size) {
p++;
offset = 0;
}
}
size -= len;
}
/* now delete the requested pages */
if (n > 0) {
b->nb_pages -= n;
memmove(del_start, del_start + n,
(b->page_table + b->nb_pages - del_start) * sizeof(Page));
qe_realloc(&b->page_table, b->nb_pages * sizeof(Page));
2004-05-29 10:17:37 +00:00
}
/* the page cache is no longer valid */
b->cur_page = NULL;
}
/* flush the log */
void log_reset(EditBuffer *b)
{
if (b->log_buffer) {
eb_free(b->log_buffer);
b->log_buffer = NULL;
b->log_new_index = 0;
b->log_current = 0;
b->nb_logs = 0;
}
b->modified = 0;
}
/* rename a buffer and add characters so that the name is unique */
void set_buffer_name(EditBuffer *b, const char *name1)
{
char name[sizeof(b->name)];
int n, pos;
pstrcpy(name, sizeof(b->name) - 10, name1);
/* set the buffer name to NULL since it will be changed */
b->name[0] = '\0';
pos = strlen(name);
n = 2;
while (eb_find(name) != NULL) {
snprintf(name + pos, sizeof(b->name) - pos, "<%d>", n);
2004-05-29 10:17:37 +00:00
n++;
}
pstrcpy(b->name, sizeof(b->name), name);
}
EditBuffer *eb_new(const char *name, int flags)
{
QEmacsState *qs = &qe_state;
EditBuffer *b;
EditBuffer **pb;
2004-05-29 10:17:37 +00:00
b = qe_mallocz(EditBuffer);
2004-05-29 10:17:37 +00:00
if (!b)
return NULL;
pstrcpy(b->name, sizeof(b->name), name);
b->flags = flags;
/* set default data type */
b->data_type = &raw_data_type;
/* XXX: suppress save_log and always use flag ? */
b->save_log = ((flags & BF_SAVELOG) != 0);
/* add buffer in global buffer list (at end for system buffers) */
pb = &qs->first_buffer;
if (*b->name == '*') {
2007-12-20 20:39:18 +00:00
while (*pb)
pb = &(*pb)->next;
}
b->next = *pb;
*pb = b;
2004-05-29 10:17:37 +00:00
/* CG: default charset should be selectable */
2004-05-29 10:17:37 +00:00
eb_set_charset(b, &charset_8859_1);
/* add mark move callback */
eb_add_callback(b, eb_offset_callback, &b->mark);
if (!strcmp(name, "*trace*"))
qs->trace_buffer = b;
2004-05-29 10:17:37 +00:00
return b;
}
/* Return an empty scratch buffer, create one if necessary */
EditBuffer *eb_scratch(const char *name)
{
EditBuffer *b;
b = eb_find(name);
if (b != NULL) {
2007-12-20 20:39:18 +00:00
eb_clear(b);
} else {
2007-12-20 20:39:18 +00:00
b = eb_new(name, 0);
}
return b;
}
void eb_clear(EditBuffer *b)
{
b->flags &= ~BF_READONLY;
b->save_log = 0;
eb_delete(b, 0, b->total_size);
log_reset(b);
/* close and reset file handle */
if (b->file_handle > 0) {
close(b->file_handle);
}
b->file_handle = 0;
/* TODO: clear buffer structure */
//memset(b, 0, offsetof(EditBuffer, remanent_area));
}
2004-05-29 10:17:37 +00:00
void eb_free(EditBuffer *b)
{
QEmacsState *qs = &qe_state;
EditBuffer **pb;
EditBufferCallbackList *l, *l1;
if (b == NULL)
2007-12-20 20:39:18 +00:00
return;
2004-05-29 10:17:37 +00:00
/* call user defined close */
if (b->close)
b->close(b);
/* free each callback */
2005-05-11 14:35:54 +00:00
for (l = b->first_callback; l != NULL;) {
2004-05-29 10:17:37 +00:00
l1 = l->next;
qe_free(&l);
2004-05-29 10:17:37 +00:00
l = l1;
}
b->first_callback = NULL;
eb_clear(b);
2004-05-29 10:17:37 +00:00
/* suppress from buffer list */
pb = &qs->first_buffer;
while (*pb != NULL) {
if (*pb == b)
break;
pb = &(*pb)->next;
}
*pb = (*pb)->next;
if (b == qs->trace_buffer)
qs->trace_buffer = NULL;
qe_free(&b);
2004-05-29 10:17:37 +00:00
}
EditBuffer *eb_find(const char *name)
{
QEmacsState *qs = &qe_state;
EditBuffer *b;
b = qs->first_buffer;
while (b != NULL) {
if (!strcmp(b->name, name))
return b;
b = b->next;
}
return NULL;
}
EditBuffer *eb_find_file(const char *filename)
{
QEmacsState *qs = &qe_state;
EditBuffer *b;
b = qs->first_buffer;
while (b != NULL) {
/* XXX: should also use stat to ensure this is same file */
if (!strcmp(b->filename, filename))
return b;
b = b->next;
}
return NULL;
}
/* Find the next window showing a given buffer */
EditState *eb_find_window(EditBuffer *b, EditState *e)
{
QEmacsState *qs = &qe_state;
for (e = e ? e->next_window : qs->first_window;
e != NULL;
e = e->next_window)
{
if (e->b == b)
return e;
}
return NULL;
}
void eb_trace_bytes(const void *buf, int size, int state)
{
QEmacsState *qs = &qe_state;
EditBuffer *b = qs->trace_buffer;
EditState *e;
int point;
if (b) {
point = b->total_size;
if (qs->trace_buffer_state != state) {
const char *str = NULL;
switch (qs->trace_buffer_state) {
case EB_TRACE_TTY:
str = "|\n";
break;
case EB_TRACE_PTY:
str = "|\n";
break;
case EB_TRACE_SHELL:
str = "|\n";
break;
}
if (str) {
eb_write(b, b->total_size, str, strlen(str));
}
qs->trace_buffer_state = state;
switch (qs->trace_buffer_state) {
case EB_TRACE_TTY:
str = "--|";
break;
case EB_TRACE_PTY:
str = ">>|";
break;
case EB_TRACE_SHELL:
str = "<<|";
break;
}
if (str) {
eb_write(b, b->total_size, str, strlen(str));
}
}
#if 0
/* CG: could make traces more readable: */
if (ch < 32 || ch == 127)
fprintf(stderr, "got %d '^%c'\n", ch, ('@' + ch) & 127);
else
fprintf(stderr, "got %d '%c'\n", ch, ch);
#endif
eb_write(b, b->total_size, buf, size);
/* If point is visible in window, should keep it so */
e = eb_find_window(b, NULL);
if (e && e->offset == point)
e->offset = b->total_size;
}
}
2004-05-29 10:17:37 +00:00
/************************************************************/
/* callbacks */
2007-12-20 20:39:18 +00:00
int eb_add_callback(EditBuffer *b, EditBufferCallback cb, void *opaque)
2004-05-29 10:17:37 +00:00
{
EditBufferCallbackList *l;
l = qe_malloc(EditBufferCallbackList);
2004-05-29 10:17:37 +00:00
if (!l)
return -1;
l->callback = cb;
l->opaque = opaque;
l->next = b->first_callback;
b->first_callback = l;
return 0;
}
2007-12-20 20:39:18 +00:00
void eb_free_callback(EditBuffer *b, EditBufferCallback cb, void *opaque)
2004-05-29 10:17:37 +00:00
{
EditBufferCallbackList **pl, *l;
2005-05-11 14:35:54 +00:00
for (pl = &b->first_callback; (*pl) != NULL; pl = &(*pl)->next) {
2004-05-29 10:17:37 +00:00
l = *pl;
if (l->callback == cb && l->opaque == opaque) {
*pl = l->next;
qe_free(&l);
2004-05-29 10:17:37 +00:00
break;
}
}
}
/* standard callback to move offsets */
void eb_offset_callback(__unused__ EditBuffer *b, void *opaque,
enum LogOperation op, int offset, int size)
2004-05-29 10:17:37 +00:00
{
int *offset_ptr = opaque;
2005-05-11 14:35:54 +00:00
switch (op) {
2004-05-29 10:17:37 +00:00
case LOGOP_INSERT:
if (*offset_ptr > offset)
*offset_ptr += size;
break;
case LOGOP_DELETE:
if (*offset_ptr > offset) {
*offset_ptr -= size;
if (*offset_ptr < offset)
*offset_ptr = offset;
}
break;
default:
break;
}
}
/************************************************************/
/* undo buffer */
static void eb_addlog(EditBuffer *b, enum LogOperation op,
int offset, int size)
{
int was_modified, len, size_trailer;
LogBuffer lb;
EditBufferCallbackList *l;
/* call each callback */
2005-05-11 14:35:54 +00:00
for (l = b->first_callback; l != NULL; l = l->next) {
2004-05-29 10:17:37 +00:00
l->callback(b, l->opaque, op, offset, size);
}
was_modified = b->modified;
b->modified = 1;
if (!b->save_log)
return;
if (!b->log_buffer) {
char buf[MAX_BUFFERNAME_SIZE];
2004-05-29 10:17:37 +00:00
snprintf(buf, sizeof(buf), "*log <%s>*", b->name);
b->log_buffer = eb_new(buf, BF_SYSTEM);
if (!b->log_buffer)
return;
}
/* XXX: better test to limit size */
if (b->nb_logs >= (NB_LOGS_MAX-1)) {
/* no free space, delete least recent entry */
2007-12-15 16:33:20 +00:00
eb_read(b->log_buffer, 0, &lb, sizeof(LogBuffer));
2004-05-29 10:17:37 +00:00
len = lb.size;
if (lb.op == LOGOP_INSERT)
len = 0;
len += sizeof(LogBuffer) + sizeof(int);
eb_delete(b->log_buffer, 0, len);
b->log_new_index -= len;
if (b->log_current > 1)
b->log_current -= len;
b->nb_logs--;
}
/* header */
2007-12-20 20:39:18 +00:00
//lb.pad1 = '\n'; /* make log buffer display readable */
//lb.pad2 = ':';
2004-05-29 10:17:37 +00:00
lb.op = op;
lb.offset = offset;
lb.size = size;
lb.was_modified = was_modified;
eb_write(b->log_buffer, b->log_new_index, &lb, sizeof(LogBuffer));
2004-05-29 10:17:37 +00:00
b->log_new_index += sizeof(LogBuffer);
/* data */
2005-05-11 14:35:54 +00:00
switch (op) {
2004-05-29 10:17:37 +00:00
case LOGOP_DELETE:
case LOGOP_WRITE:
eb_insert_buffer(b->log_buffer, b->log_new_index, b, offset, size);
b->log_new_index += size;
size_trailer = size;
break;
default:
size_trailer = 0;
break;
}
/* trailer */
eb_write(b->log_buffer, b->log_new_index, &size_trailer, sizeof(int));
2004-05-29 10:17:37 +00:00
b->log_new_index += sizeof(int);
b->nb_logs++;
}
void do_undo(EditState *s)
{
EditBuffer *b = s->b;
int log_index, saved, size_trailer;
LogBuffer lb;
if (!b->log_buffer)
return;
if (s->qe_state->last_cmd_func != do_undo)
b->log_current = 0;
if (b->log_current == 0) {
log_index = b->log_new_index;
} else {
log_index = b->log_current - 1;
}
if (log_index == 0) {
2006-12-09 19:02:07 +00:00
put_status(s, "No further undo information");
2004-05-29 10:17:37 +00:00
return;
} else {
put_status(s, "Undo!");
}
/* go backward */
log_index -= sizeof(int);
2007-12-15 16:33:20 +00:00
eb_read(b->log_buffer, log_index, &size_trailer, sizeof(int));
2004-05-29 10:17:37 +00:00
log_index -= size_trailer + sizeof(LogBuffer);
/* log_current is 1 + index to have zero as default value */
b->log_current = log_index + 1;
/* play the log entry */
2007-12-15 16:33:20 +00:00
eb_read(b->log_buffer, log_index, &lb, sizeof(LogBuffer));
2004-05-29 10:17:37 +00:00
log_index += sizeof(LogBuffer);
2005-05-11 14:35:54 +00:00
switch (lb.op) {
2004-05-29 10:17:37 +00:00
case LOGOP_WRITE:
/* we must disable the log because we want to record a single
write (we should have the single operation: eb_write_buffer) */
saved = b->save_log;
b->save_log = 0;
eb_delete(b, lb.offset, lb.size);
eb_insert_buffer(b, lb.offset, b->log_buffer, log_index, lb.size);
b->save_log = saved;
eb_addlog(b, LOGOP_WRITE, lb.offset, lb.size);
s->offset = lb.offset + lb.size;
break;
case LOGOP_DELETE:
/* we must also disable the log there because the log buffer
would be modified BEFORE we insert it by the implicit
eb_addlog */
saved = b->save_log;
b->save_log = 0;
eb_insert_buffer(b, lb.offset, b->log_buffer, log_index, lb.size);
b->save_log = saved;
eb_addlog(b, LOGOP_INSERT, lb.offset, lb.size);
s->offset = lb.offset + lb.size;
break;
case LOGOP_INSERT:
eb_delete(b, lb.offset, lb.size);
s->offset = lb.offset;
break;
default:
abort();
}
b->modified = lb.was_modified;
}
/************************************************************/
/* line related functions */
void eb_set_charset(EditBuffer *b, QECharset *charset)
{
if (b->charset) {
charset_decode_close(&b->charset_state);
}
b->charset = charset;
charset_decode_init(&b->charset_state, charset);
}
/* XXX: change API to go faster */
int eb_nextc(EditBuffer *b, int offset, int *next_ptr)
{
u8 buf[MAX_CHAR_BYTES], *p;
int ch;
if (offset >= b->total_size) {
offset = b->total_size;
ch = '\n';
} else {
eb_read(b, offset, buf, 1);
2007-12-20 20:39:18 +00:00
2004-05-29 10:17:37 +00:00
/* we use directly the charset conversion table to go faster */
ch = b->charset_state.table[buf[0]];
offset++;
if (ch == ESCAPE_CHAR) {
eb_read(b, offset, buf + 1, MAX_CHAR_BYTES - 1);
p = buf;
ch = b->charset_state.decode_func(&b->charset_state,
(const u8 **)&p);
offset += (p - buf) - 1;
}
}
if (next_ptr)
*next_ptr = offset;
return ch;
}
/* XXX: only UTF8 charset is supported */
/* XXX: suppress that */
int eb_prevc(EditBuffer *b, int offset, int *prev_ptr)
{
int ch;
u8 buf[MAX_CHAR_BYTES], *q;
if (offset <= 0) {
offset = 0;
ch = '\n';
} else {
/* XXX: it cannot be generic here. Should use the
line/column system to be really generic */
offset--;
q = buf + sizeof(buf) - 1;
eb_read(b, offset, q, 1);
if (b->charset == &charset_utf8) {
while (*q >= 0x80 && *q < 0xc0) {
if (offset == 0 || q == buf) {
2007-12-15 16:33:20 +00:00
/* error: take only previous char */
2004-05-29 10:17:37 +00:00
offset += buf - 1 - q;
ch = buf[sizeof(buf) - 1];
goto the_end;
}
offset--;
q--;
eb_read(b, offset, q, 1);
}
2005-05-04 19:30:10 +00:00
ch = utf8_decode((const char **)(void *)&q);
2004-05-29 10:17:37 +00:00
} else {
ch = *q;
}
}
the_end:
if (prev_ptr)
*prev_ptr = offset;
return ch;
}
/* return the number of lines and column position for a buffer */
2007-12-20 20:39:18 +00:00
static void get_pos(u8 *buf, int size, int *line_ptr, int *col_ptr,
2004-05-29 10:17:37 +00:00
CharsetDecodeState *s)
{
u8 *p, *p1, *lp;
int line, len, col, ch;
QASSERT(size >= 0);
2004-05-29 10:17:37 +00:00
line = 0;
p = buf;
lp = p;
p1 = p + size;
2005-05-11 14:35:54 +00:00
for (;;) {
2004-05-29 10:17:37 +00:00
p = memchr(p, '\n', p1 - p);
if (!p)
break;
p++;
lp = p;
line++;
}
/* now compute number of chars (XXX: potential problem if out of
* block, but for UTF8 it works) */
2004-05-29 10:17:37 +00:00
col = 0;
while (lp < p1) {
ch = s->table[*lp];
if (ch == ESCAPE_CHAR) {
/* XXX: utf8 only is handled */
len = utf8_length[*lp];
lp += len;
} else {
lp++;
}
col++;
}
*line_ptr = line;
*col_ptr = col;
}
int eb_goto_pos(EditBuffer *b, int line1, int col1)
{
Page *p, *p_end;
int line2, col2, line, col, offset, offset1;
u8 *q, *q_end;
line = 0;
col = 0;
offset = 0;
p = b->page_table;
p_end = b->page_table + b->nb_pages;
while (p < p_end) {
if (!(p->flags & PG_VALID_POS)) {
p->flags |= PG_VALID_POS;
2007-12-20 20:39:18 +00:00
get_pos(p->data, p->size, &p->nb_lines, &p->col,
2004-05-29 10:17:37 +00:00
&b->charset_state);
}
line2 = line + p->nb_lines;
if (p->nb_lines)
col2 = 0;
col2 = col + p->col;
if (line2 > line1 || (line2 == line1 && col2 >= col1)) {
/* compute offset */
q = p->data;
q_end = p->data + p->size;
/* seek to the correct line */
while (line < line1) {
col = 0;
q = memchr(q, '\n', q_end - q);
q++;
line++;
}
/* test if we want to go after the end of the line */
offset += q - p->data;
while (col < col1 && eb_nextc(b, offset, &offset1) != '\n') {
col++;
offset = offset1;
}
return offset;
}
line = line2;
col = col2;
offset += p->size;
p++;
}
return b->total_size;
}
int eb_get_pos(EditBuffer *b, int *line_ptr, int *col_ptr, int offset)
{
Page *p, *p_end;
int line, col, line1, col1;
QASSERT(offset >= 0);
2004-05-29 10:17:37 +00:00
line = 0;
col = 0;
p = b->page_table;
p_end = p + b->nb_pages;
2005-05-11 14:35:54 +00:00
for (;;) {
2004-05-29 10:17:37 +00:00
if (p >= p_end)
goto the_end;
if (offset < p->size)
break;
if (!(p->flags & PG_VALID_POS)) {
p->flags |= PG_VALID_POS;
get_pos(p->data, p->size, &p->nb_lines, &p->col,
&b->charset_state);
}
line += p->nb_lines;
if (p->nb_lines)
col = 0;
col += p->col;
offset -= p->size;
p++;
}
2005-05-11 14:35:54 +00:00
get_pos(p->data, offset, &line1, &col1, &b->charset_state);
2004-05-29 10:17:37 +00:00
line += line1;
if (line1)
col = 0;
col += col1;
the_end:
*line_ptr = line;
*col_ptr = col;
return line;
}
/************************************************************/
/* char offset computation */
static int get_chars(u8 *buf, int size, QECharset *charset)
{
int nb_chars, c;
u8 *buf_end, *buf_ptr;
if (charset != &charset_utf8)
return size;
nb_chars = 0;
buf_ptr = buf;
buf_end = buf + size;
while (buf_ptr < buf_end) {
c = *buf_ptr++;
if (c < 0x80 || c >= 0xc0)
nb_chars++;
}
return nb_chars;
}
static int goto_char(u8 *buf, int pos, QECharset *charset)
{
int nb_chars, c;
u8 *buf_ptr;
if (charset != &charset_utf8)
return pos;
nb_chars = 0;
buf_ptr = buf;
2005-05-11 14:35:54 +00:00
for (;;) {
2004-05-29 10:17:37 +00:00
c = *buf_ptr;
if (c < 0x80 || c >= 0xc0) {
if (nb_chars >= pos)
break;
nb_chars++;
}
buf_ptr++;
}
return buf_ptr - buf;
}
/* gives the byte offset of a given character, taking the charset into
account */
int eb_goto_char(EditBuffer *b, int pos)
{
int offset;
Page *p, *p_end;
if (b->charset != &charset_utf8) {
offset = pos;
if (offset > b->total_size)
offset = b->total_size;
} else {
offset = 0;
p = b->page_table;
p_end = b->page_table + b->nb_pages;
while (p < p_end) {
if (!(p->flags & PG_VALID_CHAR)) {
p->flags |= PG_VALID_CHAR;
p->nb_chars = get_chars(p->data, p->size, b->charset);
}
if (pos < p->nb_chars) {
offset += goto_char(p->data, pos, b->charset);
break;
} else {
pos -= p->nb_chars;
offset += p->size;
p++;
}
}
}
return offset;
}
/* get the char offset corresponding to a given byte offset, taking
the charset into account */
int eb_get_char_offset(EditBuffer *b, int offset)
{
int pos;
Page *p, *p_end;
/* if no decoding function in charset, it means it is 8 bit only */
if (b->charset_state.decode_func == NULL) {
pos = offset;
if (pos > b->total_size)
pos = b->total_size;
} else {
p = b->page_table;
p_end = p + b->nb_pages;
pos = 0;
2005-05-11 14:35:54 +00:00
for (;;) {
2004-05-29 10:17:37 +00:00
if (p >= p_end)
goto the_end;
if (offset < p->size)
break;
if (!(p->flags & PG_VALID_CHAR)) {
p->nb_chars = get_chars(p->data, p->size, b->charset);
p->flags |= PG_VALID_CHAR;
}
pos += p->nb_chars;
offset -= p->size;
p++;
}
pos += get_chars(p->data, offset, b->charset);
2007-12-20 20:39:18 +00:00
/* Should adjust if offset falls in the middle of a character */
// {
// int c = p->data[offset];
// if (c >= 0x80 && c < 0xc0)
// pos--;
//}
2004-05-29 10:17:37 +00:00
the_end: ;
}
return pos;
}
/* delete a range of bytes from the buffer, bounds in any order, return
* lower bound.
*/
int eb_delete_range(EditBuffer *b, int p1, int p2)
{
if (p1 > p2) {
2007-12-20 20:39:18 +00:00
int tmp = p1;
p1 = p2;
p2 = tmp;
}
eb_delete(b, p1, p2 - p1);
return p1;
}
/* replace 'size' bytes at offset 'offset' with 'size1' bytes from 'buf' */
void eb_replace(EditBuffer *b, int offset, int size,
const void *buf, int size1)
{
/* CG: behaviour is not exactly identical: mark, point and other
* callback based offsets will be updated differently. should
* write portion that fits and insert or delete remainder?
*/
if (size == size1) {
2007-12-20 20:39:18 +00:00
eb_write(b, offset, buf, size1);
} else {
2007-12-20 20:39:18 +00:00
eb_delete(b, offset, size);
eb_insert(b, offset, buf, size1);
}
}
2004-05-29 10:17:37 +00:00
/************************************************************/
/* buffer I/O */
#define IOBUF_SIZE 32768
#if 0
typedef struct BufferIOState {
URLContext *handle;
void (*progress_cb)(void *opaque, int size);
void (*completion_cb)(void *opaque, int err);
void *opaque;
int offset;
int saved_flags;
int saved_log;
int nolog;
unsigned char buffer[IOBUF_SIZE];
} BufferIOState;
static void load_connected_cb(void *opaque, int err);
static void load_read_cb(void *opaque, int size);
static void eb_io_stop(EditBuffer *b, int err);
/* load a buffer asynchronously and launch the callback. The buffer
stays in 'loading' state while begin loaded. It is also marked
readonly. */
int load_buffer(EditBuffer *b, const char *filename,
int offset, int nolog,
void (*progress_cb)(void *opaque, int size),
void (*completion_cb)(void *opaque, int err), void *opaque)
{
URLContext *h;
BufferIOState *s;
/* cannot load a buffer if already I/Os or readonly */
if (b->flags & (BF_LOADING | BF_SAVING | BF_READONLY))
return -1;
s = qe_malloc(BufferIOState);
2004-05-29 10:17:37 +00:00
if (!s)
return -1;
b->io_state = s;
h = url_new();
if (!h) {
qe_free(&b->io_state);
2004-05-29 10:17:37 +00:00
return -1;
}
s->handle = h;
s->saved_flags = b->flags;
s->nolog = nolog;
if (s->nolog) {
s->saved_log = b->save_log;
b->save_log = 0;
}
b->flags |= BF_LOADING | BF_READONLY;
s->handle = h;
s->progress_cb = progress_cb;
s->completion_cb = completion_cb;
s->opaque = opaque;
s->offset = offset;
printf("connect_async: '%s'\n", filename);
url_connect_async(s->handle, filename, URL_RDONLY,
load_connected_cb, b);
return 0;
}
static void load_connected_cb(void *opaque, int err)
{
EditBuffer *b = opaque;
BufferIOState *s = b->io_state;
printf("connect_cb: err=%d\n", err);
if (err) {
eb_io_stop(b, err);
return;
}
url_read_async(s->handle, s->buffer, IOBUF_SIZE, load_read_cb, b);
}
static void load_read_cb(void *opaque, int size)
{
EditBuffer *b = opaque;
BufferIOState *s = b->io_state;
printf("read_cb: size=%d\n", size);
if (size < 0) {
eb_io_stop(b, -EIO);
} else if (size == 0) {
/* end of file */
eb_io_stop(b, 0);
} else {
eb_insert(b, s->offset, s->buffer, size);
s->offset += size;
/* launch next read request */
url_read_async(s->handle, s->buffer, IOBUF_SIZE, load_read_cb, b);
}
}
static void eb_io_stop(EditBuffer *b, int err)
{
BufferIOState *s = b->io_state;
b->flags = s->saved_flags;
if (s->nolog) {
b->modified = 0;
b->save_log = s->saved_log;
}
url_close(s->handle);
s->completion_cb(s->opaque, err);
qe_free(&b->io_state);
2004-05-29 10:17:37 +00:00
}
#endif
int raw_load_buffer1(EditBuffer *b, FILE *f, int offset)
{
int len;
unsigned char buf[IOBUF_SIZE];
2005-05-11 14:35:54 +00:00
for (;;) {
2004-05-29 10:17:37 +00:00
len = fread(buf, 1, IOBUF_SIZE, f);
if (len < 0)
return -1;
if (len == 0)
break;
eb_insert(b, offset, buf, len);
offset += len;
}
return 0;
}
#ifdef WIN32
/* currently no mmap is used */
int mmap_buffer(EditBuffer *b, const char *filename)
{
return -1;
}
#else
int mmap_buffer(EditBuffer *b, const char *filename)
{
int fd, len, file_size, n, size;
u8 *file_ptr, *ptr;
Page *p;
fd = open(filename, O_RDONLY);
if (fd < 0)
return -1;
file_size = lseek(fd, 0, SEEK_END);
file_ptr = mmap(NULL, file_size, PROT_READ, MAP_SHARED, fd, 0);
if ((void*)file_ptr == MAP_FAILED) {
2004-05-29 10:17:37 +00:00
close(fd);
return -1;
}
n = (file_size + MAX_PAGE_SIZE - 1) / MAX_PAGE_SIZE;
p = qe_malloc_array(Page, n);
2004-05-29 10:17:37 +00:00
if (!p) {
close(fd);
return -1;
}
b->page_table = p;
b->total_size = file_size;
b->nb_pages = n;
size = file_size;
ptr = file_ptr;
while (size > 0) {
len = size;
if (len > MAX_PAGE_SIZE)
len = MAX_PAGE_SIZE;
p->data = ptr;
p->size = len;
p->flags = PG_READ_ONLY;
ptr += len;
size -= len;
p++;
}
b->file_handle = fd;
return 0;
}
#endif
static int raw_load_buffer(EditBuffer *b, FILE *f)
{
int ret;
struct stat st;
if (stat(b->filename, &st) == 0 &&
st.st_size >= MIN_MMAP_SIZE) {
ret = mmap_buffer(b, b->filename);
} else {
ret = raw_load_buffer1(b, f, 0);
}
return ret;
}
/* Write bytes between <start> and <end> to file filename,
* return bytes written or -1 if error
*/
static int raw_save_buffer(EditBuffer *b, int start, int end,
const char *filename)
2004-05-29 10:17:37 +00:00
{
int fd, len, size, written;
2004-05-29 10:17:37 +00:00
unsigned char buf[IOBUF_SIZE];
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (fd < 0)
return -1;
if (end < start) {
int tmp = start;
start = end;
end = tmp;
}
if (start < 0)
start = 0;
if (end > b->total_size)
end = b->total_size;
written = 0;
size = end - start;
2004-05-29 10:17:37 +00:00
while (size > 0) {
len = size;
if (len > IOBUF_SIZE)
len = IOBUF_SIZE;
eb_read(b, start, buf, len);
2004-05-29 10:17:37 +00:00
len = write(fd, buf, len);
if (len < 0) {
close(fd);
return -1;
}
written += len;
start += len;
2004-05-29 10:17:37 +00:00
size -= len;
}
close(fd);
return written;
2004-05-29 10:17:37 +00:00
}
static void raw_close_buffer(__unused__ EditBuffer *b)
2004-05-29 10:17:37 +00:00
{
/* nothing to do */
}
/* Associate a buffer with a file and rename it to match the
filename. Find a unique buffer name */
2007-12-26 11:09:34 +00:00
// should rename to eb_set_filename?
2004-05-29 10:17:37 +00:00
void set_filename(EditBuffer *b, const char *filename)
{
pstrcpy(b->filename, sizeof(b->filename), filename);
2007-12-26 11:09:34 +00:00
set_buffer_name(b, basename(filename));
2004-05-29 10:17:37 +00:00
}
int eb_printf(EditBuffer *b, const char *fmt, ...)
2004-05-29 10:17:37 +00:00
{
char buf0[1024];
char *buf;
int len, size;
2004-05-29 10:17:37 +00:00
va_list ap;
va_start(ap, fmt);
size = sizeof(buf0);
buf = buf0;
len = vsnprintf(buf, size, fmt, ap);
2004-05-29 10:17:37 +00:00
va_end(ap);
if (len >= size) {
va_start(ap, fmt);
size = len + 1;
buf = alloca(size);
vsnprintf(buf, size, fmt, ap);
va_end(ap);
}
2004-05-29 10:17:37 +00:00
eb_insert(b, b->total_size, buf, len);
return len;
2004-05-29 10:17:37 +00:00
}
/* pad current line with spaces so that it reaches column n */
void eb_line_pad(EditBuffer *b, int n)
{
int offset, i;
i = 0;
offset = b->total_size;
2005-05-11 14:35:54 +00:00
for (;;) {
2004-05-29 10:17:37 +00:00
if (eb_prevc(b, offset, &offset) == '\n')
break;
i++;
}
while (i < n) {
eb_printf(b, " ");
i++;
}
}
int eb_get_contents(EditBuffer *b, char *buf, int buf_size)
2004-05-29 10:17:37 +00:00
{
int len;
len = b->total_size;
if (len > buf_size - 1)
len = buf_size - 1;
eb_read(b, 0, buf, len);
buf[len] = '\0';
return len;
}
/* get the line starting at offset 'offset' as an array of code points */
/* offset is bumped to the beginning of the next line */
/* returns the number of code points stored in buf, excluding '\0' */
/* buf_size must be > 0 */
/* XXX: cannot detect truncation */
2004-05-29 10:17:37 +00:00
int eb_get_line(EditBuffer *b, unsigned int *buf, int buf_size,
int *offset_ptr)
{
unsigned int *buf_ptr, *buf_end;
int c, offset;
2004-05-29 10:17:37 +00:00
offset = *offset_ptr;
buf_ptr = buf;
buf_end = buf + buf_size - 1;
2005-05-11 14:35:54 +00:00
for (;;) {
2004-05-29 10:17:37 +00:00
c = eb_nextc(b, offset, &offset);
if (c == '\n')
break;
if (buf_ptr < buf_end)
*buf_ptr++ = c;
}
*buf_ptr = '\0';
2004-05-29 10:17:37 +00:00
*offset_ptr = offset;
return buf_ptr - buf;
}
/* get the line starting at offset 'offset' encoded in utf-8 */
/* offset is bumped to the beginning of the next line */
/* returns the number of bytes stored in buf, excluding '\0' */
/* buf_size must be > 0 */
/* XXX: cannot detect truncation */
2004-05-29 10:17:37 +00:00
int eb_get_strline(EditBuffer *b, char *buf, int buf_size,
int *offset_ptr)
{
char utf8_buf[6];
2004-05-29 10:17:37 +00:00
char *buf_ptr, *buf_end;
int c, offset, len;
2004-05-29 10:17:37 +00:00
offset = *offset_ptr;
buf_ptr = buf;
buf_end = buf + buf_size - 1;
2005-05-11 14:35:54 +00:00
for (;;) {
2004-05-29 10:17:37 +00:00
c = eb_nextc(b, offset, &offset);
if (c == '\n')
break;
if (c < 0x80) {
if (buf_ptr < buf_end) {
*buf_ptr++ = c;
continue;
}
} else {
len = utf8_encode(utf8_buf, c);
if (buf_ptr + len <= buf_end) {
memcpy(buf_ptr, utf8_buf, len);
buf_ptr += len;
continue;
}
}
/* overflow: skip past '\n' */
offset = eb_next_line(b, offset);
break;
2004-05-29 10:17:37 +00:00
}
*buf_ptr = '\0';
*offset_ptr = offset;
return buf_ptr - buf;
}
int eb_prev_line(EditBuffer *b, int offset)
{
int offset1, seen_nl;
for (seen_nl = 0;;) {
if (eb_prevc(b, offset, &offset1) == '\n') {
if (seen_nl++)
break;
}
offset = offset1;
}
return offset;
}
/* return offset of the beginning of the line containing offset */
2004-05-29 10:17:37 +00:00
int eb_goto_bol(EditBuffer *b, int offset)
{
int offset1;
2004-05-29 10:17:37 +00:00
2005-05-11 14:35:54 +00:00
for (;;) {
if (eb_prevc(b, offset, &offset1) == '\n')
2004-05-29 10:17:37 +00:00
break;
offset = offset1;
}
return offset;
}
/* move to the beginning of the line containing offset */
/* return offset of the beginning of the line containing offset */
/* store count of characters skipped at *countp */
int eb_goto_bol2(EditBuffer *b, int offset, int *countp)
{
int count, offset1;
for (count = 0;; count++) {
if (eb_prevc(b, offset, &offset1) == '\n')
break;
offset = offset1;
}
*countp = count;
return offset;
}
2004-05-29 10:17:37 +00:00
int eb_is_empty_line(EditBuffer *b, int offset)
{
int c;
2005-05-11 14:35:54 +00:00
for (;;) {
2004-05-29 10:17:37 +00:00
c = eb_nextc(b, offset, &offset);
if (c == '\n')
return 1;
if (!qe_isspace(c))
2004-05-29 10:17:37 +00:00
break;
}
return 0;
}
/* return offset of the end of the line containing offset */
int eb_goto_eol(EditBuffer *b, int offset)
{
int c, offset1;
for (;;) {
c = eb_nextc(b, offset, &offset1);
if (c == '\n')
break;
offset = offset1;
}
return offset;
}
2004-05-29 10:17:37 +00:00
int eb_next_line(EditBuffer *b, int offset)
{
int c;
2005-05-11 14:35:54 +00:00
for (;;) {
2004-05-29 10:17:37 +00:00
c = eb_nextc(b, offset, &offset);
if (c == '\n')
break;
}
return offset;
}
/* buffer data type handling */
void eb_register_data_type(EditBufferDataType *bdt)
{
EditBufferDataType **lp;
lp = &first_buffer_data_type;
while (*lp != NULL)
lp = &(*lp)->next;
bdt->next = NULL;
*lp = bdt;
}
/* Write buffer contents between <start> and <end> to file <filename>,
* return bytes written or -1 if error
2004-05-29 10:17:37 +00:00
*/
int eb_write_buffer(EditBuffer *b, int start, int end, const char *filename)
{
if (!b->data_type->buffer_save)
return -1;
return b->data_type->buffer_save(b, start, end, filename);
}
/* Save buffer contents to buffer associated file, handle backups,
* return bytes written or -1 if error
*/
int eb_save_buffer(EditBuffer *b)
2004-05-29 10:17:37 +00:00
{
int ret, mode;
char buf1[MAX_FILENAME_SIZE];
const char *filename;
struct stat st;
if (!b->data_type->buffer_save)
return -1;
filename = b->filename;
/* get old file permission */
mode = 0644;
if (stat(filename, &st) == 0)
mode = st.st_mode & 0777;
/* backup old file if present */
2007-12-26 09:49:52 +00:00
if (strlen(filename) < MAX_FILENAME_SIZE - 1) {
strcpy(buf1, filename);
strcat(buf1, "~");
// should check error code
rename(filename, buf1);
}
2004-05-29 10:17:37 +00:00
ret = b->data_type->buffer_save(b, 0, b->total_size, filename);
2004-05-29 10:17:37 +00:00
if (ret < 0)
return ret;
#ifndef WIN32
/* set correct file mode to old file permissions */
chmod(filename, mode);
#endif
/* reset log */
2006-12-09 19:02:07 +00:00
/* CG: should not do this! */
//log_reset(b);
2004-05-29 10:17:37 +00:00
b->modified = 0;
return ret;
2004-05-29 10:17:37 +00:00
}
/* invalidate buffer raw data */
void eb_invalidate_raw_data(EditBuffer *b)
{
b->save_log = 0;
eb_delete(b, 0, b->total_size);
log_reset(b);
}
EditBufferDataType raw_data_type = {
"raw",
raw_load_buffer,
raw_save_buffer,
raw_close_buffer,
NULL, /* next */
2004-05-29 10:17:37 +00:00
};
/* init buffer handling */
void eb_init(void)
{
eb_register_data_type(&raw_data_type);
}