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