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';
292 * lprint(long-integer) send binary integer to output buffer
295 * +---------+---------+---------+---------+
297 * | order | | | order |
298 * | byte | | | byte |
299 * +---------+---------+---------+---------+
300 * 31 --- 24 23 --- 16 15 --- 8 7 --- 0
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.
313 *lpnt++ = 255 & (x >> 8);
314 *lpnt++ = 255 & (x >> 16);
315 *lpnt++ = 255 & (x >> 24);
319 * lwrite(buf,len) write a buffer to the output buffer
323 * Enter with the address and number of bytes to write out
324 * Returns nothing of value
327 lwrite(char *buf, int len)
332 if (len > 399) { /* don't copy data if can just write it */
338 for (str = buf; len > 0; --len)
342 write(io_outfd, buf, len);
346 if (lpnt >= lpend) /* if buffer is full flush it */
348 num2 = lpbuf + BUFBIG - lpnt; /* # bytes left in output buffer */
353 while (num2--) /* copy in the bytes */
360 * long lgetc() Read one character from input buffer
362 * Returns 0 if EOF, otherwise the character
368 if (ipoint != iepoint)
369 return (inbuffer[ipoint++]);
370 if (iepoint != MAXIBUF)
372 if ((i = read(io_infd, inbuffer, MAXIBUF)) <= 0) {
374 write(1, "error reading from input file\n", 30);
375 iepoint = ipoint = 0;
384 * long lrint_x() Read one integer from input buffer
386 * +---------+---------+---------+---------+
388 * | order | | | order |
389 * | byte | | | byte |
390 * +---------+---------+---------+---------+
391 * 31 --- 24 23 --- 16 15 --- 8 7 --- 0
393 * The save order is low order first, to high order (4 bytes total)
394 * Returns the int read
401 i |= (255 & lgetc()) << 8;
402 i |= (255 & lgetc()) << 16;
403 i |= (255 & lgetc()) << 24;
408 * lrfill(address,number) put input bytes into a buffer
412 * Reads "number" bytes into the buffer pointed to by "address".
413 * Returns nothing of value
416 lrfill(char *adr, int 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);
431 num2 = iepoint - ipoint; /* # of bytes left in the buffer */
434 pnt = inbuffer + ipoint;
444 * char *lgetw() Get a whitespace ended word from input
446 * Returns pointer to a buffer that contains word. If EOF, returns a NULL
452 int n = LINBUFSIZE, quote = 0;
456 while ((cc <= 32) && (cc > '\0')); /* eat whitespace */
457 for (;; --n, cc = lgetc()) {
458 if ((cc == '\0') && (lgp == lgetwbuf)) /* EOF */
460 if ((n <= 1) || ((cc <= 32) && (quote == 0))) {
472 * char *lgetl() Function to read in a line ended by newline or EOF
474 * Returns pointer to a buffer that contains the line. If EOF, returns NULL
479 int i = LINBUFSIZE, ch;
480 char *str = lgetwbuf;
482 if ((*str++ = ch = lgetc()) == '\0') {
483 if (str == lgetwbuf + 1) /* EOF */
487 return (lgetwbuf); /* line ended by EOF */
489 if ((ch == '\n') || (i <= 1)) /* line ended by \n */
495 * lcreat(filename) Create a new file for write
498 * lcreat(NULL); means to the terminal
499 * Returns -1 if error, otherwise the file descriptor opened.
505 lpend = lpbuf + BUFBIG;
507 return (io_outfd = 1);
508 if ((io_outfd = creat(str, 0644)) < 0) {
510 lprintf("error creating file <%s>\n", str);
518 * lopen(filename) Open a file for read
521 * lopen(0) means from the terminal
522 * Returns -1 if error, otherwise the file descriptor opened.
527 ipoint = iepoint = MAXIBUF;
529 return (io_infd = 0);
530 if ((io_infd = open(str, O_RDONLY)) < 0) {
540 * lappend(filename) Open for append to an existing file
543 * lappend(0) means to the terminal
544 * Returns -1 if error, otherwise the file descriptor opened.
550 lpend = lpbuf + BUFBIG;
552 return (io_outfd = 1);
553 if ((io_outfd = open(str, O_RDWR)) < 0) {
557 lseek(io_outfd, 0, SEEK_END); /* seek to end of file */
562 * lrclose() close the input file
564 * Returns nothing of value.
574 * lwclose() close output file flushing if needed
576 * Returns nothing of value.
587 * lprcat(string) append a string to the output buffer
588 * avoids calls to lprintf (time consuming)
591 lprcat(const char *str)
597 while ((*str2++ = *str++) != '\0')
604 * cursor(x,y) Subroutine to set the cursor position
606 * x and y are the cursor coordinates, and lpbuff is the output buffer where
607 * escape sequence will be placed.
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",
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",
631 p = y_num[y]; /* get the string to print */
633 *lpnt++ = *p++; /* print the string */
635 p = x_num[x]; /* get the string to print */
637 *lpnt++ = *p++; /* print the string */
641 * cursor(x,y) Put cursor at specified coordinates staring at [1,1] (termcap)
656 * Routine to position cursor at beginning of 24th line
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
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 */
677 * init_term() Terminal initialization -- setup termcap info
683 char *capptr = cap + 10;
686 switch (tgetent(termbuf, term = getenv("TERM"))) {
688 write(2, "Cannot open termcap file.\n", 26);
691 write(2, "Cannot find entry of ", 21);
692 write(2, term, strlen(term));
693 write(2, " in termcap\n", 12);
697 CM = tgetstr("cm", &capptr); /* Cursor motion */
698 CE = tgetstr("ce", &capptr); /* Clear to eoln */
699 CL = tgetstr("cl", &capptr); /* Clear screen */
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 */
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);
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);
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);
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 */
735 * cl_line(x,y) Clear the whole line indicated by 'y' and leave cursor at [x,y]
738 cl_line(int x, int y)
751 * cl_up(x,y) Clear screen from [x,1] to current position. Leave cursor at [x,y]
758 lprcat("\33[1J\33[2K");
762 for (i = 1; i <= y; i++) {
771 * cl_dn(x,y) Clear screen from [1,y] to end of display. Leave cursor at [x,y]
778 lprcat("\33[J\33[2K");
784 for (i = y; i <= 24; i++) {
797 * standout(str) Print the argument string in inverse video (standout mode).
800 standout(const char *str)
816 * set_score_output() Called when output should be literally printed.
819 set_score_output(void)
825 * lflush() Flush the output buffer
827 * Returns nothing of value.
828 * for termcap version: Flush output in output buffer according to output
829 * status as indicated by `enable_scroll'
832 static int scrline = 18; /* line # for wraparound instead of scrolling if no DL */
842 if ((lpoint = lpnt - lpbuf) > 0) {
844 c[BYTESOUT] += lpoint;
846 if (enable_scroll <= -1) {
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 */
853 for (str = lpbuf; str < lpnt; str++) {
860 tputs(CL, 0, putchr);
865 tputs(CE, 0, putchr);
869 tputs(CD, 0, putchr);
873 tputs(SO, 0, putchr);
877 tputs(SE, 0, putchr);
883 tputs(tgoto(CM, curx, cury), 0, putchr);
887 if ((cury == 23) && enable_scroll) {
888 if (!DL || !AL) { /* wraparound or scroll? */
894 tputs(tgoto(CM, 0, scrline), 0, putchr);
895 tputs(CE, 0, putchr);
899 tputs(tgoto(CM, 0, scrline), 0, putchr);
900 tputs(CE, 0, putchr);
902 tputs(tgoto(CM, 0, 19), 0, putchr);
903 tputs(DL, 0, putchr);
904 tputs(tgoto(CM, 0, 23), 0, putchr);
920 flush_buf(); /* flush real output buffer now */
924 * lflush() flush the output buffer
926 * Returns nothing of value.
932 if ((lpoint = lpnt - lpbuf) > 0) {
934 c[BYTESOUT] += lpoint;
936 if (write(io_outfd, lpbuf, lpoint) != lpoint)
937 write(2, "error writing to output file\n", 29);
939 lpnt = lpbuf; /* point back to beginning of buffer */
944 static int pindex = 0;
946 * putchr(ch) Print one character in decoded output buffer.
951 outbuf[pindex++] = ch;
952 if (pindex >= BUFBIG)
958 * flush_buf() Flush buffer with decoded output.
964 write(io_outfd, outbuf, pindex);
969 * char *tmcapcnv(sd,ss) Routine to convert VT100 escapes to termcap format
971 * Processes only the \33[#m sequence (converts . files for termcap use
974 tmcapcnv(char *sd, char *ss)
976 int tmstate = 0; /* 0=normal, 1=\33 2=[ 3=# */
977 char tmdigit = 0; /* the # in \33[#m */
994 if (isdigit((int)*ss)) {
1017 *sd = 0; /* NULL terminator */
1023 * beep() Routine to emit a beep if enabled (see no-beep in .larnopts)