From 2129137640bcd8c94d80a9bed5c36dc641c7808c Mon Sep 17 00:00:00 2001
From: Marko Lindqvist <cazfi74@gmail.com>
Date: Sun, 18 Oct 2026 10:16:36 +0300
Subject: [PATCH 87/87] fc_stat(): Support nullptr 'buf'

See RM #2340

Signed-off-by: Marko Lindqvist <cazfi74@gmail.com>
---
 client/options.c   |  5 ++---
 server/fcdb.c      |  5 ++---
 server/stdinhand.c | 25 ++++++++++---------------
 utility/registry.c |  4 +---
 utility/shared.c   |  4 +---
 utility/support.c  | 17 +++++++++++++++--
 6 files changed, 31 insertions(+), 29 deletions(-)

diff --git a/client/options.c b/client/options.c
index 3a82b1261b..6e394ba18e 100644
--- a/client/options.c
+++ b/client/options.c
@@ -5702,7 +5702,6 @@ static const char *get_last_option_file_name(bool *allow_digital_boolean)
     fc_strlcpy(name_buffer, OPTION_FILE_NAME, sizeof(name_buffer));
 #else
     int major, minor;
-    struct stat buf;
 
     name = freeciv_storage_dir();
     if (name == NULL) {
@@ -5719,7 +5718,7 @@ static const char *get_last_option_file_name(bool *allow_digital_boolean)
               : minor >= 0); minor--) {
         fc_snprintf(name_buffer, sizeof(name_buffer),
                     "%s" DIR_SEPARATOR NEW_OPTION_FILE_NAME, name, major, minor);
-        if (0 == fc_stat(name_buffer, &buf)) {
+        if (0 == fc_stat(name_buffer, nullptr)) {
           if (MAJOR_NEW_OPTION_FILE_NAME != major
               || MINOR_NEW_OPTION_FILE_NAME != minor) {
             log_normal(_("Didn't find '%s' option file, "
@@ -5751,7 +5750,7 @@ static const char *get_last_option_file_name(bool *allow_digital_boolean)
          minor--) {
       fc_snprintf(name_buffer, sizeof(name_buffer),
                   "%s" DIR_SEPARATOR MID_OPTION_FILE_NAME, name, major, minor);
-      if (0 == fc_stat(name_buffer, &buf)) {
+      if (0 == fc_stat(name_buffer, nullptr)) {
         log_normal(_("Didn't find '%s' option file, "
                      "loading from '%s' instead."),
                    get_current_option_file_name() + strlen(name) + 1,
diff --git a/server/fcdb.c b/server/fcdb.c
index 64a1822d39..9a2b64616e 100644
--- a/server/fcdb.c
+++ b/server/fcdb.c
@@ -122,12 +122,11 @@ static bool fcdb_load_config(const char *filename)
 {
   struct section_file *secfile;
   const char *val;
-  struct stat buf;
 
   fc_assert_ret_val(filename != nullptr, FALSE);
 
-  /* does file exist? */
-  if (fc_stat(filename, &buf) != 0) {
+  /* Does file exist? */
+  if (fc_stat(filename, nullptr) != 0) {
     log_error(_("Cannot load fcdb config file '%s':\n%s"), filename,
               fc_strerror(fc_get_errno()));
     return FALSE;
diff --git a/server/stdinhand.c b/server/stdinhand.c
index 9979209a39..f36b5f234e 100644
--- a/server/stdinhand.c
+++ b/server/stdinhand.c
@@ -3850,18 +3850,14 @@ bool load_command(struct connection *caller, const char *filename, bool check)
     }
   }
 
-  {
-    struct stat buf;
-
-    /* does file exist? */
-    if (fc_stat(arg, &buf) != 0) {
-      log_error("Error loading savefile '%s': %s", arg,
-                fc_strerror(fc_get_errno()));
-      cmd_reply(CMD_LOAD, caller, C_FAIL, _("Could not load savefile: %s"),
-                arg);
-      dlsend_packet_game_load(game.est_connections, FALSE, arg);
-      return FALSE;
-    }
+  /* Does file exist? */
+  if (fc_stat(arg, nullptr) != 0) {
+    log_error("Error loading savefile '%s': %s", arg,
+              fc_strerror(fc_get_errno()));
+    cmd_reply(CMD_LOAD, caller, C_FAIL, _("Could not load savefile: %s"),
+              arg);
+    dlsend_packet_game_load(game.est_connections, FALSE, arg);
+    return FALSE;
   }
 
   /* Attempt to parse the file */
@@ -5008,7 +5004,6 @@ static const char *lua_accessor(int i)
 static bool lua_command(struct connection *caller, char *arg, bool check,
                         int read_recursion)
 {
-  struct stat statbuf;
   const char extension[] = ".lua", *real_filename = nullptr;
   char luafile[4096], tilde_filename[4096];
   char *tokens[1], *luaarg = nullptr;
@@ -5141,7 +5136,7 @@ static bool lua_command(struct connection *caller, char *arg, bool check,
               _("Loading Freeciv script file '%s'."), real_filename);
 
     if (is_reg_file_for_access(real_filename, FALSE)
-        && !fc_stat(real_filename, &statbuf)) {
+        && !fc_stat(real_filename, nullptr)) {
       ret = script_server_do_file(caller, real_filename);
     } else {
       cmd_reply(CMD_LUA, caller, C_FAIL,
@@ -5154,7 +5149,7 @@ static bool lua_command(struct connection *caller, char *arg, bool check,
               _("Loading Freeciv script file '%s'."), real_filename);
 
     if (is_reg_file_for_access(real_filename, FALSE)
-        && !fc_stat(real_filename, &statbuf)) {
+        && !fc_stat(real_filename, nullptr)) {
       ret = script_server_unsafe_do_file(caller, real_filename);
     } else {
       cmd_reply(CMD_LUA, caller, C_FAIL,
diff --git a/utility/registry.c b/utility/registry.c
index 9012784e97..b1bd6fb723 100644
--- a/utility/registry.c
+++ b/utility/registry.c
@@ -52,9 +52,7 @@ struct section_file *secfile_load(const char *filename,
                                   bool allow_duplicates)
 {
 #ifdef FREECIV_HAVE_XML_REGISTRY
-  struct stat buf;
-
-  if (fc_stat(filename, &buf) == 0) {
+  if (fc_stat(filename, nullptr) == 0) {
     xmlDoc *sec_doc;
 
     sec_doc = xmlReadFile(filename, nullptr, XML_PARSE_NOERROR);
diff --git a/utility/shared.c b/utility/shared.c
index dad285573b..b6b30b45ab 100644
--- a/utility/shared.c
+++ b/utility/shared.c
@@ -1132,10 +1132,8 @@ const char *fileinfoname(const struct strvec *dirs, const char *filename)
 #endif /* DIR_SEPARATOR_IS_DEFAULT */
 
   strvec_iterate(dirs, dirname) {
-    struct stat buf;    /* See if we can open the file or directory */
-
     astr_set(&realfile, "%s" DIR_SEPARATOR "%s", dirname, fnbuf);
-    if (fc_stat(astr_str(&realfile), &buf) == 0) {
+    if (fc_stat(astr_str(&realfile), nullptr) == 0) {
       return astr_str(&realfile);
     }
   } strvec_iterate_end;
diff --git a/utility/support.c b/utility/support.c
index 779ed93c5a..8469f7ce39 100644
--- a/utility/support.c
+++ b/utility/support.c
@@ -535,19 +535,32 @@ int fc_remove(const char *filename)
 /************************************************************************//**
   Wrapper function for stat() with filename conversion to local
   encoding on Windows.
+
+  @param   filename Name of the file to check
+  @param   buf      Stat struct to fill. Can be nullptr
+  @return  0 on success, -1 on failure
 ****************************************************************************/
 int fc_stat(const char *filename, struct stat *buf)
 {
+  struct stat dummy;
+  struct stat *ptr;
+
+  if (buf != nullptr) {
+    ptr = buf;
+  } else {
+    ptr = &dummy;
+  }
+
 #ifdef FREECIV_MSWINDOWS
   int result;
   char *filename_in_local_encoding =
     internal_to_local_string_malloc(filename);
 
-  result = stat(filename_in_local_encoding, buf);
+  result = stat(filename_in_local_encoding, ptr);
   free(filename_in_local_encoding);
   return result;
 #else  /* FREECIV_MSWINDOWS */
-  return stat(filename, buf);
+  return stat(filename, ptr);
 #endif /* FREECIV_MSWINDOWS */
 }
 
-- 
2.53.0

