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