forked from WHC/Hermit
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9862c29f58 | ||
|
|
929f7125b8 |
@@ -111,6 +111,29 @@ void os_localtime(const time_t *t, struct tm *out);
|
|||||||
int os_getpid(void);
|
int os_getpid(void);
|
||||||
int os_chmod_private(const char *path); /* best-effort 0600; no-op/ok on Win */
|
int os_chmod_private(const char *path); /* best-effort 0600; no-op/ok on Win */
|
||||||
|
|
||||||
|
/* --- UTF-8 --------------------------------------------------------------
|
||||||
|
* Encode one code point as UTF-8, independent of the C locale.
|
||||||
|
*
|
||||||
|
* wcrtomb() follows setlocale(). On Linux the environment normally selects a
|
||||||
|
* UTF-8 locale, but on Windows the C runtime maps setlocale(LC_ALL, "") to the
|
||||||
|
* ANSI code page -- 936 (GBK) on a Chinese system -- so wcrtomb() hands back
|
||||||
|
* GBK bytes where the protocol requires UTF-8, and the model receives
|
||||||
|
* mojibake. Windows' C runtime has no ".UTF-8" locale to switch to.
|
||||||
|
*
|
||||||
|
* Returns the number of bytes written (1..4), or 0 if the code point cannot be
|
||||||
|
* encoded. Surrogate halves (as they arrive on 16-bit wchar_t platforms for
|
||||||
|
* non-BMP code points) return 0 and are skipped. */
|
||||||
|
int os_utf8_encode(unsigned long cp, char out[4]);
|
||||||
|
|
||||||
|
/* Decode one UTF-8 sequence from s (at most avail bytes).
|
||||||
|
* Returns the number of bytes consumed (1..4) and stores the code point in
|
||||||
|
* *cp, or 0 if the bytes are not a valid, complete, non-overlong sequence.
|
||||||
|
*
|
||||||
|
* The counterpart of the encoder above, and needed for the same reason:
|
||||||
|
* mbrtowc() also follows setlocale(), so on Windows it would decode UTF-8
|
||||||
|
* text as GBK and the screen would show mojibake. */
|
||||||
|
int os_utf8_decode(const char *s, size_t avail, unsigned long *cp);
|
||||||
|
|
||||||
/* --- libc gaps on Windows ------------------------------------------------
|
/* --- libc gaps on Windows ------------------------------------------------
|
||||||
* MinGW-w64 provides neither strcasestr() (a GNU extension) nor
|
* MinGW-w64 provides neither strcasestr() (a GNU extension) nor
|
||||||
* setenv()/unsetenv() (POSIX): they are simply absent from its headers.
|
* setenv()/unsetenv() (POSIX): they are simply absent from its headers.
|
||||||
|
|||||||
@@ -22,6 +22,45 @@
|
|||||||
#define ERRBODY_MAX 4096
|
#define ERRBODY_MAX 4096
|
||||||
#define LLM_MAX_RETRIES 3
|
#define LLM_MAX_RETRIES 3
|
||||||
|
|
||||||
|
/* Point libcurl at a CA bundle on Windows.
|
||||||
|
*
|
||||||
|
* An OpenSSL-based libcurl keeps the path it was built with -- MSYS2's is
|
||||||
|
* "/mingw64/etc/ssl/certs/ca-bundle.crt", which resolves to nothing once the
|
||||||
|
* binary runs outside an MSYS2 shell, and that build ignores CURL_CA_BUNDLE.
|
||||||
|
* Every HTTPS request then dies with CURLE_SSL_CACERT_BADFILE (77).
|
||||||
|
*
|
||||||
|
* Order: $CST_CA_BUNDLE, then ca-bundle.crt beside the executable. If neither
|
||||||
|
* exists nothing is set, so a libcurl linked against Schannel (the Windows
|
||||||
|
* certificate store) behaves exactly as before. */
|
||||||
|
static void llm_apply_ca_bundle(CURL *curl) {
|
||||||
|
#if defined(_WIN32)
|
||||||
|
static char ca[OS_PATH_MAX];
|
||||||
|
static int checked;
|
||||||
|
if (!checked) {
|
||||||
|
checked = 1;
|
||||||
|
const char *env = getenv("CST_CA_BUNDLE");
|
||||||
|
if (env && env[0]) {
|
||||||
|
snprintf(ca, sizeof(ca), "%s", env);
|
||||||
|
} else {
|
||||||
|
char dir[OS_PATH_MAX];
|
||||||
|
if (os_exe_dir(dir, sizeof(dir)) == 0)
|
||||||
|
os_path_join(ca, sizeof(ca), dir, "ca-bundle.crt");
|
||||||
|
}
|
||||||
|
if (ca[0]) {
|
||||||
|
FILE *f = fopen(ca, "rb");
|
||||||
|
if (f)
|
||||||
|
fclose(f);
|
||||||
|
else
|
||||||
|
ca[0] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ca[0])
|
||||||
|
curl_easy_setopt(curl, CURLOPT_CAINFO, ca);
|
||||||
|
#else
|
||||||
|
(void)curl;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/* --- wire body ----------------------------------------------------------- */
|
/* --- wire body ----------------------------------------------------------- */
|
||||||
|
|
||||||
static size_t wire_tool_cap(const cst_llm_request_t *req) {
|
static size_t wire_tool_cap(const cst_llm_request_t *req) {
|
||||||
@@ -621,6 +660,7 @@ int cst_llm_chat_stream(const cst_llm_request_t *req, char *err,
|
|||||||
ret = -1;
|
ret = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
llm_apply_ca_bundle(curl);
|
||||||
sse_reset(&s);
|
sse_reset(&s);
|
||||||
s.curl = curl;
|
s.curl = curl;
|
||||||
if (rec_dir && rec_dir[0]) {
|
if (rec_dir && rec_dir[0]) {
|
||||||
@@ -768,6 +808,7 @@ int cst_llm_list_models(const char *api_base, const char *api_key,
|
|||||||
snprintf(err, err_sz, "curl init failed");
|
snprintf(err, err_sz, "curl init failed");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
llm_apply_ca_bundle(curl);
|
||||||
char url[1024];
|
char url[1024];
|
||||||
size_t bl = strlen(api_base);
|
size_t bl = strlen(api_base);
|
||||||
while (bl && api_base[bl - 1] == '/')
|
while (bl && api_base[bl - 1] == '/')
|
||||||
|
|||||||
@@ -1169,6 +1169,74 @@ void os_proc_kill(os_proc_t *p) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* --- UTF-8 ---------------------------------------------------------------
|
||||||
|
* See the declaration in os_compat.h for why wcrtomb() is not used. */
|
||||||
|
int os_utf8_encode(unsigned long cp, char out[4]) {
|
||||||
|
if (cp < 0x80) {
|
||||||
|
out[0] = (char)cp;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (cp < 0x800) {
|
||||||
|
out[0] = (char)(0xC0 | (cp >> 6));
|
||||||
|
out[1] = (char)(0x80 | (cp & 0x3F));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
if (cp >= 0xD800 && cp <= 0xDFFF)
|
||||||
|
return 0; /* lone surrogate half from a 16-bit wchar_t */
|
||||||
|
if (cp < 0x10000) {
|
||||||
|
out[0] = (char)(0xE0 | (cp >> 12));
|
||||||
|
out[1] = (char)(0x80 | ((cp >> 6) & 0x3F));
|
||||||
|
out[2] = (char)(0x80 | (cp & 0x3F));
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
if (cp <= 0x10FFFF) {
|
||||||
|
out[0] = (char)(0xF0 | (cp >> 18));
|
||||||
|
out[1] = (char)(0x80 | ((cp >> 12) & 0x3F));
|
||||||
|
out[2] = (char)(0x80 | ((cp >> 6) & 0x3F));
|
||||||
|
out[3] = (char)(0x80 | (cp & 0x3F));
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See the declaration in os_compat.h for why mbrtowc() is not used. */
|
||||||
|
int os_utf8_decode(const char *s, size_t avail, unsigned long *cp) {
|
||||||
|
const unsigned char *p = (const unsigned char *)s;
|
||||||
|
int need, i;
|
||||||
|
unsigned long v;
|
||||||
|
static const unsigned long MINV[4] = {0, 0x80, 0x800, 0x10000};
|
||||||
|
|
||||||
|
if (!s || !cp || avail == 0)
|
||||||
|
return 0;
|
||||||
|
if (p[0] < 0x80) {
|
||||||
|
*cp = p[0];
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if ((p[0] & 0xE0) == 0xC0) {
|
||||||
|
need = 1;
|
||||||
|
v = p[0] & 0x1Fu;
|
||||||
|
} else if ((p[0] & 0xF0) == 0xE0) {
|
||||||
|
need = 2;
|
||||||
|
v = p[0] & 0x0Fu;
|
||||||
|
} else if ((p[0] & 0xF8) == 0xF0) {
|
||||||
|
need = 3;
|
||||||
|
v = p[0] & 0x07u;
|
||||||
|
} else {
|
||||||
|
return 0; /* not a UTF-8 lead byte */
|
||||||
|
}
|
||||||
|
if (avail < (size_t)need + 1)
|
||||||
|
return 0; /* truncated */
|
||||||
|
for (i = 1; i <= need; i++) {
|
||||||
|
if ((p[i] & 0xC0) != 0x80)
|
||||||
|
return 0; /* bad continuation byte */
|
||||||
|
v = (v << 6) | (p[i] & 0x3Fu);
|
||||||
|
}
|
||||||
|
if (v < MINV[need] || (v >= 0xD800 && v <= 0xDFFF) || v > 0x10FFFF)
|
||||||
|
return 0; /* overlong, surrogate, or out of range */
|
||||||
|
*cp = v;
|
||||||
|
return need + 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* --- libc gaps on Windows -----------------------------------------------
|
/* --- libc gaps on Windows -----------------------------------------------
|
||||||
* strcasestr() is a GNU extension and setenv()/unsetenv() are POSIX; glibc
|
* strcasestr() is a GNU extension and setenv()/unsetenv() are POSIX; glibc
|
||||||
* has all three, MinGW-w64 has none of them. src/tui.c needs them (command
|
* has all three, MinGW-w64 has none of them. src/tui.c needs them (command
|
||||||
|
|||||||
+12
-6
@@ -3,23 +3,29 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "cst_textsel.h"
|
#include "cst_textsel.h"
|
||||||
|
#include "os_compat.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <wctype.h>
|
#include <wctype.h>
|
||||||
|
|
||||||
int cst_utf8_decode(const char *s, size_t avail, wchar_t *wc, size_t *eaten) {
|
int cst_utf8_decode(const char *s, size_t avail, wchar_t *wc, size_t *eaten) {
|
||||||
|
unsigned long cp = 0;
|
||||||
|
int n;
|
||||||
if (avail == 0)
|
if (avail == 0)
|
||||||
return -1;
|
return -1;
|
||||||
mbstate_t st;
|
/* os_utf8_decode(), not mbrtowc(): the latter follows setlocale(), which on
|
||||||
memset(&st, 0, sizeof(st));
|
* Windows means the ANSI code page, so UTF-8 text would be decoded as GBK
|
||||||
size_t n = mbrtowc(wc, s, avail, &st);
|
* and rendered as mojibake. */
|
||||||
if (n == (size_t)-1 || n == (size_t)-2 || n == 0) {
|
n = os_utf8_decode(s, avail, &cp);
|
||||||
*wc = (unsigned char)s[0];
|
if (n <= 0) {
|
||||||
|
/* Not a valid sequence: fall back to one raw byte, as before. */
|
||||||
|
*wc = (wchar_t)(unsigned char)s[0];
|
||||||
*eaten = 1;
|
*eaten = 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
*eaten = n;
|
*wc = (wchar_t)cp;
|
||||||
|
*eaten = (size_t)n;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2966,12 +2966,10 @@ static void input_clear(tui_t *t) { input_set(t, ""); }
|
|||||||
|
|
||||||
static void input_put_wc(tui_t *t, wchar_t wc) {
|
static void input_put_wc(tui_t *t, wchar_t wc) {
|
||||||
char mb[8];
|
char mb[8];
|
||||||
mbstate_t st;
|
int n = os_utf8_encode((unsigned long)wc, mb);
|
||||||
memset(&st, 0, sizeof(st));
|
if (n <= 0)
|
||||||
size_t n = wcrtomb(mb, wc, &st);
|
|
||||||
if (n == (size_t)-1 || n == 0)
|
|
||||||
return;
|
return;
|
||||||
input_insert(t, mb, n);
|
input_insert(t, mb, (size_t)n);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void input_put_byte(tui_t *t, char ch) { input_insert(t, &ch, 1); }
|
static void input_put_byte(tui_t *t, char ch) { input_insert(t, &ch, 1); }
|
||||||
@@ -3601,12 +3599,10 @@ static int picker_run(tui_t *t, const char *title, char items[][128], int n,
|
|||||||
}
|
}
|
||||||
if (iswprint(wch)) {
|
if (iswprint(wch)) {
|
||||||
char mb[8];
|
char mb[8];
|
||||||
mbstate_t st;
|
int k = os_utf8_encode((unsigned long)(wchar_t)wch, mb);
|
||||||
memset(&st, 0, sizeof(st));
|
if (k > 0 && qlen + (size_t)k < sizeof(query)) {
|
||||||
size_t k = wcrtomb(mb, (wchar_t)wch, &st);
|
memcpy(query + qlen, mb, (size_t)k);
|
||||||
if (k != (size_t)-1 && qlen + k < sizeof(query)) {
|
qlen += (size_t)k;
|
||||||
memcpy(query + qlen, mb, k);
|
|
||||||
qlen += k;
|
|
||||||
query[qlen] = 0;
|
query[qlen] = 0;
|
||||||
sel = top = 0;
|
sel = top = 0;
|
||||||
}
|
}
|
||||||
@@ -6144,12 +6140,10 @@ static void run_ask_modal(tui_t *t) {
|
|||||||
}
|
}
|
||||||
if (iswprint(wch)) {
|
if (iswprint(wch)) {
|
||||||
char mb[8];
|
char mb[8];
|
||||||
mbstate_t st;
|
int k = os_utf8_encode((unsigned long)(wchar_t)wch, mb);
|
||||||
memset(&st, 0, sizeof(st));
|
|
||||||
size_t k = wcrtomb(mb, (wchar_t)wch, &st);
|
|
||||||
size_t n = strlen(t->ask_buf);
|
size_t n = strlen(t->ask_buf);
|
||||||
if (k != (size_t)-1 && n + k + 1 < sizeof(t->ask_buf)) {
|
if (k > 0 && n + (size_t)k + 1 < sizeof(t->ask_buf)) {
|
||||||
memcpy(t->ask_buf + n, mb, k);
|
memcpy(t->ask_buf + n, mb, (size_t)k);
|
||||||
t->ask_buf[n + k] = 0;
|
t->ask_buf[n + k] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user