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