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 $
4 * Below are the functions in this file:
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()
13 * FILE OUTPUT ROUTINES
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
20 * FILE OUTPUT MACROS (in header.h)
22 * lprc(character) put the character into the output buffer
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
32 * FILE OPEN / CLOSE ROUTINES
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
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)
56 * Note: ** entries are available only in termcap mode.
63 static int rawflg
= 0;
64 static char saveeof
, saveeol
;
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 */
74 static int putchr(int);
75 static void flush_buf(void);
79 * setupvt100() Subroutine to set up terminal in correct mode for game
81 * Attributes off, clear screen, set scrolling region, set tty mode
92 * clearvt100() Subroutine to clean up terminal when the game is over
94 * Attributes off, clear screen, unset scrolling region, restore tty mode
105 * getchr() Routine to read in one character from the terminal
114 lflush(); /* be sure output buffer is flushed */
115 read(0, &byt
, 1); /* get byte from terminal */
120 * scbr() Function to set cbreak -echo for the terminal
122 * like: system("stty cbreak -echo")
131 saveeof
= ttx
.c_cc
[VMIN
];
132 saveeol
= ttx
.c_cc
[VTIME
];
136 ttx
.c_lflag
&= ~(ICANON
| ECHO
| ECHOE
| ECHOK
| ECHONL
);
137 tcsetattr(0, TCSANOW
, &ttx
);
141 * sncbr() Function to set -cbreak echo for the terminal
143 * like: system("stty -cbreak echo")
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
);
157 * newgame() Subroutine to save the initial time and seed rnd()
163 for (p
= c
, pe
= c
+ 100; p
< pe
; *p
++ = 0)
167 lcreat(NULL
); /* open buffering for output to terminal */
171 * lprintf(format, args...) printf to the output buffer
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.
184 lprintf(const char *fmt
, ...)
186 va_list ap
; /* pointer for variable argument list */
188 long wide
, left
, cont
, n
; /* data for lprintf */
189 char db
[12]; /* %d buffer in lprintf */
191 va_start(ap
, fmt
); /* initialize the var args pointer */
209 n
= va_arg(ap
, long);
217 *tmpb
= (char)(n
% 10 + '0');
219 *(--tmpb
) = (char)((n
/= 10) % 10 + '0');
221 while (tmpb
< db
+ 12)
224 wide
-= db
- tmpb
+ 12;
228 while (tmpb
< db
+ 12)
238 tmpb
= va_arg(ap
, char *);
240 while ((*outb
++ = *tmpb
++))
244 n
= wide
- strlen(tmpb
);
248 while ((*outb
++ = *tmpb
++))
259 *outb
++ = va_arg(ap
, int);
273 wide
= 10 * wide
+ *fmt
- '0';
291 * lprint(long-integer) send binary integer to output buffer
294 * +---------+---------+---------+---------+
296 * | order | | | order |
297 * | byte | | | byte |
298 * +---------+---------+---------+---------+
299 * 31 --- 24 23 --- 16 15 --- 8 7 --- 0
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.
312 *lpnt
++ = 255 & (x
>> 8);
313 *lpnt
++ = 255 & (x
>> 16);
314 *lpnt
++ = 255 & (x
>> 24);
318 * lwrite(buf,len) write a buffer to the output buffer
322 * Enter with the address and number of bytes to write out
323 * Returns nothing of value
326 lwrite(char *buf
, int len
)
331 if (len
> 399) { /* don't copy data if can just write it */
337 for (str
= buf
; len
> 0; --len
)
341 write(io_outfd
, buf
, len
);
345 if (lpnt
>= lpend
) /* if buffer is full flush it */
347 num2
= lpbuf
+ BUFBIG
- lpnt
; /* # bytes left in output buffer */
352 while (num2
--) /* copy in the bytes */
359 * long lgetc() Read one character from input buffer
361 * Returns 0 if EOF, otherwise the character
367 if (ipoint
!= iepoint
)
368 return (inbuffer
[ipoint
++]);
369 if (iepoint
!= MAXIBUF
)
371 if ((i
= read(io_infd
, inbuffer
, MAXIBUF
)) <= 0) {
373 write(1, "error reading from input file\n", 30);
374 iepoint
= ipoint
= 0;
383 * long lrint_x() Read one integer from input buffer
385 * +---------+---------+---------+---------+
387 * | order | | | order |
388 * | byte | | | byte |
389 * +---------+---------+---------+---------+
390 * 31 --- 24 23 --- 16 15 --- 8 7 --- 0
392 * The save order is low order first, to high order (4 bytes total)
393 * Returns the int read
400 i
|= (255 & lgetc()) << 8;
401 i
|= (255 & lgetc()) << 16;
402 i
|= (255 & lgetc()) << 24;
407 * lrfill(address,number) put input bytes into a buffer
411 * Reads "number" bytes into the buffer pointed to by "address".
412 * Returns nothing of value
415 lrfill(char *adr
, int 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);
430 num2
= iepoint
- ipoint
; /* # of bytes left in the buffer */
433 pnt
= inbuffer
+ ipoint
;
443 * char *lgetw() Get a whitespace ended word from input
445 * Returns pointer to a buffer that contains word. If EOF, returns a NULL
451 int n
= LINBUFSIZE
, quote
= 0;
455 while ((cc
<= 32) && (cc
> '\0')); /* eat whitespace */
456 for (;; --n
, cc
= lgetc()) {
457 if ((cc
== '\0') && (lgp
== lgetwbuf
)) /* EOF */
459 if ((n
<= 1) || ((cc
<= 32) && (quote
== 0))) {
471 * char *lgetl() Function to read in a line ended by newline or EOF
473 * Returns pointer to a buffer that contains the line. If EOF, returns NULL
478 int i
= LINBUFSIZE
, ch
;
479 char *str
= lgetwbuf
;
481 if ((*str
++ = ch
= lgetc()) == '\0') {
482 if (str
== lgetwbuf
+ 1) /* EOF */
486 return (lgetwbuf
); /* line ended by EOF */
488 if ((ch
== '\n') || (i
<= 1)) /* line ended by \n */
494 * lcreat(filename) Create a new file for write
497 * lcreat(NULL); means to the terminal
498 * Returns -1 if error, otherwise the file descriptor opened.
504 lpend
= lpbuf
+ BUFBIG
;
506 return (io_outfd
= 1);
507 if ((io_outfd
= creat(str
, 0644)) < 0) {
509 lprintf("error creating file <%s>\n", str
);
517 * lopen(filename) Open a file for read
520 * lopen(0) means from the terminal
521 * Returns -1 if error, otherwise the file descriptor opened.
526 ipoint
= iepoint
= MAXIBUF
;
528 return (io_infd
= 0);
529 if ((io_infd
= open(str
, O_RDONLY
)) < 0) {
539 * lappend(filename) Open for append to an existing file
542 * lappend(0) means to the terminal
543 * Returns -1 if error, otherwise the file descriptor opened.
549 lpend
= lpbuf
+ BUFBIG
;
551 return (io_outfd
= 1);
552 if ((io_outfd
= open(str
, O_RDWR
)) < 0) {
556 lseek(io_outfd
, 0, SEEK_END
); /* seek to end of file */
561 * lrclose() close the input file
563 * Returns nothing of value.
573 * lwclose() close output file flushing if needed
575 * Returns nothing of value.
586 * lprcat(string) append a string to the output buffer
587 * avoids calls to lprintf (time consuming)
590 lprcat(const char *str
)
596 while ((*str2
++ = *str
++) != '\0')
603 * cursor(x,y) Subroutine to set the cursor position
605 * x and y are the cursor coordinates, and lpbuff is the output buffer where
606 * escape sequence will be placed.
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",
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",
630 p
= y_num
[y
]; /* get the string to print */
632 *lpnt
++ = *p
++; /* print the string */
634 p
= x_num
[x
]; /* get the string to print */
636 *lpnt
++ = *p
++; /* print the string */
640 * cursor(x,y) Put cursor at specified coordinates staring at [1,1] (termcap)
655 * Routine to position cursor at beginning of 24th line
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
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 */
676 * init_term() Terminal initialization -- setup termcap info
682 char *capptr
= cap
+ 10;
685 switch (tgetent(termbuf
, term
= getenv("TERM"))) {
687 write(2, "Cannot open termcap file.\n", 26);
690 write(2, "Cannot find entry of ", 21);
691 write(2, term
, strlen(term
));
692 write(2, " in termcap\n", 12);
696 CM
= tgetstr("cm", &capptr
); /* Cursor motion */
697 CE
= tgetstr("ce", &capptr
); /* Clear to eoln */
698 CL
= tgetstr("cl", &capptr
); /* Clear screen */
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 */
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);
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);
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);
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 */
734 * cl_line(x,y) Clear the whole line indicated by 'y' and leave cursor at [x,y]
737 cl_line(int x
, int y
)
750 * cl_up(x,y) Clear screen from [x,1] to current position. Leave cursor at [x,y]
757 lprcat("\33[1J\33[2K");
761 for (i
= 1; i
<= y
; i
++) {
770 * cl_dn(x,y) Clear screen from [1,y] to end of display. Leave cursor at [x,y]
777 lprcat("\33[J\33[2K");
783 for (i
= y
; i
<= 24; i
++) {
796 * standout(str) Print the argument string in inverse video (standout mode).
799 standout(const char *str
)
815 * set_score_output() Called when output should be literally printed.
818 set_score_output(void)
824 * lflush() Flush the output buffer
826 * Returns nothing of value.
827 * for termcap version: Flush output in output buffer according to output
828 * status as indicated by `enable_scroll'
831 static int scrline
= 18; /* line # for wraparound instead of scrolling if no DL */
841 if ((lpoint
= lpnt
- lpbuf
) > 0) {
843 c
[BYTESOUT
] += lpoint
;
845 if (enable_scroll
<= -1) {
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 */
852 for (str
= lpbuf
; str
< lpnt
; str
++) {
859 tputs(CL
, 0, putchr
);
864 tputs(CE
, 0, putchr
);
868 tputs(CD
, 0, putchr
);
872 tputs(SO
, 0, putchr
);
876 tputs(SE
, 0, putchr
);
882 tputs(tgoto(CM
, curx
, cury
), 0, putchr
);
886 if ((cury
== 23) && enable_scroll
) {
887 if (!DL
|| !AL
) { /* wraparound or scroll? */
893 tputs(tgoto(CM
, 0, scrline
), 0, putchr
);
894 tputs(CE
, 0, putchr
);
898 tputs(tgoto(CM
, 0, scrline
), 0, putchr
);
899 tputs(CE
, 0, putchr
);
901 tputs(tgoto(CM
, 0, 19), 0, putchr
);
902 tputs(DL
, 0, putchr
);
903 tputs(tgoto(CM
, 0, 23), 0, putchr
);
919 flush_buf(); /* flush real output buffer now */
923 * lflush() flush the output buffer
925 * Returns nothing of value.
931 if ((lpoint
= lpnt
- lpbuf
) > 0) {
933 c
[BYTESOUT
] += lpoint
;
935 if (write(io_outfd
, lpbuf
, lpoint
) != lpoint
)
936 write(2, "error writing to output file\n", 29);
938 lpnt
= lpbuf
; /* point back to beginning of buffer */
943 static int pindex
= 0;
945 * putchr(ch) Print one character in decoded output buffer.
950 outbuf
[pindex
++] = ch
;
951 if (pindex
>= BUFBIG
)
957 * flush_buf() Flush buffer with decoded output.
963 write(io_outfd
, outbuf
, pindex
);
968 * char *tmcapcnv(sd,ss) Routine to convert VT100 escapes to termcap format
970 * Processes only the \33[#m sequence (converts . files for termcap use
973 tmcapcnv(char *sd
, char *ss
)
975 int tmstate
= 0; /* 0=normal, 1=\33 2=[ 3=# */
976 char tmdigit
= 0; /* the # in \33[#m */
993 if (isdigit((int)*ss
)) {
1016 *sd
= 0; /* NULL terminator */
1022 * beep() Routine to emit a beep if enabled (see no-beep in .larnopts)