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