#include <sys/queue.h>
#include <ctype.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <term.h>
#include "def.h"
#include "kbd.h"
struct video {
short v_hash;
short v_flag;
short v_color;
int v_cost;
char *v_text;
};
#define VFCHG 0x0001
#define VFHBAD 0x0002
#define VFEXT 0x0004
struct score {
int s_itrace;
int s_jtrace;
int s_cost;
};
void vtmove(int, int);
void vtputc(int, struct mgwin *);
void vtpute(int, struct mgwin *);
int vtputs(const char *, struct mgwin *);
void vteeol(void);
void updext(int, int);
void modeline(struct mgwin *, int);
void setscores(int, int);
void traceback(int, int, int, int);
void ucopy(struct video *, struct video *);
void uline(int, struct video *, struct video *);
void hash(struct video *);
int sgarbf = TRUE;
int vtrow = HUGE;
int vtcol = HUGE;
int tthue = CNONE;
int ttrow = HUGE;
int ttcol = HUGE;
int tttop = HUGE;
int ttbot = HUGE;
int lbound = 0;
struct video **vscreen;
struct video **pscreen;
struct video *video;
struct video blanks;
struct score *score;
static int linenos = TRUE;
static int colnos = FALSE;
extern int macrodef;
extern int globalwd;
int
linenotoggle(int f, int n)
{
if (f & FFARG)
linenos = n > 0;
else
linenos = !linenos;
sgarbf = TRUE;
return (TRUE);
}
int
colnotoggle(int f, int n)
{
if (f & FFARG)
colnos = n > 0;
else
colnos = !colnos;
sgarbf = TRUE;
return (TRUE);
}
int
vtresize(int force, int newrow, int newcol)
{
int i;
int rowchanged, colchanged;
static int first_run = 1;
struct video *vp;
if (newrow < 1 || newcol < 1)
return (FALSE);
rowchanged = (newrow != nrow);
colchanged = (newcol != ncol);
#define TRYREALLOC(a, n) do { \
void *tmp; \
if ((tmp = realloc((a), (n))) == NULL) { \
panic("out of memory in display code"); \
} \
(a) = tmp; \
} while (0)
#define TRYREALLOCARRAY(a, n, m) do { \
void *tmp; \
if ((tmp = reallocarray((a), (n), (m))) == NULL) {\
panic("out of memory in display code"); \
} \
(a) = tmp; \
} while (0)
if (!first_run && !force && !rowchanged && !colchanged)
return (TRUE);
if (first_run)
memset(&blanks, 0, sizeof(blanks));
if (rowchanged || first_run) {
int vidstart;
if (nrow == 0)
vidstart = 0;
else
vidstart = 2 * (nrow - 1);
if (newrow < nrow) {
for (i = 2 * (newrow - 1); i < 2 * (nrow - 1); i++) {
free(video[i].v_text);
video[i].v_text = NULL;
}
}
TRYREALLOCARRAY(score, newrow, newrow * sizeof(struct score));
TRYREALLOCARRAY(vscreen, (newrow - 1), sizeof(struct video *));
TRYREALLOCARRAY(pscreen, (newrow - 1), sizeof(struct video *));
TRYREALLOCARRAY(video, (newrow - 1), 2 * sizeof(struct video));
for (i = vidstart; i < 2 * (newrow - 1); i++)
memset(&video[i], 0, sizeof(struct video));
vp = &video[0];
for (i = 0; i < newrow - 1; ++i) {
vscreen[i] = vp;
++vp;
pscreen[i] = vp;
++vp;
}
}
if (rowchanged || colchanged || first_run) {
for (i = 0; i < 2 * (newrow - 1); i++)
TRYREALLOC(video[i].v_text, newcol);
TRYREALLOC(blanks.v_text, newcol);
}
nrow = newrow;
ncol = newcol;
if (ttrow > nrow)
ttrow = nrow;
if (ttcol > ncol)
ttcol = ncol;
first_run = 0;
return (TRUE);
}
#undef TRYREALLOC
#undef TRYREALLOCARRAY
void
vtinit(void)
{
int i;
ttopen();
ttinit();
blanks.v_color = CTEXT;
for (i = 0; i < ncol; ++i)
blanks.v_text[i] = ' ';
}
void
vttidy(void)
{
ttcolor(CTEXT);
ttnowindow();
ttmove(nrow - 1, 0);
tteeol();
tttidy();
ttflush();
ttclose();
}
void
vtmove(int row, int col)
{
vtrow = row;
vtcol = col;
}
void
vtputc(int c, struct mgwin *wp)
{
struct video *vp;
int target;
c &= 0xff;
vp = vscreen[vtrow];
if (vtcol >= ncol)
vp->v_text[ncol - 1] = '$';
else if (c == '\t') {
target = ntabstop(vtcol, wp->w_bufp->b_tabw);
do {
vtputc(' ', wp);
} while (vtcol < ncol && vtcol < target);
} else if (ISCTRL(c)) {
vtputc('^', wp);
vtputc(CCHR(c), wp);
} else if (isprint(c))
vp->v_text[vtcol++] = c;
else {
char bf[5];
snprintf(bf, sizeof(bf), "\\%o", c);
vtputs(bf, wp);
}
}
void
vtpute(int c, struct mgwin *wp)
{
struct video *vp;
int target;
c &= 0xff;
vp = vscreen[vtrow];
if (vtcol >= ncol)
vp->v_text[ncol - 1] = '$';
else if (c == '\t') {
target = ntabstop(vtcol + lbound, wp->w_bufp->b_tabw);
do {
vtpute(' ', wp);
} while (((vtcol + lbound) < target) && vtcol < ncol);
} else if (ISCTRL(c) != FALSE) {
vtpute('^', wp);
vtpute(CCHR(c), wp);
} else if (isprint(c)) {
if (vtcol >= 0)
vp->v_text[vtcol] = c;
++vtcol;
} else {
char bf[5], *cp;
snprintf(bf, sizeof(bf), "\\%o", c);
for (cp = bf; *cp != '\0'; cp++)
vtpute(*cp, wp);
}
}
void
vteeol(void)
{
struct video *vp;
vp = vscreen[vtrow];
while (vtcol < ncol)
vp->v_text[vtcol++] = ' ';
}
void
update(int modelinecolor)
{
struct line *lp;
struct mgwin *wp;
struct video *vp1;
struct video *vp2;
int c, i, j;
int hflag;
int currow, curcol;
int offs, size;
if (charswaiting())
return;
if (sgarbf) {
wp = wheadp;
while (wp != NULL) {
wp->w_rflag |= WFMODE | WFFULL;
wp = wp->w_wndp;
}
}
if (linenos || colnos) {
wp = wheadp;
while (wp != NULL) {
wp->w_rflag |= WFMODE;
wp = wp->w_wndp;
}
}
hflag = FALSE;
for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
if (wp->w_rflag == 0)
continue;
if ((wp->w_rflag & WFFRAME) == 0) {
lp = wp->w_linep;
for (i = 0; i < wp->w_ntrows; ++i) {
if (lp == wp->w_dotp)
goto out;
if (lp == wp->w_bufp->b_headp)
break;
lp = lforw(lp);
}
}
i = wp->w_frame;
if (i > 0) {
--i;
if (i >= wp->w_ntrows)
i = wp->w_ntrows - 1;
} else if (i < 0) {
i += wp->w_ntrows;
if (i < 0)
i = 0;
} else
i = wp->w_ntrows / 2;
lp = wp->w_dotp;
while (i != 0 && lback(lp) != wp->w_bufp->b_headp) {
--i;
lp = lback(lp);
}
wp->w_linep = lp;
wp->w_rflag |= WFFULL;
out:
lp = wp->w_linep;
i = wp->w_toprow;
if ((wp->w_rflag & ~WFMODE) == WFEDIT) {
while (lp != wp->w_dotp) {
++i;
lp = lforw(lp);
}
vscreen[i]->v_color = CTEXT;
vscreen[i]->v_flag |= (VFCHG | VFHBAD);
vtmove(i, 0);
for (j = 0; j < llength(lp); ++j)
vtputc(lgetc(lp, j), wp);
vteeol();
} else if ((wp->w_rflag & (WFEDIT | WFFULL)) != 0) {
hflag = TRUE;
while (i < wp->w_toprow + wp->w_ntrows) {
vscreen[i]->v_color = CTEXT;
vscreen[i]->v_flag |= (VFCHG | VFHBAD);
vtmove(i, 0);
if (lp != wp->w_bufp->b_headp) {
for (j = 0; j < llength(lp); ++j)
vtputc(lgetc(lp, j), wp);
lp = lforw(lp);
}
vteeol();
++i;
}
}
if ((wp->w_rflag & WFMODE) != 0)
modeline(wp, modelinecolor);
wp->w_rflag = 0;
wp->w_frame = 0;
}
lp = curwp->w_linep;
currow = curwp->w_toprow;
while (lp != curwp->w_dotp) {
++currow;
lp = lforw(lp);
}
curcol = 0;
i = 0;
while (i < curwp->w_doto) {
c = lgetc(lp, i++);
if (c == '\t') {
curcol = ntabstop(curcol, curwp->w_bufp->b_tabw);
} else if (ISCTRL(c) != FALSE)
curcol += 2;
else if (isprint(c))
curcol++;
else {
char bf[5];
snprintf(bf, sizeof(bf), "\\%o", c);
curcol += strlen(bf);
}
}
if (curcol >= ncol - 1) {
vscreen[currow]->v_flag |= VFEXT | VFCHG;
updext(currow, curcol);
} else
lbound = 0;
wp = wheadp;
while (wp != NULL) {
lp = wp->w_linep;
i = wp->w_toprow;
while (i < wp->w_toprow + wp->w_ntrows) {
if (vscreen[i]->v_flag & VFEXT) {
vscreen[i]->v_flag |= VFCHG;
if ((wp != curwp) || (lp != wp->w_dotp) ||
(curcol < ncol - 1)) {
vtmove(i, 0);
for (j = 0; j < llength(lp); ++j)
vtputc(lgetc(lp, j), wp);
vteeol();
vscreen[i]->v_flag &= ~VFEXT;
}
}
lp = lforw(lp);
++i;
}
if (sgarbf != FALSE)
vscreen[i]->v_flag |= VFCHG;
wp = wp->w_wndp;
}
if (sgarbf != FALSE) {
sgarbf = FALSE;
epresf = FALSE;
tttop = HUGE;
ttbot = HUGE;
tthue = CNONE;
ttmove(0, 0);
tteeop();
for (i = 0; i < nrow - 1; ++i) {
uline(i, vscreen[i], &blanks);
ucopy(vscreen[i], pscreen[i]);
}
ttmove(currow, curcol - lbound);
ttflush();
return;
}
if (hflag != FALSE) {
for (i = 0; i < nrow - 1; ++i) {
hash(vscreen[i]);
hash(pscreen[i]);
}
offs = 0;
while (offs != nrow - 1) {
vp1 = vscreen[offs];
vp2 = pscreen[offs];
if (vp1->v_color != vp2->v_color
|| vp1->v_hash != vp2->v_hash)
break;
uline(offs, vp1, vp2);
ucopy(vp1, vp2);
++offs;
}
if (offs == nrow - 1) {
ttmove(currow, curcol - lbound);
ttflush();
return;
}
size = nrow - 1;
while (size != offs) {
vp1 = vscreen[size - 1];
vp2 = pscreen[size - 1];
if (vp1->v_color != vp2->v_color
|| vp1->v_hash != vp2->v_hash)
break;
uline(size - 1, vp1, vp2);
ucopy(vp1, vp2);
--size;
}
if ((size -= offs) == 0)
panic("Illegal screen size in update");
setscores(offs, size);
traceback(offs, size, size, size);
for (i = 0; i < size; ++i)
ucopy(vscreen[offs + i], pscreen[offs + i]);
ttmove(currow, curcol - lbound);
ttflush();
return;
}
for (i = 0; i < nrow - 1; ++i) {
vp1 = vscreen[i];
vp2 = pscreen[i];
if ((vp1->v_flag & VFCHG) != 0) {
uline(i, vp1, vp2);
ucopy(vp1, vp2);
}
}
ttmove(currow, curcol - lbound);
ttflush();
}
void
ucopy(struct video *vvp, struct video *pvp)
{
vvp->v_flag &= ~VFCHG;
pvp->v_flag = vvp->v_flag;
pvp->v_hash = vvp->v_hash;
pvp->v_cost = vvp->v_cost;
pvp->v_color = vvp->v_color;
bcopy(vvp->v_text, pvp->v_text, ncol);
}
void
updext(int currow, int curcol)
{
struct line *lp;
int j;
if (ncol < 2)
return;
lbound = curcol - (curcol % (ncol >> 1)) - (ncol >> 2);
vtmove(currow, -lbound);
lp = curwp->w_dotp;
for (j = 0; j < llength(lp); ++j)
vtpute(lgetc(lp, j), curwp);
vteeol();
vscreen[currow]->v_text[0] = '$';
}
void
uline(int row, struct video *vvp, struct video *pvp)
{
char *cp1;
char *cp2;
char *cp3;
char *cp4;
char *cp5;
int nbflag;
if (vvp->v_color != pvp->v_color) {
ttmove(row, 0);
#ifdef STANDOUT_GLITCH
if (pvp->v_color != CTEXT && magic_cookie_glitch >= 0)
tteeol();
#endif
ttcolor(vvp->v_color);
#ifdef STANDOUT_GLITCH
cp1 = &vvp->v_text[magic_cookie_glitch > 0 ? magic_cookie_glitch : 0];
cp2 = &vvp->v_text[ncol - (magic_cookie_glitch >= 0 ?
(magic_cookie_glitch != 0 ? magic_cookie_glitch : 1) : 0)];
#else
cp1 = &vvp->v_text[0];
cp2 = &vvp->v_text[ncol];
#endif
while (cp1 != cp2) {
ttputc(*cp1++);
++ttcol;
}
ttcolor(CTEXT);
return;
}
cp1 = &vvp->v_text[0];
cp2 = &pvp->v_text[0];
while (cp1 != &vvp->v_text[ncol] && cp1[0] == cp2[0]) {
++cp1;
++cp2;
}
if (cp1 == &vvp->v_text[ncol])
return;
nbflag = FALSE;
cp3 = &vvp->v_text[ncol];
cp4 = &pvp->v_text[ncol];
while (cp3[-1] == cp4[-1]) {
--cp3;
--cp4;
if (cp3[0] != ' ')
nbflag = TRUE;
}
cp5 = cp3;
if (nbflag == FALSE && vvp->v_color == CTEXT) {
while (cp5 != cp1 && cp5[-1] == ' ')
--cp5;
if ((int) (cp3 - cp5) <= tceeol)
cp5 = cp3;
}
ttmove(row, (int) (cp1 - &vvp->v_text[0]));
#ifdef STANDOUT_GLITCH
if (vvp->v_color != CTEXT && magic_cookie_glitch > 0) {
if (cp1 < &vvp->v_text[magic_cookie_glitch])
cp1 = &vvp->v_text[magic_cookie_glitch];
if (cp5 > &vvp->v_text[ncol - magic_cookie_glitch])
cp5 = &vvp->v_text[ncol - magic_cookie_glitch];
} else if (magic_cookie_glitch < 0)
#endif
ttcolor(vvp->v_color);
while (cp1 != cp5) {
ttputc(*cp1++);
++ttcol;
}
if (cp5 != cp3)
tteeol();
}
void
modeline(struct mgwin *wp, int modelinecolor)
{
int n, md;
struct buffer *bp;
char sl[21];
int len;
n = wp->w_toprow + wp->w_ntrows;
vscreen[n]->v_color = modelinecolor;
vscreen[n]->v_flag |= (VFCHG | VFHBAD);
vtmove(n, 0);
bp = wp->w_bufp;
vtputc('-', wp);
vtputc('-', wp);
if ((bp->b_flag & BFREADONLY) != 0) {
vtputc('%', wp);
if ((bp->b_flag & BFCHG) != 0)
vtputc('*', wp);
else
vtputc('%', wp);
} else if ((bp->b_flag & BFCHG) != 0) {
vtputc('*', wp);
vtputc('*', wp);
} else {
vtputc('-', wp);
vtputc('-', wp);
}
vtputc('-', wp);
n = 5;
n += vtputs("Mg: ", wp);
if (bp->b_bname[0] != '\0')
n += vtputs(&(bp->b_bname[0]), wp);
while (n < 42) {
vtputc(' ', wp);
++n;
}
vtputc('(', wp);
++n;
for (md = 0; ; ) {
n += vtputs(bp->b_modes[md]->p_name, wp);
if (++md > bp->b_nmodes)
break;
vtputc('-', wp);
++n;
}
if (macrodef == TRUE)
n += vtputs("-def", wp);
if (globalwd == TRUE)
n += vtputs("-gwd", wp);
vtputc(')', wp);
++n;
if (linenos && colnos)
len = snprintf(sl, sizeof(sl), "--L%d--C%d", wp->w_dotline,
getcolpos(wp));
else if (linenos)
len = snprintf(sl, sizeof(sl), "--L%d", wp->w_dotline);
else if (colnos)
len = snprintf(sl, sizeof(sl), "--C%d", getcolpos(wp));
if ((linenos || colnos) && len < sizeof(sl) && len != -1)
n += vtputs(sl, wp);
while (n < ncol) {
vtputc('-', wp);
++n;
}
}
int
vtputs(const char *s, struct mgwin *wp)
{
int n = 0;
while (*s != '\0') {
vtputc(*s++, wp);
++n;
}
return (n);
}
void
hash(struct video *vp)
{
int i, n;
char *s;
if ((vp->v_flag & VFHBAD) != 0) {
s = &vp->v_text[ncol - 1];
for (i = ncol; i != 0; --i, --s)
if (*s != ' ')
break;
n = ncol - i;
if (n > tceeol)
n = tceeol;
vp->v_cost = i + n;
for (n = 0; i != 0; --i, --s)
n = (n << 5) + n + *s;
vp->v_hash = n;
vp->v_flag &= ~VFHBAD;
}
}
void
setscores(int offs, int size)
{
struct score *sp;
struct score *sp1;
struct video **vp, **pp;
struct video **vbase, **pbase;
int tempcost;
int bestcost;
int j, i;
vbase = &vscreen[offs - 1];
pbase = &pscreen[offs - 1];
score[0].s_itrace = 0;
score[0].s_jtrace = 0;
score[0].s_cost = 0;
sp = &score[1];
tempcost = 0;
vp = &vbase[1];
for (j = 1; j <= size; ++j) {
sp->s_itrace = 0;
sp->s_jtrace = j - 1;
tempcost += tcinsl;
tempcost += (*vp)->v_cost;
sp->s_cost = tempcost;
++vp;
++sp;
}
sp = &score[nrow];
tempcost = 0;
for (i = 1; i <= size; ++i) {
sp->s_itrace = i - 1;
sp->s_jtrace = 0;
tempcost += tcdell;
sp->s_cost = tempcost;
sp += nrow;
}
sp1 = &score[nrow + 1];
pp = &pbase[1];
for (i = 1; i <= size; ++i) {
sp = sp1;
vp = &vbase[1];
for (j = 1; j <= size; ++j) {
sp->s_itrace = i - 1;
sp->s_jtrace = j;
bestcost = (sp - nrow)->s_cost;
if (j != size)
bestcost += tcdell;
tempcost = (sp - 1)->s_cost;
tempcost += (*vp)->v_cost;
if (i != size)
tempcost += tcinsl;
if (tempcost < bestcost) {
sp->s_itrace = i;
sp->s_jtrace = j - 1;
bestcost = tempcost;
}
tempcost = (sp - nrow - 1)->s_cost;
if ((*pp)->v_color != (*vp)->v_color
|| (*pp)->v_hash != (*vp)->v_hash)
tempcost += (*vp)->v_cost;
if (tempcost < bestcost) {
sp->s_itrace = i - 1;
sp->s_jtrace = j - 1;
bestcost = tempcost;
}
sp->s_cost = bestcost;
++sp;
++vp;
}
++pp;
sp1 += nrow;
}
}
void
traceback(int offs, int size, int i, int j)
{
int itrace, jtrace;
int k;
int ninsl, ndraw, ndell;
if (i == 0 && j == 0)
return;
itrace = score[(nrow * i) + j].s_itrace;
jtrace = score[(nrow * i) + j].s_jtrace;
if (itrace == i) {
ninsl = 0;
if (i != size)
ninsl = 1;
ndraw = 1;
while (itrace != 0 || jtrace != 0) {
if (score[(nrow * itrace) + jtrace].s_itrace != itrace)
break;
jtrace = score[(nrow * itrace) + jtrace].s_jtrace;
if (i != size)
++ninsl;
++ndraw;
}
traceback(offs, size, itrace, jtrace);
if (ninsl != 0) {
ttcolor(CTEXT);
ttinsl(offs + j - ninsl, offs + size - 1, ninsl);
}
do {
k = offs + j - ndraw;
uline(k, vscreen[k], &blanks);
} while (--ndraw);
return;
}
if (jtrace == j) {
ndell = 0;
if (j != size)
ndell = 1;
while (itrace != 0 || jtrace != 0) {
if (score[(nrow * itrace) + jtrace].s_jtrace != jtrace)
break;
itrace = score[(nrow * itrace) + jtrace].s_itrace;
if (j != size)
++ndell;
}
if (ndell != 0) {
ttcolor(CTEXT);
ttdell(offs + i - ndell, offs + size - 1, ndell);
}
traceback(offs, size, itrace, jtrace);
return;
}
traceback(offs, size, itrace, jtrace);
k = offs + j - 1;
uline(k, vscreen[k], pscreen[offs + i - 1]);
}