From 94b8f3f490a400d7205c57a0bec990736663799b Mon Sep 17 00:00:00 2001
From: Sergey Fedorov <vital.had@gmail.com>
Date: Sat, 4 Oct 2025 09:20:42 +0800
Subject: [PATCH] Support CMake and Nintendo Switch

Forward-port of https://github.com/xfangfang/mongoose/commit/18e2d6198651980c4f26093e1659adf6f72aac98
---
 CMakeLists.txt |  9 +++++++++
 mongoose.c     | 20 +++++++++++++++++---
 mongoose.h     | 34 +++++++++++++++++++++++++++++++++-
 3 files changed, 59 insertions(+), 4 deletions(-)
 create mode 100644 CMakeLists.txt

diff --git CMakeLists.txt CMakeLists.txt
new file mode 100644
index 00000000..cb8c8722
--- /dev/null
+++ CMakeLists.txt
@@ -0,0 +1,9 @@
+cmake_minimum_required(VERSION 3.15)
+
+add_library(mongoose STATIC ${CMAKE_CURRENT_SOURCE_DIR}/mongoose.c)
+
+if (__SWITCH__)
+    target_compile_options(mongoose PRIVATE -DMG_ARCH=10086)
+endif()
+
+target_include_directories(mongoose PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
\ No newline at end of file
diff --git mongoose.c mongoose.c
index b1b4b669..d157cf41 100644
--- mongoose.c
+++ mongoose.c
@@ -2121,14 +2121,19 @@ struct printdirentrydata {
 static void printdirentry(const char *name, void *userdata) {
   struct printdirentrydata *d = (struct printdirentrydata *) userdata;
   struct mg_fs *fs = d->opts->fs == NULL ? &mg_fs_posix : d->opts->fs;
-  size_t size = 0;
+  size_t size = strlen(d->dir);
   time_t t = 0;
   char path[MG_PATH_MAX], sz[40], mod[40];
   int flags, n = 0;
 
   // MG_DEBUG(("[%s] [%s]", d->dir, name));
-  if (mg_snprintf(path, sizeof(path), "%s%c%s", d->dir, '/', name) >
-      sizeof(path)) {
+  if (d->dir[size - 1] == '/') {
+      size = mg_snprintf(path, sizeof(path), "%s%s", d->dir, name);
+  } else {
+      size = mg_snprintf(path, sizeof(path), "%s%c%s", d->dir, '/', name);
+  }
+
+  if (size > sizeof(path)) {
     MG_ERROR(("%s truncated", name));
   } else if ((flags = fs->st(path, &size, &t)) == 0) {
     MG_ERROR(("%lu stat(%s)", d->c->id, path));
@@ -2214,6 +2219,14 @@ static void listdir(struct mg_connection *c, struct mg_http_message *hm,
             "  <tr><td><a href=\"..\">..</a></td>"
             "<td name=-1></td><td name=-1>[DIR]</td></tr>\n");
 
+#if MG_ARCH == MG_ARCH_NX
+  n = strlen(dir);
+  if ((n == 5 && strncmp(dir, "sdmc:", 5) == 0) || (n == 6 && strncmp(dir, "romfs:", 6) == 0)) {
+    // Add a trailing slash
+    dir[n++] = '/';
+    dir[n] = '\0';
+  }
+#endif
   fs->ls(dir, printdirentry, &d);
   mg_printf(c,
             "</tbody><tfoot><tr><td colspan=\"3\"><hr></td></tr></tfoot>"
@@ -2307,6 +2320,7 @@ void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
   char path[MG_PATH_MAX];
   const char *sp = opts->ssi_pattern;
   int flags = uri_to_path(c, hm, opts, path, sizeof(path));
+
   if (flags < 0) {
     // Do nothing: the response has already been sent by uri_to_path()
   } else if (flags & MG_FS_DIR) {
diff --git mongoose.h mongoose.h
index 6197e3f3..19e7ccf0 100644
--- mongoose.h
+++ mongoose.h
@@ -43,7 +43,8 @@ extern "C" {
 #define MG_ARCH_CMSIS_RTOS2 13  // CMSIS-RTOS API v2 (Keil RTX5, FreeRTOS)
 #define MG_ARCH_RTTHREAD 14     // RT-Thread RTOS
 #define MG_ARCH_ARMCGT 15       // Texas Semi ARM-CGT
-#define MG_ARCH_CUBE 16	        // STM32Cube environment
+#define MG_ARCH_CUBE 16         // STM32Cube environment
+#define MG_ARCH_NX 10086        // Nintendo Switch
 
 #define MG_ARCH_NEWLIB MG_ARCH_ARMGCC  // Alias, deprecate in 2025
 
@@ -52,6 +53,8 @@ extern "C" {
 #define MG_ARCH MG_ARCH_UNIX
 #elif defined(_WIN32)
 #define MG_ARCH MG_ARCH_WIN32
+#elif defined(__SWITCH__)
+#define MG_ARCH MG_ARCH_NX
 #endif
 #endif  // !defined(MG_ARCH)
 
@@ -342,6 +345,35 @@ int mkdir(const char *, mode_t);
 
 #endif
 
+#if MG_ARCH == MG_ARCH_NX
+#ifndef _POSIX_TIMERS
+#define _POSIX_TIMERS
+#endif
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <poll.h>
+#include <dirent.h>
+
+#define MG_PATH_MAX NAME_MAX
+#define MG_ENABLE_POLL 1
+#ifndef MG_ENABLE_DIRLIST
+#define MG_ENABLE_DIRLIST 1
+#endif
+#endif
 
 #if MG_ARCH == MG_ARCH_RTTHREAD
 
