Home | History | Annotate | Line # | Download | only in cribbage
io.c revision 1.24
      1 /*	$NetBSD: io.c,v 1.24 2009/08/12 05:48:04 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.24 2009/08/12 05:48:04 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 *, int, 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(c, brief)
     90 	CARD c;
     91 	BOOLEAN brief;
     92 {
     93 	if (brief)
     94 		return (msgcrd(c, TRUE, NULL, TRUE));
     95 	else
     96 		return (msgcrd(c, FALSE, " of ", FALSE));
     97 }
     98 
     99 /*
    100  * msgcrd:
    101  *	Print the value of a card in ascii
    102  */
    103 static int
    104 msgcrd(CARD c, BOOLEAN brfrank, const char *mid, BOOLEAN brfsuit)
    105 {
    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);
    119 }
    120 
    121 /*
    122  * printcard:
    123  *	Print out a card.
    124  */
    125 static void
    126 printcard(WINDOW *win, int cardno, CARD c, BOOLEAN blank)
    127 {
    128 	prcard(win, cardno * 2, cardno, c, blank);
    129 }
    130 
    131 /*
    132  * prcard:
    133  *	Print out a card on the window at the specified location
    134  */
    135 void
    136 prcard(WINDOW *win, int y, int x, CARD c, BOOLEAN blank)
    137 {
    138 	if (c.rank == EMPTY)
    139 		return;
    140 
    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]);
    151 	}
    152 }
    153 
    154 /*
    155  * prhand:
    156  *	Print a hand of n cards
    157  */
    158 void
    159 prhand(const CARD h[], int n, WINDOW *win, BOOLEAN blank)
    160 {
    161 	int i;
    162 
    163 	werase(win);
    164 	for (i = 0; i < n; i++)
    165 		printcard(win, i, *h++, blank);
    166 	wrefresh(win);
    167 }
    168 
    169 /*
    170  * infrom:
    171  *	reads a card, supposedly in hand, accepting unambigous brief
    172  *	input, returns the index of the card found...
    173  */
    174 int
    175 infrom(const CARD hand[], int n, const char *prompt)
    176 {
    177 	int i, j;
    178 	CARD crd;
    179 
    180 	if (n < 1) {
    181 		printf("\nINFROM: %d = n < 1!!\n", n);
    182 		exit(74);
    183 	}
    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);
    197 				}
    198 				return (i);
    199 			}
    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);
    215 				}
    216 			} else
    217 				msg("Sorry, I missed that");
    218 	}
    219 	/* NOTREACHED */
    220 }
    221 
    222 /*
    223  * incard:
    224  *	Inputs a card in any format.  It reads a line ending with a CR
    225  *	and then parses it.
    226  */
    227 static int
    228 incard(CARD *crd)
    229 {
    230 	int i;
    231 	int rnk, sut;
    232 	char *line, *p, *p1;
    233 	BOOLEAN retval;
    234 
    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;
    245 
    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;
    253 			}
    254 		}
    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;
    263 			}
    264 		}
    265 		if (sut != EMPTY)
    266 			retval = TRUE;
    267 		goto gotit;
    268 	}
    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;
    274 		}
    275 	}
    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;
    291 	}
    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;
    297 		}
    298 	}
    299 	if (sut != EMPTY)
    300 		retval = TRUE;
    301 gotit:
    302 	(*crd).rank = rnk;
    303 	(*crd).suit = sut;
    304 	return (retval);
    305 }
    306 
    307 /*
    308  * getuchar:
    309  *	Reads and converts to upper case
    310  */
    311 int
    312 getuchar(void)
    313 {
    314 	int c;
    315 
    316 	c = readchar();
    317 	if (islower(c))
    318 		c = toupper(c);
    319 	waddch(Msgwin, c);
    320 	return (c);
    321 }
    322 
    323 /*
    324  * number:
    325  *	Reads in a decimal number and makes sure it is between "lo" and
    326  *	"hi" inclusive.
    327  */
    328 int
    329 number(int lo, int hi, const char *prompt)
    330 {
    331 	char *p;
    332 	int sum;
    333 
    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;
    340 		}
    341 		sum = 0;
    342 
    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;
    349 			}
    350 
    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);
    360 	}
    361 	return (sum);
    362 }
    363 
    364 /*
    365  * msg:
    366  *	Display a message at the top of the screen.
    367  */
    368 static char Msgbuf[BUFSIZ] = {'\0'};
    369 static int Mpos = 0;
    370 static int Newpos = 0;
    371 
    372 void
    373 msg(const char *fmt, ...)
    374 {
    375 	va_list ap;
    376 
    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();
    382 }
    383 
    384 /*
    385  * addmsg:
    386  *	Add things to the current message
    387  */
    388 void
    389 addmsg(const char *fmt, ...)
    390 {
    391 	va_list ap;
    392 
    393 	va_start(ap, fmt);
    394 	(void)vsnprintf(&Msgbuf[Newpos], sizeof(Msgbuf)-Newpos, fmt, ap);
    395 	Newpos = strlen(Msgbuf);
    396 	va_end(ap);
    397 }
    398 
    399 /*
    400  * endmsg:
    401  *	Display a new msg.
    402  */
    403 static int Lineno = 0;
    404 
    405 void
    406 endmsg(void)
    407 {
    408 	static int lastline = 0;
    409 	int len;
    410 	char *mp, *omp;
    411 
    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);
    422 		}
    423 		Lineno = 0;
    424 	}
    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);
    438 		}
    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);
    448 }
    449 
    450 /*
    451  * do_wait:
    452  *	Wait for the user to type ' ' before doing anything else
    453  */
    454 void
    455 do_wait(void)
    456 {
    457 	static const char prompt[] = {'-', '-', 'M', 'o', 'r', 'e', '-', '-', '\0'};
    458 
    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;
    466 	}
    467 	waddstr(Msgwin, prompt);
    468 	wrefresh(Msgwin);
    469 	wait_for(' ');
    470 }
    471 
    472 /*
    473  * wait_for
    474  *	Sit around until the guy types the right key
    475  */
    476 static void
    477 wait_for(int ch)
    478 {
    479 	int c;
    480 
    481 	if (ch == '\n')
    482 		while ((c = readchar()) != '\n')
    483 			continue;
    484 	else
    485 		while (readchar() != ch)
    486 			continue;
    487 }
    488 
    489 /*
    490  * readchar:
    491  *	Reads and returns a character, checking for gross input errors
    492  */
    493 static int
    494 readchar(void)
    495 {
    496 	int cnt;
    497 	unsigned char c;
    498 
    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);
    505 		}
    506 	if (c == CTRL('L')) {
    507 		wrefresh(curscr);
    508 		goto over;
    509 	}
    510 	if (c == '\r')
    511 		return ('\n');
    512 	else
    513 		return (c);
    514 }
    515 
    516 /*
    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 ','
    520  */
    521 char *
    522 get_line(void)
    523 {
    524 	char *sp;
    525 	int c, oy, ox;
    526 	WINDOW *oscr;
    527 
    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;
    537 
    538 					sp--;
    539 					for (i = strlen(unctrl(*sp)); i; i--)
    540 						addch('\b');
    541 				}
    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++;
    560 		}
    561 	}
    562 	*sp = '\0';
    563 	stdscr = oscr;
    564 	return (linebuf);
    565 }
    566 
    567 void
    568 receive_intr(int signo __unused)
    569 {
    570 	bye();
    571 	exit(1);
    572 }
    573 
    574 /*
    575  * bye:
    576  *	Leave the program, cleaning things up as we go.
    577  */
    578 void
    579 bye(void)
    580 {
    581 	signal(SIGINT, SIG_IGN);
    582 	mvcur(0, COLS - 1, LINES - 1, 0);
    583 	fflush(stdout);
    584 	endwin();
    585 	putchar('\n');
    586 }
    587