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