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 * getchar() 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() 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 ** putchar(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.
61 #ifdef SYSV /* system III or system V */
64 #define stty(_a,_b) ioctl(_a,TCSETA,_b)
65 #define gtty(_a,_b) ioctl(_a,TCGETA,_b)
66 static int rawflg = 0;
67 static char saveeof,saveeol;
68 #define doraw(_a) if(!rawflg){++rawflg;saveeof=_a.c_cc[VMIN];saveeol=_a.c_cc[VTIME];}\
69 _a.c_cc[VMIN]=1;_a.c_cc[VTIME]=1;_a.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL)
70 #define unraw(_a) _a.c_cc[VMIN]=saveeof;_a.c_cc[VTIME]=saveeol;_a.c_lflag |= ICANON|ECHO|ECHOE|ECHOK|ECHONL
75 #define CBREAK RAW /* V7 has no CBREAK */
78 #define doraw(_a) (_a.sg_flags |= CBREAK,_a.sg_flags &= ~ECHO)
79 #define unraw(_a) (_a.sg_flags &= ~CBREAK,_a.sg_flags |= ECHO)
83 #ifndef NOVARARGS /* if we have varargs */
85 #else NOVARARGS /* if we don't have varargs */
86 typedef char *va_list;
87 #define va_dcl int va_alist;
88 #define va_start(plist) plist = (char *) &va_alist
90 #define va_arg(plist,mode) ((mode *)(plist += sizeof(mode)))[-1]
93 #define LINBUFSIZE 128 /* size of the lgetw() and lgetl() buffer */
94 int lfd; /* output file numbers */
95 int fd; /* input file numbers */
96 static struct sgttyb ttx; /* storage for the tty modes */
97 static int ipoint=MAXIBUF,iepoint=MAXIBUF; /* input buffering pointers */
98 static char lgetwbuf[LINBUFSIZE]; /* get line (word) buffer */
101 * setupvt100() Subroutine to set up terminal in correct mode for game
103 * Attributes off, clear screen, set scrolling region, set tty mode
107 clear(); setscroll(); scbr(); /* system("stty cbreak -echo"); */
111 * clearvt100() Subroutine to clean up terminal when the game is over
113 * Attributes off, clear screen, unset scrolling region, restore tty mode
117 resetscroll(); clear(); sncbr(); /* system("stty -cbreak echo"); */
121 * getchar() Routine to read in one character from the terminal
129 lflush(); /* be sure output buffer is flushed */
130 read(0,&byt,1); /* get byte from terminal */
135 * scbr() Function to set cbreak -echo for the terminal
137 * like: system("stty cbreak -echo")
141 gtty(0,&ttx); doraw(ttx); stty(0,&ttx);
145 * sncbr() Function to set -cbreak echo for the terminal
147 * like: system("stty -cbreak echo")
151 gtty(0,&ttx); unraw(ttx); stty(0,&ttx);
155 * newgame() Subroutine to save the initial time and seed rnd()
160 for (p=c,pe=c+100; p<pe; *p++ =0);
161 time(&initialtime); srandomdev();
162 lcreat((char*)0); /* open buffering for output to terminal */
166 * lprintf(format,args . . .) printf to the output buffer
170 * Enter with the format string in "format", as per printf() usage
171 * and any needed arguments following it
172 * Note: lprintf() only supports %s, %c and %d, with width modifier and left
173 * or right justification.
174 * No correct checking for output buffer overflow is done, but flushes
175 * are done beforehand if needed.
176 * Returns nothing of value.
185 str = str2; /* to make lint happy */
193 str = str2; /* to make lint happy */
200 va_list ap; /* pointer for variable argument list */
203 long wide,left,cont,n; /* data for lprintf */
204 char db[12]; /* %d buffer in lprintf */
206 va_start(ap); /* initialize the var args pointer */
207 fmt = va_arg(ap, char *); /* pointer to format string */
208 if (lpnt >= lpend) lflush();
213 if (*fmt) *outb++ = *fmt++; else { lpnt=outb; return; }
214 wide = 0; left = 1; cont=1;
218 case 'd': n = va_arg(ap, long);
219 if (n<0) { n = -n; *outb++ = '-'; if (wide) --wide; }
220 tmpb = db+11; *tmpb = (char)(n % 10 + '0');
221 while (n>9) *(--tmpb) = (char)((n /= 10) % 10 + '0');
222 if (wide==0) while (tmpb < db+12) *outb++ = *tmpb++;
226 if (left) while (wide-- > 0) *outb++ = ' ';
227 while (tmpb < db+12) *outb++ = *tmpb++;
228 if (left==0) while (wide-- > 0) *outb++ = ' ';
232 case 's': tmpb = va_arg(ap, char *);
233 if (wide==0) { while (*outb++ = *tmpb++); --outb; }
236 n = wide - strlen(tmpb);
237 if (left) while (n-- > 0) *outb++ = ' ';
238 while (*outb++ = *tmpb++); --outb;
239 if (left==0) while (n-- > 0) *outb++ = ' ';
243 case 'c': *outb++ = va_arg(ap, int); cont=0; break;
254 case '9': wide = 10*wide + *fmt - '0'; break;
256 case '-': left = 0; break;
258 default: *outb++ = *fmt; cont=0; break;
267 * lprint(long-integer) send binary integer to output buffer
270 * +---------+---------+---------+---------+
272 * | order | | | order |
273 * | byte | | | byte |
274 * +---------+---------+---------+---------+
275 * 31 --- 24 23 --- 16 15 --- 8 7 --- 0
277 * The save order is low order first, to high order (4 bytes total)
278 * and is written to be system independent.
279 * No checking for output buffer overflow is done, but flushes if needed!
280 * Returns nothing of value.
285 if (lpnt >= lpend) lflush();
286 *lpnt++ = 255 & x; *lpnt++ = 255 & (x>>8);
287 *lpnt++ = 255 & (x>>16); *lpnt++ = 255 & (x>>24);
291 * lwrite(buf,len) write a buffer to the output buffer
295 * Enter with the address and number of bytes to write out
296 * Returns nothing of value
304 if (len > 399) /* don't copy data if can just write it */
311 for (str=buf; len>0; --len)
320 if (lpnt >= lpend) lflush(); /* if buffer is full flush it */
321 num2 = lpbuf+BUFBIG-lpnt; /* # bytes left in output buffer */
322 if (num2 > len) num2=len;
323 str = lpnt; len -= num2;
324 while (num2--) *str++ = *buf++; /* copy in the bytes */
330 * long lgetc() Read one character from input buffer
332 * Returns 0 if EOF, otherwise the character
337 if (ipoint != iepoint) return(inbuffer[ipoint++]);
338 if (iepoint!=MAXIBUF) return(0);
339 if ((i=read(fd,inbuffer,MAXIBUF))<=0)
341 if (i!=0) write(1,"error reading from input file\n",30);
342 iepoint = ipoint = 0; return(0);
344 ipoint=1; iepoint=i; return(*inbuffer);
348 * long lrint() Read one integer from input buffer
350 * +---------+---------+---------+---------+
352 * | order | | | order |
353 * | byte | | | byte |
354 * +---------+---------+---------+---------+
355 * 31 --- 24 23 --- 16 15 --- 8 7 --- 0
357 * The save order is low order first, to high order (4 bytes total)
358 * Returns the int read
363 i = 255 & lgetc(); i |= (255 & lgetc()) << 8;
364 i |= (255 & lgetc()) << 16; i |= (255 & lgetc()) << 24;
369 * lrfill(address,number) put input bytes into a buffer
373 * Reads "number" bytes into the buffer pointed to by "address".
374 * Returns nothing of value
384 if (iepoint == ipoint)
386 if (num>5) /* fast way */
388 if (read(fd,adr,num) != num)
389 write(2,"error reading from input file\n",30);
392 else { *adr++ = lgetc(); --num; }
396 num2 = iepoint-ipoint; /* # of bytes left in the buffer */
397 if (num2 > num) num2=num;
398 pnt = inbuffer+ipoint; num -= num2; ipoint += num2;
399 while (num2--) *adr++ = *pnt++;
405 * char *lgetw() Get a whitespace ended word from input
407 * Returns pointer to a buffer that contains word. If EOF, returns a NULL
412 int n=LINBUFSIZE,quote=0;
414 do cc=lgetc(); while ((cc <= 32) && (cc > NULL)); /* eat whitespace */
415 for ( ; ; --n,cc=lgetc())
417 if ((cc==NULL) && (lgp==lgetwbuf)) return(NULL); /* EOF */
418 if ((n<=1) || ((cc<=32) && (quote==0))) { *lgp=NULL; return(lgetwbuf); }
419 if (cc != '"') *lgp++ = cc; else quote ^= 1;
424 * char *lgetl() Function to read in a line ended by newline or EOF
426 * Returns pointer to a buffer that contains the line. If EOF, returns NULL
434 if ((*str++ = ch = lgetc()) == NULL)
436 if (str == lgetwbuf+1) return(NULL); /* EOF */
437 ot: *str = NULL; return(lgetwbuf); /* line ended by EOF */
439 if ((ch=='\n') || (i<=1)) goto ot; /* line ended by \n */
444 * lcreat(filename) Create a new file for write
447 * lcreat((char*)0); means to the terminal
448 * Returns -1 if error, otherwise the file descriptor opened.
453 lpnt = lpbuf; lpend = lpbuf+BUFBIG;
454 if (str==NULL) return(lfd=1);
455 if ((lfd=creat(str,0644)) < 0)
457 lfd=1; lprintf("error creating file <%s>\n",str); lflush(); return(-1);
463 * lopen(filename) Open a file for read
466 * lopen(0) means from the terminal
467 * Returns -1 if error, otherwise the file descriptor opened.
472 ipoint = iepoint = MAXIBUF;
473 if (str==NULL) return(fd=0);
474 if ((fd=open(str,0)) < 0)
476 lwclose(); lfd=1; lpnt=lpbuf; return(-1);
482 * lappend(filename) Open for append to an existing file
485 * lappend(0) means to the terminal
486 * Returns -1 if error, otherwise the file descriptor opened.
491 lpnt = lpbuf; lpend = lpbuf+BUFBIG;
492 if (str==NULL) return(lfd=1);
493 if ((lfd=open(str,2)) < 0)
497 lseek(lfd,0,2); /* seek to end of file */
502 * lrclose() close the input file
504 * Returns nothing of value.
508 if (fd > 0) close(fd);
512 * lwclose() close output file flushing if needed
514 * Returns nothing of value.
518 lflush(); if (lfd > 2) close(lfd);
522 * lprcat(string) append a string to the output buffer
523 * avoids calls to lprintf (time consuming)
529 if (lpnt >= lpend) lflush();
531 while (*str2++ = *str++);
537 * cursor(x,y) Subroutine to set the cursor position
539 * x and y are the cursor coordinates, and lpbuff is the output buffer where
540 * escape sequence will be placed.
542 static char *y_num[]= { "\33[","\33[","\33[2","\33[3","\33[4","\33[5","\33[6",
543 "\33[7","\33[8","\33[9","\33[10","\33[11","\33[12","\33[13","\33[14",
544 "\33[15","\33[16","\33[17","\33[18","\33[19","\33[20","\33[21","\33[22",
547 static char *x_num[]= { "H","H",";2H",";3H",";4H",";5H",";6H",";7H",";8H",";9H",
548 ";10H",";11H",";12H",";13H",";14H",";15H",";16H",";17H",";18H",";19H",
549 ";20H",";21H",";22H",";23H",";24H",";25H",";26H",";27H",";28H",";29H",
550 ";30H",";31H",";32H",";33H",";34H",";35H",";36H",";37H",";38H",";39H",
551 ";40H",";41H",";42H",";43H",";44H",";45H",";46H",";47H",";48H",";49H",
552 ";50H",";51H",";52H",";53H",";54H",";55H",";56H",";57H",";58H",";59H",
553 ";60H",";61H",";62H",";63H",";64H",";65H",";66H",";67H",";68H",";69H",
554 ";70H",";71H",";72H",";73H",";74H",";75H",";76H",";77H",";78H",";79H",
561 if (lpnt >= lpend) lflush();
563 p = y_num[y]; /* get the string to print */
564 while (*p) *lpnt++ = *p++; /* print the string */
566 p = x_num[x]; /* get the string to print */
567 while (*p) *lpnt++ = *p++; /* print the string */
571 * cursor(x,y) Put cursor at specified coordinates staring at [1,1] (termcap)
576 if (lpnt >= lpend) lflush ();
578 *lpnt++ = CURSOR; *lpnt++ = x; *lpnt++ = y;
583 * Routine to position cursor at beginning of 24th line
592 * Warning: ringing the bell is control code 7. Don't use in defines.
593 * Don't change the order of these defines.
594 * Also used in helpfiles. Codes used in helpfiles should be \E[1 to \E[7 with
598 static char cap[256];
599 char *CM, *CE, *CD, *CL, *SO, *SE, *AL, *DL;/* Termcap capabilities */
600 static char *outbuf=0; /* translated output buffer */
605 * init_term() Terminal initialization -- setup termcap info
610 char *capptr = cap+10;
614 switch (tgetent(termbuf, term = getenv("TERM")))
617 write(2, "Cannot open termcap file.\n", 26); exit(1);
619 write(2, "Cannot find entry of ", 21);
620 write(2, term, strlen (term));
621 write(2, " in termcap\n", 12);
625 if (gtty(0, &tt) == 0)
626 /* ospeed = tt.sg_ospeed */ ;
628 CM = tgetstr("cm", &capptr); /* Cursor motion */
629 CE = tgetstr("ce", &capptr); /* Clear to eoln */
630 CL = tgetstr("cl", &capptr); /* Clear screen */
633 AL = tgetstr("al", &capptr); /* Insert line */
634 DL = tgetstr("dl", &capptr); /* Delete line */
635 SO = tgetstr("so", &capptr); /* Begin standout mode */
636 SE = tgetstr("se", &capptr); /* End standout mode */
637 CD = tgetstr("cd", &capptr); /* Clear to end of display */
639 if (!CM) /* can't find cursor motion entry */
641 write(2, "Sorry, for a ",13); write(2, term, strlen(term));
642 write(2, ", I can't find the cursor motion entry in termcap\n",50);
645 if (!CE) /* can't find clear to end of line entry */
647 write(2, "Sorry, for a ",13); write(2, term, strlen(term));
648 write(2,", I can't find the clear to end of line entry in termcap\n",57);
651 if (!CL) /* can't find clear entire screen entry */
653 write(2, "Sorry, for a ",13); write(2, term, strlen(term));
654 write(2, ", I can't find the clear entire screen entry in termcap\n",56);
657 if ((outbuf=malloc(BUFBIG+16))==0) /* get memory for decoded output buffer*/
659 write(2,"Error malloc'ing memory for decoded output buffer\n",50);
660 died(-285); /* malloc() failure */
666 * cl_line(x,y) Clear the whole line indicated by 'y' and leave cursor at [x,y]
672 cursor(x,y); lprcat("\33[2K");
674 cursor(1,y); *lpnt++ = CL_LINE; cursor(x,y);
679 * cl_up(x,y) Clear screen from [x,1] to current position. Leave cursor at [x,y]
685 cursor(x,y); lprcat("\33[1J\33[2K");
689 for (i=1; i<=y; i++) { *lpnt++ = CL_LINE; *lpnt++ = '\n'; }
695 * cl_dn(x,y) Clear screen from [1,y] to end of display. Leave cursor at [x,y]
701 cursor(x,y); lprcat("\33[J\33[2K");
708 for (i=y; i<=24; i++) { *lpnt++ = CL_LINE; if (i!=24) *lpnt++ = '\n'; }
718 * standout(str) Print the argument string in inverse video (standout mode).
737 * set_score_output() Called when output should be literally printed.
745 * lflush() Flush the output buffer
747 * Returns nothing of value.
748 * for termcap version: Flush output in output buffer according to output
749 * status as indicated by `enable_scroll'
752 static int scrline=18; /* line # for wraparound instead of scrolling if no DL */
760 if ((lpoint = lpnt - lpbuf) > 0)
763 c[BYTESOUT] += lpoint;
765 if (enable_scroll <= -1)
768 if (write(lfd,lpbuf,lpoint) != lpoint)
769 write(2,"error writing to output file\n",29);
770 lpnt = lpbuf; /* point back to beginning of buffer */
773 for (str = lpbuf; str < lpnt; str++)
775 if (*str>=32) { putchar (*str); curx++; }
778 case CLEAR: tputs (CL, 0, putchar); curx = cury = 0;
781 case CL_LINE: tputs (CE, 0, putchar);
784 case CL_DOWN: tputs (CD, 0, putchar);
787 case ST_START: tputs (SO, 0, putchar);
790 case ST_END: tputs (SE, 0, putchar);
793 case CURSOR: curx = *++str - 1; cury = *++str - 1;
794 tputs (tgoto (CM, curx, cury), 0, putchar);
797 case '\n': if ((cury == 23) && enable_scroll)
799 if (!DL || !AL) /* wraparound or scroll? */
801 if (++scrline > 23) scrline=19;
803 if (++scrline > 23) scrline=19;
804 tputs (tgoto (CM, 0, scrline), 0, putchar);
805 tputs (CE, 0, putchar);
807 if (--scrline < 19) scrline=23;
808 tputs (tgoto (CM, 0, scrline), 0, putchar);
809 tputs (CE, 0, putchar);
813 tputs (tgoto (CM, 0, 19), 0, putchar);
814 tputs (DL, 0, putchar);
815 tputs (tgoto (CM, 0, 23), 0, putchar);
816 /* tputs (AL, 0, putchar); */
821 putchar ('\n'); cury++;
826 default: putchar (*str); curx++;
831 flush_buf(); /* flush real output buffer now */
835 * lflush() flush the output buffer
837 * Returns nothing of value.
842 if ((lpoint = lpnt - lpbuf) > 0)
845 c[BYTESOUT] += lpoint;
847 if (write(lfd,lpbuf,lpoint) != lpoint)
848 write(2,"error writing to output file\n",29);
850 lpnt = lpbuf; /* point back to beginning of buffer */
857 * putchar(ch) Print one character in decoded output buffer.
862 outbuf[pindex++] = c;
863 if (pindex >= BUFBIG) flush_buf();
867 * flush_buf() Flush buffer with decoded output.
871 if (pindex) write(lfd, outbuf, pindex);
876 * char *tmcapcnv(sd,ss) Routine to convert VT100 escapes to termcap format
878 * Processes only the \33[#m sequence (converts . files for termcap use
880 char *tmcapcnv(sd,ss)
883 int tmstate=0; /* 0=normal, 1=\33 2=[ 3=# */
884 char tmdigit=0; /* the # in \33[#m */
889 case 0: if (*ss=='\33') { tmstate++; break; }
893 case 1: if (*ss!='[') goto ign;
896 case 2: if (isdigit(*ss)) { tmdigit= *ss-'0'; tmstate++; break; }
897 if (*ss == 'm') { *sd++ = ST_END; goto ign2; }
899 case 3: if (*ss == 'm')
901 if (tmdigit) *sd++ = ST_START;
909 *sd=0; /* NULL terminator */
915 * beep() Routine to emit a beep if enabled (see no-beep in .larnopts)
919 if (!nobeep) *lpnt++ = '\7';