+++ /dev/null
-/*
- textbuffer.c : Text buffer handling
-
- Copyright (C) 1999-2001 Timo Sirainen
-
- This program 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 of the License, or
- (at your option) any later version.
-
- This program 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 program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-*/
-
-#define G_LOG_DOMAIN "TextBuffer"
-
-#include "module.h"
-#include "misc.h"
-#include "formats.h"
-
-#include "textbuffer.h"
-
-#ifdef HAVE_REGEX_H
-# include <regex.h>
-#endif
-
-#define TEXT_CHUNK_USABLE_SIZE (LINE_TEXT_CHUNK_SIZE-2-(int)sizeof(char*))
-
-static GMemChunk *buffer_chunk, *line_chunk, *text_chunk;
-
-TEXT_BUFFER_REC *textbuffer_create(void)
-{
- TEXT_BUFFER_REC *buffer;
-
- buffer = g_mem_chunk_alloc0(buffer_chunk);
- buffer->last_eol = TRUE;
- return buffer;
-}
-
-void textbuffer_destroy(TEXT_BUFFER_REC *buffer)
-{
- g_return_if_fail(buffer != NULL);
-
- textbuffer_remove_all_lines(buffer);
- g_mem_chunk_free(buffer_chunk, buffer);
-}
-
-static TEXT_CHUNK_REC *text_chunk_find(TEXT_BUFFER_REC *buffer,
- const unsigned char *data)
-{
- GSList *tmp;
-
- for (tmp = buffer->text_chunks; tmp != NULL; tmp = tmp->next) {
- TEXT_CHUNK_REC *rec = tmp->data;
-
- if (data >= rec->buffer &&
- data < rec->buffer+sizeof(rec->buffer))
- return rec;
- }
-
- return NULL;
-}
-
-#define mark_temp_eol(chunk) G_STMT_START { \
- (chunk)->buffer[(chunk)->pos] = 0; \
- (chunk)->buffer[(chunk)->pos+1] = LINE_CMD_EOL; \
- } G_STMT_END
-
-static TEXT_CHUNK_REC *text_chunk_create(TEXT_BUFFER_REC *buffer)
-{
- TEXT_CHUNK_REC *rec;
- unsigned char *buf, *ptr, **pptr;
-
- rec = g_mem_chunk_alloc(text_chunk);
- rec->pos = 0;
- rec->refcount = 0;
-
- if (buffer->cur_line != NULL && buffer->cur_line->text != NULL) {
- /* create a link to new block from the old block */
- buf = buffer->cur_text->buffer + buffer->cur_text->pos;
- *buf++ = 0; *buf++ = (char) LINE_CMD_CONTINUE;
-
- /* we want to store pointer to beginning of the new text
- block to char* buffer. this probably isn't ANSI-C
- compatible, and trying this without the pptr variable
- breaks at least NetBSD/Alpha, so don't go "optimize"
- it :) */
- ptr = rec->buffer; pptr = &ptr;
- memcpy(buf, pptr, sizeof(unsigned char *));
- } else {
- /* just to be safe */
- mark_temp_eol(rec);
- }
-
- buffer->cur_text = rec;
- buffer->text_chunks = g_slist_append(buffer->text_chunks, rec);
- return rec;
-}
-
-static void text_chunk_destroy(TEXT_BUFFER_REC *buffer, TEXT_CHUNK_REC *chunk)
-{
- buffer->text_chunks = g_slist_remove(buffer->text_chunks, chunk);
- g_mem_chunk_free(text_chunk, chunk);
-}
-
-static void text_chunk_line_free(TEXT_BUFFER_REC *buffer, LINE_REC *line)
-{
- TEXT_CHUNK_REC *chunk;
- const unsigned char *text;
- unsigned char cmd, *tmp = NULL;
-
- for (text = line->text;; text++) {
- if (*text != '\0')
- continue;
-
- text++;
- cmd = *text;
- if (cmd == LINE_CMD_CONTINUE || cmd == LINE_CMD_EOL) {
- if (cmd == LINE_CMD_CONTINUE)
- memcpy(&tmp, text+1, sizeof(char *));
-
- /* free the previous block */
- chunk = text_chunk_find(buffer, text);
- if (--chunk->refcount == 0) {
- if (buffer->cur_text == chunk)
- chunk->pos = 0;
- else
- text_chunk_destroy(buffer, chunk);
- }
-
- if (cmd == LINE_CMD_EOL)
- break;
-
- text = tmp-1;
- } else if (cmd == LINE_CMD_INDENT_FUNC) {
- text += sizeof(int (*) ());
- }
- }
-}
-
-static void text_chunk_append(TEXT_BUFFER_REC *buffer,
- const unsigned char *data, int len)
-{
- TEXT_CHUNK_REC *chunk;
- int left;
-
- if (len == 0)
- return;
-
- chunk = buffer->cur_text;
- while (chunk->pos + len >= TEXT_CHUNK_USABLE_SIZE) {
- left = TEXT_CHUNK_USABLE_SIZE - chunk->pos;
- if (left > 0 && data[left-1] == 0)
- left--; /* don't split the commands */
-
- memcpy(chunk->buffer + chunk->pos, data, left);
- chunk->pos += left;
-
- chunk = text_chunk_create(buffer);
- chunk->refcount++;
- len -= left; data += left;
- }
-
- memcpy(chunk->buffer + chunk->pos, data, len);
- chunk->pos += len;
-
- mark_temp_eol(chunk);
-}
-
-static LINE_REC *textbuffer_line_create(TEXT_BUFFER_REC *buffer)
-{
- LINE_REC *rec;
-
- if (buffer->cur_text == NULL)
- text_chunk_create(buffer);
-
- rec = g_mem_chunk_alloc(line_chunk);
- rec->refcount = 1;
- rec->text = buffer->cur_text->buffer + buffer->cur_text->pos;
-
- buffer->cur_text->refcount++;
- return rec;
-}
-
-static LINE_REC *textbuffer_line_insert(TEXT_BUFFER_REC *buffer,
- LINE_REC *prev)
-{
- LINE_REC *line;
-
- line = textbuffer_line_create(buffer);
- line->prev = prev;
- if (prev == NULL) {
- line->next = buffer->first_line;
- if (buffer->first_line != NULL)
- buffer->first_line->prev = line;
- buffer->first_line = line;
- } else {
- line->next = prev->next;
- if (line->next != NULL)
- line->next->prev = line;
- prev->next = line;
- }
-
- if (prev == buffer->cur_line)
- buffer->cur_line = line;
- buffer->lines_count++;
-
- return line;
-}
-
-void textbuffer_line_ref(LINE_REC *line)
-{
- g_return_if_fail(line != NULL);
-
- if (++line->refcount == 255)
- g_error("line reference counter wrapped - shouldn't happen");
-}
-
-void textbuffer_line_unref(TEXT_BUFFER_REC *buffer, LINE_REC *line)
-{
- g_return_if_fail(buffer != NULL);
- g_return_if_fail(line != NULL);
-
- if (--line->refcount == 0) {
- text_chunk_line_free(buffer, line);
- g_mem_chunk_free(line_chunk, line);
- }
-}
-
-void textbuffer_line_unref_list(TEXT_BUFFER_REC *buffer, GList *list)
-{
- g_return_if_fail(buffer != NULL);
-
- while (list != NULL) {
- if (list->data != NULL)
- textbuffer_line_unref(buffer, list->data);
- list = list->next;
- }
-}
-
-LINE_REC *textbuffer_line_last(TEXT_BUFFER_REC *buffer)
-{
- LINE_REC *line;
-
- line = buffer->cur_line;
- if (line != NULL) {
- while (line->next != NULL)
- line = line->next;
- }
- return line;
-}
-
-int textbuffer_line_exists_after(LINE_REC *line, LINE_REC *search)
-{
- while (line != NULL) {
- if (line == search)
- return TRUE;
- line = line->next;
- }
- return FALSE;
-}
-
-LINE_REC *textbuffer_append(TEXT_BUFFER_REC *buffer,
- const unsigned char *data, int len,
- LINE_INFO_REC *info)
-{
- return textbuffer_insert(buffer, buffer->cur_line, data, len, info);
-}
-
-LINE_REC *textbuffer_insert(TEXT_BUFFER_REC *buffer, LINE_REC *insert_after,
- const unsigned char *data, int len,
- LINE_INFO_REC *info)
-{
- LINE_REC *line;
-
- g_return_val_if_fail(buffer != NULL, NULL);
- g_return_val_if_fail(data != NULL, NULL);
-
- if (len == 0)
- return insert_after;
-
- line = !buffer->last_eol ? insert_after :
- textbuffer_line_insert(buffer, insert_after);
-
- if (info != NULL)
- memcpy(&line->info, info, sizeof(line->info));
-
- text_chunk_append(buffer, data, len);
-
- buffer->last_eol = len >= 2 &&
- data[len-2] == 0 && data[len-1] == LINE_CMD_EOL;
-
- return line;
-}
-
-void textbuffer_remove(TEXT_BUFFER_REC *buffer, LINE_REC *line)
-{
- g_return_if_fail(buffer != NULL);
- g_return_if_fail(line != NULL);
-
- if (buffer->first_line == line)
- buffer->first_line = line->next;
- if (line->prev != NULL)
- line->prev->next = line->next;
- if (line->next != NULL)
- line->next->prev = line->prev;
-
- if (buffer->cur_line == line) {
- buffer->cur_line = line->next != NULL ?
- line->next : line->prev;
- }
-
- line->prev = line->next = NULL;
-
- buffer->lines_count--;
- textbuffer_line_unref(buffer, line);
-}
-
-/* Removes all lines from buffer, ignoring reference counters */
-void textbuffer_remove_all_lines(TEXT_BUFFER_REC *buffer)
-{
- GSList *tmp;
- LINE_REC *line;
-
- g_return_if_fail(buffer != NULL);
-
- for (tmp = buffer->text_chunks; tmp != NULL; tmp = tmp->next)
- g_mem_chunk_free(text_chunk, tmp->data);
- g_slist_free(buffer->text_chunks);
- buffer->text_chunks = NULL;
-
- while (buffer->first_line != NULL) {
- line = buffer->first_line->next;
- g_mem_chunk_free(line_chunk, buffer->first_line);
- buffer->first_line = line;
- }
- buffer->lines_count = 0;
-
- buffer->cur_line = NULL;
- buffer->cur_text = NULL;
-
- buffer->last_eol = TRUE;
-}
-
-static void set_color(GString *str, int cmd, int *last_fg, int *last_bg)
-{
- if (cmd & LINE_COLOR_DEFAULT) {
- g_string_sprintfa(str, "\004%c", FORMAT_STYLE_DEFAULTS);
-
- /* need to reset the fg/bg color */
- if (cmd & LINE_COLOR_BG) {
- *last_bg = -1;
- if (*last_fg != -1) {
- g_string_sprintfa(str, "\004%c%c",
- *last_fg,
- FORMAT_COLOR_NOCHANGE);
- }
- } else {
- *last_fg = -1;
- if (*last_bg != -1) {
- g_string_sprintfa(str, "\004%c%c",
- FORMAT_COLOR_NOCHANGE,
- *last_bg);
- }
- }
- return;
- }
-
- if ((cmd & LINE_COLOR_BG) == 0) {
- /* change foreground color */
- *last_fg = (cmd & 0x0f)+'0';
- g_string_sprintfa(str, "\004%c%c", *last_fg,
- FORMAT_COLOR_NOCHANGE);
- } else {
- /* change background color */
- *last_bg = (cmd & 0x0f)+'0';
- g_string_sprintfa(str, "\004%c%c",
- FORMAT_COLOR_NOCHANGE, *last_bg);
- }
-}
-
-void textbuffer_line2text(LINE_REC *line, int coloring, GString *str)
-{
- unsigned char cmd, *ptr, *tmp;
- int last_fg, last_bg;
-
- g_return_if_fail(line != NULL);
- g_return_if_fail(str != NULL);
-
- g_string_truncate(str, 0);
-
- last_fg = last_bg = -1;
- for (ptr = line->text;;) {
- if (*ptr != 0) {
- g_string_append_c(str, (char) *ptr);
- ptr++;
- continue;
- }
-
- ptr++;
- cmd = *ptr;
- ptr++;
-
- if (cmd == LINE_CMD_EOL || cmd == LINE_CMD_FORMAT) {
- /* end of line */
- break;
- }
-
- if (cmd == LINE_CMD_CONTINUE) {
- /* line continues in another address.. */
- memcpy(&tmp, ptr, sizeof(unsigned char *));
- ptr = tmp;
- continue;
- }
-
- if (!coloring) {
- /* no colors, skip coloring commands */
- continue;
- }
-
- if ((cmd & 0x80) == 0) {
- /* set color */
- set_color(str, cmd, &last_fg, &last_bg);
- } else switch (cmd) {
- case LINE_CMD_UNDERLINE:
- g_string_append_c(str, 31);
- break;
- case LINE_CMD_REVERSE:
- g_string_append_c(str, 22);
- break;
- case LINE_CMD_COLOR0:
- g_string_sprintfa(str, "\004%c%c",
- '0', FORMAT_COLOR_NOCHANGE);
- break;
- case LINE_CMD_INDENT:
- break;
- case LINE_CMD_INDENT_FUNC:
- ptr += sizeof(void *);
- break;
- }
- }
-}
-
-GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
- int level, int nolevel, const char *text,
- int before, int after,
- int regexp, int fullword, int case_sensitive)
-{
-#ifdef HAVE_REGEX_H
- regex_t preg;
-#endif
- LINE_REC *line, *pre_line;
- GList *matches;
- GString *str;
- int i, match_after, line_matched;
-
- g_return_val_if_fail(buffer != NULL, NULL);
- g_return_val_if_fail(text != NULL, NULL);
-
- if (regexp) {
-#ifdef HAVE_REGEX_H
- int flags = REG_EXTENDED | REG_NOSUB |
- (case_sensitive ? 0 : REG_ICASE);
- if (regcomp(&preg, text, flags) != 0)
- return NULL;
-#else
- return NULL;
-#endif
- }
-
- matches = NULL; match_after = 0;
- str = g_string_new(NULL);
-
- line = startline != NULL ? startline : buffer->first_line;
-
- for (; line != NULL; line = line->next) {
- if ((line->info.level & level) == 0 ||
- (line->info.level & nolevel) != 0)
- continue;
-
- if (*text == '\0') {
- /* no search word, everything matches */
- textbuffer_line_ref(line);
- matches = g_list_append(matches, line);
- continue;
- }
-
- textbuffer_line2text(line, FALSE, str);
-
- line_matched =
-#ifdef HAVE_REGEX_H
- regexp ? regexec(&preg, str->str, 0, NULL, 0) == 0 :
-#endif
- fullword ? strstr_full_case(str->str, text, !case_sensitive) != NULL :
- case_sensitive ? strstr(str->str, text) != NULL :
- stristr(str->str, text) != NULL;
- if (line_matched) {
- /* add the -before lines */
- pre_line = line;
- for (i = 0; i < before; i++) {
- if (pre_line->prev == NULL ||
- g_list_find(matches, pre_line->prev) != NULL)
- break;
- pre_line = pre_line->prev;
- }
-
- for (; pre_line != line; pre_line = pre_line->next) {
- textbuffer_line_ref(pre_line);
- matches = g_list_append(matches, pre_line);
- }
-
- match_after = after;
- }
-
- if (line_matched || match_after > 0) {
- /* matched */
- textbuffer_line_ref(line);
- matches = g_list_append(matches, line);
-
- if ((!line_matched && --match_after == 0) ||
- (line_matched && match_after == 0 && before > 0))
- matches = g_list_append(matches, NULL);
- }
- }
-#ifdef HAVE_REGEX_H
- if (regexp) regfree(&preg);
-#endif
- g_string_free(str, TRUE);
- return matches;
-}
-
-void textbuffer_init(void)
-{
- buffer_chunk = g_mem_chunk_new("text buffer chunk",
- sizeof(TEXT_BUFFER_REC),
- sizeof(TEXT_BUFFER_REC)*32, G_ALLOC_AND_FREE);
- line_chunk = g_mem_chunk_new("line chunk", sizeof(LINE_REC),
- sizeof(LINE_REC)*1024, G_ALLOC_AND_FREE);
- text_chunk = g_mem_chunk_new("text chunk", sizeof(TEXT_CHUNK_REC),
- sizeof(TEXT_CHUNK_REC)*32, G_ALLOC_AND_FREE);
-}
-
-void textbuffer_deinit(void)
-{
- g_mem_chunk_destroy(buffer_chunk);
- g_mem_chunk_destroy(line_chunk);
- g_mem_chunk_destroy(text_chunk);
-}