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 } 578