ftp.nice.ch/pub/next/unix/editor/jed.N.bs.tar.gz#/jed.N.bs/src/screen.c

This is screen.c in view mode; [Download] [Up]

/*
 *  Copyright (c) 1992, 1995 John E. Davis  (davis@space.mit.edu)
 *  All Rights Reserved.
 */

#include <config.h>


#include <stdio.h>
#include <slang.h>

#include <string.h>
#ifdef pc_system
#include <dos.h>
#endif

#include "buffer.h"
#include "screen.h"
#include "window.h"
#include "paste.h"

#include "ins.h"
#include "ledit.h"
#include "display.h"
#include "sysdep.h"
#include "misc.h"
#include "vterm.h"
#include "file.h"
#include "hooks.h"

#ifdef HAS_SUBPROCESSES
#include "jprocess.h"
#endif


#ifdef FLOAT_TYPE
# define JED_VERSION "0.F97.14"
#else
# define JED_VERSION "0.97.14"
#endif
int Jed_Version_Number = 9714;

#define fputs(s,f) write(fileno(f), s, strlen(s))

Screen_Type JScreen[MAX_SCREEN_SIZE];

int Screen_Row = 1;
int Screen_Col = 1;
int Cursor_Motion;    /* indicates cursor movement only -1 ^ v +1 < > */
int Scroll_Region_1;
int Scroll_Region_2;
int Scroll_Lines;
int Jed_Dollar = '$';
int User_Prefers_Line_Numbers = 0;
int Mode_Has_Syntax_Highlight;
int Wants_Syntax_Highlight;	       /* if non-zero, highlight the syntax.
					*/
int Wants_Attributes = 1;
int Wants_HScroll = 20;		       /* controls automatic horizontal
					* scrolling.  If positive, scroll
					* only line, if negative, whole wind
					*/
int Term_Supports_Color = 1;	       /* optimistic assumption */
static Line *HScroll_Line;
static int HScroll;		       /* amount to scroll line by */
static int Absolute_Column;
static int Message_Color;

void (*X_Update_Open_Hook)(void);      /* hooks called when starting */
void (*X_Update_Close_Hook)(void);     /* and finishing update */

int Goal_Column;
int Display_Eight_Bit = 0;
int JED_CSI = -1;

unsigned short Point_On_Char;		       /* character at cursor position */
static int Point_Cursor_Flag = 1;      /* if non-zero, point cursor */

static Line Eob_Line =
{
   NULL, NULL, (unsigned char *) "[EOB]", 5
#ifdef KEEP_SPACE_INFO
     ,5
#endif
};

int Want_Eob = 0;
int Display_Time = 1;                  /* Turn on %t processing in status line */

void blank_line(int row)
{
   int n = JWindow->width;
   register unsigned short *p, *pmax;
   p = JScreen[row].old;
   if (p == NULL) return;
   pmax = p + n;
   
   while (p < pmax)
     {
	*p = 32; *(p + 1) = 32; *(p + 2) = 32; *(p + 3) = 32;
	p += 4;
     }
}

static void update_screen_txt(int row)
{
   unsigned short *tmp;
   tmp = JScreen[row].neew;
   JScreen[row].neew = JScreen[row].old;
   JScreen[row].old = tmp;
}
static unsigned char char_width[256];
static int dis8bit = -100;
static int j_csi = -1;
static int Selective_Display = -0x7FFF;

#define FIX_CHAR_WIDTH \
  if ((Display_Eight_Bit != dis8bit) || (JED_CSI != j_csi)\
      || (Selective_Display != Buffer_Local.sd)) fix_char_width()


static int fix_attributes(register unsigned short *p, register unsigned short *pmax,
			  unsigned int color)
{
   if (pmax == NULL) pmax = p + JWindow->width;
   if (p >= pmax) return(0);
   color = color << 8;
   while (p < pmax)
     {
	*p = color | (*p & 0xFF);
	p++;
     }
   return(1);
}

#ifdef HAS_DISPLAY_TABLE
unsigned char Output_Display_Table[256];
#endif


static int output(unsigned char *line, int len, int row, register int max_col,
		  register int column_offset, unsigned char *hi_beg, unsigned char *hi_end,
		  Line *buffer_line)
{
   register unsigned char ch, *p, *pmax, *b;
   register unsigned short *outp;
   register int count;
   unsigned char expb[80];
   unsigned short *out, *mark, *hi0_outp = NULL, *hi1_outp = NULL, *sp, *spmax;
   int tab, tabsize, visible, i, mini, w;
   int done = 0;
   unsigned short dollar = Jed_Dollar & 0xFF;
   
#if !defined(pc_system) && !defined(__os2__)
   dollar |= 0x20;		       /* prevent control character */
   if (*tt_Use_Ansi_Colors && Term_Supports_Color)
     {
#endif
	dollar |= JDOLLAR_COLOR << 8;
#if !defined(pc_system) && !defined(__os2__)
     }
#endif
   
   outp = out = JScreen[row - 1].neew;
   if (outp == NULL) return 1;
   tab = Buffer_Local.tab;
   
   if (Buffer_Local.sd < 0)
     *(char_width + (unsigned int) '\r') = 3;
   else
     *(char_width + (unsigned int) '\r') = 2;
   
   count = 0;
   visible = 0;
   
   mini = 1;
   while(mini >= 0)
     {
	/* deal with mini_buffer prompt */
	if (mini && (IS_MINIBUFFER) && Mini_Info.prompt_len)
	  {
	     p = (unsigned char *) Mini_Info.prompt;
	     pmax = p + Mini_Info.prompt_len;
	     mini = 0;
	  }
	else
	  {
	     p = line;
	     pmax = p + len;
	     mini = -1;
	  }
	
	while (p < pmax)
	  {
	     if (p == hi_beg) hi0_outp = outp;
	     if (p == hi_end) hi1_outp = outp;
	     
	     ch = (unsigned char) *p++;
	     b = expb;
	     /* expand char to display form */
	     
#ifdef HAS_DISPLAY_TABLE
	     ch = Output_Display_Table[(unsigned int) ch];
#endif
	     w = *(char_width + (unsigned int) ch);
	     if (w == 1)
	       {
		  /* try to optimize this by putting this here */
		  
		  count++;
		  if ((count < max_col) && (count >= column_offset))
		    {
		       *outp++ = (unsigned short) ch;
		    }
		  else if (count >= max_col)
		    {
		       done = 1;
		       p = pmax;
		    }
		  continue;
	       }
	     else if (w == 3)
	       {
		  if (ch == '\r')
		    {
		       *b++ = '.'; *b++ = '.'; *b++ = '.';
		       done = -1;
		       p = pmax;
		    }
		  else
		    {
		       *b++ = '~';
		       *b++ = '^';
		       
		       /* Be better to replace following by:
			* *b++ = ch - '@';
			*/
		       ch &= 0x7F;
		       *b++ = (ch == 127) ? '?' : ch  + 'A' - 1;
		    }
		  
	       }
	     else if (w == 2)
	       {
		  if ((ch == '\t') && (tab > 0))
		    {
		       tabsize = tab * (count / tab + 1) - count;
		       i = 0; while (i++ < tabsize) *b++ = ' ';
		       w = tabsize;
		    }
		  else if (ch & 0x80)
		    {
		       *b++ = '~'; *b++ = ch & 0x7F;
		    }
		  else
		    {
		       *b++ = '^';
		       /* Be better to replace following by:
			* *b++ = (ch | 0x80) - '@';
			*/
		       
		       *b++ = (ch == 127) ? '?' : ch  + 'A' - 1;
		    }
		  
	       }
	     else		       /* w = 0 */
	       {
		  ch = *p++;	       /* skip next char */
		  continue;
	       }
	     
	     b = expb;
	     while (w--)
	       {
		  count++;
		  if (count >= max_col)
		    {
		       done = 1;
		       p = pmax;
		    }
		  
		  else if (count >= column_offset)
		    {
		       *outp++ = (unsigned short) *b;
		    }
		  b++;
	       }
	  }
     }
   
   
   sp = out;
   while (sp < outp)
     {
	if (*sp++ != ' ')
	  {
	     visible++;
	     break;
	  }
     }
   
#if !defined(pc_system) && !defined(__os2__)
   if (*tt_Use_Ansi_Colors && Term_Supports_Color)
     {
#endif
	if (Wants_Syntax_Highlight && Mode_Has_Syntax_Highlight
	    && (line != Eob_Line.data)) syntax_highlight (out, outp);
	else if (Message_Color)
	  {
	     unsigned short *ppp = out;
	     while (ppp < outp) *ppp++ |= Message_Color;
	  }
#if !defined(pc_system) && !defined(__os2__)
     }
#endif
   
   if (outp == out)
     {
	
	*outp++ = dollar;
	visible = 1;
     }
   else if (column_offset > 1)
     {
	*out = dollar;
	visible++;
     }
   
   mark = outp;
   sp = outp;
   spmax = out + JWindow->width;
#if defined(msdos) && !defined(__WATCOMC__) && !defined(__WIN32__)
   asm mov bx, di
     asm mov cx, word ptr spmax
     asm sub cx, word ptr outp
     asm shr cx, 1
     asm mov ax, 32
     asm les di, dword ptr outp
     asm cld
     asm rep stosw
     asm mov di, bx
   /* Note that outp is not updated here! */
#else
/*   MEMSET((char *) sp, (char) ' ',  sizeof(short) * (int) (spmax - sp)); */
   /* This line burns some CPU */
     while (sp < spmax) {*sp++ = 0x0020; *sp++ = 0x0020; *sp++ = 0x0020; *sp++ = 0x0020;
     }
#endif
   
#ifdef JED_LINE_ATTRIBUTES
   if ((buffer_line != NULL)
       && (buffer_line->flags & JED_LINE_MARK))
     {
	register unsigned short *ppp = out;
	while (ppp < spmax) *ppp++ |= JLINE_COLOR << 8;
	visible++;
     }
#endif
   
   if (done == 1)
     {
	mark = out + (JWindow->width - 1);
	*mark = dollar;
	visible += 1;
	if ((hi_beg != NULL) && (hi0_outp == NULL)) hi0_outp = mark;
	mark = NULL;
     }
   out[JWindow->width] = '\0';
   
   /* fix highlighting on this line */
   if ((Wants_Attributes) && (hi0_outp != NULL))
     {
	if (hi1_outp == NULL) hi1_outp = outp + 1;
	visible += fix_attributes(hi0_outp, hi1_outp, JREGION_COLOR);
     }
   
   
   if (visible)
     {
#if defined(msdos) && !defined(MSWINDOWS)
	(void) mark;
	/* 2nd and last parameters not used in msdos smartputs so they do
	   not need to be valid */
	tt_smart_puts(out, out, *tt_Screen_Cols, row - 1);
#else
	tt_smart_puts(out, JScreen[row-1].old, *tt_Screen_Cols, row - 1);
#endif
	JScreen[row-1].n = visible;
	Point_Cursor_Flag = 1;
     }
   
   /* else take care of it in calling routine */
   return(visible);
}

static void display_line(Line *line, int row)
{
   int len, cofs = JWindow->column;
   Screen_Type *s = &JScreen[row - 1];
   
   if (line != NULL)
     {
	len = line->len;
	if (len && (row != *tt_Screen_Rows)
	    && (line->data[len - 1] == '\n'))
	  {
	     if ((s->hi1 != NULL) && (s->hi1 == line->data + len)) s->hi1--;
	     len--;
	  }
	
	if ((len > 0) || (cofs > 1)
	    || ((row == *tt_Screen_Rows) && Mini_Info.prompt_len))
	  {
	     if (Wants_HScroll && (line == HScroll_Line) && HScroll)
	       {
		  cofs += HScroll;
	       }
	     
	     len = output(line->data, len, row,
			  cofs + JWindow->width - 1, cofs,
			  s->hi0, s->hi1, line);
	  }
     }
   else len = 0;
   
   if (len <= 0)
     {
	if (s->n)
	  {
	     tt_goto_rc(row - 1, 0);
	     Screen_Row = row;
	     tt_del_eol();
	  }
	blank_line(row - 1);
	s->n = 0;
     }
   s->line = line;
   s->flags = 0;
   /* s->hi0 = s->hi1 = NULL; */
   if (len > 0) update_screen_txt(row-1);
}

static void mark_window_attributes(int wa)
{
   register Screen_Type *s = &JScreen[JWindow->top - 1],
     *smax = s + JWindow->rows, *s1, *s2;
   Mark *m;
   register Line *l = JWindow->beg.line, *ml;
   unsigned char *hi0, *hi1;
   int mn, pn, dn;
   
   s1 = s;
   
   if ((CBuf->vis_marks == 0) || (wa == 0) || (Wants_Attributes == 0))
     {
	s2 = s;
	goto done;		       /* I hate gotos but they are convenient */
     }
   m = CBuf->marks;
   
   while ((m->flags & VISIBLE_MARK) == 0) m = m->next;
   ml = m->line;
   mn = m->n;			       /* already in canonical form */
   pn = LineNum + CBuf->nup;	       /* not in canonical form */
   dn = pn - mn;
   
   while (l != CLine)		       /* find pos of point in window */
     {
	s1++;
	l = l->next;
     }
   
   /* s1 now points at current line */
   /* The whole point of all of this is to preserve the screen flags without
      touching the screen.  */
   
   if (dn > 0)			       /* mark on prev lines */
     {
	s2 = s1 + 1;
	hi0 = l->data;
	hi1 = l->data + Point;
	if ((s1->hi0 != hi0) || (s1->hi1 != hi1))
	  {
	     s1->hi0 = hi0; s1->hi1 = hi1;
	     s1->flags = 1;
	  }
	l = l->prev; s1--;
	while ((s1 >= s) && (l != ml))
	  {
	     hi0 = l->data;
	     hi1 = l->data + l->len;
	     if ((s1->hi0 != hi0) || (s1->hi1 != hi1))
	       {
		  s1->hi0 = hi0; s1->hi1 = hi1;
		  s1->flags = 1;
	       }
	     l = l->prev; s1--;
	  }
	if (s1 >= s)
	  {
	     hi0 = ml->data + m->point;
	     hi1 = ml->data + ml->len;
	     if ((s1->hi0 != hi0) || (s1->hi1 != hi1))
	       {
		  s1->hi0 = hi0; s1->hi1 = hi1;
		  s1->flags = 1;
	       }
	     s1--;
	  }
     }
   else if (dn < 0)		       /* mark ahead of point */
     {
	s2 = s1;
	s1--;
	hi0 = l->data + Point;
	hi1 = l->data + l->len;
	if ((s2->hi0 != hi0) || (s2->hi1 != hi1))
	  {
	     s2->hi0 = hi0; s2->hi1 = hi1;
	     s2->flags = 1;
	  }
	
	l = l->next;
	s2++;
	while ((s2 < smax) && (l != ml))
	  {
	     hi0 = l->data;
	     hi1 = l->data + l->len;
	     if ((s2->hi0 != hi0) || (s2->hi1 != hi1))
	       {
		  s2->hi0 = hi0; s2->hi1 = hi1;
		  s2->flags = 1;
	       }
	     l = l->next;
	     s2++;
	  }
	
	if (s2 < smax)
	  {
	     hi0 = ml->data;
	     hi1 = ml->data + m->point;
	     if ((s2->hi0 != hi0) || (s2->hi1 != hi1))
	       {
		  s2->hi0 = hi0; s2->hi1 = hi1;
		  s2->flags = 1;
	       }
	     s2++;
	  }
     }
   else				       /* same line */
     {
	if (Point < m->point)
	  {
	     s1->hi0 = l->data + Point;
	     s1->hi1 = l->data + m->point;
	  }
	else
	  {
	     s1->hi1 = l->data + Point;
	     s1->hi0 = l->data + m->point;
	  }
	s1->flags = 1;
	s2 = s1 + 1;
	s1--;
     }
   
   done:			       /* reached if there is no mark */
   
   /* now do area outside the region */
   while (s1 >= s)
     {
	if (s1->hi0 != NULL)
	  {
	     s2->hi1 = s1->hi0 = NULL;
	     s1->flags = 1;
	  }
	s1--;
     }
   
   while (s2 < smax)
     {
	if (s2->hi0 != NULL)
	  {
	     s2->hi1 = s2->hi0 = NULL;
	     s2->flags = 1;
	  }
	s2++;
     }
}


static void open_scroll(void)
{
   Scroll_Region_1 = 1;
   Scroll_Region_2 = *tt_Screen_Rows;
   Scroll_Lines = 0;
}

static void do_scroll_up(int n)
{
   tt_goto_rc(0,0);
   tt_delete_nlines(n);
}

static void do_scroll_down(int n)
{
   tt_goto_rc(0, 0);
   tt_reverse_index(n);
}

static void execute_scroll(void)
{
   if (Scroll_Lines > 0)
     {
	tt_set_scroll_region(Scroll_Region_1 - 1, Scroll_Region_2 - 1);
	execute_vscroll_up(Scroll_Region_1, Scroll_Region_2, Scroll_Lines);
	do_scroll_up(Scroll_Lines);
	
     }
   else if (Scroll_Lines < 0)
     {
	tt_set_scroll_region(Scroll_Region_1 - 1, Scroll_Region_2 - 1);
	execute_vscroll_down(Scroll_Region_1, Scroll_Region_2, -Scroll_Lines);
	do_scroll_down(-Scroll_Lines);
     }
   Scroll_Lines = 0;
}

static void queue_scroll(int n, int r1, int r2)
{
   if ((r1 != Scroll_Region_1) || (r2 != Scroll_Region_2))
     {
/*	if ((n == -1) && (Scroll_Region_1 == r1 + Scroll_Lines)
	       && (Scroll_Region_2 == r2))  */
	if ((n == -1) && (Scroll_Region_1 == r1 + Scroll_Lines)
	    && (Scroll_Region_2 == r2))  Scroll_Lines--;
	else if ((n == -1) && (Scroll_Region_1 == r1 + Scroll_Lines)
		 && (Scroll_Region_2 == r2 - 1))
	  {
	     Scroll_Region_2 = r2;
	     Scroll_Lines--;
	  }
	
	else
	  {
	     execute_scroll();
	     Scroll_Region_1 = r1;
	     Scroll_Region_2 = r2;
	     Scroll_Lines = n;
	  }
     }
   else Scroll_Lines += n;
}

static void close_scroll(void)
{
   if (Scroll_Lines) execute_scroll();
   if ((Scroll_Region_1 != 1) || (Scroll_Region_2 != *tt_Screen_Rows))
     tt_reset_scroll_region();
}

/* Here a scrolling region (t,b) is used to scroll line t + n to t.  All
   that is assumed is that  the line at t + n exists! */

int scroll_up(int n, int t, int b)
{
   int i, necess;
   
   if (n == 0) return(0);
   
    /* t = JWindow->top - 1 + t;
       b = JWindow->top - 1 + b; */
   
    /* check to see if this is really necessary */
   necess = 0;
   for (i = t - 1; i < b; i++)
     {
	if (JScreen[i].n)
	  {
	     necess = 1;
	     break;
	  }
     }
   
   if (!necess) return(0);
   
   queue_scroll(n, t, b);
   
   vscroll_up(t,b,n);
   
   return(necess);
}

/* Here a scrolling region (t,b) is used to scroll line t + n to t. */

void scroll_down(int n, int t, int b)
{
   if (n == 0) return;
   
    /* t = JWindow->top - 1 + t;
       b = JWindow->top - 1 + b; */
   tt_set_scroll_region(t - 1,b - 1);
   tt_reverse_index(n);
   tt_reset_scroll_region();
   
   vscroll_down(t,b,n);
}

static int update_insert_line(int r1, Line *line)
{
   int i, r2, r, necess;
   Line *bot;
   
    /* normalize r1: */
    /* r1 = JWindow->top - 1 + r1; */
   
    /* find the first one that is blank to delimit the region so that the
       loss will be minimal */
   
   r = r1;   /* not r1 + 1 as obvious (but naive) as it seems */
   r2 = JWindow->rows + JWindow->top - 1;
   while (r < r2)
     {
	bot = JScreen[r - 1].line;
	if ((bot == NULL) || (bot == &Eob_Line)) break;
	r++;
     }
   
   if ((r1 != r2) && (r == r2))
    /* we may have failed so check the bottom up so we don't push linesoff. */
     {
	bot = line;
	for (r = r1 + 1; r <= r2; r++)
	  {
	     bot = bot->next;
	     if (bot == NULL) break;
	     if (JScreen[r-1].line == bot) break;
	  }
	if ((bot != NULL) && (r <= r2)) r--;
	if (r > r2) r = r2;
     }
   
   if (r < r1) r = r1;
   r2 = r;
   
    /* check to see if we gain by doing this */
   necess = 0;
   for (i = r1 - 1; i < r2; i++)
     {
	if (JScreen[i].n)
	  {
	     necess = 1;
	     break;
	  }
     }
   if (r1 == r2) return(0);
   if (!necess) return(0);
   
   queue_scroll(-1, r1, r2);
   
   vscroll_down(r1, r2, 1);
   return(1);
}

Line *find_top()
{
   int n, i;
   Line *line, *next, *prev, *tthis;
   
   n = JWindow->rows - 1;
   
   if (n <= 0) return(CLine);
    /* Check the top window line.  Chances are that one of the lines
     above CLine will match in usual situations */
   line = JScreen[JWindow->top - 1].line;
   prev = CLine;
   
   if (line != NULL) for (i = 0; i < JWindow->rows; i++)
     {
	if (prev == line) return(line);
	tthis = prev->prev;
	if (tthis == NULL) break;
	else prev = tthis;
     }
   
    /*  That was the obvious choice now try some others */
    /* try bottom (or punt) */
   if (*tt_Term_Cannot_Scroll)
     {
	if (*tt_Term_Cannot_Scroll == -1)
	  {
	     if ((line != NULL) && (CLine->next == line)) return (CLine);
	     if (CLine->prev != JScreen[JWindow->rows + JWindow->top - 2].line) n = n / 2;
	  }
	else n = n / 2;
	goto no_scroll;
     }
   
   line = JScreen[JWindow->rows + JWindow->top - 2].line;
   next = CLine;
   
   if (line != NULL) for (i = 0; i < JWindow->rows; i++)
     {
	if (next == line)
	  {
	     line = CLine;
	     while(i < JWindow->rows - 1)
	       {
		  prev = line->prev;
		  if (prev == NULL) return(line);
		  line = prev;
		  i++;
	       }
	     return(line);
	  }
	next = next->next;
	if (next == NULL) break;
     }
    /* try to find CLine somewhere in the window */
   
   next = CLine->next;
   prev = CLine->prev;
   
   for (i = JWindow->top - 1; i < JWindow->top - 1 + JWindow->rows; i++)
     {
	tthis = JScreen[i].line;
	if (tthis == CLine)
	  {
	     line = CLine;
	     while(i > JWindow->top - 1)
	       {
		  i--;
		  line = line->prev;
		  if (line == NULL) return(CLine);
	       }
	     
	     return(line);
	  }
	else if ((tthis == prev) && (tthis != NULL))
	  {
	     i--;
	     while(i-- > JWindow->top - 1)
	       {
		  if (prev->prev == NULL) return(prev);
		  prev = prev->prev;
	       }
	     return(prev);
	  }
	else if ((tthis == next) && (tthis != NULL))
	  {
	     i++;
	     while(i-- > JWindow->top - 1)
	       {
		  if (next->prev == NULL) return(next);
		  next = next->prev;
	       }
	     return(next);
	  }
	
     }
   
    /* not found so check neighbors */
   line = CLine->next;
   if ((line != NULL) && (line == JScreen[JWindow->top - 1].line)) return(CLine);
   
   line = CLine->prev;
   if ((line == NULL) || (line != JScreen[n + JWindow->top - 1].line))
     {
	n = n / 2;
     }
   
   no_scroll:			       /* on terms that cannot scroll */
   
   line = prev = CLine;
   while(n--)
     {
	line = line->prev;
	if (line == NULL) return(prev);
	prev = line;
     }
   
   return(line);
}

static void do_scroll(register Line *top)
{
   Line *bot, *middle1, *middle2, *s;
   register Line *sline;
   int r1, r, r2, match, overwrite = 1, scroll_region_set = 0, srs, nrows;
   int n1, n2, n3 = 0;
   /* unsigned short sh; */
   
   nrows = JWindow->rows;
   r1 = JWindow->top;
   r2 = r1 + nrows - 1;
   
   /* Actually it might be more benificial to extend r2 to a status line.  
     The status line is easy to detect since it begins with a highlighted
     minus sign.  */
/*   
   r = r2;
   while (r < *tt_Screen_Rows - 1)
     {
	sh = JScreen[r].old[0];
	if (((sh & 0xFF) == '-') && (sh >> 8)) break;
	r++;
     }
   nrows = r - r1 + 1;
   r2 = r;
*/
   
   
   open_scroll();
   if (top != NULL) bot = top->next; else bot = NULL;
   
   
   middle1 = middle2 = NULL;
   r = 1;
   bot = top;
   n1 = nrows / 3;
   n2 = 2 * n1;
   
   /* I do not want to use CLine and the previous one as a basis 
    * for determining scroll region since we may have just created it. 
    * There is nothing right or wrong about it--- just a feeling that
    * results in optimization of screen update. */
   while (r++ < nrows)
     {
	if ((r >= n1) && (middle1 == NULL) && (bot != NULL)
	    && (bot != CLine) && (bot != CLine->prev)) middle1 = bot;
	if ((r >= n2) && (middle2 == NULL) && (bot != NULL)
	    && (bot != CLine) && (bot != CLine->prev))
	  
	  {
	     middle2 = bot;
	     n2 = bot->len;
	  }
	
	if (bot != NULL) bot = bot -> next;
     }
   /* if the middle or the bottom lines match, we do not overwrite */
   
   if (bot != NULL) n3 = bot->len;
   if (middle1 == NULL) overwrite = 0;
   else
     {
	n1 = middle1->len;
	/* I do not want to scroll if only 1 thing matches */
	for (r = r1; r <= r2; r++)
	  {
	     s = JScreen[r - 1].line;
	     if (((middle1 == s) && (n1 == s->len)
		  && n1 && (middle1->data[n1 / 2] == s->data[n1 / 2]))
		 || ((middle2 == s) && (middle2 != NULL) && (n2 == s->len)
		     && n2 && (middle2->data[n2 / 2] == s->data[n2 / 2]))
		 || ((bot != NULL) && (bot == s) && (n3 > 4) && (n3 == s->len)
		     && n3 && (bot->data[n3/2] == s->data[n3 / 2])))
	       {
		  overwrite = 0;
		  break;
	       }
	  }
     }
   
   while(r1 <= r2)
     {
	if (top == NULL) break;
	match = 0;
	for(r = r1; r <= r2; r++)
	  {
	     sline = JScreen[r - 1].line;
	     if (sline == top)
	       {
		  if (r != r1)
		    {
		       scroll_region_set = 0;
		       scroll_up(r - r1, r1, r2);
		    }
		  
		  r1++;
		  match = 1;
		  if (top != NULL) top = top->next;
		  break;
	       }
	  }
	
	if (match) continue;
	
	 /* not found so insert it */
	if ((top != NULL) && (!overwrite))
	  {
	     srs = update_insert_line(r1, top);
	     if (!scroll_region_set) scroll_region_set = srs;
	  }
	if (top != NULL) top = top->next;
	r1++;
     }
   
   /*     if (scroll_region_set) reset_scroll_region(); */
   close_scroll();
   if (overwrite) touch_window();
}

static void fix_char_width(void)
{
   int i;
   for (i = 0; i < 32; i++)
     {
	char_width[i] = 2;
	char_width[i + 128] = 3;
     }
   
   for (i = 32; i < 127; i++)
     {
	char_width[i] = 1;
	char_width[i + 128] = 2;
     }
   char_width[127] = 2;
   char_width[255] = 3;
   
   if (Display_Eight_Bit > 0)
     {
	for (i = (Display_Eight_Bit > 127 ? Display_Eight_Bit : 128);
	     i < 256; i++)
	  {
	     char_width[i] = 1;
	  }
     }
   dis8bit = Display_Eight_Bit;
   if ((Selective_Display = Buffer_Local.sd) < 0)
     {
	char_width[(unsigned char) '\r'] = 3;
     }
   if (JED_CSI > 0) char_width[(unsigned char) JED_CSI] = 0;
   j_csi = JED_CSI;
}


void point_column(int n)
{
   register unsigned char *p, *pmax;
   register int i;
   int tab, w, sd;
   
   FIX_CHAR_WIDTH;
   if (IS_MINIBUFFER) n -= Mini_Info.effective_prompt_len;
   
   if (CLine->len == 0)
     {
	Point = 0;
	return;
     }
   p = CLine->data;
   pmax = p + (CLine->len - 1);
   if (*pmax != '\n') pmax++;
   
   tab = Buffer_Local.tab;
   sd = (Buffer_Local.sd < 0);
   i = 0;
   n--;   /* start at 0 */
   while(p < pmax)
     {
	if ((*p == '\t') && tab)
	  {
	     i = tab * (i / tab + 1);
	  }
	else
	  {
	     w = char_width[*p];
	     i +=  w;
	     if (w == 0) p++;
	  }
	
	if ((i > n) || (sd  && (*p == '\r'))) break;
	p++;
     }
   Point = (int) (p - CLine->data);
}


/* given a position in a line, return apparant distance from bol
 *   expanding tabs, etc... up to pos.  
 * 
 * ****Note**** 
 * !!  misc.c calls this using the minibuffer prompt.  This routine
 * !!  can only depend on Point and CLine->data but not CLine->len!!
 */
int calculate_column()
{
   register int i;
   int tab, w;
   register unsigned char *cw = char_width;
   register unsigned char ch, *pos, *this_pos;
   int sd;
   
   FIX_CHAR_WIDTH;
   pos = CLine->data;
   this_pos = pos + Point;
   i = 1;
   tab = Buffer_Local.tab;
   sd = Buffer_Local.sd;
   while(pos < this_pos)
     {
	ch = *pos++;
	if ((ch == '\t') && tab)
	  {
	     i = tab * ((i - 1)/tab + 1) + 1;  /* tab column tabs */
	  }
	else
	  {
	     w = cw[ch];
	      /* w = char_width[ch]; */
	     i += w;
	     if (w == 0) pos++;
	  }
	
	if ((ch == '\r') && sd) break;
     }
   Absolute_Column = i;
   
   if (IS_MINIBUFFER) i += Mini_Info.effective_prompt_len;
   
   Screen_Col = i;
   return (i);
}

void point_cursor(int c)
{
   int r, row;
   Line *tthis;
   
   if (JWindow->trashed) return;
   r = 0;
   Point_Cursor_Flag = 0;
   for (row = JWindow->top; row < JWindow->top + JWindow->rows; row++)
     {
	tthis = JScreen[row-1].line;
	if (tthis == NULL) break;
	if ((tthis == CLine) || (tthis == &Eob_Line))
	  {
	     r = row;
	     break;
	  }
     }
   
   if (Point >= CLine->len)
     {
	Point = CLine->len - 1;
	
	if (Point < 0) Point = 0;
	else if ((*(CLine->data + Point) != '\n')
		 || (CBuf == MiniBuffer)) Point++;
     }
   
   if (r)
     {
	if (!c) c = calculate_column();
	
	c = c - JWindow->column + 1;
	if (CLine == HScroll_Line) c -= HScroll;
	if (c < 1) c = 1; else if (c > JWindow->width) c = JWindow->width;
	Point_On_Char = JScreen[r - 1].old[c - 1];
	tt_goto_rc (r - 1 , c - 1);
	Screen_Row = r;
	Screen_Col = c;
     }
   else
     {
	/* msg_error("Point not visible in Window."); */
	c = 1;
     }
   flush_output ();
   if (!Cursor_Motion) Goal_Column = c;
}

static unsigned short *stat_cpy(register unsigned short *s1, 
				register unsigned char *v,
				register unsigned short *smax)
{
   register unsigned char ch;
   if (v == NULL) return (s1);
   
   while ((s1 < smax) && ((ch = *v++) != 0))
     {
	if (((unsigned int)(ch & 0x7F) < 32)
	    || (ch == 127))
	  {
	     if (ch & 0x80)
	       {
		  *s1++ = (unsigned short) '~';
		  if (s1 == smax) break;
		  ch = ch & 0x7F;
	       }
	     *s1++ = (unsigned short) '^';
	     if (s1 < smax) 
	       {
		  if (ch == 127) *s1++ = (unsigned short) '?';
		  else 
		    *s1++ = (unsigned short) (ch + '@');
	       }
	  }
	else *s1++ = ch;
     }
   return (s1);
}

/* site.sl should modify this */

char Default_Status_Line[80] =
" ^Ke: quit, ^Kg: get file, ^K^W: write file | %b  (%m%n%o) %p";

static unsigned long Status_Last_Time;
static unsigned long Status_This_Time;

static char *status_get_time(void)
{
   static char status_time[10];
   register char *t, ch, *t1;
   char am;
   int n;
   
   if (Display_Time == 0) return (NULL);
   if (Status_This_Time == 0) Status_This_Time = sys_time();
   if (Status_This_Time - Status_Last_Time >= 30)
     {
	Status_Last_Time = Status_This_Time;
	am = 'a';
	t = SLcurrent_time_string ();
	/* returns a string like:  "Tue Nov 2 13:18:19 1993" */
	t1 = status_time;
	while (ch = *t, (ch <= '0') || (ch > '9')) t++;
	/* on date number, skip it */
	while (*t++ != ' ');
	if (*t == '0') t++;
	if (Display_Time > 0)
	  {
	     n = 0;
	     while ((ch = *t++) != ':')
	       {
		  n = 10 * n + (int) (ch - '0');
	       }
	     if (n >= 12) am = 'p';
	     n = n % 12;
	     if (n == 0) n = 12;
	     if (n >= 10)
	       {
		  n -= 10;
		  *t1++ = '1';
	       }
	     *t1++ = '0' + n;
	     *t1++ = ':';
	     while ((*t1++ = *t++) != ':');
	     *(t1 - 1) = am; *t1++ = 'm'; *t1 = 0;
	  }
	else
	  {
	     *t1++ = '[';
	     while ((*t1++ = *t++) != ':');
	     while ((*t1++ = *t++) != ':');
	     *--t1 = ']'; *++t1 = 0;
	  }
     }
   return (status_time);
}

static unsigned short *finish_status(unsigned short *s1, unsigned short *smax, int col_flag)
{
   unsigned int line, maxline;
   register char *v, ch;
   Line *l;
   int top, rows;
   char pstr[20], *str, col_buf[20];
   unsigned short *s1_save = s1;
   
   
   /* line = w->mark.n - buf->nup; */
   line = LineNum;
   maxline = Max_LineNum;
   
   if (!User_Prefers_Line_Numbers)
     {
	top = JWindow->top - 1;	rows = JWindow->rows - 1;
	l = JScreen[top + rows].line;
	if (l == CBuf->end) l = NULL;
	if (JScreen[top].line == CBuf->beg)
	  {
	     if (l == NULL) strcpy(pstr,"All");
	     else strcpy(pstr,"Top");
	  }
	else if (l == NULL) strcpy(pstr, "Bot");
	else
	  {
	     sprintf(pstr, "%d%%",
		     (int) ((line * 100L) / (long) maxline));
	  }
     }
   else sprintf(pstr, "%d/%d", line, maxline);
   
   v = CBuf->status_line;
   if (*v == 0) v = Default_Status_Line;
   
   while (((ch = *v++) != 0) && (s1 < smax))
     {
	if (ch != '%') *s1++ = ch;
	else
	  {
	     ch = *v++;
	     switch (ch)
	       {
		case 'a':
		  if (CBuf->flags & ABBREV_MODE) str = " abbrev"; else str = NULL;
		  break;
		case 'f': str = CBuf->file; break;
		case 'n':
		  if (CBuf->narrow != NULL) str = " Narrow"; else str = NULL;
		  break;
		case 'o':
		  if (CBuf->flags & OVERWRITE_MODE) str = " Ovwrt"; else str = NULL;
		  break;
		case 'b': str = CBuf->name; break;
		case 'p': str = pstr; break;
		case 'v': str = JED_VERSION; break;
		case 'm': str = CBuf->mode_str; break;
		case 't': str = status_get_time(); break;
		case 'c': if (User_Prefers_Line_Numbers > 1)
		    {
		       if (col_flag) (void) calculate_column ();
		       sprintf(col_buf, "%d",  Absolute_Column);
		       str = col_buf;
		    }
		  else
		    {
		       /* 100 to 1 that there is punctuation before this, kill it */
		       if (s1 > s1_save) s1--;
		       str = NULL;
		    }
		  
		  break;
		  
		  
		case '%': str = "%"; break;
		default: return(s1);
	       }
	     if (str != NULL) s1 = stat_cpy(s1, (unsigned char *) str, smax);
	  }
     }
   return (s1);
}

void set_status_format(char *f, int *local)
{
   char *s;
   if (*local) s = Default_Status_Line; else s = CBuf->status_line;
   strncpy(s, f, 79);
   s[79] = 0;
}



static void make_status_line(int col_flag)
{
   unsigned short mrk, flag, spot, star, ubit = '-';
   register unsigned short *s1, *s, *smax;
   
   if (JWindow->top == *tt_Screen_Rows) return;   /* minibuffer ? */
   s = JScreen[JWindow->rows + JWindow->top - 1].neew;
   smax = s + JWindow->width;
   mrk = flag = spot = star = '-';
   if (CBuf->marks != NULL) mrk = 'm';
   if (CBuf->flags & FILE_MODIFIED) flag = 'd';
   if (CBuf->spots != NULL) spot = 's';
   if (CBuf->flags & BUFFER_TRASHED) star = '*';
   if (CBuf->flags & READ_ONLY) star = '%';
   if (CBuf->flags & UNDO_ENABLED) ubit = '+';
   
   s1 = s;
   if (JWindow->column != 1) *s1++ = '<'; else *s1++ = '-';
   *s1++ = star; *s1++ = star; *s1++ = mrk; *s1++ = flag;
   *s1++ = spot;
   
   if (CBuf->flags & BINARY_FILE) *s1++ = 'B';
#ifdef pc_system
   else if ((CBuf->flags & ADD_CR_ON_WRITE_FLAG) == 0) *s1++ = 'L';
#else
#ifdef unix
   else if (CBuf->flags & ADD_CR_ON_WRITE_FLAG) *s1++ = 'C';
#endif
#endif
   else *s1++ = '-';
   
   *s1++ = ubit;
   s1 = finish_status(s1, smax, col_flag);
   if (Defining_Keyboard_Macro) s1 = stat_cpy(s1, (unsigned char *) " [Macro]", smax);
   while (s1 < smax) *s1++ = '-';
   *smax = 0;
   (void) fix_attributes (s, smax, JSTATUS_COLOR);
}

static int update_status_line(int col_flag)
{
   int r;
   
   if (JWindow->top == *tt_Screen_Rows) return(0);   /* minibuffer ? */
   r = JWindow->rows + JWindow->top - 1;
   make_status_line(col_flag);
   
   tt_smart_puts(JScreen[r].neew, JScreen[r].old, *tt_Screen_Cols, r);
   Point_Cursor_Flag = 1;
   update_screen_txt(r);
   JScreen[r].n = *tt_Screen_Cols;
   return(1);
}

/* if force then do update otherwise return 1 if update or 0 if not */
static int update_1(Line *top, int force)
{
   int i;
   Window_Type *w;
   int did_eob = 0, time_has_expired = 0;
   
   
   if (Batch ||
       (!force
	&& (Executing_Keyboard_Macro || (Repeat_Factor != NULL)
	    || Input_Buffer_Len || input_pending(&Number_Zero)
	    || (Read_This_Character != NULL)))
       || (CBuf != JWindow->buffer))
     
     {
	return(0);
     }
   
   if (Suspend_Screen_Update != 0)
     {
	Suspend_Screen_Update = 0;
	touch_screen ();
     }
   
   
   JWindow->mark.line = CLine;
   JWindow->mark.point = Point;
   JWindow->mark.n = LineNum + CBuf->nup;
   CBuf->linenum = LineNum;
   CBuf->max_linenum = Max_LineNum;
   
   if (Wants_Attributes && CBuf->vis_marks)
     {
	JWindow->trashed = 1;
     }
   
   /* Do not bother setting this unless it is really needed */
   if (Display_Time)
     {
	Status_This_Time = sys_time();
	time_has_expired = (Status_This_Time > Status_Last_Time + 45);
     }
   
   /* if cursor moves just left right, do not update status line */
   if (!force && !JWindow->trashed &&
       ((JWindow == JWindow->next) || (User_Prefers_Line_Numbers && Cursor_Motion))
	/* if % wanted, assume user is like me and gets annoyed with 
	   screen updates */
       && (User_Prefers_Line_Numbers || time_has_expired))
     
     {
	update_status_line(0);
	return(1);
     }
   
   if (!JWindow->trashed && Cursor_Motion) return(1);
   
   w = JWindow;
   do
     {
/* #define HANDLE_CTRL_L */
#ifdef HANDLE_CTRL_L
	int cline_found = 0;
#endif
	if (Wants_Syntax_Highlight) init_syntax_highlight ();
	did_eob = 0;
	if (top == NULL) top = find_top();
	if (top == NULL) top = CLine;
	
	JWindow->beg.line = top;
	
	 /* scroll the screen to optimal location */
	if (0 == *tt_Term_Cannot_Scroll) do_scroll(top);
	
	mark_window_attributes((w == JWindow) || (w->buffer != CBuf));

	for (i = JWindow->top - 1; i < JWindow->rows + JWindow->top - 1; i++)
	  {
	      /* the next line is really optional */
#ifndef pc_system
	     if (!force && (Exit_From_MiniBuffer ||
			    input_pending(&Number_Zero))) break;
#endif
#ifdef HANDLE_CTRL_L
	     if (CLine == top)
	       {
		  cline_found = 1;
	       }
#endif	     
	     if ((JScreen[i].line != top) || (JScreen[i].flags)
		 || (Want_Eob
		     && !did_eob
		     && (i != *tt_Screen_Rows - 1)
		     && (top == NULL)))
	       {
		  if (((top == NULL) || (top->len == 0))
		      && (Want_Eob && !did_eob && !(CBuf->flags & READ_ONLY)))
		    {
		       display_line(&Eob_Line, i + 1);
		       
			/* JScreen[i].line = top; */
		       did_eob = 1;
		    }
		  else display_line(top, i + 1);
	       }
	     if (top != NULL) 
	       {
#ifdef HANDLE_CTRL_L
		  if (cline_found && (*top->data == 0xC))
		    {
		       cline_found = -1;
		       top = NULL;
		    }
		  else
#endif
		    top = top->next;
	       }
	  }
	
	HScroll_Line = NULL;
	Mode_Has_Syntax_Highlight = 0;
	if (!force && input_pending(&Number_Zero))
	  {
	     while(JWindow != w) other_window();
	     JWindow->trashed = 1;  /* since cursor not pointed */
	     return(0);
	  }
	else update_status_line(w != JWindow);

	JWindow->trashed = 0;

	other_window();
	top = NULL;
	 /* if (!JWindow->trashed) top = JWindow->beg.line; else  top = NULL; */
	
     }
   while(JWindow != w);
   return(1);
}

int Mini_Ghost = 0;

static void update_minibuffer(void)
{
   Window_Type *w;
   
   if (Executing_Keyboard_Macro) return;
   if (MiniBuffer != NULL)
     {
	w = JWindow;
	while (!IS_MINIBUFFER) other_window();
	if ((*Message_Buffer) && JScreen[*tt_Screen_Rows - 1].n) (void) input_pending(&Number_Ten);
	JWindow->beg.line = CLine;
	mark_window_attributes(1);
	display_line(CLine, *tt_Screen_Rows);
	while (w != JWindow) other_window();
	Mini_Ghost = 1;
     }
   else if (Mini_Ghost && !*Error_Buffer && !*Message_Buffer)
     {
	/* if < 0, it is a result of flush message so let it pass this round */
	if (Mini_Ghost < 0) Mini_Ghost = 1;
	else
	  {
	     display_line(NULL, *tt_Screen_Rows);
	     if (!JWindow->trashed) point_cursor(0);
	     Mini_Ghost = 0;
	  }
     }
   else Mini_Ghost = ((*Message_Buffer) || (*Error_Buffer));
}

void do_dialog(char *b)
{
   int len = 0, dout, row = *tt_Screen_Rows - 1;
   char *quit = "Quit!";
   
   if (Batch) return;
   FIX_CHAR_WIDTH;
   if (! *b)
     {
	if(!SLKeyBoard_Quit) return;
	b = quit;
     }
   if (MiniBuffer)
     {
	len = Mini_Info.prompt_len;
	Mini_Info.prompt_len = 0;
     }
   
   if ((b == Error_Buffer) || (b == quit))
     {
	Message_Color = JERROR_COLOR << 8;
	touch_screen();
     }
   else Message_Color = JMESSAGE_COLOR << 8;
   
   dout = output((unsigned char *) b, strlen(b),
		 *tt_Screen_Rows, *tt_Screen_Cols, 1, NULL, NULL, NULL);
   Message_Color = 0;
   
   if (MiniBuffer) Mini_Info.prompt_len = len;
   
   if ((b == Error_Buffer) || (SLKeyBoard_Quit))
     {
	beep();
	flush_input();
     }
   
   if (!dout)
     {
	if (JScreen[row].n)
	  {
	     tt_goto_rc(*tt_Screen_Rows - 1, 0);
	     tt_del_eol();
	  }
	blank_line(row);
	JScreen[row].n = 0;
     }
   else
     {
	update_screen_txt(row);
	if (MiniBuffer != NULL)
	  {
	     flush_output ();
	     (void) input_pending(&Number_Ten);
	  }
     }
   
   Mini_Ghost = -dout;
}

static void set_hscroll(int col)
{
   int hdiff, whs = abs(Wants_HScroll), wc = JWindow->column - 1,
     sw = *tt_Screen_Cols - 1;
   static Line *last;
   Line *tmp;
   
   /* take care of last effect of horizontal scroll */
   if (last != NULL)
     {
	tmp = CLine;
	CLine = last;
	register_change(0);
	CLine = tmp;
	if (last != CLine)
	  {
#if 0
	     /* I need to think about this more */
	     if (Wants_HScroll < 0)
	       {
		  if (wc != 0)
		    {
		       JWindow->column = 1;
		       wc = 0;
		       touch_window ();
		    }
	       }
#endif
	     HScroll = 0;
	  }
	
	last = NULL;
     }
   
   col--;			       /* use 0 origin */
   hdiff = col - wc;
   if ((HScroll >= hdiff)
       || (HScroll <= hdiff - sw))
     {
	if (hdiff >= sw)
	  {
	     HScroll = hdiff - sw + whs;
	  }
	else if ((hdiff == 0) && (wc == 0)) HScroll = 0;
	else if (hdiff <= 1)
	  {
	     HScroll = hdiff - whs - 1;
	  }
	else HScroll = 0;
     }
   
   if (HScroll)
     {
	if (wc + HScroll < 0) HScroll = -wc;
	
	if (Wants_HScroll < 0)
	  {
	     JWindow->column += HScroll;
	     touch_window();
	     HScroll = 0;
	  }
	else
	  {
	     register_change(0);
	     last = HScroll_Line = CLine;
	  }
     }
}

static char Top_Screen_Line_Buffer[132] = "If you see this, you have an installation problem.";

void define_top_screen_line (char *neew)
{
   SLang_push_string (Top_Screen_Line_Buffer);
   strncpy (Top_Screen_Line_Buffer, neew, 130);
   Top_Screen_Line_Buffer[131] = 0;
   JScreen[0].flags = 1;
}

static void update_top_screen_line (void)
{
   register unsigned short *s, *smax, *s1;
   register unsigned char ch, *chp;
   
   if (Top_Window_Row == 1) return;
   
   chp = (unsigned char *) Top_Screen_Line_Buffer;
   
   s = s1 = JScreen[0].neew;
   smax = s + JWindow->width;
   
   while ((s1 < smax) && ((ch = *chp++) != 0))
     {
	*s1++ = (unsigned short) ch;
     }
   while (s1 < smax) *s1++ = ' ';
   *smax = 0;
   (void) fix_attributes (s, smax, JMENU_COLOR);
   
   tt_smart_puts(JScreen[0].neew, JScreen[0].old, *tt_Screen_Cols, 0);
   Point_Cursor_Flag = 1;
   update_screen_txt(0);
   JScreen[0].n = *tt_Screen_Cols;
   JScreen[0].flags = 0;
}



/* if flag is non-zero, do not touch the message/error buffers */
void update(Line *line, int force, int flag)
{
   int pc_flag = 1;
   int col;
   static unsigned long last_time;
   Line *hscroll_line_save;
   
#ifdef HAS_SUBPROCESSES
   if (Child_Status_Changed_Flag)
     {
	jed_get_child_status ();
	force = 1;
     }
#endif
   
   if (Batch) return;
   
   if (!force && !SLang_Error && !SLKeyBoard_Quit && (!*Error_Buffer))
     {
	if (Input_Buffer_Len || input_pending (&Number_Zero))
	  {
	     JWindow->trashed = 1;
	     return;
	  }
     }
   
   
   if (last_time + 30 < Status_This_Time)
     {
	if (last_time == 0) last_time = Status_This_Time;
	else
	  {
	     last_time = Status_This_Time;
	     if (SLang_run_hooks ("update_timer_hook", NULL, NULL))
	       flag = 0;
	  }
     }
   
   
   if (Suspend_Screen_Update != 0)
     {
	Suspend_Screen_Update = 0;
	touch_screen ();
     }
   
   if (X_Update_Open_Hook != NULL) (*X_Update_Open_Hook) ();
   FIX_CHAR_WIDTH;
   
   col = calculate_column();
   HScroll_Line = NULL;
   if (Wants_HScroll) set_hscroll(col); else HScroll = 0;
   hscroll_line_save = HScroll_Line;
   
   if (SLang_Error) flag = 0;	       /* update hook invalidates flag */
   
   if (SLang_Error && !(*Error_Buffer || SLKeyBoard_Quit)) SLang_doerror(NULL);
   
   if (!flag && (*Error_Buffer || SLKeyBoard_Quit))
     {
	do_dialog(Error_Buffer);
	SLKeyBoard_Quit = 0;
	SLang_restart(0);
	SLang_Error = 0;
	flag = 0;
	Mini_Ghost = 1;
	(void) update_1(line, 1);
	update_minibuffer();
     }
   else if (!flag && *Message_Buffer)
     {
	if (!update_1(line, force))
	  {
	     /* *Message_Buffer = 0; */
	     goto done;
	  }
	Mini_Ghost = 1;
	do_dialog(Message_Buffer);
	update_minibuffer();
     }
   else
     {
	pc_flag = JWindow->trashed || (JWindow != JWindow->next) || Cursor_Motion;
	if (!flag) update_minibuffer();
	if (!update_1(line, force)) goto done;
     }
   if (!flag) *Error_Buffer = *Message_Buffer = 0;
   
   if ((Top_Window_Row != 1) && JScreen[0].flags)
     {
	update_top_screen_line ();
     }
   
   
   done:
   
   HScroll_Line = hscroll_line_save;
   
   if (Point_Cursor_Flag || pc_flag) point_cursor(col);
   if (X_Update_Close_Hook != NULL) (*X_Update_Close_Hook) ();
   flush_output ();
}

/* search for the CLine in the SCreen and flag it as changed */
/* n = 0 means line was changed, n = 1 means it was destroyed */
void register_change(int n)
{
   Window_Type *w;
   register Screen_Type *s, *smax;
   register Line *cl = CLine;
   
   JWindow->trashed = 1;
   if (Suspend_Screen_Update) return;
   if (No_Screen_Update)
     {
	No_Screen_Update = 0;
	if (((n == CINSERT) || (n == CDELETE)) && (JWindow->next == JWindow))
	  {
	       /* Since no screen update, we are probably safe to do: */
	       /* JScreen[Screen_Row - 1].flags = 1; */
	     return;
	  }
	w = JWindow->next;  /* skip this window */
     }
   else w = JWindow;
   
   do
     {
	s = &JScreen[w->top - 1];
	smax = s + w->rows;
	
	while (s < smax)
	  {
	     if (s->line == cl)
	       {
		  s->flags = 1;
		  if ((n == NLDELETE) || (n == LDELETE)) s->line = NULL;
		  w->trashed = 1;
	       }
	     s++;
	  }
	w = w->next;
     }
   while(w != JWindow);
}

static int Display_Initialized;

void reset_display()
{
   int i;
   unsigned short *p;
   
   if (Display_Initialized == 0) return;
   if (Batch) return;
   Display_Initialized = 0;
   
   tt_reset_video ();
   /* tt_del_eol ();
    flush_output (); */
   for (i = 0; i < *tt_Screen_Rows; i++)
     {
	p = JScreen[i].old;  if (p != NULL) SLFREE(p); JScreen[i].old = NULL;
	p = JScreen[i].neew;  if (p != NULL) SLFREE(p); JScreen[i].neew = NULL;
     }
}

void init_display(int g)
{
   int i, r, c;
   unsigned short *old, *neew = NULL;
   
   if (Batch) return;
   if (g)
     {
	get_term_dimensions(&c, &r);
	if (r > MAX_SCREEN_SIZE) r = MAX_SCREEN_SIZE;
	if (r <= 4) r = 4;
	if (c <= 4) c = 4;
	if (JWindow != NULL)
	  {
	     /* Note that this next call will call this routine again 
	        with g = 0. */
	     change_screen_size (c, r);
	     return;
	  }
	
	*tt_Screen_Cols = c;
	*tt_Screen_Rows = r;
     }
   
   tt_init_video();
   
   for (i = 0; i < *tt_Screen_Rows; i++)
     {
	/* these are bigger to handle overflow in output routine in case
	 * special char occurs at end as well as allowing code as in blank_line*/
	if ((NULL == (old = (unsigned short *) SLCALLOC(*tt_Screen_Cols + 3, sizeof(short))))
	    || (NULL == (neew = (unsigned short *) SLCALLOC(*tt_Screen_Cols + 3, sizeof(short)))))
	  {
	     exit_error("init_display(): malloc error.", 0);
	  }
	
	JScreen[i].line = NULL;
	JScreen[i].flags = 1;
	JScreen[i].n = 0;
	JScreen[i].old = old;
	JScreen[i].neew = neew;
	neew = old + *tt_Screen_Cols;
	while (old < neew) *old++ = ' ';
     }
   Display_Initialized = 1;
}


void redraw_screen(int force)
{
   int row, center;
   Window_Type *w;
   Line *l;
   
   
   if (Batch) return;
#ifndef msdos
   /* send_string_to_term("\033[?6h"); */  /* relative origin mode */
   tt_reset_scroll_region();
   tt_end_insert();
#endif
   tt_normal_video ();
   tt_cls();
   for (row = 0; row < *tt_Screen_Rows; row++)
     {
	JScreen[row].line = NULL;
	JScreen[row].n = 0;
	JScreen[row].flags = 1;
	blank_line(row);
     }
   w = JWindow;
   center = JWindow->trashed;
   do
     {
	w->trashed = 1;
	w = w->next;
     }
   while(w != JWindow);
   
   if (center)
     {
	for (row = 0; row < JWindow->rows; row++)
	  {
	     JScreen[row + JWindow->top - 1].line = NULL;
	  }
	l = NULL;
     }
   else l = JWindow->beg.line;
   update(l, force, 0);
}

int redraw ()
{
   redraw_screen(0);
   return 0;
}

void recenter(int *np)
{
   Line *l = CLine;
   int i, n = *np;
   
   JWindow->trashed = 1;
   if (n == 0)
     {
	n = JWindow->rows / 2;
	for (i = 0; i < n; i++)
	  {
	     if (l->prev == NULL) break;
	     l = l->prev;
	  }
	JWindow->beg.line = l;
	JWindow->beg.n -= i;
	JWindow->beg.point = 0;
	redraw();
	return;
     }
   
   if (CBuf != JWindow->buffer) return;
   
   if ((n <= 0) || (n > JWindow->rows)) n = JWindow->rows / 2;
   while (--n)
     {
	l = l->prev;
	if (l == NULL)
	  {
	     l = CBuf->beg;
	     break;
	  }
     }
   
   update(l, 0, 0);
}


int window_line(void)
{
   Line *top = find_top();
   int n = 1;
   
   if (CBuf != JWindow->buffer) return(0);
   while ((top != NULL) && (top != CLine))
     {
	n++;
	top = top->next;
     }
   return(n);
}


void touch_window()
{
   int i;
   
   if (Suspend_Screen_Update) return;
   for (i = 0; i < JWindow->rows; i++)
     {
	JScreen[i + JWindow->top - 1].flags = 1;
     }
   
   JWindow->trashed = 1;
}


void touch_screen()
{
   Window_Type *w;
   
   No_Screen_Update = 0;
   if (Suspend_Screen_Update) return;
   w = JWindow;
   do
     {
	touch_window();
	JWindow = JWindow->next;
     }
   while(w != JWindow);
   if (Top_Window_Row != 1) JScreen[0].flags = 1;
}


void exit_error(char *str, int severity)
{
   SLang_Error = SLKeyBoard_Quit = 0;
   auto_save_all();
   reset_display();
   reset_tty();
   fprintf(stderr,"\007\rJED (%s): Fatal Error: %s\n", JED_VERSION, str);
   if (*Error_Buffer) fprintf (stderr, "%s\n", Error_Buffer);
   if (CBuf != NULL)
     {
	if (Batch == 0)
	  {
	     fprintf(stderr, "CBuf: %p, CLine: %p, Point %d\n", CBuf, CLine, Point);
	     if (CLine != NULL) fprintf(stderr, "CLine: data: %p, len = %d, next: %p, prev %p\n",
					CLine->data, CLine->len, CLine->next, CLine->prev);
	     fprintf(stderr, "Max_LineNum: %d, LineNum: %d\n", Max_LineNum, LineNum);
	     if (JWindow != NULL) fprintf(stderr, "JWindow: %p, top: %d, rows: %d, buffer: %p\n",
					  JWindow, JWindow->top, JWindow->rows, JWindow->buffer);
	  }
	auto_save_all();
     }
   if (severity)
     {
#ifdef unix
	fprintf(stderr, "Dumping Core.");
	abort ();
#endif
     }
   exit(-1);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.