aboutsummaryrefslogtreecommitdiff
path: root/tecdisp.c
diff options
context:
space:
mode:
authorpaulcantrell <paulcantrell>2007-12-10 21:59:20 +0000
committerpaulcantrell <paulcantrell>2007-12-10 21:59:20 +0000
commit2ee4787d1df9784d40a7eda91e61691da3d56f74 (patch)
tree0947afb6c37ffeb4ae193401a187255cf27b0ed3 /tecdisp.c
downloadvideoteco-fork-2ee4787d1df9784d40a7eda91e61691da3d56f74.tar.gz
Initial revision
Diffstat (limited to 'tecdisp.c')
-rw-r--r--tecdisp.c2867
1 files changed, 2867 insertions, 0 deletions
diff --git a/tecdisp.c b/tecdisp.c
new file mode 100644
index 0000000..a1d2bb6
--- /dev/null
+++ b/tecdisp.c
@@ -0,0 +1,2867 @@
+char *tecdisp_c_version = "tecdisp.c: $Revision: 1.1 $";
+
+/*
+ * $Date: 2007/12/10 21:59:20 $
+ * $Source: /cvsroot/videoteco/videoteco/tecdisp.c,v $
+ * $Revision: 1.1 $
+ * $Locker: $
+ */
+
+/* tecdisp.c
+ * Terminal Screen Display Subroutines
+ *
+ * COPYRIGHT (c) 1985-2003 BY
+ * PAUL CANTRELL & J. M. NISHINAGA
+ * SUDBURY, MA 01776
+ * ALL RIGHTS RESERVED
+ *
+ * This software is furnished in it's current state free of charge.
+ * The authors reserve all rights to the software. Further
+ * distribution of the software is not authorized. Modifications to
+ * the software may be made locally, but shall not be distributed
+ * without the consent of the authors. This software or any other
+ * copies thereof, may not be provided or otherwise made available
+ * to anyone without express permission of the authors. Title to and
+ * ownership of this software remains with the authors.
+ *
+ */
+
+#include "teco.h"
+#include "tecparse.h"
+
+/*
+ * Global Storage is defined here for lack of a better place.
+ */
+/*
+ * External Variables
+ */
+ extern char teco_startup;
+ extern char screen_startup;
+ extern int term_columns;
+ extern int term_lines;
+ extern int forced_height,forced_width;
+ extern int tty_input_chan;
+ extern int tty_output_chan;
+ extern char insert_delete_line_capability;
+ extern char eight_bit_output_flag;
+ extern char hide_cr_flag;
+ extern char tab_expand[];
+ extern int tab_width;
+
+#ifdef TERMCAP
+ extern int termcap_sg;
+#endif
+
+#ifdef TERMINFO
+ extern int terminfo_magic_cookie_glitch;
+#endif
+
+/*
+ * Global Variables
+ */
+ int curx;
+ int cury;
+ short cur_mode;
+ int cursor_x;
+ int cursor_y;
+ int screen_sequence;
+ int echo_cnt;
+ char ring_audible_bell;
+ int buffer_li;
+ int buffer_half_li;
+ char format_lines_invalid;
+
+ struct format_line *
+ allocate_format_buffer(struct window *,struct buff_line *);
+ struct format_line *
+ screen_find_window_format_line(struct window *,struct buff_line *);
+ struct window *create_window(int,int);
+ void screen_optimize_lines(void);
+ void screen_optimize_by_pattern_matching(void);
+ void screen_gc_format_lines(void);
+ void screen_free_window_format_lines(struct window *,struct buff_line *);
+ void screen_account_for_delete_line(int y, int count);
+ void screen_account_for_insert_line(int y, int count);
+ int screen_rebuild_from_scratch(struct window *wptr);
+ int screen_format_buff_line(struct window *wptr,struct buff_line *lbp);
+ int screen_find_dot(struct window *wptr);
+
+/*
+ * Global structures
+ */
+ struct screen_line *saved_screen;
+ char saved_screen_valid;
+
+ int next_window_number = 1;
+ struct window *curwin;
+ struct window *window_list;
+ struct format_line message_line;
+ char message_flag = NO;
+ struct format_line echo_line;
+
+ struct format_line *format_line_free_list = NULL;
+ struct format_line *format_line_alloc_list = NULL;
+
+/*
+ * External Routines, etc.
+ */
+ extern struct buff_header *curbuf;
+ extern struct buff_header *buffer_headers;
+
+ extern int term_speed;
+ extern char input_pending_flag;
+ extern char resize_flag;
+
+/* SCREEN_INIT - Initialize the TECO screen package
+ *
+ * Function:
+ *
+ * This entry point gives the screen package a chance to initialize
+ * itself.
+ */
+int
+screen_init()
+{
+register int i,j;
+register struct window *wptr;
+register struct screen_line *lp;
+register short *sp;
+
+ PREAMBLE();
+
+/*
+ * Call the terminal handling routines, and give them a chance to set
+ * the terminal up.
+ */
+ term_init();
+
+/*
+ * Set the cursor to the upper left of the display and perform a clear to end
+ * of display command. This has the effect of clearing the entire display.
+ */
+ term_goto(0,0);
+ term_clrtobot();
+
+/*
+ * If this is a restart (from ^Z most likely), then that is all we have to do.
+ * The rest of this is only to initialize data structures the first time
+ * through
+ */
+ if(screen_startup == NO) return(SUCCESS);
+ screen_startup = NO;
+
+/*
+ * Calculate how many lines are available for display of the edit buffer.
+ */
+ buffer_li = term_lines - SCREEN_RESERVED_LINES;
+ buffer_half_li = ((buffer_li + 1) & ~1) / 2;
+
+/*
+ * Allocate memory for the saved screen structure. This is used to remember
+ * what is currently showing on the terminal. Since we just cleared the screen,
+ * we set the current contents of this to be all spaces (i.e., blank).
+ */
+ i = sizeof(struct screen_line) * term_lines;
+ saved_screen = (struct screen_line *)tec_alloc(TYPE_C_SCR,i);
+ if(saved_screen == NULL) return(FAIL);
+
+ bzero(saved_screen,i);
+ screen_sequence = 1;
+
+ for(lp = saved_screen, i = 0; i < term_lines; i++,lp++){
+ lp->scr_magic = MAGIC_SCREEN;
+ lp->companion = NULL;
+ lp->sequence = 0;
+ lp->buffer = (short *)
+ tec_alloc(TYPE_C_SCRBUF,(int)(sizeof(short) * term_columns));
+ sp = lp->buffer;
+ for(j = 0; j < term_columns; j++,sp++) *sp = ' ';
+ }/* End FOR */
+
+ saved_screen_valid = 1;
+
+/*
+ * Allocate the initial window structure
+ */
+ window_list = wptr =
+ create_window(term_columns,term_lines - SCREEN_RESERVED_LINES);
+ curwin = wptr;
+ wptr->win_buffer = curbuf;
+
+ lp = saved_screen;
+ lp += term_lines - SCREEN_RESERVED_LINES - 1;
+ wptr->win_label_line.fmt_saved_line = lp;
+ lp->companion = &wptr->win_label_line;
+/*
+ * Now we set up the structures for our special lines which form the bottom
+ * of the display screen.
+ */
+ echo_line.fmt_buffer_line = message_line.fmt_buffer_line = NULL;
+ echo_line.fmt_buffer_size = message_line.fmt_buffer_size = term_columns;
+
+ echo_line.fmt_buffer = (short *)
+ tec_alloc(
+ TYPE_C_SCRBUF,
+ (int)(sizeof(short) * echo_line.fmt_buffer_size)
+ );
+ message_line.fmt_buffer = (short *)
+ tec_alloc(
+ TYPE_C_SCRBUF,
+ (int)(sizeof(short)*message_line.fmt_buffer_size)
+ );
+
+ for(i = 0; i < term_columns; i++){
+ message_line.fmt_buffer[i] = ' ';
+ echo_line.fmt_buffer[i] = ' ';
+ }/* End FOR */
+
+ echo_line.fmt_buffer[0] = '*';
+ echo_line.fmt_sequence = message_line.fmt_sequence = 1;
+ echo_line.fmt_next_line = message_line.fmt_next_line = NULL;
+
+ message_line.fmt_visible_line_position =
+ echo_line.fmt_visible_line_position = -1;
+
+ lp = saved_screen;
+ lp += term_lines - SCREEN_RESERVED_LINES;
+
+ message_line.fmt_permanent = 1;
+ message_line.fmt_saved_line = lp;
+ message_line.fmt_magic = MAGIC_FORMAT_LOOKASIDE;
+ lp->companion = &message_line;
+
+ lp += 1;
+ echo_line.fmt_permanent = 1;
+ echo_line.fmt_saved_line = lp;
+ echo_line.fmt_magic = MAGIC_FORMAT_LOOKASIDE;
+ lp->companion = &echo_line;
+
+ screen_label_line(curbuf," TECO",LABEL_C_TECONAME);
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* SCREEN_RESIZE - Cause the screen to be refreshed at a new size
+ *
+ * Function:
+ *
+ * This function is called when the size of the terminal screen has
+ * changed. This is common on a windowing terminal. This routine has
+ * to clean up the existing screen database and re-initialize it.
+ */
+void
+screen_resize()
+{
+register int i;
+register struct window *wptr;
+register struct screen_line *lp;
+
+#ifdef SUN_STYLE_WINDOW_SIZING
+ struct winsize os_window;
+#endif
+
+ PREAMBLE();
+
+/*
+ * The screen_gc_format_line is going to scavenge all the lines because
+ * we're making them look not-used here. However, we still go ahead and
+ * clobber the line_buffer's pointer, because we don't like the idea of
+ * having hanging pointers pointed at our screen lines.
+ */
+ if(saved_screen_valid){
+ for(lp = saved_screen, i = 0; i < term_lines; i++,lp++){
+ if(lp->companion){
+ if(lp->companion->fmt_saved_line != lp){
+ fprintf(
+ stderr,
+ "lp->companion 0x%x != ->fmt_saved_line 0x%x\n",
+ (unsigned int)lp->companion,
+ (unsigned int)lp->companion->fmt_saved_line
+ );
+ CAUSE_BUS_ERROR();
+ }/* End IF */
+ lp->companion->fmt_saved_line = NULL;
+ lp->companion = NULL;
+ }/* End IF */
+ }/* End FOR */
+ }/* End IF */
+
+ screen_gc_format_lines();
+
+/*
+ * Return the memory used for the saved screen structure. This structure
+ * is the one used to remember what is currently showing on the terminal.
+ */
+ if(saved_screen_valid){
+ saved_screen_valid = 0;
+ for(lp = saved_screen, i = 0; i < term_lines; i++,lp++){
+ if(lp->buffer) tec_release(TYPE_C_SCRBUF,(char *)lp->buffer);
+ lp->buffer = NULL;
+ }/* End FOR */
+
+ if(saved_screen) tec_release(TYPE_C_SCR,(char *)saved_screen);
+ saved_screen = NULL;
+
+ }/* End IF */
+
+/*
+ * Reclaim all the window structures
+ */
+ while(1){
+ wptr = window_list;
+ if(wptr == NULL) break;
+ window_list = wptr->win_next_window;
+/*
+ * Release any memory used to hold the strings in the label line
+ */
+ for(i = 0; i < SCREEN_MAX_LABEL_FIELDS; i++){
+ if(wptr->win_label_field_contents[i]){
+ tec_release(
+ TYPE_C_LABELFIELD,
+ wptr->win_label_field_contents[i]
+ );
+ wptr->win_label_field_contents[i] = NULL;
+ }/* End IF */
+ }/* End FOR */
+/*
+ * Release the buffer used to hold formatted data
+ */
+ tec_release(TYPE_C_SCRBUF,(char *)wptr->win_label_line.fmt_buffer);
+ tec_release(TYPE_C_WINDOW,(char *)wptr);
+
+ }/* End While */
+
+ window_list = NULL;
+
+ if(echo_line.fmt_buffer){
+ tec_release(TYPE_C_SCRBUF,(char *)echo_line.fmt_buffer);
+ }/* End IF */
+
+ if(message_line.fmt_buffer){
+ tec_release(TYPE_C_SCRBUF,(char *)message_line.fmt_buffer);
+ }/* End IF */
+ echo_cnt = 0;
+
+ echo_line.fmt_buffer_line = message_line.fmt_buffer_line = NULL;
+ echo_line.fmt_buffer_size = message_line.fmt_buffer_size = 0;
+
+/*
+ * If the OS supports window size ioctls, we try for that since it
+ * will tend to be more correct for a window environment.
+ */
+#ifdef SUN_STYLE_WINDOW_SIZING
+ if(ioctl(tty_output_chan,TIOCGWINSZ,&os_window) >= 0){
+ term_lines = os_window.ws_row;
+ term_columns = os_window.ws_col;
+ }/* End IF */
+#endif
+ if(forced_height > 0) term_lines = forced_height;
+ if(forced_width > 0) term_columns = forced_width;
+
+ if(term_lines == 0) term_lines = 24;
+ if(term_columns == 0) term_columns = 80;
+
+ resize_flag = NO;
+ screen_startup = YES;
+ screen_init();
+ buff_switch(curbuf,0);
+ parser_reset_echo();
+ screen_echo('\0');
+ screen_format_windows();
+
+}/* End Routine */
+
+
+
+/* SCREEN_FINISH - Here when we are exiting the editor
+ *
+ * Function:
+ *
+ * This routine is called when TECO is being exited to give us a chance
+ * to cleanly leave the screen package. We put the cursor at the bottom
+ * of the screen, send any termination escape sequences that are required
+ * and flush it all out.
+ */
+void
+screen_finish()
+{
+
+ term_finish();
+
+}/* End Routine */
+
+
+
+/* SCREEN_REFRESH - Redraw the screen based on new database
+ *
+ * Function:
+ *
+ * This routine gets called after the new companion entries have been set
+ * up for the saved_screen database. Therefore, we can run through the
+ * lines and update only those which have changed.
+ */
+void
+screen_refresh()
+{
+register int x,y;
+register struct screen_line *lp;
+register struct format_line *his_lp;
+register short *sp,*osp;
+char pos_flag;
+
+ PREAMBLE();
+
+ input_pending_flag = tty_input_pending();
+ if(input_pending_flag == YES) return;
+
+/*
+ * First try to optimize output by performing insert/delete line operations
+ */
+ if(insert_delete_line_capability){
+ screen_optimize_lines();
+ }/* End IF */
+/*
+ * Go through each line and insure that the screen appearence is changed to
+ * match those described by the line buffer structures.
+ */
+ for(y = 0; y < term_lines; y++){
+ if(term_speed <= 2400){
+ term_flush();
+ }/* End IF */
+
+ input_pending_flag = tty_input_pending();
+ if(input_pending_flag == YES) break;
+
+/*
+ * Get pointers to the saved screen entry which tells us what the screen
+ * currently looks like, and pointers to the line buffer structure which
+ * tells us what the screen ought to look like when we are done.
+ */
+ lp = saved_screen + y;
+ osp = lp->buffer;
+ his_lp = lp->companion;
+/*
+ * If there is no line buffer structure, the line ought to appear blank on
+ * the screen. If it already is blank, no sweat, otherwise we want to do
+ * a clear to end of line function. If the saved_screen sequence number
+ * is zero, this is a flag that the line is already clear.
+ */
+ if(his_lp == NULL){
+ if(lp->sequence == 0) continue;
+ for(x = 0; x < term_columns; x++,osp++){
+ if((*osp & SCREEN_M_DATA) != ' '){
+ if(cury != y || curx > x) term_goto(x,y);
+ if(cur_mode & SCREEN_M_REVERSE){
+ term_standend();
+ cur_mode &= ~SCREEN_M_REVERSE;
+ }/* End IF */
+ term_clrtoeol();
+ while(x < term_columns){
+ *osp++ = ' ';
+ x += 1;
+ }/* End While */
+ }/* End IF */
+ }/* End FOR */
+ lp->sequence = 0;
+ continue;
+ }/* End IF */
+/*
+ * If the line buffer sequence number is less than that of the saved screen
+ * sequence number, we know that this line has not been changed, and thus we
+ * don't even have to check to see whether they match.
+ */
+ if(his_lp->fmt_sequence <= lp->sequence) continue;
+ lp->sequence = screen_sequence;
+ sp = his_lp->fmt_buffer;
+ his_lp->fmt_visible_line_position = y;
+/*
+ * Well, changes have been made, so we have to go through the line on a byte
+ * by byte basis comparing character positions.
+ */
+ for(x = 0; x < term_columns; x++,sp++,osp++){
+/*
+ * If the characters are the same we don't have to take any special action
+ * unless it is a newline in which case sometimes we have special things to do
+ */
+ if((*sp & SCREEN_M_DATA) != '\n' && *osp == *sp){
+ continue;
+ }/* End If */
+/*
+ * If it IS a newline, we want to make sure that the screen shows blanks
+ * for the rest of this line. If it does not, we have to do a clear to
+ * end of line operation and update the saved screen database to reflect
+ * this.
+ */
+ if((*sp & SCREEN_M_DATA) == '\n'){
+ for(; x < term_columns; x++,osp++){
+ if(*osp == ' ') continue;
+ if(curx != x || cury != y) term_goto(x,y);
+ if(cur_mode & SCREEN_M_REVERSE){
+ term_standend();
+ cur_mode &= ~SCREEN_M_REVERSE;
+ }/* End IF */
+ term_clrtoeol();
+ for(; x < term_columns; x++){
+ *osp++ = ' ';
+ }/* End FOR */
+ }/* End FOR */
+ break;
+ }/* End IF */
+/*
+ * Copy the byte into our database to reflect the new appearance of the screen
+ */
+ *osp = *sp;
+ pos_flag = 1;
+/*
+ * If the current position of the cursor is not the position of the character
+ * we want to update, we have to position to that place. If we are on the
+ * wrong line, or if we are already to the right of the desired position,
+ * we have to do a full position escape sequence.
+ */
+ if(cury != y || curx > x){
+ term_goto(x,y);
+ pos_flag = 0;
+ }/* End IF */
+/*
+ * If we are not positioned yet, check to see if we are only a few positions
+ * to the left of where we want to be. If this is the case, it is faster to
+ * output the actual characters than to generate a full cursor motion escape
+ * sequence.
+ */
+ if(pos_flag && x - curx < 5){
+ while(curx < x){
+ if((his_lp->fmt_buffer[curx] & SCREEN_M_REVERSE) !=
+ (cur_mode & SCREEN_M_REVERSE)) break;
+ term_putc(his_lp->fmt_buffer[curx] & SCREEN_M_DATA);
+ curx++;
+ }/* End While */
+ pos_flag = 0;
+ }/* End IF */
+/*
+ * If we get to here and the position is not correct, no shortcuts helped so
+ * we have to output the full escape sequence to position the cursor
+ */
+ if(curx != x || cury != y) term_goto(x,y);
+/*
+ * Check to see that we are in the correct mode (standout or not). If not,
+ * we have to change it before we output the data byte
+ */
+ if((*sp & SCREEN_M_REVERSE) != (cur_mode & SCREEN_M_REVERSE)){
+ if(*sp & SCREEN_M_REVERSE){
+ term_standout();
+ cur_mode |= SCREEN_M_REVERSE;
+ }/* End IF */
+ else {
+ term_standend();
+ cur_mode &= ~SCREEN_M_REVERSE;
+ }/* End Else */
+ }/* End IF */
+/*
+ * Output the data byte and record that that changes the current x position
+ */
+ term_putc(*sp & SCREEN_M_DATA);
+ curx++;
+
+ }/* End FOR */
+ }/* End FOR */
+
+/*
+ * We don't want to leave the terminal in reverse video between screen updates,
+ * so if we are in reverse video mode, put it back to non-reverse.
+ */
+ if(cur_mode & SCREEN_M_REVERSE){
+ term_standend();
+ cur_mode &= ~SCREEN_M_REVERSE;
+ }/* End IF */
+
+/*
+ * Make sure that the cursor gets left in the position that indicates the
+ * "dot" position in the edit buffer.
+ */
+ if(curx != cursor_x || cury != cursor_y){
+ term_goto(cursor_x,cursor_y);
+ }/* End IF */
+
+/*
+ * Force this all to be written to the terminal, and increment the sequence
+ * number
+ */
+ term_flush();
+ screen_gc_format_lines();
+ screen_sequence += 1;
+
+}/* End Routine */
+
+
+
+/* SCREEN_OPTIMIZE_LINES - Optimize output with insert/delete line operations
+ *
+ * Function:
+ *
+ * This routine looks for left over mappings between our saved screen
+ * array and the formatted format_line structures. Old mappings can
+ * tell us how lines that are still going to be visible have moved.
+ */
+void
+screen_optimize_lines()
+{
+register int y;
+int vlp;
+register struct screen_line *lp;
+int blk_delete_count;
+int blk_insert_count;
+int companion_count;
+int offset;
+
+ PREAMBLE();
+
+ companion_count = 0;
+
+/*
+ * Search for a line which is known to be out of place
+ */
+ for(y = 0; y < buffer_li; y++){
+ lp = &saved_screen[y];
+ if(lp->companion == NULL) continue;
+ if((vlp = lp->companion->fmt_visible_line_position) == -1) continue;
+ if(vlp != y) break;
+ companion_count += 1;
+ break;
+ }/* End FOR */
+
+ if(y == buffer_li){
+ if(companion_count == 0) screen_optimize_by_pattern_matching();
+ return;
+ }/* End IF */
+
+/*
+ * Search for delete line opportunities
+ */
+ offset = 0;
+ for(y = 0; y < term_lines; y++){
+ lp = &saved_screen[y];
+ if(lp->companion == NULL) continue;
+ if((vlp = lp->companion->fmt_visible_line_position) == -1) continue;
+ vlp += offset;
+ if(vlp == y) continue;
+
+ if(vlp > y){
+ blk_delete_count = vlp - y;
+ term_delete_line(y-offset,blk_delete_count);
+ screen_account_for_delete_line(y-offset,blk_delete_count);
+ continue;
+ }/* End IF */
+
+ if(vlp < y){
+ offset += y - vlp;
+ }/* End IF */
+
+ }/* End FOR */
+/*
+ * Search for insert line opportunities
+ */
+ for(y = 0; y < term_lines; y++){
+ lp = &saved_screen[y];
+ if(lp->companion == NULL) continue;
+ if((vlp = lp->companion->fmt_visible_line_position) == -1) continue;
+ if(vlp == y) continue;
+
+ if(vlp > y){
+ void screen_message();
+ screen_message("insert_line confused");
+ }/* End IF */
+
+ if(vlp < y){
+ blk_insert_count = y - vlp;
+ term_insert_line(vlp,blk_insert_count);
+ screen_account_for_insert_line(vlp,blk_insert_count);
+ }/* End IF */
+
+ }/* End FOR */
+
+}/* End Routine */
+
+
+
+/* SCREEN_ACCOUNT_FOR_DELETE_LINE - Update screen database
+ *
+ * Function:
+ *
+ * This routine is called when a delete line sequence is sent to
+ * the terminal. It adjusts the screen database to reflect what
+ * has happened to the terminal.
+ */
+void
+screen_account_for_delete_line(y,count)
+int y;
+int count;
+{
+register int i,x;
+register struct screen_line *top_lp,*bottom_lp;
+register struct format_line *slp;
+register short *sp;
+
+ PREAMBLE();
+
+/*
+ * The result of a delete line operation will be that starting at the
+ * location it was issued, lines will be replaced by the contents of
+ * lines lower down on the screen.
+ */
+ top_lp = &saved_screen[y];
+ bottom_lp = top_lp + count;
+
+ for(i = y + count; i < term_lines; i++,top_lp++,bottom_lp++){
+ sp = top_lp->buffer;
+ top_lp->buffer = bottom_lp->buffer;
+ bottom_lp->buffer = sp;
+
+ top_lp->sequence = bottom_lp->sequence;
+ }/* End FOR */
+
+ top_lp = saved_screen;
+ for(i = 0; i < term_lines; i++,top_lp++){
+ if((slp = top_lp->companion) == NULL) continue;
+ if(slp->fmt_visible_line_position >= y){
+ slp->fmt_visible_line_position -= count;
+ }/* End IF */
+ if(slp->fmt_visible_line_position < 0){
+ slp->fmt_visible_line_position = -1;
+ }/* End IF */
+ }/* End FOR */
+
+ top_lp = &saved_screen[term_lines - count];
+ for(i = term_lines - count; i < term_lines; i++,top_lp++){
+
+ top_lp->sequence = 0;
+
+ sp = top_lp->buffer;
+ for(x = 0; x < term_columns; x++){
+ *sp++ = ' ';
+ }/* End FOR */
+
+ }/* End FOR */
+
+}/* End Routine */
+
+/* SCREEN_ACCOUNT_FOR_INSERT_LINE - Update screen database
+ *
+ * Function:
+ *
+ * This routine is called when an insert line sequence is sent to
+ * the terminal. It adjusts the screen database to reflect what
+ * has happened to the terminal appearence.
+ */
+void
+screen_account_for_insert_line(y,count)
+int y;
+int count;
+{
+register int i,x;
+register struct screen_line *top_lp,*bottom_lp;
+register struct format_line *slp;
+register short *sp;
+
+ PREAMBLE();
+
+ top_lp = &saved_screen[term_lines - count - 1];
+ bottom_lp = &saved_screen[term_lines - 1];
+
+ for(i = term_lines - count - 1; i >= y; i--,top_lp--,bottom_lp--){
+ sp = bottom_lp->buffer;
+ bottom_lp->buffer = top_lp->buffer;
+ top_lp->buffer = sp;
+
+ bottom_lp->sequence = top_lp->sequence;
+
+ }/* End FOR */
+
+ top_lp = saved_screen;
+ for(i = 0; i < term_lines; i++,top_lp++){
+ if((slp = top_lp->companion) == NULL) continue;
+ if(slp->fmt_visible_line_position >= y){
+ slp->fmt_visible_line_position += count;
+ }/* End IF */
+ if(slp->fmt_visible_line_position >= term_lines){
+ slp->fmt_visible_line_position = -1;
+ }/* End IF */
+ }/* End FOR */
+
+ top_lp = &saved_screen[y];
+ for(i = 0; i < count; i++,top_lp++){
+
+ top_lp->sequence = 0;
+
+ sp = top_lp->buffer;
+ for(x = 0; x < term_columns; x++){
+ *sp++ = ' ';
+ }/* End FOR */
+ }/* End FOR */
+
+}/* End Routine */
+
+
+
+/* SCREEN_OPTIMIZE_BY_PATTERN_MATCHING - More insert/delete line optimizations
+ *
+ * Function:
+ *
+ * This routine is called previous to the brute force screen repaint,
+ * but after the first crack at insert/delete line optimization. This
+ * routine looks for patterns of lines which indicate insert/delete
+ * line can save us some output.
+ */
+void
+screen_optimize_by_pattern_matching()
+{
+register int x,y,i,c;
+register struct screen_line *lp;
+register struct format_line *his_lp;
+register short *sp,*osp;
+char stuff_flag;
+int old_check[SCREEN_MAX_LINES],new_check[SCREEN_MAX_LINES];
+int first_change,last_change;
+
+ PREAMBLE();
+
+ stuff_flag = 0;
+ for(y = 0; y < buffer_li; y++){
+
+/*
+ * Get pointers to the saved screen entry which tells us what the screen
+ * currently looks like, and pointers to the line buffer structure which
+ * tells us what the screen ought to look like when we are done.
+ */
+ lp = saved_screen + y;
+ osp = lp->buffer;
+ his_lp = lp->companion;
+/*
+ * If there is no line buffer structure, the line ought to appear blank on
+ * the screen. If it already is blank, no sweat, otherwise we want to do
+ * a clear to end of line function.
+ */
+ new_check[y] = old_check[y] = 0;
+ if(his_lp && his_lp->fmt_sequence <= lp->sequence) continue;
+ if(his_lp == NULL && lp->sequence == 0) continue;
+/*
+ * Calculate the checksum for the data currently showing on the screen
+ */
+ for(x = 0; x < term_columns; x++,osp++){
+ if((c = *osp & SCREEN_M_DATA) != ' '){
+ old_check[y] = (old_check[y] << 1) + c;
+ stuff_flag = 1;
+ }/* End IF */
+ }/* End FOR */
+
+ if(his_lp == NULL) continue;
+ sp = his_lp->fmt_buffer;
+/*
+ * Calculate the checksum for the data which we want to be showing on the
+ * screen.
+ */
+ for(x = 0; x < term_columns; x++,sp++){
+ c = *sp & SCREEN_M_DATA;
+ if(c == ' ') continue;
+ if(c == '\n') break;
+ new_check[y] = (new_check[y] << 1) + c;
+ stuff_flag = 1;
+ }/* End FOR */
+
+ }/* End FOR */
+
+/*
+ * If no changes have been made, just return
+ */
+ if(!stuff_flag) return;
+
+/*
+ * Determine which region of the screen contains lines which have changed
+ */
+ first_change = last_change = -1;
+ for(y = 0; y < buffer_li; y++){
+ if(old_check[y] == new_check[y]) continue;
+ first_change = y;
+ break;
+ }/* End FOR */
+
+/*
+ * If checksum arrays seem to be identical or only one line changed, then
+ * just return and don't attempt further optimization.
+ */
+ if(first_change == -1) return;
+
+ for(y = buffer_li - 1; y >= first_change; y--){
+ if(old_check[y] == new_check[y]) continue;
+ last_change = y;
+ break;
+ }/* End FOR */
+
+ if(first_change == last_change) return;
+
+/*
+ * Here is the code which tries for a delete-line optimization
+ */
+ for(i = 1; i < last_change - first_change - 3; i++){
+ if(old_check[first_change + i] == new_check[first_change]){
+ for(y = first_change; y < last_change - i; y++){
+ if(old_check[y + i] != new_check[y]) break;
+ }/* End FOR */
+ if(y == (last_change - i)){
+
+ term_delete_line(first_change,i);
+ screen_account_for_delete_line(first_change,i);
+ term_insert_line(last_change+1-i,i);
+ screen_account_for_insert_line(last_change+1-i,i);
+
+ return;
+
+ }/* End IF */
+ }/* End IF */
+ }/* End FOR */
+
+/*
+ * Here is the code which tries for a insert-line optimization
+ */
+ for(i = 1; i < last_change - first_change - 3; i++){
+ if(old_check[first_change] == new_check[first_change + i]){
+ for(y = first_change; y < last_change - i; y++){
+ if(old_check[y] != new_check[y + i]) break;
+ }/* End FOR */
+
+ if(y == (last_change - i)){
+ term_delete_line(last_change+1-i,i);
+ screen_account_for_delete_line(last_change+1-i,i);
+ term_insert_line(first_change,i);
+ screen_account_for_insert_line(first_change,i);
+ break;
+ }/* End IF */
+
+ }/* End IF */
+ }/* End FOR */
+
+}/* End Routine */
+
+
+
+/* SCREEN_ECHO - Echo the character
+ *
+ * Function:
+ *
+ * This routine allows the parser to hand us input characters which need
+ * to be echoed. The reason this is done here instead of by the normal
+ * screen formatting routines is that some characters are echoed in a
+ * different way than they are normally displayed. The most notable
+ * example is newline which just indicates end of line in the normal
+ * buffer output, but which gets echoed as '<CR>' when it is an echo
+ * operation.
+ */
+void
+screen_echo(data)
+char data;
+{
+register int i;
+
+ PREAMBLE();
+
+/*
+ * If we are getting close to the right edge of the screen, we remove the
+ * first 3/4 of the echo line and just redisplay the last 1/4 to make room
+ * for future echo characters.
+ */
+ if(echo_cnt > (term_columns - 4)){
+ for(i = 0; i < (term_columns / 4); i++){
+ echo_line.fmt_buffer[ 1 + i ] =
+ echo_line.fmt_buffer[ 1 + echo_cnt - (term_columns / 4) + i ];
+ }/* End FOR */
+ echo_cnt = i;
+ for(; i < term_columns-2; i++){
+ echo_line.fmt_buffer[ 1 + i ] = ' ';
+ }/* End FOR */
+ }/* End IF */
+
+/*
+ * Dispatch the byte to determine how it should be formatted. NULL is a
+ * special case which is used to flag the fake cursor position on the
+ * echo line. This is shown by outputing a reverse-video space to the
+ * position.
+ */
+ switch(data){
+ case '\0':
+
+/*
+ * If we are on a high-speed line, do the reverse-video space trick to
+ * show a non-blinking cursor.
+ */
+ if(term_speed > 1200){
+ echo_line.fmt_buffer[ 1 + echo_cnt ] = ' ' | SCREEN_M_REVERSE;
+ echo_line.fmt_buffer[ 2 + echo_cnt ] = ' ';
+ echo_line.fmt_buffer[ 3 + echo_cnt ] = '\n';
+ }/* End IF */
+/*
+ * Otherwise, if we are on a low speed line, just insert a carriage return
+ * so that the rest of the line is shown as blanks.
+ */
+ else {
+ echo_line.fmt_buffer[ 1 + echo_cnt ] = '\n';
+ }/* End IF */
+
+ echo_line.fmt_sequence = screen_sequence;
+ return;
+
+ case ESCAPE:
+ data = '$';
+ break;
+ case '\n':
+ for(i = 0; i < strlen("<CR>"); i++){
+ screen_echo("<CR>"[i]);
+ }/* End FOR */
+ return;
+ case RUBOUT:
+ return;
+/*
+ * Tabs are always echoed as a fixed length string of spaces regardless of
+ * the screen position since normal tab algorithms look weird on the echo
+ * line.
+ */
+ case '\t':
+ for(i = 0; i < strlen(" "); i++){
+ screen_echo(" "[i]);
+ }/* End FOR */
+ return;
+ default:
+ break;
+ }/* End Switch */
+
+/*
+ * If this is a control character, echo as ^byte
+ */
+ if((unsigned char)data < ' '){
+ screen_echo('^');
+ screen_echo(data + 'A' - 1);
+ return;
+ }/* End IF */
+
+/*
+ * Here if it is a normal printable character. We can just echo the character
+ * as is with no special stuff going on.
+ */
+ echo_line.fmt_buffer[ 1 + echo_cnt++ ] = data;
+ echo_line.fmt_sequence = screen_sequence;
+
+}/* End Routine */
+
+/* SCREEN_RESET_ECHO - Reset the echo line
+ *
+ * Function:
+ *
+ * This routine is called to reset the echo line according to the current
+ * list of command tokens. This is used after difficult things like rubout
+ * which would be very difficult to back out of.
+ */
+void
+screen_reset_echo(ct)
+register struct cmd_token *ct;
+{
+
+ PREAMBLE();
+
+ echo_line.fmt_buffer[ 1 + echo_cnt ] = ' ';
+
+ while(echo_cnt){
+ echo_cnt -= 1;
+ echo_line.fmt_buffer[ 1 + echo_cnt ] = ' ';
+ }/* End While */
+
+ while(ct){
+ if(ct->opcode == TOK_C_INPUTCHAR){
+ screen_echo(ct->input_byte);
+ }/* End IF */
+ ct = ct->next_token;
+ }/* End While */
+
+ echo_line.fmt_buffer[ 1 + echo_cnt ] = '\n';
+
+ echo_line.fmt_sequence = screen_sequence;
+
+}/* End Routine */
+
+
+
+/* SCREEN_MESSAGE - Place a message in the message line
+ *
+ * Function:
+ *
+ * This routine allows the parser to hand us messages which need to be
+ * displayed. These can be error messages generated by the parse, or
+ * they can be output messages from a user generated with the ^A command.
+ */
+void
+screen_message(string)
+register char *string;
+{
+register short *sp;
+register int i;
+register int c = 0;
+register char *mep;
+char multi_echo_buffer[32];
+
+ PREAMBLE();
+
+ message_flag = YES;
+ sp = message_line.fmt_buffer;
+ i = term_columns;
+ mep = "";
+
+ while(*mep || *string){
+ while(*mep){
+ *sp++ = c = *mep++;
+ if(i-- <= 0) goto too_wide;
+ continue;
+ }/* End While */
+
+ c = *string++;
+
+ switch(c){
+// case NULL:
+ case 0:
+ goto too_wide;
+ case ESCAPE:
+ c = '$';
+ break;
+ case '\n':
+ mep = "<CR>";
+ continue;
+ case RUBOUT:
+ mep = "<RUBOUT>";
+ continue;
+ case '\t':
+ mep = " ";
+ continue;
+ }/* End Switch */
+
+ if((unsigned char)c < ' '){
+ multi_echo_buffer[0] = '^';
+ multi_echo_buffer[1] = c + 'A' - 1;
+ multi_echo_buffer[2] = '\0';
+ mep = multi_echo_buffer;
+ continue;
+ }/* End IF */
+
+ *sp++ = c;
+ if(i-- <= 0) goto too_wide;
+ }/* End While */
+
+too_wide:
+ if(c != '\n' && i > 0) *sp++ = '\n';
+ message_line.fmt_sequence = screen_sequence;
+
+}/* End Routine */
+
+/* ERROR_MESSAGE - Place an error message in the message line
+ *
+ * Function:
+ *
+ * This routine allows the parser to hand us error messages
+ * which need to be displayed. In addition, the BELL character
+ * is output to get the humans attention.
+ */
+void
+error_message(string)
+char *string;
+{
+ PREAMBLE();
+
+ screen_message(string);
+ ring_audible_bell = 1;
+
+}/* End Routine */
+
+/* SCREEN_RESET_MESSAGE - Reset the message line
+ *
+ * Function:
+ *
+ * This routine is called to reset the message line to null
+ */
+void
+screen_reset_message()
+{
+register int i;
+register short *sp;
+
+ PREAMBLE();
+
+ if(message_flag == NO) return;
+ message_flag = NO;
+ sp = message_line.fmt_buffer;
+
+ for(i = 0; i < term_columns; i++){
+ *sp++ = ' ';
+ }/* End FOR */
+
+ message_line.fmt_sequence = screen_sequence;
+
+}/* End Routine */
+
+/* SCREEN_SAVE_CURRENT_MESSAGE - Copy out the current message
+ *
+ * Function:
+ *
+ * This routine copies out the current message up to a certain length.
+ */
+void
+screen_save_current_message(message_save_buff,message_save_max_length)
+char *message_save_buff;
+int message_save_max_length;
+{
+int i;
+short *sp;
+
+ PREAMBLE();
+
+ sp = message_line.fmt_buffer;
+ i = term_columns;
+ if(i >= message_save_max_length) i = message_save_max_length - 1;
+ while(i-- > 0){
+ if((*sp & SCREEN_M_DATA) == '\n') break;
+ *message_save_buff++ = *sp++ & SCREEN_M_DATA;
+ }
+ *message_save_buff = '\0';
+
+}/* End Routine */
+
+
+
+/* SCREEN_LABEL_LINE - Routine to change the contents of the label line
+ *
+ * Function:
+ *
+ * This routine is called to change the label line
+ */
+int
+screen_label_line(buffer,string,field)
+struct buff_header *buffer;
+char *string;
+int field;
+{
+register short *sp;
+register char *cp;
+register int i;
+register int column;
+register struct window *wptr;
+
+ PREAMBLE();
+
+/*
+ * If the field specified is out of range, punt
+ */
+ if(field < 0 || field >= SCREEN_MAX_LABEL_FIELDS){
+ screen_message("Illegal field specified to label line\n");
+ return(FAIL);
+ }/* End IF */
+
+/*
+ * We want to modify the label in any window which is displaying this buffer.
+ * There could be several windows with the same buffer displayed...
+ */
+ for(wptr = window_list ; wptr != NULL; wptr = wptr->win_next_window){
+ if(wptr->win_buffer != buffer) continue;
+/*
+ * If no space has been allocated for this field yet, we need to call the
+ * allocator. Notice the overkill in that each field gets more than enough
+ * memory.
+ */
+ if(wptr->win_label_field_contents[field] == NULL){
+ wptr->win_label_field_contents[field] =
+ tec_alloc(TYPE_C_LABELFIELD,SCREEN_NOMINAL_LINE_WIDTH);
+ if(wptr->win_label_field_contents[field] == NULL){
+ return(FAIL);
+ }/* End IF */
+ *(wptr->win_label_field_contents[field]) = '\0';
+ }/* End IF */
+/*
+ * If the current contents of the field are already what we want, we don't
+ * have to change a thing.
+ */
+ if(strcmp(string,wptr->win_label_field_contents[field]) == 0) continue;
+ (void) strcpy(wptr->win_label_field_contents[field],string);
+/*
+ * Figure out which column the field begins on. This changes as fields change,
+ * so it must be computed each time a change happens.
+ */
+ for(i = 0 , column = 0; i < field; i++){
+ if(wptr->win_label_field_contents[i] == NULL) continue;
+ column += strlen(wptr->win_label_field_contents[i]) + 1;
+ }/* End FOR */
+/*
+ * Now we have to update the label line from this column onward until we
+ * reach the end of the final field.
+ */
+ sp = &wptr->win_label_line.fmt_buffer[column];
+ for(i = field; i < SCREEN_MAX_LABEL_FIELDS; i++){
+ cp = wptr->win_label_field_contents[i];
+ if(cp == NULL) continue;
+ while(*cp){
+ if(column >= term_columns) break;
+ *sp++ = *cp++ | SCREEN_M_REVERSE;
+ column += 1;
+ }/* End While */
+ if(column >= term_columns) break;
+ *sp++ = ' ' | SCREEN_M_REVERSE;
+ column += 1;
+ }/* End FOR */
+/*
+ * Blank fill to the end of the screen incase we shrank the label line.
+ */
+ while(column < term_columns){
+#ifdef TERMCAP
+ *sp++ = termcap_sg ? '-' : ' ' | SCREEN_M_REVERSE;
+#endif
+#ifdef TERMINFO
+ *sp++ = (terminfo_magic_cookie_glitch > 0) ?
+ '-' : ' ' | SCREEN_M_REVERSE;
+#endif
+ column += 1;
+ }/* End While */
+/*
+ * Insure that the next screen refresh knows this line has changed
+ */
+ wptr->win_label_line.fmt_sequence = screen_sequence;
+
+ }/* End FOR */
+
+ return( SUCCESS );
+
+}/* End Routine */
+
+
+
+/* SCREEN_LABEL_WINDOW - Routine to set the window number field
+ *
+ * Function:
+ *
+ * This routine is called to modify the TECONAME field of the
+ * label lines to reflect the window number.
+ */
+int
+screen_label_window()
+{
+register short *sp;
+register char *cp;
+register int i;
+register int column;
+register struct window *wptr;
+char tmp_buffer[SCREEN_NOMINAL_LINE_WIDTH];
+int field = LABEL_C_TECONAME;
+
+ PREAMBLE();
+
+/*
+ * We want to check the label in every window
+ */
+ for(wptr = window_list ; wptr != NULL; wptr = wptr->win_next_window){
+/*
+ * If no space has been allocated for this field yet, we need to call the
+ * allocator. Notice the overkill in that each field gets more than enough
+ * memory.
+ */
+ if(wptr->win_label_field_contents[field] == NULL){
+ wptr->win_label_field_contents[field] =
+ tec_alloc(TYPE_C_LABELFIELD,SCREEN_NOMINAL_LINE_WIDTH);
+ if(wptr->win_label_field_contents[field] == NULL){
+ return(FAIL);
+ }/* End IF */
+ *(wptr->win_label_field_contents[field]) = '\0';
+ }/* End IF */
+/*
+ * If the current contents of the field are already what we want, we don't
+ * have to change a thing.
+ */
+ sprintf(tmp_buffer," TECO-%d",wptr->win_window_number);
+ if(strcmp(tmp_buffer,wptr->win_label_field_contents[field]) == 0){
+ continue;
+ }/* End IF */
+
+ (void) strcpy(wptr->win_label_field_contents[field],tmp_buffer);
+
+/*
+ * Now we have to update the label line from this column onward until we
+ * reach the end of the final field.
+ */
+ column = 0;
+ sp = &wptr->win_label_line.fmt_buffer[column];
+ for(i = 0; i < SCREEN_MAX_LABEL_FIELDS; i++){
+ cp = wptr->win_label_field_contents[i];
+ if(cp == NULL) continue;
+ while(*cp){
+ if(column >= term_columns) break;
+ *sp++ = *cp++ | SCREEN_M_REVERSE;
+ column += 1;
+ }/* End While */
+ if(column >= term_columns) break;
+ *sp++ = ' ' | SCREEN_M_REVERSE;
+ column += 1;
+ }/* End FOR */
+/*
+ * Blank fill to the end of the screen incase we shrank the label line.
+ */
+ while(column < term_columns){
+ *sp++ = ' ' | SCREEN_M_REVERSE;
+ column += 1;
+ }/* End While */
+/*
+ * Insure that the next screen refresh knows this line has changed
+ */
+ wptr->win_label_line.fmt_sequence = screen_sequence;
+
+ }/* End FOR */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* SCREEN_FORMAT_WINDOWS - Generate display contents for all windows
+ *
+ * Function:
+ *
+ * This is the main entry point to the screen formatting function.
+ * It causes screen format_lines to be generated for all the buffers
+ * that are currently visible on the screen.
+ */
+void
+screen_format_windows()
+{
+register struct window *wptr;
+
+ PREAMBLE();
+
+ for(wptr = window_list; wptr != NULL; wptr = wptr->win_next_window){
+ screen_display_window(wptr);
+ }/* End FOR */
+
+}/* End Routine */
+
+/* SCREEN_REFORMAT_WINDOWS - Reformat windows (because tab size changed)
+ *
+ * Function:
+ *
+ * This entry point is called when the format buffers of all the
+ * displayed windows need to be regenerated. The current case in
+ * mind is when the user changes the tab stops - this causes all
+ * the format lines to be obsolete.
+ */
+void
+screen_reformat_windows()
+{
+register int i;
+register struct screen_line *lp;
+
+ PREAMBLE();
+
+/*
+ * The screen_gc_format_line is going to scavenge all the lines because
+ * we're making them look not-used here. However, we still go ahead and
+ * clobber the line_buffer's pointer, because we don't like the idea of
+ * having hanging pointers pointed at our screen lines.
+ */
+ if(saved_screen_valid){
+ for(lp = saved_screen, i = 0; i < term_lines; i++,lp++){
+ if(lp->companion){
+ if(lp->companion->fmt_saved_line != lp){
+ fprintf(
+ stderr,
+ "lp->companion 0x%x != ->fmt_saved_line 0x%x\n",
+ (unsigned int)lp->companion,
+ (unsigned int)lp->companion->fmt_saved_line
+ );
+ CAUSE_BUS_ERROR();
+ }/* End IF */
+ if( lp->companion->fmt_permanent ) continue;
+ lp->companion->fmt_saved_line = NULL;
+ lp->companion = NULL;
+ }/* End IF */
+ }/* End FOR */
+ }/* End IF */
+
+ screen_gc_format_lines();
+
+}/* End Routine */
+
+/* SCREEN_DISPLAY_WINDOW - Generate display for a window
+ *
+ * Function:
+ *
+ * This routine is called to build up the format line descriptor
+ * structures which represent the edit buffer displayed in the
+ * specified window.
+ */
+int
+screen_display_window(wptr)
+register struct window *wptr;
+{
+struct buff_header *hbp;
+struct buff_line *tlbp,*blbp;
+struct format_line *tsbp,*bsbp;
+struct screen_line *sp;
+register int i;
+int lines;
+int anchor_line;
+char saw_dot;
+
+ PREAMBLE();
+
+/*
+ * Find a line in our saved screen array that still maps to a buffer location
+ */
+ sp = saved_screen + wptr->win_y_base;
+ anchor_line = -1;
+ for(i = wptr->win_y_base; i < wptr->win_y_end; i++,sp++){
+ if(sp->companion == NULL) continue;
+ if(sp->companion->fmt_owning_buffer != wptr->win_buffer) continue;
+ anchor_line = i;
+ break;
+ }/* End FOR */
+
+ if(anchor_line < 0){
+ return(screen_rebuild_from_scratch(wptr));
+ }/* End IF */
+
+/*
+ * Now walk the top back
+ */
+ tsbp = sp->companion;
+ tlbp = tsbp->fmt_buffer_line;
+ while(i > wptr->win_y_base){
+
+/*
+ * If there is a format line in front of the one we are on, just back
+ * up to it.
+ */
+ if(tsbp->fmt_prev_line){
+ tsbp = tsbp->fmt_prev_line;
+ i -= 1;
+ continue;
+ }/* End IF */
+/*
+ * Else if there is a buffer line in front of us, get to the final
+ * format line associated with it.
+ */
+ if(tlbp->prev_line != NULL){
+ tlbp = tlbp->prev_line;
+ tsbp = screen_find_window_format_line(wptr,tlbp);
+ if(tsbp == NULL){
+ if(screen_format_buff_line(wptr,tlbp) == FAIL) return(FAIL);
+ tsbp = screen_find_window_format_line(wptr,tlbp);
+ }/* End IF */
+ while(tsbp->fmt_next_line) tsbp = tsbp->fmt_next_line;
+ i -= 1;
+ continue;
+ }/* End IF */
+/*
+ * If we got here, that means that before we could work back up to the old
+ * top line, we hit the begining of the buffer. Give up.
+ */
+ return(screen_rebuild_from_scratch(wptr));
+
+ }/* End While */
+
+/*
+ * If we make it to here, we have worked back to the top of the screen, now
+ * we want to work down to the bottom of the screen.
+ */
+ lines = 1;
+ blbp = tlbp;
+ bsbp = tsbp;
+
+/*
+ * Determine the format_line that dot lies on so that as we work forward we can
+ * keep an eye out for it.
+ */
+ hbp = wptr->win_buffer;
+ buff_find_line(hbp,hbp->dot);
+ screen_find_dot(wptr);
+ saw_dot = 0;
+
+ while(lines <= wptr->win_y_size){
+
+/*
+ * If we see the line that dot is on, remember it. The one case when we don't
+ * do this is if it is the top line of the screen. The reason for this is to
+ * avoid over-optimizing such that the user makes changes but never sees them
+ * because the changes are above the top line of the screen.
+ */
+ if(bsbp == wptr->win_dot_format_line && lines != 1) saw_dot = 1;
+
+/*
+ * If there is a format line below our current bottom one, simply go
+ * forward to it.
+ */
+ if(bsbp->fmt_next_line){
+ bsbp = bsbp->fmt_next_line;
+ lines += 1;
+ continue;
+ }/* End IF */
+/*
+ * If we have to go on to the next buffer line, but there is not one there,
+ * that means that we hit the end of the buffer.
+ */
+ if(blbp->next_line == NULL){
+ return(screen_rebuild_from_scratch(wptr));
+ }/* End IF */
+/*
+ * Else, if there is another buffer line after this, go to the first
+ * format line associated with it.
+ */
+ if(blbp->next_line != NULL){
+ blbp = blbp->next_line;
+ bsbp = screen_find_window_format_line(wptr,blbp);
+ if(bsbp == NULL){
+ if(screen_format_buff_line(wptr,blbp) == FAIL) return(FAIL);
+ bsbp = screen_find_window_format_line(wptr,blbp);
+ }/* End IF */
+ lines += 1;
+ }/* End IF */
+
+ }/* End While */
+
+/*
+ * Test to see whether we ever saw dot. If not, we need to do the scratch
+ * screen rebuild.
+ */
+ if(curwin == wptr && saw_dot == 0){
+ return(screen_rebuild_from_scratch(wptr));
+ }/* End IF */
+
+/*
+ * Now we can start setting up the screen appearance
+ */
+ sp = saved_screen + wptr->win_y_base;
+
+ for(i = wptr->win_y_base; i < wptr->win_y_end; i++,sp++){
+ if(tlbp == NULL){
+ if(sp->companion) sp->companion->fmt_saved_line = NULL;
+ sp->companion = NULL;
+ continue;
+ }/* End IF */
+
+ if(sp->companion != tsbp){
+ if(sp->companion) sp->companion->fmt_saved_line = NULL;
+ sp->companion = NULL;
+ if(tsbp->fmt_saved_line) tsbp->fmt_saved_line->companion = NULL;
+ tsbp->fmt_saved_line = NULL;
+ sp->companion = tsbp;
+ tsbp->fmt_saved_line = sp;
+ tsbp->fmt_sequence = screen_sequence;
+ }/* End IF */
+
+ if(curwin == wptr && tsbp == wptr->win_dot_format_line){
+ cursor_y = i;
+ cursor_x = wptr->win_dot_screen_offset;
+ }/* End IF */
+
+ tsbp = tsbp->fmt_next_line;
+ if(tsbp == NULL){
+ tlbp = tlbp->next_line;
+ if(tlbp){
+ tsbp = screen_find_window_format_line(wptr,tlbp);
+ }/* End IF */
+ }/* End IF */
+ }/* End FOR */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* SCREEN_REBUILD_FROM_SCRATCH - Things have changed a lot, just rebuild it
+ *
+ * Function:
+ *
+ * This routine is called to build up the format line descriptor
+ * structures which represent the current edit buffer.
+ */
+int
+screen_rebuild_from_scratch(wptr)
+register struct window *wptr;
+{
+struct buff_header *hbp;
+struct buff_line *tlbp,*blbp;
+struct format_line *tsbp,*bsbp;
+struct screen_line *scrbp;
+register int i;
+int lines,olines;
+
+ PREAMBLE();
+
+/*
+ * Find the buffer line that dot is on, and then the format_line that
+ * it is on.
+ */
+ hbp = wptr->win_buffer;
+ tlbp = blbp = buff_find_line(hbp,hbp->dot);
+ if(screen_find_dot(wptr) == FAIL) return(FAIL);
+ tsbp = bsbp = wptr->win_dot_format_line;
+
+ lines = 1;
+
+/*
+ * Now walk the top back and the bottom forward
+ */
+ while(lines < wptr->win_y_size){
+ olines = lines;
+
+/*
+ * If there is a format_line in front of the one we are on, just back
+ * up to it.
+ */
+ if(tsbp->fmt_prev_line){
+ tsbp = tsbp->fmt_prev_line;
+ lines += 1;
+ }/* End IF */
+
+/*
+ * Else if there is a buffer line in front of us, get to the final
+ * format_line associated with it.
+ */
+ else if(tlbp->prev_line != NULL){
+ tlbp = tlbp->prev_line;
+ tsbp = screen_find_window_format_line(wptr,tlbp);
+ if(tsbp == NULL){
+ if(screen_format_buff_line(wptr,tlbp) == FAIL) return(FAIL);
+ tsbp = screen_find_window_format_line(wptr,tlbp);
+ }/* End IF */
+ while(tsbp && tsbp->fmt_next_line) tsbp = tsbp->fmt_next_line;
+ lines += 1;
+ }/* End IF */
+
+/*
+ * If there is a format_line below our current bottom one, simply go
+ * forward to it.
+ */
+ if(bsbp->fmt_next_line){
+ bsbp = bsbp->fmt_next_line;
+ lines += 1;
+ }/* End IF */
+
+/*
+ * Else, if there is another buffer line after this, go to the first
+ * format_line associated with it.
+ */
+ else if(blbp->next_line != NULL){
+ blbp = blbp->next_line;
+ bsbp = screen_find_window_format_line(wptr,blbp);
+ if(bsbp == NULL){
+ if(screen_format_buff_line(wptr,blbp) == FAIL) return(FAIL);
+ bsbp = screen_find_window_format_line(wptr,blbp);
+ }/* End IF */
+ lines += 1;
+ }/* End IF */
+
+/*
+ * If we havn't made any progress, then the buffer is smaller than the
+ * screen, so just display it.
+ */
+ if(lines == olines) break;
+
+ }/* End While */
+
+/*
+ * Now we can start setting up the screen appearance
+ */
+ scrbp = saved_screen + wptr->win_y_base;
+
+ for(i = wptr->win_y_base; i < wptr->win_y_end; i++,scrbp++){
+ if(tlbp == NULL){
+ if(scrbp->companion) scrbp->companion->fmt_saved_line = NULL;
+ scrbp->companion = NULL;
+ continue;
+ }/* End IF */
+
+ if(scrbp->companion != tsbp){
+ if(scrbp->companion) scrbp->companion->fmt_saved_line = NULL;
+ scrbp->companion = NULL;
+ if(tsbp->fmt_saved_line){
+ tsbp->fmt_saved_line->companion = NULL;
+ tsbp->fmt_saved_line = NULL;
+ }/* End IF */
+ scrbp->companion = tsbp;
+ tsbp->fmt_saved_line = scrbp;
+ tsbp->fmt_sequence = screen_sequence;
+ }/* End IF */
+
+ if(curwin == wptr && tsbp == wptr->win_dot_format_line){
+ cursor_y = i;
+ cursor_x = wptr->win_dot_screen_offset;
+ }/* End IF */
+
+ tsbp = tsbp->fmt_next_line;
+ if(tsbp == NULL){
+ tlbp = tlbp->next_line;
+ if(tlbp){
+ tsbp = screen_find_window_format_line(wptr,tlbp);
+ if(tsbp == NULL){
+ if(screen_format_buff_line(wptr,tlbp) == FAIL){
+ return(FAIL);
+ }/* End IF */
+ tsbp = screen_find_window_format_line(wptr,tlbp);
+ }/* End IF */
+ }/* End IF */
+ }/* End IF */
+ }/* End FOR */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* SCREEN_SCROLL - Move the current screen up or down specified # lines
+ *
+ * Function:
+ *
+ * Here in response to the ES command. The user can scroll the screen
+ * up and down using this command. The current edit position does not
+ * move, so it is pointless to move it such that 'dot' moves off of the
+ * screen.
+ */
+void
+screen_scroll(count)
+int count;
+{
+struct window *wptr = curwin;
+struct screen_line *sp;
+register int i;
+
+ PREAMBLE();
+
+ i = count;
+ if(i < 0) i = -i;
+ if(i > wptr->win_y_end) return;
+
+/*
+ * If the scroll is forward, move the saved lines up in the array by count
+ */
+ if(count > 0){
+ sp = &saved_screen[wptr->win_y_base];
+ for(i = wptr->win_y_base; i < wptr->win_y_end - count; i++,sp++){
+ if(sp->companion) sp->companion->fmt_saved_line = NULL;
+ sp->companion = (sp+count)->companion;
+ (sp+count)->companion = NULL;
+ if(sp->companion == NULL) continue;
+ sp->companion->fmt_saved_line = sp;
+ sp->companion->fmt_sequence = screen_sequence;
+ }/* End FOR */
+ sp = &saved_screen[wptr->win_y_end - count];
+ for(i = wptr->win_y_end - count; i < wptr->win_y_end; i++,sp++){
+ if(sp->companion == NULL) continue;
+ sp->companion->fmt_saved_line = NULL;
+ sp->companion = NULL;
+ }/* End FOR */
+ }/* End IF */
+/*
+ * Else, if the scroll is backward, move the saved lines down in the array
+ */
+ else if(count < 0){
+ sp = &saved_screen[wptr->win_y_end - 1];
+ for(i = wptr->win_y_end - 1; i >= wptr->win_y_base - count; i--,sp--){
+ if(sp->companion) sp->companion->fmt_saved_line = NULL;
+ sp->companion = (sp+count)->companion;
+ (sp+count)->companion = NULL;
+ if(sp->companion == NULL) continue;
+ sp->companion->fmt_saved_line = sp;
+ sp->companion->fmt_sequence = screen_sequence;
+ }/* End FOR */
+ sp = &saved_screen[wptr->win_y_base];
+ for(i = wptr->win_y_base; i < wptr->win_y_base - count; i++,sp++){
+ if(sp->companion == NULL) continue;
+ sp->companion->fmt_saved_line = NULL;
+ sp->companion = NULL;
+ }/* End FOR */
+ }/* End IF */
+
+}/* End Routine */
+
+
+
+/* SCREEN_GC_FORMAT_LINES - Garbage collect unused format line structures
+ *
+ * Function:
+ *
+ * This routine determines which format structures are in use and
+ * deallocates the rest of them so that we don't use an excessive
+ * number of them.
+ */
+void
+screen_gc_format_lines()
+{
+register struct format_line *sbp,*tsbp;
+register struct screen_line *sp;
+register struct buff_line *lbp;
+register int i;
+struct window *wptr;
+
+ PREAMBLE();
+
+/*
+ * First mark all the format_line structures as unused
+ */
+ sbp = format_line_alloc_list;
+ while(sbp){
+ sbp->fmt_in_use = 0;
+ sbp = sbp->fmt_next_alloc;
+ }/* End While */
+
+/*
+ * Now for each displayed line, find the format structure associated with it.
+ * Then find the line_buffer structure which owns that format structure and
+ * mark all of the format structures under it as in use.
+ */
+ for(wptr = window_list ; wptr != NULL; wptr = wptr->win_next_window){
+ sp = saved_screen + wptr->win_y_base;
+ for(i = wptr->win_y_base; i < wptr->win_y_end; i++,sp++){
+ sbp = sp->companion;
+ if(sbp == NULL) continue;
+ if(sbp->fmt_permanent) continue;
+ if(sbp->fmt_buffer_line == NULL) continue;
+ sbp =
+ screen_find_window_format_line(
+ wptr,
+ sbp->fmt_buffer_line
+ );
+ if(sbp == NULL) continue;
+ while(sbp){
+ sbp->fmt_in_use = 1;
+ sbp = sbp->fmt_next_line;
+ }/* End While */
+ }/* End FOR */
+ }/* End FOR */
+/*
+ * Now go back and deallocate any format_line structures which are not in use
+ */
+ sbp = format_line_alloc_list;
+ tsbp = NULL;
+ while(sbp){
+ if(sbp->fmt_in_use){
+ tsbp = sbp;
+ sbp = sbp->fmt_next_alloc;
+ continue;
+ }/* End IF */
+
+ lbp = sbp->fmt_buffer_line;
+
+ screen_free_window_format_lines(sbp->fmt_window_ptr,lbp);
+
+ sbp = tsbp ? tsbp : format_line_alloc_list;
+
+ }/* End While */
+
+}/* End Routine */
+
+
+
+/* SCREEN_FREE_WINDOW_FORMAT_LINES - Deallocate a window's format lines
+ *
+ * Function:
+ *
+ * This routine is called to deallocate the list of format lines
+ * linked off of a line buffer, if they are associated with the
+ * specified window.
+ */
+void
+screen_free_window_format_lines(wptr,lbp)
+struct window *wptr;
+register struct buff_line *lbp;
+{
+register struct format_line *sbp,*tsbp;
+
+ PREAMBLE();
+
+/*
+ * There are a couple cases to check for: The first situation is when
+ * the very first format_line (chained off the buff_line structure) is
+ * the one we want to clobber.
+ */
+ sbp = lbp->format_line;
+ if(sbp == NULL) return;
+
+ if(sbp->fmt_window_ptr == wptr){
+ format_lines_invalid = 1;
+ tsbp = sbp->fmt_next_window;
+ lbp->format_line = tsbp;
+ if(tsbp) tsbp->fmt_prev_window = NULL;
+ sbp->fmt_next_window = NULL;
+ screen_free_format_lines(sbp);
+ format_lines_invalid = 0;
+ return;
+ }/* End IF */
+
+/*
+ * Here if it's not the head of the list. We want to keep the forward
+ * and backward pointers consistent, so we need to treat this case a
+ * little differently.
+ */
+ while((tsbp = sbp->fmt_next_window)){
+ if(tsbp->fmt_window_ptr == wptr){
+ format_lines_invalid = 1;
+ sbp->fmt_next_window = tsbp->fmt_next_window;
+ if(tsbp->fmt_next_window){
+ tsbp->fmt_next_window->fmt_prev_window = sbp;
+ }/* End IF */
+
+ tsbp->fmt_next_window = tsbp->fmt_prev_window = NULL;
+ screen_free_format_lines(tsbp);
+ format_lines_invalid = 0;
+ return;
+ }/* End IF */
+
+ sbp = tsbp;
+
+ }/* End While */
+
+}/* End Routine */
+
+
+
+/* SCREEN_FREE_FORMAT_LINES - Deallocate a list of format lines
+ *
+ * Function:
+ *
+ * This routine will clean up all the storage associated with a list of
+ * format lines. It is called with the address of the head of the list,
+ * and it will continue on down until the entire list is cleaned up.
+ * It will also take care of cleaning up any companion pointers from the
+ * screen array.
+ */
+void
+screen_free_format_lines(sbp)
+register struct format_line *sbp;
+{
+register struct format_line *osbp;
+register struct format_line *next_win;
+ PREAMBLE();
+
+/*
+ * Loop on down the list till we hit the tail
+ */
+ while(sbp){
+
+/*
+ * If this structure is tied to one of the current screen lines, break the
+ * connection.
+ */
+ if(sbp->fmt_saved_line){
+ if(sbp->fmt_saved_line->companion != sbp){
+ fprintf(
+ stderr,
+ "sbp->fmt_saved_line 0x%x != sbp->...companion 0x%x\n",
+ (unsigned int)sbp->fmt_saved_line,
+ (unsigned int)sbp->fmt_saved_line->companion
+ );
+ CAUSE_BUS_ERROR();
+ }/* End IF */
+ sbp->fmt_saved_line->companion = NULL;
+ sbp->fmt_saved_line = NULL;
+ }/* End IF */
+
+ next_win = sbp->fmt_next_window;
+ if(next_win){
+ sbp->fmt_next_window = NULL;
+ sbp->fmt_prev_window = NULL;
+ if(sbp->fmt_next_line){
+ sbp->fmt_next_line->fmt_next_window = next_win;
+ }/* End IF */
+ }/* End IF */
+/*
+ * Take it off the head of the list and place it on the free list.
+ */
+ sbp->fmt_magic = MAGIC_FORMAT_LOOKASIDE;
+ osbp = sbp;
+ sbp = sbp->fmt_next_line;
+ if(sbp == NULL) sbp = next_win;
+ osbp->fmt_next_line = format_line_free_list;
+ format_line_free_list = osbp;
+
+/*
+ * Remove it from the allocated list. This list is used to find all
+ * the format_line structures which are outstanding at any given time.
+ */
+ if(osbp->fmt_prev_alloc){
+ osbp->fmt_prev_alloc->fmt_next_alloc = osbp->fmt_next_alloc;
+ }/* End IF */
+
+ else if(format_line_alloc_list == osbp){
+ format_line_alloc_list = osbp->fmt_next_alloc;
+ }/* End Else */
+
+ if(osbp->fmt_next_alloc){
+ osbp->fmt_next_alloc->fmt_prev_alloc = osbp->fmt_prev_alloc;
+ }/* End IF */
+
+ osbp->fmt_next_alloc = osbp->fmt_prev_alloc = NULL;
+
+ }/* End While */
+
+}/* End Routine */
+
+void
+screen_check_format_lines(sbp,who)
+register struct format_line *sbp;
+int who;
+{
+
+ PREAMBLE();
+
+/*
+ * Loop on down the list till we hit the tail
+ */
+ while(sbp){
+
+/*
+ * If this structure is tied to one of the current screen lines, break the
+ * connection.
+ */
+ if(sbp->fmt_saved_line){
+ if(sbp->fmt_saved_line->companion != sbp){
+ fprintf(
+ stderr,
+ "w%d sbp->fmt_saved_line 0x%x != sbp->...companion 0x%x\n",
+ who,
+ (unsigned int)sbp->fmt_saved_line,
+ (unsigned int)sbp->fmt_saved_line->companion
+ );
+ CAUSE_BUS_ERROR();
+ }/* End IF */
+ }/* End IF */
+
+/*
+ * Take it off the head of the list and place it on the free list.
+ */
+ sbp = sbp->fmt_next_line;
+
+ }/* End While */
+
+}
+
+
+/* SCREEN_DEALLOCATE_FORMAT_LOOKASIDE_LIST
+ *
+ * Function:
+ *
+ * This routine frees up any format_lines we've cached on our local
+ * lookaside list.
+ */
+void
+screen_deallocate_format_lookaside_list()
+{
+register struct format_line *sbp;
+
+ PREAMBLE();
+
+ while((sbp = format_line_free_list) != NULL){
+ format_line_free_list = sbp->fmt_next_line;
+ sbp->fmt_magic = 0;
+ tec_release(TYPE_C_SCREENBUF,(char *)sbp->fmt_buffer);
+ tec_release(TYPE_C_SCREEN,(char *)sbp);
+ }/* End While */
+
+}/* End Routine */
+
+
+
+/* SCREEN_FIND_WINDOW_FORMAT_LINE - Finds format lines for a particular window
+ *
+ * Function:
+ *
+ * Each buffer line has chained off of it the format lines which hold
+ * the data as it appears on the screen. In order to handle multiple
+ * windows, we need the ability to have a format line per visible
+ * window. Thus, from the buff_line structure, we chain off a list
+ * of format lines. Since a buffer line may need many format lines to
+ * represent it (because it may wrap) we need a list. In addition,
+ * we want to have one of these lists on a per-window basis, so we
+ * have a pointer to the next list of format lines for another window.
+ */
+struct format_line *
+screen_find_window_format_line(wptr,lbp)
+struct window *wptr;
+struct buff_line *lbp;
+{
+struct format_line *sbp;
+
+ PREAMBLE();
+
+ sbp = lbp->format_line;
+ while(sbp){
+ if(sbp->fmt_window_ptr == wptr) return(sbp);
+ sbp = sbp->fmt_next_window;
+ }/* End While */
+
+ return(NULL);
+
+}/* End Routine */
+
+
+
+/* SCREEN_FORMAT_BUFF_LINE - Create the format_line structures for a buff line
+ *
+ * Function:
+ *
+ * This routine is called with the address of a buff_line structure. It
+ * creates the format_line structures which represent the printed version
+ * of the data, i.e., ready to be written to the terminal.
+ */
+int
+screen_format_buff_line(wptr,lbp)
+struct window *wptr;
+struct buff_line *lbp;
+{
+register struct format_line *sbp;
+register char *cp;
+register short *sp;
+register unsigned int c;
+register int i;
+int byte_count;
+int current_column;
+char *multi_byte_echo;
+char expand_buffer[MAXOF(32,MAX_TAB_WIDTH+1)];
+
+ PREAMBLE();
+
+/*
+ * If we're called with a null lbp, we've run out of memory or something,
+ * and we just punt
+ */
+ if(lbp == NULL) return(FAIL);
+
+/*
+ * If there is an old format structure hanging around (doubtful), make it
+ * go away. Note that this has been modified to try to deal with the new
+ * scheme where we have a different set of format structures for each
+ * window.
+ */
+ if(lbp->format_line){
+ screen_free_window_format_lines(wptr,lbp);
+ }/* End IF */
+
+ sbp = allocate_format_buffer(wptr,lbp);
+ if(sbp == NULL) return(FAIL);
+
+ sbp->fmt_next_window = lbp->format_line;
+ if(sbp->fmt_next_window){
+ sbp->fmt_next_window->fmt_prev_window = sbp;
+ }/* End IF */
+ lbp->format_line = sbp;
+
+ sp = sbp->fmt_buffer;
+ cp = lbp->buffer;
+ byte_count = lbp->byte_count;
+ c = ' ';
+ current_column = 0;
+ multi_byte_echo = NULL;
+
+ while(byte_count > 0){
+
+ if(current_column >= term_columns){
+ sbp->fmt_next_line = allocate_format_buffer(wptr,lbp);
+ if(sbp->fmt_next_line == NULL) return(FAIL);
+ sbp->fmt_next_line->fmt_prev_line = sbp;
+ sbp = sbp->fmt_next_line;
+ sp = sbp->fmt_buffer;
+ current_column = 0;
+ }/* End IF */
+
+ if(multi_byte_echo){
+ c = *multi_byte_echo++;
+ if(*multi_byte_echo == '\0') multi_byte_echo = NULL;
+ }/* End IF */
+
+ else {
+ c = *cp++;
+ byte_count -= 1;
+ }/* End Else */
+
+ c &= 0xFF;
+
+ switch(c){
+ case '\t':
+ i = tab_width - ( current_column % tab_width );
+ multi_byte_echo = tab_expand + MAX_TAB_WIDTH - i;
+ break;
+ case ESCAPE:
+ multi_byte_echo = "$";
+ break;
+
+ case '\n':
+ *sp++ = c;
+ current_column = 0;
+ break;
+ default:
+ if(c < ' '){
+ if( hide_cr_flag ){
+ expand_buffer[0] = ' ';
+ expand_buffer[1] = '\0';
+ } else {
+ expand_buffer[0] = '^';
+ expand_buffer[1] = c + 'A' - 1;
+ expand_buffer[2] = '\0';
+ }
+ multi_byte_echo = expand_buffer;
+ continue;
+ }/* End IF */
+/*
+ * If the high bit is on, display it as the code with that bit stripped off,
+ * or as '.' if it is a non-printable character. We display it reverse video
+ * if the terminal is not a brain damaged 'magic cookie' terminal.
+ */
+ if(eight_bit_output_flag == NO && c >= 128){
+ c -= 128;
+ if(isprint(c)) c |= SCREEN_M_REVERSE;
+ else c = '.' | SCREEN_M_REVERSE;
+#ifdef TERMCAP
+ if(termcap_sg){
+ c &= ~SCREEN_M_REVERSE;
+ }/* End IF */
+#endif
+ }/* End IF */
+ *sp++ = c;
+ current_column += 1;
+ break;
+ }/* End Switch */
+
+ }/* End While */
+
+ if(c != '\n'){
+ if(current_column < (term_columns - 1)){
+ *sp++ = '\n';
+ }/* End IF */
+ }/* End IF */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* SCREEN_FIND_DOT - Find position of dot in a format_line
+ *
+ * Function:
+ *
+ * There are several times that we need to find the format buffer that is
+ * associated with dot. This routine determines which format_line dot is
+ * on, and the offset onto that line (in bytes).
+ */
+int
+screen_find_dot(wptr)
+struct window *wptr;
+{
+register struct buff_line *lbp;
+register struct format_line *sbp;
+register char *cp;
+register unsigned char c;
+int i;
+int byte_count;
+int current_column;
+char *multi_byte_echo;
+char expand_buffer[32];
+struct buff_header *hbp = wptr->win_buffer;
+
+ PREAMBLE();
+
+/*
+ * Find the line buffer and format buffer structures
+ */
+ lbp = buff_find_line(hbp,hbp->dot);
+
+ sbp = screen_find_window_format_line(wptr,lbp);
+ if(sbp == NULL){
+ if(screen_format_buff_line(wptr,lbp) == FAIL) return(FAIL);
+ sbp = screen_find_window_format_line(wptr,lbp);
+ }/* End IF */
+
+ cp = lbp->buffer;
+ byte_count = buff_find_offset(hbp,lbp,hbp->dot);
+ current_column = 0;
+
+ multi_byte_echo = NULL;
+
+ while(byte_count > 0 || multi_byte_echo){
+
+ if(current_column >= term_columns){
+ sbp = sbp->fmt_next_line;
+ current_column = 0;
+ }/* End IF */
+
+ if(multi_byte_echo){
+ c = *multi_byte_echo++;
+ if(*multi_byte_echo == '\0') multi_byte_echo = NULL;
+ }/* End IF */
+
+ else {
+ c = *cp++;
+ byte_count -= 1;
+ }/* End Else */
+
+ switch(c){
+ case '\t':
+ i = tab_width - ( current_column % tab_width );
+ multi_byte_echo = tab_expand + MAX_TAB_WIDTH - i;
+ break;
+ case ESCAPE:
+ multi_byte_echo = "$";
+ break;
+ case '\n':
+ current_column = 0;
+ break;
+ default:
+ if(c < ' '){
+ expand_buffer[0] = '^';
+ expand_buffer[1] = c + 'A' - 1;
+ expand_buffer[2] = '\0';
+ multi_byte_echo = expand_buffer;
+ continue;
+ }/* End IF */
+ current_column += 1;
+ break;
+ }/* End Switch */
+
+ }/* End While */
+
+ wptr->win_dot_format_line = sbp;
+ wptr->win_dot_screen_offset = current_column;
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* ALLOCATE_FORMAT_BUFFER - Routine to return a format_line structure
+ *
+ * Function:
+ *
+ * This routine will return a format_line structure to the caller. It
+ * first looks on the free list, and if there is not one there, it will
+ * create one the hard way.
+ */
+struct format_line *
+allocate_format_buffer(wptr,lbp)
+struct window *wptr;
+struct buff_line *lbp;
+{
+register struct format_line *sbp;
+struct buff_header *hbp = wptr->win_buffer;
+
+ PREAMBLE();
+
+ sbp = format_line_free_list;
+ if(sbp != NULL){
+ if(sbp->fmt_buffer_size == term_columns){
+ format_line_free_list = sbp->fmt_next_line;
+ }
+ else {
+ screen_deallocate_format_lookaside_list();
+ sbp = NULL;
+ }
+ }/* End IF */
+
+ if(sbp == NULL){
+ sbp = (struct format_line *)
+ tec_alloc(TYPE_C_SCREEN,sizeof(struct format_line));
+ if(sbp == NULL) return(NULL);
+
+ sbp->fmt_buffer_size = term_columns;
+ sbp->fmt_buffer = (short *)
+ tec_alloc(
+ TYPE_C_SCREENBUF,
+ (int)(sizeof(short) * sbp->fmt_buffer_size)
+ );
+ if(sbp->fmt_buffer == NULL){
+ tec_release(TYPE_C_SCREEN,(char *)sbp);
+ return(NULL);
+ }/* End IF */
+ }/* End Else */
+
+ sbp->fmt_magic = MAGIC_FORMAT;
+ sbp->fmt_owning_buffer = hbp;
+ sbp->fmt_buffer_line = lbp;
+ sbp->fmt_sequence = screen_sequence;
+ sbp->fmt_next_line = sbp->fmt_prev_line = NULL;
+ sbp->fmt_next_window = sbp->fmt_prev_window = NULL;
+ sbp->fmt_window_ptr = wptr;
+ sbp->fmt_saved_line = NULL;
+ sbp->fmt_visible_line_position = -1;
+ sbp->fmt_permanent = 0;
+
+/*
+ * Place this at the head of the allocated queue
+ */
+ sbp->fmt_next_alloc = format_line_alloc_list;
+ sbp->fmt_prev_alloc = NULL;
+ if(sbp->fmt_next_alloc) sbp->fmt_next_alloc->fmt_prev_alloc = sbp;
+ format_line_alloc_list = sbp;
+
+ return(sbp);
+
+}/* End Routine */
+
+
+
+/* SCREEN_REDRAW - Cause a redraw of the screen
+ *
+ * Function:
+ *
+ * This routine is called when the screen may have become corrupted
+ * and needs to be refreshed.
+ */
+void
+screen_redraw()
+{
+register struct screen_line *lp;
+register short *sp;
+register int i,j;
+
+ PREAMBLE();
+
+ term_goto(0,0);
+ term_clrtobot();
+
+ for(lp = saved_screen, i = 0; i < term_lines; i++,lp++){
+ sp = lp->buffer;
+ for(j = 0; j < term_columns; j++,sp++) *sp = ' ';
+ lp->sequence = 0;
+ }/* End FOR */
+
+ screen_format_windows();
+ screen_refresh();
+
+}/* End Routine */
+
+
+
+/* CREATE_WINDOW - Create a window data structure
+ *
+ * Function:
+ *
+ * This function is called to create a window data structure which
+ * then occupies some portion of the screen. The initial structure
+ * starts out owning all but the reserved lines, but then lines are
+ * stolen from it as it is split into other windows.
+ */
+struct window *
+create_window(x_size,y_size)
+int x_size;
+int y_size;
+{
+register struct window *wptr;
+register int i;
+
+ PREAMBLE();
+
+ wptr = (struct window *)tec_alloc(TYPE_C_WINDOW,sizeof(struct window));
+ if(wptr == NULL) return(NULL);
+
+ wptr->win_label_line.fmt_buffer = (short *)
+ tec_alloc(TYPE_C_SCRBUF,(int)(sizeof(short) * x_size));
+ if(wptr->win_label_line.fmt_buffer == NULL){
+ tec_release(TYPE_C_WINDOW,(char *)wptr);
+ return(NULL);
+ }/* End IF */
+
+ wptr->win_window_number = next_window_number++;
+ wptr->win_next_window = NULL;
+ wptr->win_buffer = NULL;
+/*
+ * Initialize the xy size of the window. The y gets set to size-1 to leave
+ * room for the label line.
+ */
+ wptr->win_x_size = x_size;
+ wptr->win_y_size = y_size - 1;
+ wptr->win_y_base = 0;
+ wptr->win_y_end = wptr->win_y_base + wptr->win_y_size;
+/*
+ * Initialize the label line structure. Each window has one of these in
+ * reverse video showing which buffer is being displayed.
+ */
+ wptr->win_label_line.fmt_next_line =
+ wptr->win_label_line.fmt_prev_line =
+ wptr->win_label_line.fmt_next_alloc =
+ wptr->win_label_line.fmt_prev_alloc = NULL;
+
+ wptr->win_label_line.fmt_permanent = 1;
+ wptr->win_label_line.fmt_sequence = 0;
+ wptr->win_label_line.fmt_owning_buffer = NULL;
+ wptr->win_label_line.fmt_visible_line_position = -1;
+
+ wptr->win_label_line.fmt_window_ptr = wptr; /* FIX */
+ wptr->win_label_line.fmt_buffer_line = NULL;
+ wptr->win_label_line.fmt_buffer_size = x_size;
+
+ for(i = 0; i < x_size; i++){
+ wptr->win_label_line.fmt_buffer[i] = ' ' | SCREEN_M_REVERSE;
+ }/* End FOR */
+
+ wptr->win_label_line.fmt_sequence = screen_sequence;
+
+ for(i = 0; i < SCREEN_MAX_LABEL_FIELDS; i++){
+ wptr->win_label_field_contents[i] = NULL;
+ }/* End FOR */
+
+ return(wptr);
+
+}/* End Routine */
+
+
+
+/* SCREEN_SPLIT_WINDOW - Make two where there is only one
+ *
+ * Function:
+ *
+ * This routine is called when the user wants to split the current window
+ * into two. This allows him to display several buffers simultaneously.
+ */
+struct window *
+screen_split_window(old_wptr,lines,buffer_number)
+register struct window *old_wptr;
+int lines;
+int buffer_number;
+{
+register int i;
+register struct window *new_wptr;
+register struct screen_line *lp;
+
+ PREAMBLE();
+
+ if(old_wptr->win_y_size < SCREEN_MINIMUM_WINDOW_HEIGHT){
+ error_message("Window is already as small as possible");
+ return(NULL);
+ }/* End IF */
+
+ if(lines < SCREEN_MINIMUM_WINDOW_HEIGHT){
+ lines = SCREEN_MINIMUM_WINDOW_HEIGHT;
+ }/* End If */
+
+ if((old_wptr->win_y_size - (lines + 1)) < SCREEN_MINIMUM_WINDOW_HEIGHT){
+ error_message("Resulting windows would be too small");
+ return(NULL);
+ }/* End IF */
+
+ new_wptr = create_window(old_wptr->win_x_size,old_wptr->win_y_size-lines);
+ new_wptr->win_next_window = window_list;
+ window_list = new_wptr;
+ new_wptr->win_buffer = old_wptr->win_buffer;
+/*
+ * Take the lines of the new window away from the old window
+ */
+ old_wptr->win_y_size -= new_wptr->win_y_size + 1;
+/*
+ * Have the new window start above the old one
+ */
+ new_wptr->win_y_base = old_wptr->win_y_base;
+ old_wptr->win_y_base += new_wptr->win_y_size + 1;
+/*
+ * Recalculate the ends of each window
+ */
+ old_wptr->win_y_end = old_wptr->win_y_base + old_wptr->win_y_size;
+ new_wptr->win_y_end = new_wptr->win_y_base + new_wptr->win_y_size;
+/*
+ * Initialize the label line structure. Each window has one of these in
+ * reverse video showing which buffer is being displayed.
+ */
+ for(i = 0; i < new_wptr->win_x_size; i++){
+ new_wptr->win_label_line.fmt_buffer[i] =
+ old_wptr->win_label_line.fmt_buffer[i];
+ }/* End FOR */
+
+ for(i = 0; i < SCREEN_MAX_LABEL_FIELDS; i++){
+ if(old_wptr->win_label_field_contents[i]){
+ screen_label_line(old_wptr->win_buffer,
+ old_wptr->win_label_field_contents[i],i);
+ }/* End IF */
+ }/* End FOR */
+
+ screen_label_window();
+
+ lp = saved_screen + new_wptr->win_y_end;
+ if(new_wptr->win_label_line.fmt_saved_line){
+ new_wptr->win_label_line.fmt_saved_line->companion = NULL;
+ }/* End IF */
+ new_wptr->win_label_line.fmt_saved_line = lp;
+ if(lp->companion) lp->companion->fmt_saved_line = NULL;
+ lp->companion = &new_wptr->win_label_line;
+ new_wptr->win_label_line.fmt_sequence = screen_sequence;
+
+ if(buffer_number){
+ buff_openbuffnum(buffer_number,0);
+ }/* End IF */
+
+ return(new_wptr);
+
+}/* End Routine */
+
+
+
+/* SCREEN_DELETE_WINDOW - Here to delete a window
+ *
+ * Function:
+ *
+ * This function is called when a user wants to delete a window.
+ * The space gets given back to the window next to it.
+ */
+void
+screen_delete_window(old_wptr)
+register struct window *old_wptr;
+{
+
+register int i;
+register struct window *new_wptr;
+register struct screen_line *lp;
+
+ PREAMBLE();
+
+ if((old_wptr->win_y_size + 1) == (term_lines - SCREEN_RESERVED_LINES)){
+ error_message("Illegal to delete the final window");
+ return;
+ }/* End IF */
+/*
+ * Find the old_wptr on the list of windows, and remove it
+ */
+ if(window_list == old_wptr) window_list = old_wptr->win_next_window;
+ else {
+ new_wptr = window_list;
+ while(new_wptr->win_next_window != old_wptr){
+ new_wptr = new_wptr->win_next_window;
+ }/* End While */
+ new_wptr->win_next_window = old_wptr->win_next_window;
+ }/* End Else */
+/*
+ * Remove the label line from the screen display
+ */
+ lp = old_wptr->win_label_line.fmt_saved_line;
+ lp->companion = NULL;
+ old_wptr->win_label_line.fmt_saved_line = NULL;
+/*
+ * If the window is at the bottom of the screen, we will combine it with
+ * the window above it.
+ */
+ if((old_wptr->win_y_end + 1) == (term_lines - SCREEN_RESERVED_LINES)){
+ new_wptr = window_list;
+ while(new_wptr){
+ if((new_wptr->win_y_end + 1) == old_wptr->win_y_base) break;
+ new_wptr = new_wptr->win_next_window;
+ }/* End While */
+ if(!new_wptr){
+ error_message("Failure to find window above");
+ return;
+ }/* End IF */
+ lp = new_wptr->win_label_line.fmt_saved_line;
+ lp->companion = NULL;
+ new_wptr->win_y_size += old_wptr->win_y_size + 1;
+ new_wptr->win_y_end = new_wptr->win_y_base + new_wptr->win_y_size;
+ lp = saved_screen + new_wptr->win_y_end;
+ lp->companion = &new_wptr->win_label_line;
+ new_wptr->win_label_line.fmt_saved_line = lp;
+ }/* End IF */
+/*
+ * Else, we combine it with the window below it
+ */
+ else {
+ new_wptr = window_list;
+ while(new_wptr){
+ if(new_wptr->win_y_base == (old_wptr->win_y_end + 1)) break;
+ new_wptr = new_wptr->win_next_window;
+ }/* End While */
+ if(!new_wptr){
+ error_message("Failure to find window below");
+ return;
+ }/* End IF */
+ lp = new_wptr->win_label_line.fmt_saved_line;
+ lp->companion = NULL;
+ new_wptr->win_y_base -= old_wptr->win_y_size + 1;
+ new_wptr->win_y_size += old_wptr->win_y_size + 1;
+ new_wptr->win_y_end = new_wptr->win_y_base + new_wptr->win_y_size;
+ lp = saved_screen + new_wptr->win_y_end;
+ lp->companion = &new_wptr->win_label_line;
+ new_wptr->win_label_line.fmt_saved_line = lp;
+ }/* End IF */
+/*
+ * Release any memory used to hold the strings in the label line
+ */
+ for(i = 0; i < SCREEN_MAX_LABEL_FIELDS; i++){
+ if(old_wptr->win_label_field_contents[i]){
+ tec_release(
+ TYPE_C_LABELFIELD,
+ old_wptr->win_label_field_contents[i]
+ );
+ old_wptr->win_label_field_contents[i] = NULL;
+ }/* End IF */
+ }/* End FOR */
+/*
+ * Release the buffer used to hold formatted data
+ */
+ tec_release(TYPE_C_SCRBUF,(char *)old_wptr->win_label_line.fmt_buffer);
+ tec_release(TYPE_C_WINDOW,(char *)old_wptr);
+
+ curwin = new_wptr;
+ curbuf = new_wptr->win_buffer;
+
+}/* End Routine */
+
+
+
+/* WINDOW_SWITCH - Switch to a new window
+ *
+ * Function:
+ *
+ * This function is called to switch the current window to a different
+ * window.
+ */
+int
+window_switch(window_number)
+int window_number;
+{
+register struct window *wptr;
+
+ PREAMBLE();
+
+/*
+ * If the window number is specified as zero, just select the next window
+ * on the list.
+ */
+ if(window_number == 0){
+ if((wptr = curwin->win_next_window) == NULL) wptr = window_list;
+ window_number = wptr->win_window_number;
+ }/* End IF */
+
+ wptr = window_list;
+/*
+ * Search the list until we find the correct window.
+ */
+ while(wptr && wptr->win_window_number != window_number){
+ wptr = wptr->win_next_window;
+ }/* End While */
+/*
+ * If the search failed, give up.
+ */
+ if(wptr == NULL){
+ char tmp_message[LINE_BUFFER_SIZE];
+ sprintf(tmp_message,"can't switch to window %d",window_number);
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+
+/*
+ * Check to see that we actually found the right window.
+ */
+ if(wptr->win_window_number != window_number) return(FAIL);
+/*
+ * Switch the current window and edit buffer to the newly selected window.
+ */
+ curwin = wptr;
+ return(SUCCESS);
+
+}/* End Routine */