Browse Source

windows: Replace codecvt in console output with own WTF-8 → UTF-16 code

Fixes: issue #112
pull/120/head
Daniel Scharrer 6 years ago
parent
commit
4691bf815a
  1. 1
      CHANGELOG
  2. 79
      src/util/windows.cpp

1
CHANGELOG

@ -7,6 +7,7 @@ innoextract 1.9 (TBD)
- Fixed handling of path separators in Japanese and Korean installers
- Fixed build with newer Boost versions
- Windows: Fixed heap corruption
- Windows: Fixed garbled output
innoextract 1.8 (2019-09-15)
- Added support for Inno Setup 6.0.0 installers

79
src/util/windows.cpp

@ -75,9 +75,8 @@ class windows_console_sink : public util::ansi_console_parser<windows_console_si
const HANDLE handle;
//! Buffer for charset conversions
std::vector<wchar_t> buffer;
const utf8_codecvt * codecvt;
utf8_codecvt::state_type codecvt_state;
std::string in_buffer;
std::string out_buffer;
//! Initial console display attributes
WORD initial_attributes;
@ -236,6 +235,10 @@ class windows_console_sink : public util::ansi_console_parser<windows_console_si
}
void handle_command(CommandType type, const char * codes, const char * end) {
if(!in_buffer.empty()) {
output_text(in_buffer.c_str(), in_buffer.c_str() + in_buffer.size());
in_buffer.clear();
}
switch(type) {
case EL: erase_in_line(codes, end); break;
case SGR: select_graphic_rendition(codes, end); break;
@ -249,7 +252,7 @@ class windows_console_sink : public util::ansi_console_parser<windows_console_si
}
}
void handle_deferred_clear(wchar_t * & begin, wchar_t * end) {
void handle_deferred_clear(LPCWSTR & begin, LPCWSTR end) {
CONSOLE_SCREEN_BUFFER_INFO info;
if(!GetConsoleScreenBufferInfo(handle, &info)) {
@ -265,8 +268,8 @@ class windows_console_sink : public util::ansi_console_parser<windows_console_si
break;
}
wchar_t * cr = std::find(begin, end, L'\r');
wchar_t * ln = std::find(begin, cr, L'\n');
LPCWSTR cr = std::find(begin, end, L'\r');
LPCWSTR ln = std::find(begin, cr, L'\n');
// Insert an empty line before the "cleared" line
if(clear_line == info.dwCursorPosition.Y) {
@ -338,48 +341,48 @@ class windows_console_sink : public util::ansi_console_parser<windows_console_si
}
void output_text(const char * begin, const char * end) {
wtf8_to_utf16le(begin, end, out_buffer);
LPCWSTR obegin = reinterpret_cast<LPCWSTR>(out_buffer.c_str());
LPCWSTR oend = obegin + out_buffer.size() / 2;
if(deferred_clear) {
handle_deferred_clear(obegin, oend);
}
DWORD count;
WriteConsoleW(handle, obegin, DWORD(oend - obegin), &count, NULL);
}
void handle_text(const char * s, size_t n) {
if(!in_buffer.empty()) {
in_buffer.append(s, n);
s = in_buffer.c_str();
n = in_buffer.size();
}
const char * end = s + n;
if(s == end) {
return;
}
for(;;) {
wchar_t * obegin = &buffer.front();
wchar_t * oend = obegin + buffer.size();
wchar_t * onext = obegin;
std::codecvt_base::result res;
res = codecvt->in(codecvt_state, s, end, s, obegin, oend, onext);
if(deferred_clear) {
handle_deferred_clear(obegin, onext);
}
DWORD count;
WriteConsoleW(handle, obegin, DWORD(onext - obegin), &count, NULL);
if(res != std::codecvt_base::partial) {
break;
}
if(onext == oend) {
buffer.resize(buffer.size() * 2);
}
}
const char * e = wtf8_find_end(s, end);
output_text(s, e);
in_buffer.assign(e, end);
}
public:
windows_console_sink(HANDLE console_handle, const utf8_codecvt * converter)
windows_console_sink(HANDLE console_handle)
: handle(console_handle)
, buffer(256)
, codecvt(converter)
, initial_attributes(get_attributes())
, default_attributes(get_default_attributes())
, attributes(initial_attributes)
@ -389,6 +392,9 @@ public:
{ }
~windows_console_sink() {
if(!in_buffer.empty()) {
output_text(in_buffer.c_str(), in_buffer.c_str() + in_buffer.size());
}
if(deferred_clear) {
CONSOLE_SCREEN_BUFFER_INFO info;
if(GetConsoleScreenBufferInfo(handle, &info)) {
@ -400,9 +406,6 @@ public:
};
// UTF8 -> UTF-16 converter
static utf8_codecvt codecvt;
typedef boost::iostreams::stream_buffer<windows_console_sink> console_buffer;
struct console_buffer_info {
HANDLE handle;
@ -440,7 +443,7 @@ static bool is_console(HANDLE handle) {
static void init_console(std::ostream & os, console_buffer_info & info, DWORD n) {
info.handle = GetStdHandle(n);
if(is_console(info.handle)) {
info.buf = new console_buffer(info.handle, &codecvt);
info.buf = new console_buffer(info.handle);
info.orig_buf = os.rdbuf(info.buf);
} else {
info.handle = NULL;

Loading…
Cancel
Save