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