xref: /netbsd-src/games/cribbage/io.c (revision 0f960348b26942dfa1da0eee6744e89c2f956edf)
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
msgcard(CARD c,BOOLEAN brief)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
msgcrd(CARD c,BOOLEAN brfrank,const char * mid,BOOLEAN brfsuit)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
printcard(WINDOW * win,unsigned cardno,CARD c,BOOLEAN blank)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
prcard(WINDOW * win,int y,int x,CARD c,BOOLEAN blank)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
prhand(const CARD h[],unsigned n,WINDOW * win,BOOLEAN blank)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
infrom(const CARD hand[],int n,const char * prompt)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
incard(CARD * crd)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
getuchar(void)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
number(int lo,int hi,const char * prompt)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
msg(const char * fmt,...)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
addmsg(const char * fmt,...)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
endmsg(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
do_wait(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
wait_for(int ch)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
readchar(void)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 *
get_line(void)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
receive_intr(int signo __unused)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
bye(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 }
585