Merge from vendor branch PKGSRC:
[netbsd-mini2440.git] / games / cribbage / io.c
blobcd7690594c318a98318f0585587612955eea89bd
1 /* $NetBSD: io.c,v 1.23 2009/07/13 19:05:40 roy Exp $ */
3 /*-
4 * Copyright (c) 1980, 1993
5 * The Regents of the University of California. All rights reserved.
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.
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.
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.23 2009/07/13 19:05:40 roy Exp $");
38 #endif
39 #endif /* not lint */
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>
50 #include "deck.h"
51 #include "cribbage.h"
52 #include "cribcur.h"
54 #define LINESIZE 128
56 #ifdef CTRL
57 #undef CTRL
58 #endif
59 #define CTRL(X) (X - 'A' + 1)
61 static int msgcrd(CARD, BOOLEAN, const char *, BOOLEAN);
62 static void printcard(WINDOW *, int, CARD, BOOLEAN);
63 static int incard(CARD *);
64 static void wait_for(int);
65 static int readchar(void);
67 static char linebuf[LINESIZE];
69 static const char *const rankname[RANKS] = {
70 "ACE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN",
71 "EIGHT", "NINE", "TEN", "JACK", "QUEEN", "KING"
74 static const char *const rankchar[RANKS] = {
75 "A", "2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K"
78 static const char *const suitname[SUITS] = {
79 "SPADES", "HEARTS", "DIAMONDS", "CLUBS"
82 static const char *const suitchar[SUITS] = {"S", "H", "D", "C"};
85 * msgcard:
86 * Call msgcrd in one of two forms
88 int
89 msgcard(c, brief)
90 CARD c;
91 BOOLEAN brief;
93 if (brief)
94 return (msgcrd(c, TRUE, NULL, TRUE));
95 else
96 return (msgcrd(c, FALSE, " of ", FALSE));
100 * msgcrd:
101 * Print the value of a card in ascii
103 static int
104 msgcrd(CARD c, BOOLEAN brfrank, const char *mid, BOOLEAN brfsuit)
106 if (c.rank == EMPTY || c.suit == EMPTY)
107 return (FALSE);
108 if (brfrank)
109 addmsg("%1.1s", rankchar[c.rank]);
110 else
111 addmsg(rankname[c.rank]);
112 if (mid != NULL)
113 addmsg(mid);
114 if (brfsuit)
115 addmsg("%1.1s", suitchar[c.suit]);
116 else
117 addmsg(suitname[c.suit]);
118 return (TRUE);
122 * printcard:
123 * Print out a card.
125 static void
126 printcard(WINDOW *win, int cardno, CARD c, BOOLEAN blank)
128 prcard(win, cardno * 2, cardno, c, blank);
132 * prcard:
133 * Print out a card on the window at the specified location
135 void
136 prcard(WINDOW *win, int y, int x, CARD c, BOOLEAN blank)
138 if (c.rank == EMPTY)
139 return;
141 mvwaddstr(win, y + 0, x, "+-----+");
142 mvwaddstr(win, y + 1, x, "| |");
143 mvwaddstr(win, y + 2, x, "| |");
144 mvwaddstr(win, y + 3, x, "| |");
145 mvwaddstr(win, y + 4, x, "+-----+");
146 if (!blank) {
147 mvwaddch(win, y + 1, x + 1, rankchar[c.rank][0]);
148 waddch(win, suitchar[c.suit][0]);
149 mvwaddch(win, y + 3, x + 4, rankchar[c.rank][0]);
150 waddch(win, suitchar[c.suit][0]);
155 * prhand:
156 * Print a hand of n cards
158 void
159 prhand(const CARD h[], int n, WINDOW *win, BOOLEAN blank)
161 int i;
163 werase(win);
164 for (i = 0; i < n; i++)
165 printcard(win, i, *h++, blank);
166 wrefresh(win);
170 * infrom:
171 * reads a card, supposedly in hand, accepting unambigous brief
172 * input, returns the index of the card found...
175 infrom(const CARD hand[], int n, const char *prompt)
177 int i, j;
178 CARD crd;
180 if (n < 1) {
181 printf("\nINFROM: %d = n < 1!!\n", n);
182 exit(74);
184 for (;;) {
185 msg(prompt);
186 if (incard(&crd)) { /* if card is full card */
187 if (!is_one(crd, hand, n))
188 msg("That's not in your hand");
189 else {
190 for (i = 0; i < n; i++)
191 if (hand[i].rank == crd.rank &&
192 hand[i].suit == crd.suit)
193 break;
194 if (i >= n) {
195 printf("\nINFROM: is_one or something messed up\n");
196 exit(77);
198 return (i);
200 } else /* if not full card... */
201 if (crd.rank != EMPTY) {
202 for (i = 0; i < n; i++)
203 if (hand[i].rank == crd.rank)
204 break;
205 if (i >= n)
206 msg("No such rank in your hand");
207 else {
208 for (j = i + 1; j < n; j++)
209 if (hand[j].rank == crd.rank)
210 break;
211 if (j < n)
212 msg("Ambiguous rank");
213 else
214 return (i);
216 } else
217 msg("Sorry, I missed that");
219 /* NOTREACHED */
223 * incard:
224 * Inputs a card in any format. It reads a line ending with a CR
225 * and then parses it.
227 static int
228 incard(CARD *crd)
230 int i;
231 int rnk, sut;
232 char *line, *p, *p1;
233 BOOLEAN retval;
235 retval = FALSE;
236 rnk = sut = EMPTY;
237 if (!(line = get_line()))
238 goto gotit;
239 p = p1 = line;
240 while (*p1 != ' ' && *p1 != '\0')
241 ++p1;
242 *p1++ = '\0';
243 if (*p == '\0')
244 goto gotit;
246 /* IMPORTANT: no real card has 2 char first name */
247 if (strlen(p) == 2) { /* check for short form */
248 rnk = EMPTY;
249 for (i = 0; i < RANKS; i++) {
250 if (*p == *rankchar[i]) {
251 rnk = i;
252 break;
255 if (rnk == EMPTY)
256 goto gotit; /* it's nothing... */
257 ++p; /* advance to next char */
258 sut = EMPTY;
259 for (i = 0; i < SUITS; i++) {
260 if (*p == *suitchar[i]) {
261 sut = i;
262 break;
265 if (sut != EMPTY)
266 retval = TRUE;
267 goto gotit;
269 rnk = EMPTY;
270 for (i = 0; i < RANKS; i++) {
271 if (!strcmp(p, rankname[i]) || !strcmp(p, rankchar[i])) {
272 rnk = i;
273 break;
276 if (rnk == EMPTY)
277 goto gotit;
278 p = p1;
279 while (*p1 != ' ' && *p1 != '\0')
280 ++p1;
281 *p1++ = '\0';
282 if (*p == '\0')
283 goto gotit;
284 if (!strcmp("OF", p)) {
285 p = p1;
286 while (*p1 != ' ' && *p1 != '\0')
287 ++p1;
288 *p1++ = '\0';
289 if (*p == '\0')
290 goto gotit;
292 sut = EMPTY;
293 for (i = 0; i < SUITS; i++) {
294 if (!strcmp(p, suitname[i]) || !strcmp(p, suitchar[i])) {
295 sut = i;
296 break;
299 if (sut != EMPTY)
300 retval = TRUE;
301 gotit:
302 (*crd).rank = rnk;
303 (*crd).suit = sut;
304 return (retval);
308 * getuchar:
309 * Reads and converts to upper case
312 getuchar(void)
314 int c;
316 c = readchar();
317 if (islower(c))
318 c = toupper(c);
319 waddch(Msgwin, c);
320 return (c);
324 * number:
325 * Reads in a decimal number and makes sure it is between "lo" and
326 * "hi" inclusive.
329 number(int lo, int hi, const char *prompt)
331 char *p;
332 int sum;
334 for (sum = 0;;) {
335 msg(prompt);
336 if (!(p = get_line()) || *p == '\0') {
337 msg(quiet ? "Not a number" :
338 "That doesn't look like a number");
339 continue;
341 sum = 0;
343 if (!isdigit((unsigned char)*p))
344 sum = lo - 1;
345 else
346 while (isdigit((unsigned char)*p)) {
347 sum = 10 * sum + (*p - '0');
348 ++p;
351 if (*p != ' ' && *p != '\t' && *p != '\0')
352 sum = lo - 1;
353 if (sum >= lo && sum <= hi)
354 break;
355 if (sum == lo - 1)
356 msg("that doesn't look like a number, try again --> ");
357 else
358 msg("%d is not between %d and %d inclusive, try again --> ",
359 sum, lo, hi);
361 return (sum);
365 * msg:
366 * Display a message at the top of the screen.
368 static char Msgbuf[BUFSIZ] = {'\0'};
369 static int Mpos = 0;
370 static int Newpos = 0;
372 void
373 msg(const char *fmt, ...)
375 va_list ap;
377 va_start(ap, fmt);
378 (void)vsnprintf(&Msgbuf[Newpos], sizeof(Msgbuf)-Newpos, fmt, ap);
379 Newpos = strlen(Msgbuf);
380 va_end(ap);
381 endmsg();
385 * addmsg:
386 * Add things to the current message
388 void
389 addmsg(const char *fmt, ...)
391 va_list ap;
393 va_start(ap, fmt);
394 (void)vsnprintf(&Msgbuf[Newpos], sizeof(Msgbuf)-Newpos, fmt, ap);
395 Newpos = strlen(Msgbuf);
396 va_end(ap);
400 * endmsg:
401 * Display a new msg.
403 static int Lineno = 0;
405 void
406 endmsg(void)
408 static int lastline = 0;
409 int len;
410 char *mp, *omp;
412 /* All messages should start with uppercase */
413 mvaddch(lastline + Y_MSG_START, SCORE_X, ' ');
414 if (islower((unsigned char)Msgbuf[0]) && Msgbuf[1] != ')')
415 Msgbuf[0] = toupper((unsigned char)Msgbuf[0]);
416 mp = Msgbuf;
417 len = strlen(mp);
418 if (len / MSG_X + Lineno >= MSG_Y) {
419 while (Lineno < MSG_Y) {
420 wmove(Msgwin, Lineno++, 0);
421 wclrtoeol(Msgwin);
423 Lineno = 0;
425 mvaddch(Lineno + Y_MSG_START, SCORE_X, '*');
426 lastline = Lineno;
427 do {
428 mvwaddstr(Msgwin, Lineno, 0, mp);
429 if ((len = strlen(mp)) > MSG_X) {
430 omp = mp;
431 for (mp = &mp[MSG_X - 1]; *mp != ' '; mp--)
432 continue;
433 while (*mp == ' ')
434 mp--;
435 mp++;
436 wmove(Msgwin, Lineno, mp - omp);
437 wclrtoeol(Msgwin);
439 if (++Lineno >= MSG_Y)
440 Lineno = 0;
441 } while (len > MSG_X);
442 wclrtoeol(Msgwin);
443 Mpos = len;
444 Newpos = 0;
445 wrefresh(Msgwin);
446 refresh();
447 wrefresh(Msgwin);
451 * do_wait:
452 * Wait for the user to type ' ' before doing anything else
454 void
455 do_wait(void)
457 static const char prompt[] = {'-', '-', 'M', 'o', 'r', 'e', '-', '-', '\0'};
459 if ((int)(Mpos + sizeof prompt) < MSG_X)
460 wmove(Msgwin, Lineno > 0 ? Lineno - 1 : MSG_Y - 1, Mpos);
461 else {
462 mvwaddch(Msgwin, Lineno, 0, ' ');
463 wclrtoeol(Msgwin);
464 if (++Lineno >= MSG_Y)
465 Lineno = 0;
467 waddstr(Msgwin, prompt);
468 wrefresh(Msgwin);
469 wait_for(' ');
473 * wait_for
474 * Sit around until the guy types the right key
476 static void
477 wait_for(int ch)
479 int c;
481 if (ch == '\n')
482 while ((c = readchar()) != '\n')
483 continue;
484 else
485 while (readchar() != ch)
486 continue;
490 * readchar:
491 * Reads and returns a character, checking for gross input errors
493 static int
494 readchar(void)
496 int cnt;
497 unsigned char c;
499 over:
500 cnt = 0;
501 while (read(STDIN_FILENO, &c, sizeof(unsigned char)) <= 0)
502 if (cnt++ > 100) { /* if we are getting infinite EOFs */
503 bye(); /* quit the game */
504 exit(1);
506 if (c == CTRL('L')) {
507 wrefresh(curscr);
508 goto over;
510 if (c == '\r')
511 return ('\n');
512 else
513 return (c);
517 * get_line:
518 * Reads the next line up to '\n' or EOF. Multiple spaces are
519 * compressed to one space; a space is inserted before a ','
521 char *
522 get_line(void)
524 char *sp;
525 int c, oy, ox;
526 WINDOW *oscr;
528 oscr = stdscr;
529 stdscr = Msgwin;
530 getyx(stdscr, oy, ox);
531 refresh();
532 /* loop reading in the string, and put it in a temporary buffer */
533 for (sp = linebuf; (c = readchar()) != '\n'; clrtoeol(), refresh()) {
534 if (c == erasechar()) { /* process erase character */
535 if (sp > linebuf) {
536 int i;
538 sp--;
539 for (i = strlen(unctrl(*sp)); i; i--)
540 addch('\b');
542 continue;
543 } else
544 if (c == killchar()) { /* process kill
545 * character */
546 sp = linebuf;
547 move(oy, ox);
548 continue;
549 } else
550 if (sp == linebuf && c == ' ')
551 continue;
552 if (sp >= &linebuf[LINESIZE - 1] || !(isprint(c) || c == ' '))
553 putchar(CTRL('G'));
554 else {
555 if (islower(c))
556 c = toupper(c);
557 *sp++ = c;
558 addstr(unctrl(c));
559 Mpos++;
562 *sp = '\0';
563 stdscr = oscr;
564 return (linebuf);
567 void
568 receive_intr(int signo __unused)
570 bye();
571 exit(1);
575 * bye:
576 * Leave the program, cleaning things up as we go.
578 void
579 bye(void)
581 signal(SIGINT, SIG_IGN);
582 mvcur(0, COLS - 1, LINES - 1, 0);
583 fflush(stdout);
584 endwin();
585 putchar('\n');