[input_dispatcher] fix handling of unicode input

Fixes #791
pull/796/head
Timothy Stack 4 years ago
parent db8a3c4d38
commit f192cb7c3e

@ -20,6 +20,9 @@ lnav v0.9.1:
* When copying log lines, the file name and time offset will be included * When copying log lines, the file name and time offset will be included
in the copy if they are enabled. in the copy if they are enabled.
Fixes:
* Unicode text can now be entered in prompts.
lnav v0.9.0: lnav v0.9.0:
Features: Features:
* Added support for themes and included a few as well: default, eldar, * Added support for themes and included a few as well: default, eldar,

@ -38,6 +38,7 @@
#include <vector> #include <vector>
#include "base/lnav_log.hh" #include "base/lnav_log.hh"
#include "base/string_util.hh"
#include "base/intern_string.hh" #include "base/intern_string.hh"
/** /**
@ -464,6 +465,13 @@ public:
return *this; 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) { attr_line_t &right_justify(unsigned long width) {
long padding = width - this->length(); long padding = width - this->length();
if (padding > 0) { if (padding > 0) {

@ -33,6 +33,8 @@
#include <string.h> #include <string.h>
#include <string> #include <string>
#include "ww898/cp_utf8.hpp"
void scrub_to_utf8(char *buffer, size_t length); void scrub_to_utf8(char *buffer, size_t length);
inline bool is_line_ending(char ch) { inline bool is_line_ending(char ch) {
@ -116,4 +118,20 @@ inline std::string toupper(const std::string &str)
return toupper(str.c_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 #endif

@ -50,9 +50,12 @@
#endif #endif
#include "base/lnav_log.hh" #include "base/lnav_log.hh"
#include "ww898/cp_utf8.hpp"
#include "input_dispatcher.hh" #include "input_dispatcher.hh"
#include "lnav_util.hh" #include "lnav_util.hh"
using namespace ww898;
template<typename A> template<typename A>
static void to_key_seq(A &dst, const char *src) static void to_key_seq(A &dst, const char *src)
{ {
@ -90,13 +93,9 @@ void input_dispatcher::new_input(const struct timeval &current_time, int ch)
to_key_seq(keyseq, this->id_escape_buffer); to_key_seq(keyseq, this->id_escape_buffer);
switch (this->id_escape_matcher(keyseq.data())) { switch (this->id_escape_matcher(keyseq.data())) {
case escape_match_t::NONE: { case escape_match_t::NONE: {
if (this->id_escape_expected_size == -1) { for (int lpc = 0; this->id_escape_buffer[lpc]; lpc++) {
for (int lpc = 0; this->id_escape_buffer[lpc]; lpc++) { handled = this->id_key_handler(
handled = this->id_key_handler( this->id_escape_buffer[lpc]);
this->id_escape_buffer[lpc]);
}
} else {
handled = false;
} }
this->id_escape_index = 0; this->id_escape_index = 0;
break; break;
@ -113,15 +112,15 @@ void input_dispatcher::new_input(const struct timeval &current_time, int ch)
this->id_escape_index == this->id_escape_expected_size) { this->id_escape_index == this->id_escape_expected_size) {
this->id_escape_index = 0; 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 { } else {
snprintf(keyseq.data(), keyseq.size(), "x%02x", ch & 0xff); auto seq_size = utf::utf8::char_size([ch]() { return ch; });
handled = this->id_key_handler(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; break;
} }

@ -1471,16 +1471,19 @@ static void looper()
auto encoded_name = (char *) alloca(enc_len); auto encoded_name = (char *) alloca(enc_len);
log_info("unbound keyseq: %s", keyseq); 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 // XXX we should have a hotkey for opening a prompt that is
// pre-filled with a suggestion that the user can complete. // pre-filled with a suggestion that the user can complete.
// This quick-fix key could be used for other stuff as well // This quick-fix key could be used for other stuff as well
lnav_data.ld_rl_view->set_value(fmt::format( 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" "Unrecognized key"
ANSI_NORM ANSI_NORM
", bind to a command using \u2014 " ", bind to a command using \u2014 "
ANSI_BOLD(":config") " /ui/keymap-defs/{}/{}/command <cmd>", ANSI_BOLD(":config")
" /ui/keymap-defs/{}/{}/command <cmd>",
encoded_name, keyseq)); encoded_name, keyseq));
alerter::singleton().chime(); alerter::singleton().chime();
}; };

@ -319,18 +319,12 @@ void view_curses::mvwattrline(WINDOW *window,
break; break;
default: { default: {
int offset = 0; auto offset = 1 - (int) ww898::utf::utf8::char_size([ch]() {
return ch;
});
expanded_line[exp_index] = line[lpc]; expanded_line[exp_index] = line[lpc];
exp_index += 1; 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 (offset) {
if (char_index < lr_chars.lr_start) { if (char_index < lr_chars.lr_start) {
lr_bytes.lr_start += abs(offset); lr_bytes.lr_start += abs(offset);

@ -31,7 +31,6 @@
#include "config.h" #include "config.h"
#include <unistd.h>
#include <string.h> #include <string.h>
#include <map> #include <map>
@ -82,7 +81,7 @@ public:
const char *operator[](int ch) const const char *operator[](int ch) const
{ {
map<int, const char *>::const_iterator iter; map<int, const char *>::const_iterator iter;
const char *retval = NULL; const char *retval = nullptr;
if ((iter = this->vem_map.find(ch)) != this->vem_map.end()) { if ((iter = this->vem_map.find(ch)) != this->vem_map.end()) {
retval = iter->second; retval = iter->second;
@ -94,9 +93,9 @@ public:
const char *operator[](const char *seq) const const char *operator[](const char *seq) const
{ {
map<string, const char *>::const_iterator iter; map<string, const char *>::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)) != if ((iter = this->vem_input_map.find(seq)) !=
this->vem_input_map.end()) { this->vem_input_map.end()) {
@ -114,7 +113,7 @@ private:
static char area_buffer[1024]; static char area_buffer[1024];
char * area = area_buffer; char * area = area_buffer;
if (tgetent(NULL, "vt52") == ERR) { if (tgetent(nullptr, "vt52") == ERR) {
perror("tgetent"); perror("tgetent");
} }
this->vem_map[KEY_UP] = tgetstr((char *)"ku", &area); 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_RIGHT] = tgetstr((char *)"kr", &area);
this->vem_map[KEY_LEFT] = tgetstr((char *)"kl", &area); this->vem_map[KEY_LEFT] = tgetstr((char *)"kl", &area);
this->vem_map[KEY_HOME] = tgetstr((char *)"kh", &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_HOME] = "\x01";
} }
this->vem_map[KEY_BACKSPACE] = "\010"; this->vem_map[KEY_BACKSPACE] = "\010";
@ -132,11 +131,11 @@ private:
this->vem_map[KEY_END] = "\x05"; this->vem_map[KEY_END] = "\x05";
this->vem_map[KEY_SLEFT] = tgetstr((char *)"#4", &area); 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_SLEFT] = "\033b";
} }
this->vem_map[KEY_SRIGHT] = tgetstr((char *)"%i", &area); 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"; 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 *)"ce", &area)] = "ce";
this->vem_input_map[tgetstr((char *)"kl", &area)] = "kl"; this->vem_input_map[tgetstr((char *)"kl", &area)] = "kl";
this->vem_input_map[tgetstr((char *)"kr", &area)] = "kr"; 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. */ /** Map of ncurses keycodes to VT52 escape sequences. */
@ -153,21 +152,12 @@ private:
map<string, const char *> vem_input_map; map<string, const char *> 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 *vt52_curses::map_input(int ch, int &len_out)
{ {
const char *esc, *retval; const char *esc, *retval;
/* Check for an escape sequence, otherwise just return the char. */ /* 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; retval = esc;
len_out = strlen(retval); len_out = strlen(retval);
} }
@ -182,7 +172,7 @@ const char *vt52_curses::map_input(int ch, int &len_out)
len_out = 1; len_out = 1;
} }
ensure(retval != NULL); ensure(retval != nullptr);
ensure(len_out > 0); ensure(len_out > 0);
return retval; return retval;
@ -202,10 +192,26 @@ void vt52_curses::map_output(const char *output, int len)
this->vc_escape_len += 1; this->vc_escape_len += 1;
this->vc_escape[this->vc_escape_len] = '\0'; this->vc_escape[this->vc_escape_len] = '\0';
if ((cap = vt52_escape_map::singleton()[this->vc_escape]) != if (this->vc_expected_escape_len != -1) {
nullptr) { 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) { 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; this->vc_escape_len = 0;
} }
else if (strcmp(cap, "kl") == 0) { else if (strcmp(cap, "kl") == 0) {
@ -222,7 +228,19 @@ void vt52_curses::map_output(const char *output, int len)
} }
} }
else { 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: case STX:
this->vc_x = 0; this->vc_x = 0;
this->vc_line.clear(); this->vc_line.clear();
@ -239,6 +257,7 @@ void vt52_curses::map_output(const char *output, int len)
case ESCAPE: case ESCAPE:
this->vc_escape[0] = ESCAPE; this->vc_escape[0] = ESCAPE;
this->vc_escape_len = 1; this->vc_escape_len = 1;
this->vc_expected_escape_len = -1;
break; break;
case '\n': case '\n':
@ -250,15 +269,19 @@ void vt52_curses::map_output(const char *output, int len)
this->vc_x = 0; this->vc_x = 0;
break; break;
default: default: {
if (this->vc_x < this->vc_line.length()) { auto& line_string = this->vc_line.get_string();
this->vc_line.get_string()[this->vc_x] = output[lpc]; 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 { } else {
this->vc_line.append(1, output[lpc]); this->vc_line.append(1, next_ch);
} }
this->vc_x += 1; this->vc_x += 1;
break; break;
} }
}
} }
} }
} }
@ -269,4 +292,5 @@ void vt52_curses::do_update()
this->get_actual_y(), this->vc_left, this->get_actual_y(), this->vc_left,
this->vc_line, this->vc_line,
line_range{ 0, (int) this->vc_width }); line_range{ 0, (int) this->vc_width });
wmove(this->vc_window, this->get_actual_y(), this->vc_left + this->vc_x);
} }

@ -54,8 +54,6 @@
class vt52_curses class vt52_curses
: public view_curses { : public view_curses {
public: public:
vt52_curses();
/** @param win The curses window this view is attached to. */ /** @param win The curses window this view is attached to. */
void set_window(WINDOW *win) { this->vc_window = win; }; void set_window(WINDOW *win) { this->vc_window = win; };
@ -145,14 +143,15 @@ protected:
return retval; 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_left{0};
int vc_x; /*< The X position of the cursor. */ int vc_x{0}; /*< The X position of the cursor. */
int vc_y; /*< The Y position of the cursor. */ int vc_y{0}; /*< The Y position of the cursor. */
int vc_max_height; int vc_max_height{0};
char vc_escape[16]; /*< Storage for escape sequences. */ char vc_escape[16]; /*< Storage for escape sequences. */
int vc_escape_len; /*< The number of chars in vc_escape. */ int vc_escape_len{0}; /*< The number of chars in vc_escape. */
char vc_map_buffer; /*< int vc_expected_escape_len{-1};
char vc_map_buffer{0}; /*<
* Buffer returned by map_input for trivial * Buffer returned by map_input for trivial
* translations (one-to-one). * translations (one-to-one).
*/ */

@ -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 <cstdint>
#include <stdexcept>
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<typename PeekFn>
static size_t char_size(PeekFn && peek_fn)
{
char_type const ch0 = std::forward<PeekFn>(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<typename ReadFn>
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<typename WriteFn>
static void write(uint32_t const cp, WriteFn && write_fn)
{
if (cp < 0x80) // 0xxx_xxxx
write_fn(static_cast<char_type>(cp));
else if (cp < 0x800) // 110x_xxxx 10xx_xxxx
{
write_fn(static_cast<char_type>(0xC0 | cp >> 6));
goto _1;
}
else if (cp < 0x10000) // 1110_xxxx 10xx_xxxx 10xx_xxxx
{
write_fn(static_cast<char_type>(0xE0 | cp >> 12));
goto _2;
}
else if (cp < 0x200000) // 1111_0xxx 10xx_xxxx 10xx_xxxx 10xx_xxxx
{
write_fn(static_cast<char_type>(0xF0 | cp >> 18));
goto _3;
}
else if (cp < 0x4000000) // 1111_10xx 10xx_xxxx 10xx_xxxx 10xx_xxxx 10xx_xxxx
{
write_fn(static_cast<char_type>(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<char_type>(0xFC | cp >> 30));
goto _5;
}
else
throw std::runtime_error("Tool large UTF8 code point");
return;
_5: write_fn(static_cast<char_type>(0x80 | (cp >> 24 & 0x3F)));
_4: write_fn(static_cast<char_type>(0x80 | (cp >> 18 & 0x3F)));
_3: write_fn(static_cast<char_type>(0x80 | (cp >> 12 & 0x3F)));
_2: write_fn(static_cast<char_type>(0x80 | (cp >> 6 & 0x3F)));
_1: write_fn(static_cast<char_type>(0x80 | (cp & 0x3F)));
}
};
}}

@ -330,12 +330,12 @@ TESTS = \
test_sql_str_func.sh \ test_sql_str_func.sh \
test_sql_time_func.sh \ test_sql_time_func.sh \
test_data_parser.sh \ test_data_parser.sh \
test_pretty_print.sh test_pretty_print.sh \
test_vt52_curses.sh
DISABLED_TESTS = \ DISABLED_TESTS = \
test_top_status \ test_top_status \
test_view_colors.sh \ test_view_colors.sh
test_vt52_curses.sh
if HAVE_LIBCURL if HAVE_LIBCURL
TESTS += \ TESTS += \

@ -35,7 +35,9 @@
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <string.h> #include <string.h>
#include <locale.h>
#include "base/lnav_log.hh"
#include "view_curses.hh" #include "view_curses.hh"
#include "vt52_curses.hh" #include "vt52_curses.hh"
@ -64,11 +66,14 @@ int main(int argc, char *argv[])
{ {
int lpc, c, fd, retval = EXIT_SUCCESS; int lpc, c, fd, retval = EXIT_SUCCESS;
vt52_curses vt; 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); fd = open("/tmp/lnav.err", O_WRONLY|O_CREAT|O_APPEND, 0666);
dup2(fd, STDERR_FILENO); dup2(fd, STDERR_FILENO);
close(fd); close(fd);
fprintf(stderr, "startup\n"); fprintf(stderr, "startup\n");
lnav_log_file = stderr;
while ((c = getopt(argc, argv, "y:")) != -1) { while ((c = getopt(argc, argv, "y:")) != -1) {
switch (c) { switch (c) {
@ -81,16 +86,16 @@ int main(int argc, char *argv[])
for (lpc = 0; lpc < 1000; lpc++) { for (lpc = 0; lpc < 1000; lpc++) {
int len; int len;
assert(vt.map_input(random(), len) != NULL); assert(vt.map_input(random(), len) != nullptr);
assert(len > 0); assert(len > 0);
} }
tgetent(NULL, "vt52"); tgetent(nullptr, "vt52");
{ {
static const char *CANNED_INPUT[] = { static const char *CANNED_INPUT[] = {
"abc", "Gru\xC3\x9F",
"\r", "\r",
tgetstr((char *)"ce", NULL), tgetstr((char *)"ce", nullptr),
"de", "de",
"\n", "\n",
"1\n", "1\n",
@ -103,7 +108,7 @@ int main(int argc, char *argv[])
"8\n", "8\n",
"9\n", "9\n",
"abc", "abc",
"\x2", "\x02",
"\a", "\a",
"ab\bcdef", "ab\bcdef",
0 0

@ -210,7 +210,7 @@ static void dump_memory(FILE *dst, const char *src, int len)
int lpc; int lpc;
for (lpc = 0; lpc < len; lpc++) { for (lpc = 0; lpc < len; lpc++) {
fprintf(dst, "%02x", src[lpc]); fprintf(dst, "%02x", src[lpc] & 0xff);
} }
} }

@ -1,4 +1,5 @@
#include <stdlib.h>
#include "config.h" #include "config.h"
#define _XOPEN_SOURCE_EXTENDED 1 #define _XOPEN_SOURCE_EXTENDED 1
#include <locale.h> #include <locale.h>
@ -19,6 +20,7 @@
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
setenv("LANG", "en_US.utf-8", 1);
setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
WINDOW *stdscr = initscr(); WINDOW *stdscr = initscr();

@ -1,38 +1,38 @@
sleep 2.051763 sleep 0.867286
write 31 write 0d
sleep 0.311858 sleep 0.141596
write 32 write 0d
sleep 0.295852 sleep 0.188837
write 33 write 0d
sleep 0.319916 sleep 0.177586
write 34 write 0d
sleep 0.327742 sleep 0.159950
write 35 write 0d
sleep 0.328183 sleep 0.158958
write 36 write 0d
sleep 0.335896 sleep 0.164105
write 37 write 0d
sleep 0.360085 sleep 0.176968
write 38 write 0d
sleep 0.336000 sleep 0.165942
write 39 write 0d
sleep 0.376058 sleep 0.187011
write 30 write 0d
sleep 0.840149 sleep 0.167987
write 61 write 0d
sleep 0.783892 sleep 0.173959
write 62 write 0d
sleep 0.415990 sleep 0.176091
write 63 write 0d
sleep 0.280001 sleep 0.180728
write 64 write 0d
sleep 0.191901 sleep 0.172983
write 65 write 0d
sleep 0.295949 sleep 0.167819
write 66 write 0d
sleep 0.775841 sleep 0.165876
write 67 write 0d
sleep 0.399883 sleep 0.175857
write 68 write 0d
sleep 0.304016 sleep 0.183068
write 69 write 0d

@ -1,38 +1,39 @@
read 1b29301b371b5b3f3437681b5b313b3234721b5b6d1b5b346c1b5b481b5b324a616263 read 1b29301b371b5b3f3437681b5b313b3234721b5b6d1b5b346c1b5b481b5b324a477275c39f
# write 31 # write 0d
read 0d read 0d
# write 32 # write 0d
read 1b5b4a read 1b5b4a
# write 33 # write 0d
read 6465 read 6465
# write 34 # write 0d
read 0d1b5b4a read 0d1b5b4a
# write 35 # write 0d
read read
# write 36 # write 0d
read read
# write 37 # write 0d
read read
# write 38 # write 0d
read read
# write 39 # write 0d
read read
# write 30 # write 0d
read read
# write 61 # write 0d
read read
# write 62 # write 0d
read read
# write 63 # write 0d
read read
# write 64 # write 0d
read 616263 read 616263
# write 65 # write 0d
read 0d1b5b4a read 0d1b5b4a
# write 66 # write 0d
read 07 read 07
# write 67 # write 0d
read 6163646566 read 6163646566
# write 68 # write 0d
read read
# write 69 # write 0d
read 1b5b32343b31481b5b324a1b5b3f34376c1b380d1b5b3f316c1b3e

Loading…
Cancel
Save