--- otherlibs/stdune/src/readdir.c	2026-03-11 20:49:26.711769000 -0500
+++ otherlibs/stdune/src/readdir.c	2026-03-11 20:49:26.713044510 -0500
@@ -1,3 +1,16 @@
+/*
+ * readdir.c - illumos/Solaris compatible version for dune
+ *
+ * This file replaces otherlibs/stdune/dune_filesystem_stubs/readdir.c
+ * in dune to fix compilation on illumos/Solaris where struct dirent
+ * does not have d_type field.
+ *
+ * Apply by copying this file to:
+ *   dune-X.Y.Z/otherlibs/stdune/dune_filesystem_stubs/readdir.c
+ *
+ * For opam's bundled dune-local:
+ *   opam-X.Y.Z/src_ext/dune-local/otherlibs/stdune/dune_filesystem_stubs/readdir.c
+ */
 
 #include <caml/alloc.h>
 #include <caml/memory.h>
@@ -13,12 +26,27 @@
 #include <errno.h>
 #include <sys/types.h>
 #include <dirent.h>
+
+/* illumos/Solaris compatibility - d_type not in struct dirent */
+#ifndef DT_UNKNOWN
+#define DT_UNKNOWN 0
+#define DT_FIFO    1
+#define DT_CHR     2
+#define DT_DIR     4
+#define DT_BLK     6
+#define DT_REG     8
+#define DT_LNK    10
+#define DT_SOCK   12
+#define DT_WHT    14
+#define ILLUMOS_NO_DTYPE 1
+#endif
+
 typedef struct dirent directory_entry;
 
 value val_file_type(int typ) {
   switch(typ)
     {
-#ifndef __HAIKU__     
+#ifndef __HAIKU__
    case DT_REG:
       return Val_int(0);
     case DT_DIR:
@@ -64,10 +92,11 @@
   v_filename = caml_copy_string(e->d_name);
   v_tuple = caml_alloc_small(2, 0);
   Field(v_tuple, 0) = v_filename;
-#ifndef __HAIKU__
-  Field(v_tuple, 1) = val_file_type(e->d_type);
-#else
+#if defined(__HAIKU__) || defined(ILLUMOS_NO_DTYPE)
+  /* illumos/Solaris don't have d_type, always return unknown */
   Field(v_tuple, 1) = Val_int(7);
+#else
+  Field(v_tuple, 1) = val_file_type(e->d_type);
 #endif
   CAMLreturn(v_tuple);
 }
--- vendor/notty/src-unix/native/winsize.c	2026-03-11 20:49:26.714264146 -0500
+++ vendor/notty/src-unix/native/winsize.c	2026-03-11 20:49:26.715364983 -0500
@@ -1,44 +1,55 @@
+/*
+ * winsize.c - illumos/Solaris compatible version for notty (used by dune)
+ *
+ * This file replaces vendor/notty/src-unix/native/winsize.c
+ * in dune to fix compilation on illumos/Solaris where TIOCGWINSZ
+ * is defined in sys/termios.h rather than sys/ioctl.h.
+ *
+ * Apply by copying this file to:
+ *   dune-X.Y.Z/vendor/notty/src-unix/native/winsize.c
+ *
+ * For opam's bundled dune-local:
+ *   opam-X.Y.Z/src_ext/dune-local/vendor/notty/src-unix/native/winsize.c
+ */
+
 #include <caml/mlvalues.h>
+#include <caml/alloc.h>
+#include <caml/fail.h>
 
-#ifdef _WIN32
-#include <windows.h>
+#ifdef __sun
+/* illumos/Solaris: struct winsize and TIOCGWINSZ in termios */
+#include <sys/termios.h>
+#include <sys/ioctl.h>
 #else
 #include <sys/ioctl.h>
-#include <signal.h>
 #endif
 
-#ifdef __HAIKU__
-/* On some platforms, ioctl() is declared in <unistd.h>. */
 #include <unistd.h>
-#endif
-
-CAMLprim value caml_notty_winsize (value vfd) {
-#ifdef _WIN32
-  (void) vfd;
-  HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
-  if (hConsole == INVALID_HANDLE_VALUE) return Val_int (0);
+#include <signal.h>
 
-  CONSOLE_SCREEN_BUFFER_INFO csbi;
-  if (GetConsoleScreenBufferInfo(hConsole, &csbi)) {
-    int columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
-    int rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
-    return Val_int ((columns << 16) + ((rows & 0x7fff) << 1));
-  } 
-  return Val_int (0);
-#else
-  int fd = Int_val (vfd);
+CAMLprim value caml_notty_winsize (value fd) {
+#ifdef TIOCGWINSZ
   struct winsize w;
-  if (ioctl (fd, TIOCGWINSZ, &w) >= 0)
-    return Val_int ((w.ws_col << 16) + ((w.ws_row & 0x7fff) << 1));
-  return Val_int (0);
+  if (ioctl(Int_val(fd), TIOCGWINSZ, &w) == -1)
+    caml_failwith("ioctl");
+  value res = caml_alloc_small(2, 0);
+  Field(res, 0) = Val_int(w.ws_col);
+  Field(res, 1) = Val_int(w.ws_row);
+  return res;
+#else
+  /* Fallback: return default 80x24 */
+  value res = caml_alloc_small(2, 0);
+  Field(res, 0) = Val_int(80);
+  Field(res, 1) = Val_int(24);
+  return res;
 #endif
 }
 
-CAMLprim value caml_notty_winch_number (value vunit) {
-  (void) vunit;
-#ifdef _WIN32
-  return Val_int (0);
+/* Return SIGWINCH signal number */
+CAMLprim value caml_notty_winch_number (value unit) {
+#ifdef SIGWINCH
+  return Val_int(SIGWINCH);
 #else
-  return Val_int (SIGWINCH);
+  return Val_int(0);
 #endif
 }
--- otherlibs/stdune/src/wait4_stubs.c	2026-03-11 20:49:26.716620588 -0500
+++ otherlibs/stdune/src/wait4_stubs.c	2026-03-11 20:49:26.717848047 -0500
@@ -1,83 +1,51 @@
 #include <caml/mlvalues.h>
-
 #ifdef _WIN32
 #include <caml/fail.h>
-
-void dune_wait4(value v_pid, value flags) {
-  (void)flags;
-  caml_failwith("wait4: not supported on windows");
-}
-
+void dune_wait4(value v_pid, value flags) { caml_failwith("wait4: not supported"); }
 #else
-
 #include <caml/alloc.h>
 #include <caml/memory.h>
 #include <caml/signals.h>
 #include <caml/unixsupport.h>
-
 #include <sys/resource.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/wait.h>
-
+#include <errno.h>
+#include <string.h>
 #define TAG_WEXITED 0
 #define TAG_WSIGNALED 1
 #define TAG_WSTOPPED 2
-
-CAMLextern int caml_convert_signal_number(int);
 CAMLextern int caml_rev_convert_signal_number(int);
-
 static value alloc_process_status(int status) {
   value st;
-
-  if (WIFEXITED(status)) {
-    st = caml_alloc_small(1, TAG_WEXITED);
-    Field(st, 0) = Val_int(WEXITSTATUS(status));
-  } else if (WIFSTOPPED(status)) {
-    st = caml_alloc_small(1, TAG_WSTOPPED);
-    Field(st, 0) = Val_int(caml_rev_convert_signal_number(WSTOPSIG(status)));
-  } else {
-    st = caml_alloc_small(1, TAG_WSIGNALED);
-    Field(st, 0) = Val_int(caml_rev_convert_signal_number(WTERMSIG(status)));
-  }
+  if (WIFEXITED(status)) { st = caml_alloc_small(1, TAG_WEXITED); Field(st, 0) = Val_int(WEXITSTATUS(status)); }
+  else if (WIFSTOPPED(status)) { st = caml_alloc_small(1, TAG_WSTOPPED); Field(st, 0) = Val_int(caml_rev_convert_signal_number(WSTOPSIG(status))); }
+  else { st = caml_alloc_small(1, TAG_WSIGNALED); Field(st, 0) = Val_int(caml_rev_convert_signal_number(WTERMSIG(status))); }
   return st;
 }
-
 static int wait_flag_table[] = {WNOHANG, WUNTRACED};
-
-// see https://man7.org/linux/man-pages/man2/waitpid.2.html for a description of
-// possible v_pid values -1 is the one we typically use, which means wait for
-// any child process
 value dune_wait4(value v_pid, value flags) {
   CAMLparam2(v_pid, flags);
   CAMLlocal2(times, res);
-
-  int status, cv_flags;
-  struct timeval tp;
-  cv_flags = caml_convert_flag_list(flags, wait_flag_table);
-  pid_t pid = Int_val(v_pid);
-
+  int pid, status, cv_flags = caml_convert_flag_list(flags, wait_flag_table);
   struct rusage ru;
-
+  struct timeval tp;
+  memset(&ru, 0, sizeof(ru));
   caml_enter_blocking_section();
-  // returns the pid of the terminated process, or -1 on error
-  pid = wait4(pid, &status, cv_flags, &ru);
+  pid = waitpid(Int_val(v_pid), &status, cv_flags);
+  if (pid > 0) getrusage(RUSAGE_CHILDREN, &ru);
   gettimeofday(&tp, NULL);
   caml_leave_blocking_section();
-  if (pid == -1)
-    uerror("wait4", Nothing);
-
+  if (pid == -1) uerror("wait4", Nothing);
   times = caml_alloc_small(2 * Double_wosize, Double_array_tag);
   Store_double_field(times, 0, ru.ru_utime.tv_sec + ru.ru_utime.tv_usec / 1e6);
   Store_double_field(times, 1, ru.ru_stime.tv_sec + ru.ru_stime.tv_usec / 1e6);
-
   res = caml_alloc_tuple(4);
   Store_field(res, 0, Val_int(pid));
   Store_field(res, 1, alloc_process_status(status));
-  Store_field(res, 2,
-              caml_copy_double(((double)tp.tv_sec + (double)tp.tv_usec / 1e6)));
+  Store_field(res, 2, caml_copy_double(((double)tp.tv_sec + (double)tp.tv_usec / 1e6)));
   Store_field(res, 3, times);
   CAMLreturn(res);
 }
-
 #endif
