#include "config.h"
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <bitstring.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../common/common.h"
#include "vi.h"
static int vs_deleteln(SCR *, int);
static int vs_insertln(SCR *, int);
static int vs_sm_delete(SCR *, recno_t);
static int vs_sm_down(SCR *, MARK *, recno_t, scroll_t, SMAP *);
static int vs_sm_erase(SCR *);
static int vs_sm_insert(SCR *, recno_t);
static int vs_sm_reset(SCR *, recno_t);
static int vs_sm_up(SCR *, MARK *, recno_t, scroll_t, SMAP *);
int
vs_change(SCR *sp, recno_t lno, lnop_t op)
{
VI_PRIVATE *vip;
SMAP *p;
size_t cnt, oldy, oldx;
vip = VIP(sp);
if (((op == LINE_APPEND && lno == 0) || (op == LINE_INSERT && lno == 1)) &&
!db_exist(sp, 2)) {
lno = 1;
op = LINE_RESET;
}
if (op == LINE_APPEND) {
++lno;
op = LINE_INSERT;
}
if (lno > TMAP->lno)
return (0);
if (lno < HMAP->lno) {
switch (op) {
case LINE_APPEND:
abort();
case LINE_DELETE:
for (p = HMAP, cnt = sp->t_rows; cnt--; ++p)
--p->lno;
if (sp->lno >= lno)
--sp->lno;
F_SET(vip, VIP_N_RENUMBER);
break;
case LINE_INSERT:
for (p = HMAP, cnt = sp->t_rows; cnt--; ++p)
++p->lno;
if (sp->lno >= lno)
++sp->lno;
F_SET(vip, VIP_N_RENUMBER);
break;
case LINE_RESET:
break;
}
return (0);
}
F_SET(vip, VIP_N_REFRESH);
VI_SCR_CFLUSH(vip);
if (sp->lno == lno)
F_SET(vip, VIP_CUR_INVALID);
if (!F_ISSET(sp, SC_TINPUT_INFO) &&
(F_ISSET(sp, SC_SCR_EXWROTE) || VIP(sp)->totalcount > 1)) {
F_SET(vip, VIP_N_EX_REDRAW);
return (0);
}
(void)sp->gp->scr_cursor(sp, &oldy, &oldx);
switch (op) {
case LINE_DELETE:
if (vs_sm_delete(sp, lno))
return (1);
F_SET(vip, VIP_N_RENUMBER);
break;
case LINE_INSERT:
if (vs_sm_insert(sp, lno))
return (1);
F_SET(vip, VIP_N_RENUMBER);
break;
case LINE_RESET:
if (vs_sm_reset(sp, lno))
return (1);
break;
default:
abort();
}
(void)sp->gp->scr_move(sp, oldy, oldx);
return (0);
}
int
vs_sm_fill(SCR *sp, recno_t lno, pos_t pos)
{
SMAP *p, tmp;
size_t cnt;
for (p = HMAP, cnt = sp->t_rows; cnt--; ++p)
SMAP_FLUSH(p);
F_SET(sp, SC_SCR_REDRAW);
switch (pos) {
case P_FILL:
tmp.lno = 1;
tmp.coff = 0;
tmp.soff = 1;
if (vs_sm_nlines(sp,
&tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
lno = 1;
goto top;
}
if (db_last(sp, &tmp.lno))
return (1);
tmp.coff = 0;
tmp.soff = vs_screens(sp, tmp.lno, NULL);
if (vs_sm_nlines(sp,
&tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
TMAP->lno = tmp.lno;
TMAP->coff = tmp.coff;
TMAP->soff = tmp.soff;
goto bottom;
}
goto middle;
case P_TOP:
if (lno != OOBLNO) {
top: HMAP->lno = lno;
HMAP->coff = 0;
HMAP->soff = 1;
}
for (p = HMAP, cnt = sp->t_rows; --cnt; ++p)
if (vs_sm_next(sp, p, p + 1))
goto err;
break;
case P_MIDDLE:
middle: p = HMAP + sp->t_rows / 2;
p->lno = lno;
p->coff = 0;
p->soff = 1;
for (; p > HMAP; --p)
if (vs_sm_prev(sp, p, p - 1)) {
lno = 1;
goto top;
}
p = HMAP + sp->t_rows / 2;
for (; p < TMAP; ++p)
if (vs_sm_next(sp, p, p + 1))
goto err;
break;
case P_BOTTOM:
if (lno != OOBLNO) {
TMAP->lno = lno;
TMAP->coff = 0;
TMAP->soff = vs_screens(sp, lno, NULL);
}
bottom: for (p = TMAP; p > HMAP; --p)
if (vs_sm_prev(sp, p, p - 1)) {
lno = 1;
goto top;
}
break;
default:
abort();
}
return (0);
err: HMAP->lno = 1;
HMAP->coff = 0;
HMAP->soff = 1;
for (p = HMAP; p < TMAP; ++p)
if (vs_sm_next(sp, p, p + 1))
return (1);
return (0);
}
#define HANDLE_WEIRDNESS(cnt) { \
if ((cnt) >= sp->t_rows) { \
F_SET(sp, SC_SCR_REFORMAT); \
return (0); \
} \
}
static int
vs_sm_delete(SCR *sp, recno_t lno)
{
SMAP *p, *t;
size_t cnt_orig;
for (p = HMAP; p->lno != lno; ++p);
if (O_ISSET(sp, O_LEFTRIGHT))
cnt_orig = 1;
else
for (cnt_orig = 1, t = p + 1;
t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
HANDLE_WEIRDNESS(cnt_orig);
(void)sp->gp->scr_move(sp, p - HMAP, 0);
if (vs_deleteln(sp, cnt_orig))
return (1);
memmove(p, p + cnt_orig, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP));
for (t = TMAP - cnt_orig; p <= t; ++p)
--p->lno;
for (p = TMAP - cnt_orig;;) {
if (p < TMAP && vs_sm_next(sp, p, p + 1))
return (1);
if (vs_line(sp, ++p, NULL, NULL))
return (1);
if (p == TMAP)
break;
}
return (0);
}
static int
vs_sm_insert(SCR *sp, recno_t lno)
{
SMAP *p, *t;
size_t cnt_orig, cnt, coff;
coff = HMAP->coff;
for (p = HMAP; p->lno != lno; ++p);
cnt_orig = vs_screens(sp, lno, NULL);
HANDLE_WEIRDNESS(cnt_orig);
cnt = (TMAP - p) + 1;
if (cnt_orig > cnt)
cnt_orig = cnt;
(void)sp->gp->scr_move(sp, p - HMAP, 0);
if (vs_insertln(sp, cnt_orig))
return (1);
memmove(p + cnt_orig, p, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP));
for (t = p + cnt_orig; t <= TMAP; ++t)
++t->lno;
for (cnt = 1, t = p; cnt <= cnt_orig; ++t, ++cnt) {
t->lno = lno;
t->coff = coff;
t->soff = cnt;
SMAP_FLUSH(t);
if (vs_line(sp, t, NULL, NULL))
return (1);
}
return (0);
}
static int
vs_sm_reset(SCR *sp, recno_t lno)
{
SMAP *p, *t;
size_t cnt_orig, cnt_new, cnt, diff;
for (p = HMAP; p->lno != lno; ++p);
if (O_ISSET(sp, O_LEFTRIGHT)) {
t = p;
cnt_orig = cnt_new = 1;
} else {
for (cnt_orig = 0,
t = p; t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
cnt_new = vs_screens(sp, lno, NULL);
}
HANDLE_WEIRDNESS(cnt_orig);
if (cnt_orig == cnt_new) {
do {
SMAP_FLUSH(p);
if (vs_line(sp, p, NULL, NULL))
return (1);
} while (++p < t);
return (0);
}
if (cnt_orig < cnt_new) {
diff = cnt_new - cnt_orig;
cnt = (TMAP - p) + 1;
if (diff > cnt)
diff = cnt;
if (cnt > 1) {
(void)sp->gp->scr_move(sp, p - HMAP, 0);
if (vs_insertln(sp, diff))
return (1);
memmove(p + diff, p,
(((TMAP - p) - diff) + 1) * sizeof(SMAP));
}
for (cnt = 1, t = p; cnt_new-- && t <= TMAP; ++t, ++cnt) {
t->lno = lno;
t->soff = cnt;
SMAP_FLUSH(t);
if (vs_line(sp, t, NULL, NULL))
return (1);
}
} else {
diff = cnt_orig - cnt_new;
(void)sp->gp->scr_move(sp, p - HMAP, 0);
if (vs_deleteln(sp, diff))
return (1);
memmove(p, p + diff, (((TMAP - p) - diff) + 1) * sizeof(SMAP));
for (cnt = 1, t = p; cnt_new--; ++t, ++cnt) {
t->lno = lno;
t->soff = cnt;
SMAP_FLUSH(t);
if (vs_line(sp, t, NULL, NULL))
return (1);
}
for (t = TMAP - diff;;) {
if (t < TMAP && vs_sm_next(sp, t, t + 1))
return (1);
if (vs_line(sp, ++t, NULL, NULL))
return (1);
if (t == TMAP)
break;
}
}
return (0);
}
int
vs_sm_scroll(SCR *sp, MARK *rp, recno_t count, scroll_t scmd)
{
SMAP *smp;
F_SET(VIP(sp), VIP_CUR_INVALID);
if (vs_sm_cursor(sp, &smp))
return (1);
switch (scmd) {
case CNTRL_B:
case CNTRL_U:
case CNTRL_Y:
case Z_CARAT:
if (vs_sm_down(sp, rp, count, scmd, smp))
return (1);
break;
case CNTRL_D:
case CNTRL_E:
case CNTRL_F:
case Z_PLUS:
if (vs_sm_up(sp, rp, count, scmd, smp))
return (1);
break;
default:
abort();
}
if (scmd != CNTRL_E && scmd != CNTRL_Y &&
rp->cno == 0 && nonblank(sp, rp->lno, &rp->cno))
return (1);
return (0);
}
static int
vs_sm_up(SCR *sp, MARK *rp, recno_t count, scroll_t scmd, SMAP *smp)
{
int cursor_set, echanged, zset;
SMAP *ssmp, s1, s2;
if (vs_sm_next(sp, TMAP, &s1))
return (1);
if (s1.lno > TMAP->lno && !db_exist(sp, s1.lno)) {
if (scmd == CNTRL_E || scmd == Z_PLUS || smp == TMAP) {
v_eof(sp, NULL);
return (1);
}
if (vs_sm_next(sp, smp, &s1))
return (1);
if (s1.lno > smp->lno && !db_exist(sp, s1.lno)) {
v_eof(sp, NULL);
return (1);
}
}
cursor_set = 0;
if (IS_SMALL(sp)) {
if (count >= sp->t_maxrows || scmd == CNTRL_F) {
s1 = TMAP[0];
if (vs_sm_erase(sp))
return (1);
for (; count--; s1 = s2) {
if (vs_sm_next(sp, &s1, &s2))
return (1);
if (s2.lno != s1.lno && !db_exist(sp, s2.lno))
break;
}
TMAP[0] = s2;
if (vs_sm_fill(sp, OOBLNO, P_BOTTOM))
return (1);
return (vs_sm_position(sp, rp, 0, P_TOP));
}
cursor_set = scmd == CNTRL_E || vs_sm_cursor(sp, &ssmp);
for (; count &&
sp->t_rows != sp->t_maxrows; --count, ++sp->t_rows) {
if (vs_sm_next(sp, TMAP, &s1))
return (1);
if (TMAP->lno != s1.lno && !db_exist(sp, s1.lno))
break;
*++TMAP = s1;
if (vs_line(sp, TMAP, NULL, NULL))
return (1);
if (!cursor_set)
++ssmp;
}
if (!cursor_set) {
rp->lno = ssmp->lno;
rp->cno = ssmp->c_sboff;
}
if (count == 0)
return (0);
}
for (echanged = zset = 0; count; --count) {
if (vs_sm_next(sp, TMAP, &s1))
return (1);
if (TMAP->lno != s1.lno && !db_exist(sp, s1.lno))
break;
if (vs_sm_1up(sp))
return (1);
switch (scmd) {
case CNTRL_E:
if (smp > HMAP)
--smp;
else
echanged = 1;
break;
case Z_PLUS:
if (zset) {
if (smp > HMAP)
--smp;
} else {
smp = TMAP;
zset = 1;
}
default:
break;
}
}
if (cursor_set)
return(0);
switch (scmd) {
case CNTRL_E:
if (echanged) {
rp->lno = smp->lno;
rp->cno = vs_colpos(sp, smp->lno,
(O_ISSET(sp, O_LEFTRIGHT) ?
smp->coff : (smp->soff - 1) * sp->cols) +
sp->rcm % sp->cols);
}
return (0);
case CNTRL_F:
if (!count) {
smp = HMAP;
break;
}
case CNTRL_D:
for (; count; --count, ++smp)
if (smp == TMAP || !db_exist(sp, smp[1].lno))
break;
break;
case Z_PLUS:
break;
default:
abort();
}
if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL))
return (1);
rp->lno = smp->lno;
rp->cno = smp->c_sboff;
return (0);
}
int
vs_sm_1up(SCR *sp)
{
(void)sp->gp->scr_move(sp, 0, 0);
if (vs_deleteln(sp, 1))
return (1);
if (IS_ONELINE(sp)) {
if (vs_sm_next(sp, TMAP, TMAP))
return (1);
} else {
memmove(HMAP, HMAP + 1, (sp->rows - 1) * sizeof(SMAP));
if (vs_sm_next(sp, TMAP - 1, TMAP))
return (1);
}
return (vs_line(sp, TMAP, NULL, NULL));
}
static int
vs_deleteln(SCR *sp, int cnt)
{
GS *gp;
size_t oldy, oldx;
gp = sp->gp;
if (IS_ONELINE(sp))
(void)gp->scr_clrtoeol(sp);
else {
(void)gp->scr_cursor(sp, &oldy, &oldx);
while (cnt--) {
(void)gp->scr_deleteln(sp);
(void)gp->scr_move(sp, LASTLINE(sp), 0);
(void)gp->scr_insertln(sp);
(void)gp->scr_move(sp, oldy, oldx);
}
}
return (0);
}
static int
vs_sm_down(SCR *sp, MARK *rp, recno_t count, scroll_t scmd, SMAP *smp)
{
SMAP *ssmp, s1, s2;
int cursor_set, ychanged, zset;
if (HMAP->lno == 1 &&
(O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1) &&
(scmd == CNTRL_Y || scmd == Z_CARAT || smp == HMAP)) {
v_sof(sp, NULL);
return (1);
}
cursor_set = scmd == CNTRL_Y;
if (IS_SMALL(sp)) {
if (count >= sp->t_maxrows || scmd == CNTRL_B) {
s1 = HMAP[0];
if (vs_sm_erase(sp))
return (1);
for (; count--; s1 = s2) {
if (vs_sm_prev(sp, &s1, &s2))
return (1);
if (s2.lno == 1 &&
(O_ISSET(sp, O_LEFTRIGHT) || s2.soff == 1))
break;
}
HMAP[0] = s2;
if (vs_sm_fill(sp, OOBLNO, P_TOP))
return (1);
return (vs_sm_position(sp, rp, 0, P_BOTTOM));
}
cursor_set = scmd == CNTRL_Y || vs_sm_cursor(sp, &ssmp);
for (; count &&
sp->t_rows != sp->t_maxrows; --count, ++sp->t_rows) {
if (HMAP->lno == 1 &&
(O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1))
break;
++TMAP;
if (vs_sm_1down(sp))
return (1);
}
if (!cursor_set) {
rp->lno = ssmp->lno;
rp->cno = ssmp->c_sboff;
}
if (count == 0)
return (0);
}
for (ychanged = zset = 0; count; --count) {
if (HMAP->lno == 1 &&
(O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1))
break;
if (vs_sm_1down(sp))
return (1);
switch (scmd) {
case CNTRL_Y:
if (smp < TMAP)
++smp;
else
ychanged = 1;
break;
case Z_CARAT:
if (zset) {
if (smp < TMAP)
++smp;
} else {
smp = HMAP;
zset = 1;
}
default:
break;
}
}
if (scmd != CNTRL_Y && cursor_set)
return(0);
switch (scmd) {
case CNTRL_B:
if (!count) {
for (smp = TMAP; smp > HMAP; --smp)
if (db_exist(sp, smp->lno))
break;
break;
}
case CNTRL_U:
if (count < smp - HMAP)
smp -= count;
else
smp = HMAP;
break;
case CNTRL_Y:
if (ychanged) {
rp->lno = smp->lno;
rp->cno = vs_colpos(sp, smp->lno,
(O_ISSET(sp, O_LEFTRIGHT) ?
smp->coff : (smp->soff - 1) * sp->cols) +
sp->rcm % sp->cols);
}
return (0);
case Z_CARAT:
break;
default:
abort();
}
if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL))
return (1);
rp->lno = smp->lno;
rp->cno = smp->c_sboff;
return (0);
}
static int
vs_sm_erase(SCR *sp)
{
GS *gp;
gp = sp->gp;
(void)gp->scr_move(sp, LASTLINE(sp), 0);
(void)gp->scr_clrtoeol(sp);
for (; sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) {
(void)gp->scr_move(sp, TMAP - HMAP, 0);
(void)gp->scr_clrtoeol(sp);
}
return (0);
}
int
vs_sm_1down(SCR *sp)
{
(void)sp->gp->scr_move(sp, 0, 0);
if (vs_insertln(sp, 1))
return (1);
if (IS_ONELINE(sp)) {
if (vs_sm_prev(sp, HMAP, HMAP))
return (1);
} else {
memmove(HMAP + 1, HMAP, (sp->rows - 1) * sizeof(SMAP));
if (vs_sm_prev(sp, HMAP + 1, HMAP))
return (1);
}
return (vs_line(sp, HMAP, NULL, NULL));
}
static int
vs_insertln(SCR *sp, int cnt)
{
GS *gp;
size_t oldy, oldx;
gp = sp->gp;
if (IS_ONELINE(sp)) {
(void)gp->scr_move(sp, LASTLINE(sp), 0);
(void)gp->scr_clrtoeol(sp);
} else {
(void)gp->scr_cursor(sp, &oldy, &oldx);
while (cnt--) {
(void)gp->scr_move(sp, LASTLINE(sp) - 1, 0);
(void)gp->scr_deleteln(sp);
(void)gp->scr_move(sp, oldy, oldx);
(void)gp->scr_insertln(sp);
}
}
return (0);
}
int
vs_sm_next(SCR *sp, SMAP *p, SMAP *t)
{
size_t lcnt;
SMAP_FLUSH(t);
if (O_ISSET(sp, O_LEFTRIGHT)) {
t->lno = p->lno + 1;
t->coff = p->coff;
} else {
lcnt = vs_screens(sp, p->lno, NULL);
if (lcnt == p->soff) {
t->lno = p->lno + 1;
t->soff = 1;
} else {
t->lno = p->lno;
t->soff = p->soff + 1;
}
}
return (0);
}
int
vs_sm_prev(SCR *sp, SMAP *p, SMAP *t)
{
SMAP_FLUSH(t);
if (O_ISSET(sp, O_LEFTRIGHT)) {
t->lno = p->lno - 1;
t->coff = p->coff;
} else {
if (p->soff != 1) {
t->lno = p->lno;
t->soff = p->soff - 1;
} else {
t->lno = p->lno - 1;
t->soff = vs_screens(sp, t->lno, NULL);
}
}
return (t->lno == 0);
}
int
vs_sm_cursor(SCR *sp, SMAP **smpp)
{
SMAP *p;
if (sp->lno < HMAP->lno || sp->lno > TMAP->lno)
return (1);
for (p = HMAP; p->lno != sp->lno; ++p);
for (; p <= TMAP; ++p) {
if (p != TMAP && (p + 1)->lno != p->lno) {
*smpp = p;
return (0);
}
if (!SMAP_CACHE(p) && vs_line(sp, p, NULL, NULL))
return (1);
if (p->c_eboff >= sp->cno) {
*smpp = p;
return (0);
}
}
return (1);
}
int
vs_sm_position(SCR *sp, MARK *rp, u_long cnt, pos_t pos)
{
SMAP *smp;
recno_t last;
switch (pos) {
case P_TOP:
if (cnt > TMAP - HMAP)
goto sof;
smp = HMAP + cnt;
if (cnt && !db_exist(sp, smp->lno)) {
sof: msgq(sp, M_BERR, "Movement past the end-of-screen");
return (1);
}
break;
case P_MIDDLE:
if (!db_exist(sp, TMAP->lno)) {
if (db_last(sp, &last))
return (1);
for (smp = TMAP; smp->lno > last && smp > HMAP; --smp);
if (smp > HMAP)
smp -= (smp - HMAP) / 2;
} else
smp = (HMAP + (TMAP - HMAP) / 2) + cnt;
break;
case P_BOTTOM:
if (cnt > TMAP - HMAP)
goto eof;
smp = TMAP - cnt;
if (!db_exist(sp, smp->lno)) {
if (db_last(sp, &last))
return (1);
for (; smp->lno > last && smp > HMAP; --smp);
if (cnt > smp - HMAP) {
eof: msgq(sp, M_BERR,
"Movement past the beginning-of-screen");
return (1);
}
smp -= cnt;
}
break;
default:
abort();
}
if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL))
return (1);
rp->lno = smp->lno;
rp->cno = smp->c_sboff;
return (0);
}
recno_t
vs_sm_nlines(SCR *sp, SMAP *from_sp, recno_t to_lno, size_t max)
{
recno_t lno, lcnt;
if (O_ISSET(sp, O_LEFTRIGHT))
return (from_sp->lno > to_lno ?
from_sp->lno - to_lno : to_lno - from_sp->lno);
if (from_sp->lno == to_lno)
return (from_sp->soff - 1);
if (from_sp->lno > to_lno) {
lcnt = from_sp->soff - 1;
for (lno = from_sp->lno; --lno >= to_lno && lcnt <= max;)
lcnt += vs_screens(sp, lno, NULL);
} else {
lno = from_sp->lno;
lcnt = (vs_screens(sp, lno, NULL) - from_sp->soff) + 1;
for (; ++lno < to_lno && lcnt <= max;)
lcnt += vs_screens(sp, lno, NULL);
}
return (lcnt);
}