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