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