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