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