mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
3c3c179237
A user sent a transcript from a curses-based tool 'ncmpc', which carefully disables terminal autowrap when printing a character in the bottom right corner of the display, and then turns it back on again. After that, it expects that sending the backspace character really moves the cursor back a space, instead of clearing the wrapnext flag. But in PuTTY, we set the wrapnext flag even if we're not in wrapping mode - it just doesn't _do_ anything when the next character is sent. But it remains set, and still affects backspace. So the display is corrupted by this change of expectation. (Specifically, ncmpc is printing a time display [m:ss] in the very bottom right, so it disables wrap in order to print the final ']'. Then the next thing it needs to do is to update the low-order digit of the seconds field, so it sends \b as the simplest way to get to that character. The effect on the display is that the updated seconds digit appears where the ] was, instead of overwriting the old seconds digit.) This is a tradeoff in desirable behaviours. The point of having a backspace operation cancel the wrapnext flag and not actually move the cursor is to preserve the invariant that sending 'x', backspace, 'y' causes the y to overprint the x, even if that happens near the end of the terminal's line length. In non-wrapping mode that invariant was bound to break _eventually_, but with this change, it breaks one character earlier than before. However, I think that's less bad than breaking the expectations of curses-based full-screen applications, especially since the _main_ need for that invariant arises from naïve applications that don't want to have to think about the terminal width at all - and those applications generally run in _wrapping_ mode, where it's possible to continue the invariant across multiple lines in any case.
505 lines
18 KiB
C
505 lines
18 KiB
C
#include "putty.h"
|
|
#include "terminal.h"
|
|
|
|
void modalfatalbox(const char *p, ...)
|
|
{
|
|
va_list ap;
|
|
fprintf(stderr, "FATAL ERROR: ");
|
|
va_start(ap, p);
|
|
vfprintf(stderr, p, ap);
|
|
va_end(ap);
|
|
fputc('\n', stderr);
|
|
exit(1);
|
|
}
|
|
|
|
const char *const appname = "test_lineedit";
|
|
|
|
char *platform_default_s(const char *name)
|
|
{ return NULL; }
|
|
bool platform_default_b(const char *name, bool def)
|
|
{ return def; }
|
|
int platform_default_i(const char *name, int def)
|
|
{ return def; }
|
|
FontSpec *platform_default_fontspec(const char *name)
|
|
{ return fontspec_new_default(); }
|
|
Filename *platform_default_filename(const char *name)
|
|
{ return filename_from_str(""); }
|
|
|
|
const struct BackendVtable *const backends[] = { NULL };
|
|
|
|
typedef struct Mock {
|
|
Terminal *term;
|
|
Conf *conf;
|
|
struct unicode_data ucsdata[1];
|
|
|
|
strbuf *context;
|
|
|
|
bool any_test_failed;
|
|
|
|
TermWin tw;
|
|
} Mock;
|
|
|
|
static bool mock_setup_draw_ctx(TermWin *win) { return false; }
|
|
static void mock_draw_text(TermWin *win, int x, int y, wchar_t *text, int len,
|
|
unsigned long attrs, int lattrs, truecolour tc) {}
|
|
static void mock_draw_cursor(TermWin *win, int x, int y, wchar_t *text,
|
|
int len, unsigned long attrs, int lattrs,
|
|
truecolour tc) {}
|
|
static void mock_set_raw_mouse_mode(TermWin *win, bool enable) {}
|
|
static void mock_set_raw_mouse_mode_pointer(TermWin *win, bool enable) {}
|
|
static void mock_palette_set(TermWin *win, unsigned start, unsigned ncolours,
|
|
const rgb *colours) {}
|
|
static void mock_palette_get_overrides(TermWin *tw, Terminal *term) {}
|
|
|
|
static const TermWinVtable mock_termwin_vt = {
|
|
.setup_draw_ctx = mock_setup_draw_ctx,
|
|
.draw_text = mock_draw_text,
|
|
.draw_cursor = mock_draw_cursor,
|
|
.set_raw_mouse_mode = mock_set_raw_mouse_mode,
|
|
.set_raw_mouse_mode_pointer = mock_set_raw_mouse_mode_pointer,
|
|
.palette_set = mock_palette_set,
|
|
.palette_get_overrides = mock_palette_get_overrides,
|
|
};
|
|
|
|
static Mock *mock_new(void)
|
|
{
|
|
Mock *mk = snew(Mock);
|
|
memset(mk, 0, sizeof(*mk));
|
|
|
|
mk->conf = conf_new();
|
|
do_defaults(NULL, mk->conf);
|
|
|
|
init_ucs_generic(mk->conf, mk->ucsdata);
|
|
mk->ucsdata->line_codepage = CP_ISO8859_1;
|
|
|
|
mk->context = strbuf_new();
|
|
|
|
mk->tw.vt = &mock_termwin_vt;
|
|
|
|
return mk;
|
|
}
|
|
|
|
static void mock_free(Mock *mk)
|
|
{
|
|
strbuf_free(mk->context);
|
|
conf_free(mk->conf);
|
|
term_free(mk->term);
|
|
sfree(mk);
|
|
}
|
|
|
|
static void reset(Mock *mk)
|
|
{
|
|
term_pwron(mk->term, true);
|
|
term_size(mk->term, 24, 80, 0);
|
|
term_set_trust_status(mk->term, false);
|
|
strbuf_clear(mk->context);
|
|
}
|
|
|
|
#if 0
|
|
|
|
static void test_context(Mock *mk, const char *fmt, ...)
|
|
{
|
|
strbuf_clear(mk->context);
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
put_fmtv(mk->context, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
#endif
|
|
|
|
static void report_fail(Mock *mk, const char *file, int line,
|
|
const char *fmt, ...)
|
|
{
|
|
printf("%s:%d", file, line);
|
|
if (mk->context->len)
|
|
printf(" (%s)", mk->context->s);
|
|
printf(": ");
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
vprintf(fmt, ap);
|
|
va_end(ap);
|
|
printf("\n");
|
|
mk->any_test_failed = true;
|
|
}
|
|
|
|
static inline void check_iequal(Mock *mk, const char *file, int line,
|
|
long long lhs, long long rhs)
|
|
{
|
|
if (lhs != rhs)
|
|
report_fail(mk, file, line, "%lld != %lld / %#llx != %#llx",
|
|
lhs, rhs, lhs, rhs);
|
|
}
|
|
|
|
#define IEQUAL(lhs, rhs) check_iequal(mk, __FILE__, __LINE__, lhs, rhs)
|
|
|
|
static inline void term_datapl(Terminal *term, ptrlen pl)
|
|
{
|
|
term_data(term, pl.ptr, pl.len);
|
|
}
|
|
|
|
static struct termchar get_termchar(Terminal *term, int x, int y)
|
|
{
|
|
termline *tl = term_get_line(term, y);
|
|
termchar tc;
|
|
if (0 <= x && x < tl->cols)
|
|
tc = tl->chars[x];
|
|
else
|
|
tc = term->erase_char;
|
|
term_release_line(tl);
|
|
return tc;
|
|
}
|
|
|
|
static unsigned short get_lineattr(Terminal *term, int y)
|
|
{
|
|
termline *tl = term_get_line(term, y);
|
|
unsigned short lattr = tl->lattr;
|
|
term_release_line(tl);
|
|
return lattr;
|
|
}
|
|
|
|
static void test_hello_world(Mock *mk)
|
|
{
|
|
/* A trivial test just to kick off this test framework */
|
|
mk->ucsdata->line_codepage = CP_ISO8859_1;
|
|
|
|
reset(mk);
|
|
term_datapl(mk->term, PTRLEN_LITERAL("hello, world"));
|
|
IEQUAL(mk->term->curs.x, 12);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(get_termchar(mk->term, 0, 0).chr, CSET_ASCII | 'h');
|
|
IEQUAL(get_termchar(mk->term, 1, 0).chr, CSET_ASCII | 'e');
|
|
IEQUAL(get_termchar(mk->term, 2, 0).chr, CSET_ASCII | 'l');
|
|
IEQUAL(get_termchar(mk->term, 3, 0).chr, CSET_ASCII | 'l');
|
|
IEQUAL(get_termchar(mk->term, 4, 0).chr, CSET_ASCII | 'o');
|
|
IEQUAL(get_termchar(mk->term, 5, 0).chr, CSET_ASCII | ',');
|
|
IEQUAL(get_termchar(mk->term, 6, 0).chr, CSET_ASCII | ' ');
|
|
IEQUAL(get_termchar(mk->term, 7, 0).chr, CSET_ASCII | 'w');
|
|
IEQUAL(get_termchar(mk->term, 8, 0).chr, CSET_ASCII | 'o');
|
|
IEQUAL(get_termchar(mk->term, 9, 0).chr, CSET_ASCII | 'r');
|
|
IEQUAL(get_termchar(mk->term, 10, 0).chr, CSET_ASCII | 'l');
|
|
IEQUAL(get_termchar(mk->term, 11, 0).chr, CSET_ASCII | 'd');
|
|
}
|
|
|
|
static void test_wrap(Mock *mk)
|
|
{
|
|
/* Test behaviour when printing characters wrap to the next line */
|
|
mk->ucsdata->line_codepage = CP_UTF8;
|
|
|
|
/* Print 'abc' without enough space for the c, in wrapping mode */
|
|
reset(mk);
|
|
mk->term->curs.x = 78;
|
|
mk->term->curs.y = 0;
|
|
mk->term->wrap = true;
|
|
/* The 'a' prints without anything unusual happening */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("a"));
|
|
IEQUAL(mk->term->curs.x, 79);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
IEQUAL(get_termchar(mk->term, 78, 0).chr, CSET_ASCII | 'a');
|
|
/* The 'b' prints, leaving the cursor where it is with wrapnext set */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("b"));
|
|
IEQUAL(mk->term->curs.x, 79);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 1);
|
|
IEQUAL(get_lineattr(mk->term, 0), 0);
|
|
IEQUAL(get_termchar(mk->term, 79, 0).chr, CSET_ASCII | 'b');
|
|
/* And now the 'c' causes a deferred wrap and goes to the next line */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("c"));
|
|
IEQUAL(mk->term->curs.x, 1);
|
|
IEQUAL(mk->term->curs.y, 1);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
IEQUAL(get_lineattr(mk->term, 0), LATTR_WRAPPED);
|
|
IEQUAL(get_termchar(mk->term, 79, 0).chr, CSET_ASCII | 'b');
|
|
IEQUAL(get_termchar(mk->term, 0, 1).chr, CSET_ASCII | 'c');
|
|
/* If we backspace once, the cursor moves back on to the c */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("\b"));
|
|
IEQUAL(mk->term->curs.x, 0);
|
|
IEQUAL(mk->term->curs.y, 1);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
/* Now backspace again, and the cursor returns to the b */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("\b"));
|
|
IEQUAL(mk->term->curs.x, 79);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
|
|
/* Now try it with a double-width character in place of ab */
|
|
mk->term->curs.x = 78;
|
|
mk->term->curs.y = 0;
|
|
mk->term->wrap = true;
|
|
/* The DW character goes directly to the wrapnext state */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("\xEA\xB0\x80"));
|
|
IEQUAL(mk->term->curs.x, 79);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 1);
|
|
IEQUAL(get_termchar(mk->term, 78, 0).chr, 0xAC00);
|
|
IEQUAL(get_termchar(mk->term, 79, 0).chr, UCSWIDE);
|
|
/* And the 'c' causes a deferred wrap as before */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("c"));
|
|
IEQUAL(mk->term->curs.x, 1);
|
|
IEQUAL(mk->term->curs.y, 1);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
IEQUAL(get_lineattr(mk->term, 0), LATTR_WRAPPED);
|
|
IEQUAL(get_termchar(mk->term, 78, 0).chr, 0xAC00);
|
|
IEQUAL(get_termchar(mk->term, 79, 0).chr, UCSWIDE);
|
|
IEQUAL(get_termchar(mk->term, 0, 1).chr, CSET_ASCII | 'c');
|
|
/* If we backspace once, the cursor moves back on to the c */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("\b"));
|
|
IEQUAL(mk->term->curs.x, 0);
|
|
IEQUAL(mk->term->curs.y, 1);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
/* Now backspace again, and the cursor goes to the RHS of the DW char */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("\b"));
|
|
IEQUAL(mk->term->curs.x, 79);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
|
|
/* Now put the DW character in place of bc */
|
|
reset(mk);
|
|
mk->term->curs.x = 78;
|
|
mk->term->curs.y = 0;
|
|
mk->term->wrap = true;
|
|
/* The 'a' prints as before */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("a"));
|
|
IEQUAL(mk->term->curs.x, 79);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
IEQUAL(get_termchar(mk->term, 78, 0).chr, CSET_ASCII | 'a');
|
|
/* The DW character wraps, setting LATTR_WRAPPED2 */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("\xEA\xB0\x80"));
|
|
IEQUAL(mk->term->curs.x, 2);
|
|
IEQUAL(mk->term->curs.y, 1);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
IEQUAL(get_lineattr(mk->term, 0), LATTR_WRAPPED | LATTR_WRAPPED2);
|
|
IEQUAL(get_termchar(mk->term, 78, 0).chr, CSET_ASCII | 'a');
|
|
IEQUAL(get_termchar(mk->term, 79, 0).chr, CSET_ASCII | ' ');
|
|
IEQUAL(get_termchar(mk->term, 0, 1).chr, 0xAC00);
|
|
IEQUAL(get_termchar(mk->term, 1, 1).chr, UCSWIDE);
|
|
/* If we backspace once, cursor moves to the RHS of the DW char */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("\b"));
|
|
IEQUAL(mk->term->curs.x, 1);
|
|
IEQUAL(mk->term->curs.y, 1);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
/* Backspace again, and cursor moves from RHS to LHS of that char */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("\b"));
|
|
IEQUAL(mk->term->curs.x, 0);
|
|
IEQUAL(mk->term->curs.y, 1);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
/* Now backspace again, and the cursor skips the empty column so
|
|
* that it can return to the previous logical character, to wit, the a */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("\b"));
|
|
IEQUAL(mk->term->curs.x, 78);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
|
|
/* Print 'ab' up to the rightmost column, and then backspace */
|
|
reset(mk);
|
|
mk->term->curs.x = 78;
|
|
mk->term->curs.y = 0;
|
|
mk->term->wrap = true;
|
|
/* As before, the 'ab' put us in the rightmost column with wrapnext set */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("ab"));
|
|
IEQUAL(mk->term->curs.x, 79);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 1);
|
|
IEQUAL(get_lineattr(mk->term, 0), 0);
|
|
IEQUAL(get_termchar(mk->term, 78, 0).chr, CSET_ASCII | 'a');
|
|
IEQUAL(get_termchar(mk->term, 79, 0).chr, CSET_ASCII | 'b');
|
|
/* Backspacing just clears the wrapnext flag, so we're logically
|
|
* back on the b again */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("\b"));
|
|
IEQUAL(mk->term->curs.x, 79);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
|
|
/* For completeness, the easy case: just print 'a' then backspace */
|
|
reset(mk);
|
|
mk->term->curs.x = 78;
|
|
mk->term->curs.y = 0;
|
|
mk->term->wrap = true;
|
|
/* 'a' printed in column n-1 takes us to column n */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("a"));
|
|
IEQUAL(mk->term->curs.x, 79);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
IEQUAL(get_lineattr(mk->term, 0), 0);
|
|
IEQUAL(get_termchar(mk->term, 78, 0).chr, CSET_ASCII | 'a');
|
|
/* Backspacing moves us back a space on to the a */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("\b"));
|
|
IEQUAL(mk->term->curs.x, 78);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
|
|
/*
|
|
* Now test the special cases that arise when the terminal is only
|
|
* one column wide!
|
|
*/
|
|
|
|
reset(mk);
|
|
term_size(mk->term, 24, 1, 0);
|
|
mk->term->curs.x = 0;
|
|
mk->term->curs.y = 0;
|
|
mk->term->wrap = true;
|
|
/* Printing a single-width character takes us into wrapnext immediately */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("a"));
|
|
IEQUAL(mk->term->curs.x, 0);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 1);
|
|
IEQUAL(get_lineattr(mk->term, 0), 0);
|
|
IEQUAL(get_termchar(mk->term, 0, 0).chr, CSET_ASCII | 'a');
|
|
/* Printing a second one wraps, and takes us _back_ to wrapnext */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("b"));
|
|
IEQUAL(mk->term->curs.x, 0);
|
|
IEQUAL(mk->term->curs.y, 1);
|
|
IEQUAL(mk->term->wrapnext, 1);
|
|
IEQUAL(get_lineattr(mk->term, 0), LATTR_WRAPPED);
|
|
IEQUAL(get_termchar(mk->term, 0, 0).chr, CSET_ASCII | 'a');
|
|
IEQUAL(get_termchar(mk->term, 0, 1).chr, CSET_ASCII | 'b');
|
|
/* Backspacing once clears the wrapnext flag, putting us on the b */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("\b"));
|
|
IEQUAL(mk->term->curs.x, 0);
|
|
IEQUAL(mk->term->curs.y, 1);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
/* Backspacing again returns to the previous line, putting us on the a */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("\b"));
|
|
IEQUAL(mk->term->curs.x, 0);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
|
|
/* And now try with a double-width character */
|
|
reset(mk);
|
|
term_size(mk->term, 24, 1, 0);
|
|
mk->term->curs.x = 0;
|
|
mk->term->curs.y = 0;
|
|
mk->term->wrap = true;
|
|
/* DW character won't fit at all, so it transforms into U+FFFD
|
|
* REPLACEMENT CHARACTER and then behaves like a SW char */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("\xEA\xB0\x80"));
|
|
IEQUAL(mk->term->curs.x, 0);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 1);
|
|
IEQUAL(get_lineattr(mk->term, 0), 0);
|
|
IEQUAL(get_termchar(mk->term, 0, 0).chr, 0xFFFD);
|
|
}
|
|
|
|
static void test_nonwrap(Mock *mk)
|
|
{
|
|
/* Test behaviour when printing characters hit end of line without wrap.
|
|
* The wrapnext flag is never set in this mode. */
|
|
mk->ucsdata->line_codepage = CP_UTF8;
|
|
|
|
/* Print 'abc' without enough space for the c */
|
|
reset(mk);
|
|
mk->term->curs.x = 78;
|
|
mk->term->curs.y = 0;
|
|
mk->term->wrap = false;
|
|
/* The 'a' prints without anything unusual happening */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("a"));
|
|
IEQUAL(mk->term->curs.x, 79);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
IEQUAL(get_termchar(mk->term, 78, 0).chr, CSET_ASCII | 'a');
|
|
/* The 'b' prints, leaving the cursor where it is */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("b"));
|
|
IEQUAL(mk->term->curs.x, 79);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
IEQUAL(get_lineattr(mk->term, 0), 0);
|
|
IEQUAL(get_termchar(mk->term, 79, 0).chr, CSET_ASCII | 'b');
|
|
/* The 'c' overwrites the b */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("c"));
|
|
IEQUAL(mk->term->curs.x, 79);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
IEQUAL(get_lineattr(mk->term, 0), 0);
|
|
IEQUAL(get_termchar(mk->term, 78, 0).chr, CSET_ASCII | 'a');
|
|
IEQUAL(get_termchar(mk->term, 79, 0).chr, CSET_ASCII | 'c');
|
|
/* Since wrapnext was never set, backspacing returns us to the a */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("\b"));
|
|
IEQUAL(mk->term->curs.x, 78);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
|
|
/* Now try it with a double-width character in place of ab */
|
|
mk->term->curs.x = 78;
|
|
mk->term->curs.y = 0;
|
|
mk->term->wrap = false;
|
|
/* The DW character occupies the rightmost two columns */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("\xEA\xB0\x80"));
|
|
IEQUAL(mk->term->curs.x, 79);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
IEQUAL(get_termchar(mk->term, 78, 0).chr, 0xAC00);
|
|
IEQUAL(get_termchar(mk->term, 79, 0).chr, UCSWIDE);
|
|
/* The 'c' must overprint the RHS of the DW char, clearing the LHS */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("c"));
|
|
IEQUAL(mk->term->curs.x, 79);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
IEQUAL(get_lineattr(mk->term, 0), 0);
|
|
IEQUAL(get_termchar(mk->term, 78, 0).chr, CSET_ASCII | ' ');
|
|
IEQUAL(get_termchar(mk->term, 79, 0).chr, CSET_ASCII | 'c');
|
|
|
|
/* Now put the DW char in place of the bc */
|
|
reset(mk);
|
|
mk->term->curs.x = 78;
|
|
mk->term->curs.y = 0;
|
|
mk->term->wrap = false;
|
|
/* The 'a' prints as before */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("a"));
|
|
IEQUAL(mk->term->curs.x, 79);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
IEQUAL(get_termchar(mk->term, 78, 0).chr, CSET_ASCII | 'a');
|
|
/* The DW char won't fit, so turns into U+FFFD REPLACEMENT CHARACTER */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("\xEA\xB0\x80"));
|
|
IEQUAL(mk->term->curs.x, 79);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
IEQUAL(get_lineattr(mk->term, 0), 0);
|
|
IEQUAL(get_termchar(mk->term, 78, 0).chr, CSET_ASCII | 'a');
|
|
IEQUAL(get_termchar(mk->term, 79, 0).chr, 0xFFFD);
|
|
|
|
/* Just for completeness, try both of those together */
|
|
reset(mk);
|
|
mk->term->curs.x = 78;
|
|
mk->term->curs.y = 0;
|
|
mk->term->wrap = false;
|
|
/* First DW character occupies the rightmost columns */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("\xEA\xB0\x80"));
|
|
IEQUAL(mk->term->curs.x, 79);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
IEQUAL(get_termchar(mk->term, 78, 0).chr, 0xAC00);
|
|
IEQUAL(get_termchar(mk->term, 79, 0).chr, UCSWIDE);
|
|
/* Second DW char becomes U+FFFD, overwriting RHS of the first one */
|
|
term_datapl(mk->term, PTRLEN_LITERAL("\xEA\xB0\x81"));
|
|
IEQUAL(mk->term->curs.x, 79);
|
|
IEQUAL(mk->term->curs.y, 0);
|
|
IEQUAL(mk->term->wrapnext, 0);
|
|
IEQUAL(get_lineattr(mk->term, 0), 0);
|
|
IEQUAL(get_termchar(mk->term, 78, 0).chr, CSET_ASCII | ' ');
|
|
IEQUAL(get_termchar(mk->term, 79, 0).chr, 0xFFFD);
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
Mock *mk = mock_new();
|
|
mk->term = term_init(mk->conf, mk->ucsdata, &mk->tw);
|
|
|
|
test_hello_world(mk);
|
|
test_wrap(mk);
|
|
test_nonwrap(mk);
|
|
|
|
bool failed = mk->any_test_failed;
|
|
mock_free(mk);
|
|
|
|
if (failed) {
|
|
printf("Test suite FAILED!\n");
|
|
return 1;
|
|
} else {
|
|
printf("Test suite passed\n");
|
|
return 0;
|
|
}
|
|
}
|