DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T s

⟦b75a185d3⟧ TextFile

    Length: 17208 (0x4338)
    Types: TextFile
    Names: »scrn.c«

Derivation

└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
    └─⟦this⟧ »EUUGD18/General/Cchess/scrn.c« 

TextFile

/* CCHESS SCREEN ROUTINES.					Version 1.00
 *
 *   This is mostly low level i/o stuff to interface to the TERMCAP library.
 *   In case you are planning to steal this code for another application, be
 *   careful of wputchar().  It has a lot of scrolling features custom made
 *   for cchess's screen handling style (i.e. the top of the screen held
 *   fixed, and the bottom half scrolled.
 *
 *   (C) Copyright Jan Wolter - Apr 1986.
 */

#include "cchess.h"

/* Stuff for the termcap library */

#ifndef NOTERMCAP
extern short ospeed;		/* The output baud rate */
static char PC= '\000';		/* The pad characters (default ^@) */
static boolean AM;		/* Does terminal have automatic margins? */
static boolean BW;		/* Does terminal wrap on backspaces? */
static char area[1024];		/* Memory to store the following: */
static char *BC;		/* The backspace string (default `\b`) */
       char *BL;		/* The bell character */
static char *CD;		/* The clear to bottom of screen string */
       char *CE;		/* The clear to end of line string */
static char *CL;		/* The clear screen string */
static char *CM;		/* The cursor move string */
static char *CR;		/* The carriage return string */
       char *DL;		/* The delete line string */
static char *DO;		/* The cursor down string */
static char *HO;		/* The home cursor string */
static char *ND;		/* The cursor right string */
static char *SE;		/* The end stand out mode string */
static char *SO;		/* The start stand out mode string */
static char *TE;		/* Terminal uninitialization string */
static char *TI;		/* Terminal initialization string */
static char *UP;		/* The cursor up string */
extern char *tgetstr(), *tgoto(), *getenv();
extern char *malloc();
#endif NOTERMCAP


/* READTERM()
 *
 *   Read in terminal capabilities description (termcap).  Terminal type is
 *   obtained from $TERM environment variable.  All sorts of funny stuff is
 *   set up here.
 */

readterm()
{
#ifndef NOTERMCAP
register char *bp, *pad;
char *tp;
#endif NOTERMCAP

	/* Innocent until proven guilty */
	issmart = FALSE;
	erases = TRUE;
	cleardown = FALSE;

#ifndef NOTERMCAP
	/* Get the termcap entry */
	bp = (char *) malloc(1024);
	if (tgetent(bp,getenv("TERM")) != 1)
	{
		/* No termcap: */
		/* assume you can backspace with ^H, return with ^M */
		free(bp);
		area[0] = '\b'; area[1] = '\0'; area[2] = '\r'; area[3] = '\0';
		area[4] = '\007'; area[5] = '\0';
		BC = area;		/* Backspace with ^H */
		CL = area+1;		/* Clear is null string */
		CR = area+2;		/* Carraige return with ^M */
		BL = area+4;		/* Ring with ^G */
		BW = TRUE;		/* You wrap on left side */
		AM = TRUE;		/* You wrap on right side */
		return;
	}

	/* Load the pad character for tputs.  Default to null. */
	tp = area;
	if(pad = tgetstr("pc",&tp))
		PC = *pad;

	/* Get the clear screen string.  Null string if undefined. */
	if (!(CL = tgetstr("cl",&tp)))
	{
		CL = tp;
		*tp++ = '\000';
	}

	/* Don't absolutely need these, but they may prove useful */
	SO = tgetstr("so",&tp);		/* Start standout mode */
	SE = tgetstr("se",&tp);		/* End standout mode */
	AM = tgetflag("am");		/* Automatic margins? */
	BW = tgetflag("bw");		/* Backspace wraps? */
	if(!SO || !SE) SO = SE = 0;

	/* Get number of lines and columns. */
#ifdef U43BSD
	getwindow();
	if (COLS == 0)
	{
#endif U43BSD
	    LINES = tgetnum("li");
	    if (LINES < 20) LINES = 0;  /* Treat short screens like teletypes */
	    COLS = tgetnum("co");
	    if (COLS < 0) COLS = 40;  /* Default to 40 if not defined. */
#ifdef U43BSD
	}
#endif U43BSD
	if (COLS < 22)
	{
		printf("Cchess requires at least a 22 column terminal.\n");
		exit(1);
	}
	/* Believe it or not, there are 22 column terminals in the world. */
	/* Do some special stuff to make them work.  24 and above is fine */
	commodore = (COLS < 24);

	/* Get the backspace string */
	if (tgetflag("bs"))
	{
		BC = tp;
		*tp++ = '\b';
		*tp++ = '\000';
	}
	else if (!(BC = tgetstr("bc",&tp)))
		BC = tgetstr("le",&tp);

	/* Get the bell string, prefering visual bell if it is there */
	if (!(BL = tgetstr("vb",&tp)) && !(BL = tgetstr("bl",&tp)))
	{
		BL = tp;
		*tp++ = '\007';
		*tp++ = '\000';
	}

	/* Get the carriage return string */
	if (tgetflag("nc"))
		CR = (char *) 0;
	else if (!(CR = tgetstr("cr",&tp)))
	{
		CR = tp;
		*tp++ = '\r';
		*tp++ = '\000';
	}

	/* Does this critter backspace in a useful way? */
	erases = BC && !tgetflag("os") && !tgetflag("hc");

	/* If it's not obviously brain-damaged, see if it is smart */
	if (LINES != 0 && erases && *CL != 0)
	{
		/* Get the strings we need to be smart */
		CM = tgetstr("cm",&tp);		/* Cursor Addressing */
		HO = tgetstr("ho",&tp);		/* Home Cursor */
		DO = tgetstr("do",&tp);		/* Cursor Down */
		ND = tgetstr("nd",&tp);		/* Cursor Right */
		UP = tgetstr("up",&tp);		/* Move cursor up */
		/* Are we smart? */
		if (issmart = (CM || ((HO || UP) && DO && ND)))
		{
			/* Get strings we can use if we are smart */
			CD = tgetstr("cd",&tp);	   /* Clear rest of screen */
			CE = tgetstr("ce",&tp);	   /* Clear rest of line */
			cleardown = CD || CE;
			DL = tgetstr("dl",&tp);	   /* Delete line */
			TI = tgetstr("ti",&tp);	   /* Initialize */
			TE = tgetstr("te",&tp);	   /* Uninitialize */
		}
	}

	/* Check if termcap buffer overflowed.  Free memory */
	if (tp-area > sizeof(area))
	{
		printf("Panic: Termcap too big.  %d bytes.\n",tp-area);
		exit(1);
	}
	free(bp);
	term_init();
#endif NOTERMCAP
}

/* TERM_INIT(), TERM_EXIT()
 *
 * Put the terminal in to or out of visual mode.
 */

term_init()
{
#ifndef NOTERMCAP
	if (TI) xputs(TI);
#endif NOTERMCAP
}

term_exit()
{
#ifndef NOTERMCAP
	if (TE) xputs(TE);
#endif NOTERMCAP
}


#ifndef NOTERMCAP
/* XPUTC()
 *
 *   putchar() is a macro.  We need a real subroutine to use with tputs.
 *   This is it.
 */

int xputc(ch)
char ch;
{
	putchar(ch);
}

/* XPUTS()
 *
 *   send out a control sequence.
 */
xputs(s)
char *s;
{
	(void) tputs(s, 1, xputc);
}

/* NXPUTS()
 *
 *   Put out the same control sequence <s> n times.
 */

nxputs(s,n)
char *s;
REGISTER int n;
{
	for ( ; n > 0; n--)
		(void) tputs(s, 1, xputc);
}

/* CMMOVE()
 *
 * Use the "cm" string to do direct cursor motion.  Don't call this.  Use
 * calls to the higher level cursor() routine instead.
 */

cmmove(x,y)
REGISTER int x,y;
{
	xputs(tgoto(CM,x-1,y-1));
	cx = x;
	cy = y;
}

/* CURSOR()
 *   Move the cursor to line <y> and column <x>.  If the current positions are
 *   negative, the cursor position is unknown, and an absolute move is made to
 *   locate them.  Otherwise, whichever of the availble capabilities gives the
 *   fastest move are used (well, almost).  If the move cannot be made, this
 *   routine returns FALSE.  If you have CM or (HO and DO and ND) all moves
 *   should be possible.  If the current position is always known, UP will
 *   substitute for HO.  A boolean is returned to indicate the success or
 *   failure of the move.
 */

cursor(x,y)
REGISTER int x,y;
{
	/* If cursor position is unknown, put it in a known position */
	if (cy < 0)
	{
		if (x < 3 && y < 3 && HO && DO && ND)
			xputs(HO);
		else if (CM)
		{
			cmmove(x,y);
			return (TRUE);
		}
		else if (HO && DO && ND)
			xputs(HO);
		else
			return(FALSE);
	}
	else if (cx < 0)
	{
		if (x < 3 && CR && ND)
			xputs(CR);
		else if (CM)
		{
			cmmove(x,y);
			return (TRUE);
		}
		else if (CR && ND)
			xputs(CR);
		else
			return (FALSE);
		cx = 1;
	}

	/* Do the vertical movement */
	if (vcursor(x,y))
	{
		/* Do the horizontal movement */

		/* If we are already where we want, do nothing */
		if (cx == x) return(TRUE);

		/* Move cursor left */
		if (cx > x)
		{
			if ((!CM || cx - x < 3) && BC)
			{
				nxputs(BC,cx - x);
				cx = x;
				return(TRUE);
			}
			if ((x < 3 || !CM) && ND && CR)
			{
				xputs(CR);
				cx = 1;
			}
			else if (CM)
			{
				cmmove(x,y);
				return(TRUE);
			}
			else
				return(FALSE);
		}
		
		/* Move cursor right */

		if (cx < x)
		{
			if (ND && (x - cx < 3 || !CM))
			{
				nxputs(ND,x - cx);
				cx = x;
				return(TRUE);
			}
			if (CM)
			{
				cmmove(x,y);
				return(TRUE);
			}
			return(FALSE);
		}
		return(TRUE);
	}
	else
		return(FALSE);
}

/* VCURSOR()
 *
 *   Move the cursor into line <y>.  This routine trys it's best to make the
 *   best use of capabilities available, and to come up with reasonably
 *   efficient moves.  It ain't perfect.  Beware of convoluted logic.
 */

vcursor(x,y)
REGISTER int x,y;
{
	/* If already in the goal line, return */
	if (cy == y) return(TRUE);

	/* Move upwards */
	if (y < cy)
	{
		if (UP && cy - y < 3 && abs(cx - x) < 3)
		{
			nxputs(UP,cy - y);
			cy = y;
			return(TRUE);
		}
		if (HO && ((x<3 && y<3) || (!CM && !UP)))
			xputs(HO);
		else if (CM)
		{
			cmmove(x,y);
			return (TRUE);
		}
		else if (UP)
		{
			nxputs(UP,cy - y);
			cy = y;
			return(TRUE);
		}
		else
			return (FALSE);
	}
	
	/* Move downwards */

	if (y > cy)
	{
		if (DO && ((y - cy < 3 && abs(x-cx) < 3) || !CM))
		{
			nxputs(DO,y - cy);
			cy = y;
			return(TRUE);
		}
		if (CM)
		{
			cmmove(x,y);
			return(TRUE);
		}
		return (FALSE);
	}
	return(TRUE);
}


/* CLINE()
 *
 *   Clear to the end of the line.  Leave the cursor where it was.  Should
 *   only be called if the CE string is defined, or the terminal erases.
 */

cline()
{
short ox;
	if (CE)
		xputs(CE);	/* Use the clear to end of line cmd */
	else if (erases)
	{
		ox = cx;	/* Use overprinted spaces */
		for ( ; cx < COLS; cx++)
			putchar(' ');
		cursor(ox,cy);
	}
}

/* CLBOT()
 *
 *   Clear to the bottom of the display.  Leave the cursor where it was.  Should
 *   only be called if the CD string is defined, or the terminal is smart and
 *   erases.  If the CE and CD strings are both missing, this can be very slow,
 *   since it will then do the whole thing with spaces.
 */

clbot()
{
register short ox,oy;

	if (CD)
	{
		if (cx == 1)
			xputs(CD);
		else
		{
			/* CD can only be used from the beginning of a line */
			cline();
			if (cy < LINES)
			{
				ox = cx;
				oy = cy;
				wputchar('\n');
				xputs(CD);
				cursor(ox,oy);
			}
		}
	}
	else if (issmart)
	{
		ox = cx;
		oy = cy;
		cline();
		while (cy < LINES)
		{
			wputchar('\n');
			cline();
		}
		cursor(ox,oy);
	}
}


/* DELINE()
 *
 *   This deletes the line <n> from the display.  It should be called only if
 *   the DL string is defined, and the terminal is SMART (ie, can move it's
 *   cursors wherever necessary).  The cursor is returned to the place it came
 *   from afterwards.
 */

deline(n)
short n;
{
short ox=cx,oy=cy;		/* Save the old position */
	vcursor(1,n);		/* Get the cursor into line n */
	xputs(DL);		/* Delete the line */
	if (oy == n) ox = 0;	/* If deleted current line */
	else if (oy > n) oy--;	/* Old position may scroll up */
	cursor(ox,oy);		/* Put the cursor back */
}
#endif NOTERMCAP


/* BEG_SO(), END_SO()
 *
 *   Enter and exit standout mode.
 */

beg_so()
{
#ifndef NOTERMCAP
	if(SO) xputs(SO);
#endif NOTERMCAP
}

end_so()
{
#ifndef NOTERMCAP
	if(SE) xputs(SE);
#endif NOTERMCAP
}


/* INDENT()
 *
 *   This routine indents <n> spaces on the current line.  It works for any
 *   terminal.  It may use spaces.  <n> is assumed to be less than COLS and
 *   BLANK_LEN.  If cx > 1 before entry, the indent will be on the next line,
 *   not this line.
 */

indent(n)
int n;
{
#ifndef NOTERMCAP
	if (n < 3 || !issmart)
	{
#endif
		if (cx > 1) wputchar('\n');
		printf(blanks(n));
		cx = n+1;
#ifndef NOTERMCAP
	}
	else
		cursor(n+1, cy + (cx > 1));
#endif NOTERMCAP
}

/* BACKSPACE()
 *
 *   This sends a backspace, and updates the current cursor position.
 *   It fixes terminals that don't wrap on backspace to do so, if possible.
 */

backspace()
{
#ifndef NOTERMCAP
	if (!BC)
	{
		wputchar('<');	/* Can't backspace.  Make a mark instead. */
		return;
	}
	if (cx-- == 1)
	{
		if (cy > 1)
		{
			/* Wrap back to previous line */
			if (BW)
			{
				xputs(BC);		/* use backspace */
				cx = COLS;
				--cy;
			}
			else if (issmart) 
				cursor(COLS,cy-1);	/* use cursor motion */
			else 
				cx++;	     /* Can't backspace--do nothing */
		}
		else
			cx = 1;		/* At home position.  Do nothing */
	}
	else
		xputs(BC);		/* Ordinary backspacing */
#else
	putchar('\b');
#endif NOTERMCAP
}

bell()
{
#ifndef NOTERMCAP
	xputs(BL);
#else
	putchar('\007');
#endif NOTERMCAP
}


/* CLR()
 *
 *   Clear the screen.  If we have the termcap library, use tputs.
 *   Otherwise, just make a note to redraw next time we need to.
 */

extern short movelen;
clr()
{
#ifndef NOTERMCAP
	tputs(CL,LINES,xputc);
#endif NOTERMCAP
	cx = cy = 1;
	scrolled = FALSE;
	movelen = 0;
}


/* WPUTCHAR()
 *
 *   This routine is like putchar(), but it updates the current cursor position.
 *   It doesn't know about tabs or returns, and should not be sent any.  It
 *   Does handle backspaces and newlines.  The latter are output as carriage-
 *   return-newline combinations.  If the terminal doesn't have automatic
 *   margins, it fakes them.
 */

wputchar(c)
char c;
{
#ifdef NOTERMCAP
	if (c == '\n')
		putchar('\r');
	putchar(c);
#else
	/* If a backspace, do it */
	if (c == '\b')
		backspace();
	/* If newline or auto-newline due to typing past last column */
	else if (c == '\n' || ++cx > COLS)
	{
		if (cy == LINES)	/* this should NOT be ">=" */
		{
			if (msgarea == 0)
			{
				scrolled = TRUE;
				cy--;
			}
			else if (DL)
				deline(msgarea);
			else if (cleardown)
			{
				cursor(1,msgarea);
				clbot();
				return;
			}
			else
			{
				scrolled = TRUE;
				cy--;
			}
		}

		/* If autowrapping, send character first */
		if (c != '\n')
			putchar(c);
		/* Send the newline, unless automatic margins do it for us */
		if (c == '\n' || !AM)
		{
			if (CR)
				xputs(CR);
			else
				cursor(1,cy);
			putchar('\n');	/* Don't want to have DO here */
		}
		cx = 1;
		cy++;
	}
	else
		putchar(c);
#endif NOTERMCAP
}

/* WPRINTF()
 *
 *   This routine is like printf(), but it updates the cursor position, and can
 *   take at most five arguments.  The wprint() routine is the same, but takes
 *   no arguments.
 */

/*VARARGS1*/ wprintf(str,arg1,arg2,arg3,arg4,arg5)
char *str,*arg1,*arg2,*arg3,*arg4,*arg5;
{
char buf[MB_LEN];

	sprintf(buf,str,arg1,arg2,arg3,arg4,arg5);
	wprint(buf);
}

wprint(str)
char *str;
{
	for ( ; *str != 0; str++)
		wputchar(*str);
}

/* MPRINTF()
 *
 * This is like wprintf(), except it plays games kind of like more in
 * the message area.  To use it, first call initmore() to set up the
 * message area for moring.  Space, Newline and 'Q' are accepted at the
 * more prompt and do the usual things.  It erases the more prompts if
 * the terminal has any brains at all.
 */

static int morespace,morecount;
static boolean domore;

initmore()
{
#ifndef NOTERMCAP
	/* Try to make room in the message area */
	if (!DL && cleardown)
	{
		cursor(1,msgarea);
		clbot();
	}
	if (LINES)
		morecount = morespace = LINES - msgarea;
	else
		morecount = -2;		/* Don't "more" on teletypes */
	domore = FALSE;
#endif NOTERMCAP
}

/*VARARGS1*/ mprintf(str,arg1,arg2,arg3,arg4)
char *str,*arg1,*arg2,*arg3,*arg4;
{
char buf[MB_LEN];

	sprintf(buf,str,arg1,arg2,arg3,arg4);
	mprint(buf);
}

mprint(str)
char *str;
{
#ifndef NOTERMCAP
char c,p;

	if (morecount == -1) return;

	while (*str != '\000')
	{
		if (cx == COLS)
			c = '\n';
		else
			c = *str++;

		if (domore)
		{
			beg_so();
			wprint("--More--");
			end_so();
			p = xenter(" Q\n");
			/* Erase More Prompt */
			if (DL && (CE || erases))
			{
				cursor(1,cy);
				cline();
			}
			else
				wputchar('\n');

			domore = FALSE;
			if (p == 'Q')
			{
				/* Ignore mprint calls until next initmore */
				morecount = -1;
				return;
			}
			else if (DL && p == '\n')
				morecount = 1;
			else
				morecount = morespace;
		}

		wputchar(c);

		if (c == '\n' && --morecount == 0)
			domore = TRUE;
	}

#else
	while (*str != '\000')
		wputchar(*str++);
#endif NOTERMCAP
}


/* CPRINTF()
 *
 *   Print string <st> centered on the screen.  Up to five arguments can be used
 *   as in printf.  No newline is added at the end.  If the line is longer than
 *   the screen, is it is broken up as well as possible, and each chunk is
 *   centered.  This routine is weird.  Cprint is simlar, but takes no
 *   arguments.  Oh yes, if the first character is a *, the message is printed
 *   in standout mode.
 */

/*VARARGS1*/ cprintf(str,arg1,arg2,arg3,arg4)
char *str,*arg1,*arg2,*arg3,*arg4;
{
char buf[MB_LEN+10];

	sprintf(buf,str,arg1,arg2,arg3,arg4);
	cprint(buf);
}

cprint(c)
REGISTER char *c;
{
char pop = 0;
boolean standout;
register int n;

	if (standout = (*c == '*')) c++;
		
	for(;;)
	{
		if ((n = strlen(c)) > COLS)
		{
			n = findbreak(c,COLS-1);
			pop = c[n];
			c[n] = '\000';
		}
		indent((int)(COLS-n)/2);

		if (standout) beg_so();
		wprint(c);
		if (standout) end_so();

		if (pop == 0)
			break;
		else
		{
			c[n] = pop;
			c = c + n;
			while (*c != 0 && (*c == ' ' || *c == '\t')) c++;
			pop = 0;
		}
	}
}

/* FINDBREAK()
 *
 *   Find a place for a word break just before column <n> of the string <s>.
 *   If no break is found within 20 characters, column n is returned.
 */

int findbreak(s,n)
char *s;
REGISTER int n;
{
register int i;
char c;
	for (i = 0; i < 20 && i < n ; i++)
	{
		if ((c = s[n-i]) == ' ' || c == '\t')
			return(n-i);
	}
	return(n);
}