]> git.cameronkatri.com Git - bsdgames-darwin.git/blob - cribbage/io.c
set the cursor to invisible
[bsdgames-darwin.git] / cribbage / io.c
1 /* $NetBSD: io.c,v 1.27 2012/10/13 20:36:06 dholland Exp $ */
2
3 /*-
4 * Copyright (c) 1980, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)io.c 8.1 (Berkeley) 5/31/93";
36 #else
37 __RCSID("$NetBSD: io.c,v 1.27 2012/10/13 20:36:06 dholland Exp $");
38 #endif
39 #endif /* not lint */
40
41 #include <ctype.h>
42 #include <curses.h>
43 #include <signal.h>
44 #include <stdarg.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <termios.h>
48 #include <unistd.h>
49
50 #include "deck.h"
51 #include "cribbage.h"
52 #include "cribcur.h"
53
54 #define LINESIZE 128
55
56 #ifdef CTRL
57 #undef CTRL
58 #endif
59 #define CTRL(X) (X - 'A' + 1)
60
61 static int msgcrd(CARD, BOOLEAN, const char *, BOOLEAN);
62 static void printcard(WINDOW *, unsigned, CARD, BOOLEAN);
63 static int incard(CARD *);
64 static void wait_for(int);
65 static int readchar(void);
66
67 static char linebuf[LINESIZE];
68
69 static const char *const rankname[RANKS] = {
70 "ACE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN",
71 "EIGHT", "NINE", "TEN", "JACK", "QUEEN", "KING"
72 };
73
74 static const char *const rankchar[RANKS] = {
75 "A", "2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K"
76 };
77
78 static const char *const suitname[SUITS] = {
79 "SPADES", "HEARTS", "DIAMONDS", "CLUBS"
80 };
81
82 static const char *const suitchar[SUITS] = {"S", "H", "D", "C"};
83
84 /*
85 * msgcard:
86 * Call msgcrd in one of two forms
87 */
88 int
89 msgcard(CARD c, BOOLEAN brief)
90 {
91 if (brief)
92 return (msgcrd(c, TRUE, NULL, TRUE));
93 else
94 return (msgcrd(c, FALSE, " of ", FALSE));
95 }
96
97 /*
98 * msgcrd:
99 * Print the value of a card in ascii
100 */
101 static int
102 msgcrd(CARD c, BOOLEAN brfrank, const char *mid, BOOLEAN brfsuit)
103 {
104 if (c.rank == EMPTY || c.suit == EMPTY)
105 return (FALSE);
106 if (brfrank)
107 addmsg("%1.1s", rankchar[c.rank]);
108 else
109 addmsg("%s", rankname[c.rank]);
110 if (mid != NULL)
111 addmsg("%s", mid);
112 if (brfsuit)
113 addmsg("%1.1s", suitchar[c.suit]);
114 else
115 addmsg("%s", suitname[c.suit]);
116 return (TRUE);
117 }
118
119 /*
120 * printcard:
121 * Print out a card.
122 */
123 static void
124 printcard(WINDOW *win, unsigned cardno, CARD c, BOOLEAN blank)
125 {
126 prcard(win, cardno * 2, cardno, c, blank);
127 }
128
129 /*
130 * prcard:
131 * Print out a card on the window at the specified location
132 */
133 void
134 prcard(WINDOW *win, int y, int x, CARD c, BOOLEAN blank)
135 {
136 if (c.rank == EMPTY)
137 return;
138
139 mvwaddstr(win, y + 0, x, "+-----+");
140 mvwaddstr(win, y + 1, x, "| |");
141 mvwaddstr(win, y + 2, x, "| |");
142 mvwaddstr(win, y + 3, x, "| |");
143 mvwaddstr(win, y + 4, x, "+-----+");
144 if (!blank) {
145 mvwaddch(win, y + 1, x + 1, rankchar[c.rank][0]);
146 waddch(win, suitchar[c.suit][0]);
147 mvwaddch(win, y + 3, x + 4, rankchar[c.rank][0]);
148 waddch(win, suitchar[c.suit][0]);
149 }
150 }
151
152 /*
153 * prhand:
154 * Print a hand of n cards
155 */
156 void
157 prhand(const CARD h[], unsigned n, WINDOW *win, BOOLEAN blank)
158 {
159 unsigned i;
160
161 werase(win);
162 for (i = 0; i < n; i++)
163 printcard(win, i, *h++, blank);
164 wrefresh(win);
165 }
166
167 /*
168 * infrom:
169 * reads a card, supposedly in hand, accepting unambigous brief
170 * input, returns the index of the card found...
171 */
172 int
173 infrom(const CARD hand[], int n, const char *prompt)
174 {
175 int i, j;
176 CARD crd;
177
178 if (n < 1) {
179 printf("\nINFROM: %d = n < 1!!\n", n);
180 exit(74);
181 }
182 for (;;) {
183 msg("%s", prompt);
184 if (incard(&crd)) { /* if card is full card */
185 if (!is_one(crd, hand, n))
186 msg("That's not in your hand");
187 else {
188 for (i = 0; i < n; i++)
189 if (hand[i].rank == crd.rank &&
190 hand[i].suit == crd.suit)
191 break;
192 if (i >= n) {
193 printf("\nINFROM: is_one or something messed up\n");
194 exit(77);
195 }
196 return (i);
197 }
198 } else /* if not full card... */
199 if (crd.rank != EMPTY) {
200 for (i = 0; i < n; i++)
201 if (hand[i].rank == crd.rank)
202 break;
203 if (i >= n)
204 msg("No such rank in your hand");
205 else {
206 for (j = i + 1; j < n; j++)
207 if (hand[j].rank == crd.rank)
208 break;
209 if (j < n)
210 msg("Ambiguous rank");
211 else
212 return (i);
213 }
214 } else
215 msg("Sorry, I missed that");
216 }
217 /* NOTREACHED */
218 }
219
220 /*
221 * incard:
222 * Inputs a card in any format. It reads a line ending with a CR
223 * and then parses it.
224 */
225 static int
226 incard(CARD *crd)
227 {
228 int i;
229 int rnk, sut;
230 char *line, *p, *p1;
231 BOOLEAN retval;
232
233 retval = FALSE;
234 rnk = sut = EMPTY;
235 if (!(line = get_line()))
236 goto gotit;
237 p = p1 = line;
238 while (*p1 != ' ' && *p1 != '\0')
239 ++p1;
240 *p1++ = '\0';
241 if (*p == '\0')
242 goto gotit;
243
244 /* IMPORTANT: no real card has 2 char first name */
245 if (strlen(p) == 2) { /* check for short form */
246 rnk = EMPTY;
247 for (i = 0; i < RANKS; i++) {
248 if (*p == *rankchar[i]) {
249 rnk = i;
250 break;
251 }
252 }
253 if (rnk == EMPTY)
254 goto gotit; /* it's nothing... */
255 ++p; /* advance to next char */
256 sut = EMPTY;
257 for (i = 0; i < SUITS; i++) {
258 if (*p == *suitchar[i]) {
259 sut = i;
260 break;
261 }
262 }
263 if (sut != EMPTY)
264 retval = TRUE;
265 goto gotit;
266 }
267 rnk = EMPTY;
268 for (i = 0; i < RANKS; i++) {
269 if (!strcmp(p, rankname[i]) || !strcmp(p, rankchar[i])) {
270 rnk = i;
271 break;
272 }
273 }
274 if (rnk == EMPTY)
275 goto gotit;
276 p = p1;
277 while (*p1 != ' ' && *p1 != '\0')
278 ++p1;
279 *p1++ = '\0';
280 if (*p == '\0')
281 goto gotit;
282 if (!strcmp("OF", p)) {
283 p = p1;
284 while (*p1 != ' ' && *p1 != '\0')
285 ++p1;
286 *p1++ = '\0';
287 if (*p == '\0')
288 goto gotit;
289 }
290 sut = EMPTY;
291 for (i = 0; i < SUITS; i++) {
292 if (!strcmp(p, suitname[i]) || !strcmp(p, suitchar[i])) {
293 sut = i;
294 break;
295 }
296 }
297 if (sut != EMPTY)
298 retval = TRUE;
299 gotit:
300 (*crd).rank = rnk;
301 (*crd).suit = sut;
302 return (retval);
303 }
304
305 /*
306 * getuchar:
307 * Reads and converts to upper case
308 */
309 int
310 getuchar(void)
311 {
312 int c;
313
314 c = readchar();
315 if (islower(c))
316 c = toupper(c);
317 waddch(Msgwin, c);
318 return (c);
319 }
320
321 /*
322 * number:
323 * Reads in a decimal number and makes sure it is between "lo" and
324 * "hi" inclusive.
325 */
326 int
327 number(int lo, int hi, const char *prompt)
328 {
329 char *p;
330 int sum;
331
332 for (sum = 0;;) {
333 msg("%s", prompt);
334 if (!(p = get_line()) || *p == '\0') {
335 msg(quiet ? "Not a number" :
336 "That doesn't look like a number");
337 continue;
338 }
339 sum = 0;
340
341 if (!isdigit((unsigned char)*p))
342 sum = lo - 1;
343 else
344 while (isdigit((unsigned char)*p)) {
345 sum = 10 * sum + (*p - '0');
346 ++p;
347 }
348
349 if (*p != ' ' && *p != '\t' && *p != '\0')
350 sum = lo - 1;
351 if (sum >= lo && sum <= hi)
352 break;
353 if (sum == lo - 1)
354 msg("that doesn't look like a number, try again --> ");
355 else
356 msg("%d is not between %d and %d inclusive, try again --> ",
357 sum, lo, hi);
358 }
359 return (sum);
360 }
361
362 /*
363 * msg:
364 * Display a message at the top of the screen.
365 */
366 static char Msgbuf[BUFSIZ] = {'\0'};
367 static int Mpos = 0;
368 static int Newpos = 0;
369
370 void
371 msg(const char *fmt, ...)
372 {
373 va_list ap;
374
375 va_start(ap, fmt);
376 (void)vsnprintf(&Msgbuf[Newpos], sizeof(Msgbuf)-Newpos, fmt, ap);
377 Newpos = strlen(Msgbuf);
378 va_end(ap);
379 endmsg();
380 }
381
382 /*
383 * addmsg:
384 * Add things to the current message
385 */
386 void
387 addmsg(const char *fmt, ...)
388 {
389 va_list ap;
390
391 va_start(ap, fmt);
392 (void)vsnprintf(&Msgbuf[Newpos], sizeof(Msgbuf)-Newpos, fmt, ap);
393 Newpos = strlen(Msgbuf);
394 va_end(ap);
395 }
396
397 /*
398 * endmsg:
399 * Display a new msg.
400 */
401 static int Lineno = 0;
402
403 void
404 endmsg(void)
405 {
406 static int lastline = 0;
407 int len;
408 char *mp, *omp;
409
410 /* All messages should start with uppercase */
411 mvaddch(lastline + Y_MSG_START, SCORE_X, ' ');
412 if (islower((unsigned char)Msgbuf[0]) && Msgbuf[1] != ')')
413 Msgbuf[0] = toupper((unsigned char)Msgbuf[0]);
414 mp = Msgbuf;
415 len = strlen(mp);
416 if (len / MSG_X + Lineno >= MSG_Y) {
417 while (Lineno < MSG_Y) {
418 wmove(Msgwin, Lineno++, 0);
419 wclrtoeol(Msgwin);
420 }
421 Lineno = 0;
422 }
423 mvaddch(Lineno + Y_MSG_START, SCORE_X, '*');
424 lastline = Lineno;
425 do {
426 mvwaddstr(Msgwin, Lineno, 0, mp);
427 if ((len = strlen(mp)) > MSG_X) {
428 omp = mp;
429 for (mp = &mp[MSG_X - 1]; *mp != ' '; mp--)
430 continue;
431 while (*mp == ' ')
432 mp--;
433 mp++;
434 wmove(Msgwin, Lineno, mp - omp);
435 wclrtoeol(Msgwin);
436 }
437 if (++Lineno >= MSG_Y)
438 Lineno = 0;
439 } while (len > MSG_X);
440 wclrtoeol(Msgwin);
441 Mpos = len;
442 Newpos = 0;
443 wrefresh(Msgwin);
444 refresh();
445 wrefresh(Msgwin);
446 }
447
448 /*
449 * do_wait:
450 * Wait for the user to type ' ' before doing anything else
451 */
452 void
453 do_wait(void)
454 {
455 static const char prompt[] = {'-', '-', 'M', 'o', 'r', 'e', '-', '-', '\0'};
456
457 if ((int)(Mpos + sizeof prompt) < MSG_X)
458 wmove(Msgwin, Lineno > 0 ? Lineno - 1 : MSG_Y - 1, Mpos);
459 else {
460 mvwaddch(Msgwin, Lineno, 0, ' ');
461 wclrtoeol(Msgwin);
462 if (++Lineno >= MSG_Y)
463 Lineno = 0;
464 }
465 waddstr(Msgwin, prompt);
466 wrefresh(Msgwin);
467 wait_for(' ');
468 }
469
470 /*
471 * wait_for
472 * Sit around until the guy types the right key
473 */
474 static void
475 wait_for(int ch)
476 {
477 int c;
478
479 if (ch == '\n')
480 while ((c = readchar()) != '\n')
481 continue;
482 else
483 while (readchar() != ch)
484 continue;
485 }
486
487 /*
488 * readchar:
489 * Reads and returns a character, checking for gross input errors
490 */
491 static int
492 readchar(void)
493 {
494 int cnt;
495 unsigned char c;
496
497 over:
498 cnt = 0;
499 while (read(STDIN_FILENO, &c, sizeof(unsigned char)) <= 0)
500 if (cnt++ > 100) { /* if we are getting infinite EOFs */
501 bye(); /* quit the game */
502 exit(1);
503 }
504 if (c == CTRL('L')) {
505 wrefresh(curscr);
506 goto over;
507 }
508 if (c == '\r')
509 return ('\n');
510 else
511 return (c);
512 }
513
514 /*
515 * get_line:
516 * Reads the next line up to '\n' or EOF. Multiple spaces are
517 * compressed to one space; a space is inserted before a ','
518 */
519 char *
520 get_line(void)
521 {
522 size_t pos;
523 int c, oy, ox;
524 WINDOW *oscr;
525
526 oscr = stdscr;
527 stdscr = Msgwin;
528 getyx(stdscr, oy, ox);
529 refresh();
530 /* loop reading in the string, and put it in a temporary buffer */
531 for (pos = 0; (c = readchar()) != '\n'; clrtoeol(), refresh()) {
532 if (c == erasechar()) { /* process erase character */
533 if (pos > 0) {
534 int i;
535
536 pos--;
537 for (i = strlen(unctrl(linebuf[pos])); i; i--)
538 addch('\b');
539 }
540 continue;
541 } else
542 if (c == killchar()) { /* process kill
543 * character */
544 pos = 0;
545 move(oy, ox);
546 continue;
547 } else
548 if (pos == 0 && c == ' ')
549 continue;
550 if (pos >= LINESIZE - 1 || !(isprint(c) || c == ' '))
551 putchar(CTRL('G'));
552 else {
553 if (islower(c))
554 c = toupper(c);
555 linebuf[pos++] = c;
556 addstr(unctrl(c));
557 Mpos++;
558 }
559 }
560 linebuf[pos] = '\0';
561 stdscr = oscr;
562 return (linebuf);
563 }
564
565 void
566 receive_intr(int signo __unused)
567 {
568 bye();
569 exit(1);
570 }
571
572 /*
573 * bye:
574 * Leave the program, cleaning things up as we go.
575 */
576 void
577 bye(void)
578 {
579 signal(SIGINT, SIG_IGN);
580 mvcur(0, COLS - 1, LINES - 1, 0);
581 fflush(stdout);
582 endwin();
583 putchar('\n');
584 }