]> git.cameronkatri.com Git - bsdgames-darwin.git/blobdiff - monop/execute.c
Remove extra semicolon.
[bsdgames-darwin.git] / monop / execute.c
index 828d9908331304e65f6a78af87a6ab241e106be0..3466c7ec140f051acee638e0425f41e5174069e9 100644 (file)
@@ -1,4 +1,4 @@
-/*     $NetBSD: execute.c,v 1.11 2004/01/27 20:30:30 jsm Exp $ */
+/*     $NetBSD: execute.c,v 1.22 2012/06/19 05:35:32 dholland Exp $    */
 
 /*
  * Copyright (c) 1980, 1993
 #if 0
 static char sccsid[] = "@(#)execute.c  8.1 (Berkeley) 5/31/93";
 #else
-__RCSID("$NetBSD: execute.c,v 1.11 2004/01/27 20:30:30 jsm Exp $");
+__RCSID("$NetBSD: execute.c,v 1.22 2012/06/19 05:35:32 dholland Exp $");
 #endif
 #endif /* not lint */
 
-#include "monop.ext"
 #include <fcntl.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <limits.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <time.h>
+#include <errno.h>
 
-#define        SEGSIZE 8192
+#include "deck.h"
+#include "monop.h"
+
+#define MIN_FORMAT_VERSION 1
+#define CUR_FORMAT_VERSION 1
+#define MAX_FORMAT_VERSION 1
 
 typedef        struct stat     STAT;
 typedef        struct tm       TIME;
@@ -55,16 +61,24 @@ typedef     struct tm       TIME;
 static char    buf[257];
 
 static bool    new_play;       /* set if move on to new player         */
-extern void    *heapstart;
 
 static void show_move(void);
 
+static void restore_reset(void);
+static int restore_parseline(char *txt);
+static int restore_toplevel_attr(const char *attribute, char *txt);
+static int restore_player_attr(const char *attribute, char *txt);
+static int restore_deck_attr(const char *attribute, char *txt);
+static int restore_square_attr(const char *attribute, char *txt);
+static int getnum(const char *what, char *txt, int min, int max, int *ret);
+static int getnum_withbrace(const char *what, char *txt, int min, int max,
+               int *ret);
+
 /*
  *     This routine executes the given command by index number
  */
 void
-execute(com_num)
-       int com_num; 
+execute(int com_num)
 {
        new_play = FALSE;       /* new_play is true if fixing   */
        (*func[com_num])();
@@ -80,7 +94,7 @@ execute(com_num)
  *     This routine moves a piece around.
  */
 void
-do_move(
+do_move(void)
 {
        int r1, r2;
        bool was_jail;
@@ -113,8 +127,7 @@ ret:
  *     This routine moves a normal move
  */
 void
-move(rl)
-       int rl; 
+move(int rl)
 {
        int old_loc;
 
@@ -131,7 +144,7 @@ move(rl)
  *     This routine shows the results of a move
  */
 static void
-show_move(
+show_move(void)
 {
        SQUARE *sqp;
 
@@ -142,15 +155,20 @@ show_move()
                printf("That is a safe place\n");
                break;
          case CC:
-               cc(); break;
+               cc();
+               break;
          case CHANCE:
-               chance(); break;
+               chance();
+               break;
          case INC_TAX:
-               inc_tax(); break;
+               inc_tax();
+               break;
          case GOTO_J:
-               goto_jail(); break;
+               goto_jail();
+               break;
          case LUX_TAX:
-               lux_tax(); break;
+               lux_tax();
+               break;
          case PRPTY:
          case RR:
          case UTIL:
@@ -170,50 +188,142 @@ show_move()
        }
 }
 
+/*
+ * Reset the game state.
+ */
+static void
+reset_game(void)
+{
+       int i;
+
+       for (i = 0; i < N_SQRS; i++) {
+               board[i].owner = -1;
+               if (board[i].type == PRPTY) {
+                       board[i].desc->morg = 0;
+                       board[i].desc->houses = 0;
+               } else if (board[i].type == RR || board[i].type == UTIL) {
+                       board[i].desc->morg = 0;
+               }
+       }
+
+       for (i = 0; i < 2; i++) {
+               deck[i].top_card = 0;
+               deck[i].gojf_used = FALSE;
+       }
+
+       if (play) {
+               for (i = 0; i < num_play; i++) {
+                       free(play[i].name);
+                       play[i].name = NULL;
+               }
+               free(play);
+               play = NULL;
+       }
+
+       for (i = 0; i < MAX_PL+2; i++) {
+               name_list[i] = NULL;
+       }
+
+       cur_p = NULL;
+       num_play = 0;
+       player = 0;
+       num_doub = 0;
+       fixing = FALSE;
+       trading = FALSE;
+       told_em = FALSE;
+       spec = FALSE;
+}
+
+
 /*
  *     This routine saves the current game for use at a later date
  */
 void
-save(
+save(void)
 {
        char *sp;
-       int outf, num;
+       FILE *outf;
        time_t t;
        struct stat sb;
-       char *start, *end;
+       int i, j;
 
        printf("Which file do you wish to save it in? ");
-       sp = buf;
-       while ((*sp++=getchar()) != '\n')
-               continue;
-       *--sp = '\0';
+       fgets(buf, sizeof(buf), stdin);
+       if (feof(stdin))
+               return;
+       sp = strchr(buf, '\n');
+       if (sp)
+               *sp = '\0';
 
        /*
         * check for existing files, and confirm overwrite if needed
         */
 
-       if (stat(buf, &sb) > -1
+       if (stat(buf, &sb) == 0
            && getyn("File exists.  Do you wish to overwrite? ") > 0)
                return;
 
-       if ((outf=creat(buf, 0644)) < 0) {
-               perror(buf);
+       outf = fopen(buf, "w");
+       if (outf == NULL) {
+               warn("%s", buf);
                return;
        }
        printf("\"%s\" ", buf);
        time(&t);                       /* get current time             */
+
+       /* Header */
+       fprintf(outf, "NetBSD monop format v%d\n", CUR_FORMAT_VERSION);
+       fprintf(outf, "time %s", ctime(&t));  /* ctime includes a \n */
+       fprintf(outf, "numplayers %d\n", num_play);
+       fprintf(outf, "currentplayer %d\n", player);
+       fprintf(outf, "doubles %d\n", num_doub);
+
+       /* Players */
+       for (i = 0; i < num_play; i++) {
+               fprintf(outf, "player %d {\n", i);
+               fprintf(outf, "    name %s\n", name_list[i]);
+               fprintf(outf, "    money %d\n", play[i].money);
+               fprintf(outf, "    loc %d\n", play[i].loc);
+               fprintf(outf, "    num_gojf %d\n", play[i].num_gojf);
+               fprintf(outf, "    in_jail %d\n", play[i].in_jail);
+               fprintf(outf, "}\n");
+       }
+
+       /* Decks */
+       for (i = 0; i < 2; i++) {
+               fprintf(outf, "deck %d {\n", i);
+               fprintf(outf, "    numcards %d\n", deck[i].num_cards);
+               fprintf(outf, "    topcard %d\n", deck[i].top_card);
+               fprintf(outf, "    gojf_used %d\n", deck[i].gojf_used);
+               fprintf(outf, "    cards");
+               for (j = 0; j < deck[i].num_cards; j++)
+                       fprintf(outf, " %d", deck[i].cards[j]);
+               fprintf(outf, "\n");
+               fprintf(outf, "}\n");
+       }
+
+       /* Board */
+       for (i = 0; i < N_SQRS; i++) {
+               fprintf(outf, "square %d {\n", i);
+               fprintf(outf, "owner %d\n", board[i].owner);
+               if (board[i].owner < 0) {
+                       /* nothing */
+               } else if (board[i].type == PRPTY) {
+                       fprintf(outf, "morg %d\n", board[i].desc->morg);
+                       fprintf(outf, "houses %d\n", board[i].desc->houses);
+               } else if (board[i].type == RR || board[i].type == UTIL) {
+                       fprintf(outf, "morg %d\n", board[i].desc->morg);
+               }
+               fprintf(outf, "}\n");
+       }
+       if (ferror(outf) || fflush(outf))
+               warnx("write error");
+       fclose(outf);
+
        strcpy(buf, ctime(&t));
        for (sp = buf; *sp != '\n'; sp++)
                continue;
        *sp = '\0';
-       start = heapstart;
-       end = sbrk(0);
-       while (start < end) {           /* write out entire data space */
-               num = start + 16 * 1024 > end ? end - start : 16 * 1024;
-               write(outf, start, num);
-               start += num;
-       }
-       close(outf);
        printf("[%s]\n", buf);
 }
 
@@ -221,52 +331,431 @@ save()
  *     This routine restores an old game from a file
  */
 void
-restore(
+restore(void)
 {
        char *sp;
 
-       printf("Which file do you wish to restore from? ");
-       for (sp = buf; (*sp=getchar()) != '\n'; sp++)
-               continue;
-       *sp = '\0';
-       rest_f(buf);
+       for (;;) {
+               printf("Which file do you wish to restore from? ");
+               fgets(buf, sizeof(buf), stdin);
+               if (feof(stdin))
+                       return;
+               sp = strchr(buf, '\n');
+               if (sp)
+                       *sp = '\0';
+               if (rest_f(buf) == 0)
+                       break;
+       }
 }
 
 /*
- *     This does the actual restoring.  It returns TRUE if the
- * backup was successful, else false.
+ * This does the actual restoring.  It returns zero on success,
+ * and -1 on failure.
  */
 int
-rest_f(file)
-       const char *file; 
+rest_f(const char *file)
 {
        char *sp;
-       int inf, num;
-       char buf[80];
-       char *start, *end;
+       FILE *inf;
+       char xbuf[80];
        STAT sbuf;
+       char readbuf[512];
+       int ret = 0;
 
-       if ((inf=open(file, O_RDONLY)) < 0) {
-               perror(file);
-               return FALSE;
+       inf = fopen(file, "r");
+       if (inf == NULL) {
+               warn("%s", file);
+               return -1;
        }
        printf("\"%s\" ", file);
-       if (fstat(inf, &sbuf) < 0) {            /* get file stats       */
-               perror(file);
-               exit(1);
+       if (fstat(fileno(inf), &sbuf) < 0) {
+               err(1, "%s: fstat", file);
+       }
+
+       /* Clear the game state to prevent brokenness on misordered files. */
+       reset_game();
+
+       /* Reset the parser */
+       restore_reset();
+
+       /* Note: can't use buf[], file might point at it. (Lame...) */
+       while (fgets(readbuf, sizeof(readbuf), inf)) {
+               /*
+                * The input buffer is long enough to handle anything
+                * that's supposed to be in the output buffer, so if
+                * we get a partial line, complain.
+                */
+               sp = strchr(readbuf, '\n');
+               if (sp == NULL) {
+                       printf("file is corrupt: long lines.\n");
+                       ret = -1;
+                       break;
+               }
+               *sp = '\0';
+
+               if (restore_parseline(readbuf)) {
+                       ret = -1;
+                       break;
+               }
        }
-       start = heapstart;
-       brk(end = start + sbuf.st_size);
-       while (start < end) {           /* write out entire data space */
-               num = start + 16 * 1024 > end ? end - start : 16 * 1024;
-               read(inf, start, num);
-               start += num;
+
+       if (ferror(inf))
+               warnx("%s: read error", file);
+       fclose(inf);
+
+       if (ret < 0)
+               return -1;
+
+       name_list[num_play] = "done";
+
+       if (play == NULL || cur_p == NULL || num_play < 2) {
+               printf("save file is incomplete.\n");
+               return -1;
        }
-       close(inf);
-       strcpy(buf, ctime(&sbuf.st_mtime));
-       for (sp = buf; *sp != '\n'; sp++)
+
+       /*
+        * We could at this point crosscheck the following:
+        *    - there are only two GOJF cards floating around
+        *    - total number of houses and hotels does not exceed maximums
+        *    - no props are both built and mortgaged
+        * but for now we don't.
+        */
+
+       strcpy(xbuf, ctime(&sbuf.st_mtime));
+       for (sp = xbuf; *sp != '\n'; sp++)
                continue;
        *sp = '\0';
-       printf("[%s]\n", buf);
-       return TRUE;
+       printf("[%s]\n", xbuf);
+       return 0;
+}
+
+/*
+ * State of the restore parser
+ */
+static int restore_version;
+static enum {
+       RI_NONE,
+       RI_PLAYER,
+       RI_DECK,
+       RI_SQUARE
+} restore_item;
+static int restore_itemnum;
+
+/*
+ * Reset the restore parser
+ */
+static void
+restore_reset(void)
+{
+       restore_version = -1;
+       restore_item = RI_NONE;
+       restore_itemnum = -1;
+}
+
+/*
+ * Handle one line of the save file
+ */
+static int
+restore_parseline(char *txt)
+{
+       char *attribute;
+       char *s;
+
+       if (restore_version < 0) {
+               /* Haven't seen the header yet. Demand it right away. */
+               if (!strncmp(txt, "NetBSD monop format v", 21)) {
+                       return getnum("format version", txt+21,
+                                     MIN_FORMAT_VERSION,
+                                     MAX_FORMAT_VERSION,
+                                     &restore_version);
+               }
+               printf("file is not a monop save file.\n");
+               return -1;
+       }
+
+       /* Check for lines that are right braces. */
+       if (!strcmp(txt, "}")) {
+               if (restore_item == RI_NONE) {
+                       printf("mismatched close brace.\n");
+                       return -1;
+               }
+               restore_item = RI_NONE;
+               restore_itemnum = -1;
+               return 0;
+       }
+
+       /* Any other line must begin with a word, which is the attribute. */
+       s = txt;
+       while (*s==' ')
+               s++;
+       attribute = s;
+       s = strchr(attribute, ' ');
+       if (s == NULL) {
+               printf("file is corrupt: attribute %s lacks value.\n",
+                   attribute);
+               return -1;
+       }
+       *(s++) = '\0';
+       while (*s==' ')
+               s++;
+       /* keep the remaining text for further handling */
+       txt = s;
+
+       switch (restore_item) {
+           case RI_NONE:
+               /* toplevel attributes */
+               return restore_toplevel_attr(attribute, txt);
+
+           case RI_PLAYER:
+               /* player attributes */
+               return restore_player_attr(attribute, txt);
+
+           case RI_DECK:
+               /* deck attributes */
+               return restore_deck_attr(attribute, txt);
+
+           case RI_SQUARE:
+               /* board square attributes */
+               return restore_square_attr(attribute, txt);
+       }
+       /* NOTREACHED */
+       printf("internal logic error\n");
+       return -1;
+}
+
+static int
+restore_toplevel_attr(const char *attribute, char *txt)
+{
+       if (!strcmp(attribute, "time")) {
+               /* nothing */
+       } else if (!strcmp(attribute, "numplayers")) {
+               if (getnum("numplayers", txt, 2, MAX_PL, &num_play) < 0) {
+                       return -1;
+               }
+               if (play != NULL) {
+                       printf("numplayers: multiple settings\n");
+                       return -1;
+               }
+               play = calloc((size_t)num_play, sizeof(play[0]));
+               if (play == NULL) {
+                       err(1, "calloc");
+               }
+       } else if (!strcmp(attribute, "currentplayer")) {
+               if (getnum("currentplayer", txt, 0, num_play-1, &player) < 0) {
+                       return -1;
+               }
+               if (play == NULL) {
+                       printf("currentplayer: before numplayers\n");
+                       return -1;
+               }
+               cur_p = &play[player];
+       } else if (!strcmp(attribute, "doubles")) {
+               if (getnum("doubles", txt, 0, 2, &num_doub) < 0) {
+                       return -1;
+               }
+       } else if (!strcmp(attribute, "player")) {
+               if (getnum_withbrace("player", txt, 0, num_play-1,
+                   &restore_itemnum) < 0) {
+                       return -1;
+               }
+               restore_item = RI_PLAYER;
+       } else if (!strcmp(attribute, "deck")) {
+               if (getnum_withbrace("deck", txt, 0, 1,
+                   &restore_itemnum) < 0) {
+                       return -1;
+               }
+               restore_item = RI_DECK;
+       } else if (!strcmp(attribute, "square")) {
+               if (getnum_withbrace("square", txt, 0, N_SQRS-1,
+                   &restore_itemnum) < 0) {
+                       return -1;
+               }
+               restore_item = RI_SQUARE;
+       } else {
+               printf("unknown attribute %s\n", attribute);
+               return -1;
+       }
+       return 0;
+}
+
+static int
+restore_player_attr(const char *attribute, char *txt)
+{
+       PLAY *pp;
+       int tmp;
+
+       if (play == NULL) {
+               printf("player came before numplayers.\n");
+               return -1;
+       }
+       pp = &play[restore_itemnum];
+
+       if (!strcmp(attribute, "name")) {
+               if (pp->name != NULL) {
+                       printf("player has multiple names.\n");
+                       return -1;
+               }
+               /* XXX should really systematize the max name length */
+               if (strlen(txt) > 256) {
+                       txt[256] = 0;
+               }
+               pp->name = strdup(txt);
+               if (pp->name == NULL)
+                       err(1, "strdup");
+               name_list[restore_itemnum] = pp->name;
+       } else if (!strcmp(attribute, "money")) {
+               if (getnum(attribute, txt, 0, INT_MAX, &pp->money) < 0) {
+                       return -1;
+               }
+       } else if (!strcmp(attribute, "loc")) {
+               /* note: not N_SQRS-1 */
+               if (getnum(attribute, txt, 0, N_SQRS, &tmp) < 0) {
+                       return -1;
+               }
+               pp->loc = tmp;
+       } else if (!strcmp(attribute, "num_gojf")) {
+               if (getnum(attribute, txt, 0, 2, &tmp) < 0) {
+                       return -1;
+               }
+               pp->num_gojf = tmp;
+       } else if (!strcmp(attribute, "in_jail")) {
+               if (getnum(attribute, txt, 0, 3, &tmp) < 0) {
+                       return -1;
+               }
+               pp->in_jail = tmp;
+               if (pp->in_jail > 0 && pp->loc != JAIL) {
+                       printf("player escaped from jail?\n");
+                       return -1;
+               }
+       } else {
+               printf("unknown attribute %s\n", attribute);
+               return -1;
+       }
+       return 0;
+}
+
+static int
+restore_deck_attr(const char *attribute, char *txt)
+{
+       int tmp, j;
+       char *s;
+       DECK *dp;
+
+       dp = &deck[restore_itemnum];
+
+       if (!strcmp(attribute, "numcards")) {
+               if (getnum(attribute, txt, dp->num_cards, dp->num_cards,
+                   &tmp) < 0) {
+                       return -1;
+               }
+       } else if (!strcmp(attribute, "topcard")) {
+               if (getnum(attribute, txt, 0, dp->num_cards,
+                   &dp->top_card) < 0) {
+                       return -1;
+               }
+       } else if (!strcmp(attribute, "gojf_used")) {
+               if (getnum(attribute, txt, 0, 1, &tmp) < 0) {
+                       return -1;
+               }
+               dp->gojf_used = tmp;
+       } else if (!strcmp(attribute, "cards")) {
+               errno = 0;
+               s = txt;
+               for (j = 0; j<dp->num_cards; j++) {
+                       tmp = strtol(s, &s, 10);
+                       if (tmp < 0 || tmp >= dp->num_cards) {
+                               printf("cards: out of range value\n");
+                               return -1;
+                       }
+                       dp->cards[j] = tmp;
+               }
+               if (errno) {
+                       printf("cards: invalid values\n");
+                       return -1;
+               }
+       } else {
+               printf("unknown attribute %s\n", attribute);
+               return -1;
+       }
+       return 0;
+}
+
+static int
+restore_square_attr(const char *attribute, char *txt)
+{
+       SQUARE *sp = &board[restore_itemnum];
+       int tmp;
+
+       if (!strcmp(attribute, "owner")) {
+               if (getnum(attribute, txt, -1, num_play-1, &tmp) < 0) {
+                       return -1;
+               }
+               sp->owner = tmp;
+               if (tmp >= 0)
+                       add_list(tmp, &play[tmp].own_list, restore_itemnum);
+       } else if (!strcmp(attribute, "morg")) {
+               if (sp->type != PRPTY && sp->type != RR && sp->type != UTIL) {
+                       printf("unownable property is mortgaged.\n");
+                       return -1;
+               }
+               if (getnum(attribute, txt, 0, 1, &tmp) < 0) {
+                       return -1;
+               }
+               sp->desc->morg = tmp;
+       } else if (!strcmp(attribute, "houses")) {
+               if (sp->type != PRPTY) {
+                       printf("unbuildable property has houses.\n");
+                       return -1;
+               }
+               if (getnum(attribute, txt, 0, 5, &tmp) < 0) {
+                       return -1;
+               }
+               sp->desc->houses = tmp;
+       } else {
+               printf("unknown attribute %s\n", attribute);
+               return -1;
+       }
+       return 0;
+}
+
+static int
+getnum(const char *what, char *txt, int min, int max, int *ret)
+{
+       char *s;
+       long l;
+
+       errno = 0;
+       l = strtol(txt, &s, 10);
+       if (errno || strlen(s)>0) {
+               printf("%s: not a number.\n", what);
+               return -1;
+       }
+       if (l < min || l > max) {
+               printf("%s: out of range.\n", what);
+       }
+       *ret = l;
+       return 0;
+}
+
+static int
+getnum_withbrace(const char *what, char *txt, int min, int max, int *ret)
+{
+       char *s;
+       s = strchr(txt, ' ');
+       if (s == NULL) {
+               printf("%s: expected open brace\n", what);
+               return -1;
+       }
+       *(s++) = '\0';
+       while (*s == ' ')
+               s++;
+       if (*s != '{') {
+               printf("%s: expected open brace\n", what);
+               return -1;
+       }
+       if (s[1] != 0) {
+               printf("%s: garbage after open brace\n", what);
+               return -1;
+       }
+       return getnum(what, txt, min, max, ret);
 }