diff --git a/NEWS b/NEWS index 305dc7bb..795b3bec 100644 --- a/NEWS +++ b/NEWS @@ -20,6 +20,9 @@ lnav v0.9.1: * When copying log lines, the file name and time offset will be included in the copy if they are enabled. + Fixes: + * Unicode text can now be entered in prompts. + lnav v0.9.0: Features: * Added support for themes and included a few as well: default, eldar, diff --git a/src/attr_line.hh b/src/attr_line.hh index e4ec9647..5d5e84c8 100644 --- a/src/attr_line.hh +++ b/src/attr_line.hh @@ -38,6 +38,7 @@ #include #include "base/lnav_log.hh" +#include "base/string_util.hh" #include "base/intern_string.hh" /** @@ -464,6 +465,13 @@ public: return *this; }; + attr_line_t &erase_utf8_chars(size_t start) { + auto byte_index = utf8_char_to_byte_index(this->al_string, start); + this->erase(byte_index); + + return *this; + }; + attr_line_t &right_justify(unsigned long width) { long padding = width - this->length(); if (padding > 0) { diff --git a/src/base/string_util.hh b/src/base/string_util.hh index e2ca9178..6ac71935 100644 --- a/src/base/string_util.hh +++ b/src/base/string_util.hh @@ -33,6 +33,8 @@ #include #include +#include "ww898/cp_utf8.hpp" + void scrub_to_utf8(char *buffer, size_t length); inline bool is_line_ending(char ch) { @@ -116,4 +118,20 @@ inline std::string toupper(const std::string &str) return toupper(str.c_str()); } +inline ssize_t utf8_char_to_byte_index(const std::string &str, ssize_t ch_index) +{ + ssize_t retval = 0; + + while (ch_index > 0) { + auto ch_len = ww898::utf::utf8::char_size([&str, retval]() { + return str[retval]; + }); + + retval += ch_len; + ch_index -= 1; + } + + return retval; +} + #endif diff --git a/src/input_dispatcher.cc b/src/input_dispatcher.cc index 96248085..8d4edc8a 100644 --- a/src/input_dispatcher.cc +++ b/src/input_dispatcher.cc @@ -50,9 +50,12 @@ #endif #include "base/lnav_log.hh" +#include "ww898/cp_utf8.hpp" #include "input_dispatcher.hh" #include "lnav_util.hh" +using namespace ww898; + template static void to_key_seq(A &dst, const char *src) { @@ -90,13 +93,9 @@ void input_dispatcher::new_input(const struct timeval ¤t_time, int ch) to_key_seq(keyseq, this->id_escape_buffer); switch (this->id_escape_matcher(keyseq.data())) { case escape_match_t::NONE: { - if (this->id_escape_expected_size == -1) { - for (int lpc = 0; this->id_escape_buffer[lpc]; lpc++) { - handled = this->id_key_handler( - this->id_escape_buffer[lpc]); - } - } else { - handled = false; + for (int lpc = 0; this->id_escape_buffer[lpc]; lpc++) { + handled = this->id_key_handler( + this->id_escape_buffer[lpc]); } this->id_escape_index = 0; break; @@ -113,15 +112,15 @@ void input_dispatcher::new_input(const struct timeval ¤t_time, int ch) this->id_escape_index == this->id_escape_expected_size) { this->id_escape_index = 0; } - } else if ((ch & 0xf8) == 0xf0) { - this->reset_escape_buffer(ch, current_time, 4); - } else if ((ch & 0xf0) == 0xe0) { - this->reset_escape_buffer(ch, current_time, 3); - } else if ((ch & 0xe0) == 0xc0) { - this->reset_escape_buffer(ch, current_time, 2); } else { - snprintf(keyseq.data(), keyseq.size(), "x%02x", ch & 0xff); - handled = this->id_key_handler(ch); + auto seq_size = utf::utf8::char_size([ch]() { return ch; }); + + if (seq_size == 1) { + snprintf(keyseq.data(), keyseq.size(), "x%02x", ch & 0xff); + handled = this->id_key_handler(ch); + } else { + this->reset_escape_buffer(ch, current_time, seq_size); + } } break; } diff --git a/src/lnav.cc b/src/lnav.cc index 8459c8c3..675204dd 100644 --- a/src/lnav.cc +++ b/src/lnav.cc @@ -1471,16 +1471,19 @@ static void looper() auto encoded_name = (char *) alloca(enc_len); log_info("unbound keyseq: %s", keyseq); - json_ptr::encode(encoded_name, enc_len, lnav_config.lc_ui_keymap.c_str()); + json_ptr::encode(encoded_name, enc_len, + lnav_config.lc_ui_keymap.c_str()); // XXX we should have a hotkey for opening a prompt that is // pre-filled with a suggestion that the user can complete. // This quick-fix key could be used for other stuff as well lnav_data.ld_rl_view->set_value(fmt::format( - ANSI_CSI ANSI_COLOR_PARAM(COLOR_YELLOW) ";" ANSI_BOLD_PARAM ANSI_CHAR_ATTR + ANSI_CSI ANSI_COLOR_PARAM(COLOR_YELLOW) + ";" ANSI_BOLD_PARAM ANSI_CHAR_ATTR "Unrecognized key" ANSI_NORM ", bind to a command using \u2014 " - ANSI_BOLD(":config") " /ui/keymap-defs/{}/{}/command ", + ANSI_BOLD(":config") + " /ui/keymap-defs/{}/{}/command ", encoded_name, keyseq)); alerter::singleton().chime(); }; diff --git a/src/view_curses.cc b/src/view_curses.cc index e0ebdbb6..9aff5ee8 100644 --- a/src/view_curses.cc +++ b/src/view_curses.cc @@ -319,18 +319,12 @@ void view_curses::mvwattrline(WINDOW *window, break; default: { - int offset = 0; + auto offset = 1 - (int) ww898::utf::utf8::char_size([ch]() { + return ch; + }); expanded_line[exp_index] = line[lpc]; exp_index += 1; - if ((ch & 0xf8) == 0xf0) { - offset = -3; - } else if ((ch & 0xf0) == 0xe0) { - offset = -2; - } else if ((ch & 0xe0) == 0xc0) { - offset = -1; - } - if (offset) { if (char_index < lr_chars.lr_start) { lr_bytes.lr_start += abs(offset); diff --git a/src/vt52_curses.cc b/src/vt52_curses.cc index 2a73bef2..03052a01 100644 --- a/src/vt52_curses.cc +++ b/src/vt52_curses.cc @@ -31,7 +31,6 @@ #include "config.h" -#include #include #include @@ -82,7 +81,7 @@ public: const char *operator[](int ch) const { map::const_iterator iter; - const char *retval = NULL; + const char *retval = nullptr; if ((iter = this->vem_map.find(ch)) != this->vem_map.end()) { retval = iter->second; @@ -94,9 +93,9 @@ public: const char *operator[](const char *seq) const { map::const_iterator iter; - const char *retval = NULL; + const char *retval = nullptr; - require(seq != NULL); + require(seq != nullptr); if ((iter = this->vem_input_map.find(seq)) != this->vem_input_map.end()) { @@ -114,7 +113,7 @@ private: static char area_buffer[1024]; char * area = area_buffer; - if (tgetent(NULL, "vt52") == ERR) { + if (tgetent(nullptr, "vt52") == ERR) { perror("tgetent"); } this->vem_map[KEY_UP] = tgetstr((char *)"ku", &area); @@ -122,7 +121,7 @@ private: this->vem_map[KEY_RIGHT] = tgetstr((char *)"kr", &area); this->vem_map[KEY_LEFT] = tgetstr((char *)"kl", &area); this->vem_map[KEY_HOME] = tgetstr((char *)"kh", &area); - if (this->vem_map[KEY_HOME] == NULL) { + if (this->vem_map[KEY_HOME] == nullptr) { this->vem_map[KEY_HOME] = "\x01"; } this->vem_map[KEY_BACKSPACE] = "\010"; @@ -132,11 +131,11 @@ private: this->vem_map[KEY_END] = "\x05"; this->vem_map[KEY_SLEFT] = tgetstr((char *)"#4", &area); - if (this->vem_map[KEY_SLEFT] == NULL) { + if (this->vem_map[KEY_SLEFT] == nullptr) { this->vem_map[KEY_SLEFT] = "\033b"; } this->vem_map[KEY_SRIGHT] = tgetstr((char *)"%i", &area); - if (this->vem_map[KEY_SRIGHT] == NULL) { + if (this->vem_map[KEY_SRIGHT] == nullptr) { this->vem_map[KEY_SRIGHT] = "\033f"; } @@ -145,7 +144,7 @@ private: this->vem_input_map[tgetstr((char *)"ce", &area)] = "ce"; this->vem_input_map[tgetstr((char *)"kl", &area)] = "kl"; this->vem_input_map[tgetstr((char *)"kr", &area)] = "kr"; - tgetent(NULL, getenv("TERM")); + tgetent(nullptr, getenv("TERM")); }; /** Map of ncurses keycodes to VT52 escape sequences. */ @@ -153,21 +152,12 @@ private: map vem_input_map; }; -vt52_curses::vt52_curses() - : vc_window(NULL), - vc_x(0), - vc_y(0), - vc_max_height(0), - vc_escape_len(0), - vc_map_buffer(0) -{ } - const char *vt52_curses::map_input(int ch, int &len_out) { const char *esc, *retval; /* Check for an escape sequence, otherwise just return the char. */ - if ((esc = vt52_escape_map::singleton()[ch]) != NULL) { + if ((esc = vt52_escape_map::singleton()[ch]) != nullptr) { retval = esc; len_out = strlen(retval); } @@ -182,7 +172,7 @@ const char *vt52_curses::map_input(int ch, int &len_out) len_out = 1; } - ensure(retval != NULL); + ensure(retval != nullptr); ensure(len_out > 0); return retval; @@ -202,10 +192,26 @@ void vt52_curses::map_output(const char *output, int len) this->vc_escape_len += 1; this->vc_escape[this->vc_escape_len] = '\0'; - if ((cap = vt52_escape_map::singleton()[this->vc_escape]) != - nullptr) { + if (this->vc_expected_escape_len != -1) { + if (this->vc_escape_len == this->vc_expected_escape_len) { + auto& line_string = this->vc_line.get_string(); + auto x_byte_index = utf8_char_to_byte_index(line_string, this->vc_x); + + for (int esc_index = 0; esc_index < this->vc_escape_len; esc_index++) { + if (x_byte_index < this->vc_line.length()) { + line_string[x_byte_index] = this->vc_escape[esc_index]; + } else { + this->vc_line.append(1, this->vc_escape[esc_index]); + } + x_byte_index += 1; + } + this->vc_x += 1; + this->vc_escape_len = 0; + } + } else if ((cap = vt52_escape_map::singleton()[this->vc_escape]) != + nullptr) { if (strcmp(cap, "ce") == 0) { - this->vc_line.erase(this->vc_x); + this->vc_line.erase_utf8_chars(this->vc_x); this->vc_escape_len = 0; } else if (strcmp(cap, "kl") == 0) { @@ -222,7 +228,19 @@ void vt52_curses::map_output(const char *output, int len) } } else { - switch (output[lpc]) { + auto next_ch = output[lpc]; + auto seq_size = ww898::utf::utf8::char_size([next_ch]() { + return next_ch; + }); + + if (seq_size > 1) { + this->vc_escape[0] = next_ch; + this->vc_escape_len = 1; + this->vc_expected_escape_len = seq_size; + continue; + } + + switch (next_ch) { case STX: this->vc_x = 0; this->vc_line.clear(); @@ -239,6 +257,7 @@ void vt52_curses::map_output(const char *output, int len) case ESCAPE: this->vc_escape[0] = ESCAPE; this->vc_escape_len = 1; + this->vc_expected_escape_len = -1; break; case '\n': @@ -250,15 +269,19 @@ void vt52_curses::map_output(const char *output, int len) this->vc_x = 0; break; - default: - if (this->vc_x < this->vc_line.length()) { - this->vc_line.get_string()[this->vc_x] = output[lpc]; + default: { + auto& line_string = this->vc_line.get_string(); + auto x_byte_index = utf8_char_to_byte_index(line_string, this->vc_x); + + if (x_byte_index < this->vc_line.length()) { + line_string[x_byte_index] = next_ch; } else { - this->vc_line.append(1, output[lpc]); + this->vc_line.append(1, next_ch); } this->vc_x += 1; break; } + } } } } @@ -269,4 +292,5 @@ void vt52_curses::do_update() this->get_actual_y(), this->vc_left, this->vc_line, line_range{ 0, (int) this->vc_width }); + wmove(this->vc_window, this->get_actual_y(), this->vc_left + this->vc_x); } diff --git a/src/vt52_curses.hh b/src/vt52_curses.hh index cab10fdd..d53378c9 100644 --- a/src/vt52_curses.hh +++ b/src/vt52_curses.hh @@ -54,8 +54,6 @@ class vt52_curses : public view_curses { public: - vt52_curses(); - /** @param win The curses window this view is attached to. */ void set_window(WINDOW *win) { this->vc_window = win; }; @@ -145,14 +143,15 @@ protected: return retval; }; - WINDOW *vc_window; /*< The window that contains this view. */ + WINDOW *vc_window{nullptr}; /*< The window that contains this view. */ int vc_left{0}; - int vc_x; /*< The X position of the cursor. */ - int vc_y; /*< The Y position of the cursor. */ - int vc_max_height; + int vc_x{0}; /*< The X position of the cursor. */ + int vc_y{0}; /*< The Y position of the cursor. */ + int vc_max_height{0}; char vc_escape[16]; /*< Storage for escape sequences. */ - int vc_escape_len; /*< The number of chars in vc_escape. */ - char vc_map_buffer; /*< + int vc_escape_len{0}; /*< The number of chars in vc_escape. */ + int vc_expected_escape_len{-1}; + char vc_map_buffer{0}; /*< * Buffer returned by map_input for trivial * translations (one-to-one). */ diff --git a/src/ww898/cp_utf8.hpp b/src/ww898/cp_utf8.hpp new file mode 100644 index 00000000..7c8c68d0 --- /dev/null +++ b/src/ww898/cp_utf8.hpp @@ -0,0 +1,158 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Mikhail Pilin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace ww898 { +namespace utf { + +// Supported combinations: +// 0xxx_xxxx +// 110x_xxxx 10xx_xxxx +// 1110_xxxx 10xx_xxxx 10xx_xxxx +// 1111_0xxx 10xx_xxxx 10xx_xxxx 10xx_xxxx +// 1111_10xx 10xx_xxxx 10xx_xxxx 10xx_xxxx 10xx_xxxx +// 1111_110x 10xx_xxxx 10xx_xxxx 10xx_xxxx 10xx_xxxx 10xx_xxxx +struct utf8 final +{ + static size_t const max_unicode_symbol_size = 4; + static size_t const max_supported_symbol_size = 6; + + static uint32_t const max_supported_code_point = 0x7FFFFFFF; + + using char_type = uint8_t; + + template + static size_t char_size(PeekFn && peek_fn) + { + char_type const ch0 = std::forward(peek_fn)(); + if (ch0 < 0x80) // 0xxx_xxxx + return 1; + if (ch0 < 0xC0) + throw std::runtime_error("The utf8 first char in sequence is incorrect"); + if (ch0 < 0xE0) // 110x_xxxx 10xx_xxxx + return 2; + if (ch0 < 0xF0) // 1110_xxxx 10xx_xxxx 10xx_xxxx + return 3; + if (ch0 < 0xF8) // 1111_0xxx 10xx_xxxx 10xx_xxxx 10xx_xxxx + return 4; + if (ch0 < 0xFC) // 1111_10xx 10xx_xxxx 10xx_xxxx 10xx_xxxx 10xx_xxxx + return 5; + if (ch0 < 0xFE) // 1111_110x 10xx_xxxx 10xx_xxxx 10xx_xxxx 10xx_xxxx 10xx_xxxx + return 6; + throw std::runtime_error("The utf8 first char in sequence is incorrect"); + } + + template + static uint32_t read(ReadFn && read_fn) + { + char_type const ch0 = read_fn(); + if (ch0 < 0x80) // 0xxx_xxxx + return ch0; + if (ch0 < 0xC0) + throw std::runtime_error("The utf8 first char in sequence is incorrect"); + if (ch0 < 0xE0) // 110x_xxxx 10xx_xxxx + { + char_type const ch1 = read_fn(); if (ch1 >> 6 != 2) goto _err; + return (ch0 << 6) + ch1 - 0x3080; + } + if (ch0 < 0xF0) // 1110_xxxx 10xx_xxxx 10xx_xxxx + { + char_type const ch1 = read_fn(); if (ch1 >> 6 != 2) goto _err; + char_type const ch2 = read_fn(); if (ch2 >> 6 != 2) goto _err; + return (ch0 << 12) + (ch1 << 6) + ch2 - 0xE2080; + } + if (ch0 < 0xF8) // 1111_0xxx 10xx_xxxx 10xx_xxxx 10xx_xxxx + { + char_type const ch1 = read_fn(); if (ch1 >> 6 != 2) goto _err; + char_type const ch2 = read_fn(); if (ch2 >> 6 != 2) goto _err; + char_type const ch3 = read_fn(); if (ch3 >> 6 != 2) goto _err; + return (ch0 << 18) + (ch1 << 12) + (ch2 << 6) + ch3 - 0x3C82080; + } + if (ch0 < 0xFC) // 1111_10xx 10xx_xxxx 10xx_xxxx 10xx_xxxx 10xx_xxxx + { + char_type const ch1 = read_fn(); if (ch1 >> 6 != 2) goto _err; + char_type const ch2 = read_fn(); if (ch2 >> 6 != 2) goto _err; + char_type const ch3 = read_fn(); if (ch3 >> 6 != 2) goto _err; + char_type const ch4 = read_fn(); if (ch4 >> 6 != 2) goto _err; + return (ch0 << 24) + (ch1 << 18) + (ch2 << 12) + (ch3 << 6) + ch4 - 0xFA082080; + } + if (ch0 < 0xFE) // 1111_110x 10xx_xxxx 10xx_xxxx 10xx_xxxx 10xx_xxxx 10xx_xxxx + { + char_type const ch1 = read_fn(); if (ch1 >> 6 != 2) goto _err; + char_type const ch2 = read_fn(); if (ch2 >> 6 != 2) goto _err; + char_type const ch3 = read_fn(); if (ch3 >> 6 != 2) goto _err; + char_type const ch4 = read_fn(); if (ch4 >> 6 != 2) goto _err; + char_type const ch5 = read_fn(); if (ch5 >> 6 != 2) goto _err; + return (ch0 << 30) + (ch1 << 24) + (ch2 << 18) + (ch3 << 12) + (ch4 << 6) + ch5 - 0x82082080; + } + throw std::runtime_error("The utf8 first char in sequence is incorrect"); + _err: throw std::runtime_error("The utf8 slave char in sequence is incorrect"); + } + + template + static void write(uint32_t const cp, WriteFn && write_fn) + { + if (cp < 0x80) // 0xxx_xxxx + write_fn(static_cast(cp)); + else if (cp < 0x800) // 110x_xxxx 10xx_xxxx + { + write_fn(static_cast(0xC0 | cp >> 6)); + goto _1; + } + else if (cp < 0x10000) // 1110_xxxx 10xx_xxxx 10xx_xxxx + { + write_fn(static_cast(0xE0 | cp >> 12)); + goto _2; + } + else if (cp < 0x200000) // 1111_0xxx 10xx_xxxx 10xx_xxxx 10xx_xxxx + { + write_fn(static_cast(0xF0 | cp >> 18)); + goto _3; + } + else if (cp < 0x4000000) // 1111_10xx 10xx_xxxx 10xx_xxxx 10xx_xxxx 10xx_xxxx + { + write_fn(static_cast(0xF8 | cp >> 24)); + goto _4; + } + else if (cp < 0x80000000) // 1111_110x 10xx_xxxx 10xx_xxxx 10xx_xxxx 10xx_xxxx 10xx_xxxx + { + write_fn(static_cast(0xFC | cp >> 30)); + goto _5; + } + else + throw std::runtime_error("Tool large UTF8 code point"); + return; + _5: write_fn(static_cast(0x80 | (cp >> 24 & 0x3F))); + _4: write_fn(static_cast(0x80 | (cp >> 18 & 0x3F))); + _3: write_fn(static_cast(0x80 | (cp >> 12 & 0x3F))); + _2: write_fn(static_cast(0x80 | (cp >> 6 & 0x3F))); + _1: write_fn(static_cast(0x80 | (cp & 0x3F))); + } +}; + +}} diff --git a/test/Makefile.am b/test/Makefile.am index 9b476026..b95b8b93 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -330,12 +330,12 @@ TESTS = \ test_sql_str_func.sh \ test_sql_time_func.sh \ test_data_parser.sh \ - test_pretty_print.sh + test_pretty_print.sh \ + test_vt52_curses.sh DISABLED_TESTS = \ test_top_status \ - test_view_colors.sh \ - test_vt52_curses.sh + test_view_colors.sh if HAVE_LIBCURL TESTS += \ diff --git a/test/drive_vt52_curses.cc b/test/drive_vt52_curses.cc index f8487489..d936a1ad 100644 --- a/test/drive_vt52_curses.cc +++ b/test/drive_vt52_curses.cc @@ -35,7 +35,9 @@ #include #include #include +#include +#include "base/lnav_log.hh" #include "view_curses.hh" #include "vt52_curses.hh" @@ -64,11 +66,14 @@ int main(int argc, char *argv[]) { int lpc, c, fd, retval = EXIT_SUCCESS; vt52_curses vt; - + + setenv("LANG", "en_US.utf-8", 1); + setlocale(LC_ALL, ""); fd = open("/tmp/lnav.err", O_WRONLY|O_CREAT|O_APPEND, 0666); dup2(fd, STDERR_FILENO); close(fd); fprintf(stderr, "startup\n"); + lnav_log_file = stderr; while ((c = getopt(argc, argv, "y:")) != -1) { switch (c) { @@ -81,16 +86,16 @@ int main(int argc, char *argv[]) for (lpc = 0; lpc < 1000; lpc++) { int len; - assert(vt.map_input(random(), len) != NULL); + assert(vt.map_input(random(), len) != nullptr); assert(len > 0); } - tgetent(NULL, "vt52"); + tgetent(nullptr, "vt52"); { static const char *CANNED_INPUT[] = { - "abc", + "Gru\xC3\x9F", "\r", - tgetstr((char *)"ce", NULL), + tgetstr((char *)"ce", nullptr), "de", "\n", "1\n", @@ -103,7 +108,7 @@ int main(int argc, char *argv[]) "8\n", "9\n", "abc", - "\x2", + "\x02", "\a", "ab\bcdef", 0 diff --git a/test/scripty.cc b/test/scripty.cc index 55c343a6..cedb62f5 100644 --- a/test/scripty.cc +++ b/test/scripty.cc @@ -210,7 +210,7 @@ static void dump_memory(FILE *dst, const char *src, int len) int lpc; for (lpc = 0; lpc < len; lpc++) { - fprintf(dst, "%02x", src[lpc]); + fprintf(dst, "%02x", src[lpc] & 0xff); } } diff --git a/test/test_ncurses_unicode.cc b/test/test_ncurses_unicode.cc index b6f10bb6..f9449734 100644 --- a/test/test_ncurses_unicode.cc +++ b/test/test_ncurses_unicode.cc @@ -1,4 +1,5 @@ +#include #include "config.h" #define _XOPEN_SOURCE_EXTENDED 1 #include @@ -19,6 +20,7 @@ int main(int argc, char *argv[]) { + setenv("LANG", "en_US.utf-8", 1); setlocale(LC_ALL, ""); WINDOW *stdscr = initscr(); diff --git a/test/vt52_curses_input.0 b/test/vt52_curses_input.0 index 59e93a88..982d97fb 100644 --- a/test/vt52_curses_input.0 +++ b/test/vt52_curses_input.0 @@ -1,38 +1,38 @@ -sleep 2.051763 -write 31 -sleep 0.311858 -write 32 -sleep 0.295852 -write 33 -sleep 0.319916 -write 34 -sleep 0.327742 -write 35 -sleep 0.328183 -write 36 -sleep 0.335896 -write 37 -sleep 0.360085 -write 38 -sleep 0.336000 -write 39 -sleep 0.376058 -write 30 -sleep 0.840149 -write 61 -sleep 0.783892 -write 62 -sleep 0.415990 -write 63 -sleep 0.280001 -write 64 -sleep 0.191901 -write 65 -sleep 0.295949 -write 66 -sleep 0.775841 -write 67 -sleep 0.399883 -write 68 -sleep 0.304016 -write 69 +sleep 0.867286 +write 0d +sleep 0.141596 +write 0d +sleep 0.188837 +write 0d +sleep 0.177586 +write 0d +sleep 0.159950 +write 0d +sleep 0.158958 +write 0d +sleep 0.164105 +write 0d +sleep 0.176968 +write 0d +sleep 0.165942 +write 0d +sleep 0.187011 +write 0d +sleep 0.167987 +write 0d +sleep 0.173959 +write 0d +sleep 0.176091 +write 0d +sleep 0.180728 +write 0d +sleep 0.172983 +write 0d +sleep 0.167819 +write 0d +sleep 0.165876 +write 0d +sleep 0.175857 +write 0d +sleep 0.183068 +write 0d diff --git a/test/vt52_curses_output.0 b/test/vt52_curses_output.0 index 99b89649..b77311a5 100644 --- a/test/vt52_curses_output.0 +++ b/test/vt52_curses_output.0 @@ -1,38 +1,39 @@ -read 1b29301b371b5b3f3437681b5b313b3234721b5b6d1b5b346c1b5b481b5b324a616263 -# write 31 +read 1b29301b371b5b3f3437681b5b313b3234721b5b6d1b5b346c1b5b481b5b324a477275c39f +# write 0d read 0d -# write 32 +# write 0d read 1b5b4a -# write 33 +# write 0d read 6465 -# write 34 +# write 0d read 0d1b5b4a -# write 35 +# write 0d read -# write 36 +# write 0d read -# write 37 +# write 0d read -# write 38 +# write 0d read -# write 39 +# write 0d read -# write 30 +# write 0d read -# write 61 +# write 0d read -# write 62 +# write 0d read -# write 63 +# write 0d read -# write 64 +# write 0d read 616263 -# write 65 +# write 0d read 0d1b5b4a -# write 66 +# write 0d read 07 -# write 67 +# write 0d read 6163646566 -# write 68 +# write 0d read -# write 69 +# write 0d +read 1b5b32343b31481b5b324a1b5b3f34376c1b380d1b5b3f316c1b3e