Use wcs2string when outputting the screen

The screen output code predates the ENCODE_DIRECT scheme, and
directly-encoded bytes were not being properly output. This manifested as
private-user characters being mangled when printed as part of fish_prompt.
Just use str2wcstring instead.

Fixes #7723
This commit is contained in:
ridiculousfish 2021-02-17 15:38:46 -08:00
parent f1b6a5939d
commit 8648f52857
2 changed files with 9 additions and 61 deletions

View file

@ -309,64 +309,9 @@ int outputter_t::term_puts(const char *str, int affcnt) {
return res;
}
/// Write a wide character to the outputter. This should only be used when writing characters from
/// user supplied strings. This is needed due to our use of the ENCODE_DIRECT_BASE mechanism to
/// allow the user to specify arbitrary byte values to be output. Such as in a `printf` invocation
/// that includes literal byte values such as `\x1B`. This should not be used for writing non-user
/// supplied characters.
int outputter_t::writech(wint_t ch) {
char buff[MB_LEN_MAX + 1];
size_t len;
if (ch >= ENCODE_DIRECT_BASE && ch < ENCODE_DIRECT_BASE + 256) {
buff[0] = ch - ENCODE_DIRECT_BASE;
len = 1;
} else if (MB_CUR_MAX == 1) {
// single-byte locale (C/POSIX/ISO-8859)
// If `wc` contains a wide character we emit a question-mark.
buff[0] = ch & ~0xFF ? '?' : ch;
len = 1;
} else {
mbstate_t state = {};
len = std::wcrtomb(buff, ch, &state);
if (len == static_cast<size_t>(-1)) {
return 1;
}
}
this->writestr(buff, len);
return 0;
}
/// Write a wide character string to stdout. This should not be used to output things like warning
/// messages; just use debug() or std::fwprintf() for that. It should only be used to output user
/// supplied strings that might contain literal bytes; e.g., "\342\224\214" from issue #1894. This
/// is needed because those strings may contain chars specially encoded using ENCODE_DIRECT_BASE.
void outputter_t::writestr(const wchar_t *str) {
assert(str && "Empty input string");
if (MB_CUR_MAX == 1) {
// Single-byte locale (C/POSIX/ISO-8859).
while (*str) writech(*str++);
return;
}
size_t len = wcstombs(nullptr, str, 0); // figure amount of space needed
if (len == static_cast<size_t>(-1)) {
FLOGF(output_invalid, L"Tried to print invalid wide character string");
return;
}
// Convert the string.
len++;
char *buffer, static_buffer[256];
if (len <= sizeof static_buffer) {
buffer = static_buffer;
} else {
buffer = new char[len];
}
int new_len = wcstombs(buffer, str, len);
this->writestr(buffer, new_len);
if (buffer != static_buffer) delete[] buffer;
void outputter_t::writestr(const wchar_t *str, size_t len) {
wcs2string_appending(str, len, &contents_);
maybe_flush();
}
outputter_t &outputter_t::stdoutput() {

View file

@ -59,17 +59,20 @@ class outputter_t {
void set_color(rgb_color_t c, rgb_color_t c2);
/// Write a wide character to the receiver.
int writech(wint_t ch);
void writech(wchar_t ch) { writestr(&ch, 1); }
/// Write a NUL-terminated wide character string to the receiver.
void writestr(const wchar_t *str);
void writestr(const wchar_t *str) { writestr(str, wcslen(str)); }
/// Write a wide character string to the receiver.
void writestr(const wcstring &str) { writestr(str.c_str()); }
void writestr(const wcstring &str) { writestr(str.data(), str.size()); }
/// Write the given terminfo string to the receiver, like tputs().
int term_puts(const char *str, int affcnt);
/// Write a wide string of the given length.
void writestr(const wchar_t *str, size_t len);
/// Write a narrow string of the given length.
void writestr(const char *str, size_t len) {
contents_.append(str, len);