-int suspend(FILE *input)
-{
- long i, k;
- FILE *fp = NULL;
-
- /* Suspend. Offer to save things in a file, but charging
- * some points (so can't win by using saved games to retry
- * battles or to start over after learning zzword). */
- RSPEAK(SUSPEND_WARNING);
- if (!YES(input,THIS_ACCEPTABLE,OK_MAN,OK_MAN)) return GO_CLEAROBJ;
- game.saved=game.saved+5;
-
- while (fp == NULL) {
- char* name = linenoise("\nFile name: ");
- if (name == NULL)
- return GO_TOP;
- fp = fopen(name, WRITE_MODE);
- if (fp == NULL)
- printf("Can't open file %s, try again.\n", name);
- linenoiseFree(name);
- }
-
- DATIME(&i,&k);
- k=i+650*k;
- save.savetime = k;
- save.mode = -1;
- save.version = VRSION;
- memcpy(&save.game, &game, sizeof(struct game_t));
- save.bird = OBJSND[BIRD];
- save.bivalve = OBJTXT[OYSTER];
- IGNORE(fwrite(&save, sizeof(struct save_t), 1, fp));
- fclose(fp);
- RSPEAK(RESUME_HELP);
- exit(0);
+
+static char *strip(char *name) {
+ // Trim leading whitespace
+ while (isspace((unsigned char)*name)) {
+ name++; // LCOV_EXCL_LINE
+ }
+ if (*name != '\0') {
+ // Trim trailing whitespace;
+ // might be left there by autocomplete
+ char *end = name + strlen(name) - 1;
+ while (end > name && isspace((unsigned char)*end)) {
+ end--;
+ }
+ // Write new null terminator character
+ end[1] = '\0';
+ }
+
+ return name;
+}
+
+int suspend(void) {
+ /* Suspend. Offer to save things in a file, but charging
+ * some points (so can't win by using saved games to retry
+ * battles or to start over after learning zzword).
+ * If ADVENT_NOSAVE is defined, gripe instead. */
+
+#if defined ADVENT_NOSAVE || defined ADVENT_AUTOSAVE
+ rspeak(SAVERESUME_DISABLED);
+ return GO_TOP;
+#endif
+ FILE *fp = NULL;
+
+ rspeak(SUSPEND_WARNING);
+ if (!yes_or_no(arbitrary_messages[THIS_ACCEPTABLE],
+ arbitrary_messages[OK_MAN],
+ arbitrary_messages[OK_MAN])) {
+ return GO_CLEAROBJ;
+ }
+ game.saved = game.saved + 5;
+
+ while (fp == NULL) {
+ char *name = myreadline("\nFile name: ");
+ if (name == NULL) {
+ return GO_TOP;
+ }
+ name = strip(name);
+ if (strlen(name) == 0) {
+ return GO_TOP; // LCOV_EXCL_LINE
+ }
+ fp = fopen(strip(name), WRITE_MODE);
+ if (fp == NULL) {
+ printf("Can't open file %s, try again.\n", name);
+ }
+ free(name);
+ }
+
+ savefile(fp);
+ fclose(fp);
+ rspeak(RESUME_HELP);
+ exit(EXIT_SUCCESS);
+}
+
+int resume(void) {
+ /* Resume. Read a suspended game back from a file.
+ * If ADVENT_NOSAVE is defined, gripe instead. */
+
+#if defined ADVENT_NOSAVE || defined ADVENT_AUTOSAVE
+ rspeak(SAVERESUME_DISABLED);
+ return GO_TOP;
+#endif
+ FILE *fp = NULL;
+
+ if (game.loc != LOC_START || game.locs[LOC_START].abbrev != 1) {
+ rspeak(RESUME_ABANDON);
+ if (!yes_or_no(arbitrary_messages[THIS_ACCEPTABLE],
+ arbitrary_messages[OK_MAN],
+ arbitrary_messages[OK_MAN])) {
+ return GO_CLEAROBJ;
+ }
+ }
+
+ while (fp == NULL) {
+ char *name = myreadline("\nFile name: ");
+ if (name == NULL) {
+ return GO_TOP;
+ }
+ name = strip(name);
+ if (strlen(name) == 0) {
+ return GO_TOP; // LCOV_EXCL_LINE
+ }
+ fp = fopen(name, READ_MODE);
+ if (fp == NULL) {
+ printf("Can't open file %s, try again.\n", name);
+ }
+ free(name);
+ }
+
+ return restore(fp);
+}
+
+int restore(FILE *fp) {
+ /* Read and restore game state from file, assuming
+ * sane initial state.
+ * If ADVENT_NOSAVE is defined, gripe instead. */
+#ifdef ADVENT_NOSAVE
+ rspeak(SAVERESUME_DISABLED);
+ return GO_TOP;
+#endif
+
+ IGNORE(fread(&save, sizeof(struct save_t), 1, fp));
+ fclose(fp);
+ if (memcmp(save.magic, ADVENT_MAGIC, sizeof(ADVENT_MAGIC)) != 0 ||
+ save.canary != ENDIAN_MAGIC) {
+ rspeak(BAD_SAVE);
+ } else if (save.version != SAVE_VERSION) {
+ rspeak(VERSION_SKEW, save.version / 10, MOD(save.version, 10),
+ SAVE_VERSION / 10, MOD(SAVE_VERSION, 10));
+ } else if (!is_valid(save.game)) {
+ rspeak(SAVE_TAMPERING);
+ exit(EXIT_SUCCESS);
+ } else {
+ game = save.game;
+ }
+ return GO_TOP;