From f3640ecf4fa5edd34d1bd4409557a2fccf1585a8 Mon Sep 17 00:00:00 2001
From: Paul Smith <psmith@gnu.org>
Date: Sun, 25 Sep 2022 16:12:21 -0400
Subject: [PATCH] Provide new functions to convert long long to string

The previous attempt to use PRI* macros to avoid compiler-specific
printf format specifiers didn't work because we are using raw
long long type, not the uintX_t types.  On systems where long and
long long are the same size, uint64_t might be type "long" and PRId64
is just "ld".

Instead write new functions that convert [unsigned] long long to a
string and call those instead.

* src/makeint.h: Declare make_lltoa() and make_ulltoa().
* src/misc.c (make_lltoa): New function that writes a long long value
into a provided buffer.  Return the buffer for ease-of-use.
(make_ulltoa): Ditto, for unsigned long long.
* src/function.c (func_wordlist): Call these new methods.  Also
rework the error strings so we share the translated string.
* src/dir.c (print_dir_data_base): Call the new methods instead of
using MSVC macros.
---
 src/dir.c      | 39 +++++++++++++++++----------------------
 src/function.c | 16 +++++++---------
 src/makeint.h  | 10 ++--------
 src/misc.c     | 24 ++++++++++++++++++++++++
 4 files changed, 50 insertions(+), 39 deletions(-)

diff --git a/src/dir.c b/src/dir.c
index f18a02e0..cca968c7 100644
--- a/src/dir.c
+++ b/src/dir.c
@@ -1102,6 +1102,9 @@ print_dir_data_base (void)
   unsigned int impossible;
   struct directory **dir_slot;
   struct directory **dir_end;
+#ifdef WINDOWS32
+  char buf[INTSTR_LENGTH + 1];
+#endif
 
   puts (_("\n# Directories\n"));
 
@@ -1117,24 +1120,19 @@ print_dir_data_base (void)
           if (dir->contents == 0)
             printf (_("# %s: could not be stat'd.\n"), dir->name);
           else if (dir->contents->dirfiles.ht_vec == 0)
-            {
 #ifdef WINDOWS32
-              printf (_("# %s (key %s, mtime %I64u): could not be opened.\n"),
-                      dir->name, dir->contents->path_key,
-                      (unsigned long long)dir->contents->mtime);
-#else  /* WINDOWS32 */
-#ifdef VMS_INO_T
-              printf (_("# %s (device %d, inode [%d,%d,%d]): could not be opened.\n"),
-                      dir->name, dir->contents->dev,
-                      dir->contents->ino[0], dir->contents->ino[1],
-                      dir->contents->ino[2]);
+            printf (_("# %s (key %s, mtime %s): could not be opened.\n"),
+                    dir->name, dir->contents->path_key,
+                    make_ulltoa ((unsigned long long)dir->contents->mtime, buf));
+#elif defined(VMS_INO_T)
+            printf (_("# %s (device %d, inode [%d,%d,%d]): could not be opened.\n"),
+                    dir->name, dir->contents->dev,
+                    dir->contents->ino[0], dir->contents->ino[1],
+                    dir->contents->ino[2]);
 #else
-              printf (_("# %s (device %ld, inode %ld): could not be opened.\n"),
-                      dir->name, (long int) dir->contents->dev,
-                      (long int) dir->contents->ino);
+            printf (_("# %s (device %ld, inode %ld): could not be opened.\n"),
+                    dir->name, (long) dir->contents->dev, (long) dir->contents->ino);
 #endif
-#endif /* WINDOWS32 */
-            }
           else
             {
               unsigned int f = 0;
@@ -1156,21 +1154,18 @@ print_dir_data_base (void)
                     }
                 }
 #ifdef WINDOWS32
-              printf (_("# %s (key %s, mtime %I64u): "),
+              printf (_("# %s (key %s, mtime %s): "),
                       dir->name, dir->contents->path_key,
-                      (unsigned long long)dir->contents->mtime);
-#else  /* WINDOWS32 */
-#ifdef VMS_INO_T
+                      make_ulltoa ((unsigned long long)dir->contents->mtime, buf));
+#elif defined(VMS_INO_T)
               printf (_("# %s (device %d, inode [%d,%d,%d]): "),
                       dir->name, dir->contents->dev,
                       dir->contents->ino[0], dir->contents->ino[1],
                       dir->contents->ino[2]);
 #else
-              printf (_("# %s (device %ld, inode %ld): "),
-                      dir->name,
+              printf (_("# %s (device %ld, inode %ld): "), dir->name,
                       (long)dir->contents->dev, (long)dir->contents->ino);
 #endif
-#endif /* WINDOWS32 */
               if (f == 0)
                 fputs (_("No"), stdout);
               else
diff --git a/src/function.c b/src/function.c
index ce6e7603..6dbafdf9 100644
--- a/src/function.c
+++ b/src/function.c
@@ -816,20 +816,18 @@ func_word (char *o, char **argv, const char *funcname UNUSED)
 static char *
 func_wordlist (char *o, char **argv, const char *funcname UNUSED)
 {
+  char buf[INTSTR_LENGTH + 1];
   long long start, stop, count;
+  const char* badfirst = _("invalid first argument to 'wordlist' function");
+  const char* badsecond = _("invalid second argument to 'wordlist' function");
 
-  start = parse_numeric (argv[0],
-                         _("invalid first argument to 'wordlist' function"));
-  stop = parse_numeric (argv[1],
-                        _("invalid second argument to 'wordlist' function"));
-
+  start = parse_numeric (argv[0], badfirst);
   if (start < 1)
-    ON (fatal, *expanding_var,
-        "invalid first argument to 'wordlist' function: '%" PRId64 "'", start);
+    OSS (fatal, *expanding_var, "%s: '%s'", badfirst, make_lltoa (start, buf));
 
+  stop = parse_numeric (argv[1], badsecond);
   if (stop < 0)
-    ON (fatal, *expanding_var,
-        "invalid second argument to 'wordlist' function: '%" PRId64 "'", stop);
+    OSS (fatal, *expanding_var, "%s: '%s'", badsecond, make_lltoa (stop, buf));
 
   count = stop - start + 1;
 
diff --git a/src/makeint.h b/src/makeint.h
index a9cc864f..b8dbc1ff 100644
--- a/src/makeint.h
+++ b/src/makeint.h
@@ -290,14 +290,6 @@ char *strerror (int errnum);
 
 #if HAVE_INTTYPES_H
 # include <inttypes.h>
-#else
-# ifndef PRId64
-#  ifdef WINDOWS32
-#   define PRId64 "I64d"
-#  else
-#   define PRId64 "lld"
-#  endif
-# endif
 #endif
 #if HAVE_STDINT_H
 # include <stdint.h>
@@ -543,6 +535,8 @@ void pfatal_with_name (const char *) NORETURN;
 void perror_with_name (const char *, const char *);
 #define xstrlen(_s) ((_s)==NULL ? 0 : strlen (_s))
 unsigned int make_toui (const char*, const char**);
+char *make_lltoa (long long, char *);
+char *make_ulltoa (unsigned long long, char *);
 pid_t make_pid ();
 void *xmalloc (size_t);
 void *xcalloc (size_t);
diff --git a/src/misc.c b/src/misc.c
index c309084e..8f9be328 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -54,6 +54,30 @@ make_toui (const char *str, const char **error)
   return val;
 }
 
+#if WINDOWS32
+  /* MSVCRT does not support the 'll' format specifier.  */
+# define LLFMT "I64"
+#else
+# define LLFMT "ll"
+#endif
+
+/* Convert val into a string, written to buf.  buf must be large enough
+   to hold the largest possible value, plus a nul byte.  Returns buf.  */
+
+char *
+make_lltoa (long long val, char *buf)
+{
+  sprintf (buf, "%" LLFMT "d", val);
+  return buf;
+}
+
+char *
+make_ulltoa (unsigned long long val, char *buf)
+{
+  sprintf (buf, "%" LLFMT "u", val);
+  return buf;
+}
+
 /* Compare strings *S1 and *S2.
    Return negative if the first is less, positive if it is greater,
    zero if they are equal.  */