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