1 /* $NetBSD: mach.c,v 1.22 2021/05/02 12:50:43 rillig 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.22 2021/05/02 12:50:43 rillig 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
setup(int sflag,time_t seed)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
cleanup(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
results(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
prword(const char * const base[],int indx)153 prword(const char *const base[], int indx)
154 {
155 printw("%s", base[indx]);
156 }
157
158 static int
prwidth(const char * const base[],int indx)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 *
get_line(char * q)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 (NULL);
261 return(q);
262 }
263
264 int
inputch(void)265 inputch(void)
266 {
267 return (getch() & 0177);
268 }
269
270 void
redraw(void)271 redraw(void)
272 {
273 clearok(stdscr, 1);
274 refresh();
275 }
276
277 void
flushin(FILE * fp)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
stoptime(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
starttime(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
startwords(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
addword(const char * w)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
badword(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
showword(int n)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
findword(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
showstr(const char * str,int delaysecs)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
getword(char * q)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
showboard(const char * b)543 showboard(const char *b)
544 {
545 tty_showboard(b);
546 }
547
548 void
prompt(const char * mesg)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
tty_setup(void)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
stop_catcher(int signo __unused)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
cont_catcher(int signo __unused)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
winch_catcher(int signo __unused)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
tty_cleanup(void)630 tty_cleanup(void)
631 {
632 move(nlines - 1, 0);
633 refresh();
634 noraw();
635 echo();
636 endwin();
637 }
638
639 static void
tty_showboard(const char * b)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