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 38static 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 64static int ccol, crow, maxw; 65static int colstarts[MAXCOLS], ncolstarts; 66static int lastline; 67static int ncols; 68int nlines; 69 70extern const char *pword[], *mword[]; 71extern int ngames, nmwords, npwords, tnmwords, tnpwords; 72extern char board[]; 73extern int usedbits, wordpath[]; 74extern time_t start_t; 75extern int debug; 76 77static void cont_catcher(int); 78static int prwidth(const char *const [], int); 79static void prword(const char *const [], int); 80static void stop_catcher(int); 81static void tty_cleanup(void); 82static int tty_setup(void); 83static void tty_showboard(const char *); 84static void winch_catcher(int); 85static void getword(char *); 86static void starttime(void); 87static void stoptime(void); 88 89 90/* 91 * Do system dependent initialization 92 * This is called once, when the program starts 93 */ 94int 95setup(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 */ 112void 113cleanup(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 */ 122void 123results(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 152static void 153prword(const char *const base[], int indx) 154{ 155 printw("%s", base[indx]); 156} 157 158static int 159prwidth(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 */ 169char * 170get_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 (NULL); 261 return(q); 262} 263 264int 265inputch(void) 266{ 267 return (getch() & 0177); 268} 269 270void 271redraw(void) 272{ 273 clearok(stdscr, 1); 274 refresh(); 275} 276 277void 278flushin(FILE *fp) 279{ 280 281 (void) tcflush(fileno(fp), TCIFLUSH); 282} 283 284static int gone; 285 286/* 287 * Stop the game timer 288 */ 289static void 290stoptime(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 */ 301static void 302starttime(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 */ 317void 318startwords(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 */ 334void 335addword(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 */ 357void 358badword(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 */ 370void 371showword(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 */ 398void 399findword(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 */ 471void 472showstr(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 */ 485static void 486getword(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 542void 543showboard(const char *b) 544{ 545 tty_showboard(b); 546} 547 548void 549prompt(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 557static int 558tty_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 581static void 582stop_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 601static void 602cont_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 */ 616static void 617winch_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 629static void 630tty_cleanup(void) 631{ 632 move(nlines - 1, 0); 633 refresh(); 634 noraw(); 635 echo(); 636 endwin(); 637} 638 639static void 640tty_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