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