Home | History | Annotate | Line # | Download | only in cribbage
io.c revision 1.22
      1 /*	$NetBSD: io.c,v 1.22 2009/06/04 04: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.22 2009/06/04 04: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 char    linebuf[LINESIZE];
     62 
     63 const char   *const rankname[RANKS] = {
     64 	"ACE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN",
     65 	"EIGHT", "NINE", "TEN", "JACK", "QUEEN", "KING"
     66 };
     67 
     68 const char   *const rankchar[RANKS] = {
     69 	"A", "2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K"
     70 };
     71 
     72 const char *const suitname[SUITS] = {"SPADES", "HEARTS", "DIAMONDS", "CLUBS"};
     73 
     74 const char   *const suitchar[SUITS] = {"S", "H", "D", "C"};
     75 
     76 /*
     77  * msgcard:
     78  *	Call msgcrd in one of two forms
     79  */
     80 int
     81 msgcard(c, brief)
     82 	CARD c;
     83 	BOOLEAN brief;
     84 {
     85 	if (brief)
     86 		return (msgcrd(c, TRUE, NULL, TRUE));
     87 	else
     88 		return (msgcrd(c, FALSE, " of ", FALSE));
     89 }
     90 
     91 /*
     92  * msgcrd:
     93  *	Print the value of a card in ascii
     94  */
     95 int
     96 msgcrd(CARD c, BOOLEAN brfrank, const char *mid, BOOLEAN brfsuit)
     97 {
     98 	if (c.rank == EMPTY || c.suit == EMPTY)
     99 		return (FALSE);
    100 	if (brfrank)
    101 		addmsg("%1.1s", rankchar[c.rank]);
    102 	else
    103 		addmsg(rankname[c.rank]);
    104 	if (mid != NULL)
    105 		addmsg(mid);
    106 	if (brfsuit)
    107 		addmsg("%1.1s", suitchar[c.suit]);
    108 	else
    109 		addmsg(suitname[c.suit]);
    110 	return (TRUE);
    111 }
    112 
    113 /*
    114  * printcard:
    115  *	Print out a card.
    116  */
    117 void
    118 printcard(WINDOW *win, int cardno, CARD c, BOOLEAN blank)
    119 {
    120 	prcard(win, cardno * 2, cardno, c, blank);
    121 }
    122 
    123 /*
    124  * prcard:
    125  *	Print out a card on the window at the specified location
    126  */
    127 void
    128 prcard(WINDOW *win, int y, int x, CARD c, BOOLEAN blank)
    129 {
    130 	if (c.rank == EMPTY)
    131 		return;
    132 
    133 	mvwaddstr(win, y + 0, x, "+-----+");
    134 	mvwaddstr(win, y + 1, x, "|     |");
    135 	mvwaddstr(win, y + 2, x, "|     |");
    136 	mvwaddstr(win, y + 3, x, "|     |");
    137 	mvwaddstr(win, y + 4, x, "+-----+");
    138 	if (!blank) {
    139 		mvwaddch(win, y + 1, x + 1, rankchar[c.rank][0]);
    140 		waddch(win, suitchar[c.suit][0]);
    141 		mvwaddch(win, y + 3, x + 4, rankchar[c.rank][0]);
    142 		waddch(win, suitchar[c.suit][0]);
    143 	}
    144 }
    145 
    146 /*
    147  * prhand:
    148  *	Print a hand of n cards
    149  */
    150 void
    151 prhand(const CARD h[], int n, WINDOW *win, BOOLEAN blank)
    152 {
    153 	int i;
    154 
    155 	werase(win);
    156 	for (i = 0; i < n; i++)
    157 		printcard(win, i, *h++, blank);
    158 	wrefresh(win);
    159 }
    160 
    161 /*
    162  * infrom:
    163  *	reads a card, supposedly in hand, accepting unambigous brief
    164  *	input, returns the index of the card found...
    165  */
    166 int
    167 infrom(const CARD hand[], int n, const char *prompt)
    168 {
    169 	int i, j;
    170 	CARD crd;
    171 
    172 	if (n < 1) {
    173 		printf("\nINFROM: %d = n < 1!!\n", n);
    174 		exit(74);
    175 	}
    176 	for (;;) {
    177 		msg(prompt);
    178 		if (incard(&crd)) {	/* if card is full card */
    179 			if (!is_one(crd, hand, n))
    180 				msg("That's not in your hand");
    181 			else {
    182 				for (i = 0; i < n; i++)
    183 					if (hand[i].rank == crd.rank &&
    184 					    hand[i].suit == crd.suit)
    185 						break;
    186 				if (i >= n) {
    187 			printf("\nINFROM: is_one or something messed up\n");
    188 					exit(77);
    189 				}
    190 				return (i);
    191 			}
    192 		} else			/* if not full card... */
    193 			if (crd.rank != EMPTY) {
    194 				for (i = 0; i < n; i++)
    195 					if (hand[i].rank == crd.rank)
    196 						break;
    197 				if (i >= n)
    198 					msg("No such rank in your hand");
    199 				else {
    200 					for (j = i + 1; j < n; j++)
    201 						if (hand[j].rank == crd.rank)
    202 							break;
    203 					if (j < n)
    204 						msg("Ambiguous rank");
    205 					else
    206 						return (i);
    207 				}
    208 			} else
    209 				msg("Sorry, I missed that");
    210 	}
    211 	/* NOTREACHED */
    212 }
    213 
    214 /*
    215  * incard:
    216  *	Inputs a card in any format.  It reads a line ending with a CR
    217  *	and then parses it.
    218  */
    219 int
    220 incard(CARD *crd)
    221 {
    222 	int i;
    223 	int rnk, sut;
    224 	char *line, *p, *p1;
    225 	BOOLEAN retval;
    226 
    227 	retval = FALSE;
    228 	rnk = sut = EMPTY;
    229 	if (!(line = getline()))
    230 		goto gotit;
    231 	p = p1 = line;
    232 	while (*p1 != ' ' && *p1 != '\0')
    233 		++p1;
    234 	*p1++ = '\0';
    235 	if (*p == '\0')
    236 		goto gotit;
    237 
    238 	/* IMPORTANT: no real card has 2 char first name */
    239 	if (strlen(p) == 2) {	/* check for short form */
    240 		rnk = EMPTY;
    241 		for (i = 0; i < RANKS; i++) {
    242 			if (*p == *rankchar[i]) {
    243 				rnk = i;
    244 				break;
    245 			}
    246 		}
    247 		if (rnk == EMPTY)
    248 			goto gotit;	/* it's nothing... */
    249 		++p;		/* advance to next char */
    250 		sut = EMPTY;
    251 		for (i = 0; i < SUITS; i++) {
    252 			if (*p == *suitchar[i]) {
    253 				sut = i;
    254 				break;
    255 			}
    256 		}
    257 		if (sut != EMPTY)
    258 			retval = TRUE;
    259 		goto gotit;
    260 	}
    261 	rnk = EMPTY;
    262 	for (i = 0; i < RANKS; i++) {
    263 		if (!strcmp(p, rankname[i]) || !strcmp(p, rankchar[i])) {
    264 			rnk = i;
    265 			break;
    266 		}
    267 	}
    268 	if (rnk == EMPTY)
    269 		goto gotit;
    270 	p = p1;
    271 	while (*p1 != ' ' && *p1 != '\0')
    272 		++p1;
    273 	*p1++ = '\0';
    274 	if (*p == '\0')
    275 		goto gotit;
    276 	if (!strcmp("OF", p)) {
    277 		p = p1;
    278 		while (*p1 != ' ' && *p1 != '\0')
    279 			++p1;
    280 		*p1++ = '\0';
    281 		if (*p == '\0')
    282 			goto gotit;
    283 	}
    284 	sut = EMPTY;
    285 	for (i = 0; i < SUITS; i++) {
    286 		if (!strcmp(p, suitname[i]) || !strcmp(p, suitchar[i])) {
    287 			sut = i;
    288 			break;
    289 		}
    290 	}
    291 	if (sut != EMPTY)
    292 		retval = TRUE;
    293 gotit:
    294 	(*crd).rank = rnk;
    295 	(*crd).suit = sut;
    296 	return (retval);
    297 }
    298 
    299 /*
    300  * getuchar:
    301  *	Reads and converts to upper case
    302  */
    303 int
    304 getuchar(void)
    305 {
    306 	int c;
    307 
    308 	c = readchar();
    309 	if (islower(c))
    310 		c = toupper(c);
    311 	waddch(Msgwin, c);
    312 	return (c);
    313 }
    314 
    315 /*
    316  * number:
    317  *	Reads in a decimal number and makes sure it is between "lo" and
    318  *	"hi" inclusive.
    319  */
    320 int
    321 number(int lo, int hi, const char *prompt)
    322 {
    323 	char *p;
    324 	int sum;
    325 
    326 	for (sum = 0;;) {
    327 		msg(prompt);
    328 		if (!(p = getline()) || *p == '\0') {
    329 			msg(quiet ? "Not a number" :
    330 			    "That doesn't look like a number");
    331 			continue;
    332 		}
    333 		sum = 0;
    334 
    335 		if (!isdigit((unsigned char)*p))
    336 			sum = lo - 1;
    337 		else
    338 			while (isdigit((unsigned char)*p)) {
    339 				sum = 10 * sum + (*p - '0');
    340 				++p;
    341 			}
    342 
    343 		if (*p != ' ' && *p != '\t' && *p != '\0')
    344 			sum = lo - 1;
    345 		if (sum >= lo && sum <= hi)
    346 			break;
    347 		if (sum == lo - 1)
    348 			msg("that doesn't look like a number, try again --> ");
    349 		else
    350 		msg("%d is not between %d and %d inclusive, try again --> ",
    351 			    sum, lo, hi);
    352 	}
    353 	return (sum);
    354 }
    355 
    356 /*
    357  * msg:
    358  *	Display a message at the top of the screen.
    359  */
    360 char    Msgbuf[BUFSIZ] = {'\0'};
    361 int     Mpos = 0;
    362 static int Newpos = 0;
    363 
    364 void
    365 msg(const char *fmt, ...)
    366 {
    367 	va_list ap;
    368 
    369 	va_start(ap, fmt);
    370 	(void)vsnprintf(&Msgbuf[Newpos], sizeof(Msgbuf)-Newpos, fmt, ap);
    371 	Newpos = strlen(Msgbuf);
    372 	va_end(ap);
    373 	endmsg();
    374 }
    375 
    376 /*
    377  * addmsg:
    378  *	Add things to the current message
    379  */
    380 void
    381 addmsg(const char *fmt, ...)
    382 {
    383 	va_list ap;
    384 
    385 	va_start(ap, fmt);
    386 	(void)vsnprintf(&Msgbuf[Newpos], sizeof(Msgbuf)-Newpos, fmt, ap);
    387 	Newpos = strlen(Msgbuf);
    388 	va_end(ap);
    389 }
    390 
    391 /*
    392  * endmsg:
    393  *	Display a new msg.
    394  */
    395 int     Lineno = 0;
    396 
    397 void
    398 endmsg(void)
    399 {
    400 	static int lastline = 0;
    401 	int len;
    402 	char *mp, *omp;
    403 
    404 	/* All messages should start with uppercase */
    405 	mvaddch(lastline + Y_MSG_START, SCORE_X, ' ');
    406 	if (islower((unsigned char)Msgbuf[0]) && Msgbuf[1] != ')')
    407 		Msgbuf[0] = toupper((unsigned char)Msgbuf[0]);
    408 	mp = Msgbuf;
    409 	len = strlen(mp);
    410 	if (len / MSG_X + Lineno >= MSG_Y) {
    411 		while (Lineno < MSG_Y) {
    412 			wmove(Msgwin, Lineno++, 0);
    413 			wclrtoeol(Msgwin);
    414 		}
    415 		Lineno = 0;
    416 	}
    417 	mvaddch(Lineno + Y_MSG_START, SCORE_X, '*');
    418 	lastline = Lineno;
    419 	do {
    420 		mvwaddstr(Msgwin, Lineno, 0, mp);
    421 		if ((len = strlen(mp)) > MSG_X) {
    422 			omp = mp;
    423 			for (mp = &mp[MSG_X - 1]; *mp != ' '; mp--)
    424 				continue;
    425 			while (*mp == ' ')
    426 				mp--;
    427 			mp++;
    428 			wmove(Msgwin, Lineno, mp - omp);
    429 			wclrtoeol(Msgwin);
    430 		}
    431 		if (++Lineno >= MSG_Y)
    432 			Lineno = 0;
    433 	} while (len > MSG_X);
    434 	wclrtoeol(Msgwin);
    435 	Mpos = len;
    436 	Newpos = 0;
    437 	wrefresh(Msgwin);
    438 	refresh();
    439 	wrefresh(Msgwin);
    440 }
    441 
    442 /*
    443  * do_wait:
    444  *	Wait for the user to type ' ' before doing anything else
    445  */
    446 void
    447 do_wait(void)
    448 {
    449 	static const char prompt[] = {'-', '-', 'M', 'o', 'r', 'e', '-', '-', '\0'};
    450 
    451 	if ((int)(Mpos + sizeof prompt) < MSG_X)
    452 		wmove(Msgwin, Lineno > 0 ? Lineno - 1 : MSG_Y - 1, Mpos);
    453 	else {
    454 		mvwaddch(Msgwin, Lineno, 0, ' ');
    455 		wclrtoeol(Msgwin);
    456 		if (++Lineno >= MSG_Y)
    457 			Lineno = 0;
    458 	}
    459 	waddstr(Msgwin, prompt);
    460 	wrefresh(Msgwin);
    461 	wait_for(' ');
    462 }
    463 
    464 /*
    465  * wait_for
    466  *	Sit around until the guy types the right key
    467  */
    468 void
    469 wait_for(int ch)
    470 {
    471 	int c;
    472 
    473 	if (ch == '\n')
    474 		while ((c = readchar()) != '\n')
    475 			continue;
    476 	else
    477 		while (readchar() != ch)
    478 			continue;
    479 }
    480 
    481 /*
    482  * readchar:
    483  *	Reads and returns a character, checking for gross input errors
    484  */
    485 int
    486 readchar(void)
    487 {
    488 	int cnt;
    489 	unsigned char c;
    490 
    491 over:
    492 	cnt = 0;
    493 	while (read(STDIN_FILENO, &c, sizeof(unsigned char)) <= 0)
    494 		if (cnt++ > 100) {	/* if we are getting infinite EOFs */
    495 			bye();		/* quit the game */
    496 			exit(1);
    497 		}
    498 	if (c == CTRL('L')) {
    499 		wrefresh(curscr);
    500 		goto over;
    501 	}
    502 	if (c == '\r')
    503 		return ('\n');
    504 	else
    505 		return (c);
    506 }
    507 
    508 /*
    509  * getline:
    510  *      Reads the next line up to '\n' or EOF.  Multiple spaces are
    511  *	compressed to one space; a space is inserted before a ','
    512  */
    513 char *
    514 getline(void)
    515 {
    516 	char *sp;
    517 	int c, oy, ox;
    518 	WINDOW *oscr;
    519 
    520 	oscr = stdscr;
    521 	stdscr = Msgwin;
    522 	getyx(stdscr, oy, ox);
    523 	refresh();
    524 	/* loop reading in the string, and put it in a temporary buffer */
    525 	for (sp = linebuf; (c = readchar()) != '\n'; clrtoeol(), refresh()) {
    526 			if (c == erasechar()) {	/* process erase character */
    527 				if (sp > linebuf) {
    528 					int i;
    529 
    530 					sp--;
    531 					for (i = strlen(unctrl(*sp)); i; i--)
    532 						addch('\b');
    533 				}
    534 				continue;
    535 			} else
    536 				if (c == killchar()) {	/* process kill
    537 							 * character */
    538 					sp = linebuf;
    539 					move(oy, ox);
    540 					continue;
    541 				} else
    542 					if (sp == linebuf && c == ' ')
    543 						continue;
    544 		if (sp >= &linebuf[LINESIZE - 1] || !(isprint(c) || c == ' '))
    545 			putchar(CTRL('G'));
    546 		else {
    547 			if (islower(c))
    548 				c = toupper(c);
    549 			*sp++ = c;
    550 			addstr(unctrl(c));
    551 			Mpos++;
    552 		}
    553 	}
    554 	*sp = '\0';
    555 	stdscr = oscr;
    556 	return (linebuf);
    557 }
    558 
    559 void
    560 receive_intr(int signo __unused)
    561 {
    562 	bye();
    563 	exit(1);
    564 }
    565 
    566 /*
    567  * bye:
    568  *	Leave the program, cleaning things up as we go.
    569  */
    570 void
    571 bye(void)
    572 {
    573 	signal(SIGINT, SIG_IGN);
    574 	mvcur(0, COLS - 1, LINES - 1, 0);
    575 	fflush(stdout);
    576 	endwin();
    577 	putchar('\n');
    578 }
    579