/* vi:set sw=4: rbbufcommon.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. */ /* ================================================================== buffer ================================================================== */ #define freeze_check(buf) \ if (OBJ_FROZEN(buf)) \ rb_raise(rb_eArgError, "can't modify frozen buffer"); \ else static void buffer_mark(p) void *p; { buffer_t *buf = (buffer_t*)p; rb_gc_mark(buf->vhead); rb_gc_mark(buf->vtail); } static void buffer_free(p) void *p; { buffer_t *buf = (buffer_t*)p; buf->vhead = Qnil; buf->vtail = Qnil; buf_free(buf); } static void bufmark_mark(p) void *p; { rb_gc_mark(((mark_t*)p)->vbuf); } static void bufmark_free(p) void *p; { mark_free((mark_t*)p); } static VALUE buffer_s_new(self) VALUE self; { buffer_t *buf; volatile VALUE v[2]; buf = buf_new(); buf->vhead = v[0] = WRAP_MARK(buf->head, MARK_CLASS); OBJ_FREEZE(buf->vhead); buf->vtail = v[1] = WRAP_MARK(buf->tail, MARK_CLASS); OBJ_FREEZE(buf->vtail); return WRAP_BUF(buf, self); } static VALUE buffer_new_mark(argc, argv, self) int argc; VALUE *argv; VALUE self; { buffer_t *buf; mark_t *mark; VALUE vc, vflag; long c; unsigned long f; rb_scan_args(argc, argv, "02", &vc, &vflag); c = NIL_P(vc) ? 0 : NUM2LONG(vc); f = sym2iflag(vflag); GET_BUF(self, buf); CRAMP_CIDX(buf, c); mark = mark_char_new(buf, c, f); mark->vbuf = self; return WRAP_MARK(mark, MARK_CLASS); } static VALUE buffer_head(self) VALUE self; { buffer_t *buf; GET_BUF(self, buf); return buf->vhead; } static VALUE buffer_tail(self) VALUE self; { buffer_t *buf; GET_BUF(self, buf); return buf->vtail; } static VALUE buffer_len(self) VALUE self; { buffer_t *buf; GET_BUF(self, buf); return INT2NUM(LEN(buf)); } static VALUE buffer_clen(self) VALUE self; { buffer_t *buf; GET_BUF(self, buf); return INT2NUM(CLEN(buf)); } static VALUE buffer_empty_p(self) VALUE self; { buffer_t *buf; GET_BUF(self, buf); return LEN(buf) ? Qfalse : Qtrue; } static VALUE buffer_concat(self, str) VALUE self, str; { buffer_t *buf; freeze_check(self); GET_BUF(self, buf); mark_insert(buf->tail, RSTRING(str)->ptr, RSTRING(str)->len); return self; } static VALUE buffer_clear(self) VALUE self; { buffer_t *buf; freeze_check(self); GET_BUF(self, buf); mark_delete(buf->head, LEN(buf)); return self; } #ifdef IS_TEXT_BUFFER static VALUE buffer_substr(self, cidx, clen) VALUE self, cidx, clen; { struct sf_textbuf *buf; long ci, len; GET_BUF(self, buf); ci = NUM2LONG(cidx); CRAMP_CIDX(buf, ci); if (buf->point->cidx > ci) mark_back_char(buf->point, buf->point->cidx - ci); else mark_forward_char(buf->point, ci - buf->point->cidx); len = byte_length(buf, buf->point->idx, NUM2LONG(clen), 0); return substr(buf, buf->point->idx, len); } #else static VALUE buffer_substr(self, idx, len) VALUE self, idx, len; { struct sf_strbuf *buf; long i, le; GET_BUF(self, buf); i = NUM2LONG(idx); CRAMP_IDX(buf, i); le = NUM2LONG(len); if (le >= 0) { CRAMP_LEN(buf, i, le); } else { le = -le; if (le > i) le = i; i -= le; } return substr(buf, i, le); } #endif static VALUE buffer_each_line(self) VALUE self; { buffer_t *buf; GET_BUF(self, buf); each_line(buf, 0); return Qnil; } static VALUE buffer_rev_each_line(self) VALUE self; { buffer_t *buf; GET_BUF(self, buf); r_each_line(buf, 0); return Qnil; } static VALUE buffer_string(self) VALUE self; { buffer_t *buf; GET_BUF(self, buf); return substr(buf, 0, LEN(buf)); } static void get_prr(len, pos, range, rev) long len; long *pos, *range; int *rev; { if (*pos > len) { *pos = -1; return; } if (*pos < 0) { *pos += len; if (*pos < 0) return; } if (*range < 0) { *range = -(*range); *rev = -(*rev); } if (*rev) { if (*range > *pos) *range = *pos; } else { if (*pos + *range > len) *range = len - *pos; } } static VALUE buffer_index(argc, argv, self) int argc; VALUE *argv; VALUE self; { buffer_t *buf; VALUE vre, vpos, vrange; long pos, range, ret; int rev; GET_BUF(self, buf); rb_scan_args(argc, argv, "12", &vre, &vpos, &vrange); pos = NIL_P(vpos) ? 0 : NUM2LONG(vpos); range = NIL_P(vrange) ? CLEN(buf) : NUM2LONG(vrange); rev = 0; get_prr(CLEN(buf), &pos, &range, &rev); if (pos < 0) return Qnil; ret = buf_search(buf, vre, pos, range, rev, 0); return ret < 0 ? Qnil : INT2NUM(ret); } static VALUE buffer_rindex(argc, argv, self) int argc; VALUE *argv; VALUE self; { buffer_t *buf; VALUE vre, vpos, vrange; long pos, range, ret; int rev; GET_BUF(self, buf); rb_scan_args(argc, argv, "12", &vre, &vpos, &vrange); pos = NIL_P(vpos) ? 0 : NUM2LONG(vpos); range = NIL_P(vrange) ? CLEN(buf) : NUM2LONG(vrange); rev = 1; get_prr(CLEN(buf), &pos, &range, &rev); if (pos < 0) return Qnil; ret = buf_search(buf, vre, pos, range, rev, 0); return ret < 0 ? Qnil : INT2NUM(ret); } static VALUE buffer_match_to(argc, argv, self) int argc; VALUE *argv; VALUE self; { buffer_t *buf; VALUE vre, vpos, vrange; long pos, range; int rev; struct re_registers *regi; GET_BUF(self, buf); rb_scan_args(argc, argv, "12", &vre, &vpos, &vrange); pos = NIL_P(vpos) ? 0 : NUM2LONG(vpos); range = NIL_P(vrange) ? CLEN(buf) : NUM2LONG(vrange); rev = 0; get_prr(CLEN(buf), &pos, &range, &rev); if (pos < 0) return Qnil; buf_search(buf, vre, pos, range, rev, ®i); return bufregi_new(regi); } #ifdef IS_TEXT_BUFFER static VALUE buffer_inspect(self) VALUE self; { struct sf_textbuf *buf; char tmp[128]; int len; GET_BUF(self, buf); len = sprintf(tmp, "#", (unsigned long)buf, buf->len, buf->gap, buf->gaplen, LEN(buf), CLEN(buf)); return rb_str_new(tmp, len); } #else static VALUE buffer_inspect(self) VALUE self; { struct sf_strbuf *buf; char tmp[128]; int len; GET_BUF(self, buf); len = sprintf(tmp, "#", (unsigned long)buf, buf->len, buf->gap, buf->gaplen, LEN(buf)); return rb_str_new(tmp, len); } #endif /* ======================================================================== mark ======================================================================== */ #define bufmark_p(v) \ ((TYPE(v) == T_DATA) && (RDATA(v)->dfree == bufmark_free)) #define origin_check(m1, m2) \ if (m1->buf != m2->buf) \ rb_raise(rb_eArgError, "marks belonging to different buffer used"); \ else #undef freeze_check #define freeze_check(m) \ if (OBJ_FROZEN(m)) \ rb_raise(rb_eArgError, "can't move frozen mark"); \ else #define alive_check(mark) \ if (!ALIVE_P(mark)) \ rb_raise(rb_eArgError, "method called for dead mark"); \ else static void begm_len(mark, val, mvar, lvar) mark_t *mark; VALUE val; mark_t **mvar; long *lvar; { if (bufmark_p(val)) { mark_t *m; GET_MARK_CHK(val, m); origin_check(mark, m); if (m->idx < mark->idx) { *mvar = m; *lvar = mark->idx - m->idx; } else { *mvar = mark; *lvar = m->idx - mark->idx; } } else { long tmp = NUM2LONG(val); if (tmp >= 0) { CRAMP_CLEN(mark->buf, CIDX(mark), tmp); *mvar = mark; } else { CRAMP_CLEN_NEG(mark->buf, CIDX(mark), tmp); mark_goto(mark->buf->point, CIDX(mark) - tmp); *mvar = mark->buf->point; } *lvar = byte_length(mark->buf, (*mvar)->idx, tmp, 0); } } static VALUE bufmark_buffer(self) VALUE self; { mark_t *mark; GET_MARK_CHK(self, mark); return mark->vbuf; } static VALUE bufmark_dup(argc, argv, self) int argc; VALUE *argv; VALUE self; { mark_t *mark, *p; VALUE clone; VALUE vflag; unsigned long flag = INSERT_BEFORE; if (rb_scan_args(argc, argv, "01", &vflag) == 1) { flag = sym2iflag(vflag); } GET_MARK_CHK(self, mark); p = mark_new_com(mark->buf, IDX(mark), CIDX(mark), mark->line, flag); clone = WRAP_MARK(p, CLASS_OF(self)); CLONESETUP(clone, self); return clone; } static VALUE bufmark_clone(self) VALUE self; { VALUE tmp = bufmark_dup(self); if (OBJ_FROZEN(self)) OBJ_FREEZE(tmp); return tmp; } static VALUE bufmark_alive_p(self) VALUE self; { mark_t *mark; GET_MARK(self, mark); return ALIVE_P(mark) ? Qtrue : Qfalse; } static VALUE bufmark_discard(self) VALUE self; { mark_t *mark; GET_MARK(self, mark); remove_mark(mark); mark->flags &= ~ALIVE_FLAG; mark->buf = 0; mark->vbuf = Qnil; return self; } static VALUE bufmark_insert(self, str) VALUE self, str; { mark_t *mark; GET_MARK_CHK(self, mark); mark_insert(mark, RSTRING(str)->ptr, RSTRING(str)->len); return self; } #if 0 static void sbm_beg_len _((mark_t*, VALUE, long*, long*)); static void tbm_begm_len _((mark_t*, VALUE, mark_t**, long*)); static VALUE bufmark_copyof(self, i, l) VALUE self, i, l; { mark_t *mark, *m; buffer_t *buf; long beg, len; void (*func)(mark_t*, char*, long); GET_MARK_CHK(self, mark); if (! bufmark_p(i)) rb_raise(rb_eTypeError, "wrong arg type %s (expected BufferMark)", rb_class2name(CLASS_OF(i))); GET_MARK_CHK(i, m); buf = m->buf; if (TEXTBUFMARK_P(m)) { mark_t *begm; tbm_begm_len(m, l, &begm, &len); beg = begm->idx; func = mark_insert; } else { sbm_beg_len(m, l, &beg, &len); func = smark_insert; } if (beg <= buf->gap && beg + len > buf->gap) { (*func)(mark, buf->ptr + REAL_INDEX(buf, beg), buf->gap - beg); (*func)(mark, buf->ptr + REAL_INDEX(buf, buf->gap), beg + len - buf->gap); } else { (*func)(mark, buf->ptr + REAL_INDEX(buf, beg), len); } return self; } #endif static VALUE bufmark_delete(self, val) VALUE self, val; { mark_t *mark, *m; long len; GET_MARK_CHK(self, mark); begm_len(mark, val, &m, &len); mark_delete(m, len); return self; } static VALUE bufmark_substr(self, val) VALUE self, val; { mark_t *mark, *m; long len; GET_MARK_CHK(self, mark); begm_len(mark, val, &m, &len); return substr(mark->buf, m->idx, len); } static VALUE bufmark_rosubstr(self, val) VALUE self, val; { mark_t *mark, *m; long len; GET_MARK_CHK(self, mark); begm_len(mark, val, &m, &len); substr_ro(mark->buf, m->idx, len); return Qnil; } static VALUE bufmark_roeach(self) VALUE self; { mark_t *mark; GET_MARK_CHK(self, mark); each_line_ro(mark->buf, mark->idx); return self; } static VALUE bufmark_fwd(self, len) VALUE self, len; { mark_t *mark; GET_MARK_CHK(self, mark); freeze_check(self); mark_move(mark, NUM2LONG(len)); return self; } static VALUE bufmark_back(self, v) VALUE self, v; { mark_t *mark; long len; GET_MARK_CHK(self, mark); freeze_check(self); len = NUM2LONG(v); mark_move(mark, -len); return self; } static VALUE bufmark_char_index(self) VALUE self; { mark_t *mark; GET_MARK_CHK(self, mark); return INT2NUM(CIDX(mark)); } static VALUE bufmark_byte_index(self) VALUE self; { mark_t *mark; GET_MARK_CHK(self, mark); return INT2NUM(IDX(mark)); } static VALUE bufmark_setidx(self, idx) VALUE self; { mark_t *mark; long i = 0; GET_MARK_CHK(self, mark); freeze_check(self); if (FIXNUM_P(idx)) { i = FIX2LONG(idx); } else if (bufmark_p(idx)) { mark_t *m; GET_MARK_CHK(idx, m); origin_check(mark, m); mark_moveto(mark, m); return self; } else { i = NUM2LONG(idx); } mark_goto(mark, i); return self; } static VALUE bufmark_skip(argc, argv, self) int argc; VALUE *argv; VALUE self; { mark_t *mark; VALUE vre, vrange, vrev; long range; int rev, ret; GET_MARK_CHK(self, mark); freeze_check(self); rb_scan_args(argc, argv, "12", &vre, &vrange, &vrev); rev = RTEST(vrev); if (NIL_P(vrange)) { if (rev) range = IDX(mark); else range = LEN(mark->buf) - IDX(mark); } else { range = NUM2LONG(vrange); } if (!mark->regi) { mark->regi = ALLOC(struct re_registers); memset(mark->regi, 0, sizeof(struct re_registers)); } ret = buf_search(mark->buf, vre, mark->idx, range, rev, &mark->regi); if (ret >= 0) { mark_goto(mark, (rev ? mark->regi->beg : mark->regi->end)[0]); return INT2FIX(ret); } else { return Qnil; } } typedef struct { long capa; mark_t **beg; mark_t **end; } bufregi_t; VALUE defun(mark_full_match)(self, re, range, rev, regi) VALUE self, range, rev; bufregi_t *regi; { VALUE argv[3], ret; argv[0] = re; argv[1] = range; argv[2] = rev; ret = bufmark_skip(3, argv, self); if (NIL_P(ret)) { return Qnil; } else { mark_t *mark; long i; GET_MARK(self, mark); if (regi->capa < mark->regi->num_regs) { regi->capa = mark->regi->num_regs; REALLOC_N(regi->beg, mark_t*, regi->capa * 2); regi->end = regi->beg + regi->capa; } for (i = 0; i < mark->regi->num_regs; i++) { if (!regi->beg[i]) { regi->beg[i] = mark_char_new(mark->buf, 0, INSERT_BEFORE); regi->end[i] = mark_char_new(mark->buf, 0, INSERT_BEFORE); } mark_goto(regi->beg[i], mark->regi->beg[i]); mark_goto(regi->end[i], mark->regi->end[i]); } return ret; } } static VALUE bufmark_go_beg(self) VALUE self; { mark_t *mark; GET_MARK_CHK(self, mark); freeze_check(self); mark_moveto(mark, mark->buf->head); return self; } static VALUE bufmark_go_end(self) VALUE self; { mark_t *mark; GET_MARK_CHK(self, mark); freeze_check(self); mark_moveto(mark, mark->buf->tail); return self; } static VALUE bufmark_plus(self, idx) VALUE self, idx; { mark_t *mark, *m; VALUE ret; GET_MARK_CHK(self, mark); ret = bufmark_dup(self); GET_MARK(ret, m); mark_move(mark, NUM2LONG(idx)); return ret; } static VALUE bufmark_minus(self, idx) VALUE self, idx; { mark_t *mark, *m; VALUE ret; long i; GET_MARK_CHK(self, mark); ret = bufmark_dup(self); GET_MARK(ret, m); i = NUM2LONG(idx); mark_move(mark, -i); return ret; } #ifdef IS_TEXT_BUFFER static VALUE bufmark_line(self) VALUE self; { struct sf_textbufmark *mark; GET_MARK_CHK(self, mark); return INT2NUM(mark->line); } static VALUE bufmark_setline(self, n) VALUE self; { struct sf_textbufmark *mark; long i; GET_MARK_CHK(self, mark); freeze_check(self); i = NUM2LONG(n); if (mark->line > i) { mark_back_nline(mark, mark->line - i); } else { mark_forward_nline(mark, i - mark->line); } return n; } #endif static VALUE bufmark_next_line(self) VALUE self; { mark_t *mark; GET_MARK_CHK(self, mark); freeze_check(self); mark_nextline(mark); return self; } static VALUE bufmark_prev_line(self) VALUE self; { mark_t *mark; GET_MARK_CHK(self, mark); freeze_check(self); mark_prevline(mark); return self; } static VALUE bufmark_line_head(self) VALUE self; { mark_t *mark; GET_MARK_CHK(self, mark); freeze_check(self); mark_linehead(mark); return self; } static VALUE bufmark_line_tail(self) VALUE self; { mark_t *mark; GET_MARK_CHK(self, mark); freeze_check(self); mark_linetail(mark); return self; } static VALUE bufmark_getline(self) VALUE self; { mark_t *mark; GET_MARK_CHK(self, mark); return getline(mark->buf, mark->idx); } static VALUE bufmark_col(self) VALUE self; { mark_t *mark; long i; GET_MARK_CHK(self, mark); i = mark_col(mark); return INT2NUM(i); } static VALUE bufmark_bytecol(self) VALUE self; { mark_t *mark; long i; GET_MARK_CHK(self, mark); i = mark_bcol(mark); return INT2NUM(i); } static VALUE bufmark_setcol(self, n) VALUE self; { mark_t *mark; long i; GET_MARK_CHK(self, mark); freeze_check(self); i = NUM2LONG(n); mark_setcol(mark, i); return n; } #ifdef IS_TEXT_BUFFER static VALUE bufmark_inspect(self) VALUE self; { mark_t *mark; char buf[128]; int len; GET_MARK(self, mark); if (ALIVE_P(mark)) { len = sprintf(buf, "#", (unsigned long)mark, (unsigned long)mark->buf, mark->idx, mark->cidx); } else { len = sprintf(buf, "#", (unsigned long)mark); } return rb_str_new(buf, len); } #else static VALUE bufmark_inspect(self) VALUE self; { mark_t *mark; char buf[128]; int len; GET_MARK(self, mark); if (ALIVE_P(mark)) { len = sprintf(buf, "#", (unsigned long)mark, (unsigned long)mark->buf, mark->idx); } else { len = sprintf(buf, "#", (unsigned long)mark); } return rb_str_new(buf, len); } #endif