xref: /netbsd-src/games/larn/io.c (revision 89c5a767f8fc7a4633b2d409966e2becbb98ff92)
1 /*	$NetBSD: io.c,v 1.8 1999/10/04 23:27:02 lukem Exp $	*/
2 
3 /*
4  * io.c			 Larn is copyrighted 1986 by Noah Morgan.
5  *
6  * Below are the functions in this file:
7  *
8  * setupvt100() 	Subroutine to set up terminal in correct mode for game
9  * clearvt100()  	Subroutine to clean up terminal when the game is over
10  * getchar() 		Routine to read in one character from the terminal
11  * scbr()			Function to set cbreak -echo for the terminal
12  * sncbr()			Function to set -cbreak echo for the terminal
13  * newgame() 		Subroutine to save the initial time and seed rnd()
14  *
15  * FILE OUTPUT ROUTINES
16  *
17  * lprintf(format,args . . .)	printf to the output buffer lprint(integer)
18  * end binary integer to output buffer lwrite(buf,len)
19  * rite a buffer to the output buffer lprcat(str)
20  * ent string to output buffer
21  *
22  * FILE OUTPUT MACROS (in header.h)
23  *
24  * lprc(character)				put the character into the output
25  * buffer
26  *
27  * FILE INPUT ROUTINES
28  *
29  * long lgetc()				read one character from input buffer
30  * long lrint()				read one integer from input buffer
31  * lrfill(address,number)		put input bytes into a buffer char
32  * *lgetw()				get a whitespace ended word from
33  * input char *lgetl()				get a \n or EOF ended line
34  * from input
35  *
36  * FILE OPEN / CLOSE ROUTINES
37  *
38  * lcreat(filename)			create a new file for write
39  * lopen(filename)				open a file for read
40  * lappend(filename)			open for append to an existing file
41  * lrclose()					close the input file
42  * lwclose()					close output file lflush()
43  * lush the output buffer
44  *
45  * Other Routines
46  *
47  * cursor(x,y)					position cursor at [x,y]
48  * cursors()					position cursor at [1,24]
49  * (saves memory) cl_line(x,y)         		Clear line at [1,y] and leave
50  * cursor at [x,y] cl_up(x,y)    				Clear screen
51  * from [x,1] to current line. cl_dn(x,y)
52  * lear screen from [1,y] to end of display. standout(str)
53  * rint the string in standout mode. set_score_output()
54  * alled when output should be literally printed. * xputchar(ch)
55  * rint one character in decoded output buffer. * flush_buf()
56  * lush buffer with decoded output. * init_term()
57  * erminal initialization -- setup termcap info *	char *tmcapcnv(sd,ss)
58  * outine to convert VT100 \33's to termcap format beep()
59  * e to emit a beep if enabled (see no-beep in .larnopts)
60  *
61  * Note: ** entries are available only in termcap mode.
62  */
63 #include <sys/cdefs.h>
64 #ifndef lint
65 __RCSID("$NetBSD: io.c,v 1.8 1999/10/04 23:27:02 lukem Exp $");
66 #endif /* not lint */
67 
68 #include "header.h"
69 #include "extern.h"
70 #include <string.h>
71 #include <unistd.h>
72 #include <stdlib.h>
73 #include <termcap.h>
74 #include <fcntl.h>
75 #include <errno.h>
76 
77 #ifdef TERMIO
78 #include <termio.h>
79 #define sgttyb termio
80 #define stty(_a,_b) ioctl(_a,TCSETA,_b)
81 #define gtty(_a,_b) ioctl(_a,TCGETA,_b)
82 #endif
83 #ifdef TERMIOS
84 #include <termios.h>
85 #define sgttyb termios
86 #define stty(_a,_b) tcsetattr(_a,TCSADRAIN,_b)
87 #define gtty(_a,_b) tcgetattr(_a,_b)
88 #endif
89 
90 #if defined(TERMIO) || defined(TERMIOS)
91 static int      rawflg = 0;
92 static char     saveeof, saveeol;
93 #define doraw(_a) \
94 	if(!rawflg) { \
95 		++rawflg; \
96 		saveeof = _a.c_cc[VMIN]; \
97 		saveeol = _a.c_cc[VTIME]; \
98 	} \
99     	_a.c_cc[VMIN] = 1; \
100 	_a.c_cc[VTIME] = 1; \
101 	_a.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL)
102 #define unraw(_a) \
103 	_a.c_cc[VMIN] = saveeof; \
104 	_a.c_cc[VTIME] = saveeol; \
105 	_a.c_lflag |= ICANON|ECHO|ECHOE|ECHOK|ECHONL
106 
107 #else	/* not TERMIO or TERMIOS */
108 
109 #ifndef BSD
110 #define CBREAK RAW		/* V7 has no CBREAK */
111 #endif
112 
113 #define doraw(_a) (_a.sg_flags |= CBREAK,_a.sg_flags &= ~ECHO)
114 #define unraw(_a) (_a.sg_flags &= ~CBREAK,_a.sg_flags |= ECHO)
115 #include <sgtty.h>
116 #endif	/* not TERMIO or TERMIOS */
117 
118 #ifndef NOVARARGS	/* if we have varargs */
119 #ifdef __STDC__
120 #include <stdarg.h>
121 #else
122 #include <varargs.h>
123 #endif
124 #else	/* NOVARARGS */	/* if we don't have varargs */
125 typedef char   *va_list;
126 #define va_dcl int va_alist;
127 #define va_start(plist) plist = (char *) &va_alist
128 #define va_end(plist)
129 #define va_arg(plist,mode) ((mode *)(plist += sizeof(mode)))[-1]
130 #endif	/* NOVARARGS */
131 
132 #define LINBUFSIZE 128	/* size of the lgetw() and lgetl() buffer */
133 int             lfd;	/* output file numbers */
134 int             fd;	/* input file numbers */
135 static struct sgttyb ttx;/* storage for the tty modes */
136 static int      ipoint = MAXIBUF, iepoint = MAXIBUF;	/* input buffering
137 							 * pointers    */
138 static char     lgetwbuf[LINBUFSIZE];	/* get line (word) buffer */
139 
140 /*
141  *	setupvt100() Subroutine to set up terminal in correct mode for game
142  *
143  *	Attributes off, clear screen, set scrolling region, set tty mode
144  */
145 void
146 setupvt100()
147 {
148 	clear();
149 	setscroll();
150 	scbr();			/* system("stty cbreak -echo"); */
151 }
152 
153 /*
154  *	clearvt100() 	Subroutine to clean up terminal when the game is over
155  *
156  *	Attributes off, clear screen, unset scrolling region, restore tty mode
157  */
158 void
159 clearvt100()
160 {
161 	resetscroll();
162 	clear();
163 	sncbr();		/* system("stty -cbreak echo"); */
164 }
165 
166 /*
167  *	getchar() 	Routine to read in one character from the terminal
168  */
169 int
170 getchar()
171 {
172 	char            byt;
173 #ifdef EXTRA
174 	c[BYTESIN]++;
175 #endif
176 	lflush();		/* be sure output buffer is flushed */
177 	read(0, &byt, 1);	/* get byte from terminal */
178 	return (byt);
179 }
180 
181 /*
182  *	scbr()		Function to set cbreak -echo for the terminal
183  *
184  *	like: system("stty cbreak -echo")
185  */
186 void
187 scbr()
188 {
189 	gtty(0, &ttx);
190 	doraw(ttx);
191 	stty(0, &ttx);
192 }
193 
194 /*
195  *	sncbr()		Function to set -cbreak echo for the terminal
196  *
197  *	like: system("stty -cbreak echo")
198  */
199 void
200 sncbr()
201 {
202 	gtty(0, &ttx);
203 	unraw(ttx);
204 	stty(0, &ttx);
205 }
206 
207 /*
208  *	newgame() 	Subroutine to save the initial time and seed rnd()
209  */
210 void
211 newgame()
212 {
213 	long  *p, *pe;
214 	for (p = c, pe = c + 100; p < pe; *p++ = 0);
215 	time(&initialtime);
216 	srand(initialtime);
217 	lcreat((char *) 0);	/* open buffering for output to terminal */
218 }
219 
220 /*
221  *	lprintf(format,args . . .)		printf to the output buffer
222  *		char *format;
223  *		??? args . . .
224  *
225  *	Enter with the format string in "format", as per printf() usage
226  *		and any needed arguments following it
227  *	Note: lprintf() only supports %s, %c and %d, with width modifier and left
228  *		or right justification.
229  *	No correct checking for output buffer overflow is done, but flushes
230  *		are done beforehand if needed.
231  *	Returns nothing of value.
232  */
233 #ifdef lint
234 /* VARARGS */
235 lprintf(str)
236 	char           *str;
237 {
238 	char           *str2;
239 	str2 = str;
240 	str = str2;		/* to make lint happy */
241 }
242 /* VARARGS */
243 sprintf(str)
244 	char           *str;
245 {
246 	char           *str2;
247 	str2 = str;
248 	str = str2;		/* to make lint happy */
249 }
250 #else	/* lint */
251 /* VARARGS */
252 #ifdef __STDC__
253 void lprintf(const char *fmt, ...)
254 #else
255 void
256 lprintf(va_alist)
257 va_dcl
258 #endif
259 {
260 	va_list         ap;	/* pointer for variable argument list */
261 	char  *outb, *tmpb;
262 	long   wide, left, cont, n;	/* data for lprintf	 */
263 	char            db[12];	/* %d buffer in lprintf	 */
264 #ifndef __STDC__
265 	char  *fmt;
266 
267 	va_start(ap);		/* initialize the var args pointer */
268 	fmt = va_arg(ap, char *);	/* pointer to format string */
269 #else
270 	va_start(ap, fmt);
271 #endif
272 	if (lpnt >= lpend)
273 		lflush();
274 	outb = lpnt;
275 	for (;;) {
276 		while (*fmt != '%')
277 			if (*fmt)
278 				*outb++ = *fmt++;
279 			else {
280 				lpnt = outb;
281 				return;
282 			}
283 		wide = 0;
284 		left = 1;
285 		cont = 1;
286 		while (cont)
287 			switch (*(++fmt)) {
288 			case 'd':
289 				n = va_arg(ap, long);
290 				if (n < 0) {
291 					n = -n;
292 					*outb++ = '-';
293 					if (wide)
294 						--wide;
295 				}
296 				tmpb = db + 11;
297 				*tmpb = (char) (n % 10 + '0');
298 				while (n > 9)
299 					*(--tmpb) = (char) ((n /= 10) % 10 + '0');
300 				if (wide == 0)
301 					while (tmpb < db + 12)
302 						*outb++ = *tmpb++;
303 				else {
304 					wide -= db - tmpb + 12;
305 					if (left)
306 						while (wide-- > 0)
307 							*outb++ = ' ';
308 					while (tmpb < db + 12)
309 						*outb++ = *tmpb++;
310 					if (left == 0)
311 						while (wide-- > 0)
312 							*outb++ = ' ';
313 				}
314 				cont = 0;
315 				break;
316 
317 			case 's':
318 				tmpb = va_arg(ap, char *);
319 				if (wide == 0) {
320 					while ((*outb++ = *tmpb++) != '\0')
321 						continue;
322 					--outb;
323 				} else {
324 					n = wide - strlen(tmpb);
325 					if (left)
326 						while (n-- > 0)
327 							*outb++ = ' ';
328 					while ((*outb++ = *tmpb++) != '\0')
329 						continue;
330 					--outb;
331 					if (left == 0)
332 						while (n-- > 0)
333 							*outb++ = ' ';
334 				}
335 				cont = 0;
336 				break;
337 
338 			case 'c':
339 				*outb++ = va_arg(ap, int);
340 				cont = 0;
341 				break;
342 
343 			case '0':
344 			case '1':
345 			case '2':
346 			case '3':
347 			case '4':
348 			case '5':
349 			case '6':
350 			case '7':
351 			case '8':
352 			case '9':
353 				wide = 10 * wide + *fmt - '0';
354 				break;
355 
356 			case '-':
357 				left = 0;
358 				break;
359 
360 			default:
361 				*outb++ = *fmt;
362 				cont = 0;
363 				break;
364 			};
365 		fmt++;
366 	}
367 	va_end(ap);
368 }
369 #endif	/* lint */
370 
371 /*
372  *	lprint(long-integer)	send binary integer to output buffer
373  *		long integer;
374  *
375  *		+---------+---------+---------+---------+
376  *		|   high  |	    |	      |	  low	|
377  *		|  order  |	    |	      |  order	|
378  *		|   byte  |	    |	      |	  byte	|
379  *		+---------+---------+---------+---------+
380  *	        31  ---  24 23 --- 16 15 ---  8 7  ---   0
381  *
382  *	The save order is low order first, to high order (4 bytes total)
383  *	and is written to be system independent.
384  *	No checking for output buffer overflow is done, but flushes if needed!
385  *	Returns nothing of value.
386  */
387 void
388 lprint(x)
389 	long   x;
390 {
391 	if (lpnt >= lpend)
392 		lflush();
393 	*lpnt++ = 255 & x;
394 	*lpnt++ = 255 & (x >> 8);
395 	*lpnt++ = 255 & (x >> 16);
396 	*lpnt++ = 255 & (x >> 24);
397 }
398 
399 /*
400  *	lwrite(buf,len)		write a buffer to the output buffer
401  *		char *buf;
402  *		int len;
403  *
404  *	Enter with the address and number of bytes to write out
405  *	Returns nothing of value
406  */
407 void
408 lwrite(buf, len)
409 	char  *buf;
410 	int             len;
411 {
412 	char  *str;
413 	int    num2;
414 	if (len > 399) {	/* don't copy data if can just write it */
415 #ifdef EXTRA
416 		c[BYTESOUT] += len;
417 #endif
418 
419 #ifndef VT100
420 		for (str = buf; len > 0; --len)
421 			lprc(*str++);
422 #else	/* VT100 */
423 		lflush();
424 		write(lfd, buf, len);
425 #endif	/* VT100 */
426 	} else
427 		while (len) {
428 			if (lpnt >= lpend)
429 				lflush();	/* if buffer is full flush it	 */
430 			num2 = lpbuf + BUFBIG - lpnt;	/* # bytes left in
431 							 * output buffer	 */
432 			if (num2 > len)
433 				num2 = len;
434 			str = lpnt;
435 			len -= num2;
436 			while (num2--)
437 				*str++ = *buf++;	/* copy in the bytes */
438 			lpnt = str;
439 		}
440 }
441 
442 /*
443  *	long lgetc()	Read one character from input buffer
444  *
445  *  Returns 0 if EOF, otherwise the character
446  */
447 long
448 lgetc()
449 {
450 	int    i;
451 	if (ipoint != iepoint)
452 		return (inbuffer[ipoint++]);
453 	if (iepoint != MAXIBUF)
454 		return (0);
455 	if ((i = read(fd, inbuffer, MAXIBUF)) <= 0) {
456 		if (i != 0)
457 			write(1, "error reading from input file\n", 30);
458 		iepoint = ipoint = 0;
459 		return (0);
460 	}
461 	ipoint = 1;
462 	iepoint = i;
463 	return (*inbuffer);
464 }
465 
466 /*
467  *	long lrint()	Read one integer from input buffer
468  *
469  *		+---------+---------+---------+---------+
470  *		|   high  |	    |	      |	  low	|
471  *		|  order  |	    |	      |  order	|
472  *		|   byte  |	    |	      |	  byte	|
473  *		+---------+---------+---------+---------+
474  *	       31  ---  24 23 --- 16 15 ---  8 7  ---   0
475  *
476  *	The save order is low order first, to high order (4 bytes total)
477  *	Returns the int read
478  */
479 long
480 lrint()
481 {
482 	unsigned long i;
483 	i = 255 & lgetc();
484 	i |= (255 & lgetc()) << 8;
485 	i |= (255 & lgetc()) << 16;
486 	i |= (255 & lgetc()) << 24;
487 	return (i);
488 }
489 
490 /*
491  *	lrfill(address,number)		put input bytes into a buffer
492  *		char *address;
493  *		int number;
494  *
495  *	Reads "number" bytes into the buffer pointed to by "address".
496  *	Returns nothing of value
497  */
498 void
499 lrfill(adr, num)
500 	char  *adr;
501 	int             num;
502 {
503 	char  *pnt;
504 	int    num2;
505 	while (num) {
506 		if (iepoint == ipoint) {
507 			if (num > 5) {	/* fast way */
508 				if (read(fd, adr, num) != num)
509 					write(2, "error reading from input file\n", 30);
510 				num = 0;
511 			} else {
512 				*adr++ = lgetc();
513 				--num;
514 			}
515 		} else {
516 			num2 = iepoint - ipoint;	/* # of bytes left in
517 							 * the buffer	 */
518 			if (num2 > num)
519 				num2 = num;
520 			pnt = inbuffer + ipoint;
521 			num -= num2;
522 			ipoint += num2;
523 			while (num2--)
524 				*adr++ = *pnt++;
525 		}
526 	}
527 }
528 
529 /*
530  *	char *lgetw()			Get a whitespace ended word from input
531  *
532  *	Returns pointer to a buffer that contains word.  If EOF, returns a NULL
533  */
534 char *
535 lgetw()
536 {
537 	char  *lgp, cc;
538 	int    n = LINBUFSIZE, quote = 0;
539 	lgp = lgetwbuf;
540 	do
541 		cc = lgetc();
542 	while ((cc <= 32) && (cc > '\0'));	/* eat whitespace */
543 	for (;; --n, cc = lgetc()) {
544 		if ((cc == '\0') && (lgp == lgetwbuf))
545 			return (NULL);	/* EOF */
546 		if ((n <= 1) || ((cc <= 32) && (quote == 0))) {
547 			*lgp = '\0';
548 			return (lgetwbuf);
549 		}
550 		if (cc != '"')
551 			*lgp++ = cc;
552 		else
553 			quote ^= 1;
554 	}
555 }
556 
557 /*
558  *	char *lgetl()	Function to read in a line ended by newline or EOF
559  *
560  * Returns pointer to a buffer that contains the line.  If EOF, returns NULL
561  */
562 char *
563 lgetl()
564 {
565 	int    i = LINBUFSIZE, ch;
566 	char  *str = lgetwbuf;
567 	for (;; --i) {
568 		if ((*str++ = ch = lgetc()) == '\0') {
569 			if (str == lgetwbuf + 1)
570 				return (NULL);	/* EOF */
571 	ot:		*str = '\0';
572 			return (lgetwbuf);	/* line ended by EOF */
573 		}
574 		if ((ch == '\n') || (i <= 1))
575 			goto ot;/* line ended by \n */
576 	}
577 }
578 
579 /*
580  *	lcreat(filename)			Create a new file for write
581  *		char *filename;
582  *
583  *	lcreat((char*)0); means to the terminal
584  *	Returns -1 if error, otherwise the file descriptor opened.
585  */
586 int
587 lcreat(str)
588 	char *str;
589 {
590 	lpnt = lpbuf;
591 	lpend = lpbuf + BUFBIG;
592 	if (str == NULL)
593 		return (lfd = 1);
594 	if ((lfd = creat(str, 0644)) < 0) {
595 		lfd = 1;
596 		lprintf("error creating file <%s>: %s\n", str,
597 			strerror(errno));
598 		lflush();
599 		return (-1);
600 	}
601 	return (lfd);
602 }
603 
604 /*
605  *	lopen(filename)			Open a file for read
606  *		char *filename;
607  *
608  *	lopen(0) means from the terminal
609  *	Returns -1 if error, otherwise the file descriptor opened.
610  */
611 int
612 lopen(str)
613 	char           *str;
614 {
615 	ipoint = iepoint = MAXIBUF;
616 	if (str == NULL)
617 		return (fd = 0);
618 	if ((fd = open(str, 0)) < 0) {
619 		lwclose();
620 		lfd = 1;
621 		lpnt = lpbuf;
622 		return (-1);
623 	}
624 	return (fd);
625 }
626 
627 /*
628  *	lappend(filename)		Open for append to an existing file
629  *		char *filename;
630  *
631  *	lappend(0) means to the terminal
632  *	Returns -1 if error, otherwise the file descriptor opened.
633  */
634 int
635 lappend(str)
636 	char           *str;
637 {
638 	lpnt = lpbuf;
639 	lpend = lpbuf + BUFBIG;
640 	if (str == NULL)
641 		return (lfd = 1);
642 	if ((lfd = open(str, 2)) < 0) {
643 		lfd = 1;
644 		return (-1);
645 	}
646 	lseek(lfd, 0, 2);	/* seek to end of file */
647 	return (lfd);
648 }
649 
650 /*
651  *	lrclose() close the input file
652  *
653  *	Returns nothing of value.
654  */
655 void
656 lrclose()
657 {
658 	if (fd > 0)
659 		close(fd);
660 }
661 
662 /*
663  *	lwclose() close output file flushing if needed
664  *
665  *	Returns nothing of value.
666  */
667 void
668 lwclose()
669 {
670 	lflush();
671 	if (lfd > 2)
672 		close(lfd);
673 }
674 
675 /*
676  *	lprcat(string)	append a string to the output buffer
677  *			    	avoids calls to lprintf (time consuming)
678  */
679 void
680 lprcat(str)
681 	char  *str;
682 {
683 	char  *str2;
684 	if (lpnt >= lpend)
685 		lflush();
686 	str2 = lpnt;
687 	while ((*str2++ = *str++) != '\0')
688 		continue;
689 	lpnt = str2 - 1;
690 }
691 
692 #ifdef VT100
693 /*
694  *	cursor(x,y) 		Subroutine to set the cursor position
695  *
696  *	x and y are the cursor coordinates, and lpbuff is the output buffer where
697  *	escape sequence will be placed.
698  */
699 static char    *y_num[] = {
700 "\33[", "\33[", "\33[2", "\33[3", "\33[4", "\33[5", "\33[6",
701 "\33[7", "\33[8", "\33[9", "\33[10", "\33[11", "\33[12", "\33[13", "\33[14",
702 "\33[15", "\33[16", "\33[17", "\33[18", "\33[19", "\33[20", "\33[21", "\33[22",
703 "\33[23", "\33[24"};
704 
705 static char    *x_num[] = {
706 "H", "H", ";2H", ";3H", ";4H", ";5H", ";6H", ";7H", ";8H", ";9H",
707 ";10H", ";11H", ";12H", ";13H", ";14H", ";15H", ";16H", ";17H", ";18H", ";19H",
708 ";20H", ";21H", ";22H", ";23H", ";24H", ";25H", ";26H", ";27H", ";28H", ";29H",
709 ";30H", ";31H", ";32H", ";33H", ";34H", ";35H", ";36H", ";37H", ";38H", ";39H",
710 ";40H", ";41H", ";42H", ";43H", ";44H", ";45H", ";46H", ";47H", ";48H", ";49H",
711 ";50H", ";51H", ";52H", ";53H", ";54H", ";55H", ";56H", ";57H", ";58H", ";59H",
712 ";60H", ";61H", ";62H", ";63H", ";64H", ";65H", ";66H", ";67H", ";68H", ";69H",
713 ";70H", ";71H", ";72H", ";73H", ";74H", ";75H", ";76H", ";77H", ";78H", ";79H",
714 ";80H"};
715 
716 void
717 cursor(x, y)
718 	int             x, y;
719 {
720 	char  *p;
721 	if (lpnt >= lpend)
722 		lflush();
723 
724 	p = y_num[y];		/* get the string to print */
725 	while (*p)
726 		*lpnt++ = *p++;	/* print the string */
727 
728 	p = x_num[x];		/* get the string to print */
729 	while (*p)
730 		*lpnt++ = *p++;	/* print the string */
731 }
732 #else	/* VT100 */
733 /*
734  * cursor(x,y)	  Put cursor at specified coordinates staring at [1,1] (termcap)
735  */
736 void
737 cursor(x, y)
738 	int             x, y;
739 {
740 	if (lpnt >= lpend)
741 		lflush();
742 
743 	*lpnt++ = CURSOR;
744 	*lpnt++ = x;
745 	*lpnt++ = y;
746 }
747 #endif	/* VT100 */
748 
749 /*
750  *	Routine to position cursor at beginning of 24th line
751  */
752 void
753 cursors()
754 {
755 	cursor(1, 24);
756 }
757 
758 #ifndef VT100
759 /*
760  * Warning: ringing the bell is control code 7. Don't use in defines.
761  * Don't change the order of these defines.
762  * Also used in helpfiles. Codes used in helpfiles should be \E[1 to \E[7 with
763  * obvious meanings.
764  */
765 
766 static char     cap[256];
767 char           *CM, *CE, *CD, *CL, *SO, *SE, *AL, *DL;	/* Termcap capabilities */
768 static char    *outbuf = 0;	/* translated output buffer */
769 
770 /*
771  * init_term()		Terminal initialization -- setup termcap info
772  */
773 void
774 init_term()
775 {
776 	char            termbuf[1024];
777 	char           *capptr = cap + 10;
778 	char           *term;
779 
780 	switch (tgetent(termbuf, term = getenv("TERM"))) {
781 	case -1:
782 		write(2, "Cannot open termcap file.\n", 26);
783 		exit(1);
784 	case 0:
785 		write(2, "Cannot find entry of ", 21);
786 		write(2, term, strlen(term));
787 		write(2, " in termcap\n", 12);
788 		exit(1);
789 	};
790 
791 	CM = tgetstr("cm", &capptr);	/* Cursor motion */
792 	CE = tgetstr("ce", &capptr);	/* Clear to eoln */
793 	CL = tgetstr("cl", &capptr);	/* Clear screen */
794 
795 	/* OPTIONAL */
796 	AL = tgetstr("al", &capptr);	/* Insert line */
797 	DL = tgetstr("dl", &capptr);	/* Delete line */
798 	SO = tgetstr("so", &capptr);	/* Begin standout mode */
799 	SE = tgetstr("se", &capptr);	/* End standout mode */
800 	CD = tgetstr("cd", &capptr);	/* Clear to end of display */
801 
802 	if (!CM) {		/* can't find cursor motion entry */
803 		write(2, "Sorry, for a ", 13);
804 		write(2, term, strlen(term));
805 		write(2, ", I can't find the cursor motion entry in termcap\n", 50);
806 		exit(1);
807 	}
808 	if (!CE) {		/* can't find clear to end of line entry */
809 		write(2, "Sorry, for a ", 13);
810 		write(2, term, strlen(term));
811 		write(2, ", I can't find the clear to end of line entry in termcap\n", 57);
812 		exit(1);
813 	}
814 	if (!CL) {		/* can't find clear entire screen entry */
815 		write(2, "Sorry, for a ", 13);
816 		write(2, term, strlen(term));
817 		write(2, ", I can't find the clear entire screen entry in termcap\n", 56);
818 		exit(1);
819 	}
820 	if ((outbuf = malloc(BUFBIG + 16)) == 0) {	/* get memory for
821 							 * decoded output buffer */
822 		write(2, "Error malloc'ing memory for decoded output buffer\n", 50);
823 		died(-285);	/* malloc() failure */
824 	}
825 }
826 #endif	/* VT100 */
827 
828 /*
829  * cl_line(x,y)  Clear the whole line indicated by 'y' and leave cursor at [x,y]
830  */
831 void
832 cl_line(x, y)
833 	int             x, y;
834 {
835 #ifdef VT100
836 	cursor(x, y);
837 	lprcat("\33[2K");
838 #else	/* VT100 */
839 	cursor(1, y);
840 	*lpnt++ = CL_LINE;
841 	cursor(x, y);
842 #endif	/* VT100 */
843 }
844 
845 /*
846  * cl_up(x,y) Clear screen from [x,1] to current position. Leave cursor at [x,y]
847  */
848 void
849 cl_up(x, y)
850 	int    x, y;
851 {
852 #ifdef VT100
853 	cursor(x, y);
854 	lprcat("\33[1J\33[2K");
855 #else	/* VT100 */
856 	int    i;
857 	cursor(1, 1);
858 	for (i = 1; i <= y; i++) {
859 		*lpnt++ = CL_LINE;
860 		*lpnt++ = '\n';
861 	}
862 	cursor(x, y);
863 #endif	/* VT100 */
864 }
865 
866 /*
867  * cl_dn(x,y) 	Clear screen from [1,y] to end of display. Leave cursor at [x,y]
868  */
869 void
870 cl_dn(x, y)
871 	int    x, y;
872 {
873 #ifdef VT100
874 	cursor(x, y);
875 	lprcat("\33[J\33[2K");
876 #else	/* VT100 */
877 	int    i;
878 	cursor(1, y);
879 	if (!CD) {
880 		*lpnt++ = CL_LINE;
881 		for (i = y; i <= 24; i++) {
882 			*lpnt++ = CL_LINE;
883 			if (i != 24)
884 				*lpnt++ = '\n';
885 		}
886 		cursor(x, y);
887 	} else
888 		*lpnt++ = CL_DOWN;
889 	cursor(x, y);
890 #endif	/* VT100 */
891 }
892 
893 /*
894  * standout(str)	Print the argument string in inverse video (standout mode).
895  */
896 void
897 standout(str)
898 	char  *str;
899 {
900 #ifdef VT100
901 	setbold();
902 	while (*str)
903 		*lpnt++ = *str++;
904 	resetbold();
905 #else	/* VT100 */
906 	*lpnt++ = ST_START;
907 	while (*str)
908 		*lpnt++ = *str++;
909 	*lpnt++ = ST_END;
910 #endif	/* VT100 */
911 }
912 
913 /*
914  * set_score_output() 	Called when output should be literally printed.
915  */
916 void
917 set_score_output()
918 {
919 	enable_scroll = -1;
920 }
921 
922 /*
923  *	lflush()	Flush the output buffer
924  *
925  *	Returns nothing of value.
926  *	for termcap version: Flush output in output buffer according to output
927  *	status as indicated by `enable_scroll'
928  */
929 #ifndef VT100
930 static int      scrline = 18;	/* line # for wraparound instead of scrolling
931 				 * if no DL */
932 void
933 lflush()
934 {
935 	int    lpoint;
936 	u_char  *str;
937 	static int      curx = 0;
938 	static int      cury = 0;
939 
940 	if ((lpoint = lpnt - lpbuf) > 0) {
941 #ifdef EXTRA
942 		c[BYTESOUT] += lpoint;
943 #endif
944 		if (enable_scroll <= -1) {
945 			flush_buf();
946 			if (write(lfd, lpbuf, lpoint) != lpoint)
947 				write(2, "error writing to output file\n", 29);
948 			lpnt = lpbuf;	/* point back to beginning of buffer */
949 			return;
950 		}
951 		for (str = lpbuf; str < lpnt; str++) {
952 			if (*str >= 32) {
953 				xputchar(*str);
954 				curx++;
955 			} else
956 				switch (*str) {
957 				case CLEAR:
958 					tputs(CL, 0, xputchar);
959 					curx = cury = 0;
960 					break;
961 
962 				case CL_LINE:
963 					tputs(CE, 0, xputchar);
964 					break;
965 
966 				case CL_DOWN:
967 					tputs(CD, 0, xputchar);
968 					break;
969 
970 				case ST_START:
971 					tputs(SO, 0, xputchar);
972 					break;
973 
974 				case ST_END:
975 					tputs(SE, 0, xputchar);
976 					break;
977 
978 				case CURSOR:
979 					curx = *++str - 1;
980 					cury = *++str - 1;
981 					tputs(tgoto(CM, curx, cury), 0, xputchar);
982 					break;
983 
984 				case '\n':
985 					if ((cury == 23) && enable_scroll) {
986 						if (!DL || !AL) {	/* wraparound or scroll? */
987 							if (++scrline > 23)
988 								scrline = 19;
989 
990 							if (++scrline > 23)
991 								scrline = 19;
992 							tputs(tgoto(CM, 0, scrline), 0, xputchar);
993 							tputs(CE, 0, xputchar);
994 
995 							if (--scrline < 19)
996 								scrline = 23;
997 							tputs(tgoto(CM, 0, scrline), 0, xputchar);
998 							tputs(CE, 0, xputchar);
999 						} else {
1000 							tputs(tgoto(CM, 0, 19), 0, xputchar);
1001 							tputs(DL, 0, xputchar);
1002 							tputs(tgoto(CM, 0, 23), 0, xputchar);
1003 							/*
1004 							 * tputs (AL, 0,
1005 							 * xputchar);
1006 							 */
1007 						}
1008 					} else {
1009 						xputchar('\n');
1010 						cury++;
1011 					}
1012 					curx = 0;
1013 					break;
1014 
1015 				default:
1016 					xputchar(*str);
1017 					curx++;
1018 				};
1019 		}
1020 	}
1021 	lpnt = lpbuf;
1022 	flush_buf();		/* flush real output buffer now */
1023 }
1024 #else	/* VT100 */
1025 /*
1026  *	lflush()		flush the output buffer
1027  *
1028  *	Returns nothing of value.
1029  */
1030 void
1031 lflush()
1032 {
1033 	int    lpoint;
1034 	if ((lpoint = lpnt - lpbuf) > 0) {
1035 #ifdef EXTRA
1036 		c[BYTESOUT] += lpoint;
1037 #endif
1038 		if (write(lfd, lpbuf, lpoint) != lpoint)
1039 			write(2, "error writing to output file\n", 29);
1040 	}
1041 	lpnt = lpbuf;		/* point back to beginning of buffer */
1042 }
1043 #endif	/* VT100 */
1044 
1045 #ifndef VT100
1046 static int      vindex = 0;
1047 /*
1048  * xputchar(ch)		Print one character in decoded output buffer.
1049  */
1050 int
1051 xputchar(c)
1052 	int             c;
1053 {
1054 	outbuf[vindex++] = c;
1055 	if (vindex >= BUFBIG)
1056 		flush_buf();
1057 	return (0);
1058 }
1059 
1060 /*
1061  * flush_buf()			Flush buffer with decoded output.
1062  */
1063 void
1064 flush_buf()
1065 {
1066 	if (vindex)
1067 		write(lfd, outbuf, vindex);
1068 	vindex = 0;
1069 }
1070 
1071 /*
1072  *	char *tmcapcnv(sd,ss)  Routine to convert VT100 escapes to termcap
1073  *	format
1074  *	Processes only the \33[#m sequence (converts . files for termcap use
1075  */
1076 char *
1077 tmcapcnv(sd, ss)
1078 	char  *sd, *ss;
1079 {
1080 	int    tmstate = 0;	/* 0=normal, 1=\33 2=[ 3=# */
1081 	char            tmdigit = 0;	/* the # in \33[#m */
1082 	while (*ss) {
1083 		switch (tmstate) {
1084 		case 0:
1085 			if (*ss == '\33') {
1086 				tmstate++;
1087 				break;
1088 			}
1089 	ign:		*sd++ = *ss;
1090 	ign2:		tmstate = 0;
1091 			break;
1092 		case 1:
1093 			if (*ss != '[')
1094 				goto ign;
1095 			tmstate++;
1096 			break;
1097 		case 2:
1098 			if (isdigit((u_char)*ss)) {
1099 				tmdigit = *ss - '0';
1100 				tmstate++;
1101 				break;
1102 			}
1103 			if (*ss == 'm') {
1104 				*sd++ = ST_END;
1105 				goto ign2;
1106 			}
1107 			goto ign;
1108 		case 3:
1109 			if (*ss == 'm') {
1110 				if (tmdigit)
1111 					*sd++ = ST_START;
1112 				else
1113 					*sd++ = ST_END;
1114 				goto ign2;
1115 			}
1116 		default:
1117 			goto ign;
1118 		};
1119 		ss++;
1120 	}
1121 	*sd = 0;		/* NULL terminator */
1122 	return (sd);
1123 }
1124 #endif	/* VT100 */
1125 
1126 /*
1127  *	beep()	Routine to emit a beep if enabled (see no-beep in .larnopts)
1128  */
1129 void
1130 beep()
1131 {
1132 	if (!nobeep)
1133 		*lpnt++ = '\7';
1134 }
1135