curlx: replace `mbstowcs`/`wcstombs` with `_s` counterparts (Windows)

They are used in Windows-specific `fopen()`, `freopen`, `open()` and
`curlx_get_winapi_error()` calls, and in `fix_excessive_path()` in
Unicode builds.

Refs:
https://learn.microsoft.com/cpp/c-runtime-library/reference/mbstowcs-mbstowcs-l
https://learn.microsoft.com/cpp/c-runtime-library/reference/mbstowcs-s-mbstowcs-s-l
https://learn.microsoft.com/cpp/c-runtime-library/reference/wcstombs-wcstombs-l
https://learn.microsoft.com/cpp/c-runtime-library/reference/wcstombs-s-wcstombs-s-l

Also ban these functions via checksrc.

Co-authored-by: Jay Satiro

Closes #19581
diff --git a/lib/curl_setup.h b/lib/curl_setup.h
index d75de94..db438a8 100644
--- a/lib/curl_setup.h
+++ b/lib/curl_setup.h
@@ -97,8 +97,7 @@
 #ifndef _CRT_SECURE_NO_WARNINGS
 #define _CRT_SECURE_NO_WARNINGS  /* for __sys_errlist, __sys_nerr, _open(),
                                     _wfopen(), _wopen(), fopen(), freopen(),
-                                    getenv(), gmtime(), mbstowcs(), sprintf(),
-                                    strcpy(), wcstombs(),
+                                    getenv(), gmtime(), sprintf(), strcpy(),
                                     in tests: localtime(), open(), sscanf() */
 #endif
 #endif /* _MSC_VER */
diff --git a/lib/curlx/fopen.c b/lib/curlx/fopen.c
index f330753..dc1c2bb 100644
--- a/lib/curlx/fopen.c
+++ b/lib/curlx/fopen.c
@@ -97,15 +97,16 @@
 
 #ifndef _UNICODE
   /* convert multibyte input to unicode */
-  needed = mbstowcs(NULL, in, 0);
-  if(needed == (size_t)-1 || needed >= max_path_len)
+  if(mbstowcs_s(&needed, NULL, 0, in, 0))
     goto cleanup;
-  ++needed; /* for NUL */
+  if(!needed || needed >= max_path_len)
+    goto cleanup;
   ibuf = (malloc)(needed * sizeof(wchar_t));
   if(!ibuf)
     goto cleanup;
-  count = mbstowcs(ibuf, in, needed);
-  if(count == (size_t)-1 || count >= needed)
+  if(mbstowcs_s(&count, ibuf, needed, in, needed - 1))
+    goto cleanup;
+  if(count != needed)
     goto cleanup;
   in_w = ibuf;
 #else
@@ -193,15 +194,16 @@
 
 #ifndef _UNICODE
   /* convert unicode full path to multibyte output */
-  needed = wcstombs(NULL, fbuf, 0);
-  if(needed == (size_t)-1 || needed >= max_path_len)
+  if(wcstombs_s(&needed, NULL, 0, fbuf, 0))
     goto cleanup;
-  ++needed; /* for NUL */
+  if(!needed || needed >= max_path_len)
+    goto cleanup;
   obuf = (malloc)(needed);
   if(!obuf)
     goto cleanup;
-  count = wcstombs(obuf, fbuf, needed);
-  if(count == (size_t)-1 || count >= needed)
+  if(wcstombs_s(&count, obuf, needed, fbuf, needed - 1))
+    goto cleanup;
+  if(count != needed)
     goto cleanup;
   *out = obuf;
   obuf = NULL;
diff --git a/lib/curlx/winapi.c b/lib/curlx/winapi.c
index 2dc277c..7b3d6b6 100644
--- a/lib/curlx/winapi.c
+++ b/lib/curlx/winapi.c
@@ -52,6 +52,7 @@
 {
   char *p;
   wchar_t wbuf[256];
+  DWORD wlen;
 
   if(!buflen)
     return NULL;
@@ -62,23 +63,18 @@
   /* We return the local codepage version of the error string because if it is
      output to the user's terminal it will likely be with functions which
      expect the local codepage (eg fprintf, failf, infof). */
-  if(FormatMessageW((FORMAT_MESSAGE_FROM_SYSTEM |
-                     FORMAT_MESSAGE_IGNORE_INSERTS), NULL, err,
-                    LANG_NEUTRAL, wbuf, CURL_ARRAYSIZE(wbuf), NULL)) {
-    size_t written = wcstombs(buf, wbuf, buflen - 1);
-    if(written != (size_t)-1)
-      buf[written] = '\0';
-    else
-      *buf = '\0';
-  }
-
-  /* Truncate multiple lines */
-  p = strchr(buf, '\n');
-  if(p) {
-    if(p > buf && *(p-1) == '\r')
-      *(p-1) = '\0';
-    else
-      *p = '\0';
+  wlen = FormatMessageW((FORMAT_MESSAGE_FROM_SYSTEM |
+                         FORMAT_MESSAGE_IGNORE_INSERTS), NULL, err,
+                        LANG_NEUTRAL, wbuf, CURL_ARRAYSIZE(wbuf), NULL);
+  if(wlen && !wcstombs_s(NULL, buf, buflen, wbuf, wlen)) {
+    /* Truncate multiple lines */
+    p = strchr(buf, '\n');
+    if(p) {
+      if(p > buf && *(p-1) == '\r')
+        *(p-1) = '\0';
+      else
+        *p = '\0';
+    }
   }
 
   return *buf ? buf : NULL;
diff --git a/scripts/checksrc.pl b/scripts/checksrc.pl
index 88db59f..a2f458b 100755
--- a/scripts/checksrc.pl
+++ b/scripts/checksrc.pl
@@ -86,6 +86,8 @@
     "wcsdup" => 1,
     "wcscpy" => 1,
     "wcsncpy" => 1,
+    "mbstowcs" => 1,
+    "wcstombs" => 1,
     "LoadLibrary" => 1,
     "LoadLibraryA" => 1,
     "LoadLibraryW" => 1,