Large diffs are not rendered by default.

@@ -0,0 +1,56 @@
/*
* Some utility functions on VTermRect structures
*/

#define STRFrect "(%d,%d-%d,%d)"
#define ARGSrect(r) (r).start_row, (r).start_col, (r).end_row, (r).end_col

/* Expand dst to contain src as well */
static void rect_expand(VTermRect *dst, VTermRect *src)
{
if(dst->start_row > src->start_row) dst->start_row = src->start_row;
if(dst->start_col > src->start_col) dst->start_col = src->start_col;
if(dst->end_row < src->end_row) dst->end_row = src->end_row;
if(dst->end_col < src->end_col) dst->end_col = src->end_col;
}

/* Clip the dst to ensure it does not step outside of bounds */
static void rect_clip(VTermRect *dst, VTermRect *bounds)
{
if(dst->start_row < bounds->start_row) dst->start_row = bounds->start_row;
if(dst->start_col < bounds->start_col) dst->start_col = bounds->start_col;
if(dst->end_row > bounds->end_row) dst->end_row = bounds->end_row;
if(dst->end_col > bounds->end_col) dst->end_col = bounds->end_col;
/* Ensure it doesn't end up negatively-sized */
if(dst->end_row < dst->start_row) dst->end_row = dst->start_row;
if(dst->end_col < dst->start_col) dst->end_col = dst->start_col;
}

/* True if the two rectangles are equal */
static int rect_equal(VTermRect *a, VTermRect *b)
{
return (a->start_row == b->start_row) &&
(a->start_col == b->start_col) &&
(a->end_row == b->end_row) &&
(a->end_col == b->end_col);
}

/* True if small is contained entirely within big */
static int rect_contains(VTermRect *big, VTermRect *small)
{
if(small->start_row < big->start_row) return 0;
if(small->start_col < big->start_col) return 0;
if(small->end_row > big->end_row) return 0;
if(small->end_col > big->end_col) return 0;
return 1;
}

/* True if the rectangles overlap at all */
static int rect_intersects(VTermRect *a, VTermRect *b)
{
if(a->start_row > b->end_row || b->start_row > a->end_row)
return 0;
if(a->start_col > b->end_col || b->start_col > a->end_col)
return 0;
return 1;
}

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -0,0 +1,331 @@
#include "vterm_internal.h"

/* ### The following from http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
* With modifications:
* made functions static
* moved 'combining' table to file scope, so other functions can see it
* ###################################################################
*/

/*
* This is an implementation of wcwidth() and wcswidth() (defined in
* IEEE Std 1002.1-2001) for Unicode.
*
* http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
* http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
*
* In fixed-width output devices, Latin characters all occupy a single
* "cell" position of equal width, whereas ideographic CJK characters
* occupy two such cells. Interoperability between terminal-line
* applications and (teletype-style) character terminals using the
* UTF-8 encoding requires agreement on which character should advance
* the cursor by how many cell positions. No established formal
* standards exist at present on which Unicode character shall occupy
* how many cell positions on character terminals. These routines are
* a first attempt of defining such behavior based on simple rules
* applied to data provided by the Unicode Consortium.
*
* For some graphical characters, the Unicode standard explicitly
* defines a character-cell width via the definition of the East Asian
* FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
* In all these cases, there is no ambiguity about which width a
* terminal shall use. For characters in the East Asian Ambiguous (A)
* class, the width choice depends purely on a preference of backward
* compatibility with either historic CJK or Western practice.
* Choosing single-width for these characters is easy to justify as
* the appropriate long-term solution, as the CJK practice of
* displaying these characters as double-width comes from historic
* implementation simplicity (8-bit encoded characters were displayed
* single-width and 16-bit ones double-width, even for Greek,
* Cyrillic, etc.) and not any typographic considerations.
*
* Much less clear is the choice of width for the Not East Asian
* (Neutral) class. Existing practice does not dictate a width for any
* of these characters. It would nevertheless make sense
* typographically to allocate two character cells to characters such
* as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
* represented adequately with a single-width glyph. The following
* routines at present merely assign a single-cell width to all
* neutral characters, in the interest of simplicity. This is not
* entirely satisfactory and should be reconsidered before
* establishing a formal standard in this area. At the moment, the
* decision which Not East Asian (Neutral) characters should be
* represented by double-width glyphs cannot yet be answered by
* applying a simple rule from the Unicode database content. Setting
* up a proper standard for the behavior of UTF-8 character terminals
* will require a careful analysis not only of each Unicode character,
* but also of each presentation form, something the author of these
* routines has avoided to do so far.
*
* http://www.unicode.org/unicode/reports/tr11/
*
* Markus Kuhn -- 2007-05-26 (Unicode 5.0)
*
* Permission to use, copy, modify, and distribute this software
* for any purpose and without fee is hereby granted. The author
* disclaims all warranties with regard to this software.
*
* Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
*/

struct interval {
int first;
int last;
};

/* sorted list of non-overlapping intervals of non-spacing characters */
/* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
static const struct interval combining[] = {
{ 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 },
{ 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
{ 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 },
{ 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 },
{ 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
{ 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
{ 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 },
{ 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D },
{ 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 },
{ 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD },
{ 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C },
{ 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D },
{ 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC },
{ 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD },
{ 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C },
{ 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D },
{ 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 },
{ 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 },
{ 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC },
{ 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
{ 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
{ 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
{ 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
{ 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
{ 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
{ 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
{ 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
{ 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
{ 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
{ 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F },
{ 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 },
{ 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD },
{ 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD },
{ 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 },
{ 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B },
{ 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 },
{ 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
{ 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF },
{ 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 },
{ 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F },
{ 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B },
{ 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F },
{ 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB },
{ 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F },
{ 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 },
{ 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD },
{ 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F },
{ 0xE0100, 0xE01EF }
};


/* auxiliary function for binary search in interval table */
static int bisearch(uint32_t ucs, const struct interval *table, int max) {
int min = 0;
int mid;

if ((int)ucs < table[0].first || (int)ucs > table[max].last)
return 0;
while (max >= min) {
mid = (min + max) / 2;
if ((int)ucs > table[mid].last)
min = mid + 1;
else if ((int)ucs < table[mid].first)
max = mid - 1;
else
return 1;
}

return 0;
}


/* The following two functions define the column width of an ISO 10646
* character as follows:
*
* - The null character (U+0000) has a column width of 0.
*
* - Other C0/C1 control characters and DEL will lead to a return
* value of -1.
*
* - Non-spacing and enclosing combining characters (general
* category code Mn or Me in the Unicode database) have a
* column width of 0.
*
* - SOFT HYPHEN (U+00AD) has a column width of 1.
*
* - Other format characters (general category code Cf in the Unicode
* database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
*
* - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
* have a column width of 0.
*
* - Spacing characters in the East Asian Wide (W) or East Asian
* Full-width (F) category as defined in Unicode Technical
* Report #11 have a column width of 2.
*
* - All remaining characters (including all printable
* ISO 8859-1 and WGL4 characters, Unicode control characters,
* etc.) have a column width of 1.
*
* This implementation assumes that uint32_t characters are encoded
* in ISO 10646.
*/


static int mk_wcwidth(uint32_t ucs)
{
/* test for 8-bit control characters */
if (ucs == 0)
return 0;
if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
return -1;

/* binary search in table of non-spacing characters */
if (bisearch(ucs, combining,
sizeof(combining) / sizeof(struct interval) - 1))
return 0;

/* if we arrive here, ucs is not a combining or C0/C1 control character */

return 1 +
(ucs >= 0x1100 &&
(ucs <= 0x115f || /* Hangul Jamo init. consonants */
ucs == 0x2329 || ucs == 0x232a ||
(ucs >= 0x2e80 && ucs <= 0xa4cf &&
ucs != 0x303f) || /* CJK ... Yi */
(ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
(ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
(ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */
(ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
(ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */
(ucs >= 0xffe0 && ucs <= 0xffe6) ||
(ucs >= 0x20000 && ucs <= 0x2fffd) ||
(ucs >= 0x30000 && ucs <= 0x3fffd)));
}

#if 0 /* unused */
static int mk_wcswidth(const uint32_t *pwcs, size_t n)
{
int w, width = 0;

for (;*pwcs && n-- > 0; pwcs++)
if ((w = mk_wcwidth(*pwcs)) < 0)
return -1;
else
width += w;

return width;
}


/*
* The following functions are the same as mk_wcwidth() and
* mk_wcswidth(), except that spacing characters in the East Asian
* Ambiguous (A) category as defined in Unicode Technical Report #11
* have a column width of 2. This variant might be useful for users of
* CJK legacy encodings who want to migrate to UCS without changing
* the traditional terminal character-width behaviour. It is not
* otherwise recommended for general use.
*/
static int mk_wcwidth_cjk(uint32_t ucs)
{
/* sorted list of non-overlapping intervals of East Asian Ambiguous
* characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */
static const struct interval ambiguous[] = {
{ 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 },
{ 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 },
{ 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 },
{ 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 },
{ 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED },
{ 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA },
{ 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 },
{ 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B },
{ 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 },
{ 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 },
{ 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 },
{ 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE },
{ 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 },
{ 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA },
{ 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 },
{ 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB },
{ 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB },
{ 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 },
{ 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 },
{ 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 },
{ 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 },
{ 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 },
{ 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 },
{ 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 },
{ 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC },
{ 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 },
{ 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 },
{ 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 },
{ 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 },
{ 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 },
{ 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 },
{ 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B },
{ 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 },
{ 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 },
{ 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E },
{ 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 },
{ 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 },
{ 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F },
{ 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 },
{ 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF },
{ 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B },
{ 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 },
{ 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 },
{ 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 },
{ 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 },
{ 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 },
{ 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 },
{ 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 },
{ 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 },
{ 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F },
{ 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF },
{ 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD }
};

/* binary search in table of non-spacing characters */
if (bisearch(ucs, ambiguous,
sizeof(ambiguous) / sizeof(struct interval) - 1))
return 2;

return mk_wcwidth(ucs);
}

static int mk_wcswidth_cjk(const uint32_t *pwcs, size_t n)
{
int w, width = 0;

for (;*pwcs && n-- > 0; pwcs++)
if ((w = mk_wcwidth_cjk(*pwcs)) < 0)
return -1;
else
width += w;

return width;
}
#endif

/* ################################
* ### The rest added by Paul Evans */

INTERNAL int vterm_unicode_width(uint32_t codepoint)
{
return mk_wcwidth(codepoint);
}

INTERNAL int vterm_unicode_is_combining(uint32_t codepoint)
{
return bisearch(codepoint, combining, sizeof(combining) / sizeof(struct interval) - 1);
}
@@ -0,0 +1,47 @@
/* The following functions copied and adapted from libtermkey
*
* http://www.leonerd.org.uk/code/libtermkey/
*/
unsigned int utf8_seqlen(long codepoint);

#if defined(DEFINE_INLINES) || USE_INLINE
INLINE unsigned int utf8_seqlen(long codepoint)
{
if(codepoint < 0x0000080) return 1;
if(codepoint < 0x0000800) return 2;
if(codepoint < 0x0010000) return 3;
if(codepoint < 0x0200000) return 4;
if(codepoint < 0x4000000) return 5;
return 6;
}
#endif

/* Does NOT NUL-terminate the buffer */
int fill_utf8(long codepoint, char *str);

#if defined(DEFINE_INLINES) || USE_INLINE
INLINE int fill_utf8(long codepoint, char *str)
{
int nbytes = utf8_seqlen(codepoint);

/* This is easier done backwards */
int b = nbytes;
while(b > 1) {
b--;
str[b] = 0x80 | (codepoint & 0x3f);
codepoint >>= 6;
}

switch(nbytes) {
case 1: str[0] = (codepoint & 0x7f); break;
case 2: str[0] = 0xc0 | (codepoint & 0x1f); break;
case 3: str[0] = 0xe0 | (codepoint & 0x0f); break;
case 4: str[0] = 0xf0 | (codepoint & 0x07); break;
case 5: str[0] = 0xf8 | (codepoint & 0x03); break;
case 6: str[0] = 0xfc | (codepoint & 0x01); break;
}

return nbytes;
}
#endif
/* end copy */
@@ -0,0 +1,385 @@
#define DEFINE_INLINES

#include "vterm_internal.h"

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

#include "utf8.h"

/*****************
* API functions *
*****************/

static void *default_malloc(size_t size, void *allocdata UNUSED)
{
void *ptr = malloc(size);
if(ptr)
memset(ptr, 0, size);
return ptr;
}

static void default_free(void *ptr, void *allocdata UNUSED)
{
free(ptr);
}

static VTermAllocatorFunctions default_allocator = {
&default_malloc, /* malloc */
&default_free /* free */
};

VTerm *vterm_new(int rows, int cols)
{
return vterm_new_with_allocator(rows, cols, &default_allocator, NULL);
}

VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *funcs, void *allocdata)
{
/* Need to bootstrap using the allocator function directly */
VTerm *vt = (*funcs->malloc)(sizeof(VTerm), allocdata);

vt->allocator = funcs;
vt->allocdata = allocdata;

vt->rows = rows;
vt->cols = cols;

vt->parser_state = NORMAL;

vt->parser_callbacks = NULL;
vt->cbdata = NULL;

vt->strbuffer_len = 64;
vt->strbuffer_cur = 0;
vt->strbuffer = vterm_allocator_malloc(vt, vt->strbuffer_len);

vt->outbuffer_len = 64;
vt->outbuffer_cur = 0;
vt->outbuffer = vterm_allocator_malloc(vt, vt->outbuffer_len);

return vt;
}

void vterm_free(VTerm *vt)
{
if(vt->screen)
vterm_screen_free(vt->screen);

if(vt->state)
vterm_state_free(vt->state);

vterm_allocator_free(vt, vt->strbuffer);
vterm_allocator_free(vt, vt->outbuffer);

vterm_allocator_free(vt, vt);
}

INTERNAL void *vterm_allocator_malloc(VTerm *vt, size_t size)
{
return (*vt->allocator->malloc)(size, vt->allocdata);
}

INTERNAL void vterm_allocator_free(VTerm *vt, void *ptr)
{
(*vt->allocator->free)(ptr, vt->allocdata);
}

void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp)
{
if(rowsp)
*rowsp = vt->rows;
if(colsp)
*colsp = vt->cols;
}

void vterm_set_size(VTerm *vt, int rows, int cols)
{
vt->rows = rows;
vt->cols = cols;

if(vt->parser_callbacks && vt->parser_callbacks->resize)
(*vt->parser_callbacks->resize)(rows, cols, vt->cbdata);
}

int vterm_get_utf8(const VTerm *vt)
{
return vt->mode.utf8;
}

void vterm_set_utf8(VTerm *vt, int is_utf8)
{
vt->mode.utf8 = is_utf8;
}

INTERNAL void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len)
{
if(len > vt->outbuffer_len - vt->outbuffer_cur) {
DEBUG_LOG("vterm_push_output(): buffer overflow; truncating output\n");
len = vt->outbuffer_len - vt->outbuffer_cur;
}

memcpy(vt->outbuffer + vt->outbuffer_cur, bytes, len);
vt->outbuffer_cur += len;
}

static int outbuffer_is_full(VTerm *vt)
{
return vt->outbuffer_cur >= vt->outbuffer_len - 1;
}

INTERNAL void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args)
{
int written;
char buffer[1024]; /* 1Kbyte is enough for everybody, right? */

if(outbuffer_is_full(vt)) {
DEBUG_LOG("vterm_push_output(): buffer overflow; truncating output\n");
return;
}

written = vsprintf(buffer, format, args);

if(written >= (int)(vt->outbuffer_len - vt->outbuffer_cur)) {
/* output was truncated */
written = vt->outbuffer_len - vt->outbuffer_cur;
}
if (written > 0)
{
strncpy(vt->outbuffer + vt->outbuffer_cur, buffer, written + 1);
vt->outbuffer_cur += written;
}
}

INTERNAL void vterm_push_output_sprintf(VTerm *vt, const char *format, ...)
{
va_list args;
va_start(args, format);
vterm_push_output_vsprintf(vt, format, args);
va_end(args);
}

INTERNAL void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt, ...)
{
size_t orig_cur = vt->outbuffer_cur;
va_list args;

if(ctrl >= 0x80 && !vt->mode.ctrl8bit)
vterm_push_output_sprintf(vt, ESC_S "%c", ctrl - 0x40);
else
vterm_push_output_sprintf(vt, "%c", ctrl);

va_start(args, fmt);
vterm_push_output_vsprintf(vt, fmt, args);
va_end(args);

if(outbuffer_is_full(vt))
vt->outbuffer_cur = orig_cur;
}

INTERNAL void vterm_push_output_sprintf_dcs(VTerm *vt, const char *fmt, ...)
{
size_t orig_cur = vt->outbuffer_cur;
va_list args;

if(!vt->mode.ctrl8bit)
vterm_push_output_sprintf(vt, ESC_S "%c", C1_DCS - 0x40);
else
vterm_push_output_sprintf(vt, "%c", C1_DCS);

va_start(args, fmt);
vterm_push_output_vsprintf(vt, fmt, args);
va_end(args);

vterm_push_output_sprintf_ctrl(vt, C1_ST, "");

if(outbuffer_is_full(vt))
vt->outbuffer_cur = orig_cur;
}

size_t vterm_output_get_buffer_size(const VTerm *vt)
{
return vt->outbuffer_len;
}

size_t vterm_output_get_buffer_current(const VTerm *vt)
{
return vt->outbuffer_cur;
}

size_t vterm_output_get_buffer_remaining(const VTerm *vt)
{
return vt->outbuffer_len - vt->outbuffer_cur;
}

size_t vterm_output_read(VTerm *vt, char *buffer, size_t len)
{
if(len > vt->outbuffer_cur)
len = vt->outbuffer_cur;

memcpy(buffer, vt->outbuffer, len);

if(len < vt->outbuffer_cur)
memmove(vt->outbuffer, vt->outbuffer + len, vt->outbuffer_cur - len);

vt->outbuffer_cur -= len;

return len;
}

void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user)
{
vt->parser_callbacks = callbacks;
vt->cbdata = user;
}

void *vterm_parser_get_cbdata(VTerm *vt)
{
return vt->cbdata;
}

VTermValueType vterm_get_attr_type(VTermAttr attr)
{
switch(attr) {
case VTERM_ATTR_BOLD: return VTERM_VALUETYPE_BOOL;
case VTERM_ATTR_UNDERLINE: return VTERM_VALUETYPE_INT;
case VTERM_ATTR_ITALIC: return VTERM_VALUETYPE_BOOL;
case VTERM_ATTR_BLINK: return VTERM_VALUETYPE_BOOL;
case VTERM_ATTR_REVERSE: return VTERM_VALUETYPE_BOOL;
case VTERM_ATTR_STRIKE: return VTERM_VALUETYPE_BOOL;
case VTERM_ATTR_FONT: return VTERM_VALUETYPE_INT;
case VTERM_ATTR_FOREGROUND: return VTERM_VALUETYPE_COLOR;
case VTERM_ATTR_BACKGROUND: return VTERM_VALUETYPE_COLOR;
}
return 0; /* UNREACHABLE */
}

VTermValueType vterm_get_prop_type(VTermProp prop)
{
switch(prop) {
case VTERM_PROP_CURSORVISIBLE: return VTERM_VALUETYPE_BOOL;
case VTERM_PROP_CURSORBLINK: return VTERM_VALUETYPE_BOOL;
case VTERM_PROP_ALTSCREEN: return VTERM_VALUETYPE_BOOL;
case VTERM_PROP_TITLE: return VTERM_VALUETYPE_STRING;
case VTERM_PROP_ICONNAME: return VTERM_VALUETYPE_STRING;
case VTERM_PROP_REVERSE: return VTERM_VALUETYPE_BOOL;
case VTERM_PROP_CURSORSHAPE: return VTERM_VALUETYPE_INT;
case VTERM_PROP_MOUSE: return VTERM_VALUETYPE_INT;
}
return 0; /* UNREACHABLE */
}

void vterm_scroll_rect(VTermRect rect,
int downward,
int rightward,
int (*moverect)(VTermRect src, VTermRect dest, void *user),
int (*eraserect)(VTermRect rect, int selective, void *user),
void *user)
{
VTermRect src;
VTermRect dest;

if(abs(downward) >= rect.end_row - rect.start_row ||
abs(rightward) >= rect.end_col - rect.start_col) {
/* Scroll more than area; just erase the lot */
(*eraserect)(rect, 0, user);
return;
}

if(rightward >= 0) {
/* rect: [XXX................]
* src: [----------------]
* dest: [----------------]
*/
dest.start_col = rect.start_col;
dest.end_col = rect.end_col - rightward;
src.start_col = rect.start_col + rightward;
src.end_col = rect.end_col;
}
else {
/* rect: [................XXX]
* src: [----------------]
* dest: [----------------]
*/
int leftward = -rightward;
dest.start_col = rect.start_col + leftward;
dest.end_col = rect.end_col;
src.start_col = rect.start_col;
src.end_col = rect.end_col - leftward;
}

if(downward >= 0) {
dest.start_row = rect.start_row;
dest.end_row = rect.end_row - downward;
src.start_row = rect.start_row + downward;
src.end_row = rect.end_row;
}
else {
int upward = -downward;
dest.start_row = rect.start_row + upward;
dest.end_row = rect.end_row;
src.start_row = rect.start_row;
src.end_row = rect.end_row - upward;
}

if(moverect)
(*moverect)(dest, src, user);

if(downward > 0)
rect.start_row = rect.end_row - downward;
else if(downward < 0)
rect.end_row = rect.start_row - downward;

if(rightward > 0)
rect.start_col = rect.end_col - rightward;
else if(rightward < 0)
rect.end_col = rect.start_col - rightward;

(*eraserect)(rect, 0, user);
}

void vterm_copy_cells(VTermRect dest,
VTermRect src,
void (*copycell)(VTermPos dest, VTermPos src, void *user),
void *user)
{
int downward = src.start_row - dest.start_row;
int rightward = src.start_col - dest.start_col;

int init_row, test_row, init_col, test_col;
int inc_row, inc_col;

VTermPos pos;

if(downward < 0) {
init_row = dest.end_row - 1;
test_row = dest.start_row - 1;
inc_row = -1;
}
else /* downward >= 0 */ {
init_row = dest.start_row;
test_row = dest.end_row;
inc_row = +1;
}

if(rightward < 0) {
init_col = dest.end_col - 1;
test_col = dest.start_col - 1;
inc_col = -1;
}
else /* rightward >= 0 */ {
init_col = dest.start_col;
test_col = dest.end_col;
inc_col = +1;
}

for(pos.row = init_row; pos.row != test_row; pos.row += inc_row)
for(pos.col = init_col; pos.col != test_col; pos.col += inc_col) {
VTermPos srcpos;
srcpos.row = pos.row + downward;
srcpos.col = pos.col + rightward;
(*copycell)(pos, srcpos, user);
}
}
@@ -0,0 +1,237 @@
#ifndef __VTERM_INTERNAL_H__
#define __VTERM_INTERNAL_H__

#include "vterm.h"

#include <stdarg.h>

#if defined(__GNUC__)
# define INTERNAL __attribute__((visibility("internal")))
# define UNUSED __attribute__((unused))
#else
# define INTERNAL
# define UNUSED
#endif

#ifdef DEBUG
# define DEBUG_LOG(s) fprintf(stderr, s)
# define DEBUG_LOG1(s, a) fprintf(stderr, s, a)
# define DEBUG_LOG2(s, a, b) fprintf(stderr, s, a, b)
# define DEBUG_LOG3(s, a, b, c) fprintf(stderr, s, a, b, c)
#else
# define DEBUG_LOG(s)
# define DEBUG_LOG1(s, a)
# define DEBUG_LOG2(s, a, b)
# define DEBUG_LOG3(s, a, b, c)
#endif

#define ESC_S "\x1b"

typedef struct VTermEncoding VTermEncoding;

typedef struct {
VTermEncoding *enc;

/* This size should be increased if required by other stateful encodings */
char data[4*sizeof(uint32_t)];
} VTermEncodingInstance;

struct VTermPen
{
VTermColor fg;
VTermColor bg;
unsigned int bold:1;
unsigned int underline:2;
unsigned int italic:1;
unsigned int blink:1;
unsigned int reverse:1;
unsigned int strike:1;
unsigned int font:4; /* To store 0-9 */
};

int vterm_color_equal(VTermColor a, VTermColor b);

#if defined(DEFINE_INLINES) || USE_INLINE
INLINE int vterm_color_equal(VTermColor a, VTermColor b)
{
return a.red == b.red && a.green == b.green && a.blue == b.blue;
}
#endif

struct VTermState
{
VTerm *vt;

const VTermStateCallbacks *callbacks;
void *cbdata;

const VTermParserCallbacks *fallbacks;
void *fbdata;

int rows;
int cols;

/* Current cursor position */
VTermPos pos;

int at_phantom; /* True if we're on the "81st" phantom column to defer a wraparound */

int scrollregion_top;
int scrollregion_bottom; /* -1 means unbounded */
#define SCROLLREGION_BOTTOM(state) ((state)->scrollregion_bottom > -1 ? (state)->scrollregion_bottom : (state)->rows)
int scrollregion_left;
#define SCROLLREGION_LEFT(state) ((state)->mode.leftrightmargin ? (state)->scrollregion_left : 0)
int scrollregion_right; /* -1 means unbounded */
#define SCROLLREGION_RIGHT(state) ((state)->mode.leftrightmargin && (state)->scrollregion_right > -1 ? (state)->scrollregion_right : (state)->cols)

/* Bitvector of tab stops */
unsigned char *tabstops;

VTermLineInfo *lineinfo;
#define ROWWIDTH(state,row) ((state)->lineinfo[(row)].doublewidth ? ((state)->cols / 2) : (state)->cols)
#define THISROWWIDTH(state) ROWWIDTH(state, (state)->pos.row)

/* Mouse state */
int mouse_col, mouse_row;
int mouse_buttons;
int mouse_flags;
#define MOUSE_WANT_CLICK 0x01
#define MOUSE_WANT_DRAG 0x02
#define MOUSE_WANT_MOVE 0x04

enum { MOUSE_X10, MOUSE_UTF8, MOUSE_SGR, MOUSE_RXVT } mouse_protocol;

/* Last glyph output, for Unicode recombining purposes */
uint32_t *combine_chars;
size_t combine_chars_size; /* Number of ELEMENTS in the above */
int combine_width; /* The width of the glyph above */
VTermPos combine_pos; /* Position before movement */

struct {
unsigned int keypad:1;
unsigned int cursor:1;
unsigned int autowrap:1;
unsigned int insert:1;
unsigned int newline:1;
unsigned int cursor_visible:1;
unsigned int cursor_blink:1;
unsigned int cursor_shape:2;
unsigned int alt_screen:1;
unsigned int origin:1;
unsigned int screen:1;
unsigned int leftrightmargin:1;
unsigned int bracketpaste:1;
} mode;

VTermEncodingInstance encoding[4], encoding_utf8;
int gl_set, gr_set, gsingle_set;

struct VTermPen pen;

VTermColor default_fg;
VTermColor default_bg;
VTermColor colors[16]; /* Store the 8 ANSI and the 8 ANSI high-brights only */

int fg_index;
int bg_index;
int bold_is_highbright;

unsigned int protected_cell : 1;

/* Saved state under DEC mode 1048/1049 */
struct {
VTermPos pos;
struct VTermPen pen;

struct {
int cursor_visible:1;
int cursor_blink:1;
unsigned int cursor_shape:2;
} mode;
} saved;
};

struct VTerm
{
VTermAllocatorFunctions *allocator;
void *allocdata;

int rows;
int cols;

struct {
unsigned int utf8:1;
unsigned int ctrl8bit:1;
} mode;

enum VTermParserState {
NORMAL,
CSI,
OSC,
DCS,
ESC,
ESC_IN_OSC,
ESC_IN_DCS
} parser_state;
const VTermParserCallbacks *parser_callbacks;
void *cbdata;

/* len == malloc()ed size; cur == number of valid bytes */
char *strbuffer;
size_t strbuffer_len;
size_t strbuffer_cur;

char *outbuffer;
size_t outbuffer_len;
size_t outbuffer_cur;

VTermState *state;
VTermScreen *screen;
};

struct VTermEncoding {
void (*init) (VTermEncoding *enc, void *data);
void (*decode)(VTermEncoding *enc, void *data,
uint32_t cp[], int *cpi, int cplen,
const char bytes[], size_t *pos, size_t len);
};

typedef enum {
ENC_UTF8,
ENC_SINGLE_94
} VTermEncodingType;

void *vterm_allocator_malloc(VTerm *vt, size_t size);
void vterm_allocator_free(VTerm *vt, void *ptr);

void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len);
void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args);
void vterm_push_output_sprintf(VTerm *vt, const char *format, ...);
void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt, ...);
void vterm_push_output_sprintf_dcs(VTerm *vt, const char *fmt, ...);

void vterm_state_free(VTermState *state);

void vterm_state_newpen(VTermState *state);
void vterm_state_resetpen(VTermState *state);
void vterm_state_setpen(VTermState *state, const long args[], int argcount);
int vterm_state_getpen(VTermState *state, long args[], int argcount);
void vterm_state_savepen(VTermState *state, int save);

enum {
C1_SS3 = 0x8f,
C1_DCS = 0x90,
C1_CSI = 0x9b,
C1_ST = 0x9c
};

void vterm_state_push_output_sprintf_CSI(VTermState *vts, const char *format, ...);

void vterm_screen_free(VTermScreen *screen);

VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation);

int vterm_unicode_width(uint32_t codepoint);
int vterm_unicode_is_combining(uint32_t codepoint);

#endif
@@ -0,0 +1,200 @@
INIT
UTF8 0
WANTPARSER

!Basic text
PUSH "hello"
text 0x68, 0x65, 0x6c, 0x6c, 0x6f

!C0
PUSH "\x03"
control 3

PUSH "\x1f"
control 0x1f

!C1 8bit
PUSH "\x83"
control 0x83

PUSH "\x9f"
control 0x9f

!C1 7bit
PUSH "\e\x43"
control 0x83

PUSH "\e\x5f"
control 0x9f

!High bytes
PUSH "\xa0\xcc\xfe"
text 0xa0, 0xcc, 0xfe

!Mixed
PUSH "1\n2"
text 0x31
control 10
text 0x32

!Escape
PUSH "\e="
escape "="

!Escape 2-byte
PUSH "\e(X"
escape "(X"

!Split write Escape
PUSH "\e("
PUSH "Y"
escape "(Y"

!Escape cancels Escape, starts another
PUSH "\e(\e)Z"
escape ")Z"

!CAN cancels Escape, returns to normal mode
PUSH "\e(\x{18}AB"
text 0x41, 0x42

!C0 in Escape interrupts and continues
PUSH "\e(\nX"
control 10
escape "(X"

!CSI 0 args
PUSH "\e[a"
csi 0x61 *

!CSI 1 arg
PUSH "\e[9b"
csi 0x62 9

!CSI 2 args
PUSH "\e[3;4c"
csi 0x63 3,4

!CSI 1 arg 1 sub
PUSH "\e[1:2c"
csi 0x63 1+,2

!CSI many digits
PUSH "\e[678d"
csi 0x64 678

!CSI leading zero
PUSH "\e[007e"
csi 0x65 7

!CSI qmark
PUSH "\e[?2;7f"
csi 0x66 L=3f 2,7

!CSI greater
PUSH "\e[>c"
csi 0x63 L=3e *

!CSI SP
PUSH "\e[12 q"
csi 0x71 12 I=20

!Mixed CSI
PUSH "A\e[8mB"
text 0x41
csi 0x6d 8
text 0x42

!Split write
PUSH "\e"
PUSH "[a"
csi 0x61 *
PUSH "foo\e["
text 0x66, 0x6f, 0x6f
PUSH "4b"
csi 0x62 4
PUSH "\e[12;"
PUSH "3c"
csi 0x63 12,3

!Escape cancels CSI, starts Escape
PUSH "\e[123\e9"
escape "9"

!CAN cancels CSI, returns to normal mode
PUSH "\e[12\x{18}AB"
text 0x41, 0x42

!C0 in Escape interrupts and continues
PUSH "\e[12\n;3X"
control 10
csi 0x58 12,3

!OSC BEL
PUSH "\e]1;Hello\x07"
osc "1;Hello"

!OSC ST (7bit)
PUSH "\e]1;Hello\e\\"
osc "1;Hello"

!OSC ST (8bit)
PUSH "\x{9d}1;Hello\x9c"
osc "1;Hello"

!Escape cancels OSC, starts Escape
PUSH "\e]Something\e9"
escape "9"

!CAN cancels OSC, returns to normal mode
PUSH "\e]12\x{18}AB"
text 0x41, 0x42

!C0 in OSC interrupts and continues
PUSH "\e]2;\nBye\x07"
control 10
osc "2;Bye"

!DCS BEL
PUSH "\ePHello\x07"
dcs "Hello"

!DCS ST (7bit)
PUSH "\ePHello\e\\"
dcs "Hello"

!DCS ST (8bit)
PUSH "\x{90}Hello\x9c"
dcs "Hello"

!Escape cancels DCS, starts Escape
PUSH "\ePSomething\e9"
escape "9"

!CAN cancels DCS, returns to normal mode
PUSH "\eP12\x{18}AB"
text 0x41, 0x42

!C0 in OSC interrupts and continues
PUSH "\ePBy\ne\x07"
control 10
dcs "Bye"

!NUL ignored
PUSH "\x{00}"

!NUL ignored within CSI
PUSH "\e[12\x{00}3m"
csi 0x6d 123

!DEL ignored
PUSH "\x{7f}"

!DEL ignored within CSI
PUSH "\e[12\x{7f}3m"
csi 0x6d 123

!DEL inside text"
PUSH "AB\x{7f}C"
text 0x41,0x42
text 0x43
@@ -0,0 +1,122 @@
INIT
WANTENCODING

!Low
ENCIN "123"
encout 0x31,0x32,0x33

# We want to prove the UTF-8 parser correctly handles all the sequences.
# Easy way to do this is to check it does low/high boundary cases, as that
# leaves only two for each sequence length
#
# These ranges are therefore:
#
# Two bytes:
# U+0080 = 000 10000000 => 00010 000000
# => 11000010 10000000 = C2 80
# U+07FF = 111 11111111 => 11111 111111
# => 11011111 10111111 = DF BF
#
# Three bytes:
# U+0800 = 00001000 00000000 => 0000 100000 000000
# => 11100000 10100000 10000000 = E0 A0 80
# U+FFFD = 11111111 11111101 => 1111 111111 111101
# => 11101111 10111111 10111101 = EF BF BD
# (We avoid U+FFFE and U+FFFF as they're invalid codepoints)
#
# Four bytes:
# U+10000 = 00001 00000000 00000000 => 000 010000 000000 000000
# => 11110000 10010000 10000000 10000000 = F0 90 80 80
# U+1FFFFF = 11111 11111111 11111111 => 111 111111 111111 111111
# => 11110111 10111111 10111111 10111111 = F7 BF BF BF

!2 byte
ENCIN "\xC2\x80\xDF\xBF"
encout 0x0080, 0x07FF

!3 byte
ENCIN "\xE0\xA0\x80\xEF\xBF\xBD"
encout 0x0800,0xFFFD

!4 byte
ENCIN "\xF0\x90\x80\x80\xF7\xBF\xBF\xBF"
encout 0x10000,0x1fffff

# Next up, we check some invalid sequences
# + Early termination (back to low bytes too soon)
# + Early restart (another sequence introduction before the previous one was finished)

!Early termination
ENCIN "\xC2!"
encout 0xfffd,0x21

ENCIN "\xE0!\xE0\xA0!"
encout 0xfffd,0x21,0xfffd,0x21

ENCIN "\xF0!\xF0\x90!\xF0\x90\x80!"
encout 0xfffd,0x21,0xfffd,0x21,0xfffd,0x21

!Early restart
ENCIN "\xC2\xC2\x90"
encout 0xfffd,0x0090

ENCIN "\xE0\xC2\x90\xE0\xA0\xC2\x90"
encout 0xfffd,0x0090,0xfffd,0x0090

ENCIN "\xF0\xC2\x90\xF0\x90\xC2\x90\xF0\x90\x80\xC2\x90"
encout 0xfffd,0x0090,0xfffd,0x0090,0xfffd,0x0090

# Test the overlong sequences by giving an overlong encoding of U+0000 and
# an encoding of the highest codepoint still too short
#
# Two bytes:
# U+0000 = C0 80
# U+007F = 000 01111111 => 00001 111111 =>
# => 11000001 10111111 => C1 BF
#
# Three bytes:
# U+0000 = E0 80 80
# U+07FF = 00000111 11111111 => 0000 011111 111111
# => 11100000 10011111 10111111 = E0 9F BF
#
# Four bytes:
# U+0000 = F0 80 80 80
# U+FFFF = 11111111 11111111 => 000 001111 111111 111111
# => 11110000 10001111 10111111 10111111 = F0 8F BF BF

!Overlong
ENCIN "\xC0\x80\xC1\xBF"
encout 0xfffd,0xfffd

ENCIN "\xE0\x80\x80\xE0\x9F\xBF"
encout 0xfffd,0xfffd

ENCIN "\xF0\x80\x80\x80\xF0\x8F\xBF\xBF"
encout 0xfffd,0xfffd

# UTF-16 surrogates U+D800 and U+DFFF
!UTF-16 Surrogates
ENCIN "\xED\xA0\x80\xED\xBF\xBF"
encout 0xfffd,0xfffd

!Split write
ENCIN "\xC2"
ENCIN "\xA0"
encout 0x000A0

ENCIN "\xE0"
ENCIN "\xA0\x80"
encout 0x00800
ENCIN "\xE0\xA0"
ENCIN "\x80"
encout 0x00800

ENCIN "\xF0"
ENCIN "\x90\x80\x80"
encout 0x10000
ENCIN "\xF0\x90"
ENCIN "\x80\x80"
encout 0x10000
ENCIN "\xF0\x90\x80"
ENCIN "\x80"
encout 0x10000
@@ -0,0 +1,55 @@
INIT
UTF8 1
WANTSTATE g

!Low
RESET
PUSH "ABC"
putglyph 0x41 1 0,0
putglyph 0x42 1 0,1
putglyph 0x43 1 0,2

!UTF-8 1 char
# U+00C1 = 0xC3 0x81 name: LATIN CAPITAL LETTER A WITH ACUTE
# U+00E9 = 0xC3 0xA9 name: LATIN SMALL LETTER E WITH ACUTE
RESET
PUSH "\xC3\x81\xC3\xA9"
putglyph 0xc1 1 0,0
putglyph 0xe9 1 0,1

!UTF-8 wide char
# U+FF10 = 0xEF 0xBC 0x90 name: FULLWIDTH DIGIT ZERO
RESET
PUSH "\xEF\xBC\x90 "
putglyph 0xff10 2 0,0
putglyph 0x20 1 0,2

!UTF-8 combining chars
# U+0301 = 0xCC 0x81 name: COMBINING ACUTE
RESET
PUSH "e\xCC\x81Z"
putglyph 0x65,0x301 1 0,0
putglyph 0x5a 1 0,1

!Combining across buffers
RESET
PUSH "e"
putglyph 0x65 1 0,0
PUSH "\xCC\x81Z"
putglyph 0x65,0x301 1 0,0
putglyph 0x5a 1 0,1

RESET
PUSH "e"
putglyph 0x65 1 0,0
PUSH "\xCC\x81"
putglyph 0x65,0x301 1 0,0
PUSH "\xCC\x82"
putglyph 0x65,0x301,0x302 1 0,0

!DECSCA protected
RESET
PUSH "A\e[1\"qB\e[2\"qC"
putglyph 0x41 1 0,0
putglyph 0x42 1 0,1 prot
putglyph 0x43 1 0,2
@@ -0,0 +1,224 @@
INIT
UTF8 1
WANTSTATE

!Implicit
PUSH "ABC"
?cursor = 0,3
!Backspace
PUSH "\b"
?cursor = 0,2
!Horizontal Tab
PUSH "\t"
?cursor = 0,8
!Carriage Return
PUSH "\r"
?cursor = 0,0
!Linefeed
PUSH "\n"
?cursor = 1,0

!Backspace bounded by lefthand edge
PUSH "\e[4;2H"
?cursor = 3,1
PUSH "\b"
?cursor = 3,0
PUSH "\b"
?cursor = 3,0

!Backspace cancels phantom
PUSH "\e[4;80H"
?cursor = 3,79
PUSH "X"
?cursor = 3,79
PUSH "\b"
?cursor = 3,78

!HT bounded by righthand edge
PUSH "\e[1;78H"
?cursor = 0,77
PUSH "\t"
?cursor = 0,79
PUSH "\t"
?cursor = 0,79

RESET

!Index
PUSH "ABC\eD"
?cursor = 1,3
!Reverse Index
PUSH "\eM"
?cursor = 0,3
!Newline
PUSH "\eE"
?cursor = 1,0

RESET

!Cursor Forward
PUSH "\e[B"
?cursor = 1,0
PUSH "\e[3B"
?cursor = 4,0
PUSH "\e[0B"
?cursor = 5,0

!Cursor Down
PUSH "\e[C"
?cursor = 5,1
PUSH "\e[3C"
?cursor = 5,4
PUSH "\e[0C"
?cursor = 5,5

!Cursor Up
PUSH "\e[A"
?cursor = 4,5
PUSH "\e[3A"
?cursor = 1,5
PUSH "\e[0A"
?cursor = 0,5

!Cursor Backward
PUSH "\e[D"
?cursor = 0,4
PUSH "\e[3D"
?cursor = 0,1
PUSH "\e[0D"
?cursor = 0,0

!Cursor Next Line
PUSH " "
?cursor = 0,3
PUSH "\e[E"
?cursor = 1,0
PUSH " "
?cursor = 1,3
PUSH "\e[2E"
?cursor = 3,0
PUSH "\e[0E"
?cursor = 4,0

!Cursor Previous Line
PUSH " "
?cursor = 4,3
PUSH "\e[F"
?cursor = 3,0
PUSH " "
?cursor = 3,3
PUSH "\e[2F"
?cursor = 1,0
PUSH "\e[0F"
?cursor = 0,0

!Cursor Horizonal Absolute
PUSH "\n"
?cursor = 1,0
PUSH "\e[20G"
?cursor = 1,19
PUSH "\e[G"
?cursor = 1,0

!Cursor Position
PUSH "\e[10;5H"
?cursor = 9,4
PUSH "\e[8H"
?cursor = 7,0
PUSH "\e[H"
?cursor = 0,0

!Cursor Position cancels phantom
PUSH "\e[10;78H"
?cursor = 9,77
PUSH "ABC"
?cursor = 9,79
PUSH "\e[10;80H"
PUSH "C"
?cursor = 9,79
PUSH "X"
?cursor = 10,1

RESET

!Bounds Checking
PUSH "\e[A"
?cursor = 0,0
PUSH "\e[D"
?cursor = 0,0
PUSH "\e[25;80H"
?cursor = 24,79
PUSH "\e[B"
?cursor = 24,79
PUSH "\e[C"
?cursor = 24,79
PUSH "\e[E"
?cursor = 24,0
PUSH "\e[H"
?cursor = 0,0
PUSH "\e[F"
?cursor = 0,0
PUSH "\e[999G"
?cursor = 0,79
PUSH "\e[99;99H"
?cursor = 24,79

RESET

!Horizontal Position Absolute
PUSH "\e[5`"
?cursor = 0,4

!Horizontal Position Relative
PUSH "\e[3a"
?cursor = 0,7

!Horizontal Position Backward
PUSH "\e[3j"
?cursor = 0,4

!Horizontal and Vertical Position
PUSH "\e[3;3f"
?cursor = 2,2

!Vertical Position Absolute
PUSH "\e[5d"
?cursor = 4,2

!Vertical Position Relative
PUSH "\e[2e"
?cursor = 6,2

!Vertical Position Backward
PUSH "\e[2k"
?cursor = 4,2

RESET

!Horizontal Tab
PUSH "\t"
?cursor = 0,8
PUSH " "
?cursor = 0,11
PUSH "\t"
?cursor = 0,16
PUSH " "
?cursor = 0,23
PUSH "\t"
?cursor = 0,24
PUSH " "
?cursor = 0,32
PUSH "\t"
?cursor = 0,40

!Cursor Horizontal Tab
PUSH "\e[I"
?cursor = 0,48
PUSH "\e[2I"
?cursor = 0,64

!Cursor Backward Tab
PUSH "\e[Z"
?cursor = 0,56
PUSH "\e[2Z"
?cursor = 0,40
@@ -0,0 +1,150 @@
INIT
UTF8 1
WANTSTATE s

!Linefeed
PUSH "\n"x24
?cursor = 24,0
PUSH "\n"
scrollrect 0..25,0..80 => +1,+0
?cursor = 24,0

RESET

!Index
PUSH "\e[25H"
PUSH "\eD"
scrollrect 0..25,0..80 => +1,+0

RESET

!Reverse Index
PUSH "\eM"
scrollrect 0..25,0..80 => -1,+0

RESET

!Linefeed in DECSTBM
PUSH "\e[1;10r"
?cursor = 0,0
PUSH "\n"x9
?cursor = 9,0
PUSH "\n"
scrollrect 0..10,0..80 => +1,+0
?cursor = 9,0

!Linefeed outside DECSTBM
PUSH "\e[20H"
?cursor = 19,0
PUSH "\n"
?cursor = 20,0

!Index in DECSTBM
PUSH "\e[10H"
PUSH "\e[9;10r"
PUSH "\eM"
?cursor = 8,0
PUSH "\eM"
scrollrect 8..10,0..80 => -1,+0

!Reverse Index in DECSTBM
PUSH "\e[25H"
?cursor = 24,0
PUSH "\n"
# no scrollrect
?cursor = 24,0

!Linefeed in DECSTBM+DECSLRM
PUSH "\e[?69h"
PUSH "\e[3;10r\e[10;40s"
PUSH "\e[10;10H\n"
scrollrect 2..10,9..40 => +1,+0

!IND/RI in DECSTBM+DECSLRM
PUSH "\eD"
scrollrect 2..10,9..40 => +1,+0
PUSH "\e[3;10H\eM"
scrollrect 2..10,9..40 => -1,+0

!DECRQSS on DECSTBM
PUSH "\eP\$qr\e\\"
output "\eP1\$r3;10r\e\\"

!DECRQSS on DECSLRM
PUSH "\eP\$qs\e\\"
output "\eP1\$r10;40s\e\\"

!Setting invalid DECSLRM with !DECVSSM is still rejected
PUSH "\e[?69l\e[;0s\e[?69h"

RESET

!Scroll Down
PUSH "\e[S"
scrollrect 0..25,0..80 => +1,+0
?cursor = 0,0
PUSH "\e[2S"
scrollrect 0..25,0..80 => +2,+0
?cursor = 0,0
PUSH "\e[100S"
scrollrect 0..25,0..80 => +25,+0

!Scroll Up
PUSH "\e[T"
scrollrect 0..25,0..80 => -1,+0
?cursor = 0,0
PUSH "\e[2T"
scrollrect 0..25,0..80 => -2,+0
?cursor = 0,0
PUSH "\e[100T"
scrollrect 0..25,0..80 => -25,+0

!SD/SU in DECSTBM
PUSH "\e[5;20r"
PUSH "\e[S"
scrollrect 4..20,0..80 => +1,+0
PUSH "\e[T"
scrollrect 4..20,0..80 => -1,+0

RESET

!SD/SU in DECSTBM+DECSLRM
PUSH "\e[?69h"
PUSH "\e[3;10r\e[10;40s"
?cursor = 0,0
PUSH "\e[3;10H"
?cursor = 2,9
PUSH "\e[S"
scrollrect 2..10,9..40 => +1,+0
PUSH "\e[?69l"
PUSH "\e[S"
scrollrect 2..10,0..80 => +1,+0

!Invalid boundaries
RESET

PUSH "\e[100;105r\eD"
PUSH "\e[5;2r\eD"

RESET
WANTSTATE -s+me

!Scroll Down move+erase emulation
PUSH "\e[S"
moverect 1..25,0..80 -> 0..24,0..80
erase 24..25,0..80
?cursor = 0,0
PUSH "\e[2S"
moverect 2..25,0..80 -> 0..23,0..80
erase 23..25,0..80
?cursor = 0,0

!Scroll Up move+erase emulation
PUSH "\e[T"
moverect 0..24,0..80 -> 1..25,0..80
erase 0..1,0..80
?cursor = 0,0
PUSH "\e[2T"
moverect 0..23,0..80 -> 2..25,0..80
erase 0..2,0..80
?cursor = 0,0
@@ -0,0 +1,300 @@
INIT
UTF8 1
WANTSTATE se

!ICH
RESET
erase 0..25,0..80
?cursor = 0,0
PUSH "ACD"
PUSH "\e[2D"
?cursor = 0,1
PUSH "\e[@"
scrollrect 0..1,1..80 => +0,-1
?cursor = 0,1
PUSH "B"
?cursor = 0,2
PUSH "\e[3@"
scrollrect 0..1,2..80 => +0,-3

!ICH with DECSLRM
PUSH "\e[?69h"
PUSH "\e[;50s"
PUSH "\e[20G\e[@"
scrollrect 0..1,19..50 => +0,-1

!ICH outside DECSLRM
PUSH "\e[70G\e[@"
# nothing happens

!DCH
RESET
erase 0..25,0..80
?cursor = 0,0
PUSH "ABBC"
PUSH "\e[3D"
?cursor = 0,1
PUSH "\e[P"
scrollrect 0..1,1..80 => +0,+1
?cursor = 0,1
PUSH "\e[3P"
scrollrect 0..1,1..80 => +0,+3
?cursor = 0,1

!DCH with DECSLRM
PUSH "\e[?69h"
PUSH "\e[;50s"
PUSH "\e[20G\e[P"
scrollrect 0..1,19..50 => +0,+1

!DCH outside DECSLRM
PUSH "\e[70G\e[P"
# nothing happens

!ECH
RESET
erase 0..25,0..80
?cursor = 0,0
PUSH "ABC"
PUSH "\e[2D"
?cursor = 0,1
PUSH "\e[X"
erase 0..1,1..2
?cursor = 0,1
PUSH "\e[3X"
erase 0..1,1..4
?cursor = 0,1
# ECH more columns than there are should be bounded
PUSH "\e[100X"
erase 0..1,1..80

!IL
RESET
erase 0..25,0..80
?cursor = 0,0
PUSH "A\r\nC"
?cursor = 1,1
PUSH "\e[L"
scrollrect 1..25,0..80 => -1,+0
# TODO: ECMA-48 says we should move to line home, but neither xterm nor
# xfce4-terminal do this
?cursor = 1,1
PUSH "\rB"
?cursor = 1,1
PUSH "\e[3L"
scrollrect 1..25,0..80 => -3,+0

!IL with DECSTBM
PUSH "\e[5;15r"
PUSH "\e[5H\e[L"
scrollrect 4..15,0..80 => -1,+0

!IL outside DECSTBM
PUSH "\e[20H\e[L"
# nothing happens

!IL with DECSTBM+DECSLRM
PUSH "\e[?69h"
PUSH "\e[10;50s"
PUSH "\e[5;10H\e[L"
scrollrect 4..15,9..50 => -1,+0

!DL
RESET
erase 0..25,0..80
?cursor = 0,0
PUSH "A\r\nB\r\nB\r\nC"
?cursor = 3,1
PUSH "\e[2H"
?cursor = 1,0
PUSH "\e[M"
scrollrect 1..25,0..80 => +1,+0
?cursor = 1,0
PUSH "\e[3M"
scrollrect 1..25,0..80 => +3,+0
?cursor = 1,0

!DL with DECSTBM
PUSH "\e[5;15r"
PUSH "\e[5H\e[M"
scrollrect 4..15,0..80 => +1,+0

!DL outside DECSTBM
PUSH "\e[20H\e[M"
# nothing happens

!DL with DECSTBM+DECSLRM
PUSH "\e[?69h"
PUSH "\e[10;50s"
PUSH "\e[5;10H\e[M"
scrollrect 4..15,9..50 => +1,+0

!DECIC
RESET
erase 0..25,0..80
PUSH "\e[20G\e[5'}"
scrollrect 0..25,19..80 => +0,-5

!DECIC with DECSTBM+DECSLRM
PUSH "\e[?69h"
PUSH "\e[4;20r\e[20;60s"
PUSH "\e[4;20H\e[3'}"
scrollrect 3..20,19..60 => +0,-3

!DECIC outside DECSLRM
PUSH "\e[70G\e['}"
# nothing happens

!DECDC
RESET
erase 0..25,0..80
PUSH "\e[20G\e[5'~"
scrollrect 0..25,19..80 => +0,+5

!DECDC with DECSTBM+DECSLRM
PUSH "\e[?69h"
PUSH "\e[4;20r\e[20;60s"
PUSH "\e[4;20H\e[3'~"
scrollrect 3..20,19..60 => +0,+3

!DECDC outside DECSLRM
PUSH "\e[70G\e['~"
# nothing happens

!EL 0
RESET
erase 0..25,0..80
?cursor = 0,0
PUSH "ABCDE"
PUSH "\e[3D"
?cursor = 0,2
PUSH "\e[0K"
erase 0..1,2..80
?cursor = 0,2

!EL 1
RESET
erase 0..25,0..80
?cursor = 0,0
PUSH "ABCDE"
PUSH "\e[3D"
?cursor = 0,2
PUSH "\e[1K"
erase 0..1,0..3
?cursor = 0,2

!EL 2
RESET
erase 0..25,0..80
?cursor = 0,0
PUSH "ABCDE"
PUSH "\e[3D"
?cursor = 0,2
PUSH "\e[2K"
erase 0..1,0..80
?cursor = 0,2

!SEL
RESET
erase 0..25,0..80
?cursor = 0,0
PUSH "\e[11G"
?cursor = 0,10
PUSH "\e[?0K"
erase 0..1,10..80 selective
?cursor = 0,10
PUSH "\e[?1K"
erase 0..1,0..11 selective
?cursor = 0,10
PUSH "\e[?2K"
erase 0..1,0..80 selective
?cursor = 0,10

!ED 0
RESET
erase 0..25,0..80
?cursor = 0,0
PUSH "\e[2;2H"
?cursor = 1,1
PUSH "\e[0J"
erase 1..2,1..80
erase 2..25,0..80
?cursor = 1,1

!ED 1
RESET
erase 0..25,0..80
?cursor = 0,0
PUSH "\e[2;2H"
?cursor = 1,1
PUSH "\e[1J"
erase 0..1,0..80
erase 1..2,0..2
?cursor = 1,1

!ED 2
RESET
erase 0..25,0..80
?cursor = 0,0
PUSH "\e[2;2H"
?cursor = 1,1
PUSH "\e[2J"
erase 0..25,0..80
?cursor = 1,1

!SED
RESET
erase 0..25,0..80
PUSH "\e[5;5H"
?cursor = 4,4
PUSH "\e[?0J"
erase 4..5,4..80 selective
erase 5..25,0..80 selective
?cursor = 4,4
PUSH "\e[?1J"
erase 0..4,0..80 selective
erase 4..5,0..5 selective
?cursor = 4,4
PUSH "\e[?2J"
erase 0..25,0..80 selective
?cursor = 4,4

!DECRQSS on DECSCA
PUSH "\e[2\"q"
PUSH "\eP\$q\"q\e\\"
output "\eP1\$r2\"q\e\\"

WANTSTATE -s+m

!ICH move+erase emuation
RESET
erase 0..25,0..80
?cursor = 0,0
PUSH "ACD"
PUSH "\e[2D"
?cursor = 0,1
PUSH "\e[@"
moverect 0..1,1..79 -> 0..1,2..80
erase 0..1,1..2
?cursor = 0,1
PUSH "B"
?cursor = 0,2
PUSH "\e[3@"
moverect 0..1,2..77 -> 0..1,5..80
erase 0..1,2..5

!DCH move+erase emulation
RESET
erase 0..25,0..80
?cursor = 0,0
PUSH "ABBC"
PUSH "\e[3D"
?cursor = 0,1
PUSH "\e[P"
moverect 0..1,2..80 -> 0..1,1..79
erase 0..1,79..80
?cursor = 0,1
PUSH "\e[3P"
moverect 0..1,4..80 -> 0..1,1..77
erase 0..1,77..80
?cursor = 0,1
@@ -0,0 +1,105 @@
INIT
WANTSTATE g

!Default
RESET
PUSH "#"
putglyph 0x23 1 0,0

!Designate G0=UK
RESET
PUSH "\e(A"
PUSH "#"
putglyph 0x00a3 1 0,0

!Designate G0=DEC drawing
RESET
PUSH "\e(0"
PUSH "a"
putglyph 0x2592 1 0,0

!Designate G1 + LS1
RESET
PUSH "\e)0"
PUSH "a"
putglyph 0x61 1 0,0
PUSH "\x0e"
PUSH "a"
putglyph 0x2592 1 0,1
!LS0
PUSH "\x0f"
PUSH "a"
putglyph 0x61 1 0,2

!Designate G2 + LS2
PUSH "\e*0"
PUSH "a"
putglyph 0x61 1 0,3
PUSH "\en"
PUSH "a"
putglyph 0x2592 1 0,4
PUSH "\x0f"
PUSH "a"
putglyph 0x61 1 0,5

!Designate G3 + LS3
PUSH "\e+0"
PUSH "a"
putglyph 0x61 1 0,6
PUSH "\eo"
PUSH "a"
putglyph 0x2592 1 0,7
PUSH "\x0f"
PUSH "a"
putglyph 0x61 1 0,8

!SS2
PUSH "a\x{8e}aa"
putglyph 0x61 1 0,9
putglyph 0x2592 1 0,10
putglyph 0x61 1 0,11

!SS3
PUSH "a\x{8f}aa"
putglyph 0x61 1 0,12
putglyph 0x2592 1 0,13
putglyph 0x61 1 0,14

!LS1R
RESET
PUSH "\e~"
PUSH "\xe1"
putglyph 0x61 1 0,0
PUSH "\e)0"
PUSH "\xe1"
putglyph 0x2592 1 0,1

!LS2R
RESET
PUSH "\e}"
PUSH "\xe1"
putglyph 0x61 1 0,0
PUSH "\e*0"
PUSH "\xe1"
putglyph 0x2592 1 0,1

!LS3R
RESET
PUSH "\e|"
PUSH "\xe1"
putglyph 0x61 1 0,0
PUSH "\e+0"
PUSH "\xe1"
putglyph 0x2592 1 0,1

UTF8 1

!Mixed US-ASCII and UTF-8
# U+0108 == 0xc4 0x88
RESET
PUSH "\e(B"
PUSH "AB\xc4\x88D"
putglyph 0x0041 1 0,0
putglyph 0x0042 1 0,1
putglyph 0x0108 1 0,2
putglyph 0x0044 1 0,3
@@ -0,0 +1,86 @@
INIT
UTF8 1
WANTSTATE gme

!Insert/Replace Mode
RESET
erase 0..25,0..80
?cursor = 0,0
PUSH "AC\e[DB"
putglyph 0x41 1 0,0
putglyph 0x43 1 0,1
putglyph 0x42 1 0,1
PUSH "\e[4h"
PUSH "\e[G"
PUSH "AC\e[DB"
moverect 0..1,0..79 -> 0..1,1..80
erase 0..1,0..1
putglyph 0x41 1 0,0
moverect 0..1,1..79 -> 0..1,2..80
erase 0..1,1..2
putglyph 0x43 1 0,1
moverect 0..1,1..79 -> 0..1,2..80
erase 0..1,1..2
putglyph 0x42 1 0,1

!Insert mode only happens once for UTF-8 combining
PUSH "e"
moverect 0..1,2..79 -> 0..1,3..80
erase 0..1,2..3
putglyph 0x65 1 0,2
PUSH "\xCC\x81"
putglyph 0x65,0x301 1 0,2

!Newline/Linefeed mode
RESET
erase 0..25,0..80
?cursor = 0,0
PUSH "\e[5G\n"
?cursor = 1,4
PUSH "\e[20h"
PUSH "\e[5G\n"
?cursor = 2,0

!DEC origin mode
RESET
erase 0..25,0..80
?cursor = 0,0
PUSH "\e[5;15r"
PUSH "\e[H"
?cursor = 0,0
PUSH "\e[3;3H"
?cursor = 2,2
PUSH "\e[?6h"
PUSH "\e[H"
?cursor = 4,0
PUSH "\e[3;3H"
?cursor = 6,2

!DECRQM on DECOM
PUSH "\e[?6h"
PUSH "\e[?6\$p"
output "\e[?6;1\$y"
PUSH "\e[?6l"
PUSH "\e[?6\$p"
output "\e[?6;2\$y"

!Origin mode with DECSLRM
PUSH "\e[?6h"
PUSH "\e[?69h"
PUSH "\e[20;60s"
PUSH "\e[H"
?cursor = 4,19

PUSH "\e[?69l"

!Origin mode bounds cursor to scrolling region
PUSH "\e[H"
PUSH "\e[10A"
?cursor = 4,0
PUSH "\e[20B"
?cursor = 14,0

!Origin mode without scroll region
PUSH "\e[?6l"
PUSH "\e[r\e[?6h"
?cursor = 0,0
@@ -0,0 +1,48 @@
INIT
WANTSTATE g

!Placement
RESET
PUSH "AB\e[79GCDE"
putglyph 0x41 1 0,0
putglyph 0x42 1 0,1
putglyph 0x43 1 0,78
putglyph 0x44 1 0,79
putglyph 0x45 1 1,0

!Resize
RESET
RESIZE 27,85
PUSH "AB\e[79GCDE"
putglyph 0x41 1 0,0
putglyph 0x42 1 0,1
putglyph 0x43 1 0,78
putglyph 0x44 1 0,79
putglyph 0x45 1 0,80
?cursor = 0,81

!Resize without reset
RESIZE 28,90
?cursor = 0,81
PUSH "FGHI"
putglyph 0x46 1 0,81
putglyph 0x47 1 0,82
putglyph 0x48 1 0,83
putglyph 0x49 1 0,84
?cursor = 0,85

!Resize shrink moves cursor
RESIZE 25,80
?cursor = 0,79

!Resize grow doesn't cancel phantom
RESET
PUSH "\e[79GAB"
putglyph 0x41 1 0,78
putglyph 0x42 1 0,79
?cursor = 0,79
RESIZE 30,100
?cursor = 0,80
PUSH "C"
putglyph 0x43 1 0,80
?cursor = 0,81
@@ -0,0 +1,172 @@
INIT
WANTSTATE p

!DECRQM on with mouse off
PUSH "\e[?1000\$p"
output "\e[?1000;2\$y"
PUSH "\e[?1002\$p"
output "\e[?1002;2\$y"
PUSH "\e[?1003\$p"
output "\e[?1003;2\$y"

!Mouse in simple button report mode
RESET
settermprop 1 true
settermprop 2 true
settermprop 7 1
PUSH "\e[?1000h"
settermprop 8 1

!Press 1
MOUSEMOVE 0,0 0
MOUSEBTN d 1 0
output "\e[M\x20\x21\x21"

!Release 1
MOUSEBTN u 1 0
output "\e[M\x23\x21\x21"

!Ctrl-Press 1
MOUSEBTN d 1 C
output "\e[M\x30\x21\x21"
MOUSEBTN u 1 C
output "\e[M\x33\x21\x21"

!Button 2
MOUSEBTN d 2 0
output "\e[M\x21\x21\x21"
MOUSEBTN u 2 0
output "\e[M\x23\x21\x21"

!Position
MOUSEMOVE 10,20 0
MOUSEBTN d 1 0
output "\e[M\x20\x35\x2b"

MOUSEBTN u 1 0
output "\e[M\x23\x35\x2b"
MOUSEMOVE 10,21 0
# no output

!Wheel events
MOUSEBTN d 4 0
output "\e[M\x60\x36\x2b"
MOUSEBTN d 4 0
output "\e[M\x60\x36\x2b"
MOUSEBTN d 5 0
output "\e[M\x61\x36\x2b"

!DECRQM on mouse button mode
PUSH "\e[?1000\$p"
output "\e[?1000;1\$y"
PUSH "\e[?1002\$p"
output "\e[?1002;2\$y"
PUSH "\e[?1003\$p"
output "\e[?1003;2\$y"

!Drag events
RESET
settermprop 1 true
settermprop 2 true
settermprop 7 1
PUSH "\e[?1002h"
settermprop 8 2

MOUSEMOVE 5,5 0
MOUSEBTN d 1 0
output "\e[M\x20\x26\x26"
MOUSEMOVE 5,6 0
output "\e[M\x40\x27\x26"
MOUSEMOVE 6,6 0
output "\e[M\x40\x27\x27"
MOUSEMOVE 6,6 0
# no output
MOUSEBTN u 1 0
output "\e[M\x23\x27\x27"
MOUSEMOVE 6,7
# no output

!DECRQM on mouse drag mode
PUSH "\e[?1000\$p"
output "\e[?1000;2\$y"
PUSH "\e[?1002\$p"
output "\e[?1002;1\$y"
PUSH "\e[?1003\$p"
output "\e[?1003;2\$y"

!Non-drag motion events
PUSH "\e[?1003h"
settermprop 8 3

MOUSEMOVE 6,8 0
output "\e[M\x43\x29\x27"

!DECRQM on mouse motion mode
PUSH "\e[?1000\$p"
output "\e[?1000;2\$y"
PUSH "\e[?1002\$p"
output "\e[?1002;2\$y"
PUSH "\e[?1003\$p"
output "\e[?1003;1\$y"

!Bounds checking
MOUSEMOVE 300,300 0
output "\e[M\x43\xff\xff"
MOUSEBTN d 1 0
output "\e[M\x20\xff\xff"
MOUSEBTN u 1 0
output "\e[M\x23\xff\xff"

!DECRQM on standard encoding mode
PUSH "\e[?1005\$p"
output "\e[?1005;2\$y"
PUSH "\e[?1006\$p"
output "\e[?1006;2\$y"
PUSH "\e[?1015\$p"
output "\e[?1015;2\$y"

!UTF-8 extended encoding mode
# 300 + 32 + 1 = 333 = U+014d = \xc5\x8d
PUSH "\e[?1005h"
MOUSEBTN d 1 0
output "\e[M\x20\xc5\x8d\xc5\x8d"
MOUSEBTN u 1 0
output "\e[M\x23\xc5\x8d\xc5\x8d"

!DECRQM on UTF-8 extended encoding mode
PUSH "\e[?1005\$p"
output "\e[?1005;1\$y"
PUSH "\e[?1006\$p"
output "\e[?1006;2\$y"
PUSH "\e[?1015\$p"
output "\e[?1015;2\$y"

!SGR extended encoding mode
PUSH "\e[?1006h"
MOUSEBTN d 1 0
output "\e[<0;301;301M"
MOUSEBTN u 1 0
output "\e[<0;301;301m"

!DECRQM on SGR extended encoding mode
PUSH "\e[?1005\$p"
output "\e[?1005;2\$y"
PUSH "\e[?1006\$p"
output "\e[?1006;1\$y"
PUSH "\e[?1015\$p"
output "\e[?1015;2\$y"

!rxvt extended encoding mode
PUSH "\e[?1015h"
MOUSEBTN d 1 0
output "\e[0;301;301M"
MOUSEBTN u 1 0
output "\e[3;301;301M"

!DECRQM on rxvt extended encoding mode
PUSH "\e[?1005\$p"
output "\e[?1005;2\$y"
PUSH "\e[?1006\$p"
output "\e[?1006;2\$y"
PUSH "\e[?1015\$p"
output "\e[?1015;1\$y"
@@ -0,0 +1,36 @@
INIT
WANTSTATE p

RESET
settermprop 1 true
settermprop 2 true
settermprop 7 1

!Cursor visibility
PUSH "\e[?25h"
settermprop 1 true
PUSH "\e[?25\$p"
output "\e[?25;1\$y"
PUSH "\e[?25l"
settermprop 1 false
PUSH "\e[?25\$p"
output "\e[?25;2\$y"

!Cursor blink
PUSH "\e[?12h"
settermprop 2 true
PUSH "\e[?12\$p"
output "\e[?12;1\$y"
PUSH "\e[?12l"
settermprop 2 false
PUSH "\e[?12\$p"
output "\e[?12;2\$y"

!Cursor shape
PUSH "\e[3 q"
settermprop 2 true
settermprop 7 2

!Title
PUSH "\e]2;Here is my title\a"
settermprop 4 "Here is my title"
@@ -0,0 +1,69 @@
INIT
UTF8 1
WANTSTATE gm

!79th Column
PUSH "\e[75G"
PUSH "A"x5
putglyph 0x41 1 0,74
putglyph 0x41 1 0,75
putglyph 0x41 1 0,76
putglyph 0x41 1 0,77
putglyph 0x41 1 0,78
?cursor = 0,79

!80th Column Phantom
PUSH "A"
putglyph 0x41 1 0,79
?cursor = 0,79

!Line Wraparound
PUSH "B"
putglyph 0x42 1 1,0
?cursor = 1,1

!Line Wraparound during combined write
PUSH "\e[78G"
PUSH "BBBCC"
putglyph 0x42 1 1,77
putglyph 0x42 1 1,78
putglyph 0x42 1 1,79
putglyph 0x43 1 2,0
putglyph 0x43 1 2,1
?cursor = 2,2

!DEC Auto Wrap Mode
RESET
PUSH "\e[?7l"
PUSH "\e[75G"
PUSH "D"x6
putglyph 0x44 1 0,74
putglyph 0x44 1 0,75
putglyph 0x44 1 0,76
putglyph 0x44 1 0,77
putglyph 0x44 1 0,78
putglyph 0x44 1 0,79
?cursor = 0,79
PUSH "D"
putglyph 0x44 1 0,79
?cursor = 0,79
PUSH "\e[?7h"

!80th column causes linefeed on wraparound
PUSH "\e[25;78HABC"
putglyph 0x41 1 24,77
putglyph 0x42 1 24,78
putglyph 0x43 1 24,79
?cursor = 24,79
PUSH "D"
moverect 1..25,0..80 -> 0..24,0..80
putglyph 0x44 1 24,0

!80th column phantom linefeed phantom cancelled by explicit cursor move
PUSH "\e[25;78HABC"
putglyph 0x41 1 24,77
putglyph 0x42 1 24,78
putglyph 0x43 1 24,79
?cursor = 24,79
PUSH "\e[25;1HD"
putglyph 0x44 1 24,0
@@ -0,0 +1,60 @@
INIT
WANTSTATE g

!Initial
RESET
PUSH "\tX"
putglyph 0x58 1 0,8
PUSH "\tX"
putglyph 0x58 1 0,16
?cursor = 0,17

!HTS
PUSH "\e[5G\eH"
PUSH "\e[G\tX"
putglyph 0x58 1 0,4
?cursor = 0,5

!TBC 0
PUSH "\e[9G\e[g"
PUSH "\e[G\tX\tX"
putglyph 0x58 1 0,4
putglyph 0x58 1 0,16
?cursor = 0,17

!TBC 3
PUSH "\e[3g\e[50G\eH\e[G"
?cursor = 0,0
PUSH "\tX"
putglyph 0x58 1 0,49
?cursor = 0,50

!Tabstops after resize
RESET
RESIZE 30,100
# Should be 100/8 = 12 tabstops
PUSH "\tX"
putglyph 0x58 1 0,8
PUSH "\tX"
putglyph 0x58 1 0,16
PUSH "\tX"
putglyph 0x58 1 0,24
PUSH "\tX"
putglyph 0x58 1 0,32
PUSH "\tX"
putglyph 0x58 1 0,40
PUSH "\tX"
putglyph 0x58 1 0,48
PUSH "\tX"
putglyph 0x58 1 0,56
PUSH "\tX"
putglyph 0x58 1 0,64
PUSH "\tX"
putglyph 0x58 1 0,72
PUSH "\tX"
putglyph 0x58 1 0,80
PUSH "\tX"
putglyph 0x58 1 0,88
PUSH "\tX"
putglyph 0x58 1 0,96
?cursor = 0,97
@@ -0,0 +1,64 @@
INIT
WANTSTATE p

RESET
settermprop 1 true
settermprop 2 true
settermprop 7 1

!Set up state
PUSH "\e[2;2H"
?cursor = 1,1
PUSH "\e[1m"
?pen bold = on

!Save
PUSH "\e[?1048h"

!Change state
PUSH "\e[5;5H"
?cursor = 4,4
PUSH "\e[4 q"
settermprop 2 false
settermprop 7 2
PUSH "\e[22;4m"
?pen bold = off
?pen underline = 1

!Restore
PUSH "\e[?1048l"
settermprop 1 true
settermprop 2 true
settermprop 7 1
?cursor = 1,1
?pen bold = on
?pen underline = 0

!Save/restore using DECSC/DECRC
PUSH "\e[2;2H\e7"
?cursor = 1,1

PUSH "\e[5;5H"
?cursor = 4,4
PUSH "\e8"
settermprop 1 true
settermprop 2 true
settermprop 7 1
?cursor = 1,1

!Save twice, restore twice happens on both edge transitions
PUSH "\e[2;10H\e[?1048h\e[6;10H\e[?1048h"
PUSH "\e[H"
?cursor = 0,0
PUSH "\e[?1048l"
settermprop 1 true
settermprop 2 true
settermprop 7 1
?cursor = 5,9
PUSH "\e[H"
?cursor = 0,0
PUSH "\e[?1048l"
settermprop 1 true
settermprop 2 true
settermprop 7 1
?cursor = 5,9
@@ -0,0 +1,132 @@
INIT
WANTSTATE

!Unmodified ASCII
INCHAR 0 41
output "A"
INCHAR 0 61
output "a"

!Ctrl modifier on ASCII letters
INCHAR C 41
output "\e[65;5u"
INCHAR C 61
output "\x01"

!Alt modifier on ASCII letters
INCHAR A 41
output "\eA"
INCHAR A 61
output "\ea"

!Ctrl-Alt modifier on ASCII letters
INCHAR CA 41
output "\e[65;7u"
INCHAR CA 61
output "\e\x01"

!Special handling of Ctrl-I
INCHAR 0 49
output "I"
INCHAR 0 69
output "i"
INCHAR C 49
output "\e[73;5u"
INCHAR C 69
output "\e[105;5u"
INCHAR A 49
output "\eI"
INCHAR A 69
output "\ei"
INCHAR CA 49
output "\e[73;7u"
INCHAR CA 69
output "\e[105;7u"

!Special handling of Space
INCHAR 0 20
output " "
INCHAR S 20
output "\e[32;2u"
INCHAR C 20
output "\0"
INCHAR SC 20
output "\e[32;6u"
INCHAR A 20
output "\e "
INCHAR SA 20
output "\e[32;4u"
INCHAR CA 20
output "\e\0"
INCHAR SCA 20
output "\e[32;8u"

!Cursor keys in reset (cursor) mode
INKEY 0 Up
output "\e[A"
INKEY S Up
output "\e[1;2A"
INKEY C Up
output "\e[1;5A"
INKEY SC Up
output "\e[1;6A"
INKEY A Up
output "\e[1;3A"
INKEY SA Up
output "\e[1;4A"
INKEY CA Up
output "\e[1;7A"
INKEY SCA Up
output "\e[1;8A"

!Cursor keys in application mode
PUSH "\e[?1h"
# Plain "Up" should be SS3 A now
INKEY 0 Up
output "\eOA"
# Modified keys should still use CSI
INKEY S Up
output "\e[1;2A"
INKEY C Up
output "\e[1;5A"

!Shift-Tab should be different
INKEY 0 Tab
output "\x09"
INKEY S Tab
output "\e[Z"
INKEY C Tab
output "\e[9;5u"
INKEY A Tab
output "\e\x09"
INKEY CA Tab
output "\e[9;7u"

!Enter in linefeed mode
INKEY 0 Enter
output "\x0d"

!Enter in newline mode
PUSH "\e[20h"
INKEY 0 Enter
output "\x0d\x0a"

!Keypad in DECKPNM
INKEY 0 KP0
output "0"

!Keypad in DECKPAM
PUSH "\e="
INKEY 0 KP0
output "\eOp"

!Bracketed paste mode off
PASTE START
PASTE END

!Bracketed paste mode on
PUSH "\e[?2004h"
PASTE START
output "\e[200~"
PASTE END
output "\e[201~"
@@ -0,0 +1,62 @@
INIT
WANTSTATE

!DA
RESET
PUSH "\e[c"
output "\e[?1;2c"

!DSR
RESET
PUSH "\e[5n"
output "\e[0n"

!CPR
PUSH "\e[6n"
output "\e[1;1R"
PUSH "\e[10;10H\e[6n"
output "\e[10;10R"

!DECCPR
PUSH "\e[?6n"
output "\e[?10;10R"

!DECRQSS on DECSCUSR
PUSH "\e[3 q"
PUSH "\eP\$q q\e\\"
output "\eP1\$r3 q\e\\"

!DECRQSS on SGR
PUSH "\e[1;5;7m"
PUSH "\eP\$qm\e\\"
output "\eP1\$r1;5;7m\e\\"

!DECRQSS on SGR ANSI colours
PUSH "\e[0;31;42m"
PUSH "\eP\$qm\e\\"
output "\eP1\$r31;42m\e\\"

!DECRQSS on SGR ANSI hi-bright colours
PUSH "\e[0;93;104m"
PUSH "\eP\$qm\e\\"
output "\eP1\$r93;104m\e\\"

!DECRQSS on SGR 256-palette colours
PUSH "\e[0;38:5:56;48:5:78m"
PUSH "\eP\$qm\e\\"
output "\eP1\$r38:5:56;48:5:78m\e\\"

!DECRQSS on SGR RGB8 colours
PUSH "\e[0;38:2:24:68:112;48:2:13:57:101m"
PUSH "\eP\$qm\e\\"
output "\eP1\$r38:2:24:68:112;48:2:13:57:101m\e\\"

!S8C1T on DSR
PUSH "\e G"
PUSH "\e[5n"
output "\x{9b}0n"
PUSH "\e F"

!Truncation on attempted buffer overflow
PUSH "\e[6n" x 20
output "\e[10;10R" x 7
@@ -0,0 +1,32 @@
INIT
WANTSTATE

RESET

!RIS homes cursor
PUSH "\e[5;5H"
?cursor = 4,4
WANTSTATE +m
PUSH "\ec"
?cursor = 0,0
WANTSTATE -m

!RIS cancels scrolling region
PUSH "\e[5;10r"
WANTSTATE +s
PUSH "\ec\e[25H\n"
scrollrect 0..25,0..80 => +1,+0
WANTSTATE -s

!RIS erases screen
PUSH "ABCDE"
WANTSTATE +e
PUSH "\ec"
erase 0..25,0..80
WANTSTATE -e

!RIS clears tabstops
PUSH "\e[5G\eH\e[G\t"
?cursor = 0,4
PUSH "\ec\t"
?cursor = 0,8