From fe4ade53b73c6063a92d0382cbfca08fb34a0584 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 5 Aug 2025 13:26:44 +0200 Subject: [PATCH] tool_cb_wrt: split out win_console() from tool_write_cb() Closes #18180 --- src/tool_cb_wrt.c | 252 +++++++++++++++++++++++----------------------- 1 file changed, 127 insertions(+), 125 deletions(-) diff --git a/src/tool_cb_wrt.c b/src/tool_cb_wrt.c index d34c1fa2ee..7b570b8e57 100644 --- a/src/tool_cb_wrt.c +++ b/src/tool_cb_wrt.c @@ -124,6 +124,130 @@ bool tool_create_output_file(struct OutStruct *outs, return TRUE; } +#if defined(_WIN32) && !defined(UNDER_CE) +static size_t win_console(intptr_t fhnd, struct OutStruct *outs, + char *buffer, size_t bytes, + size_t *retp) +{ + wchar_t *wc_buf; + DWORD wc_len, chars_written; + unsigned char *rbuf = (unsigned char *)buffer; + DWORD rlen = (DWORD)bytes; + +#define IS_TRAILING_BYTE(x) (0x80 <= (x) && (x) < 0xC0) + + /* attempt to complete an incomplete UTF-8 sequence from previous call. the + sequence does not have to be well-formed. */ + if(outs->utf8seq[0] && rlen) { + bool complete = false; + /* two byte sequence (lead byte 110yyyyy) */ + if(0xC0 <= outs->utf8seq[0] && outs->utf8seq[0] < 0xE0) { + outs->utf8seq[1] = *rbuf++; + --rlen; + complete = true; + } + /* three byte sequence (lead byte 1110zzzz) */ + else if(0xE0 <= outs->utf8seq[0] && outs->utf8seq[0] < 0xF0) { + if(!outs->utf8seq[1]) { + outs->utf8seq[1] = *rbuf++; + --rlen; + } + if(rlen && !outs->utf8seq[2]) { + outs->utf8seq[2] = *rbuf++; + --rlen; + complete = true; + } + } + /* four byte sequence (lead byte 11110uuu) */ + else if(0xF0 <= outs->utf8seq[0] && outs->utf8seq[0] < 0xF8) { + if(!outs->utf8seq[1]) { + outs->utf8seq[1] = *rbuf++; + --rlen; + } + if(rlen && !outs->utf8seq[2]) { + outs->utf8seq[2] = *rbuf++; + --rlen; + } + if(rlen && !outs->utf8seq[3]) { + outs->utf8seq[3] = *rbuf++; + --rlen; + complete = true; + } + } + + if(complete) { + WCHAR prefix[3] = {0}; /* UTF-16 (1-2 WCHARs) + NUL */ + + if(MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)outs->utf8seq, -1, + prefix, CURL_ARRAYSIZE(prefix))) { + DEBUGASSERT(prefix[2] == L'\0'); + if(!WriteConsoleW((HANDLE) fhnd, prefix, prefix[1] ? 2 : 1, + &chars_written, NULL)) { + return CURL_WRITEFUNC_ERROR; + } + } + /* else: UTF-8 input was not well formed and OS is pre-Vista which drops + invalid characters instead of writing U+FFFD to output. */ + memset(outs->utf8seq, 0, sizeof(outs->utf8seq)); + } + } + + /* suppress an incomplete utf-8 sequence at end of rbuf */ + if(!outs->utf8seq[0] && rlen && (rbuf[rlen - 1] & 0x80)) { + /* check for lead byte from a two, three or four byte sequence */ + if(0xC0 <= rbuf[rlen - 1] && rbuf[rlen - 1] < 0xF8) { + outs->utf8seq[0] = rbuf[rlen - 1]; + rlen -= 1; + } + else if(rlen >= 2 && IS_TRAILING_BYTE(rbuf[rlen - 1])) { + /* check for lead byte from a three or four byte sequence */ + if(0xE0 <= rbuf[rlen - 2] && rbuf[rlen - 2] < 0xF8) { + outs->utf8seq[0] = rbuf[rlen - 2]; + outs->utf8seq[1] = rbuf[rlen - 1]; + rlen -= 2; + } + else if(rlen >= 3 && IS_TRAILING_BYTE(rbuf[rlen - 2])) { + /* check for lead byte from a four byte sequence */ + if(0xF0 <= rbuf[rlen - 3] && rbuf[rlen - 3] < 0xF8) { + outs->utf8seq[0] = rbuf[rlen - 3]; + outs->utf8seq[1] = rbuf[rlen - 2]; + outs->utf8seq[2] = rbuf[rlen - 1]; + rlen -= 3; + } + } + } + } + + if(rlen) { + /* calculate buffer size for wide characters */ + wc_len = (DWORD)MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)rbuf, (int)rlen, + NULL, 0); + if(!wc_len) + return CURL_WRITEFUNC_ERROR; + + wc_buf = (wchar_t*) malloc(wc_len * sizeof(wchar_t)); + if(!wc_buf) + return CURL_WRITEFUNC_ERROR; + + wc_len = (DWORD)MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)rbuf, (int)rlen, + wc_buf, (int)wc_len); + if(!wc_len) { + free(wc_buf); + return CURL_WRITEFUNC_ERROR; + } + + if(!WriteConsoleW((HANDLE) fhnd, wc_buf, wc_len, &chars_written, NULL)) { + free(wc_buf); + return CURL_WRITEFUNC_ERROR; + } + free(wc_buf); + } + + *retp = bytes; + return 0; +} +#endif + /* ** callback for CURLOPT_WRITEFUNCTION */ @@ -215,131 +339,9 @@ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata) /* if Windows console then UTF-8 must be converted to UTF-16 */ if(isatty(fileno(outs->stream)) && GetConsoleScreenBufferInfo((HANDLE)fhnd, &console_info)) { - wchar_t *wc_buf; - DWORD wc_len, chars_written; - unsigned char *rbuf = (unsigned char *)buffer; - DWORD rlen = (DWORD)bytes; - -#define IS_TRAILING_BYTE(x) (0x80 <= (x) && (x) < 0xC0) - - /* attempt to complete an incomplete UTF-8 sequence from previous call. - the sequence does not have to be well-formed. */ - if(outs->utf8seq[0] && rlen) { - bool complete = false; - /* two byte sequence (lead byte 110yyyyy) */ - if(0xC0 <= outs->utf8seq[0] && outs->utf8seq[0] < 0xE0) { - outs->utf8seq[1] = *rbuf++; - --rlen; - complete = true; - } - /* three byte sequence (lead byte 1110zzzz) */ - else if(0xE0 <= outs->utf8seq[0] && outs->utf8seq[0] < 0xF0) { - if(!outs->utf8seq[1]) { - outs->utf8seq[1] = *rbuf++; - --rlen; - } - if(rlen && !outs->utf8seq[2]) { - outs->utf8seq[2] = *rbuf++; - --rlen; - complete = true; - } - } - /* four byte sequence (lead byte 11110uuu) */ - else if(0xF0 <= outs->utf8seq[0] && outs->utf8seq[0] < 0xF8) { - if(!outs->utf8seq[1]) { - outs->utf8seq[1] = *rbuf++; - --rlen; - } - if(rlen && !outs->utf8seq[2]) { - outs->utf8seq[2] = *rbuf++; - --rlen; - } - if(rlen && !outs->utf8seq[3]) { - outs->utf8seq[3] = *rbuf++; - --rlen; - complete = true; - } - } - - if(complete) { - WCHAR prefix[3] = {0}; /* UTF-16 (1-2 WCHARs) + NUL */ - - if(MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)outs->utf8seq, -1, - prefix, CURL_ARRAYSIZE(prefix))) { - DEBUGASSERT(prefix[2] == L'\0'); - if(!WriteConsoleW( - (HANDLE) fhnd, - prefix, - prefix[1] ? 2 : 1, - &chars_written, - NULL)) { - return CURL_WRITEFUNC_ERROR; - } - } - /* else: UTF-8 input was not well formed and OS is pre-Vista which - drops invalid characters instead of writing U+FFFD to output. */ - - memset(outs->utf8seq, 0, sizeof(outs->utf8seq)); - } - } - - /* suppress an incomplete utf-8 sequence at end of rbuf */ - if(!outs->utf8seq[0] && rlen && (rbuf[rlen - 1] & 0x80)) { - /* check for lead byte from a two, three or four byte sequence */ - if(0xC0 <= rbuf[rlen - 1] && rbuf[rlen - 1] < 0xF8) { - outs->utf8seq[0] = rbuf[rlen - 1]; - rlen -= 1; - } - else if(rlen >= 2 && IS_TRAILING_BYTE(rbuf[rlen - 1])) { - /* check for lead byte from a three or four byte sequence */ - if(0xE0 <= rbuf[rlen - 2] && rbuf[rlen - 2] < 0xF8) { - outs->utf8seq[0] = rbuf[rlen - 2]; - outs->utf8seq[1] = rbuf[rlen - 1]; - rlen -= 2; - } - else if(rlen >= 3 && IS_TRAILING_BYTE(rbuf[rlen - 2])) { - /* check for lead byte from a four byte sequence */ - if(0xF0 <= rbuf[rlen - 3] && rbuf[rlen - 3] < 0xF8) { - outs->utf8seq[0] = rbuf[rlen - 3]; - outs->utf8seq[1] = rbuf[rlen - 2]; - outs->utf8seq[2] = rbuf[rlen - 1]; - rlen -= 3; - } - } - } - } - - if(rlen) { - /* calculate buffer size for wide characters */ - wc_len = (DWORD)MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)rbuf, (int)rlen, - NULL, 0); - if(!wc_len) - return CURL_WRITEFUNC_ERROR; - - wc_buf = (wchar_t*) malloc(wc_len * sizeof(wchar_t)); - if(!wc_buf) - return CURL_WRITEFUNC_ERROR; - - wc_len = (DWORD)MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)rbuf, (int)rlen, - wc_buf, (int)wc_len); - if(!wc_len) { - free(wc_buf); - return CURL_WRITEFUNC_ERROR; - } - - if(!WriteConsoleW( - (HANDLE) fhnd, - wc_buf, - wc_len, - &chars_written, - NULL)) { - free(wc_buf); - return CURL_WRITEFUNC_ERROR; - } - free(wc_buf); - } - - rc = bytes; + size_t retval = win_console(fhnd, outs, buffer, bytes, &rc); + if(retval) + return retval; } else #endif -- 2.47.3