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