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