1713 lines
51 KiB
C
1713 lines
51 KiB
C
/* ELLE - Copyright 1982, 1987 by Ken Harrenstien, SRI International
|
||
* This software is quasi-public; it may be used freely with
|
||
* like software, but may NOT be sold or made part of licensed
|
||
* products without permission of the author.
|
||
*/
|
||
/* EEDISP Redisplay and screen image routines
|
||
*/
|
||
|
||
#if 0
|
||
|
||
Note that there are several different types of "efficiency" criteria
|
||
involved with respect to display updating:
|
||
(1) Terminal speed: minimize # characters output.
|
||
(2) Program speed: minimize CPU time used.
|
||
(3) Program size: minimize code and memory usage.
|
||
(4) Program modularity: minimize "hooks" between edit/display rtns.
|
||
The current algorithms necessarily represent a compromise among all of
|
||
these objectives.
|
||
|
||
The cursor is always located at CUR_DOT in the buffer CUR_BUF
|
||
of the current window CUR_WIN. This may not be true during function
|
||
execution, but is always true at the top-level loop of command
|
||
execution and redisplay. In order to minimize update overhead, there
|
||
are various flags or variables that the edit functions can use to
|
||
communicate with "redisplay" and tell it how extensive the updates
|
||
really need to be.
|
||
|
||
The entire known screen is always represented by a linked list
|
||
of "windows"; updating the entire screen consists of separately
|
||
updating every window on the list. Windows can only be defined
|
||
horizontally (as a range of lines), and must not overlap. Each window
|
||
has a buffer associated with it; the redisplay routines are responsible
|
||
for displaying the contents of this buffer.
|
||
|
||
The lowest level data structure for the screen consists of an
|
||
array of SCR_LINE structures, one for each possible physical screen
|
||
line. Each line structure has some flags, and pointers to three different
|
||
representations of what should be on the line:
|
||
(1) SL_BOFF, SL_LEN - Defines the range of the buffer data which
|
||
this screen line should represent.
|
||
If the flag SL_EOL is set, this range ends with (and includes)
|
||
an EOL character.
|
||
(2) SL_LINE, SL_COL - Always keeps a copy of the current physical
|
||
screen line image. Each byte is a character which occupies
|
||
only one column position on the screen.
|
||
If the flag SL_CSO is set, the line is in standout mode.
|
||
(3) SL_NLIN, SL_NCOL - The desired "new" screen line image.
|
||
This is only valid if the SL_MOD flag is set for the line,
|
||
indicating that these variables are set and point to the
|
||
new image of what the screen line should be.
|
||
If the flag SL_NSO is set, the new line should be in standout
|
||
mode.
|
||
|
||
Lastly there is a variable SL_CONT, which is needed for
|
||
continuation of too-long logical lines over several physical lines. If
|
||
SL_CONT is:
|
||
0 = logical line fits entirely on the screen.
|
||
Either SL_EOL is set, or this line is ended by EOF
|
||
(end of the buffer).
|
||
1 = logical line is too long, but the last buffer char fits
|
||
entirely on this physical line. SL_EOL is never set.
|
||
>1 = logical line is too long, and the last buffer char
|
||
"overruns" the end of the physical image; that is, part of
|
||
its representation is at the end of this line, but the
|
||
rest of it is at the start of the next line. This can
|
||
only happen with "big" characters like TAB, ^A, ~^A, etc.
|
||
that need more than one column of representation.
|
||
There are SL_CONT-1 chars of overrun stored at the
|
||
end of SL_LINE (SL_NLIN if SL_MOD is set).
|
||
SL_EOL is never set.
|
||
|
||
Note that if a line contains any overrun, and the next line is also
|
||
part of the same window, the next line''s screen image will start with
|
||
the SL_CONT-1 chars of overrun, rather than with the representation of
|
||
that line''s first buffer char.
|
||
|
||
The "EOL" character on Unix systems is normally the new-line
|
||
character '\n' (ASCII LF). However, on other systems EOL may be
|
||
indicated by a two-character CR-LF sequence, with either CR or LF alone
|
||
considered to be "stray". For this reason, the buffer flag B_EOLCRLF
|
||
exists to control handling and display of EOLs. If the flag is off,
|
||
the EOL mode is LF, and there are no problems of splitting up characters.
|
||
If the flag is on, however, the EOL mode is CRLF and the following rules
|
||
hold:
|
||
EOL is the sequence CR-LF only.
|
||
LF without preceding CR is a "stray" LF, displayed as ^J.
|
||
CR without following LF is a "stray" CR, displayed as ^M.
|
||
Stray LFs and CRs do not terminate a logical line.
|
||
"End of Line" as a position is the dot just before the CR of a CR-LF.
|
||
"Beg of Line" as a position is the dot just after the LF of a CR-LF.
|
||
If the current dot is between a CR and LF, it is positioned at
|
||
the beginning of the physical screen line.
|
||
|
||
|
||
SL_LINE and SL_COL are always accurate at every stage of processing.
|
||
The other variables are accurate only after fix_wind has been called
|
||
to "fix up" the line structures within a window. If either
|
||
RD_WINRES or RD_TMOD is set, none of these "other variables" should
|
||
be depended on. Any functions which are screen-relative (d_ type)
|
||
must be sure that fix_wind is called if necessary, and must give
|
||
preference to the "new" representation in SL_NLINE and SL_NCOL if
|
||
SL_MOD is set.
|
||
|
||
The flag RD_UPDWIN will be set by fix_wind if any lines have been
|
||
modified. Because fix_wind does not perform any actual display update,
|
||
it is possible for functions to continue operating on the buffer and
|
||
screen image without requiring that changes be displayed until there is
|
||
nothing else left to do. The routine upd_wind performs the actual
|
||
terminal I/O necessary to update all the screen lines which have SL_MOD
|
||
set. Although the process of updating each line is currently
|
||
non-interruptible, it is possible for upd_wind to interrupt itself
|
||
between line updates if it detects that user input has happened, and it will
|
||
return with the window only partially updated. The screen image state
|
||
will be completely consistent, however, and the RD_UPDWIN flag will
|
||
remain set.
|
||
|
||
Communication between the editing functions and the redisplay routines
|
||
is limited as much as possible to the flags in the global RD_TYPE.
|
||
Each window has its own copy of these flags in W_REDP, so that if
|
||
windows are changed, the update hints for that window will be
|
||
preserved. The flags that can be set are listed below. Those marked
|
||
with "*" are global in nature; all others apply only within a single
|
||
window (normally the current window).
|
||
|
||
* RD_SCREEN - Total refresh. Clears entire screen and redisplays all
|
||
windows.
|
||
* RD_MODE - Mode line has changed, update it.
|
||
* RD_CHKALL - Check ALL windows for any redisplay flags, and perform
|
||
any updates necessary. Otherwise only the current (or specified)
|
||
window flags are checked.
|
||
* RD_WINDS - Updates all windows. Like RD_WINRES applied to all windows.
|
||
RD_WINRES - Update window (assume completely changed).
|
||
RD_TMOD - Text changed in this window. The range of changes is
|
||
specified by W_BMOD and W_EMOD in combination with W_OLDZ.
|
||
Redisplay checking will limit itself to this range.
|
||
These vars are set by buf_tmod in the main command loop, and
|
||
reset by fix_wind when the window is fixed up.
|
||
RD_MOVE - Cursor has moved within current window; may have moved outside
|
||
the window. W_DOT or CUR_DOT specifies where it should be.
|
||
RD_ILIN - Hint: Line insert done. Currently no function sets this.
|
||
RD_DLIN - Hint: Line delete done. Currently no function sets this.
|
||
|
||
Internal flags:
|
||
RD_UPDWIN - Window needs updating. Used by fix_wind and upd_wind only.
|
||
Set when window has been "fixed up" and at least one screen
|
||
line was modified.
|
||
RD_FIXWIN - Supposed to mean window needs fixing (via call to fix_wind).
|
||
Not really used.
|
||
|
||
Not implemented, may never be, but comments retained:
|
||
RD_WINCLR - Clear window (not entire screen)
|
||
RD_NEWWIN - Window has moved. (not needed? Random stuff here)
|
||
a. to follow cursor; redisplay selects a new TOPLDOT.
|
||
b. randomly; new TOPLDOT furnished, use unless cursor out (then a).
|
||
c. find new TOPLDOT as directed (move up/down N screen lines)
|
||
For now, assume that (c) doesn''t apply (ie C-V uses (b) and sets
|
||
TOPLDOT itself). So fix_wind selects new one only if cursor
|
||
won''t fit. topldot takes precedence over sl_boff.
|
||
|
||
#endif /*COMMENT*/
|
||
|
||
/* Declarations and stuff */
|
||
|
||
#include "elle.h"
|
||
|
||
static int sctr();
|
||
|
||
int trm_mode; /* 0 = TTY in normal, non-edit mode.
|
||
* 1 = TTY in edit mode.
|
||
* -1 = TTY detached (hung up).
|
||
* This flag is only used by the 3 routines below,
|
||
* plus hup_exit.
|
||
*/
|
||
|
||
/* REDP_INIT() - Called once-only at startup to initialize redisplay
|
||
* and terminal
|
||
*/
|
||
redp_init ()
|
||
{
|
||
trm_mode = 0; /* Ensure flag says not in edit mode */
|
||
ts_init(); /* Get sys term info, set up stuff */
|
||
if (trm_ospeed == 0) /* Default speed to 9600 if unknown */
|
||
trm_ospeed = 13;
|
||
t_init(); /* Identify term type, set term-dep stuff */
|
||
set_scr(); /* Set up software screen image */
|
||
set_tty(); /* Enter editing mode! */
|
||
redp(RD_SCREEN|RD_MODE); /* Force full re-display, new mode line */
|
||
}
|
||
|
||
/* SET_TTY() - Set up terminal modes for editing */
|
||
|
||
set_tty()
|
||
{ if(trm_mode) return; /* Ignore if detached or in edit mode */
|
||
trm_mode++;
|
||
ts_enter(); /* Set up system's ideas about terminal */
|
||
t_enter(); /* Set terminal up for editing */
|
||
}
|
||
|
||
/* CLEAN_EXIT() - Restore original terminal modes.
|
||
* Returns previous state.
|
||
*/
|
||
clean_exit ()
|
||
{ register int prevstate = trm_mode;
|
||
|
||
if(prevstate > 0) /* Ignore unless in editing mode */
|
||
{ trm_mode = 0;
|
||
t_curpos(scr_ht-1, 0); /* Go to screen bottom */
|
||
t_exit(); /* Clean up the terminal */
|
||
tbufls(); /* Force out all buffered output */
|
||
ts_exit(); /* Restore system's old term state */
|
||
#if ! IMAGEN
|
||
writez(1,"\n"); /* Get fresh line using OS output */
|
||
#endif /*-IMAGEN*/
|
||
}
|
||
return prevstate;
|
||
}
|
||
|
||
/* SET_SCR() - Allocate screen image, set up screenline pointer table */
|
||
|
||
set_scr()
|
||
{ register struct scr_line **scrp, *stp;
|
||
register scrsiz;
|
||
char *sbuf;
|
||
|
||
scr_wd0 = scr_wid - 1;
|
||
scrsiz = scr_ht*(scr_wid+MAXCHAR);
|
||
if( scr_ht > MAXHT || scr_wid > MAXLINE)
|
||
{ clean_exit();
|
||
printf("ELLE: %dx%d screen too big\n",scr_ht,scr_wid);
|
||
exit(1);
|
||
}
|
||
if((stp = (struct scr_line *) calloc(scr_ht*sizeof(struct scr_line)
|
||
+ scrsiz*2,1)) == 0)
|
||
{ clean_exit();
|
||
printf("ELLE: not enough memory\n");
|
||
exit(1);
|
||
}
|
||
sbuf = (char *)stp + scr_ht*sizeof(struct scr_line);
|
||
for(scrp = &scr[0]; scrp < &scr[scr_ht]; sbuf += scr_wid+MAXCHAR)
|
||
{ stp->sl_line = sbuf;
|
||
stp->sl_nlin = sbuf + scrsiz;
|
||
*scrp++ = stp++;
|
||
}
|
||
}
|
||
|
||
/* REDISPLAY()
|
||
* Main function of redisplay routines. Called every time ELLE
|
||
* forces update of the terminal screen. "rd_type" contains hints
|
||
* as to what has changed or needs updating, to avoid wasting time
|
||
* on things which don't need attention.
|
||
*/
|
||
redisplay ()
|
||
{ register struct window *w;
|
||
register i;
|
||
struct window *make_mode();
|
||
|
||
w = cur_win;
|
||
w->w_redp |= rd_type&RDS_WINFLGS; /* Set cur_win's flags */
|
||
rd_type &= ~RDS_WINFLGS; /* Leave only globals */
|
||
|
||
if (rd_type & RD_SCREEN) /* Clear and refresh? */
|
||
{
|
||
t_clear (); /* Clear the screen */
|
||
for(i = scr_ht; --i >= 0;) /* Clear screen image */
|
||
scr[i]->sl_col = 0;
|
||
if(w != ask_win) /* If not in ask-window */
|
||
{ chg_win(ask_win);
|
||
e_reset(); /* Then flush its contents */
|
||
chg_win(w);
|
||
}
|
||
redp(RD_WINDS); /* Update all windows */
|
||
rd_type &= ~RD_SCREEN; /* If redisplay is interrupted, */
|
||
/* don't do it all over again */
|
||
}
|
||
if (rd_type & RD_WINDS) /* Update all windows? */
|
||
{ redp(RD_CHKALL);
|
||
for (w = win_head; w; w = w -> w_next) /* For each win */
|
||
w->w_redp |= RD_WINRES;
|
||
rd_type &= ~RD_WINDS;
|
||
}
|
||
if (rd_type & RD_CHKALL) /* Check all windows for changes? */
|
||
{ for (w = win_head; w; w = w->w_next) /* For each win */
|
||
if(!(w->w_flags&W_MODE)) /* skip mode wins */
|
||
if(w->w_redp && upd_wind(w))
|
||
return; /* May be interrupted */
|
||
|
||
}
|
||
|
||
/* See if ask-window needs updating (to avoid RD_CHKALL in SAY) */
|
||
if((w = ask_win)->w_redp && upd_wind(w))
|
||
return; /* May be interrupted */
|
||
|
||
/* Check current window for changes */
|
||
if((w = cur_win)->w_redp && upd_wind(w))
|
||
return; /* May be interrupted */
|
||
|
||
/* Now update mode line(s) if necessary */
|
||
if(rd_type&RD_MODE)
|
||
{
|
||
fupd_wind(w = make_mode(user_win));
|
||
#if FX_2MODEWINDS
|
||
if (sep_win /* If 2 windows */
|
||
&& (sep_win->w_flags&W_MODE) /* and 2 mode windows */
|
||
&& (sep_win->w_redp || mode_win->w_redp)) /* Check */
|
||
fupd_wind(make_mode(oth_win)); /* Must update both */
|
||
#endif
|
||
}
|
||
|
||
/* Finally, leave cursor in right place. */
|
||
if(upd_curs(cur_dot)==0) /* If something screwed up, */
|
||
errbarf("Cursor out of window"); /* Complain, */
|
||
/* and leave cursor at bot */
|
||
rd_type = 0;
|
||
tbufls(); /* Force out all terminal output */
|
||
}
|
||
|
||
fupd_wind(w) /* Force window update */
|
||
register struct window *w;
|
||
{
|
||
w->w_redp |= RD_WINRES;
|
||
if(fix_wind(w))
|
||
upd_wind(w);
|
||
}
|
||
|
||
/*
|
||
* UPD_CURS
|
||
* Move screen cursor to position of specified dot within current window.
|
||
* Returns 0 if dot was not within window (and cursor was not moved),
|
||
* otherwise returns 1 for success.
|
||
*/
|
||
upd_curs(adot)
|
||
chroff adot;
|
||
{ register struct scr_line *s;
|
||
register int y, x;
|
||
chroff savdot;
|
||
|
||
if((y = d_line(adot)) < 0)
|
||
return(0); /* Fail, not within window */
|
||
s = scr[y]; /* Now have line that dot is on */
|
||
|
||
/* Get proper offset for any continuation chars from prev line */
|
||
if(y > cur_win->w_pos)
|
||
{ if((x = scr[y-1]->sl_cont) > 0)
|
||
x--;
|
||
}
|
||
else x = 0;
|
||
|
||
savdot = e_dot();
|
||
e_go(s->sl_boff);
|
||
if((x = d_ncols((int)(adot - s->sl_boff),x)) < 0)
|
||
{ /* If lost, assume it's because we are just after a char
|
||
** which has its representation continued onto next line.
|
||
** Move cursor to end of that continuation.
|
||
** d_line should have ensured that this is safe, but
|
||
** we double-check just to make sure.
|
||
*/
|
||
if((x = s->sl_cont) > 0) /* Set X to end of cont */
|
||
--x;
|
||
/* and on next line down */
|
||
if(++y >= (cur_win->w_pos + cur_win->w_ht))
|
||
{ e_go(savdot); /* Failed, below window */
|
||
return(0);
|
||
}
|
||
}
|
||
e_go(savdot);
|
||
t_move(y, x); /* Move cursor cleverly */
|
||
return(1); /* Return success! */
|
||
}
|
||
|
||
/* Return line # for given dot, -1 if out of current window */
|
||
d_line(cdot)
|
||
chroff cdot;
|
||
{ register struct scr_line *s;
|
||
register struct window *w;
|
||
register int i;
|
||
chroff savdot;
|
||
int bot;
|
||
|
||
w = cur_win;
|
||
i = w->w_pos;
|
||
bot = i + w->w_ht;
|
||
for(; i < bot; i++)
|
||
{ s = scr[i];
|
||
if(cdot <= s->sl_boff)
|
||
goto gotl;
|
||
}
|
||
/* End of window, repeat test specially for last line */
|
||
savdot = s->sl_boff + (chroff)s->sl_len;
|
||
if(cdot > savdot) /* If past last char of last line */
|
||
return(-1); /* then clearly outside */
|
||
--i; /* Make i match s (bottom line) */
|
||
if(savdot != cdot) /* If not exactly at end */
|
||
return(i); /* Then we're inside for sure */
|
||
goto linbet;
|
||
|
||
gotl: if(s->sl_boff != cdot) /* Are we on line boundary? */
|
||
{ if(i <= w->w_pos) /* No, off top of window? */
|
||
return(-1); /* Above top, out for sure */
|
||
return(--i);
|
||
}
|
||
|
||
/* Here, dot is exactly on line boundary, have to decide which
|
||
* line it really belongs to.
|
||
* Get S = pointer to line which cursor is at the end of.
|
||
*/
|
||
if(i <= w->w_pos) /* Quick chk of trivial case, empty buffer */
|
||
return(i);
|
||
s = scr[--i];
|
||
linbet:
|
||
if((s->sl_flg&SL_EOL) /* If line has LF */
|
||
|| (s->sl_cont > 1)) /* or a continued char */
|
||
if(++i >= bot) /* Then cursor is on next line */
|
||
return(-1);
|
||
return(i);
|
||
}
|
||
|
||
/* D_NCOLS - auxiliary for UPD_CURS. (also called by indtion() in EEFD)
|
||
** We are positioned at a place in the current buffer corresponding to
|
||
** the beginning of the screen line, and given:
|
||
** lcnt - # of chars in buffer to move forward over
|
||
** ccol - current column position
|
||
** Returns the new column position. There are some special cases:
|
||
** Hits EOF: returns normally (new column position)
|
||
** Hits EOL: returns -1
|
||
** Position is past end of screen: returns -1
|
||
** The buffer position has changed, but this is irrelevant as upd_curs
|
||
** restores it just after the call.
|
||
*/
|
||
d_ncols(lcnt, ccol)
|
||
int lcnt;
|
||
int ccol;
|
||
{ register int col, i;
|
||
register SBBUF *sb;
|
||
int c;
|
||
char tmp[MAXCHAR*2]; /* MAXCHAR is enough, but *2 just in case */
|
||
|
||
col = ccol;
|
||
sb = (SBBUF *) cur_buf;
|
||
if((i = lcnt) > 0)
|
||
do { if((c = sb_getc(sb)) == EOF)
|
||
break;
|
||
/* Check to see if we've run into an EOL */
|
||
#if FX_EOLMODE
|
||
if(c == CR)
|
||
{ if(eolcrlf(sb))
|
||
{ if((c = sb_getc(sb)) == LF) /* EOL? */
|
||
/* Real EOL. Fail unless point
|
||
** is between CR and LF, in which case
|
||
** we return 0 (left margin).
|
||
*/
|
||
return (i==1 ? 0 : -1);
|
||
/* Stray CR, back up & fall thru */
|
||
if(c != EOF)
|
||
sb_backc(sb);
|
||
c = CR;
|
||
}
|
||
} else if (c == LF)
|
||
{ if(!eolcrlf(sb)) /* Real EOL? */
|
||
return -1; /* Yes, fail */
|
||
/* If EOL mode is CRLF then hitting a LF
|
||
** can only happen for stray LFs (the
|
||
** previous check for CR takes care of
|
||
** CRLFs, and we never start scanning
|
||
** from the middle of a CRLF.
|
||
** Drop thru to show stray LF.
|
||
*/
|
||
}
|
||
#else
|
||
if(c == LF)
|
||
return(-1);
|
||
#endif /*-FX_EOLMODE*/
|
||
col += sctr(c, tmp, col);
|
||
} while(--i);
|
||
if(col > scr_wd0)
|
||
return(-1);
|
||
return(col);
|
||
}
|
||
|
||
/* D_LUPD - called from command level to completely redisplay a
|
||
* specific line on the screen.
|
||
*/
|
||
d_lupd(w, idx)
|
||
struct window *w; /* Window this line belongs to, if known */
|
||
int idx;
|
||
{ t_curpos(idx, 0);
|
||
t_docleol(); /* Zap physical screen line */
|
||
scr[idx]->sl_col = 0; /* Reflect it on phys screen image */
|
||
if(w) /* Mark window for updating */
|
||
w->w_redp |= RD_WINRES;
|
||
else redp(RD_WINDS); /* No window given, assume global */
|
||
redp(RD_MOVE); /* Cursor has moved */
|
||
}
|
||
|
||
/* Clear a window completely the "quickest possible way" */
|
||
clear_wind(w)
|
||
register struct window *w;
|
||
{
|
||
register int i = w->w_pos; /* Top line of window */
|
||
register int bot = i + w->w_ht; /* Bottom line (plus 1) of window */
|
||
|
||
for ( ; i < bot; ++i)
|
||
d_lupd(w, i); /* Zap that line */
|
||
}
|
||
|
||
/* FIX_WIND - Sets up window screen image. Does not generate any
|
||
* terminal output, but completely specifies what the new screen
|
||
* image should look like.
|
||
* Only the following 4 flags (lumped together in RDS_DOFIX)
|
||
* provoke fix_wind to do something:
|
||
* RD_MOVE - cursor has moved, must make sure still within
|
||
* window, and select new one if not.
|
||
* RD_TMOD - Text has been changed somewhere.
|
||
* RD_FIXWIN - Something requested that fix_wind fix things.
|
||
* Normally this is set when a new w_topldot is set.
|
||
* RD_WINRES - Window needs to be completely regenerated.
|
||
* Results:
|
||
* Verifies that the current dot for the window (w_dot) exists.
|
||
* If it is past the end of buffer, it is reset to EOB, and if this is
|
||
* the current window, also updates cur_dot. Otherwise, w_dot is never
|
||
* adjusted; it is fix_wind's responsibility to make sure that the window
|
||
* displays w_dot.
|
||
* Verifies that current w_topldot setting will result in cursor
|
||
* (specified by w_dot) appearing within window. If not, resets w_topldot
|
||
* to an appropriate value (1/3 of way down from top, unless
|
||
* moving up in which case 1/3 of way up from bottom).
|
||
* Makes sure that sl_boff, sl_len, sl_flg, and sl_cont
|
||
* are set properly for all lines in window. SL_MOD is set
|
||
* for any lines requiring screen updates; these lines
|
||
* also have sl_nlin and sl_ncol properly set.
|
||
* Note that sl_line and sl_col are NOT updated or changed, because
|
||
* the physical screen has not been altered!
|
||
*
|
||
* Returns 0 if no physical screen updates are needed (other than
|
||
* cursor moving and mode line updating).
|
||
* Returns 1 if screen updates are needed; RD_UPDWIN is set in w_redp,
|
||
* indicating that UPD_WIND should be called.
|
||
*/
|
||
|
||
fix_wind (win)
|
||
struct window *win;
|
||
{
|
||
register struct window *w;
|
||
register int i;
|
||
register struct scr_line *s;
|
||
chroff cdot, bdelta, updot, sdot, newz;
|
||
chroff savdot;
|
||
struct buffer *savbuf;
|
||
int bot, nlmod, savi, contf, ocontf, randomflg;
|
||
int newpct;
|
||
|
||
if(!(w = win))
|
||
return(0);
|
||
if(!(w->w_redp&RDS_DOFIX)) /* Anything we need to do? */
|
||
return(0); /* Nope, just ignore */
|
||
|
||
/* Find current dot for this window, and set up other stuff */
|
||
cdot = (w == cur_win) ? cur_dot : w->w_dot;
|
||
bot = w->w_pos + w->w_ht;
|
||
savbuf = cur_buf;
|
||
cur_buf = w->w_buf;
|
||
savdot = e_dot();
|
||
nlmod = 0; /* No screen image changes so far */
|
||
|
||
/* Dot (ie cursor) is before current top? If so, must move
|
||
* backwards to find a new topldot. Note also that buffer may have
|
||
* changed so that either cdot or topldot points past EOF.
|
||
*/
|
||
if(w->w_topldot > cdot)
|
||
{ /* Yes, must search backwards scrht/3 screen lines */
|
||
/* from cdot in order to find topldot. */
|
||
/* Don't bother updating scr stuff beforehand since we'll
|
||
* have to revise everything anyway and can do it on the fly.
|
||
*/
|
||
i = (ev_mvpct * w->w_ht) / 100;
|
||
goto skipdn;
|
||
|
||
finddn: i = ((100 - ev_mvpct) * w->w_ht) / 100;
|
||
skipdn: if(i <= 0) i = 1; /* Ensure # is reasonable */
|
||
else if(i >= w->w_ht) i = w->w_ht-1;
|
||
e_go(cdot); /* Start here (may normalize to EOF)*/
|
||
d_backup(i ? i : 1); /* Try to back up cleverly */
|
||
w->w_topldot = e_dot();
|
||
randomflg = 0; /* We have some idea where we are */
|
||
fixall: /* Entry point for later recheck, with randomflg==1 */
|
||
newz = e_blen();
|
||
if(newz < cdot) /* Part of buf may have gone away */
|
||
{ /* So normalize dot to EOF */
|
||
w->w_dot = cdot = newz;
|
||
if(w == cur_win) /* Special check for fixing */
|
||
cur_dot = newz; /* up cur_dot too! */
|
||
goto finddn; /* and get a new top-of-window loc */
|
||
}
|
||
retry: i = w->w_pos;
|
||
contf = 0;
|
||
s = 0;
|
||
for(; i < bot; i++)
|
||
{ nlmod++;
|
||
fix_line(scr[i], s); /* s = 0 the first time */
|
||
s = scr[i];
|
||
#if FX_SOWIND
|
||
if(w->w_flags & W_STANDOUT)
|
||
s->sl_flg |= SL_NSO;
|
||
else s->sl_flg &= ~SL_NSO;
|
||
#endif
|
||
}
|
||
if(inwinp(w,cdot)) /* Ensure in window */
|
||
goto mdone;
|
||
if(randomflg) /* If jumped randomly, */
|
||
{ i = (ev_nwpct * w->w_ht) / 100;
|
||
goto skipdn; /* Try to select new window */
|
||
}
|
||
|
||
/* We tried to back up and went too far. */
|
||
if(cdot < w->w_topldot) /* Verify place is ahead */
|
||
{ errbarf("fix_wind failed"); /* Didn't back up?? */
|
||
goto finddn;
|
||
}
|
||
/* Move down one line and try again */
|
||
if(w->w_ht > 1)
|
||
w->w_topldot = scr[w->w_pos+1]->sl_boff;
|
||
else
|
||
{ s = scr[w->w_pos];
|
||
w->w_topldot = s->sl_boff + s->sl_len;
|
||
}
|
||
e_go(w->w_topldot);
|
||
goto retry;
|
||
}
|
||
|
||
/* At some future point, could separate out processing for
|
||
* RD_WINRES and RD_FIXWIN. Latter flag implies only w_topldot
|
||
* has changed (new window selected). Former implies whole
|
||
* buffer has been munged, and everything is completely redone.
|
||
*/
|
||
if(w->w_redp&(RD_WINRES|RD_FIXWIN)) /* If re-figuring whole window */
|
||
{ e_go(w->w_topldot); /* Start here, and */
|
||
randomflg = 1; /* set up flag saying random jump */
|
||
goto fixall; /* and go crunch all lines. */
|
||
}
|
||
if((w->w_redp&RD_TMOD)==0) /* If claims no text mods, */
|
||
{ if(inwinp(w,cdot)==0) /* Just verify cursor loc. */
|
||
goto finddn; /* Sigh.... */
|
||
newz = w->w_oldz; /* Win, set up for exit. */
|
||
goto done;
|
||
}
|
||
/* Here only when RD_TMOD is set, indicating changes are
|
||
* between range variables.
|
||
*/
|
||
/* Find upper bound of any mods. This is a little gross in the
|
||
* speed dept and some faster way should perhaps be devised.
|
||
* In particular the main loop should incrementally keep track of
|
||
* buffer size, and should set a flag RD_TEXT if anything has
|
||
* actually been changed. Edit routines should have lots of
|
||
* flags available to tell main loop more precisely what they did,
|
||
* so main loop can take care of updating b/emod and stuff.
|
||
*/
|
||
if((newz = e_blen()) == 0)
|
||
goto finddn; /* Ensure blank window is cleared */
|
||
bdelta = newz - w->w_oldz;
|
||
if((updot = newz) > w->w_emod)
|
||
updot -= w->w_emod;
|
||
if(bdelta == 0 && (updot == w->w_bmod))
|
||
goto inwinq;
|
||
|
||
/* Could also check for updot < w_topldot (changes above win)
|
||
* or sl_boff+sl_len < w_bmod (changes below win) but those
|
||
* cases are probably pretty rare.
|
||
*/
|
||
/* First find line where changes start */
|
||
for(i = w->w_pos; i < bot; i++)
|
||
{ s = scr[i];
|
||
if(w->w_bmod <= s->sl_boff) /* Changes prior to this? */
|
||
break;
|
||
}
|
||
if(i >= bot) /* Test last line specially */
|
||
{ if(w->w_bmod > (s->sl_boff + (chroff)s->sl_len))
|
||
goto inwinq; /* Outside window */
|
||
/* Last line changed, hack it */
|
||
}
|
||
if(i > w->w_pos /* If we have a prev line */
|
||
&& (s->sl_len == 0 /* and we're at EOF, */
|
||
|| w->w_bmod != s->sl_boff /* or not at start of line */
|
||
|| scr[i-1]->sl_cont)) /* or prev line is continuation */
|
||
s = scr[--i]; /* then it's prev line we want */
|
||
|
||
/* I has index for screen line changes begin on; S has ptr.
|
||
* This piece of code handles case where buffer has been modified
|
||
* starting at BMOD, and BDELTA chars have been inserted/deleted;
|
||
* range of changes ends at UPDOT.
|
||
*/
|
||
savi = i;
|
||
while(++i < bot)
|
||
scr[i]->sl_boff += bdelta;
|
||
i = savi;
|
||
|
||
/* Now start with 1st changed line and start figuring new line
|
||
* lengths. Stop when hit end, or past updot and boff is correct
|
||
* for start of line.
|
||
*/
|
||
/* can improve this by jumping out when past emod, and testing for
|
||
* an EOL - then know stuff has to match someplace, so look for that.
|
||
* could then simply update lengths or something?
|
||
*/
|
||
if(i > w->w_pos) /* Find # cols already there from prev line*/
|
||
contf = scr[i-1]->sl_cont;
|
||
else contf = 0;
|
||
ocontf = 1; /* Fake it so always update 1st line*/
|
||
e_go(sdot = s->sl_boff);
|
||
for(; i < bot; i++)
|
||
{ s = scr[i];
|
||
if(updot <= sdot /* If past changed stuff */
|
||
&& sdot == s->sl_boff /* and locs are lined up */
|
||
&& contf == 0 /* and previous line clean */
|
||
&& ocontf == 0) /* (both old and new images) */
|
||
break; /* Then done. */
|
||
nlmod++;
|
||
ocontf = s->sl_cont; /* Save old-image contf value */
|
||
fix_line(s, (i > w->w_pos) ? scr[i-1] : 0);
|
||
#if FX_SOWIND
|
||
if(w->w_flags & W_STANDOUT)
|
||
s->sl_flg |= SL_NSO;
|
||
else s->sl_flg &= ~SL_NSO;
|
||
#endif
|
||
sdot = e_dot();
|
||
contf = s->sl_cont; /* Get new-image contf value */
|
||
}
|
||
if(inwinp(w,cdot)) /* OK, screen fixed, see if cursor inside */
|
||
goto mdone;
|
||
goto finddn;
|
||
|
||
/* Test if still in window and dispatch appropriately */
|
||
inwinq: if(inwinp(w,cdot))
|
||
goto done;
|
||
else goto finddn;
|
||
|
||
/* Come here when done, after mods made to window.
|
||
* Calculate new %-of-buffer position for window's view, and
|
||
* see if it's changed from current %.
|
||
*/
|
||
mdone: if(w != cur_win) goto done; /* If not current window, ignore */
|
||
s = scr[bot-1];
|
||
if((s->sl_boff + (chroff)s->sl_len) >= newz)
|
||
if(w->w_topldot) newpct = 150; /* BOT */
|
||
else newpct = 200; /* ALL */
|
||
else if(w->w_topldot == 0)
|
||
newpct = -1; /* TOP */
|
||
else /* NOTE: This won't work if topldot is huge */
|
||
newpct = (w->w_topldot*100)/newz; /* nn% */
|
||
if(newpct != w->w_pct) /* OK, now compare with old % */
|
||
{ w->w_pct = newpct; /* Different, must set and */
|
||
redp(RD_MODE); /* invoke redisplay of mode line! */
|
||
}
|
||
|
||
done: w->w_bmod = -1; /* To indicate vars not set */
|
||
w->w_oldz = newz;
|
||
w->w_redp &= ~RDS_DOFIX; /* Clear flags that invoked us */
|
||
if(nlmod)
|
||
w->w_redp |= RD_UPDWIN; /* Say stuff to be updated */
|
||
e_go(savdot);
|
||
cur_buf = savbuf;
|
||
return(nlmod);
|
||
}
|
||
|
||
/* INWINP - Returns true if given dot is inside given window.
|
||
*/
|
||
inwinp(win,cdot)
|
||
struct window *win;
|
||
chroff cdot;
|
||
{ register struct scr_line *s;
|
||
register struct window *w;
|
||
chroff sdot;
|
||
|
||
w = win;
|
||
if(cdot < w->w_topldot)
|
||
return(0);
|
||
s = scr[(w->w_pos + w->w_ht) - 1];
|
||
sdot = s->sl_boff + (chroff)s->sl_len;
|
||
if(cdot < sdot)
|
||
return(1); /* Yup, inside window. */
|
||
if(cdot > sdot)
|
||
return(0);
|
||
|
||
/* Dot is exactly at end of window, must check further. */
|
||
if(s->sl_len /* If line exists, */
|
||
&& ((s->sl_flg&SL_EOL) /* and ends in LF, */
|
||
|| s->sl_cont > 1)) /* or sl_cont > 1, lose. */
|
||
return(0);
|
||
return(1); /* Else inside, win. */
|
||
}
|
||
|
||
/*
|
||
* UPD_WIND
|
||
* If argument 0, assumes cur_win and DOESN'T interrupt if input
|
||
* detected.
|
||
*/
|
||
|
||
upd_wind(win)
|
||
struct window *win;
|
||
{ register int i, n;
|
||
register struct scr_line *s;
|
||
struct window *w;
|
||
int top, bot, dspf, num, noicost, nodcost, iline, dline;
|
||
#if FX_SOWIND
|
||
int oldso;
|
||
#endif
|
||
#if IMAGEN
|
||
int origdspf;
|
||
char redpmsg[128];
|
||
#endif /*IMAGEN*/
|
||
|
||
if((w=win)==0)
|
||
w = cur_win;
|
||
dspf = w->w_redp; /* Get update flags for window */
|
||
#if IMAGEN
|
||
origdspf = dspf;
|
||
#endif /*IMAGEN*/
|
||
if(w == cur_win) /* If updating current window, */
|
||
dspf |= rd_type; /* merge in global flags */
|
||
if((dspf &= RDS_WINFLGS) == 0) /* Well, it might happen sometimes */
|
||
goto zdone;
|
||
w->w_redp = dspf;
|
||
if(dspf&(RD_WINRES|RD_TMOD|RD_MOVE|RD_FIXWIN))
|
||
{ fix_wind(w); /* May set some flags, so */
|
||
dspf = w->w_redp; /* get them back... */
|
||
}
|
||
if((dspf&RD_UPDWIN)==0) /* Must ask for update! */
|
||
goto zdone;
|
||
#if IMAGEN
|
||
if (dbg_redp)
|
||
{ sprintf(redpmsg,
|
||
"buffer: %14s, rd_type: %06o, w_redp: %06o, dspf: %06o",
|
||
w->w_buf->b_name, rd_type, origdspf, dspf);
|
||
barf2(redpmsg);
|
||
}
|
||
#endif /*IMAGEN*/
|
||
|
||
/* Assume screen structure set up by FIX_WIND, just go
|
||
* effect change for every line modified.
|
||
*/
|
||
#if FX_SOWIND
|
||
oldso = t_dostandout((w->w_flags&W_STANDOUT)? 1:0);
|
||
#endif
|
||
top = w->w_pos;
|
||
bot = top + w->w_ht;
|
||
for(i = top; i < bot; ++i)
|
||
if((s = scr[i])->sl_flg&SL_MOD)
|
||
{ if(win && tinwait()) /* If OK, stop if any chars typed */
|
||
{ tbufls();
|
||
w->w_redp = dspf;
|
||
#if FX_SOWIND
|
||
t_dostandout(oldso);
|
||
#endif
|
||
return(1); /* Return immediately, say int'd */
|
||
}
|
||
if(slineq(s,s)) /* Compare old with new */
|
||
goto ldone; /* Lines equal, no update needed */
|
||
|
||
#if IMAGEN
|
||
/* If hint says redo entirely */
|
||
if (dspf & RD_REDO)
|
||
{ s->sl_flg |= SL_REDO; /* Do "fast update" */
|
||
goto nodel; /* Just go update line */
|
||
}
|
||
#endif /*IMAGEN*/
|
||
if((trm_flags&TF_IDLIN)==0)
|
||
goto nodel; /* Just go update line */
|
||
|
||
|
||
/* Check for I/D line. If no hints exist, check for both
|
||
* insert and delete.
|
||
*/
|
||
if((dspf&(RD_ILIN|RD_DLIN))==0)
|
||
dspf |= RD_ILIN|RD_DLIN;
|
||
noicost = 0;
|
||
nodcost = 0;
|
||
|
||
/* Check for insert line. See if the current old screen
|
||
* line is duplicated among any of the new lines which
|
||
* follow it. If a match is found, keep looking and add
|
||
* up the number of characters in the matching lines.
|
||
*/
|
||
if(dspf&RD_ILIN)
|
||
{
|
||
/* See if this old screen line is needed elsewhere */
|
||
if(s->sl_col == 0) /* Ignore if blank */
|
||
goto noins;
|
||
|
||
for(n = i+1; n < bot; n++)
|
||
{ if((scr[n]->sl_flg&SL_MOD)==0)
|
||
break;
|
||
if(slineq(s, scr[n])) /* Old, new */
|
||
{ if(!noicost) iline = n; /* 1st time */
|
||
noicost += s->sl_col;
|
||
s++;
|
||
}
|
||
else if(noicost) break;
|
||
}
|
||
if(!noicost) /* If no match, forget it */
|
||
goto noins; /* S will not have changed. */
|
||
s = scr[i]; /* Restore S */
|
||
n = iline; /* Have matches, get index
|
||
* of first matching line */
|
||
|
||
/* Heuristic to decide whether to perform
|
||
* insert-line operation. Kind of stupid, but
|
||
* good enough for now.
|
||
*/
|
||
num = (n-i)*(tvc_ldn+tvc_lin) + (tvc_li + tvc_ld);
|
||
if((n-i) >= (scr_ht-(ECHOLINES+3))
|
||
/* Don't move lines all the
|
||
* way down full screen! */
|
||
|| num >= noicost) /* Compare cost with estimated
|
||
* cost of not doing insert.*/
|
||
goto noins;
|
||
|
||
/* Insert lines! */
|
||
dspf &= ~RD_ILIN;
|
||
inslin(i, n - i, w);
|
||
for(; i < n; i++) /* Update intervening lines */
|
||
upd_line (i);
|
||
goto ldone;
|
||
}
|
||
noins:
|
||
|
||
/* Check for delete line. See if the new screen line
|
||
* is duplicated among any of the old lines already on
|
||
* the screen. If a match is found, keep looking and add
|
||
* up the number of characters in the matching lines.
|
||
*/
|
||
if(dspf&RD_DLIN)
|
||
{
|
||
/* See if the new line already exists elsewhere */
|
||
if(s->sl_ncol == 0) /* Ignore blank lines */
|
||
goto nodel;
|
||
for (n = i + 1; n < bot; n++)
|
||
{ if((scr[n]->sl_flg&SL_MOD)==0)
|
||
break;
|
||
if(slineq(scr[n],s)) /* Old, new */
|
||
{ if(!nodcost) dline = n; /* 1st time */
|
||
nodcost += s->sl_ncol;
|
||
s++;
|
||
}
|
||
else if(nodcost) break;
|
||
}
|
||
if(!nodcost) /* If no match, forget it */
|
||
goto nodel; /* S will not have changed. */
|
||
s = scr[i]; /* Restore S */
|
||
n = dline; /* Index of 1st match */
|
||
|
||
/* Heuristic to decide whether to perform
|
||
* delete-line operation. Same hack as for
|
||
* insert-line.
|
||
*/
|
||
num = (n-i)*(tvc_ldn+tvc_lin) + (tvc_li + tvc_ld);
|
||
if((n-i) >= (scr_ht-(ECHOLINES+3))
|
||
|| num >= nodcost)
|
||
goto nodel;
|
||
|
||
/* Delete lines! */
|
||
dspf &= ~RD_DLIN;
|
||
dellin(i, n - i, w);
|
||
goto ldone;
|
||
}
|
||
nodel:
|
||
/* All failed, so just update line */
|
||
upd_line(i);
|
||
ldone: s->sl_flg &= ~SL_MOD; /* Clear mod flag */
|
||
}
|
||
done:
|
||
#if FX_SOWIND
|
||
t_dostandout(oldso); /* Back to previous mode */
|
||
#endif
|
||
zdone: w->w_redp = 0;
|
||
return(0); /* Say completed */
|
||
}
|
||
|
||
|
||
/*
|
||
* SLINEQ - Compare old, new screen image lines. If new line doesn't
|
||
* have the modified flag set, use its old image.
|
||
* If the standout mode differs, always fails.
|
||
*/
|
||
|
||
slineq(olds, news)
|
||
struct scr_line *olds;
|
||
struct scr_line *news;
|
||
{ register char *cpo, *cpn;
|
||
register int cnt;
|
||
|
||
cpo = (char *)news;
|
||
if(((struct scr_line *)cpo)->sl_flg&SL_MOD)
|
||
{ cnt = ((struct scr_line *)cpo)->sl_ncol;
|
||
cpn = ((struct scr_line *)cpo)->sl_nlin;
|
||
#if FX_SOWIND /* Mode of old must match mode of new */
|
||
if(((olds->sl_flg & SL_CSO)==0) !=
|
||
((((struct scr_line *)cpo)->sl_flg & SL_NSO)==0))
|
||
return 0;
|
||
#endif
|
||
}
|
||
else
|
||
{ cnt = ((struct scr_line *)cpo)->sl_col;
|
||
cpn = ((struct scr_line *)cpo)->sl_line;
|
||
#if FX_SOWIND /* Modes of current lines must match */
|
||
if((olds->sl_flg & SL_CSO) !=
|
||
(((struct scr_line *)cpo)->sl_flg & SL_CSO))
|
||
return 0;
|
||
#endif
|
||
}
|
||
|
||
/* Crufty match stuff */
|
||
if(cnt != olds->sl_col)
|
||
return(0);
|
||
if(cnt)
|
||
{ cpo = olds->sl_line;
|
||
do { if(*cpo++ != *cpn++)
|
||
return(0);
|
||
} while(--cnt);
|
||
}
|
||
return(1);
|
||
}
|
||
|
||
/* UPD_LINE(lineno) - Effects the update of a physical screen line,
|
||
* assuming that the screen line structure for that line has been
|
||
* properly set up by fix_wind. It cannot be interrupted by typein.
|
||
* Does a lot of work to check out optimization for char I/D.
|
||
* Someday it could also check out the possibility of doing a CLEOL at
|
||
* some point to reduce the number of spaces that need to be output.
|
||
*/
|
||
|
||
upd_line(y)
|
||
int y;
|
||
{ register i;
|
||
register char *sci, *cp;
|
||
struct scr_line *s;
|
||
|
||
int xpos; /* actual screen position */
|
||
int c, c2, p2, cmpcost, delcost;
|
||
int savc, ocol, ncol;
|
||
char *savcp, *savsci;
|
||
#if FX_SOWIND
|
||
int oldso, newso;
|
||
int writall = 0;
|
||
#endif
|
||
|
||
s = scr[y];
|
||
savsci = sci = s->sl_line; /* What is currently on the screen */
|
||
#if IMAGEN
|
||
if (s->sl_flg & SL_REDO)
|
||
{ /* Check for line-redo flag */
|
||
s->sl_flg &= ~SL_REDO; /* Clear it: we are handling it */
|
||
writall = 1; /* Re-do this line completely */
|
||
t_move(y, 0);
|
||
t_docleol();
|
||
s->sl_col = 0;
|
||
}
|
||
#endif /*IMAGEN*/
|
||
|
||
#if FX_SOWIND
|
||
/* See whether modes of the lines are the same or not. */
|
||
newso = (s->sl_flg & SL_NSO)!=0; /* Get new mode (true if SO)*/
|
||
if(((s->sl_flg & SL_CSO)!=0) != newso)
|
||
{ t_move(y, 0); /* Not same, must zap existing line */
|
||
t_docleol();
|
||
s->sl_col = 0;
|
||
writall = newso; /* Output all if SO is new mode */
|
||
}
|
||
oldso = t_dostandout(newso); /* Get in right mode */
|
||
#endif
|
||
|
||
ocol = s->sl_col;
|
||
savcp = cp = s->sl_nlin;
|
||
ncol = s->sl_ncol;
|
||
|
||
/* Find leading equalness */
|
||
i = ocol;
|
||
if(i > ncol) i = ncol; /* Use minimum count */
|
||
if(i)
|
||
{ do { if(*cp++ != *sci++)
|
||
{ --cp;
|
||
break;
|
||
}
|
||
} while(--i);
|
||
i = cp - savcp;
|
||
sci = savsci; /* Restore ptr to beg of cur line */
|
||
}
|
||
|
||
/* From here on, "i" is now the x-coordinate (column addr)
|
||
* of the first position that doesn't match. "cp" points to
|
||
* the first nonmatching char in the new line image.
|
||
*/
|
||
#if COHERENT /* Has direct video interface capability */
|
||
if(trm_flags&TF_DIRVID)
|
||
{ if(ncol < ocol)
|
||
{ /* Flesh out new line to completely replace old */
|
||
fillsp(&s->sl_nlin[ncol], ocol-ncol);
|
||
ncol = ocol;
|
||
}
|
||
/* Spit out changed stuff. t_direct will handle the
|
||
* case where i == ncol (ie no changes needed).
|
||
*/
|
||
t_direct(y,i,cp,ncol-i);
|
||
goto done;
|
||
}
|
||
#endif /*COHERENT*/
|
||
|
||
if(i == ncol) /* Matched up to end of new line? */
|
||
goto idone; /* Yes, can skip big loop! */
|
||
|
||
#if FX_SOWIND
|
||
if(writall) /* If simply writing everything...*/
|
||
{ t_move(y, 0);
|
||
tputn(cp, ncol); /* Output them all */
|
||
curs_col = ncol; /* Update cursor position */
|
||
goto idone; /* then wrap up! */
|
||
}
|
||
#endif
|
||
|
||
/* Now must fill out remainder of old line with blanks. */
|
||
if(ocol < scr_wid)
|
||
{
|
||
#if FX_SOWIND
|
||
if(newso) fillset(&sci[ocol], scr_wid-ocol, 0);
|
||
else
|
||
#endif
|
||
fillsp(&sci[ocol],scr_wid-ocol); /* Fill out */
|
||
}
|
||
|
||
/****** Main update loop. ******/
|
||
for (; i < ncol; i++)
|
||
{ c = *cp++; /* Note *CP will point to next */
|
||
if(c == sci[i])
|
||
continue;
|
||
if(i >= ocol) /* Past EOL of old line? */
|
||
{
|
||
putin: sci[i] = c;
|
||
if(y != curs_lin || i != curs_col)
|
||
t_move(y, i);
|
||
tput(c);
|
||
curs_col++;
|
||
continue;
|
||
}
|
||
|
||
if((trm_flags&TF_IDCHR)==0) /* Replace */
|
||
goto putin;
|
||
|
||
/* Do checking to see whether char I/D operations should
|
||
* be invoked. This code is quite CPU intensive and
|
||
* can cause noticeable pauses if run on a slow CPU with
|
||
* a fast (9600) terminal line. The optimization tradeoff
|
||
* seems worthwhile most of the time, however.
|
||
*/
|
||
cmpcost = 0; /* Default is don't compare */
|
||
if(ncol == ocol) /* If line lengths same, must chk */
|
||
{
|
||
/* if(ncol >= scr_wid) */ /* If line overrun, compare */
|
||
cmpcost++;
|
||
}
|
||
#if 0
|
||
If ncol == ocol, have problem with tabs:
|
||
If don''t use I/D char, but tabs exist, lots of wasteful update.
|
||
If DO use I/D char, and no tabs exist, potential for mistakenly
|
||
using I/D when didn''t have to. Not too bad, though?
|
||
If DO use I/D char, then mild screw when inserting/deleting
|
||
just before a tab, since could have just overwritten,
|
||
but I/D insists on jerking things around.
|
||
Insert test:
|
||
If old char was space, replace? Problem: will cause cursor
|
||
jump if really should have shifted a long run of spaces.
|
||
But that is probably okay.
|
||
Delete test:
|
||
If new char is space, replace? again, will cause cursor jump
|
||
with long run of spaces.
|
||
#endif /*COMMENT*/
|
||
|
||
if(ncol < ocol || cmpcost) /* Try delete-char */
|
||
{
|
||
/* Search old for match of c and nextc */
|
||
dodel: savc = c;
|
||
if(i >= ncol-1)
|
||
goto putin;
|
||
c2 = *cp;
|
||
if(c == SP && ncol == ocol)
|
||
goto tryins;
|
||
p2 = i;
|
||
for(;;)
|
||
{ if(c == sci[i] && c2 == sci[i+1])
|
||
break;
|
||
if(++i < ocol)
|
||
continue;
|
||
i = p2;
|
||
if(cmpcost) {cmpcost = 0; goto tryins;}
|
||
goto putin;
|
||
}
|
||
/* Find # chars that match (i.e. will be saved) */
|
||
for(c=1; (i+c < ncol) && (sci[i+c] == cp[c-1]); c++);
|
||
delcost = tvc_cd + tvc_cdn*(i - p2);
|
||
if(delcost >= c)
|
||
{ c = savc;
|
||
i = p2;
|
||
if(cmpcost) { cmpcost = 0; goto tryins;}
|
||
goto putin; /* Punt */
|
||
}
|
||
if(cmpcost)
|
||
{ c = savc; i = p2;
|
||
goto tryins;
|
||
}
|
||
t_move(y, p2);
|
||
c = i - p2; /* Find # chars to flush */
|
||
strncpy(&sci[p2],&sci[i], ocol-i);
|
||
ocol -= c;
|
||
fillsp(&sci[ocol], c);
|
||
i = p2; /* Restore i */
|
||
t_delchr(c); /* Flush this many cols */
|
||
continue;
|
||
}
|
||
|
||
/* Try ins-char */
|
||
/* Search new for match of i and i+1 */
|
||
/* Note this cannot be used while in standout mode, since
|
||
** the new spaces created will probably be in the wrong mode.
|
||
*/
|
||
tryins:
|
||
#if FX_SOWIND
|
||
if(newso) goto putin;
|
||
#endif
|
||
if(i+1 >= ocol)
|
||
goto putin;
|
||
|
||
savc = c;
|
||
savcp = cp;
|
||
c2 = sci[i+1];
|
||
if(sci[i] == SP && ncol == ocol)
|
||
goto putin;
|
||
xpos = i; /* save current col */
|
||
i++;
|
||
for(;;)
|
||
{ if(i >= ncol) goto puntx;
|
||
c = *cp++;
|
||
inlp2: if(c != sci[xpos])
|
||
{ if(i > scr_wid) goto puntx;
|
||
i++;
|
||
continue;
|
||
}
|
||
if(i >= ncol) goto puntx;
|
||
c = *cp++;
|
||
if(c != c2)
|
||
{ i++; /* Allow for previous c */
|
||
goto inlp2; /* which is always 1 */
|
||
}
|
||
break;
|
||
}
|
||
if(i >= scr_wid) goto puntx;
|
||
|
||
/* Find how many chars match (i.e. will be saved) */
|
||
for(c = 2; xpos+c < ncol && sci[xpos+c] == *cp++; c++);
|
||
if((p2 = tvc_ci + tvc_cin*(i - xpos)) >= c)
|
||
goto puntx; /* Not worth it... */
|
||
if(cmpcost && p2 >= delcost)
|
||
goto puntx; /* Do delchr instead */
|
||
|
||
/* We've decided to insert some chars! */
|
||
i -= xpos; /* Get # char positions to insert */
|
||
cp = savcp; /* Get ptr to newline string */
|
||
--cp; /* Point at 1st char to insert */
|
||
/* Make room in scr array */
|
||
inspc(&sci[xpos],
|
||
&sci[(ocol+i >= scr_wid) ? scr_wid-i : ocol], i);
|
||
ocol += i; /* Update size of old line */
|
||
strncpy(&sci[xpos], cp, i); /* Copy all inserted chars */
|
||
|
||
t_move(y, xpos); /* Now ensure in right place */
|
||
t_inschr(i, cp); /* and insert string onto screen! */
|
||
|
||
cp += i; /* Update source ptr */
|
||
cp++; /* Point to next char */
|
||
i += xpos;
|
||
continue; /* Now continue loop! */
|
||
|
||
puntx: i = xpos;
|
||
c = savc;
|
||
cp = savcp;
|
||
if(cmpcost) { cmpcost = 0; goto dodel;}
|
||
goto putin;
|
||
}
|
||
|
||
/* All done putting up new stuff. Now see if any remaining old
|
||
** stuff needs to be cleared from end of line.
|
||
*/
|
||
idone: if(i < ocol) /* if still have text to right, */
|
||
{ t_move(y,i); /* move there */
|
||
t_docleol(); /* and clear old stuff. */
|
||
}
|
||
|
||
done: s->sl_line = s->sl_nlin; /* Replace old image by new */
|
||
s->sl_col = s->sl_ncol;
|
||
s->sl_nlin = sci;
|
||
s->sl_flg &= ~SL_MOD;
|
||
#if FX_SOWIND /* Copy standout mode to current */
|
||
if(newso) s->sl_flg |= SL_CSO;
|
||
else s->sl_flg &= ~SL_CSO;
|
||
#endif
|
||
}
|
||
|
||
#if FX_SOWIND
|
||
fillset(str,cnt,c)
|
||
char *str;
|
||
int cnt;
|
||
int c;
|
||
{ register int n;
|
||
register char *cp;
|
||
if((n = cnt) <= 0) return;
|
||
cp = str;
|
||
do{ *cp++ = c;
|
||
} while(--n);
|
||
}
|
||
#endif
|
||
|
||
fillsp(str,cnt)
|
||
char *str;
|
||
int cnt;
|
||
{ register int n;
|
||
register char *cp;
|
||
if((n = cnt) <= 0) return;
|
||
cp = str;
|
||
do{ *cp++ = SP;
|
||
} while(--n);
|
||
}
|
||
inspc(cp0, cpl, cnt)
|
||
char *cp0, *cpl;
|
||
int cnt;
|
||
{ register char *cp, *cp2;
|
||
register n;
|
||
if((n = cnt) <= 0) return;
|
||
cp = cpl; /* ptr to last+1 char in string */
|
||
cp2 = cp+n; /* ptr to loc+1 to move to */
|
||
n = cp - cp0; /* # chars to move */
|
||
do *--cp2 = *--cp;
|
||
while(--n);
|
||
n = cnt; /* Now fill gap with spaces */
|
||
do *cp++ = SP;
|
||
while(--n);
|
||
}
|
||
|
||
/* FIX_LINE - Fixes up new screen image for a single line. Does not
|
||
* do any actual terminal I/O, and does not change the old screen
|
||
* image. Assumes that previous line (if any is furnished) has
|
||
* already been properly set up.
|
||
*/
|
||
|
||
int sctreol = 0; /* Ugly crock for talking to sctrin() */
|
||
/* 0 = no EOL seen, 1 = EOL seen, -1 = EOF seen */
|
||
fix_line(slp, olds)
|
||
struct scr_line *slp;
|
||
struct scr_line *olds;
|
||
{ register struct scr_line *s;
|
||
register int col, scrw;
|
||
char *cp;
|
||
|
||
col = 0;
|
||
scrw = scr_wid;
|
||
cp = slp->sl_nlin;
|
||
if((s = olds) && (col = s->sl_cont))
|
||
{ if(--col)
|
||
strncpy(cp, (s->sl_flg&SL_MOD) ?
|
||
&s->sl_nlin[scrw]
|
||
: &s->sl_line[scrw], col);
|
||
cp += col;
|
||
}
|
||
scrw--; /* Note now using scr_wd0 !! */
|
||
s = slp;
|
||
s->sl_boff = e_dot();
|
||
col = sctrin(cp, scrw, col);
|
||
if (col < scrw || sctreol) /* Does line need continuation mark? */
|
||
s->sl_cont = 0; /* No, say no cont chars */
|
||
else {
|
||
/* Yes, find # cols of overflow. If not 0, must be > 0 */
|
||
/* and char is a biggie. Make room for continuation chars */
|
||
if(col -= scrw)
|
||
inspc(&s->sl_nlin[scrw],&s->sl_nlin[scrw+col], 1);
|
||
s->sl_cont = col+1; /* # cont chars, plus 1 */
|
||
s->sl_nlin[scrw] = CI_CLINE; /* Display "contin" mark */
|
||
col = scrw+1;
|
||
}
|
||
|
||
s->sl_ncol = col;
|
||
s->sl_len = e_dot() - s->sl_boff;
|
||
s->sl_flg |= (SL_MOD|SL_EOL); /* Say new, and assume line has EOL */
|
||
if(sctreol <= 0) /* unless it doesn't really */
|
||
s->sl_flg &= ~SL_EOL; /* in which case turn off flag */
|
||
return;
|
||
}
|
||
|
||
/* SCTRIN - auxiliary for FIX_LINE.
|
||
* lim - # cols chars are allowed to use
|
||
* ccol - current column (0 = bol)
|
||
* Returns when see EOL or EOF, or
|
||
* when all columns have been filled up. Retval-ccol = # overflow.
|
||
* Note that any overflow is indivisible (i.e. a char with a
|
||
* multi-col representation is responsible for the overflow).
|
||
* So, overflow = 0 means next char would be in 1st non-ex column
|
||
* and overflow > 0 means last char read has extra columns, but
|
||
* it did start within bounds.
|
||
*/
|
||
sctrin(to, lim, ccol)
|
||
char *to;
|
||
int lim;
|
||
int ccol;
|
||
{ register SBBUF *sb;
|
||
register col, cnt;
|
||
|
||
sb = (SBBUF *) cur_buf;
|
||
col = ccol;
|
||
sctreol = 0; /* No EOL or EOF seen */
|
||
do
|
||
{ cnt = sb_getc(sb);
|
||
if(cnt == EOF)
|
||
{ --sctreol; /* Say EOF seen! */
|
||
return(col);
|
||
}
|
||
#if FX_EOLMODE
|
||
if(cnt == CR) /* Possible EOL? */
|
||
{ if(eolcrlf(sb))
|
||
{ if((cnt = sb_getc(sb)) == LF) /* Real EOL? */
|
||
{ sctreol++;
|
||
return col; /* Yes, return */
|
||
}
|
||
/* Stray CR, back up & fall thru */
|
||
if(cnt != EOF)
|
||
sb_backc(sb);
|
||
cnt = CR; /* Show stray CR */
|
||
}
|
||
} else if (cnt == LF)
|
||
{ if(!eolcrlf(sb)) /* Real EOL? */
|
||
{ sctreol++;
|
||
return col; /* Yes, return */
|
||
}
|
||
/* If EOL mode is CRLF then hitting a LF
|
||
** can only happen for stray LFs (the
|
||
** previous check for CR takes care of
|
||
** CRLFs, and we never start scanning
|
||
** from the middle of a CRLF.
|
||
** Drop thru to show stray LF.
|
||
*/
|
||
}
|
||
#else
|
||
if(cnt == LF)
|
||
{ sctreol++; /* Say EOL seen */
|
||
return col;
|
||
}
|
||
#endif /*_FX_EOLMODE*/
|
||
cnt = sctr(cnt, to, col);
|
||
to += cnt;
|
||
col += cnt;
|
||
} while(col < lim);
|
||
|
||
/* If we're stopping because last char put us precisely at the
|
||
** end of the line, make a further check to see whether an EOL
|
||
** is next. If so, we can include that in the line since it
|
||
** doesn't need any more columns for representation!
|
||
*/
|
||
if (col == lim) /* If stopping exactly at edge of screen */
|
||
switch (sb_getc(sb)) /* Check out next char */
|
||
{ case EOF:
|
||
--sctreol; /* Yes, note EOF seen */
|
||
break; /* and can return immed */
|
||
#if FX_EOLMODE
|
||
case CR: /* Possible EOL? */
|
||
if(eolcrlf(sb))
|
||
{ if((cnt = sb_getc(sb)) == LF) /* Real EOL? */
|
||
{ sctreol++; /* Yes, set flag */
|
||
break; /* and return */
|
||
}
|
||
/* Stray CR, back up & fall thru */
|
||
if(cnt != EOF) /* Back up char that */
|
||
sb_backc(sb); /* came after the CR */
|
||
sb_rgetc(sb); /* Then back over CR */
|
||
break;
|
||
}
|
||
sb_backc(sb);
|
||
break;
|
||
case LF:
|
||
if(!eolcrlf(sb)) /* Real EOL? */
|
||
{ sctreol++; /* Yes, set flag */
|
||
break; /* and return */
|
||
}
|
||
/* If EOL mode is CRLF then hitting a LF
|
||
** can only happen for stray LFs (the
|
||
** previous check for CR takes care of
|
||
** CRLFs, and we never start scanning
|
||
** from the middle of a CRLF.
|
||
** Drop thru into default to back up over LF.
|
||
*/
|
||
#else
|
||
case LF:
|
||
sctreol++; /* Say EOL seen */
|
||
break; /* and return */
|
||
#endif /*-FX_EOLMODE*/
|
||
default:
|
||
sb_backc(sb); /* Back up over random char */
|
||
break;
|
||
}
|
||
return(col);
|
||
}
|
||
|
||
/* SCTR - Screen Char TRanslation routine.
|
||
** This routine is completely responsible for the way a buffer char is
|
||
** displayed on the screen. Given a char and the current column position,
|
||
** it stores the representation using the given pointer and returns
|
||
** the number of chars (columns) used by the representation.
|
||
** Normal printing chars (plus space) are simply themselves.
|
||
** TAB is a variable number of spaces depending on the column pos.
|
||
** (we use standard tabstops of 8)
|
||
** All control chars are uparrow followed by a printing char.
|
||
** e.g. ctrl-A = ^A
|
||
** This includes ESC which is ^[.
|
||
** DEL is shown as ^?.
|
||
** Chars with the 8th bit set have the prefix CI_META (currently ~) and
|
||
** the rest of the representation is as above (except for TAB).
|
||
** Chars with the 9th bit set have the prefix CI_TOP (currently |) and
|
||
** the rest of the representation is as above (except for TAB).
|
||
** This only exists for systems with 9-bit chars such as TOPS-20.
|
||
*/
|
||
|
||
static int
|
||
sctr(ch, to, ccol)
|
||
int ch; /* Buffer char to translate */
|
||
char *to; /* Place to deposit translation in */
|
||
int ccol; /* Current column position */
|
||
{ register char *cp;
|
||
register c, n;
|
||
|
||
c = ch;
|
||
if(037 < c && c < 0177) /* Most common case */
|
||
{ *to = c;
|
||
return(1);
|
||
}
|
||
cp = to;
|
||
if(c == TAB) /* Next most common case */
|
||
{ n = 010 - (ccol&07); /* Tab stops are every 8 cols */
|
||
ccol = n; /* Save value */
|
||
do *cp++ = SP;
|
||
while (--n);
|
||
return(ccol);
|
||
}
|
||
ccol = 1; /* Re-use var */
|
||
#if TOPS20
|
||
if(c&0400) /* 9th bit set? */
|
||
{ *cp++ = CI_TOP;
|
||
ccol++;
|
||
}
|
||
#endif /*TOPS20*/
|
||
if(c&0200)
|
||
{ *cp++ = CI_META;
|
||
ccol++;
|
||
}
|
||
if((c &= 0177) <= 037 || c == 0177)
|
||
{ *cp++ = CI_CNTRL;
|
||
c ^= 0100; /* Transform cntrl char */
|
||
ccol++;
|
||
}
|
||
*cp = c;
|
||
return(ccol);
|
||
}
|
||
|
||
/* INSLIN(line, N, wind) - Insert lines
|
||
* DELLIN(line, N, wind) - Delete lines
|
||
* Both routines insert/delete N lines at "line" in window "wind"
|
||
* and update the screen image accordingly.
|
||
*/
|
||
|
||
inslin (line, n, win)
|
||
int line; /* line number to insert BEFORE */
|
||
int n; /* number of lines to insert */
|
||
struct window *win; /* window we are in */
|
||
{ register int i;
|
||
register int bot;
|
||
register char **savp;
|
||
char *savscr[MAXHT];
|
||
|
||
bot = win -> w_ht + win -> w_pos;
|
||
t_curpos (line, 0);
|
||
t_inslin (n, bot); /* do the insertion on the screen */
|
||
savp = &savscr[0];
|
||
for (i = 1; i <= n; i++) /* free lines that fall off-screen */
|
||
*savp++ = scr[bot - i]->sl_line;
|
||
|
||
for (i = bot - 1; i >= line + n; i--) /* move down lines */
|
||
{ scr[i]->sl_line = scr[i - n]->sl_line; /* below the insertion */
|
||
scr[i]->sl_col = scr[i - n]->sl_col;
|
||
}
|
||
savp = &savscr[0];
|
||
for (i = line + n - 1; i >= line; i--)
|
||
/* blank lines where inserted */
|
||
{ scr[i]->sl_line = *savp++;
|
||
scr[i]->sl_col = 0;
|
||
}
|
||
for(i = line; i < bot; ++i)
|
||
scr[i]->sl_flg |= SL_MOD;
|
||
}
|
||
|
||
dellin (line, n, win)
|
||
int line; /* first line to be deleted */
|
||
int n; /* number of lines to be deleted */
|
||
struct window *win; /* window we are in */
|
||
{ register int i;
|
||
register int bot;
|
||
register char **savp;
|
||
char *savscr[MAXHT];
|
||
|
||
bot = win -> w_ht + win -> w_pos;
|
||
|
||
t_curpos (line, 0);
|
||
t_dellin (n, bot); /* do the deletion on the screen */
|
||
savp = &savscr[0];
|
||
for (i = line; i < line + n; i++) /* free the deleted lines */
|
||
*savp++ = scr[i]->sl_line;
|
||
for (i = line; i < bot - n; i++) /* move lines up to fill */
|
||
{ scr[i]->sl_line = scr[i + n]->sl_line; /* deleted spaces */
|
||
scr[i]->sl_col = scr[i + n]->sl_col;
|
||
}
|
||
|
||
savp = &savscr[0];
|
||
for (i = bot - n; i < bot; i++) /* blank lines at bottom */
|
||
{ scr[i]->sl_line = *savp++;
|
||
scr[i]->sl_col = 0;
|
||
}
|
||
for(i = line; i < bot; ++i)
|
||
scr[i]->sl_flg |= SL_MOD;
|
||
}
|
||
|
||
/* T_ Terminal functions - these are similar to the terminal-dependent
|
||
* routines in EETERM (which they call) but rely on some knowledge of
|
||
* the screen image in order to do their job cleverly.
|
||
*/
|
||
|
||
#if FX_SOWIND
|
||
|
||
/* T_DOSTANDOUT(on) - Turn standout mode on or off, cleverly.
|
||
** Returns previous state.
|
||
*/
|
||
static int curso = 0; /* Current state (initially off) */
|
||
int
|
||
t_dostandout(on)
|
||
int on;
|
||
{
|
||
int oldso;
|
||
|
||
if ((oldso = curso) != on) /* If desired state doesn't match, */
|
||
{ t_standout(on); /* invoke new state. */
|
||
curso = on;
|
||
}
|
||
return oldso;
|
||
}
|
||
#endif
|
||
|
||
|
||
t_move(y,x)
|
||
register int y,x;
|
||
{ register int d;
|
||
|
||
if(y != curs_lin) /* No vertical smarts yet */
|
||
{ t_curpos(y, x);
|
||
return;
|
||
}
|
||
if((d = (x - curs_col)) >= 0) /* Find diff in column position */
|
||
{ if(d == 0) return; /* If none, nothing to do! */
|
||
|
||
/* Moving right. If distance is less than abs-move cost,
|
||
* do clever right-move by copying screen image */
|
||
if(d < tvc_pos)
|
||
#if FX_SOWIND /* Ensure not in standout mode */
|
||
if((scr[y]->sl_flg&(SL_CSO|SL_NSO))==0)
|
||
#endif
|
||
{
|
||
tputn(&scr[y]->sl_line[curs_col], d);
|
||
curs_col = x;
|
||
return;
|
||
}
|
||
}
|
||
/* Moving to left, try to do clever left-move by backspacing
|
||
* instead of using abs move.
|
||
*/
|
||
else if((d = -d)*tvc_bs < tvc_pos)
|
||
{ do { t_backspace();
|
||
} while(--d);
|
||
return;
|
||
}
|
||
/* No luck with cleverness, just move. */
|
||
t_curpos(y, x);
|
||
}
|
||
|
||
t_docleol()
|
||
{ register struct scr_line *s;
|
||
register int cnt, ocol;
|
||
|
||
if(trm_flags&TF_CLEOL) t_cleol(); /* Winning */
|
||
else /* Losing */
|
||
{ s = scr[curs_lin];
|
||
if((cnt = s->sl_col - curs_col) > 0)
|
||
{
|
||
#if FX_SOWIND
|
||
int oldso = t_dostandout(0);
|
||
#endif
|
||
ocol = curs_col;
|
||
do { tput(SP); curs_col++;
|
||
} while(--cnt);
|
||
#if FX_SOWIND
|
||
t_dostandout(oldso);
|
||
#endif
|
||
t_move(curs_lin, ocol);
|
||
}
|
||
}
|
||
}
|
||
|