diff --git a/tools/.gitignore b/tools/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..09ec20cd417e9c18f75dc9c87d7425e65f485045
--- /dev/null
+++ b/tools/.gitignore
@@ -0,0 +1,6 @@
+*
+!.gitignore
+!*.c
+!*.cc
+!*.cpp
+!*.sh
diff --git a/tools/realpath_test.c b/tools/realpath_test.c
new file mode 100644
index 0000000000000000000000000000000000000000..0753dab4eeb1a2283e380303c064cca0457e2ec5
--- /dev/null
+++ b/tools/realpath_test.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2024 Frederick H. G. Wright II <fw@fwright.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * This provides a purely manual realpath test, for investigative purposes.
+ * It reports the results for a given input, for all available versions,
+ * with and without a supplied output buffer.  It always reports the results
+ * of the unadulterated OS functions, and optionally reports the results of
+ * the functions provided by a specified library.
+ */
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <libgen.h>
+#include <limits.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stddef.h>  /* For NULL */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/wait.h>
+
+#include <malloc/malloc.h>  /* For malloc_size() */
+
+/* RTLD_FIRST is unavailable on 10.4.  It's probably unimportant, anyway. */
+#ifndef RTLD_FIRST
+#define RTLD_FIRST 0
+#endif
+
+/* Function type for realpath() */
+typedef char *rp_func_t(const char * __restrict, char * __restrict);
+
+typedef struct rp_entry_s {
+  int local;
+  const char *name;
+  rp_func_t *func;
+} rp_entry_t;
+
+static rp_entry_t funcs[] = {
+  {0, "realpath", NULL},
+  {0, "realpath$UNIX2003", NULL},
+  {0, "realpath$DARWIN_EXTSN", NULL},
+  {1, "realpath", NULL},
+  {1, "realpath$UNIX2003", NULL},
+  {1, "realpath$DARWIN_EXTSN", NULL},
+};
+#define NUM_FUNCS (sizeof(funcs) / sizeof(funcs[0]))
+
+typedef struct sig_entry_s {
+  int sig;
+  const char *name;
+} sig_entry_t;
+
+static sig_entry_t sigs[] = {
+  {SIGBUS, "Bus error"},
+  {SIGSEGV, "Segmentation violation"},
+};
+#define NUM_SIGS (sizeof(sigs) / sizeof(sigs[0]))
+
+static void *lib_handle = NULL;
+
+static int
+load_lib(const char *path, int verbose)
+{
+  lib_handle = dlopen(path, RTLD_LAZY | RTLD_LOCAL | RTLD_FIRST);
+  if (!lib_handle) return 1;
+  if (verbose) {
+    printf("  Loaded %s at handle 0x%0lX\n", path, (unsigned long) lib_handle);
+  }
+  return 0;
+}
+
+static void
+unload_lib(void)
+{
+  if (!lib_handle) return;
+  if (dlclose(lib_handle)) {
+    fprintf(stderr, "Can't close library: %s\n", dlerror());
+  }
+  lib_handle = NULL;
+}
+
+static void
+find_sym(rp_entry_t *ent, int verbose)
+{
+  void *handle = ent->local ? lib_handle : RTLD_NEXT;
+  const char *type = ent->local ? "lib" : "OS ";
+
+  if (!handle) return;
+  ent->func = (rp_func_t *) dlsym(handle, ent->name);
+
+  if (verbose) {
+    if (ent->func) {
+      printf("    Found:     %s %s at 0x%0lX\n",
+             type, ent->name, (unsigned long) ent->func);
+    } else {
+      printf("    Not found: %s %s\n", type, ent->name);
+    }
+  }
+}
+
+static void
+find_syms(int verbose)
+{
+  int i;
+
+  for (i = 0; i < NUM_FUNCS; ++i) {
+    find_sym(&funcs[i], verbose);
+  }
+}
+
+static const char *
+get_signame(int sig)
+{
+  int i;
+
+  for (i = 0; i < NUM_SIGS; ++i) {
+    if (sigs[i].sig == sig) return sigs[i].name;
+  }
+  return NULL;
+}
+
+/*
+ * Some cases we test may crash with bad memory accesses.  We want to catch
+ * those without crashing the entire program.  The straightforward way
+ * to do that would be with a signal handler, but merely returning from
+ * the signal handler (after recording the signal) repeats the bad access,
+ * ad infinitum, and bailing from the handler via longjmp() isn't safe in
+ * this context.  So our simplest alternative is to run the test call in
+ * a subprocess, which then abnormally exits in any crash case.
+ *
+ * If we were to perform the test "for real" in a subprocess, we'd need
+ * to set up a shared communication page to capture the result for the
+ * main process.  In lieu of that, we use the subprocess for a "dry run",
+ * and then repeat the test in the main process if the dry run didn't crash.
+ */
+static void
+try_case(rp_entry_t *ent, const char *path, int no_buf)
+{
+  const char *type = ent->local ? "lib" : "OS ";
+  char *result;
+  pid_t child, done;
+  int status;
+  const char *signame;
+  char buf[PATH_MAX];
+
+  if (!ent->func) return;
+
+  printf("  %s %s (buf%s supplied) for '%s':\n",
+         type, ent->name, no_buf ? " not" : "", path);
+
+  /* First a "dry run" in a subprocess, in case it crashes. */
+  fflush(stdout);  /* Forking with unflushed buffers may cause trouble. */
+  child = fork();
+  if (child < 0) {
+    perror("fork() failed");
+    return;
+  }
+  if (child == 0) {
+    result = (*ent->func)(path, no_buf ? NULL : buf);
+    /* Leave any allocated buffer for the process exit to clean up. */
+    exit(0);
+  }
+  done = wait(&status);
+  if (done != child) {
+    fprintf(stderr, "***** Unexpected wait() pid, %d != %d\n", done, child);
+    return;
+  }
+  if (status) {
+    if(!(signame = get_signame(status))) signame = "(unknown)";
+    printf("    ***** crashed with exit status %d (%s)\n", status, signame);
+    return;
+  }
+
+  /* Now do it for real */
+  errno = 0;
+  result = (*ent->func)(path, no_buf ? NULL : buf);
+
+  if (result) {
+    if (errno) {
+      printf("    ***** 'success' with errno = %d (%s)\n",
+             errno, strerror(errno));
+    }
+    if (!no_buf && result != buf) {
+      printf("    ***** returned buffer adr 0x%0lX != supplied adr 0x%0lx\n",
+             (unsigned long) result, (unsigned long) buf);
+    }
+
+    printf("    '%s'\n", result);
+
+    /*
+     * There is a bug in the 32-bit 10.6 non-POSIX realpath(), where some
+     * cases that would normally be errors instead "succeed" with a pointer
+     * to an internal buffer (if one wasn't supplied by the caller), rather
+     * than one from malloc().  This buffer is probably unsafe to reference
+     * in general (though we do it above, apparently successfully), and
+     * cannot be freed with free(), thereby violating the API.
+     *
+     * We check for and report this case here.
+     */
+    if (no_buf) {
+      if (!malloc_size(result)) {
+        printf("    ***** returned buffer adr 0x%0lX is not from malloc()\n",
+               (unsigned long) result);
+      } else {
+        free(result);
+      }
+    }
+  } else {
+    printf("    failed, errno = %d (%s)\n", errno, strerror(errno));
+  }
+}
+
+static void
+try_all(const char *path)
+{
+  int i;
+
+  for (i = 0; i < NUM_FUNCS; ++i) {
+    try_case(&funcs[i], path, 0);
+  }
+  for (i = 0; i < NUM_FUNCS; ++i) {
+    try_case(&funcs[i], path, 1);
+  }
+}
+
+int
+main(int argc, char *argv[])
+{
+  int argn = 1, verbose = 0;
+  const char *lib_path = NULL;
+
+  while (argn < argc) {
+    if (!strcmp(argv[argn], "-h")) {
+      printf("Usage is: %s [-v] [-l <library path>] <test path>...\n",
+             basename(argv[0]));
+      return 0;
+    }
+    if (!strcmp(argv[argn], "-v")) {
+      verbose = 1;
+      ++argn;
+      continue;
+    }
+    if (!strcmp(argv[argn], "-l")) {
+      ++argn;
+      if (argn < argc) {
+        lib_path = argv[argn];
+        ++argn;
+      } else {
+        fprintf(stderr, "-l needs library path arg\n");
+        return 10;
+      }
+      continue;
+    }
+    break;
+  }
+
+  if (lib_path && load_lib(lib_path, verbose)) {
+    fprintf(stderr, "Can't open library %s: %s\n", lib_path, dlerror());
+    return 20;
+  }
+
+  find_syms(verbose);
+
+  if (argn >= argc) {
+    printf(" Defaulting test case to '.'\n");
+    try_all(".");
+  } else {
+    while (argn < argc) {
+      try_all(argv[argn]);
+      ++argn;
+    }
+  }
+
+  unload_lib();
+
+  return 0;
+}