/* vi:set sw=4: sftextbuf.c Copyright (c) 2000 Minero Aoki This program is free software. You can distribute/modify this program under the terms of the GNU Lesser General Public License version 2 or later. */ /* byte string buffer gap | V ptr -> OoOoOoOo--------------OoOoOoOoOoOoOo |<- gaplen ->| |<------------ len --------------->| O : first byte of multi byte char o : rest byte - : gap gap never divide multi byte char or nl nl is /\n|\r\n|\r/ nl is one char number of first line is 1 byte/char index of buffer-mark DOES NOT include gap length */ #ifdef IS_TEXT_BUFFER typedef struct sf_textbuf buffer_t; typedef struct sf_textbufmark mark_t; typedef char char_t; #define defun(nm) sf_t ## nm #define BUFFER_FLAG TEXT_BUFFER #define LEN(buf) ((buf)->len - (buf)->gaplen) #define CLEN(buf) ((buf)->tail->cidx) #define IDX(mark) mark->idx #define CIDX(mark) mark->cidx #define C(code) code #define L(code) code #endif #ifdef IS_STRING_BUFFER typedef struct sf_strbuf buffer_t; typedef struct sf_strbufmark mark_t; typedef char char_t; #define defun(nm) sf_s ## nm #define BUFFER_FLAG STRING_BUFFER #define LEN(buf) ((buf)->len - (buf)->gaplen) #define CLEN(buf) LEN(buf) #define IDX(mark) mark->idx #define CIDX(mark) mark->idx #define C(code) #define L(code) #endif #ifdef IS_WCHAR_BUFFER typedef struct sf_wcharbuf buffer_t; typedef struct sf_wcharbufmark mark_t; typedef unsigned long char_t; #define defun(nm) sf_w ## nm #define BUFFER_FLAG WCHAR_BUFFER #define LEN(buf) ((buf)->len) #define CLEN(buf) ((buf)->len) #define IDX(mark) ((mark)->idx) #define CIDX(mark) ((mark)->idx) #define C(code) #define L(code) code #endif static int dodebug = 0; /* extern functions */ #define buf_new() defun(buf_new)() #define buf_free(b) defun(buf_free)(b) #define buf_len(b) defun(buf_len)(b) #define buf_clen(b) defun(buf_clen)(b) #ifdef IS_TEXT_BUFFER #define mark_new(b,i,c,l,f) defun(mark_new)(b,i,c,l,f) #define mark_new_com(b,i,c,l,f) mark_new(b,i,c,l,f) #else #define mark_new(b,i,f) defun(mark_new)(b,i,f) #define mark_new_com(b,i,c,l,f) mark_new(b,c,f) #endif #define mark_char_new(b,c,f) defun(mark_char_new)(b,c,f) #define mark_free(m) defun(mark_free)(m) #define mark_alive_p(m) defun(mark_alive_p)(m) #define mark_move(m,l) defun(mark_move)(m,l) #define mark_goto(m,i) defun(mark_goto)(m,i) #define mark_moveto(m,to) defun(mark_moveto)(m,to) #define mark_insert(m,s,l) defun(mark_insert)(m,s,l) #define mark_delete(m,l) defun(mark_delete)(m,l) #define mark_substr(m,l) defun(mark_substr)(m,l) #define mark_nextline(m) defun(mark_nextline)(m) #define mark_prevline(m) defun(mark_prevline)(m) #define mark_linetail(m) defun(mark_linetail)(m) #define mark_linehead(m) defun(mark_linehead)(m) #define mark_forward_nline(m,n) defun(mark_forward_nline)(m,n) #define mark_back_nline(m,n) defun(mark_back_nline)(m,n) #define mark_col(m) defun(mark_col)(m) #define mark_bcol(m) defun(mark_bcol)(m) #define mark_setcol(m,i) defun(mark_setcol)(m,i) /* mark list */ static void insert_mark_between _((mark_t *mark, mark_t *prev, mark_t *next)); static void remove_mark _((mark_t *mark)); static void adjust_mark_forward _((mark_t *mark)); static void adjust_mark_backward _((mark_t *mark)); static mark_t* first_mark _((mark_t *mark)); /* mark */ #ifdef IS_TEXT_BUFFER static void mark_forward_char _((mark_t *mark, long clen)); static void mark_forward_byte _((mark_t *mark, long len)); static void mark_back_char _((mark_t *mark, long clen)); static void mark_back_byte _((mark_t *mark, long len)); #endif /* buffer */ static long fwdnl _((buffer_t *buf, long idx)); static long backnl _((buffer_t *buf, long idx)); static long linehead _((buffer_t *buf, long idx)); static long linetail _((buffer_t *buf, long idx)); static long forward_nline _((buffer_t *buf, long idx, long n)); static long back_nline _((buffer_t *buf, long idx, long n)); #ifdef IS_TEXT_BUFFER static long byte_idx _((buffer_t *buf, long begidx, long clen, long *line)); static long byte_length _((buffer_t *buf, long begidx, long clen, long *line)); static long char_length _((buffer_t *buf, long begidx, long len, long *line)); static long char_idx _((buffer_t *buf, long begidx, long len, long *line)); #endif static long fwdcol _((buffer_t *buf, long beg, long clen)); static void setgap _((buffer_t *buf, long idx)); static void insert _((buffer_t *buf, long idx, char_t *str, long len)); static void delete _((buffer_t *buf, long idx, long len)); static VALUE substr _((buffer_t *buf, long idx, long len)); static VALUE getline _((buffer_t *buf, long idx)); static void each_line _((buffer_t *buf, long beg)); static void r_each_line _((buffer_t *buf, long beg)); static void each_line_ro _((buffer_t *buf, long idx)); static void substr_ro _((buffer_t *buf, long idx, long len)); static long buf_index _((buffer_t *buf, char_t *sub, long slen, long idx, long range)); static long buf_rindex _((buffer_t *buf, char_t *sub, long slen, long idx, long range)); static long buf_search _((buffer_t *buf, VALUE vre, long pos, long range, int rev, struct re_registers **regi)); #define INITLEN 4 #define CRITICAL_FLAG (1UL << (TEXTBUF_EXTERN_FLAG_BIT + 1)) #define CRITICAL_CHECK(buf) do { \ if (buf->flags & CRITICAL_FLAG) \ rb_raise(rb_eArgError, "can't modify buffer in critical session"); \ } while (0) #define CRITICAL_BEGIN(buf) do { \ CRITICAL_CHECK(buf); \ buf->flags |= CRITICAL_FLAG; \ } while (0) #define CRITICAL_END(buf) buf->flags &= ~CRITICAL_FLAG #define BUF_LOCK(buf) do { \ VALUE th = rb_thread_current(); \ if (buf->lock && buf->lock != th) \ rb_thread_schedule(); \ else \ buf->lock = th; \ } while (0) #define BUF_UNLOCK(buf) buf->lock = Qfalse; #ifdef DEBUG #define CRAMP_ASSERT(var, name) do { \ if (var < 0) { \ fprintf(stderr, "***ERROR*** CRAMP_%s: wrong value %ld\n", \ name, var); \ break; \ } \ } while (0) #else #define CRAMP_ASSERT(var, name) #endif #define CRAMP_IDX(buf, idx) do { \ CRAMP_ASSERT(idx, "IDX"); \ if (idx > LEN(buf)) \ idx = LEN(buf); \ } while(0) #define CRAMP_CIDX(buf, idx) do { \ CRAMP_ASSERT(idx, "CIDX"); \ if (idx > CLEN(buf)) \ idx = CLEN(buf); \ } while(0) #define CRAMP_LEN(buf, idx, len) do { \ CRAMP_ASSERT(len, "LEN"); \ if (idx + len > LEN(buf)) \ len = LEN(buf) - idx; \ } while(0) #define CRAMP_CLEN(buf, idx, len) do { \ CRAMP_ASSERT(len, "CLEN"); \ if (idx + len > CLEN(buf)) \ len = CLEN(buf) - idx; \ } while(0) #define CRAMP_LEN_NEG(buf, idx, len) do { \ len = -len; \ CRAMP_ASSERT(len, "LEN_NEG"); \ if (len > idx) \ len = idx; \ } while(0) #define CRAMP_CLEN_NEG(buf, idx, len) do { \ len = -len; \ CRAMP_ASSERT(len, "CLEN_NEG"); \ if (len > idx) \ len = idx; \ } while(0) #define CANONICAL_BEG_LEN(buf, idx, len) do { \ if (len >= 0) { \ if (idx + len > LEN(buf)) \ len = LEN(buf) - idx; \ } \ else { \ len = -len; \ if (len > idx) \ len = idx; \ idx -= len; \ } \ } while (0) #ifdef DEBUG #define MARK_ASSERT(var, condi, name) \ if (var < 0) { \ fprintf(stderr, \ "***ERROR*** mark_%s: param negative\n", \ name); \ return; \ } \ if (condi) { \ fprintf(stderr, \ "***ERROR*** mark_%s: param out of range %ld\n", \ name, var); \ return; \ } #else #define MARK_ASSERT(var, condi, name) #endif /* ================================================================== debug ================================================================== */ #if defined(DEBUG) || defined(DEBUGB) || defined(DEBUGC) || \ defined(DEBUGD) || defined(DEBUGI) || defined(DEBUGS) #ifdef IS_TEXT_BUFFER static void p_buf(buf) struct sf_textbuf *buf; { printf("#\n", (unsigned long)buf, buf->len, buf->gap, buf->gaplen, LEN(buf), CLEN(buf)); } static void p_mark(mark) struct sf_textbufmark *mark; { printf("#\n", (unsigned long)mark, (unsigned long)mark->buf, mark->idx, mark->cidx, mark->line); } #endif #ifdef IS_STRING_BUFFER static void p_buf(buf) buffer_t *buf; { printf("#\n", (unsigned long)buf, buf->len, buf->gap, buf->gaplen, LEN(buf)); } static void p_mark(mark) struct sf_strbufmark *mark; { printf("#\n", (unsigned long)mark, (unsigned long)mark->buf, mark->idx); } #endif #endif #ifdef DEBUG # define D(code) code #else # define D(code) #endif #ifdef DEBUGB # define Db(code) if (dodebug) code #else # define Db(code) #endif #ifdef DEBUGC # define Dc(code) if (dodebug) code #else # define Dc(code) #endif #ifdef DEBUGD # define Dd(code) if (dodebug) code #else # define Dd(code) #endif #ifdef DEBUGI # define Di(code) if (dodebug) code #else # define Di(code) #ifdef DEBUGS # define Ds(code) if (dodebug) code #else # define Ds(code) #endif #endif #ifdef DEBUGR # define Dr(code) if (dodebug) code #else # define Dr(code) #endif static VALUE debugon(self) VALUE self; { dodebug = 1; return Qnil; } static VALUE debugoff(self) VALUE self; { dodebug = 0; return Qnil; } /* ================================================================== util ================================================================== */ static unsigned long sym2iflag(v) VALUE v; { ID id; unsigned long flag = INSERT_BEFORE; if (NIL_P(v)) return flag; if (! SYMBOL_P(v)) { rb_raise(rb_eTypeError, "not symbol"); } id = SYM2ID(v); if (id == rb_intern("insert_before")) { flag = INSERT_BEFORE; } else if (id == rb_intern("insert_after")) { flag = INSERT_AFTER; } else { rb_raise(rb_eArgError, "unknown symbol"); } return flag; } /* ================================================================== creation ================================================================== */ buffer_t* defun(buf_new)() { buffer_t *buf; buf = ALLOC(buffer_t); MEMZERO(buf, buffer_t, 1); buf->flags = BUFFER_FLAG; buf->ptr = 0; buf->len = 0; buf->gap = 0; buf->point = 0; buf->ptr = ALLOC_N(char_t, INITLEN); buf->ptr = calloc(sizeof(char), INITLEN); buf->len = INITLEN; buf->gaplen = INITLEN; buf->head = mark_new_com(buf, 0, 0, 1, INSERT_AFTER); buf->tail = mark_new_com(buf, 0, 0, 1, INSERT_BEFORE); buf->point = mark_new_com(buf, 0, 0, 1, INSERT_BEFORE); return buf; } void defun(buf_free)(buf) buffer_t *buf; { mark_t *m; if (buf->ptr) free(buf->ptr); buf->ptr = 0; buf->len = 0; buf->gap = 0; remove_mark(buf->point); free(buf->point); buf->point = 0; for (m = buf->marks; m; m = m->next) m->flags &= ~ALIVE_FLAG; buf->marks = 0; free(buf); } mark_t* #ifdef IS_TEXT_BUFFER defun(mark_new)(buf, idx, cidx, line, flags) buffer_t *buf; long idx, cidx, line; unsigned long flags; #else defun(mark_new)(buf, idx, flags) buffer_t *buf; long idx; unsigned long flags; #endif { mark_t *mark; mark = ALLOC(mark_t); mark->buf = buf; mark->idx = idx; #ifdef IS_TEXT_BUFFER mark->cidx = cidx; mark->line = line; #endif mark->prev = 0; mark->next = 0; mark->regi = 0; mark->flags = ALIVE_FLAG | BUFFER_FLAG | flags; mark->next = buf->marks; buf->marks = mark; adjust_mark_forward(mark); return mark; } mark_t* defun(mark_char_new)(buf, cidx, flags) buffer_t *buf; long cidx; unsigned long flags; { #ifdef IS_TEXT_BUFFER long i, line; i = byte_length(buf, 0, cidx, &line); return mark_new(buf, i, cidx, 1 + line, flags); #else return mark_new(buf, cidx, flags); #endif } void defun(mark_free)(mark) mark_t *mark; { if (ALIVE_P(mark)) remove_mark(mark); mark->buf = 0; mark->vbuf = Qnil; if (mark->regi) { re_free_registers(mark->regi); mark->regi = 0; } free(mark); } /* =================================================================== byte string handle =================================================================== */ long defun(buf_len)(buf) buffer_t *buf; { return LEN(buf); } long defun(buf_clen)(buf) buffer_t *buf; { return CLEN(buf); } #define REAL_INDEX(buf,i) ((i >= buf->gap) ? i + buf->gaplen : i) #define PSEUDO_INDEX(buf,i) ((i > buf->gap) ? i - buf->gaplen : i) #define TB_EACH_CHAR(buf,beg,var) \ do { \ long edge = beg >= buf->gap ? buf->len : buf->gap; \ var = REAL_INDEX(buf, beg); \ ec_again: \ while (var < edge) { #define TB_EACH_CHAR_END(buf,var) \ } \ if (edge == buf->gap) { \ var += buf->gaplen; \ edge = buf->len; \ if (var < edge) \ goto ec_again; \ } \ ec_end: \ if (var > buf->gap) \ var -= buf->gaplen; \ } while (0); #define TB_EACH_CHAR_REVERSE(buf,beg,var) \ do { \ long edge = beg >= buf->gap ? buf->gap + buf->gaplen : 0; \ var = REAL_INDEX(buf, beg); \ ec_again: \ while (var > edge) { #define TB_EACH_CHAR_REVERSE_END(buf,var) \ } \ if (edge != 0) { \ var -= buf->gaplen; \ edge = 0; \ goto ec_again; \ } \ ec_end: \ if (var > buf->gap) \ var -= buf->gaplen; \ } while (0); #define TB_BREAK(buf,var) goto ec_end #define FWDNL(buf, i) do { \ long edge; \ if (i < buf->gap) { \ edge = buf->gap; \ } else if (i == buf->gap) { \ i += buf->gaplen; \ edge = buf->len; \ } else { \ edge = buf->len; \ } \ if (i != edge) { \ i++; \ if (buf->ptr[i-1] == '\r' && i != edge && buf->ptr[i] == '\n') \ i++; \ } \ } while (0) #define BACKNL(buf, i) do { \ long edge; \ if (i == buf->gap + buf->gaplen) { \ i -= buf->gaplen; \ edge = 0; \ } else if (i > buf->gap) { \ edge = buf->gap + buf->gaplen; \ } else { \ edge = 0; \ } \ if (i != edge) { \ i--; \ if (buf->ptr[i] == '\n' && i != edge && buf->ptr[i-1] == '\r') \ i--; \ } \ } while (0) /* must be called idx is just before NL */ static long fwdnl(buf, idx) buffer_t *buf; long idx; { idx = REAL_INDEX(buf, idx); FWDNL(buf, idx); return PSEUDO_INDEX(buf, idx); } /* must be called idx is just after NL */ static long backnl(buf, idx) buffer_t *buf; long idx; { idx = REAL_INDEX(buf, idx); BACKNL(buf, idx); return PSEUDO_INDEX(buf, idx); } static long linehead(buf, idx) buffer_t *buf; long idx; { long i; TB_EACH_CHAR_REVERSE(buf, idx, i) if (buf->ptr[i-1] == '\n' || buf->ptr[i-1] == '\r') TB_BREAK(buf, i); i--; TB_EACH_CHAR_REVERSE_END(buf, i) return i; } static long linetail(buf, idx) buffer_t *buf; long idx; { long i; TB_EACH_CHAR(buf, idx, i) if (buf->ptr[i] == '\n' || buf->ptr[i] == '\r') TB_BREAK(buf, i); i++; TB_EACH_CHAR_END(buf, i) return i; } static long forward_nline(buf, idx, n) buffer_t *buf; long idx, n; { long i; if (!n) return idx; TB_EACH_CHAR(buf, idx, i) if (buf->ptr[i] == '\n' || buf->ptr[i] == '\r') { FWDNL(buf, i); n--; if (!n) TB_BREAK(buf, i); } else { i++; } TB_EACH_CHAR_END(buf, i) return i; } static long back_nline(buf, idx, n) buffer_t *buf; long idx, n; { long i; if (!n) return idx; TB_EACH_CHAR_REVERSE(buf, idx, i) if (buf->ptr[i-1] == '\n' || buf->ptr[i-1] == '\r') { BACKNL(buf, i); n--; if (!n) TB_BREAK(buf, i); } else { i--; } TB_EACH_CHAR_REVERSE_END(buf, i) return i; } #ifdef IS_TEXT_BUFFER static long byte_idx(buf, begidx, clen, line) buffer_t *buf; long begidx, clen; long *line; { long i; long c = clen; long dummy; Db(printf("byte_idx> beg=%ld, clen=%ld\n", begidx, clen)); Db(p_buf(buf)); if (!line) line = &dummy; (*line) = 0; TB_EACH_CHAR(buf, begidx, i) if (!c) TB_BREAK(buf, i); switch (buf->ptr[i]) { case '\n': case '\r': FWDNL(buf, i); (*line)++; break; default: i += mbclen(buf->ptr[i]); break; } c--; TB_EACH_CHAR_END(buf, i) Db(printf("byte_idx< clen/len=%ld/%ld\n", clen, i - begidx)); return i; } static long byte_length(buf, begidx, clen, line) buffer_t *buf; long begidx, clen; long *line; { return byte_idx(buf, begidx, clen, line) - begidx; } static long char_length(buf, begidx, len, line) buffer_t *buf; long begidx, len; long *line; { long i; long c = 0; long targ; long dummy; Dc(printf("char_len> beg=%ld, len=%ld\n", begidx, len)); Dc(p_buf(buf)); if (!line) line = &dummy; *line = 0; targ = begidx + len; targ = REAL_INDEX(buf, targ); TB_EACH_CHAR(buf, begidx, i) if (i == targ) TB_BREAK(buf, i); switch (buf->ptr[i]) { case '\n': case '\r': FWDNL(buf, i); (*line)++; break; default: i += mbclen(buf->ptr[i]); break; } c++; TB_EACH_CHAR_END(buf, i) Dc(printf("char_len< clen=%ld line=%+ld\n", c, *line)); return c; } static long char_idx(buf, begidx, len, line) buffer_t *buf; long begidx, len; long *line; { return begidx + char_length(buf, begidx, len, line); } #else #define byte_idx(buf, begidx, clen, line) ((begidx) + (clen)) #define byte_length(buf, begidx, clen, line) (clen) #define char_length(buf, begidx, len, line) (len) #define char_idx(buf, begidx, len, line) ((begidx) + (len)) #endif static long fwdcol(buf, beg, clen) buffer_t *buf; long beg, clen; { long i; long c = clen; Db(printf("fwdcol> beg=%ld, clen=%ld\n", beg, clen)); Db(p_buf(buf)); TB_EACH_CHAR(buf, beg, i) if (!c) TB_BREAK(buf, i); switch (buf->ptr[i]) { case '\n': case '\r': TB_BREAK(buf, i); break; default: i += mbclen(buf->ptr[i]); break; } c--; TB_EACH_CHAR_END(buf, i) Db(printf("fwdcol< clen/len=%ld/%ld\n", clen, i - beg)); return i; } static void setgap(buf, idx) buffer_t *buf; long idx; { if (idx < buf->gap) { memmove(buf->ptr + (idx + buf->gaplen), buf->ptr + idx, buf->gap - idx); } else { memmove(buf->ptr + buf->gap, buf->ptr + (buf->gap + buf->gaplen), idx - buf->gap); } buf->gap = idx; } #define ISIZE 1024 extern int reall; static void insert(buf, idx, str, len) buffer_t *buf; long idx; char_t *str; long len; { CRITICAL_CHECK(buf); if (buf->gaplen < len) { long newcapa, inc; if (len < ISIZE) inc = ISIZE; else inc = len * 2; newcapa = buf->len + inc; REALLOC_N(buf->ptr, char_t, newcapa); if (idx < buf->gap) { long pl1, pl2; pl1 = buf->len - buf->gap - buf->gaplen; memmove(buf->ptr + (newcapa - pl1), buf->ptr + (buf->gap + buf->gaplen), pl1); pl2 = buf->gap - idx; memmove(buf->ptr + (newcapa - pl1 - pl2), buf->ptr + idx, pl2); } else { long pl1, pl2; pl1 = buf->len - (idx + buf->gaplen); memmove(buf->ptr + (newcapa - pl1), buf->ptr + (buf->len - pl1), pl1); pl2 = idx - buf->gap; memmove(buf->ptr + buf->gap, buf->ptr + buf->gap + buf->gaplen, pl2); } buf->len = newcapa; buf->gap = idx; buf->gaplen += inc; } else { setgap(buf, idx); } memmove(buf->ptr + idx, str, sizeof(char_t) * len); buf->gap += len; buf->gaplen -= len; } static void delete(buf, idx, len) buffer_t *buf; long idx, len; { Dd(printf("\ndelete\n")); Dd(p_buf(buf)); Dd(printf("idx=%ld, len=%ld\n", idx, len)); CRITICAL_CHECK(buf); if (idx < buf->gap) { if (idx + len > buf->gap) { /* OOOOOxxxx-----xxOOOO OOOOO-----------OOOO */ buf->gap = idx; buf->gaplen += len; } else { /* OOOOOxxxxxxXXX----OOOOOO OOOOO----------XXXOOOOOO */ memmove(buf->ptr + (idx + len) + buf->gaplen, buf->ptr + (idx + len), buf->gap - (idx + len)); buf->gap = idx; buf->gaplen += len; } } else { /* OOOOO---XXxxxOOOO OOOOOXX------OOOO */ long pl = idx - buf->gap; memmove(buf->ptr + buf->gap, buf->ptr + buf->gap + buf->gaplen, pl); buf->gap += pl; buf->gaplen += len; } } static VALUE substr(buf, idx, len) buffer_t *buf; long idx, len; { Ds(printf("substr: idx=%ld, len=%ld\n", idx, len)); if ((idx <= buf->gap) && (idx + len > buf->gap)) { VALUE ret; Ds(printf("1=%ld 2=%ld\n", buf->gap - idx, idx + len - buf->gap)); ret = rb_str_new(buf->ptr + idx, len); memcpy(RSTRING(ret)->ptr + (buf->gap - idx), buf->ptr + buf->gap + buf->gaplen, idx + len - buf->gap); return ret; } else { Ds(printf("1=%ld\n", len)); return rb_str_new(buf->ptr + REAL_INDEX(buf, idx), len); } } static VALUE getline(buf, idx) buffer_t *buf; long idx; { long head, tail; head = linehead(buf, idx); tail = linetail(buf, idx); if (head == LEN(buf)) return Qnil; return substr(buf, head, tail - head); } static void each_line(buf, beg) buffer_t *buf; long beg; { long head, tail; if (LEN(buf) == 0) { rb_yield(rb_str_new("", 0)); return; } head = linehead(buf, beg); while (head < LEN(buf)) { tail = linetail(buf, head); tail = fwdnl(buf, tail); rb_yield(substr(buf, head, tail - head)); head = tail; } } static void r_each_line(buf, beg) buffer_t *buf; long beg; { long head, tail; if (LEN(buf) == 0) { rb_yield(rb_str_new("", 0)); return; } tail = linetail(buf, beg); tail = fwdnl(buf, tail); while (tail > 0) { head = backnl(buf, tail); head = linehead(buf, head); rb_yield(substr(buf, head, tail - head)); tail = head; } } struct roextr_param { buffer_t *buf; long idx; long len; struct RString *str; }; static VALUE roeach_i(arg) VALUE arg; { struct roextr_param *p = (struct roextr_param*)arg; buffer_t *buf = p->buf; struct RString *str = p->str; long head, tail; head = p->idx; while (head < LEN(buf)) { tail = linetail(buf, head); tail = fwdnl(buf, tail); if (head < buf->gap && tail > buf->gap) setgap(buf, tail); str->ptr = buf->ptr + REAL_INDEX(buf, head); str->len = tail - head; rb_yield((VALUE)str); head = tail; } return Qnil; } static VALUE roextr_ensure(arg) VALUE arg; { struct roextr_param *p = (struct roextr_param*)arg; RSTRING(p->str)->ptr = 0; RSTRING(p->str)->len = 0; CRITICAL_END(p->buf); return Qnil; } static void each_line_ro(buf, idx) buffer_t *buf; long idx; { struct roextr_param p; NEWOBJ(str, struct RString); OBJSETUP(str, rb_cString, T_STRING); str->ptr = 0; str->len = 0; OBJ_FREEZE(str); CRITICAL_BEGIN(buf); p.str = str; p.buf = buf; p.idx = idx; p.len = 0; rb_ensure(roeach_i, (VALUE)&p, roextr_ensure, (VALUE)&p); } static VALUE rosubstr_i(arg) VALUE arg; { struct roextr_param *p = (struct roextr_param*)arg; buffer_t *buf = p->buf; struct RString *str = p->str; long idx = p->idx; long len = p->len; if (idx <= buf->gap && idx + len > buf->gap) setgap(buf, idx + len); str->ptr = buf->ptr + idx; str->len = len; rb_yield((VALUE)str); return Qnil; } static void substr_ro(buf, idx, len) buffer_t *buf; long idx, len; { struct roextr_param p; NEWOBJ(str, struct RString); OBJSETUP(str, rb_cString, T_STRING); str->ptr = 0; str->len = 0; OBJ_FREEZE(str); CRITICAL_BEGIN(buf); p.str = str; p.buf = buf; p.idx = idx; p.len = len; rb_ensure(rosubstr_i, (VALUE)&p, roextr_ensure, (VALUE)&p); } #define PART_BEFORE_GAP(retcode, nextch, edgeptr, OP) \ while (p OP pend) { \ if (*p == ch) { \ if (p + slen > edgeptr) { \ long sl = edgeptr - p; \ if (p - buf->ptr + slen > LEN(buf)) \ return -1; \ if (memcmp(p, sub, sl) == 0 && \ memcmp(redge, sub + sl, slen - sl) == 0) { \ retcode; \ } \ } \ else { \ if (memcmp(p, sub, slen) == 0) { \ retcode; \ } \ } \ } \ nextch; \ } #define PART_AFTER_GAP(retcode, nextch, OP) \ while (p OP pend) { \ if (*p == ch && memcmp(p, sub, slen)) \ retcode; \ nextch; \ } #define SEARCH_FUNCTION(inccode, retcode1, retcode2) \ char_t *p, *pend, *redge; \ char_t ch; \ \ if (slen == 0) return idx; \ ch = sub[0]; \ redge = buf->ptr + buf->gap + buf->gaplen; \ \ p = buf->ptr + idx; \ pend = buf->ptr + buf->gap - slen; \ Dr(printf("search (before gap) i=%ld end=%ld r-edge=%ld\n", \ p - buf->ptr, pend - buf->ptr, redge - buf->ptr)); \ PART_BEFORE_GAP(retcode1, inccode, pend, <) \ \ p += buf->gaplen; \ pend = buf->ptr + buf->len - slen; \ Dr(printf("search (after gap) i=%ld end=%ld\n", \ p - buf->ptr - buf->gaplen, \ pend - buf->ptr - buf->gaplen)); \ PART_AFTER_GAP(retcode2, inccode, <) \ \ return -1; #define SEARCH_R_FUNCTION(deccode, retcode1, retcode2) \ char_t *p, *pend, *gapp, *redge; \ char_t ch; \ \ if (slen == 0) return 0; \ ch = sub[0]; \ gapp = buf->ptr + buf->gap; \ redge = gapp + buf->gaplen; \ \ p = buf->ptr + idx + buf->gaplen; \ pend = redge; \ if (idx + slen <= LEN(buf)) \ PART_AFTER_GAP(retcode2, deccode, >=) \ \ p -= buf->gaplen; \ pend = buf->ptr; \ if (idx + slen > LEN(buf)) \ p -= idx + slen - LEN(buf); \ PART_BEFORE_GAP(retcode1, deccode, gapp, >=) \ \ return -1; #ifdef IS_TEXT_BUFFER /* return offset (char) from "idx" (char) */ static long buf_index(buf, sub, slen, idx, range) buffer_t *buf; char *sub; long slen, idx, range; { long offset = idx; idx = byte_idx(buf, 0, offset, 0); /* idx: byte, offset: char */ if (1) { SEARCH_FUNCTION( (p += mbclen(*p), offset++), return offset, return offset) } } static int charbound_p(buf, p) buffer_t *buf; char *p; { mark_t *m; long idx = p - buf->ptr; long i, upp; for (m = buf->marks; m->next; m = m->next) { if (m->next->idx > idx) break; } upp = REAL_INDEX(buf, idx); TB_EACH_CHAR(buf, m->idx, i) if (i >= upp) TB_BREAK(buf, i); switch (buf->ptr[i]) { case '\n': case '\r': FWDNL(buf, i); break; default: i += mbclen(buf->ptr[i]); break; } TB_EACH_CHAR_END(buf, i) return i == idx ? 1 : 0; } /* return offset (char) from "idx" (byte) */ static long buf_rindex(buf, sub, slen, idx, range) buffer_t *buf; char_t *sub; long slen, idx, range; { long offset = idx; idx = byte_idx(buf, 0, idx, 0); if (1) { SEARCH_R_FUNCTION( (p -= mbclen(*p), offset++), if (charbound_p(buf, p)) return offset, if (charbound_p(buf, p)) return offset) } } #else static long buf_index(buf, sub, slen, idx, range) buffer_t *buf; char_t *sub; long slen, idx, range; { SEARCH_FUNCTION( p++, return p - buf->ptr, return p - buf->ptr - buf->gaplen) } static long buf_rindex(buf, sub, slen, idx, range) buffer_t *buf; char_t *sub; long slen, idx, range; { SEARCH_R_FUNCTION( p--, return p - buf->ptr, return p - buf->ptr - buf->gaplen) } #endif static long buf_reg_search(buf, re, pos, range, regi) buffer_t *buf; struct re_pattern_buffer *re; long pos, range; struct re_registers *regi; { long ret; if (buf->gap > pos && buf->gap + buf->gaplen < LEN(buf)) setgap(buf, LEN(buf)); Dr(printf("pos=%ld, len=%ld, range=%ld\n", pos, LEN(buf)-pos, range)); ret = ruby_re_search(re, buf->ptr + pos, LEN(buf) - pos, 0, range, regi); if (ret == -2) rb_raise(rb_eRuntimeError, "regexp stack overflow"); if (ret >= 0) ret += pos; if (regi) { int i; for (i = 0; i < regi->num_regs; i++) { regi->beg[i] += pos; regi->end[i] += pos; } } return ret; } static VALUE bufregi_new(p) struct re_registers *p; { ruby_re_free_registers(p); return Qnil; } static long buf_search(buf, vre, pos, range, rev, regi) buffer_t *buf; VALUE vre; long pos, range; int rev; struct re_registers **regi; { struct re_pattern_buffer *re; struct re_registers *rp; long ret; long (*func)(); int alloc = 0; switch (TYPE(vre)) { case T_REGEXP: regextype: re = RREGEXP(vre)->ptr; if (regi) { if (!*regi) { rp = *regi; } else { rp = ALLOC(struct re_registers); memset(rp, 0, sizeof(struct re_registers)); alloc = 1; } } else { rp = 0; } ret = buf_reg_search(buf, re, pos, range, rp); if (regi && alloc) { if (ret < 0) { ruby_re_free_registers(rp); rp = 0; } *regi = rp; } return ret; case T_STRING: strtype: if (regi) { vre = rb_reg_regcomp(vre); goto regextype; } if (rev) { range = -range; func = buf_rindex; } else { func = buf_index; } return (*func)(buf, RSTRING(vre)->ptr, RSTRING(vre)->len, pos, range); default: vre = rb_String(vre); goto strtype; } } /* ================================================================== mark list handle ================================================================== */ static void insert_mark_between(mark, prev, next) mark_t *mark, *prev, *next; { mark->prev = prev; mark->next = next; if (prev) prev->next = mark; if (next) next->prev = mark; } static void remove_mark(mark) mark_t *mark; { if (mark->prev) mark->prev->next = mark->next; if (mark->next) mark->next->prev = mark->prev; mark->prev = 0; mark->next = 0; } static mark_t* first_mark(mark) mark_t *mark; { mark_t *top; top = mark->buf->marks ? mark->buf->marks : mark; while (top->prev) top = top->prev; return top; } static void adjust_mark_forward(mark) mark_t *mark; { mark_t *prev, *next; prev = mark->prev; next = mark->next; remove_mark(mark); while (next) { if (mark->idx == next->idx) { if ((mark->flags & INSERT_BEFORE) <= (next->flags & INSERT_BEFORE)) /* ins-after is "little" than ins-before */ break; } else if (mark->idx < next->idx) { /* if ((prev && prev->idx < mark->idx) || !prev) */ break; } prev = next; next = next->next; } insert_mark_between(mark, prev, next); mark->buf->marks = first_mark(mark); } static void adjust_mark_backward(mark) mark_t *mark; { mark_t *top, *prev, *next; prev = mark->prev; next = mark->next; remove_mark(mark); while (prev) { if (prev->idx == mark->idx) { if ((prev->flags & INSERT_BEFORE) <= (mark->flags & INSERT_BEFORE)) /* ins-after is "little" than ins-before */ break; } else if (prev->idx < mark->idx) { /* if ((next && mark->idx < next->idx) || !next) */ break; } next = prev; prev = prev->prev; } insert_mark_between(mark, prev, next); top = mark->buf->marks ? mark->buf->marks : mark; while (top->prev) top = top->prev; mark->buf->marks = top; } /* =================================================================== mark =================================================================== */ #define linehead_m(m) linehead(m->buf, m->idx) #define linetail_m(m) linetail(m->buf, m->idx) #ifdef IS_TEXT_BUFFER static void mark_forward_char(mark, clen) mark_t *mark; long clen; { mark_t *m; long targ; long line; MARK_ASSERT(clen, mark->cidx + clen > CLEN(mark->buf), "fwd_char"); targ = mark->cidx + clen; for (m = mark; m->next; m = m->next) { if (m->next->cidx > targ) break; } mark->idx = byte_idx(mark->buf, m->idx, clen, &line); mark->cidx = targ; mark->line += line; adjust_mark_forward(mark); } #else # define mark_forward_char(m,l) mark_forward_byte(m,l) #endif static void mark_forward_byte(mark, len) mark_t *mark; long len; { L( long line; ) MARK_ASSERT(len, mark->idx + len > LEN(mark->buf), "fwd_byte"); C( mark->cidx += ) L( char_length(mark->buf, mark->idx, len, &line); ) L( mark->line += line; ) mark->idx += len; /* DO NOT MOVE */ adjust_mark_forward(mark); } #ifdef IS_TEXT_BUFFER static void mark_back_char(mark, clen) mark_t *mark; long clen; { mark_t *m; long targ, i, cl, line; MARK_ASSERT(clen, clen > mark->cidx, "back_char"); targ = mark->cidx - clen; for (m = mark; m->prev; m = m->prev) { if (m->prev->cidx < targ) { m = m->prev; break; } } if (m->cidx < targ) { i = m->idx; cl = targ - m->cidx; } else { m = 0; i = 0; cl = targ; } mark->idx = byte_idx(mark->buf, i, cl, &line); mark->cidx = targ; mark->line = m ? m->line + line : 1 + line; adjust_mark_backward(mark); } #else # define mark_back_char(m,l) mark_back_byte(m,l) #endif static void mark_back_byte(mark, len) mark_t *mark; long len; { L( long line; ) MARK_ASSERT(len, len > mark->idx, "back_byte"); mark->idx -= len; C( mark->cidx -= ) L( char_length(mark->buf, mark->idx, len, &line); ) L( mark->line -= line; ) adjust_mark_backward(mark); } void defun(mark_move)(mark, len) mark_t *mark; long len; { if (len >= 0) { CRAMP_CLEN(mark->buf, CIDX(mark), len); mark_forward_char(mark, len); } else { CRAMP_CLEN_NEG(mark->buf, CIDX(mark), len); mark_back_char(mark, len); } } void defun(mark_moveto)(mark, m) mark_t *mark, *m; { long i; i = mark->idx; mark->idx = m->idx; C( mark->cidx = m->cidx; ) L( mark->line = m->line; ) if (i > m->idx) adjust_mark_backward(mark); else adjust_mark_forward(mark); } void defun(mark_goto)(mark, i) mark_t *mark; long i; { mark_move(mark, i - CIDX(mark)); } void defun(mark_nextline)(mark) mark_t *mark; { long i; i = linetail_m(mark); if (i < mark->buf->len) { i = fwdnl(mark->buf, i); } mark_forward_byte(mark, i - mark->idx); } void defun(mark_prevline)(mark) mark_t *mark; { long i; i = linehead_m(mark); if (i > 0) { i = backnl(mark->buf, i); } mark_back_byte(mark, mark->idx - i); } void defun(mark_forward_nline)(mark, n) mark_t *mark; long n; { long i; i = forward_nline(mark->buf, mark->idx, n); mark_forward_byte(mark, i - mark->idx); } void defun(mark_back_nline)(mark, n) mark_t *mark; long n; { long i; i = back_nline(mark->buf, mark->idx, n); mark_back_byte(mark, mark->idx - i); } void defun(mark_linetail)(mark) mark_t *mark; { long i; i = linetail_m(mark); mark_forward_byte(mark, i - mark->idx); } void defun(mark_linehead)(mark) mark_t *mark; { long i; i = linehead_m(mark); mark_back_byte(mark, mark->idx - i); } long defun(mark_col)(mark) mark_t *mark; { long i; i = linehead_m(mark); return char_length(mark->buf, i, IDX(mark) - i, 0); } long defun(mark_bcol)(mark) mark_t *mark; { return IDX(mark) - linehead_m(mark); } void defun(mark_setcol)(mark, i) mark_t *mark; long i; { long head, orig; orig = mark->idx; head = linehead_m(mark); i = fwdcol(mark->buf, head, i); mark->idx = i; C( mark->cidx = char_idx(mark->buf, head, i - head, 0); ) if (i > orig) adjust_mark_forward(mark); else adjust_mark_backward(mark); } void defun(mark_insert)(mark, str, len) mark_t *mark; char *str; long len; { mark_t *m; long pnt = mark->idx; /* We must save this because mark->idx will be changed */ C( long clen; ) L( long line; ) insert(mark->buf, mark->idx, str, len); L( clen = char_length(mark->buf, mark->idx, len, &line); ) for (m = mark; m->prev; m = m->prev) { if (m->prev->idx != m->idx) break; } for (; m && m->idx == pnt; m = m->next) { if (m->flags & INSERT_BEFORE) { m->idx += len; C( m->cidx += clen; ) L( m->line += line; ) } } for (; m; m = m->next) { m->idx += len; C( m->cidx += clen; ) L( m->line += line; ) } } /* -L <= len <= L, len is byte */ void defun(mark_delete)(mark, len) mark_t *mark; long len; { mark_t *m; long pnt = mark->idx; long upper = mark->idx + len; C( long clen; ) L( long line; ) MARK_ASSERT(len, upper > LEN(mark->buf), "delete"); L( clen = char_length(mark->buf, pnt, len, &line); ) delete(mark->buf, pnt, len); for (m = mark; m->prev; m = m->prev) { if (m->prev->idx != m->idx) break; } for (; m && m->idx < upper; m = m->next) { m->idx = pnt; C( m->cidx = mark->cidx; ) L( m->line = mark->line; ) } for (; m; m = m->next) { m->idx -= len; C( m->cidx -= clen; ) L( m->line -= line; ) } }