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