1 /* $NetBSD: mach.c,v 1.11 2001/02/05 00:27:35 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Barry Brachman. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the University of 21 * California, Berkeley and its contributors. 22 * 4. Neither the name of the University nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 #ifndef lint 41 #if 0 42 static char sccsid[] = "@(#)mach.c 8.1 (Berkeley) 6/11/93"; 43 #else 44 __RCSID("$NetBSD: mach.c,v 1.11 2001/02/05 00:27:35 christos Exp $"); 45 #endif 46 #endif /* not lint */ 47 48 /* 49 * Terminal interface 50 * 51 * Input is raw and unechoed 52 */ 53 #include <sys/ioctl.h> 54 55 #include <ctype.h> 56 #include <curses.h> 57 #include <fcntl.h> 58 #include <signal.h> 59 #include <stdio.h> 60 #include <stdlib.h> 61 #include <string.h> 62 #include <termios.h> 63 #include <time.h> 64 65 #include "bog.h" 66 #include "extern.h" 67 68 static int ccol, crow, maxw; 69 static int colstarts[MAXCOLS], ncolstarts; 70 static int lastline; 71 int ncols, nlines; 72 73 extern const char *pword[], *mword[]; 74 extern int ngames, nmwords, npwords, tnmwords, tnpwords; 75 extern char board[]; 76 extern int usedbits, wordpath[]; 77 extern time_t start_t; 78 extern int debug; 79 80 static void cont_catcher __P((int)); 81 static int prwidth __P((const char *const [], int)); 82 static void prword __P((const char *const [], int)); 83 static void stop_catcher __P((int)); 84 static void tty_cleanup __P((void)); 85 static int tty_setup __P((void)); 86 static void tty_showboard __P((const char *)); 87 static void winch_catcher __P((int)); 88 89 /* 90 * Do system dependent initialization 91 * This is called once, when the program starts 92 */ 93 int 94 setup(sflag, seed) 95 int sflag; 96 time_t seed; 97 { 98 if (tty_setup() < 0) 99 return(-1); 100 101 if (!sflag) 102 time(&seed); 103 srandom(seed); 104 if (debug) 105 (void) printf("seed = %ld\n", (long) seed); 106 return(0); 107 } 108 109 /* 110 * Do system dependent clean up 111 * This is called once, just before the program terminates 112 */ 113 void 114 cleanup() 115 { 116 tty_cleanup(); 117 } 118 119 /* 120 * Display the player's word list, the list of words not found, and the running 121 * stats 122 */ 123 void 124 results() 125 { 126 int col, row; 127 int denom1, denom2; 128 129 move(LIST_LINE, LIST_COL); 130 clrtobot(); 131 printw("Words you found (%d):", npwords); 132 refresh(); 133 move(LIST_LINE + 1, LIST_COL); 134 prtable(pword, npwords, 0, ncols, prword, prwidth); 135 136 getyx(stdscr, row, col); 137 move(row + 1, col); 138 printw("Words you missed (%d):", nmwords); 139 refresh(); 140 move(row + 2, col); 141 prtable(mword, nmwords, 0, ncols, prword, prwidth); 142 143 denom1 = npwords + nmwords; 144 denom2 = tnpwords + tnmwords; 145 146 move(SCORE_LINE, SCORE_COL); 147 printw("Percentage: %0.2f%% (%0.2f%% over %d game%s)\n", 148 denom1 ? (100.0 * npwords) / (double) (npwords + nmwords) : 0.0, 149 denom2 ? (100.0 * tnpwords) / (double) (tnpwords + tnmwords) : 0.0, 150 ngames, ngames > 1 ? "s" : ""); 151 } 152 153 static void 154 prword(base, indx) 155 const char *const base[]; 156 int indx; 157 { 158 printw("%s", base[indx]); 159 } 160 161 static int 162 prwidth(base, indx) 163 const char *const base[]; 164 int indx; 165 { 166 return (strlen(base[indx])); 167 } 168 169 /* 170 * Main input routine 171 * 172 * - doesn't accept words longer than MAXWORDLEN or containing caps 173 */ 174 char * 175 getline(q) 176 char *q; 177 { 178 int ch, done; 179 char *p; 180 int row, col; 181 182 p = q; 183 done = 0; 184 while (!done) { 185 ch = timerch(); 186 switch (ch) { 187 case '\n': 188 case '\r': 189 case ' ': 190 done = 1; 191 break; 192 case '\033': 193 findword(); 194 break; 195 case '\177': /* <del> */ 196 case '\010': /* <bs> */ 197 if (p == q) 198 break; 199 p--; 200 getyx(stdscr, row, col); 201 move(row, col - 1); 202 clrtoeol(); 203 refresh(); 204 break; 205 case '\025': /* <^u> */ 206 case '\027': /* <^w> */ 207 if (p == q) 208 break; 209 getyx(stdscr, row, col); 210 move(row, col - (int) (p - q)); 211 p = q; 212 clrtoeol(); 213 refresh(); 214 break; 215 #ifdef SIGTSTP 216 case '\032': /* <^z> */ 217 stop_catcher(0); 218 break; 219 #endif 220 case '\023': /* <^s> */ 221 stoptime(); 222 printw("<PAUSE>"); 223 refresh(); 224 while ((ch = inputch()) != '\021' && ch != '\023') 225 ; 226 move(crow, ccol); 227 clrtoeol(); 228 refresh(); 229 starttime(); 230 break; 231 case '\003': /* <^c> */ 232 cleanup(); 233 exit(0); 234 /*NOTREACHED*/ 235 case '\004': /* <^d> */ 236 done = 1; 237 ch = EOF; 238 break; 239 case '\014': /* <^l> */ 240 case '\022': /* <^r> */ 241 redraw(); 242 break; 243 case '?': 244 stoptime(); 245 if (help() < 0) 246 showstr("Can't open help file", 1); 247 starttime(); 248 break; 249 default: 250 if (!islower(ch)) 251 break; 252 if ((int) (p - q) == MAXWORDLEN) { 253 p = q; 254 badword(); 255 break; 256 } 257 *p++ = ch; 258 addch(ch); 259 refresh(); 260 break; 261 } 262 } 263 *p = '\0'; 264 if (ch == EOF) 265 return((char *) NULL); 266 return(q); 267 } 268 269 int 270 inputch() 271 { 272 return (getch() & 0177); 273 } 274 275 void 276 redraw() 277 { 278 clearok(stdscr, 1); 279 refresh(); 280 } 281 282 void 283 flushin(fp) 284 FILE *fp; 285 { 286 287 (void) tcflush(fileno(fp), TCIFLUSH); 288 } 289 290 static int gone; 291 292 /* 293 * Stop the game timer 294 */ 295 void 296 stoptime() 297 { 298 time_t t; 299 300 (void)time(&t); 301 gone = (int) (t - start_t); 302 } 303 304 /* 305 * Restart the game timer 306 */ 307 void 308 starttime() 309 { 310 time_t t; 311 312 (void)time(&t); 313 start_t = t - (long) gone; 314 } 315 316 /* 317 * Initialize for the display of the player's words as they are typed 318 * This display starts at (LIST_LINE, LIST_COL) and goes "down" until the last 319 * line. After the last line a new column is started at LIST_LINE 320 * Keep track of each column position for showword() 321 * There is no check for exceeding COLS 322 */ 323 void 324 startwords() 325 { 326 crow = LIST_LINE; 327 ccol = LIST_COL; 328 maxw = 0; 329 ncolstarts = 1; 330 colstarts[0] = LIST_COL; 331 move(LIST_LINE, LIST_COL); 332 refresh(); 333 } 334 335 /* 336 * Add a word to the list and start a new column if necessary 337 * The maximum width of the current column is maintained so we know where 338 * to start the next column 339 */ 340 void 341 addword(w) 342 const char *w; 343 { 344 int n; 345 346 if (crow == lastline) { 347 crow = LIST_LINE; 348 ccol += (maxw + 5); 349 colstarts[ncolstarts++] = ccol; 350 maxw = 0; 351 move(crow, ccol); 352 } 353 else { 354 move(++crow, ccol); 355 if ((n = strlen(w)) > maxw) 356 maxw = n; 357 } 358 refresh(); 359 } 360 361 /* 362 * The current word is unacceptable so erase it 363 */ 364 void 365 badword() 366 { 367 368 move(crow, ccol); 369 clrtoeol(); 370 refresh(); 371 } 372 373 /* 374 * Highlight the nth word in the list (starting with word 0) 375 * No check for wild arg 376 */ 377 void 378 showword(n) 379 int n; 380 { 381 int col, row; 382 383 row = LIST_LINE + n % (lastline - LIST_LINE + 1); 384 col = colstarts[n / (lastline - LIST_LINE + 1)]; 385 move(row, col); 386 standout(); 387 printw("%s", pword[n]); 388 standend(); 389 move(crow, ccol); 390 refresh(); 391 delay(15); 392 move(row, col); 393 printw("%s", pword[n]); 394 move(crow, ccol); 395 refresh(); 396 } 397 398 /* 399 * Get a word from the user and check if it is in either of the two 400 * word lists 401 * If it's found, show the word on the board for a short time and then 402 * erase the word 403 * 404 * Note: this function knows about the format of the board 405 */ 406 void 407 findword() 408 { 409 int c, col, found, i, r, row; 410 char buf[MAXWORDLEN + 1]; 411 412 getyx(stdscr, r, c); 413 getword(buf); 414 found = 0; 415 for (i = 0; i < npwords; i++) { 416 if (strcmp(buf, pword[i]) == 0) { 417 found = 1; 418 break; 419 } 420 } 421 if (!found) { 422 for (i = 0; i < nmwords; i++) { 423 if (strcmp(buf, mword[i]) == 0) { 424 found = 1; 425 break; 426 } 427 } 428 } 429 for (i = 0; i < MAXWORDLEN; i++) 430 wordpath[i] = -1; 431 usedbits = 0; 432 if (!found || checkword(buf, -1, wordpath) == -1) { 433 move(r, c); 434 clrtoeol(); 435 addstr("[???]"); 436 refresh(); 437 delay(10); 438 move(r, c); 439 clrtoeol(); 440 refresh(); 441 return; 442 } 443 444 standout(); 445 for (i = 0; wordpath[i] != -1; i++) { 446 row = BOARD_LINE + (wordpath[i] / 4) * 2 + 1; 447 col = BOARD_COL + (wordpath[i] % 4) * 4 + 2; 448 move(row, col); 449 if (board[wordpath[i]] == 'q') 450 printw("Qu"); 451 else 452 printw("%c", toupper(board[wordpath[i]])); 453 move(r, c); 454 refresh(); 455 delay(5); 456 } 457 458 standend(); 459 460 for (i = 0; wordpath[i] != -1; i++) { 461 row = BOARD_LINE + (wordpath[i] / 4) * 2 + 1; 462 col = BOARD_COL + (wordpath[i] % 4) * 4 + 2; 463 move(row, col); 464 if (board[wordpath[i]] == 'q') 465 printw("Qu"); 466 else 467 printw("%c", toupper(board[wordpath[i]])); 468 } 469 move(r, c); 470 clrtoeol(); 471 refresh(); 472 } 473 474 /* 475 * Display a string at the current cursor position for the given number of secs 476 */ 477 void 478 showstr(str, delaysecs) 479 const char *str; 480 int delaysecs; 481 { 482 addstr(str); 483 refresh(); 484 delay(delaysecs * 10); 485 move(crow, ccol); 486 clrtoeol(); 487 refresh(); 488 } 489 490 void 491 putstr(s) 492 const char *s; 493 { 494 addstr(s); 495 } 496 497 /* 498 * Get a valid word and put it in the buffer 499 */ 500 void 501 getword(q) 502 char *q; 503 { 504 int ch, col, done, i, row; 505 char *p; 506 507 done = 0; 508 i = 0; 509 p = q; 510 addch('['); 511 refresh(); 512 while (!done && i < MAXWORDLEN - 1) { 513 ch = getch() & 0177; 514 switch (ch) { 515 case '\177': /* <del> */ 516 case '\010': /* <bs> */ 517 if (p == q) 518 break; 519 p--; 520 getyx(stdscr, row, col); 521 move(row, col - 1); 522 clrtoeol(); 523 break; 524 case '\025': /* <^u> */ 525 case '\027': /* <^w> */ 526 if (p == q) 527 break; 528 getyx(stdscr, row, col); 529 move(row, col - (int) (p - q)); 530 p = q; 531 clrtoeol(); 532 break; 533 case ' ': 534 case '\n': 535 case '\r': 536 done = 1; 537 break; 538 case '\014': /* <^l> */ 539 case '\022': /* <^r> */ 540 clearok(stdscr, 1); 541 refresh(); 542 break; 543 default: 544 if (islower(ch)) { 545 *p++ = ch; 546 addch(ch); 547 i++; 548 } 549 break; 550 } 551 refresh(); 552 } 553 *p = '\0'; 554 addch(']'); 555 refresh(); 556 } 557 558 void 559 showboard(b) 560 const char *b; 561 { 562 tty_showboard(b); 563 } 564 565 void 566 prompt(mesg) 567 const char *mesg; 568 { 569 move(PROMPT_LINE, PROMPT_COL); 570 printw("%s", mesg); 571 move(PROMPT_LINE + 1, PROMPT_COL); 572 refresh(); 573 } 574 575 static int 576 tty_setup() 577 { 578 initscr(); 579 raw(); 580 noecho(); 581 582 /* 583 * Does curses look at the winsize structure? 584 * Should handle SIGWINCH ... 585 */ 586 nlines = LINES; 587 lastline = nlines - 1; 588 ncols = COLS; 589 590 signal(SIGTSTP, stop_catcher); 591 signal(SIGCONT, cont_catcher); 592 signal(SIGWINCH, winch_catcher); 593 return(0); 594 } 595 596 static void 597 stop_catcher(signo) 598 int signo __attribute__((__unused__)); 599 { 600 sigset_t sigset, osigset; 601 602 stoptime(); 603 noraw(); 604 echo(); 605 move(nlines - 1, 0); 606 refresh(); 607 608 signal(SIGTSTP, SIG_DFL); 609 sigemptyset(&sigset); 610 sigaddset(&sigset, SIGTSTP); 611 sigprocmask(SIG_UNBLOCK, &sigset, &osigset); 612 kill(0, SIGTSTP); 613 sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0); 614 signal(SIGTSTP, stop_catcher); 615 } 616 617 static void 618 cont_catcher(signo) 619 int signo __attribute__((__unused__)); 620 { 621 noecho(); 622 raw(); 623 clearok(stdscr, 1); 624 move(crow, ccol); 625 refresh(); 626 starttime(); 627 } 628 629 /* 630 * The signal is caught but nothing is done about it... 631 * It would mean reformatting the entire display 632 */ 633 static void 634 winch_catcher(signo) 635 int signo __attribute__((__unused__)); 636 { 637 struct winsize win; 638 639 (void) signal(SIGWINCH, winch_catcher); 640 (void) ioctl(fileno(stdout), TIOCGWINSZ, &win); 641 /* 642 LINES = win.ws_row; 643 COLS = win.ws_col; 644 */ 645 } 646 647 static void 648 tty_cleanup() 649 { 650 move(nlines - 1, 0); 651 refresh(); 652 noraw(); 653 echo(); 654 endwin(); 655 } 656 657 static void 658 tty_showboard(b) 659 const char *b; 660 { 661 int i; 662 int line; 663 664 clear(); 665 move(BOARD_LINE, BOARD_COL); 666 line = BOARD_LINE; 667 printw("+---+---+---+---+"); 668 move(++line, BOARD_COL); 669 for (i = 0; i < 16; i++) { 670 if (b[i] == 'q') 671 printw("| Qu"); 672 else 673 printw("| %c ", toupper(b[i])); 674 if ((i + 1) % 4 == 0) { 675 printw("|"); 676 move(++line, BOARD_COL); 677 printw("+---+---+---+---+"); 678 move(++line, BOARD_COL); 679 } 680 } 681 move(SCORE_LINE, SCORE_COL); 682 printw("Type '?' for help"); 683 refresh(); 684 } 685