xref: /dragonfly/games/larn/io.c (revision 31524921)
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 		fmt++;
286 	}
287 	va_end(ap);
288 }
289 
290 /*
291  *	lprint(long-integer)	send binary integer to output buffer
292  *		long integer;
293  *
294  *		+---------+---------+---------+---------+
295  *		|   high  |	    |	      |	  low	|
296  *		|  order  |	    |	      |  order	|
297  *		|   byte  |	    |	      |	  byte	|
298  *		+---------+---------+---------+---------+
299  *	        31  ---  24 23 --- 16 15 ---  8 7  ---   0
300  *
301  *	The save order is low order first, to high order (4 bytes total)
302  *	and is written to be system independent.
303  *	No checking for output buffer overflow is done, but flushes if needed!
304  *	Returns nothing of value.
305  */
306 void
307 lprint(long x)
308 {
309 	if (lpnt >= lpend)
310 		lflush();
311 	*lpnt++ = 255 & x;
312 	*lpnt++ = 255 & (x >> 8);
313 	*lpnt++ = 255 & (x >> 16);
314 	*lpnt++ = 255 & (x >> 24);
315 }
316 
317 /*
318  *	lwrite(buf,len)		write a buffer to the output buffer
319  *		char *buf;
320  *		int len;
321  *
322  *	Enter with the address and number of bytes to write out
323  *	Returns nothing of value
324  */
325 void
326 lwrite(char *buf, int len)
327 {
328 	char *str;
329 	int   num2;
330 
331 	if (len > 399) {	/* don't copy data if can just write it */
332 #ifdef EXTRA
333 		c[BYTESOUT] += len;
334 #endif
335 
336 #ifndef VT100
337 		for (str = buf; len > 0; --len)
338 			lprc(*str++);
339 #else /* VT100 */
340 		lflush();
341 		write(io_outfd, buf, len);
342 #endif /* VT100 */
343 	} else
344 		while (len) {
345 			if (lpnt >= lpend)	/* if buffer is full flush it */
346 				lflush();
347 			num2 = lpbuf + BUFBIG - lpnt;	/* # bytes left in output buffer */
348 			if (num2 > len)
349 				num2 = len;
350 			str = lpnt;
351 			len -= num2;
352 			while (num2--)		/* copy in the bytes */
353 				*str++ = *buf++;
354 			lpnt = str;
355 		}
356 }
357 
358 /*
359  *	long lgetc()	Read one character from input buffer
360  *
361  *  Returns 0 if EOF, otherwise the character
362  */
363 long
364 lgetc(void)
365 {
366 	int i;
367 	if (ipoint != iepoint)
368 		return (inbuffer[ipoint++]);
369 	if (iepoint != MAXIBUF)
370 		return (0);
371 	if ((i = read(io_infd, inbuffer, MAXIBUF)) <= 0) {
372 		if (i != 0)
373 			write(1, "error reading from input file\n", 30);
374 		iepoint = ipoint = 0;
375 		return (0);
376 	}
377 	ipoint = 1;
378 	iepoint = i;
379 	return (*inbuffer);
380 }
381 
382 /*
383  *	long lrint_x()	Read one integer from input buffer
384  *
385  *		+---------+---------+---------+---------+
386  *		|   high  |	    |	      |	  low	|
387  *		|  order  |	    |	      |  order	|
388  *		|   byte  |	    |	      |	  byte	|
389  *		+---------+---------+---------+---------+
390  *	       31  ---  24 23 --- 16 15 ---  8 7  ---   0
391  *
392  *	The save order is low order first, to high order (4 bytes total)
393  *	Returns the int read
394  */
395 long
396 lrint_x(void)
397 {
398 	unsigned long i;
399 	i = 255 & lgetc();
400 	i |= (255 & lgetc()) << 8;
401 	i |= (255 & lgetc()) << 16;
402 	i |= (255 & lgetc()) << 24;
403 	return (i);
404 }
405 
406 /*
407  *	lrfill(address,number)	put input bytes into a buffer
408  *		char *address;
409  *		int number;
410  *
411  *	Reads "number" bytes into the buffer pointed to by "address".
412  *	Returns nothing of value
413  */
414 void
415 lrfill(char *adr, int num)
416 {
417 	char *pnt;
418 	int   num2;
419 	while (num) {
420 		if (iepoint == ipoint) {
421 			if (num > 5) {	/* fast way */
422 				if (read(io_infd, adr, num) != num)
423 					write(2, "error reading from input file\n", 30);
424 				num = 0;
425 			} else {
426 				*adr++ = lgetc();
427 				--num;
428 			}
429 		} else {
430 			num2 = iepoint - ipoint;	/* # of bytes left in the buffer */
431 			if (num2 > num)
432 				num2 = num;
433 			pnt = inbuffer + ipoint;
434 			num -= num2;
435 			ipoint += num2;
436 			while (num2--)
437 				*adr++ = *pnt++;
438 		}
439 	}
440 }
441 
442 /*
443  *	char *lgetw()	Get a whitespace ended word from input
444  *
445  *	Returns pointer to a buffer that contains word.  If EOF, returns a NULL
446  */
447 char *
448 lgetw(void)
449 {
450 	char *lgp, cc;
451 	int   n = LINBUFSIZE, quote = 0;
452 	lgp = lgetwbuf;
453 	do
454 		cc = lgetc();
455 	while ((cc <= 32) && (cc > '\0'));	/* eat whitespace */
456 	for (;; --n, cc = lgetc()) {
457 		if ((cc == '\0') && (lgp == lgetwbuf))	/* EOF */
458 			return (NULL);
459 		if ((n <= 1) || ((cc <= 32) && (quote == 0))) {
460 			*lgp = '\0';
461 			return (lgetwbuf);
462 		}
463 		if (cc != '"')
464 			*lgp++ = cc;
465 		else
466 			quote ^= 1;
467 	}
468 }
469 
470 /*
471  *	char *lgetl()	Function to read in a line ended by newline or EOF
472  *
473  *	Returns pointer to a buffer that contains the line.  If EOF, returns NULL
474  */
475 char *
476 lgetl(void)
477 {
478 	int   i = LINBUFSIZE, ch;
479 	char *str = lgetwbuf;
480 	for (;; --i) {
481 		if ((*str++ = ch = lgetc()) == '\0') {
482 			if (str == lgetwbuf + 1)	/* EOF */
483 				return (NULL);
484 ot:
485 			*str = 0;
486 			return (lgetwbuf);	/* line ended by EOF */
487 		}
488 		if ((ch == '\n') || (i <= 1))	/* line ended by \n */
489 			goto ot;
490 	}
491 }
492 
493 /*
494  *	lcreat(filename)	Create a new file for write
495  *		char *filename;
496  *
497  *	lcreat(NULL); means to the terminal
498  *	Returns -1 if error, otherwise the file descriptor opened.
499  */
500 int
501 lcreat(char *str)
502 {
503 	lpnt = lpbuf;
504 	lpend = lpbuf + BUFBIG;
505 	if (str == NULL)
506 		return (io_outfd = 1);
507 	if ((io_outfd = creat(str, 0644)) < 0) {
508 		io_outfd = 1;
509 		lprintf("error creating file <%s>\n", str);
510 		lflush();
511 		return (-1);
512 	}
513 	return (io_outfd);
514 }
515 
516 /*
517  *	lopen(filename)		Open a file for read
518  *		char *filename;
519  *
520  *	lopen(0) means from the terminal
521  *	Returns -1 if error, otherwise the file descriptor opened.
522  */
523 int
524 lopen(char *str)
525 {
526 	ipoint = iepoint = MAXIBUF;
527 	if (str == NULL)
528 		return (io_infd = 0);
529 	if ((io_infd = open(str, O_RDONLY)) < 0) {
530 		lwclose();
531 		io_outfd = 1;
532 		lpnt = lpbuf;
533 		return (-1);
534 	}
535 	return (io_infd);
536 }
537 
538 /*
539  *	lappend(filename)	Open for append to an existing file
540  *		char *filename;
541  *
542  *	lappend(0) means to the terminal
543  *	Returns -1 if error, otherwise the file descriptor opened.
544  */
545 int
546 lappend(char *str)
547 {
548 	lpnt = lpbuf;
549 	lpend = lpbuf + BUFBIG;
550 	if (str == NULL)
551 		return (io_outfd = 1);
552 	if ((io_outfd = open(str, O_RDWR)) < 0) {
553 		io_outfd = 1;
554 		return (-1);
555 	}
556 	lseek(io_outfd, 0, SEEK_END);	/* seek to end of file */
557 	return (io_outfd);
558 }
559 
560 /*
561  *	lrclose()	close the input file
562  *
563  *	Returns nothing of value.
564  */
565 void
566 lrclose(void)
567 {
568 	if (io_infd > 0)
569 		close(io_infd);
570 }
571 
572 /*
573  *	lwclose()	close output file flushing if needed
574  *
575  *	Returns nothing of value.
576  */
577 void
578 lwclose(void)
579 {
580 	lflush();
581 	if (io_outfd > 2)
582 		close(io_outfd);
583 }
584 
585 /*
586  *	lprcat(string)	append a string to the output buffer
587  *			    avoids calls to lprintf (time consuming)
588  */
589 void
590 lprcat(const char *str)
591 {
592 	char *str2;
593 	if (lpnt >= lpend)
594 		lflush();
595 	str2 = lpnt;
596 	while ((*str2++ = *str++) != '\0')
597 		continue;
598 	lpnt = str2 - 1;
599 }
600 
601 #ifdef VT100
602 /*
603  *	cursor(x,y)	Subroutine to set the cursor position
604  *
605  *	x and y are the cursor coordinates, and lpbuff is the output buffer where
606  *	escape sequence will be placed.
607  */
608 static const char *y_num[]= { "\33[","\33[","\33[2","\33[3","\33[4","\33[5","\33[6",
609 	"\33[7","\33[8","\33[9","\33[10","\33[11","\33[12","\33[13","\33[14",
610 	"\33[15","\33[16","\33[17","\33[18","\33[19","\33[20","\33[21","\33[22",
611 	"\33[23","\33[24" };
612 
613 static const char *x_num[]= { "H","H",";2H",";3H",";4H",";5H",";6H",";7H",";8H",";9H",
614 	";10H",";11H",";12H",";13H",";14H",";15H",";16H",";17H",";18H",";19H",
615 	";20H",";21H",";22H",";23H",";24H",";25H",";26H",";27H",";28H",";29H",
616 	";30H",";31H",";32H",";33H",";34H",";35H",";36H",";37H",";38H",";39H",
617 	";40H",";41H",";42H",";43H",";44H",";45H",";46H",";47H",";48H",";49H",
618 	";50H",";51H",";52H",";53H",";54H",";55H",";56H",";57H",";58H",";59H",
619 	";60H",";61H",";62H",";63H",";64H",";65H",";66H",";67H",";68H",";69H",
620 	";70H",";71H",";72H",";73H",";74H",";75H",";76H",";77H",";78H",";79H",
621 	";80H" };
622 
623 void
624 cursor(int x, int y)
625 {
626 	char *p;
627 	if (lpnt >= lpend)
628 		lflush();
629 
630 	p = y_num[y];			/* get the string to print */
631 	while (*p)
632 		*lpnt++ = *p++;		/* print the string */
633 
634 	p = x_num[x];			/* get the string to print */
635 	while (*p)
636 		*lpnt++ = *p++;		/* print the string */
637 }
638 #else /* VT100 */
639 /*
640  *	cursor(x,y)	Put cursor at specified coordinates staring at [1,1] (termcap)
641  */
642 void
643 cursor(int x, int y)
644 {
645 	if (lpnt >= lpend)
646 		lflush();
647 
648 	*lpnt++ = CURSOR;
649 	*lpnt++ = x;
650 	*lpnt++ = y;
651 }
652 #endif /* VT100 */
653 
654 /*
655  *	Routine to position cursor at beginning of 24th line
656  */
657 void
658 cursors(void)
659 {
660 	cursor(1, 24);
661 }
662 
663 #ifndef VT100
664 /*
665  * Warning: ringing the bell is control code 7. Don't use in defines.
666  * Don't change the order of these defines.
667  * Also used in helpfiles. Codes used in helpfiles should be \E[1 to \E[7 with
668  * obvious meanings.
669  */
670 
671 static char cap[256];
672 char *CM, *CE, *CD, *CL, *SO, *SE, *AL, *DL;/* Termcap capabilities */
673 static char *outbuf = NULL;	/* translated output buffer */
674 
675 /*
676  * init_term()		Terminal initialization -- setup termcap info
677  */
678 void
679 init_term(void)
680 {
681 	char  termbuf[1024];
682 	char *capptr = cap + 10;
683 	char *term;
684 
685 	switch (tgetent(termbuf, term = getenv("TERM"))) {
686 	case -1:
687 		write(2, "Cannot open termcap file.\n", 26);
688 		exit(1);
689 	case 0:
690 		write(2, "Cannot find entry of ", 21);
691 		write(2, term, strlen(term));
692 		write(2, " in termcap\n", 12);
693 		exit(1);
694 	}
695 
696 	CM = tgetstr("cm", &capptr);	/* Cursor motion */
697 	CE = tgetstr("ce", &capptr);	/* Clear to eoln */
698 	CL = tgetstr("cl", &capptr);	/* Clear screen */
699 
700 /* OPTIONAL */
701 	AL = tgetstr("al", &capptr);	/* Insert line */
702 	DL = tgetstr("dl", &capptr);	/* Delete line */
703 	SO = tgetstr("so", &capptr);	/* Begin standout mode */
704 	SE = tgetstr("se", &capptr);	/* End standout mode */
705 	CD = tgetstr("cd", &capptr);	/* Clear to end of display */
706 
707 	if (!CM) {		/* can't find cursor motion entry */
708 		write(2, "Sorry, for a ", 13);
709 		write(2, term, strlen(term));
710 		write(2, ", I can't find the cursor motion entry in termcap\n", 50);
711 		exit(1);
712 	}
713 	if (!CE) {		/* can't find clear to end of line entry */
714 		write(2, "Sorry, for a ", 13);
715 		write(2, term, strlen(term));
716 		write(2, ", I can't find the clear to end of line entry in termcap\n", 57);
717 		exit(1);
718 	}
719 	if (!CL) {		/* can't find clear entire screen entry */
720 		write(2, "Sorry, for a ", 13);
721 		write(2, term, strlen(term));
722 		write(2, ", I can't find the clear entire screen entry in termcap\n", 56);
723 		exit(1);
724 	}
725 	/* get memory for decoded output buffer*/
726 	if ((outbuf = malloc(BUFBIG + 16)) == NULL) {
727 		write(2, "Error malloc'ing memory for decoded output buffer\n", 50);
728 		died(-285);	/* malloc() failure */
729 	}
730 }
731 #endif /* VT100 */
732 
733 /*
734  *	cl_line(x,y)	Clear the whole line indicated by 'y' and leave cursor at [x,y]
735  */
736 void
737 cl_line(int x, int y)
738 {
739 #ifdef VT100
740 	cursor(x, y);
741 	lprcat("\33[2K");
742 #else /* VT100 */
743 	cursor(1, y);
744 	*lpnt++ = CL_LINE;
745 	cursor(x, y);
746 #endif /* VT100 */
747 }
748 
749 /*
750  *	cl_up(x,y)	Clear screen from [x,1] to current position. Leave cursor at [x,y]
751  */
752 void
753 cl_up(int x, int y)
754 {
755 #ifdef VT100
756 	cursor(x, y);
757 	lprcat("\33[1J\33[2K");
758 #else /* VT100 */
759 	int i;
760 	cursor(1, 1);
761 	for (i = 1; i <= y; i++) {
762 		*lpnt++ = CL_LINE;
763 		*lpnt++ = '\n';
764 	}
765 	cursor(x, y);
766 #endif /* VT100 */
767 }
768 
769 /*
770  *	cl_dn(x,y)	Clear screen from [1,y] to end of display. Leave cursor at [x,y]
771  */
772 void
773 cl_dn(int x, int y)
774 {
775 #ifdef VT100
776 	cursor(x, y);
777 	lprcat("\33[J\33[2K");
778 #else /* VT100 */
779 	int i;
780 	cursor(1, y);
781 	if (!CD) {
782 		*lpnt++ = CL_LINE;
783 		for (i = y; i <= 24; i++) {
784 			*lpnt++ = CL_LINE;
785 			if (i != 24)
786 				*lpnt++ = '\n';
787 		}
788 		cursor(x, y);
789 	} else
790 		*lpnt++ = CL_DOWN;
791 	cursor(x, y);
792 #endif /* VT100 */
793 }
794 
795 /*
796  *	standout(str)	Print the argument string in inverse video (standout mode).
797  */
798 void
799 standout(const char *str)
800 {
801 #ifdef VT100
802 	setbold();
803 	while (*str)
804 		*lpnt++ = *str++;
805 	resetbold();
806 #else /* VT100 */
807 	*lpnt++ = ST_START;
808 	while (*str)
809 		*lpnt++ = *str++;
810 	*lpnt++ = ST_END;
811 #endif /* VT100 */
812 }
813 
814 /*
815  *	set_score_output()	Called when output should be literally printed.
816  */
817 void
818 set_score_output(void)
819 {
820 	enable_scroll = -1;
821 }
822 
823 /*
824  *	lflush()	Flush the output buffer
825  *
826  *	Returns nothing of value.
827  *	for termcap version: Flush output in output buffer according to output
828  *			     status as indicated by `enable_scroll'
829  */
830 #ifndef VT100
831 static int scrline = 18;	/* line # for wraparound instead of scrolling if no DL */
832 
833 void
834 lflush(void)
835 {
836 	int lpoint;
837 	char *str;
838 	static int curx = 0;
839 	static int cury = 0;
840 
841 	if ((lpoint = lpnt - lpbuf) > 0) {
842 #ifdef EXTRA
843 		c[BYTESOUT] += lpoint;
844 #endif
845 		if (enable_scroll <= -1) {
846 			flush_buf();
847 			if (write(io_outfd, lpbuf, lpoint) != lpoint)
848 				write(2, "error writing to output file\n", 29);
849 			lpnt = lpbuf;	/* point back to beginning of buffer */
850 			return;
851 		}
852 		for (str = lpbuf; str < lpnt; str++) {
853 			if (*str >= 32) {
854 				putchr(*str);
855 				curx++;
856 			} else
857 				switch (*str) {
858 				case CLEAR:
859 					tputs(CL, 0, putchr);
860 					curx = cury = 0;
861 					break;
862 
863 				case CL_LINE:
864 					tputs(CE, 0, putchr);
865 					break;
866 
867 				case CL_DOWN:
868 					tputs(CD, 0, putchr);
869 					break;
870 
871 				case ST_START:
872 					tputs(SO, 0, putchr);
873 					break;
874 
875 				case ST_END:
876 					tputs(SE, 0, putchr);
877 					break;
878 
879 				case CURSOR:
880 					curx = *++str - 1;
881 					cury = *++str - 1;
882 					tputs(tgoto(CM, curx, cury), 0, putchr);
883 					break;
884 
885 				case '\n':
886 					if ((cury == 23) && enable_scroll) {
887 						if (!DL || !AL) {	/* wraparound or scroll? */
888 							if (++scrline > 23)
889 								scrline = 19;
890 
891 							if (++scrline > 23)
892 								scrline = 19;
893 							tputs(tgoto(CM, 0, scrline), 0, putchr);
894 							tputs(CE, 0, putchr);
895 
896 							if (--scrline < 19)
897 								scrline = 23;
898 							tputs(tgoto(CM, 0, scrline), 0, putchr);
899 							tputs(CE, 0, putchr);
900 						} else {
901 							tputs(tgoto(CM, 0, 19), 0, putchr);
902 							tputs(DL, 0, putchr);
903 							tputs(tgoto(CM, 0, 23), 0, putchr);
904 						}
905 					} else {
906 						putchr('\n');
907 						cury++;
908 					}
909 					curx = 0;
910 					break;
911 
912 				default:
913 					putchr(*str);
914 					curx++;
915 				}
916 		}
917 	}
918 	lpnt = lpbuf;
919 	flush_buf();		/* flush real output buffer now */
920 }
921 #else /* VT100 */
922 /*
923  *	lflush()	flush the output buffer
924  *
925  *	Returns nothing of value.
926  */
927 void
928 lflush(void)
929 {
930 	int lpoint;
931 	if ((lpoint = lpnt - lpbuf) > 0) {
932 #ifdef EXTRA
933 		c[BYTESOUT] += lpoint;
934 #endif
935 		if (write(io_outfd, lpbuf, lpoint) != lpoint)
936 			write(2, "error writing to output file\n", 29);
937 	}
938 	lpnt = lpbuf;		/* point back to beginning of buffer */
939 }
940 #endif /* VT100 */
941 
942 #ifndef VT100
943 static int pindex = 0;
944 /*
945  *	putchr(ch)	Print one character in decoded output buffer.
946  */
947 static int
948 putchr(int ch)
949 {
950 	outbuf[pindex++] = ch;
951 	if (pindex >= BUFBIG)
952 		flush_buf();
953 	return (0);
954 }
955 
956 /*
957  *	flush_buf()	Flush buffer with decoded output.
958  */
959 static void
960 flush_buf(void)
961 {
962 	if (pindex)
963 		write(io_outfd, outbuf, pindex);
964 	pindex = 0;
965 }
966 
967 /*
968  *	char *tmcapcnv(sd,ss)	Routine to convert VT100 escapes to termcap format
969  *
970  *	Processes only the \33[#m sequence (converts . files for termcap use
971  */
972 char *
973 tmcapcnv(char *sd, char *ss)
974 {
975 	int tmstate = 0;	/* 0=normal, 1=\33 2=[ 3=# */
976 	char tmdigit = 0;	/* the # in \33[#m */
977 	while (*ss) {
978 		switch (tmstate) {
979 		case 0:
980 			if (*ss == '\33') {
981 				tmstate++;
982 				break;
983 			}
984 ign:  *sd++ = *ss;
985 ign2: tmstate = 0;
986 			break;
987 		case 1:
988 			if (*ss != '[')
989 				goto ign;
990 			tmstate++;
991 			break;
992 		case 2:
993 			if (isdigit((int)*ss)) {
994 				tmdigit = *ss - '0';
995 				tmstate++;
996 				break;
997 			}
998 			if (*ss == 'm') {
999 				*sd++ = ST_END;
1000 				goto ign2;
1001 			}
1002 			goto ign;
1003 		case 3:
1004 			if (*ss == 'm') {
1005 				if (tmdigit)
1006 					*sd++ = ST_START;
1007 				else
1008 					*sd++ = ST_END;
1009 				goto ign2;
1010 			}
1011 		default:
1012 			goto ign;
1013 		}
1014 		ss++;
1015 	}
1016 	*sd = 0;	/* NULL terminator */
1017 	return (sd);
1018 }
1019 #endif /* VT100 */
1020 
1021 /*
1022  *	beep()		Routine to emit a beep if enabled (see no-beep in .larnopts)
1023  */
1024 void
1025 beep(void)
1026 {
1027 	if (!nobeep)
1028 		*lpnt++ = '\7';
1029 }
1030