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