]> git.cameronkatri.com Git - bsdgames-darwin.git/blob - cribbage/io.c
KNF and WARNS=3
[bsdgames-darwin.git] / cribbage / io.c
1 /* $NetBSD: io.c,v 1.20 2005/07/02 08:32:32 jmc 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.20 2005/07/02 08:32:32 jmc 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)vsprintf(&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)vsprintf(&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 __attribute__((__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 }