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