From 7e4f2cac6f5248676ad28017031605a5f1500098 Mon Sep 17 00:00:00 2001
From: Christian Heller <c.heller@plomlompom.de>
Date: Wed, 21 Aug 2013 20:15:01 +0200
Subject: [PATCH] main() handles file IO now safely, exits on errors; also
 analogously re-phrased error messages in save_game(), turn_over().

---
 src/main.c | 67 +++++++++++++++++++++++++++++++++++++++---------------
 src/misc.c | 20 ++++++++--------
 2 files changed, 59 insertions(+), 28 deletions(-)

diff --git a/src/main.c b/src/main.c
index 24e0058..3ca00e7 100644
--- a/src/main.c
+++ b/src/main.c
@@ -61,33 +61,49 @@ int main(int argc, char *argv[])
     world.monster = 0;
     world.item = 0;
     init_map_object_defs(&world, "defs");
-    uint8_t err = 0;
 
     /* For interactive mode, try to load world state from savefile. */
+    char * err_o = "Error loading game: "
+                   "Unable to open 'savefile' for reading.";
+    char * err_r = "Error loading game: "
+                   "Trouble reading from opened 'savefile'.";
+    char * err_c = "Error loading game: "
+                   "Unable to close opened 'savefile'.";
+    char * savefile = "savefile";
     FILE * file;
-    if (1 == world.interactive && 0 == access("savefile", F_OK))
+    if (1 == world.interactive && 0 == access(savefile, F_OK))
     {
-        file = fopen("savefile", "r");
-        err = err | read_uint32_bigendian(file, &world.seed);
-        err = err | read_uint32_bigendian(file, &world.turn);
-        err = err | read_uint16_bigendian(file, &player.pos.y);
-        err = err | read_uint16_bigendian(file, &player.pos.x);
+        file = fopen(savefile, "r");
+        exit_err(0 == file, &world, err_o);
+        if (   read_uint32_bigendian(file, &world.seed)
+            || read_uint32_bigendian(file, &world.turn)
+            || read_uint16_bigendian(file, &player.pos.y)
+            || read_uint16_bigendian(file, &player.pos.x)
+            || read_uint8(file, &player.hitpoints)
+            || read_map_objects(&world, &world.monster, file)
+            || read_map_objects(&world, &world.item,    file))
+        {
+            exit_err(1, &world, err_r);
+        }
+        exit_err(fclose(file), &world, err_c);
         player.pos.y--;
         player.pos.x--;
-        err = err | read_uint8(file, &player.hitpoints);
-        err = err | read_map_objects(&world, &world.monster, file);
-        err = err | read_map_objects(&world, &world.item,    file);
-        fclose(file);
     }
 
     /* For non-interactive mode, try to load world state from record file. */
     else
     {
+        err_o = "Error loading record file: "
+                "Unable to open file 'record' for reading.";
+        err_r = "Error loading record file: "
+                "Trouble reading from opened file 'record'.";
+        char * recordfile = "record";
         world.turn = 1;
         if (0 == world.interactive)
         {
-            file = fopen("record", "r");
-            err = err | read_uint32_bigendian(file, &world.seed);
+            file = fopen(recordfile, "r");
+            exit_err(0 == file, &world, err_o);
+            exit_err(read_uint32_bigendian(file, &world.seed), &world, err_r);
         }
 
         /* For interactive-mode in newly started world, generate a start seed
@@ -95,15 +111,28 @@ int main(int argc, char *argv[])
          */
         else
         {
-            file = fopen("record", "w");
             world.seed = time(NULL);
-            err = err | write_uint32_bigendian(world.seed, file);
-            fclose(file);
+
+            char * err_x = "Error recording new seed: "
+                           "A file 'record' already exists, when it shouldn't.";
+            err_o        = "Error recording new seed: "
+                           "Unable to open 'record_tmp' file for writing.";
+            char * err_w = "Error recording new seed: "
+                           "Trouble writing to opened 'record_tmp' file.";
+            err_c        = "Error recording new seed: "
+                           "Unable to close opened file 'record_tmp'.";
+            char * err_m = "Error recording new seed: "
+                           "Unable to rename file 'record_tmp' to 'record'.";
+            char * recordfile_tmp = "record_tmp";
+            exit_err(!access(recordfile, F_OK), &world, err_x);
+            file = fopen(recordfile_tmp, "w");
+            exit_err(0 == file, &world, err_o);
+            exit_err(write_uint32_bigendian(world.seed, file), &world, err_w);
+            exit_err(fclose(file), &world, err_c);
+            exit_err(rename(recordfile_tmp, recordfile), &world, err_m);
         }
     }
 
-    exit_err(err, &world, "Failure initializing game.");
-
 
     /* Generate map from seed and, if newly generated world, start positions of
      * actors.
@@ -206,6 +235,8 @@ int main(int argc, char *argv[])
                                              &win_map, &win_info, &win_log);
                 if (1 == quit_called)
                 {
+                    err_c = "Error closing read 'record' file.";
+                    exit_err(fclose(file), &world, err_c);
                     exit_game(&world);
                 }
             }
diff --git a/src/misc.c b/src/misc.c
index 44e9a08..6f8c6c7 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -115,15 +115,15 @@ extern uint16_t center_offset(uint16_t pos, uint16_t mapsize,
 extern void turn_over(struct World * world, char action)
 {
     char * err_open  = "Error recording move: "
-                       "Unable to open 'record_tmp' for appending.";
+                       "Unable to open file 'record_tmp' for appending.";
     char * err_write = "Error recording move: "
-                       "Trouble writing to opened 'record_tmp'.";
+                       "Trouble writing to opened file 'record_tmp'.";
     char * err_close = "Error recording move: "
-                       "Unable to close opened 'record_tmp'.";
+                       "Unable to close opened file 'record_tmp'.";
     char * err_unl   = "Error recording move: "
-                       "Unable to unlink old 'record' file.";
+                       "Unable to unlink old file 'record'.";
     char * err_move  = "Error recording move: "
-                        "Unable to rename 'record_tmp' to 'record'.";
+                        "Unable to rename file 'record_tmp' to 'record'.";
     char * recordfile_tmp = "record_tmp";
     char * recordfile     = "record";
     if (1 == world->interactive)
@@ -152,15 +152,15 @@ extern void turn_over(struct World * world, char action)
 extern void save_game(struct World * world)
 {
     char * err_open  = "Error saving game: "
-                       "Unable to open 'savefile_tmp' for writing.";
+                       "Unable to open file 'savefile_tmp' for writing.";
     char * err_write = "Error saving game: "
-                       "Trouble writing to opened 'savefile_tmp'.";
+                       "Trouble writing to opened file 'savefile_tmp'.";
     char * err_close = "Error saving game: "
-                       "Unable to close opened 'savefile_tmp'.";
+                       "Unable to close opened file 'savefile_tmp'.";
     char * err_unl   = "Error saving game: "
-                       "Unable to unlink old 'savefile'.";
+                       "Unable to unlink old 'savefile' file.";
     char * err_move  = "Error saving game: "
-                        "Unable to rename 'savefile_tmp' to 'savefile'.";
+                        "Unable to rename 'file savefile_tmp' to 'savefile'.";
     char * savefile_tmp = "savefile_tmp";
     char * savefile     = "savefile";
     FILE * file = fopen(savefile_tmp, "w");
-- 
2.30.2