1 /* $NetBSD: execute.c,v 1.22 2012/06/19 05:35:32 dholland 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 35 static char sccsid[] = "@(#)execute.c 8.1 (Berkeley) 5/31/93"; 36 #else 37 __RCSID("$NetBSD: execute.c,v 1.22 2012/06/19 05:35:32 dholland Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 #include <fcntl.h> 42 #include <stdlib.h> 43 #include <unistd.h> 44 #include <limits.h> 45 #include <sys/types.h> 46 #include <sys/stat.h> 47 #include <sys/time.h> 48 #include <time.h> 49 #include <errno.h> 50 51 #include "deck.h" 52 #include "monop.h" 53 54 #define MIN_FORMAT_VERSION 1 55 #define CUR_FORMAT_VERSION 1 56 #define MAX_FORMAT_VERSION 1 57 58 typedef struct stat STAT; 59 typedef struct tm TIME; 60 61 static char buf[257]; 62 63 static bool new_play; /* set if move on to new player */ 64 65 static void show_move(void); 66 67 static void restore_reset(void); 68 static int restore_parseline(char *txt); 69 static int restore_toplevel_attr(const char *attribute, char *txt); 70 static int restore_player_attr(const char *attribute, char *txt); 71 static int restore_deck_attr(const char *attribute, char *txt); 72 static int restore_square_attr(const char *attribute, char *txt); 73 static int getnum(const char *what, char *txt, int min, int max, int *ret); 74 static int getnum_withbrace(const char *what, char *txt, int min, int max, 75 int *ret); 76 77 /* 78 * This routine executes the given command by index number 79 */ 80 void 81 execute(int com_num) 82 { 83 new_play = FALSE; /* new_play is true if fixing */ 84 (*func[com_num])(); 85 notify(); 86 force_morg(); 87 if (new_play) 88 next_play(); 89 else if (num_doub) 90 printf("%s rolled doubles. Goes again\n", cur_p->name); 91 } 92 93 /* 94 * This routine moves a piece around. 95 */ 96 void 97 do_move(void) 98 { 99 int r1, r2; 100 bool was_jail; 101 102 new_play = was_jail = FALSE; 103 printf("roll is %d, %d\n", r1=roll(1, 6), r2=roll(1, 6)); 104 if (cur_p->loc == JAIL) { 105 was_jail++; 106 if (!move_jail(r1, r2)) { 107 new_play++; 108 goto ret; 109 } 110 } 111 else { 112 if (r1 == r2 && ++num_doub == 3) { 113 printf("That's 3 doubles. You go to jail\n"); 114 goto_jail(); 115 new_play++; 116 goto ret; 117 } 118 move(r1+r2); 119 } 120 if (r1 != r2 || was_jail) 121 new_play++; 122 ret: 123 return; 124 } 125 126 /* 127 * This routine moves a normal move 128 */ 129 void 130 move(int rl) 131 { 132 int old_loc; 133 134 old_loc = cur_p->loc; 135 cur_p->loc = (cur_p->loc + rl) % N_SQRS; 136 if (cur_p->loc < old_loc && rl > 0) { 137 cur_p->money += 200; 138 printf("You pass %s and get $200\n", board[0].name); 139 } 140 show_move(); 141 } 142 143 /* 144 * This routine shows the results of a move 145 */ 146 static void 147 show_move(void) 148 { 149 SQUARE *sqp; 150 151 sqp = &board[cur_p->loc]; 152 printf("That puts you on %s\n", sqp->name); 153 switch (sqp->type) { 154 case SAFE: 155 printf("That is a safe place\n"); 156 break; 157 case CC: 158 cc(); 159 break; 160 case CHANCE: 161 chance(); 162 break; 163 case INC_TAX: 164 inc_tax(); 165 break; 166 case GOTO_J: 167 goto_jail(); 168 break; 169 case LUX_TAX: 170 lux_tax(); 171 break; 172 case PRPTY: 173 case RR: 174 case UTIL: 175 if (sqp->owner < 0) { 176 printf("That would cost $%d\n", sqp->cost); 177 if (getyn("Do you want to buy? ") == 0) { 178 buy(player, sqp); 179 cur_p->money -= sqp->cost; 180 } 181 else if (num_play > 2) 182 bid(); 183 } 184 else if (sqp->owner == player) 185 printf("You own it.\n"); 186 else 187 rent(sqp); 188 } 189 } 190 191 /* 192 * Reset the game state. 193 */ 194 static void 195 reset_game(void) 196 { 197 int i; 198 199 for (i = 0; i < N_SQRS; i++) { 200 board[i].owner = -1; 201 if (board[i].type == PRPTY) { 202 board[i].desc->morg = 0; 203 board[i].desc->houses = 0; 204 } else if (board[i].type == RR || board[i].type == UTIL) { 205 board[i].desc->morg = 0; 206 } 207 } 208 209 for (i = 0; i < 2; i++) { 210 deck[i].top_card = 0; 211 deck[i].gojf_used = FALSE; 212 } 213 214 if (play) { 215 for (i = 0; i < num_play; i++) { 216 free(play[i].name); 217 play[i].name = NULL; 218 } 219 free(play); 220 play = NULL; 221 } 222 223 for (i = 0; i < MAX_PL+2; i++) { 224 name_list[i] = NULL; 225 } 226 227 cur_p = NULL; 228 num_play = 0; 229 player = 0; 230 num_doub = 0; 231 fixing = FALSE; 232 trading = FALSE; 233 told_em = FALSE; 234 spec = FALSE; 235 } 236 237 238 /* 239 * This routine saves the current game for use at a later date 240 */ 241 void 242 save(void) 243 { 244 char *sp; 245 FILE *outf; 246 time_t t; 247 struct stat sb; 248 int i, j; 249 250 printf("Which file do you wish to save it in? "); 251 fgets(buf, sizeof(buf), stdin); 252 if (feof(stdin)) 253 return; 254 sp = strchr(buf, '\n'); 255 if (sp) 256 *sp = '\0'; 257 258 /* 259 * check for existing files, and confirm overwrite if needed 260 */ 261 262 if (stat(buf, &sb) == 0 263 && getyn("File exists. Do you wish to overwrite? ") > 0) 264 return; 265 266 outf = fopen(buf, "w"); 267 if (outf == NULL) { 268 warn("%s", buf); 269 return; 270 } 271 printf("\"%s\" ", buf); 272 time(&t); /* get current time */ 273 274 /* Header */ 275 fprintf(outf, "NetBSD monop format v%d\n", CUR_FORMAT_VERSION); 276 fprintf(outf, "time %s", ctime(&t)); /* ctime includes a \n */ 277 fprintf(outf, "numplayers %d\n", num_play); 278 fprintf(outf, "currentplayer %d\n", player); 279 fprintf(outf, "doubles %d\n", num_doub); 280 281 /* Players */ 282 for (i = 0; i < num_play; i++) { 283 fprintf(outf, "player %d {\n", i); 284 fprintf(outf, " name %s\n", name_list[i]); 285 fprintf(outf, " money %d\n", play[i].money); 286 fprintf(outf, " loc %d\n", play[i].loc); 287 fprintf(outf, " num_gojf %d\n", play[i].num_gojf); 288 fprintf(outf, " in_jail %d\n", play[i].in_jail); 289 fprintf(outf, "}\n"); 290 } 291 292 /* Decks */ 293 for (i = 0; i < 2; i++) { 294 fprintf(outf, "deck %d {\n", i); 295 fprintf(outf, " numcards %d\n", deck[i].num_cards); 296 fprintf(outf, " topcard %d\n", deck[i].top_card); 297 fprintf(outf, " gojf_used %d\n", deck[i].gojf_used); 298 fprintf(outf, " cards"); 299 for (j = 0; j < deck[i].num_cards; j++) 300 fprintf(outf, " %d", deck[i].cards[j]); 301 fprintf(outf, "\n"); 302 fprintf(outf, "}\n"); 303 } 304 305 /* Board */ 306 for (i = 0; i < N_SQRS; i++) { 307 fprintf(outf, "square %d {\n", i); 308 fprintf(outf, "owner %d\n", board[i].owner); 309 if (board[i].owner < 0) { 310 /* nothing */ 311 } else if (board[i].type == PRPTY) { 312 fprintf(outf, "morg %d\n", board[i].desc->morg); 313 fprintf(outf, "houses %d\n", board[i].desc->houses); 314 } else if (board[i].type == RR || board[i].type == UTIL) { 315 fprintf(outf, "morg %d\n", board[i].desc->morg); 316 } 317 fprintf(outf, "}\n"); 318 } 319 if (ferror(outf) || fflush(outf)) 320 warnx("write error"); 321 fclose(outf); 322 323 strcpy(buf, ctime(&t)); 324 for (sp = buf; *sp != '\n'; sp++) 325 continue; 326 *sp = '\0'; 327 printf("[%s]\n", buf); 328 } 329 330 /* 331 * This routine restores an old game from a file 332 */ 333 void 334 restore(void) 335 { 336 char *sp; 337 338 for (;;) { 339 printf("Which file do you wish to restore from? "); 340 fgets(buf, sizeof(buf), stdin); 341 if (feof(stdin)) 342 return; 343 sp = strchr(buf, '\n'); 344 if (sp) 345 *sp = '\0'; 346 if (rest_f(buf) == 0) 347 break; 348 } 349 } 350 351 /* 352 * This does the actual restoring. It returns zero on success, 353 * and -1 on failure. 354 */ 355 int 356 rest_f(const char *file) 357 { 358 char *sp; 359 FILE *inf; 360 char xbuf[80]; 361 STAT sbuf; 362 char readbuf[512]; 363 int ret = 0; 364 365 inf = fopen(file, "r"); 366 if (inf == NULL) { 367 warn("%s", file); 368 return -1; 369 } 370 printf("\"%s\" ", file); 371 if (fstat(fileno(inf), &sbuf) < 0) { 372 err(1, "%s: fstat", file); 373 } 374 375 /* Clear the game state to prevent brokenness on misordered files. */ 376 reset_game(); 377 378 /* Reset the parser */ 379 restore_reset(); 380 381 /* Note: can't use buf[], file might point at it. (Lame...) */ 382 while (fgets(readbuf, sizeof(readbuf), inf)) { 383 /* 384 * The input buffer is long enough to handle anything 385 * that's supposed to be in the output buffer, so if 386 * we get a partial line, complain. 387 */ 388 sp = strchr(readbuf, '\n'); 389 if (sp == NULL) { 390 printf("file is corrupt: long lines.\n"); 391 ret = -1; 392 break; 393 } 394 *sp = '\0'; 395 396 if (restore_parseline(readbuf)) { 397 ret = -1; 398 break; 399 } 400 } 401 402 if (ferror(inf)) 403 warnx("%s: read error", file); 404 fclose(inf); 405 406 if (ret < 0) 407 return -1; 408 409 name_list[num_play] = "done"; 410 411 if (play == NULL || cur_p == NULL || num_play < 2) { 412 printf("save file is incomplete.\n"); 413 return -1; 414 } 415 416 /* 417 * We could at this point crosscheck the following: 418 * - there are only two GOJF cards floating around 419 * - total number of houses and hotels does not exceed maximums 420 * - no props are both built and mortgaged 421 * but for now we don't. 422 */ 423 424 strcpy(xbuf, ctime(&sbuf.st_mtime)); 425 for (sp = xbuf; *sp != '\n'; sp++) 426 continue; 427 *sp = '\0'; 428 printf("[%s]\n", xbuf); 429 return 0; 430 } 431 432 /* 433 * State of the restore parser 434 */ 435 static int restore_version; 436 static enum { 437 RI_NONE, 438 RI_PLAYER, 439 RI_DECK, 440 RI_SQUARE 441 } restore_item; 442 static int restore_itemnum; 443 444 /* 445 * Reset the restore parser 446 */ 447 static void 448 restore_reset(void) 449 { 450 restore_version = -1; 451 restore_item = RI_NONE; 452 restore_itemnum = -1; 453 } 454 455 /* 456 * Handle one line of the save file 457 */ 458 static int 459 restore_parseline(char *txt) 460 { 461 char *attribute; 462 char *s; 463 464 if (restore_version < 0) { 465 /* Haven't seen the header yet. Demand it right away. */ 466 if (!strncmp(txt, "NetBSD monop format v", 21)) { 467 return getnum("format version", txt+21, 468 MIN_FORMAT_VERSION, 469 MAX_FORMAT_VERSION, 470 &restore_version); 471 } 472 printf("file is not a monop save file.\n"); 473 return -1; 474 } 475 476 /* Check for lines that are right braces. */ 477 if (!strcmp(txt, "}")) { 478 if (restore_item == RI_NONE) { 479 printf("mismatched close brace.\n"); 480 return -1; 481 } 482 restore_item = RI_NONE; 483 restore_itemnum = -1; 484 return 0; 485 } 486 487 /* Any other line must begin with a word, which is the attribute. */ 488 s = txt; 489 while (*s==' ') 490 s++; 491 attribute = s; 492 s = strchr(attribute, ' '); 493 if (s == NULL) { 494 printf("file is corrupt: attribute %s lacks value.\n", 495 attribute); 496 return -1; 497 } 498 *(s++) = '\0'; 499 while (*s==' ') 500 s++; 501 /* keep the remaining text for further handling */ 502 txt = s; 503 504 switch (restore_item) { 505 case RI_NONE: 506 /* toplevel attributes */ 507 return restore_toplevel_attr(attribute, txt); 508 509 case RI_PLAYER: 510 /* player attributes */ 511 return restore_player_attr(attribute, txt); 512 513 case RI_DECK: 514 /* deck attributes */ 515 return restore_deck_attr(attribute, txt); 516 517 case RI_SQUARE: 518 /* board square attributes */ 519 return restore_square_attr(attribute, txt); 520 } 521 /* NOTREACHED */ 522 printf("internal logic error\n"); 523 return -1; 524 } 525 526 static int 527 restore_toplevel_attr(const char *attribute, char *txt) 528 { 529 if (!strcmp(attribute, "time")) { 530 /* nothing */ 531 } else if (!strcmp(attribute, "numplayers")) { 532 if (getnum("numplayers", txt, 2, MAX_PL, &num_play) < 0) { 533 return -1; 534 } 535 if (play != NULL) { 536 printf("numplayers: multiple settings\n"); 537 return -1; 538 } 539 play = calloc((size_t)num_play, sizeof(play[0])); 540 if (play == NULL) { 541 err(1, "calloc"); 542 } 543 } else if (!strcmp(attribute, "currentplayer")) { 544 if (getnum("currentplayer", txt, 0, num_play-1, &player) < 0) { 545 return -1; 546 } 547 if (play == NULL) { 548 printf("currentplayer: before numplayers\n"); 549 return -1; 550 } 551 cur_p = &play[player]; 552 } else if (!strcmp(attribute, "doubles")) { 553 if (getnum("doubles", txt, 0, 2, &num_doub) < 0) { 554 return -1; 555 } 556 } else if (!strcmp(attribute, "player")) { 557 if (getnum_withbrace("player", txt, 0, num_play-1, 558 &restore_itemnum) < 0) { 559 return -1; 560 } 561 restore_item = RI_PLAYER; 562 } else if (!strcmp(attribute, "deck")) { 563 if (getnum_withbrace("deck", txt, 0, 1, 564 &restore_itemnum) < 0) { 565 return -1; 566 } 567 restore_item = RI_DECK; 568 } else if (!strcmp(attribute, "square")) { 569 if (getnum_withbrace("square", txt, 0, N_SQRS-1, 570 &restore_itemnum) < 0) { 571 return -1; 572 } 573 restore_item = RI_SQUARE; 574 } else { 575 printf("unknown attribute %s\n", attribute); 576 return -1; 577 } 578 return 0; 579 } 580 581 static int 582 restore_player_attr(const char *attribute, char *txt) 583 { 584 PLAY *pp; 585 int tmp; 586 587 if (play == NULL) { 588 printf("player came before numplayers.\n"); 589 return -1; 590 } 591 pp = &play[restore_itemnum]; 592 593 if (!strcmp(attribute, "name")) { 594 if (pp->name != NULL) { 595 printf("player has multiple names.\n"); 596 return -1; 597 } 598 /* XXX should really systematize the max name length */ 599 if (strlen(txt) > 256) { 600 txt[256] = 0; 601 } 602 pp->name = strdup(txt); 603 if (pp->name == NULL) 604 err(1, "strdup"); 605 name_list[restore_itemnum] = pp->name; 606 } else if (!strcmp(attribute, "money")) { 607 if (getnum(attribute, txt, 0, INT_MAX, &pp->money) < 0) { 608 return -1; 609 } 610 } else if (!strcmp(attribute, "loc")) { 611 /* note: not N_SQRS-1 */ 612 if (getnum(attribute, txt, 0, N_SQRS, &tmp) < 0) { 613 return -1; 614 } 615 pp->loc = tmp; 616 } else if (!strcmp(attribute, "num_gojf")) { 617 if (getnum(attribute, txt, 0, 2, &tmp) < 0) { 618 return -1; 619 } 620 pp->num_gojf = tmp; 621 } else if (!strcmp(attribute, "in_jail")) { 622 if (getnum(attribute, txt, 0, 3, &tmp) < 0) { 623 return -1; 624 } 625 pp->in_jail = tmp; 626 if (pp->in_jail > 0 && pp->loc != JAIL) { 627 printf("player escaped from jail?\n"); 628 return -1; 629 } 630 } else { 631 printf("unknown attribute %s\n", attribute); 632 return -1; 633 } 634 return 0; 635 } 636 637 static int 638 restore_deck_attr(const char *attribute, char *txt) 639 { 640 int tmp, j; 641 char *s; 642 DECK *dp; 643 644 dp = &deck[restore_itemnum]; 645 646 if (!strcmp(attribute, "numcards")) { 647 if (getnum(attribute, txt, dp->num_cards, dp->num_cards, 648 &tmp) < 0) { 649 return -1; 650 } 651 } else if (!strcmp(attribute, "topcard")) { 652 if (getnum(attribute, txt, 0, dp->num_cards, 653 &dp->top_card) < 0) { 654 return -1; 655 } 656 } else if (!strcmp(attribute, "gojf_used")) { 657 if (getnum(attribute, txt, 0, 1, &tmp) < 0) { 658 return -1; 659 } 660 dp->gojf_used = tmp; 661 } else if (!strcmp(attribute, "cards")) { 662 errno = 0; 663 s = txt; 664 for (j = 0; j<dp->num_cards; j++) { 665 tmp = strtol(s, &s, 10); 666 if (tmp < 0 || tmp >= dp->num_cards) { 667 printf("cards: out of range value\n"); 668 return -1; 669 } 670 dp->cards[j] = tmp; 671 } 672 if (errno) { 673 printf("cards: invalid values\n"); 674 return -1; 675 } 676 } else { 677 printf("unknown attribute %s\n", attribute); 678 return -1; 679 } 680 return 0; 681 } 682 683 static int 684 restore_square_attr(const char *attribute, char *txt) 685 { 686 SQUARE *sp = &board[restore_itemnum]; 687 int tmp; 688 689 if (!strcmp(attribute, "owner")) { 690 if (getnum(attribute, txt, -1, num_play-1, &tmp) < 0) { 691 return -1; 692 } 693 sp->owner = tmp; 694 if (tmp >= 0) 695 add_list(tmp, &play[tmp].own_list, restore_itemnum); 696 } else if (!strcmp(attribute, "morg")) { 697 if (sp->type != PRPTY && sp->type != RR && sp->type != UTIL) { 698 printf("unownable property is mortgaged.\n"); 699 return -1; 700 } 701 if (getnum(attribute, txt, 0, 1, &tmp) < 0) { 702 return -1; 703 } 704 sp->desc->morg = tmp; 705 } else if (!strcmp(attribute, "houses")) { 706 if (sp->type != PRPTY) { 707 printf("unbuildable property has houses.\n"); 708 return -1; 709 } 710 if (getnum(attribute, txt, 0, 5, &tmp) < 0) { 711 return -1; 712 } 713 sp->desc->houses = tmp; 714 } else { 715 printf("unknown attribute %s\n", attribute); 716 return -1; 717 } 718 return 0; 719 } 720 721 static int 722 getnum(const char *what, char *txt, int min, int max, int *ret) 723 { 724 char *s; 725 long l; 726 727 errno = 0; 728 l = strtol(txt, &s, 10); 729 if (errno || strlen(s)>0) { 730 printf("%s: not a number.\n", what); 731 return -1; 732 } 733 if (l < min || l > max) { 734 printf("%s: out of range.\n", what); 735 } 736 *ret = l; 737 return 0; 738 } 739 740 static int 741 getnum_withbrace(const char *what, char *txt, int min, int max, int *ret) 742 { 743 char *s; 744 s = strchr(txt, ' '); 745 if (s == NULL) { 746 printf("%s: expected open brace\n", what); 747 return -1; 748 } 749 *(s++) = '\0'; 750 while (*s == ' ') 751 s++; 752 if (*s != '{') { 753 printf("%s: expected open brace\n", what); 754 return -1; 755 } 756 if (s[1] != 0) { 757 printf("%s: garbage after open brace\n", what); 758 return -1; 759 } 760 return getnum(what, txt, min, max, ret); 761 } 762