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