548 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			548 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/* Copyright (c) 1985 Ceriel J.H. Jacobs */
 | 
						|
 | 
						|
# ifndef lint
 | 
						|
static char rcsid[] = "$Header$";
 | 
						|
# endif
 | 
						|
 | 
						|
# define _DISPLAY_
 | 
						|
 | 
						|
# include "in_all.h"
 | 
						|
# include "display.h"
 | 
						|
# include "assert.h"
 | 
						|
# include "machine.h"
 | 
						|
# include "term.h"
 | 
						|
# include "output.h"
 | 
						|
# include "options.h"
 | 
						|
# include "process.h"
 | 
						|
# include "getline.h"
 | 
						|
# include "main.h"
 | 
						|
 | 
						|
STATIC char * do_line();
 | 
						|
 | 
						|
/*
 | 
						|
 * Fill n lines of the screen, each with "str".
 | 
						|
 */
 | 
						|
 | 
						|
STATIC VOID
 | 
						|
fillscr(n,str) char *str; int n; {
 | 
						|
 | 
						|
	while (n-- > 0) {
 | 
						|
		putline(str);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Skip "n" screenlines of line "p", and return what's left of it.
 | 
						|
 */
 | 
						|
 | 
						|
STATIC char *
 | 
						|
skiplines(p,n) char *p; int n; {
 | 
						|
 | 
						|
	while (n-- > 0) {
 | 
						|
		p = do_line(p,0);
 | 
						|
		scr_info.currentpos--;
 | 
						|
	}
 | 
						|
	return p;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Redraw screen.
 | 
						|
 * "n" = 1 if it is a real redraw, 0 if one page must be displayed.
 | 
						|
 * It is also called when yap receives a stop signal.
 | 
						|
 */
 | 
						|
 | 
						|
VOID
 | 
						|
redraw(n) int n; {
 | 
						|
	register struct scr_info *p = &scr_info;
 | 
						|
	register int i;
 | 
						|
 | 
						|
	i = pagesize;
 | 
						|
	if (n && p->currentpos) {
 | 
						|
		i = p->currentpos;
 | 
						|
	}
 | 
						|
	(VOID) display(p->firstline,p->nf,i,1);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Compute return value for the routines "display" and "scrollf".
 | 
						|
 * This return value indicates wether we are at the end of file
 | 
						|
 * or at the start, or both.
 | 
						|
 * "s" contains that part of the last line that was not displayed.
 | 
						|
 */
 | 
						|
 | 
						|
STATIC int
 | 
						|
compretval(s) char *s; {
 | 
						|
	register int i;
 | 
						|
	register struct scr_info *p = &scr_info;
 | 
						|
 | 
						|
	i = 0;
 | 
						|
	if (!s || (!*s && !getline(p->lastline+1, 1))) {
 | 
						|
		i = EOFILE;
 | 
						|
	}
 | 
						|
	if (p->firstline == 1 && !p->nf) {
 | 
						|
		i |= START;
 | 
						|
	}
 | 
						|
	status = i;
 | 
						|
	return i;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Display nlines, starting at line n, not displaying the first
 | 
						|
 * nd screenlines of n.
 | 
						|
 * If reallydispl = 0, the actual displaying is not performed,
 | 
						|
 * only the computing associated with it is done.
 | 
						|
 */
 | 
						|
 | 
						|
int
 | 
						|
display(n,nd,nlines,reallydispl)
 | 
						|
  long n; int nd; register int nlines; int reallydispl; {
 | 
						|
 | 
						|
	register struct scr_info *s = &scr_info;
 | 
						|
	register char *p;	/* pointer to line to be displayed */
 | 
						|
 | 
						|
	if (startcomm)	{	/* No displaying on a command from the
 | 
						|
				 * yap command line. In this case, displaying
 | 
						|
				 * will be done after executing the command,
 | 
						|
				 * by a redraw.
 | 
						|
				 */
 | 
						|
		reallydispl = 0;
 | 
						|
	}
 | 
						|
	if (!n) {
 | 
						|
		n = 1L;
 | 
						|
		nd = 0;
 | 
						|
	}
 | 
						|
	if (reallydispl) {	/* move cursor to starting point */
 | 
						|
		if (stupid) {
 | 
						|
			putline(currentfile);
 | 
						|
			putline(", line ");
 | 
						|
			prnum(n);
 | 
						|
			nlines--;
 | 
						|
		}
 | 
						|
		if (cflag) {
 | 
						|
			putline("\r\n");
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			home();
 | 
						|
			clrscreen();
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/*
 | 
						|
	 * Now, do computations and display
 | 
						|
	 */
 | 
						|
	s->currentpos = 0;
 | 
						|
	s->nf = nd;
 | 
						|
	s->head = s->tail;
 | 
						|
	s->tail->cnt = 0;
 | 
						|
	s->tail->line = n;
 | 
						|
	p = skiplines(getline(n,1),nd);
 | 
						|
	while (nlines && p) {
 | 
						|
		/*
 | 
						|
		 * While there is room,
 | 
						|
		 * and there is something left to display ...
 | 
						|
		 */
 | 
						|
		(s->tail->cnt)++;
 | 
						|
		nlines--;
 | 
						|
		if (*(p = do_line(p,reallydispl)) == '\0') {
 | 
						|
			/*
 | 
						|
			 * File-line finished, get next one ...
 | 
						|
			 */
 | 
						|
			p = getline(++n,1);
 | 
						|
			if (nlines && p) {
 | 
						|
				s->tail = s->tail->next;
 | 
						|
				s->tail->cnt = 0;
 | 
						|
				s->tail->line = n;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (!stupid) {
 | 
						|
		s->currentpos += nlines;
 | 
						|
		if (reallydispl) {
 | 
						|
			fillscr(nlines, "~\r\n");
 | 
						|
			fillscr(maxpagesize - s->currentpos, "\r\n");
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return compretval(p);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Scroll forwards n lines.
 | 
						|
 */
 | 
						|
 | 
						|
int
 | 
						|
scrollf(n,reallydispl) int n; int reallydispl; {
 | 
						|
 | 
						|
	register struct scr_info *s = &scr_info;
 | 
						|
	register char *p;
 | 
						|
	register long ll;
 | 
						|
	register int i;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * First, find out how many screenlines of the last line were already
 | 
						|
	 * on the screen, and possibly above it.
 | 
						|
	 */
 | 
						|
 | 
						|
	if (n <= 0 || (status & EOFILE)) return status;
 | 
						|
	if (startcomm) reallydispl = 0;
 | 
						|
	/*
 | 
						|
	 * Find out where to begin displaying
 | 
						|
	 */
 | 
						|
	i = s->tail->cnt;
 | 
						|
	if ((ll = s->lastline) == s->firstline) i += s->nf;
 | 
						|
	p = skiplines(getline(ll, 1), i);
 | 
						|
	/*
 | 
						|
	 * Now, place the cursor at the first free line
 | 
						|
	 */
 | 
						|
	if (reallydispl && !stupid) {
 | 
						|
		clrbline();
 | 
						|
		mgoto(s->currentpos);
 | 
						|
	}
 | 
						|
	/*
 | 
						|
	 * Now display lines, keeping track of which lines are on the screen.
 | 
						|
	 */
 | 
						|
	while (n-- > 0) {	/* There are still rows to be displayed */
 | 
						|
		if (!*p) {	/* End of line, get next one */
 | 
						|
			if (!(p = getline(++ll, 1))) {
 | 
						|
				/*
 | 
						|
				 * No lines left. At end of file
 | 
						|
				 */
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			s->tail = s->tail->next;
 | 
						|
			s->tail->cnt = 0;
 | 
						|
			s->tail->line = ll;
 | 
						|
		}
 | 
						|
		if (s->currentpos >= maxpagesize) {
 | 
						|
			/*
 | 
						|
			 * No room, delete first screen-line
 | 
						|
			 */
 | 
						|
			s->currentpos--;
 | 
						|
			s->nf++;
 | 
						|
			if (--(s->head->cnt) == 0) {
 | 
						|
				/*
 | 
						|
				 * The first file-line on the screen is wiped
 | 
						|
				 * out completely; update administration
 | 
						|
				 * accordingly.
 | 
						|
				 */
 | 
						|
				s->nf = 0;
 | 
						|
				s->head = s->head->next;
 | 
						|
				assert(s->head->cnt > 0);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		s->tail->cnt++;
 | 
						|
		p = do_line(p, reallydispl);
 | 
						|
	}
 | 
						|
	return compretval(p);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Scroll back n lines
 | 
						|
 */
 | 
						|
 | 
						|
int
 | 
						|
scrollb(n, reallydispl) int n, reallydispl; {
 | 
						|
 | 
						|
	register struct scr_info *s = &scr_info;
 | 
						|
	register char *p;	/* Holds string to be displayed */
 | 
						|
	register int i;
 | 
						|
	register int count;
 | 
						|
	register long ln;	/* a line number */
 | 
						|
	register int nodispl;
 | 
						|
	int cannotscroll;	/* stupid or no insert-line */
 | 
						|
 | 
						|
	/*
 | 
						|
	 * First, find out where to start
 | 
						|
	 */
 | 
						|
	if ((count = n) <= 0 || (status & START)) return status;
 | 
						|
	if (startcomm) reallydispl = 0;
 | 
						|
	cannotscroll = stupid || (!*AL && !*SR);
 | 
						|
	ln = s->firstline;
 | 
						|
	nodispl = s->nf;
 | 
						|
	while (count) { /* While scrolling back ... */
 | 
						|
		if (i = nodispl) {
 | 
						|
			/*
 | 
						|
			 * There were screen-lines of s->firstline that were not
 | 
						|
			 * displayed.
 | 
						|
			 * We can use them now, but only "count" of them.
 | 
						|
			 */
 | 
						|
			if (i > count) i = count;
 | 
						|
			s->currentpos += i;
 | 
						|
			nodispl -= i;
 | 
						|
			count -= i;
 | 
						|
		}
 | 
						|
		else {	/* Get previous line */
 | 
						|
			if (ln == 1) break; /* isn't there ... */
 | 
						|
			p = getline(--ln, 1);
 | 
						|
			/*
 | 
						|
			 * Make it the first line of the screen and compute
 | 
						|
			 * how many screenlines it takes. These lines are not
 | 
						|
			 * displayed, but nodispl is set to this count, so
 | 
						|
			 * that it will be nonzero next time around
 | 
						|
			 */
 | 
						|
			nodispl = 0;
 | 
						|
			do {	/* Find out how many screenlines */
 | 
						|
				nodispl++;
 | 
						|
				p = skiplines(p, 1);
 | 
						|
			} while (*p);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	n -= count;
 | 
						|
	if ((i = s->currentpos) > maxpagesize) i = maxpagesize;
 | 
						|
	if (reallydispl && hardcopy) i = n;
 | 
						|
	/*
 | 
						|
	 * Now that we know where to start, we can use "display" to do the
 | 
						|
	 * rest of the computing for us, and maybe even the displaying ...
 | 
						|
	 */
 | 
						|
	i = display(ln,
 | 
						|
		    nodispl,
 | 
						|
		    i,
 | 
						|
		    reallydispl && cannotscroll);
 | 
						|
	if (cannotscroll || !reallydispl) {
 | 
						|
		/*
 | 
						|
		 * Yes, "display" did the displaying, or we did'nt have to
 | 
						|
		 * display at all.
 | 
						|
		 * I like it, but the user obviously does not.
 | 
						|
		 * Let him buy another (smarter) terminal ...
 | 
						|
		 */
 | 
						|
		return i;
 | 
						|
	}
 | 
						|
	/*
 | 
						|
	 * Now, all we have to do is the displaying. And we are dealing with
 | 
						|
	 * a smart terminal (it can insert lines or scroll back).
 | 
						|
	 */
 | 
						|
	home();
 | 
						|
	/*
 | 
						|
	 * Insert lines all at once
 | 
						|
	 */
 | 
						|
	for (i = n; i; i--) {
 | 
						|
		if (DB && *CE) {
 | 
						|
			/*
 | 
						|
			 * Grumble..., terminal retains lines below, so we have
 | 
						|
			 * to clear the lines that we push off the screen
 | 
						|
			 */
 | 
						|
			clrbline();
 | 
						|
			home();
 | 
						|
		}
 | 
						|
		if (*SR) {
 | 
						|
			scrollreverse();
 | 
						|
		}
 | 
						|
		else {
 | 
						|
# ifdef VT100_PATCH
 | 
						|
			insert_line(0);
 | 
						|
# else
 | 
						|
			insert_line();
 | 
						|
# endif
 | 
						|
		}
 | 
						|
	}
 | 
						|
	p = skiplines(getline(ln = s->firstline, 1), s->nf);
 | 
						|
	for (i = 0; i < n; i++) {
 | 
						|
		p = do_line(p,1);
 | 
						|
		s->currentpos--;
 | 
						|
		if (!*p) {
 | 
						|
			p = getline(++ln, 1);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return count;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Process a line.
 | 
						|
 * If reallydispl > 0 then display it.
 | 
						|
 */
 | 
						|
 | 
						|
STATIC char *
 | 
						|
do_line(str, reallydispl) register char *str; int reallydispl; {
 | 
						|
 | 
						|
	char buf[1024];
 | 
						|
	register char *p = buf;
 | 
						|
	register int pos = COLS;
 | 
						|
	register int c;
 | 
						|
	register int c1;
 | 
						|
	register int do_ul = 0, do_hl = 0;
 | 
						|
	int lastmode = 0, lasthlmode = 0;
 | 
						|
	int c2;
 | 
						|
 | 
						|
	while (*str && pos > 0) {
 | 
						|
		if (*str < ' ' && (c1 = match(str,&c2,sppat)) > 0) {
 | 
						|
			/*
 | 
						|
			 * We found a string that matches, and thus must be
 | 
						|
			 * echoed literally
 | 
						|
			 */
 | 
						|
			if ((pos - c2) <= 0) {
 | 
						|
				/*
 | 
						|
				 * It did not fit
 | 
						|
				 */
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			pos -= c2;
 | 
						|
			str += c1;
 | 
						|
			if (reallydispl) {
 | 
						|
				c = *str;
 | 
						|
				*p = *str = 0;
 | 
						|
				cputline(p = buf);
 | 
						|
				putline(str - c1);
 | 
						|
				*str = c;
 | 
						|
			}
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
		c = *str++;
 | 
						|
		do_hl = 0;
 | 
						|
		if (*str == '\b' && *(str+1) != 0
 | 
						|
					&& (c != '_' || *(str+2) == '\b')) {
 | 
						|
			while (*str == '\b' && *(str+1) != 0) {
 | 
						|
				str++;
 | 
						|
				c = *str++;
 | 
						|
				do_hl = 1;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		do_ul = 1;
 | 
						|
		/*
 | 
						|
		 * Find underline sequences ...
 | 
						|
		 */
 | 
						|
		if (c == '_' && *str == '\b') {
 | 
						|
			str++;
 | 
						|
			c = *str++;
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			if (*str == '\b' && *(str+1) == '_') {
 | 
						|
				str += 2;
 | 
						|
			}
 | 
						|
			else	do_ul = 0;
 | 
						|
		}
 | 
						|
		if (reallydispl && do_hl != lasthlmode) {
 | 
						|
			*p = 0;
 | 
						|
			cputline(p = buf);
 | 
						|
			if (do_hl) bold();
 | 
						|
			else end_bold();
 | 
						|
		}
 | 
						|
		lasthlmode = do_hl;
 | 
						|
		if (reallydispl && do_ul != lastmode) {
 | 
						|
			*p = 0;
 | 
						|
			cputline(p = buf);
 | 
						|
			if (do_ul) underline();
 | 
						|
			else end_underline();
 | 
						|
		}
 | 
						|
		lastmode = do_ul;
 | 
						|
		*p++ = c;
 | 
						|
		if (c >= ' ' && c < 0177) {
 | 
						|
			pos--;
 | 
						|
			if (reallydispl && do_ul && *UC && pos > 0) {
 | 
						|
				/*
 | 
						|
				 * Underlining apparently is done one
 | 
						|
				 * character at a time.
 | 
						|
				 */
 | 
						|
				*p = 0;
 | 
						|
				cputline(p = buf);
 | 
						|
				backspace();
 | 
						|
				underchar();
 | 
						|
			}
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
		if (c == '\t') {
 | 
						|
			p--;
 | 
						|
			c1 = 8 - ((COLS - pos) & 07);
 | 
						|
			/*
 | 
						|
			 * Actually, if COLS is a multiple of 8, this can be
 | 
						|
			 * simplified to
 | 
						|
			 *     c1 = pos & 07;
 | 
						|
			 * But of course, we don't know that for sure.
 | 
						|
			 */
 | 
						|
			if (pos - c1 < 0) break;
 | 
						|
			pos -= c1;
 | 
						|
			if (reallydispl) {
 | 
						|
				if (expandtabs) {
 | 
						|
					/*
 | 
						|
					 * Expand tabs. We cannot let the
 | 
						|
					 * kernel take care of this
 | 
						|
					 * for two reasons:
 | 
						|
					 * 1. There can be tabs in cursor
 | 
						|
					 *    addressing strings,
 | 
						|
					 * 2. We probably do it better.
 | 
						|
					 */
 | 
						|
					while (c1-- > 0) {
 | 
						|
						*p++ = ' ';
 | 
						|
					}
 | 
						|
				}
 | 
						|
				else {
 | 
						|
					*p = 0;
 | 
						|
					cputline(p = buf);
 | 
						|
					givetab();
 | 
						|
				}
 | 
						|
			}
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
		/*
 | 
						|
		 * Now we have a control character, which takes two positions
 | 
						|
		 */
 | 
						|
		if (pos <= 1) {
 | 
						|
			p--;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		pos -= 2;
 | 
						|
	}
 | 
						|
	if (reallydispl) {
 | 
						|
		*p = 0;
 | 
						|
		cputline(buf);
 | 
						|
		if (pos > 0 || (pos <= 0 && (!AM || XN))) {
 | 
						|
			putline("\r\n");
 | 
						|
		}
 | 
						|
		/*
 | 
						|
		 * The next should be here! I.e. it may not be before printing
 | 
						|
		 * the newline. This has to do with XN. We don't know exactly
 | 
						|
		 * WHEN the terminal will stop ignoring the newline.
 | 
						|
		 * I have for example a terminal (Ampex a230) that will
 | 
						|
		 * continue to ignore the newline after a clear to end of line
 | 
						|
		 * sequence, but not after an end_underline sequence.
 | 
						|
		 */
 | 
						|
		if (do_ul) {
 | 
						|
			end_underline();
 | 
						|
		}
 | 
						|
		if (do_hl) {
 | 
						|
			standend();
 | 
						|
		}
 | 
						|
	}
 | 
						|
	scr_info.currentpos++;
 | 
						|
	return str;
 | 
						|
}
 | 
						|
 | 
						|
/* ARGSUSED */
 | 
						|
int
 | 
						|
setmark(cnt) long cnt; {	/* Set a mark on the current page */
 | 
						|
	register struct scr_info *p = &scr_info;
 | 
						|
 | 
						|
	p->savfirst = p->firstline;
 | 
						|
	p->savnf = p->nf;
 | 
						|
}
 | 
						|
 | 
						|
/* ARGSUSED */
 | 
						|
int
 | 
						|
tomark(cnt) long cnt; {		/* Go to the mark */
 | 
						|
	register struct scr_info *p = &scr_info;
 | 
						|
 | 
						|
	(VOID) display(p->savfirst,p->savnf,pagesize,1);
 | 
						|
}
 | 
						|
 | 
						|
/* ARGSUSED */
 | 
						|
int
 | 
						|
exgmark(cnt) long cnt; {	/* Exchange mark and current page */
 | 
						|
	register struct scr_info *p = &scr_info;
 | 
						|
	register long svfirst;
 | 
						|
	register int svnf;
 | 
						|
 | 
						|
	svfirst = p->firstline;
 | 
						|
	svnf = p->nf;
 | 
						|
	tomark(0L);
 | 
						|
	p->savfirst = svfirst;
 | 
						|
	p->savnf = svnf;
 | 
						|
}
 | 
						|
 | 
						|
VOID
 | 
						|
d_clean() {			/* Clean up */
 | 
						|
	register struct scr_info *p = &scr_info;
 | 
						|
 | 
						|
	p->savnf = 0;
 | 
						|
	p->savfirst = 0;
 | 
						|
	p->head = p->tail;
 | 
						|
	p->head->line = 0;
 | 
						|
	p->currentpos = 0;
 | 
						|
}
 |