sistema_progs

Programas para customizar o meu entorno de traballo nos meus equipos persoais
Log | Files | Refs

st.c (59200B)


      1 /* See LICENSE for license details. */
      2 #include <ctype.h>
      3 #include <errno.h>
      4 #include <fcntl.h>
      5 #include <limits.h>
      6 #include <pwd.h>
      7 #include <stdarg.h>
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 #include <signal.h>
     12 #include <sys/ioctl.h>
     13 #include <sys/select.h>
     14 #include <sys/types.h>
     15 #include <sys/wait.h>
     16 #include <termios.h>
     17 #include <unistd.h>
     18 #include <wchar.h>
     19 
     20 #include "st.h"
     21 #include "win.h"
     22 
     23 #if   defined(__linux)
     24  #include <pty.h>
     25 #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
     26  #include <util.h>
     27 #elif defined(__FreeBSD__) || defined(__DragonFly__)
     28  #include <libutil.h>
     29 #endif
     30 
     31 /* Arbitrary sizes */
     32 #define UTF_INVALID   0xFFFD
     33 #define UTF_SIZ       4
     34 #define ESC_BUF_SIZ   (128*UTF_SIZ)
     35 #define ESC_ARG_SIZ   16
     36 #define STR_BUF_SIZ   ESC_BUF_SIZ
     37 #define STR_ARG_SIZ   ESC_ARG_SIZ
     38 #define HISTSIZE      2000
     39 
     40 /* macros */
     41 #define IS_SET(flag)		((term.mode & (flag)) != 0)
     42 #define ISCONTROLC0(c)		(BETWEEN(c, 0, 0x1f) || (c) == 0x7f)
     43 #define ISCONTROLC1(c)		(BETWEEN(c, 0x80, 0x9f))
     44 #define ISCONTROL(c)		(ISCONTROLC0(c) || ISCONTROLC1(c))
     45 #define ISDELIM(u)		(u && wcschr(worddelimiters, u))
     46 #define TLINE(y)		((y) < term.scr ? term.hist[((y) + term.histi - \
     47 				term.scr + HISTSIZE + 1) % HISTSIZE] : \
     48 				term.line[(y) - term.scr])
     49 
     50 enum term_mode {
     51 	MODE_WRAP        = 1 << 0,
     52 	MODE_INSERT      = 1 << 1,
     53 	MODE_ALTSCREEN   = 1 << 2,
     54 	MODE_CRLF        = 1 << 3,
     55 	MODE_ECHO        = 1 << 4,
     56 	MODE_PRINT       = 1 << 5,
     57 	MODE_UTF8        = 1 << 6,
     58 };
     59 
     60 enum cursor_movement {
     61 	CURSOR_SAVE,
     62 	CURSOR_LOAD
     63 };
     64 
     65 enum cursor_state {
     66 	CURSOR_DEFAULT  = 0,
     67 	CURSOR_WRAPNEXT = 1,
     68 	CURSOR_ORIGIN   = 2
     69 };
     70 
     71 enum charset {
     72 	CS_GRAPHIC0,
     73 	CS_GRAPHIC1,
     74 	CS_UK,
     75 	CS_USA,
     76 	CS_MULTI,
     77 	CS_GER,
     78 	CS_FIN
     79 };
     80 
     81 enum escape_state {
     82 	ESC_START      = 1,
     83 	ESC_CSI        = 2,
     84 	ESC_STR        = 4,  /* DCS, OSC, PM, APC */
     85 	ESC_ALTCHARSET = 8,
     86 	ESC_STR_END    = 16, /* a final string was encountered */
     87 	ESC_TEST       = 32, /* Enter in test mode */
     88 	ESC_UTF8       = 64,
     89 };
     90 
     91 typedef struct {
     92 	Glyph attr; /* current char attributes */
     93 	int x;
     94 	int y;
     95 	char state;
     96 } TCursor;
     97 
     98 typedef struct {
     99 	int mode;
    100 	int type;
    101 	int snap;
    102 	/*
    103 	 * Selection variables:
    104 	 * nb – normalized coordinates of the beginning of the selection
    105 	 * ne – normalized coordinates of the end of the selection
    106 	 * ob – original coordinates of the beginning of the selection
    107 	 * oe – original coordinates of the end of the selection
    108 	 */
    109 	struct {
    110 		int x, y;
    111 	} nb, ne, ob, oe;
    112 
    113 	int alt;
    114 } Selection;
    115 
    116 /* Internal representation of the screen */
    117 typedef struct {
    118 	int row;      /* nb row */
    119 	int col;      /* nb col */
    120 	Line *line;   /* screen */
    121 	Line *alt;    /* alternate screen */
    122 	Line hist[HISTSIZE]; /* history buffer */
    123 	int histi;    /* history index */
    124 	int scr;      /* scroll back */
    125 	int *dirty;   /* dirtyness of lines */
    126 	TCursor c;    /* cursor */
    127 	int ocx;      /* old cursor col */
    128 	int ocy;      /* old cursor row */
    129 	int top;      /* top    scroll limit */
    130 	int bot;      /* bottom scroll limit */
    131 	int mode;     /* terminal mode flags */
    132 	int esc;      /* escape state flags */
    133 	char trantbl[4]; /* charset table translation */
    134 	int charset;  /* current charset */
    135 	int icharset; /* selected charset for sequence */
    136 	int *tabs;
    137 	Rune lastc;   /* last printed char outside of sequence, 0 if control */
    138 } Term;
    139 
    140 /* CSI Escape sequence structs */
    141 /* ESC '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */
    142 typedef struct {
    143 	char buf[ESC_BUF_SIZ]; /* raw string */
    144 	size_t len;            /* raw string length */
    145 	char priv;
    146 	int arg[ESC_ARG_SIZ];
    147 	int narg;              /* nb of args */
    148 	char mode[2];
    149 } CSIEscape;
    150 
    151 /* STR Escape sequence structs */
    152 /* ESC type [[ [<priv>] <arg> [;]] <mode>] ESC '\' */
    153 typedef struct {
    154 	char type;             /* ESC type ... */
    155 	char *buf;             /* allocated raw string */
    156 	size_t siz;            /* allocation size */
    157 	size_t len;            /* raw string length */
    158 	char *args[STR_ARG_SIZ];
    159 	int narg;              /* nb of args */
    160 } STREscape;
    161 
    162 static void execsh(char *, char **);
    163 static void stty(char **);
    164 static void sigchld(int);
    165 static void ttywriteraw(const char *, size_t);
    166 
    167 static void csidump(void);
    168 static void csihandle(void);
    169 static void csiparse(void);
    170 static void csireset(void);
    171 static int eschandle(uchar);
    172 static void strdump(void);
    173 static void strhandle(void);
    174 static void strparse(void);
    175 static void strreset(void);
    176 
    177 static void tprinter(char *, size_t);
    178 static void tdumpsel(void);
    179 static void tdumpline(int);
    180 static void tdump(void);
    181 static void tclearregion(int, int, int, int);
    182 static void tcursor(int);
    183 static void tdeletechar(int);
    184 static void tdeleteline(int);
    185 static void tinsertblank(int);
    186 static void tinsertblankline(int);
    187 static int tlinelen(int);
    188 static void tmoveto(int, int);
    189 static void tmoveato(int, int);
    190 static void tnewline(int);
    191 static void tputtab(int);
    192 static void tputc(Rune);
    193 static void treset(void);
    194 static void tscrollup(int, int, int);
    195 static void tscrolldown(int, int, int);
    196 static void tsetattr(const int *, int);
    197 static void tsetchar(Rune, const Glyph *, int, int);
    198 static void tsetdirt(int, int);
    199 static void tsetscroll(int, int);
    200 static void tswapscreen(void);
    201 static void tsetmode(int, int, const int *, int);
    202 static int twrite(const char *, int, int);
    203 static void tfulldirt(void);
    204 static void tcontrolcode(uchar );
    205 static void tdectest(char );
    206 static void tdefutf8(char);
    207 static int32_t tdefcolor(const int *, int *, int);
    208 static void tdeftran(char);
    209 static void tstrsequence(uchar);
    210 
    211 static void drawregion(int, int, int, int);
    212 
    213 static void selnormalize(void);
    214 static void selscroll(int, int);
    215 static void selsnap(int *, int *, int);
    216 
    217 static size_t utf8decode(const char *, Rune *, size_t);
    218 static Rune utf8decodebyte(char, size_t *);
    219 static char utf8encodebyte(Rune, size_t);
    220 static size_t utf8validate(Rune *, size_t);
    221 
    222 static char *base64dec(const char *);
    223 static char base64dec_getc(const char **);
    224 
    225 static ssize_t xwrite(int, const char *, size_t);
    226 
    227 /* Globals */
    228 static Term term;
    229 static Selection sel;
    230 static CSIEscape csiescseq;
    231 static STREscape strescseq;
    232 static int iofd = 1;
    233 static int cmdfd;
    234 static pid_t pid;
    235 
    236 static const uchar utfbyte[UTF_SIZ + 1] = {0x80,    0, 0xC0, 0xE0, 0xF0};
    237 static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
    238 static const Rune utfmin[UTF_SIZ + 1] = {       0,    0,  0x80,  0x800,  0x10000};
    239 static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
    240 
    241 ssize_t
    242 xwrite(int fd, const char *s, size_t len)
    243 {
    244 	size_t aux = len;
    245 	ssize_t r;
    246 
    247 	while (len > 0) {
    248 		r = write(fd, s, len);
    249 		if (r < 0)
    250 			return r;
    251 		len -= r;
    252 		s += r;
    253 	}
    254 
    255 	return aux;
    256 }
    257 
    258 void *
    259 xmalloc(size_t len)
    260 {
    261 	void *p;
    262 
    263 	if (!(p = malloc(len)))
    264 		die("malloc: %s\n", strerror(errno));
    265 
    266 	return p;
    267 }
    268 
    269 void *
    270 xrealloc(void *p, size_t len)
    271 {
    272 	if ((p = realloc(p, len)) == NULL)
    273 		die("realloc: %s\n", strerror(errno));
    274 
    275 	return p;
    276 }
    277 
    278 char *
    279 xstrdup(const char *s)
    280 {
    281 	char *p;
    282 
    283 	if ((p = strdup(s)) == NULL)
    284 		die("strdup: %s\n", strerror(errno));
    285 
    286 	return p;
    287 }
    288 
    289 size_t
    290 utf8decode(const char *c, Rune *u, size_t clen)
    291 {
    292 	size_t i, j, len, type;
    293 	Rune udecoded;
    294 
    295 	*u = UTF_INVALID;
    296 	if (!clen)
    297 		return 0;
    298 	udecoded = utf8decodebyte(c[0], &len);
    299 	if (!BETWEEN(len, 1, UTF_SIZ))
    300 		return 1;
    301 	for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
    302 		udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
    303 		if (type != 0)
    304 			return j;
    305 	}
    306 	if (j < len)
    307 		return 0;
    308 	*u = udecoded;
    309 	utf8validate(u, len);
    310 
    311 	return len;
    312 }
    313 
    314 Rune
    315 utf8decodebyte(char c, size_t *i)
    316 {
    317 	for (*i = 0; *i < LEN(utfmask); ++(*i))
    318 		if (((uchar)c & utfmask[*i]) == utfbyte[*i])
    319 			return (uchar)c & ~utfmask[*i];
    320 
    321 	return 0;
    322 }
    323 
    324 size_t
    325 utf8encode(Rune u, char *c)
    326 {
    327 	size_t len, i;
    328 
    329 	len = utf8validate(&u, 0);
    330 	if (len > UTF_SIZ)
    331 		return 0;
    332 
    333 	for (i = len - 1; i != 0; --i) {
    334 		c[i] = utf8encodebyte(u, 0);
    335 		u >>= 6;
    336 	}
    337 	c[0] = utf8encodebyte(u, len);
    338 
    339 	return len;
    340 }
    341 
    342 char
    343 utf8encodebyte(Rune u, size_t i)
    344 {
    345 	return utfbyte[i] | (u & ~utfmask[i]);
    346 }
    347 
    348 size_t
    349 utf8validate(Rune *u, size_t i)
    350 {
    351 	if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
    352 		*u = UTF_INVALID;
    353 	for (i = 1; *u > utfmax[i]; ++i)
    354 		;
    355 
    356 	return i;
    357 }
    358 
    359 static const char base64_digits[] = {
    360 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    361 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0,
    362 	63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, -1, 0, 0, 0, 0, 1,
    363 	2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
    364 	22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34,
    365 	35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0,
    366 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    367 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    368 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    369 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    370 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    371 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    372 };
    373 
    374 char
    375 base64dec_getc(const char **src)
    376 {
    377 	while (**src && !isprint(**src))
    378 		(*src)++;
    379 	return **src ? *((*src)++) : '=';  /* emulate padding if string ends */
    380 }
    381 
    382 char *
    383 base64dec(const char *src)
    384 {
    385 	size_t in_len = strlen(src);
    386 	char *result, *dst;
    387 
    388 	if (in_len % 4)
    389 		in_len += 4 - (in_len % 4);
    390 	result = dst = xmalloc(in_len / 4 * 3 + 1);
    391 	while (*src) {
    392 		int a = base64_digits[(unsigned char) base64dec_getc(&src)];
    393 		int b = base64_digits[(unsigned char) base64dec_getc(&src)];
    394 		int c = base64_digits[(unsigned char) base64dec_getc(&src)];
    395 		int d = base64_digits[(unsigned char) base64dec_getc(&src)];
    396 
    397 		/* invalid input. 'a' can be -1, e.g. if src is "\n" (c-str) */
    398 		if (a == -1 || b == -1)
    399 			break;
    400 
    401 		*dst++ = (a << 2) | ((b & 0x30) >> 4);
    402 		if (c == -1)
    403 			break;
    404 		*dst++ = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2);
    405 		if (d == -1)
    406 			break;
    407 		*dst++ = ((c & 0x03) << 6) | d;
    408 	}
    409 	*dst = '\0';
    410 	return result;
    411 }
    412 
    413 void
    414 selinit(void)
    415 {
    416 	sel.mode = SEL_IDLE;
    417 	sel.snap = 0;
    418 	sel.ob.x = -1;
    419 }
    420 
    421 int
    422 tlinelen(int y)
    423 {
    424 	int i = term.col;
    425 
    426 	if (TLINE(y)[i - 1].mode & ATTR_WRAP)
    427 		return i;
    428 
    429 	while (i > 0 && TLINE(y)[i - 1].u == ' ')
    430 		--i;
    431 
    432 	return i;
    433 }
    434 
    435 void
    436 selstart(int col, int row, int snap)
    437 {
    438 	selclear();
    439 	sel.mode = SEL_EMPTY;
    440 	sel.type = SEL_REGULAR;
    441 	sel.alt = IS_SET(MODE_ALTSCREEN);
    442 	sel.snap = snap;
    443 	sel.oe.x = sel.ob.x = col;
    444 	sel.oe.y = sel.ob.y = row;
    445 	selnormalize();
    446 
    447 	if (sel.snap != 0)
    448 		sel.mode = SEL_READY;
    449 	tsetdirt(sel.nb.y, sel.ne.y);
    450 }
    451 
    452 void
    453 selextend(int col, int row, int type, int done)
    454 {
    455 	int oldey, oldex, oldsby, oldsey, oldtype;
    456 
    457 	if (sel.mode == SEL_IDLE)
    458 		return;
    459 	if (done && sel.mode == SEL_EMPTY) {
    460 		selclear();
    461 		return;
    462 	}
    463 
    464 	oldey = sel.oe.y;
    465 	oldex = sel.oe.x;
    466 	oldsby = sel.nb.y;
    467 	oldsey = sel.ne.y;
    468 	oldtype = sel.type;
    469 
    470 	sel.oe.x = col;
    471 	sel.oe.y = row;
    472 	selnormalize();
    473 	sel.type = type;
    474 
    475 	if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY)
    476 		tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey));
    477 
    478 	sel.mode = done ? SEL_IDLE : SEL_READY;
    479 }
    480 
    481 void
    482 selnormalize(void)
    483 {
    484 	int i;
    485 
    486 	if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) {
    487 		sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x;
    488 		sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x;
    489 	} else {
    490 		sel.nb.x = MIN(sel.ob.x, sel.oe.x);
    491 		sel.ne.x = MAX(sel.ob.x, sel.oe.x);
    492 	}
    493 	sel.nb.y = MIN(sel.ob.y, sel.oe.y);
    494 	sel.ne.y = MAX(sel.ob.y, sel.oe.y);
    495 
    496 	selsnap(&sel.nb.x, &sel.nb.y, -1);
    497 	selsnap(&sel.ne.x, &sel.ne.y, +1);
    498 
    499 	/* expand selection over line breaks */
    500 	if (sel.type == SEL_RECTANGULAR)
    501 		return;
    502 	i = tlinelen(sel.nb.y);
    503 	if (i < sel.nb.x)
    504 		sel.nb.x = i;
    505 	if (tlinelen(sel.ne.y) <= sel.ne.x)
    506 		sel.ne.x = term.col - 1;
    507 }
    508 
    509 int
    510 selected(int x, int y)
    511 {
    512 	if (sel.mode == SEL_EMPTY || sel.ob.x == -1 ||
    513 			sel.alt != IS_SET(MODE_ALTSCREEN))
    514 		return 0;
    515 
    516 	if (sel.type == SEL_RECTANGULAR)
    517 		return BETWEEN(y, sel.nb.y, sel.ne.y)
    518 		    && BETWEEN(x, sel.nb.x, sel.ne.x);
    519 
    520 	return BETWEEN(y, sel.nb.y, sel.ne.y)
    521 	    && (y != sel.nb.y || x >= sel.nb.x)
    522 	    && (y != sel.ne.y || x <= sel.ne.x);
    523 }
    524 
    525 void
    526 selsnap(int *x, int *y, int direction)
    527 {
    528 	int newx, newy, xt, yt;
    529 	int delim, prevdelim;
    530 	const Glyph *gp, *prevgp;
    531 
    532 	switch (sel.snap) {
    533 	case SNAP_WORD:
    534 		/*
    535 		 * Snap around if the word wraps around at the end or
    536 		 * beginning of a line.
    537 		 */
    538 		prevgp = &TLINE(*y)[*x];
    539 		prevdelim = ISDELIM(prevgp->u);
    540 		for (;;) {
    541 			newx = *x + direction;
    542 			newy = *y;
    543 			if (!BETWEEN(newx, 0, term.col - 1)) {
    544 				newy += direction;
    545 				newx = (newx + term.col) % term.col;
    546 				if (!BETWEEN(newy, 0, term.row - 1))
    547 					break;
    548 
    549 				if (direction > 0)
    550 					yt = *y, xt = *x;
    551 				else
    552 					yt = newy, xt = newx;
    553 				if (!(TLINE(yt)[xt].mode & ATTR_WRAP))
    554 					break;
    555 			}
    556 
    557 			if (newx >= tlinelen(newy))
    558 				break;
    559 
    560 			gp = &TLINE(newy)[newx];
    561 			delim = ISDELIM(gp->u);
    562 			if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
    563 					|| (delim && gp->u != prevgp->u)))
    564 				break;
    565 
    566 			*x = newx;
    567 			*y = newy;
    568 			prevgp = gp;
    569 			prevdelim = delim;
    570 		}
    571 		break;
    572 	case SNAP_LINE:
    573 		/*
    574 		 * Snap around if the the previous line or the current one
    575 		 * has set ATTR_WRAP at its end. Then the whole next or
    576 		 * previous line will be selected.
    577 		 */
    578 		*x = (direction < 0) ? 0 : term.col - 1;
    579 		if (direction < 0) {
    580 			for (; *y > 0; *y += direction) {
    581 				if (!(TLINE(*y-1)[term.col-1].mode
    582 						& ATTR_WRAP)) {
    583 					break;
    584 				}
    585 			}
    586 		} else if (direction > 0) {
    587 			for (; *y < term.row-1; *y += direction) {
    588 				if (!(TLINE(*y)[term.col-1].mode
    589 						& ATTR_WRAP)) {
    590 					break;
    591 				}
    592 			}
    593 		}
    594 		break;
    595 	}
    596 }
    597 
    598 char *
    599 getsel(void)
    600 {
    601 	char *str, *ptr;
    602 	int y, bufsize, lastx, linelen;
    603 	const Glyph *gp, *last;
    604 
    605 	if (sel.ob.x == -1)
    606 		return NULL;
    607 
    608 	bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ;
    609 	ptr = str = xmalloc(bufsize);
    610 
    611 	/* append every set & selected glyph to the selection */
    612 	for (y = sel.nb.y; y <= sel.ne.y; y++) {
    613 		if ((linelen = tlinelen(y)) == 0) {
    614 			*ptr++ = '\n';
    615 			continue;
    616 		}
    617 
    618 		if (sel.type == SEL_RECTANGULAR) {
    619 			gp = &TLINE(y)[sel.nb.x];
    620 			lastx = sel.ne.x;
    621 		} else {
    622 			gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0];
    623 			lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
    624 		}
    625 		last = &TLINE(y)[MIN(lastx, linelen-1)];
    626 		while (last >= gp && last->u == ' ')
    627 			--last;
    628 
    629 		for ( ; gp <= last; ++gp) {
    630 			if (gp->mode & ATTR_WDUMMY)
    631 				continue;
    632 
    633 			ptr += utf8encode(gp->u, ptr);
    634 		}
    635 
    636 		/*
    637 		 * Copy and pasting of line endings is inconsistent
    638 		 * in the inconsistent terminal and GUI world.
    639 		 * The best solution seems like to produce '\n' when
    640 		 * something is copied from st and convert '\n' to
    641 		 * '\r', when something to be pasted is received by
    642 		 * st.
    643 		 * FIXME: Fix the computer world.
    644 		 */
    645 		if ((y < sel.ne.y || lastx >= linelen) &&
    646 		    (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR))
    647 			*ptr++ = '\n';
    648 	}
    649 	*ptr = 0;
    650 	return str;
    651 }
    652 
    653 void
    654 selclear(void)
    655 {
    656 	if (sel.ob.x == -1)
    657 		return;
    658 	sel.mode = SEL_IDLE;
    659 	sel.ob.x = -1;
    660 	tsetdirt(sel.nb.y, sel.ne.y);
    661 }
    662 
    663 void
    664 die(const char *errstr, ...)
    665 {
    666 	va_list ap;
    667 
    668 	va_start(ap, errstr);
    669 	vfprintf(stderr, errstr, ap);
    670 	va_end(ap);
    671 	exit(1);
    672 }
    673 
    674 void
    675 execsh(char *cmd, char **args)
    676 {
    677 	char *sh, *prog, *arg;
    678 	const struct passwd *pw;
    679 
    680 	errno = 0;
    681 	if ((pw = getpwuid(getuid())) == NULL) {
    682 		if (errno)
    683 			die("getpwuid: %s\n", strerror(errno));
    684 		else
    685 			die("who are you?\n");
    686 	}
    687 
    688 	if ((sh = getenv("SHELL")) == NULL)
    689 		sh = (pw->pw_shell[0]) ? pw->pw_shell : cmd;
    690 
    691 	if (args) {
    692 		prog = args[0];
    693 		arg = NULL;
    694 	} else if (scroll) {
    695 		prog = scroll;
    696 		arg = utmp ? utmp : sh;
    697 	} else if (utmp) {
    698 		prog = utmp;
    699 		arg = NULL;
    700 	} else {
    701 		prog = sh;
    702 		arg = NULL;
    703 	}
    704 	DEFAULT(args, ((char *[]) {prog, arg, NULL}));
    705 
    706 	unsetenv("COLUMNS");
    707 	unsetenv("LINES");
    708 	unsetenv("TERMCAP");
    709 	setenv("LOGNAME", pw->pw_name, 1);
    710 	setenv("USER", pw->pw_name, 1);
    711 	setenv("SHELL", sh, 1);
    712 	setenv("HOME", pw->pw_dir, 1);
    713 	setenv("TERM", termname, 1);
    714 
    715 	signal(SIGCHLD, SIG_DFL);
    716 	signal(SIGHUP, SIG_DFL);
    717 	signal(SIGINT, SIG_DFL);
    718 	signal(SIGQUIT, SIG_DFL);
    719 	signal(SIGTERM, SIG_DFL);
    720 	signal(SIGALRM, SIG_DFL);
    721 
    722 	execvp(prog, args);
    723 	_exit(1);
    724 }
    725 
    726 void
    727 sigchld(int a)
    728 {
    729 	int stat;
    730 	pid_t p;
    731 
    732 	if ((p = waitpid(pid, &stat, WNOHANG)) < 0)
    733 		die("waiting for pid %hd failed: %s\n", pid, strerror(errno));
    734 
    735 	if (pid != p)
    736 		return;
    737 
    738 	if (WIFEXITED(stat) && WEXITSTATUS(stat))
    739 		die("child exited with status %d\n", WEXITSTATUS(stat));
    740 	else if (WIFSIGNALED(stat))
    741 		die("child terminated due to signal %d\n", WTERMSIG(stat));
    742 	_exit(0);
    743 }
    744 
    745 void
    746 stty(char **args)
    747 {
    748 	char cmd[_POSIX_ARG_MAX], **p, *q, *s;
    749 	size_t n, siz;
    750 
    751 	if ((n = strlen(stty_args)) > sizeof(cmd)-1)
    752 		die("incorrect stty parameters\n");
    753 	memcpy(cmd, stty_args, n);
    754 	q = cmd + n;
    755 	siz = sizeof(cmd) - n;
    756 	for (p = args; p && (s = *p); ++p) {
    757 		if ((n = strlen(s)) > siz-1)
    758 			die("stty parameter length too long\n");
    759 		*q++ = ' ';
    760 		memcpy(q, s, n);
    761 		q += n;
    762 		siz -= n + 1;
    763 	}
    764 	*q = '\0';
    765 	if (system(cmd) != 0)
    766 		perror("Couldn't call stty");
    767 }
    768 
    769 int
    770 ttynew(const char *line, char *cmd, const char *out, char **args)
    771 {
    772 	int m, s;
    773 
    774 	if (out) {
    775 		term.mode |= MODE_PRINT;
    776 		iofd = (!strcmp(out, "-")) ?
    777 			  1 : open(out, O_WRONLY | O_CREAT, 0666);
    778 		if (iofd < 0) {
    779 			fprintf(stderr, "Error opening %s:%s\n",
    780 				out, strerror(errno));
    781 		}
    782 	}
    783 
    784 	if (line) {
    785 		if ((cmdfd = open(line, O_RDWR)) < 0)
    786 			die("open line '%s' failed: %s\n",
    787 			    line, strerror(errno));
    788 		dup2(cmdfd, 0);
    789 		stty(args);
    790 		return cmdfd;
    791 	}
    792 
    793 	/* seems to work fine on linux, openbsd and freebsd */
    794 	if (openpty(&m, &s, NULL, NULL, NULL) < 0)
    795 		die("openpty failed: %s\n", strerror(errno));
    796 
    797 	switch (pid = fork()) {
    798 	case -1:
    799 		die("fork failed: %s\n", strerror(errno));
    800 		break;
    801 	case 0:
    802 		close(iofd);
    803 		close(m);
    804 		setsid(); /* create a new process group */
    805 		dup2(s, 0);
    806 		dup2(s, 1);
    807 		dup2(s, 2);
    808 		if (ioctl(s, TIOCSCTTY, NULL) < 0)
    809 			die("ioctl TIOCSCTTY failed: %s\n", strerror(errno));
    810 		if (s > 2)
    811 			close(s);
    812 #ifdef __OpenBSD__
    813 		if (pledge("stdio getpw proc exec", NULL) == -1)
    814 			die("pledge\n");
    815 #endif
    816 		execsh(cmd, args);
    817 		break;
    818 	default:
    819 #ifdef __OpenBSD__
    820 		if (pledge("stdio rpath tty proc", NULL) == -1)
    821 			die("pledge\n");
    822 #endif
    823 		close(s);
    824 		cmdfd = m;
    825 		signal(SIGCHLD, sigchld);
    826 		break;
    827 	}
    828 	return cmdfd;
    829 }
    830 
    831 size_t
    832 ttyread(void)
    833 {
    834 	static char buf[BUFSIZ];
    835 	static int buflen = 0;
    836 	int ret, written;
    837 
    838 	/* append read bytes to unprocessed bytes */
    839 	ret = read(cmdfd, buf+buflen, LEN(buf)-buflen);
    840 
    841 	switch (ret) {
    842 	case 0:
    843 		exit(0);
    844 	case -1:
    845 		die("couldn't read from shell: %s\n", strerror(errno));
    846 	default:
    847 		buflen += ret;
    848 		written = twrite(buf, buflen, 0);
    849 		buflen -= written;
    850 		/* keep any incomplete UTF-8 byte sequence for the next call */
    851 		if (buflen > 0)
    852 			memmove(buf, buf + written, buflen);
    853 		return ret;
    854 	}
    855 }
    856 
    857 void
    858 ttywrite(const char *s, size_t n, int may_echo)
    859 {
    860 	const char *next;
    861 	Arg arg = (Arg) { .i = term.scr };
    862 
    863 	kscrolldown(&arg);
    864 
    865 	if (may_echo && IS_SET(MODE_ECHO))
    866 		twrite(s, n, 1);
    867 
    868 	if (!IS_SET(MODE_CRLF)) {
    869 		ttywriteraw(s, n);
    870 		return;
    871 	}
    872 
    873 	/* This is similar to how the kernel handles ONLCR for ttys */
    874 	while (n > 0) {
    875 		if (*s == '\r') {
    876 			next = s + 1;
    877 			ttywriteraw("\r\n", 2);
    878 		} else {
    879 			next = memchr(s, '\r', n);
    880 			DEFAULT(next, s + n);
    881 			ttywriteraw(s, next - s);
    882 		}
    883 		n -= next - s;
    884 		s = next;
    885 	}
    886 }
    887 
    888 void
    889 ttywriteraw(const char *s, size_t n)
    890 {
    891 	fd_set wfd, rfd;
    892 	ssize_t r;
    893 	size_t lim = 256;
    894 
    895 	/*
    896 	 * Remember that we are using a pty, which might be a modem line.
    897 	 * Writing too much will clog the line. That's why we are doing this
    898 	 * dance.
    899 	 * FIXME: Migrate the world to Plan 9.
    900 	 */
    901 	while (n > 0) {
    902 		FD_ZERO(&wfd);
    903 		FD_ZERO(&rfd);
    904 		FD_SET(cmdfd, &wfd);
    905 		FD_SET(cmdfd, &rfd);
    906 
    907 		/* Check if we can write. */
    908 		if (pselect(cmdfd+1, &rfd, &wfd, NULL, NULL, NULL) < 0) {
    909 			if (errno == EINTR)
    910 				continue;
    911 			die("select failed: %s\n", strerror(errno));
    912 		}
    913 		if (FD_ISSET(cmdfd, &wfd)) {
    914 			/*
    915 			 * Only write the bytes written by ttywrite() or the
    916 			 * default of 256. This seems to be a reasonable value
    917 			 * for a serial line. Bigger values might clog the I/O.
    918 			 */
    919 			if ((r = write(cmdfd, s, (n < lim)? n : lim)) < 0)
    920 				goto write_error;
    921 			if (r < n) {
    922 				/*
    923 				 * We weren't able to write out everything.
    924 				 * This means the buffer is getting full
    925 				 * again. Empty it.
    926 				 */
    927 				if (n < lim)
    928 					lim = ttyread();
    929 				n -= r;
    930 				s += r;
    931 			} else {
    932 				/* All bytes have been written. */
    933 				break;
    934 			}
    935 		}
    936 		if (FD_ISSET(cmdfd, &rfd))
    937 			lim = ttyread();
    938 	}
    939 	return;
    940 
    941 write_error:
    942 	die("write error on tty: %s\n", strerror(errno));
    943 }
    944 
    945 void
    946 ttyresize(int tw, int th)
    947 {
    948 	struct winsize w;
    949 
    950 	w.ws_row = term.row;
    951 	w.ws_col = term.col;
    952 	w.ws_xpixel = tw;
    953 	w.ws_ypixel = th;
    954 	if (ioctl(cmdfd, TIOCSWINSZ, &w) < 0)
    955 		fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno));
    956 }
    957 
    958 void
    959 ttyhangup()
    960 {
    961 	/* Send SIGHUP to shell */
    962 	kill(pid, SIGHUP);
    963 }
    964 
    965 int
    966 tattrset(int attr)
    967 {
    968 	int i, j;
    969 
    970 	for (i = 0; i < term.row-1; i++) {
    971 		for (j = 0; j < term.col-1; j++) {
    972 			if (term.line[i][j].mode & attr)
    973 				return 1;
    974 		}
    975 	}
    976 
    977 	return 0;
    978 }
    979 
    980 void
    981 tsetdirt(int top, int bot)
    982 {
    983 	int i;
    984 
    985 	LIMIT(top, 0, term.row-1);
    986 	LIMIT(bot, 0, term.row-1);
    987 
    988 	for (i = top; i <= bot; i++)
    989 		term.dirty[i] = 1;
    990 }
    991 
    992 void
    993 tsetdirtattr(int attr)
    994 {
    995 	int i, j;
    996 
    997 	for (i = 0; i < term.row-1; i++) {
    998 		for (j = 0; j < term.col-1; j++) {
    999 			if (term.line[i][j].mode & attr) {
   1000 				tsetdirt(i, i);
   1001 				break;
   1002 			}
   1003 		}
   1004 	}
   1005 }
   1006 
   1007 void
   1008 tfulldirt(void)
   1009 {
   1010 	tsetdirt(0, term.row-1);
   1011 }
   1012 
   1013 void
   1014 tcursor(int mode)
   1015 {
   1016 	static TCursor c[2];
   1017 	int alt = IS_SET(MODE_ALTSCREEN);
   1018 
   1019 	if (mode == CURSOR_SAVE) {
   1020 		c[alt] = term.c;
   1021 	} else if (mode == CURSOR_LOAD) {
   1022 		term.c = c[alt];
   1023 		tmoveto(c[alt].x, c[alt].y);
   1024 	}
   1025 }
   1026 
   1027 void
   1028 treset(void)
   1029 {
   1030 	uint i;
   1031 
   1032 	term.c = (TCursor){{
   1033 		.mode = ATTR_NULL,
   1034 		.fg = defaultfg,
   1035 		.bg = defaultbg
   1036 	}, .x = 0, .y = 0, .state = CURSOR_DEFAULT};
   1037 
   1038 	memset(term.tabs, 0, term.col * sizeof(*term.tabs));
   1039 	for (i = tabspaces; i < term.col; i += tabspaces)
   1040 		term.tabs[i] = 1;
   1041 	term.top = 0;
   1042 	term.bot = term.row - 1;
   1043 	term.mode = MODE_WRAP|MODE_UTF8;
   1044 	memset(term.trantbl, CS_USA, sizeof(term.trantbl));
   1045 	term.charset = 0;
   1046 
   1047 	for (i = 0; i < 2; i++) {
   1048 		tmoveto(0, 0);
   1049 		tcursor(CURSOR_SAVE);
   1050 		tclearregion(0, 0, term.col-1, term.row-1);
   1051 		tswapscreen();
   1052 	}
   1053 }
   1054 
   1055 void
   1056 tnew(int col, int row)
   1057 {
   1058 	term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } };
   1059 	tresize(col, row);
   1060 	treset();
   1061 }
   1062 
   1063 void
   1064 tswapscreen(void)
   1065 {
   1066 	Line *tmp = term.line;
   1067 
   1068 	term.line = term.alt;
   1069 	term.alt = tmp;
   1070 	term.mode ^= MODE_ALTSCREEN;
   1071 	tfulldirt();
   1072 }
   1073 
   1074 void
   1075 kscrolldown(const Arg* a)
   1076 {
   1077 	int n = a->i;
   1078 
   1079 	if (n < 0)
   1080 		n = term.row + n;
   1081 
   1082 	if (n > term.scr)
   1083 		n = term.scr;
   1084 
   1085 	if (term.scr > 0) {
   1086 		term.scr -= n;
   1087 		selscroll(0, -n);
   1088 		tfulldirt();
   1089 	}
   1090 }
   1091 
   1092 void
   1093 kscrollup(const Arg* a)
   1094 {
   1095 	int n = a->i;
   1096 
   1097 	if (n < 0)
   1098 		n = term.row + n;
   1099 
   1100 	if (term.scr <= HISTSIZE-n) {
   1101 		term.scr += n;
   1102 		selscroll(0, n);
   1103 		tfulldirt();
   1104 	}
   1105 }
   1106 
   1107 void
   1108 tscrolldown(int orig, int n, int copyhist)
   1109 {
   1110 	int i;
   1111 	Line temp;
   1112 
   1113 	LIMIT(n, 0, term.bot-orig+1);
   1114 	if (copyhist) {
   1115 		term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE;
   1116 		temp = term.hist[term.histi];
   1117 		term.hist[term.histi] = term.line[term.bot];
   1118 		term.line[term.bot] = temp;
   1119 	}
   1120 
   1121 
   1122 	tsetdirt(orig, term.bot-n);
   1123 	tclearregion(0, term.bot-n+1, term.col-1, term.bot);
   1124 
   1125 	for (i = term.bot; i >= orig+n; i--) {
   1126 		temp = term.line[i];
   1127 		term.line[i] = term.line[i-n];
   1128 		term.line[i-n] = temp;
   1129 	}
   1130 
   1131 	if (term.scr == 0)
   1132 		selscroll(orig, n);
   1133 }
   1134 
   1135 void
   1136 tscrollup(int orig, int n, int copyhist)
   1137 {
   1138 	int i;
   1139 	Line temp;
   1140 
   1141 	LIMIT(n, 0, term.bot-orig+1);
   1142 
   1143 	if (copyhist) {
   1144 		term.histi = (term.histi + 1) % HISTSIZE;
   1145 		temp = term.hist[term.histi];
   1146 		term.hist[term.histi] = term.line[orig];
   1147 		term.line[orig] = temp;
   1148 	}
   1149 
   1150 	if (term.scr > 0 && term.scr < HISTSIZE)
   1151 		term.scr = MIN(term.scr + n, HISTSIZE-1);
   1152 
   1153 	tclearregion(0, orig, term.col-1, orig+n-1);
   1154 	tsetdirt(orig+n, term.bot);
   1155 
   1156 	for (i = orig; i <= term.bot-n; i++) {
   1157 		temp = term.line[i];
   1158 		term.line[i] = term.line[i+n];
   1159 		term.line[i+n] = temp;
   1160 	}
   1161 
   1162 	if (term.scr == 0)
   1163 		selscroll(orig, -n);
   1164 }
   1165 
   1166 void
   1167 selscroll(int orig, int n)
   1168 {
   1169 	if (sel.ob.x == -1)
   1170 		return;
   1171 
   1172 	if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) {
   1173 		selclear();
   1174 	} else if (BETWEEN(sel.nb.y, orig, term.bot)) {
   1175 		sel.ob.y += n;
   1176 		sel.oe.y += n;
   1177 		if (sel.ob.y < term.top || sel.ob.y > term.bot ||
   1178 		    sel.oe.y < term.top || sel.oe.y > term.bot) {
   1179 			selclear();
   1180 		} else {
   1181 			selnormalize();
   1182 		}
   1183 	}
   1184 }
   1185 
   1186 void
   1187 tnewline(int first_col)
   1188 {
   1189 	int y = term.c.y;
   1190 
   1191 	if (y == term.bot) {
   1192 		tscrollup(term.top, 1, 1);
   1193 	} else {
   1194 		y++;
   1195 	}
   1196 	tmoveto(first_col ? 0 : term.c.x, y);
   1197 }
   1198 
   1199 void
   1200 csiparse(void)
   1201 {
   1202 	char *p = csiescseq.buf, *np;
   1203 	long int v;
   1204 
   1205 	csiescseq.narg = 0;
   1206 	if (*p == '?') {
   1207 		csiescseq.priv = 1;
   1208 		p++;
   1209 	}
   1210 
   1211 	csiescseq.buf[csiescseq.len] = '\0';
   1212 	while (p < csiescseq.buf+csiescseq.len) {
   1213 		np = NULL;
   1214 		v = strtol(p, &np, 10);
   1215 		if (np == p)
   1216 			v = 0;
   1217 		if (v == LONG_MAX || v == LONG_MIN)
   1218 			v = -1;
   1219 		csiescseq.arg[csiescseq.narg++] = v;
   1220 		p = np;
   1221 		if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ)
   1222 			break;
   1223 		p++;
   1224 	}
   1225 	csiescseq.mode[0] = *p++;
   1226 	csiescseq.mode[1] = (p < csiescseq.buf+csiescseq.len) ? *p : '\0';
   1227 }
   1228 
   1229 /* for absolute user moves, when decom is set */
   1230 void
   1231 tmoveato(int x, int y)
   1232 {
   1233 	tmoveto(x, y + ((term.c.state & CURSOR_ORIGIN) ? term.top: 0));
   1234 }
   1235 
   1236 void
   1237 tmoveto(int x, int y)
   1238 {
   1239 	int miny, maxy;
   1240 
   1241 	if (term.c.state & CURSOR_ORIGIN) {
   1242 		miny = term.top;
   1243 		maxy = term.bot;
   1244 	} else {
   1245 		miny = 0;
   1246 		maxy = term.row - 1;
   1247 	}
   1248 	term.c.state &= ~CURSOR_WRAPNEXT;
   1249 	term.c.x = LIMIT(x, 0, term.col-1);
   1250 	term.c.y = LIMIT(y, miny, maxy);
   1251 }
   1252 
   1253 void
   1254 tsetchar(Rune u, const Glyph *attr, int x, int y)
   1255 {
   1256 	static const char *vt100_0[62] = { /* 0x41 - 0x7e */
   1257 		"↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */
   1258 		0, 0, 0, 0, 0, 0, 0, 0, /* H - O */
   1259 		0, 0, 0, 0, 0, 0, 0, 0, /* P - W */
   1260 		0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */
   1261 		"◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */
   1262 		"␤", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */
   1263 		"⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */
   1264 		"│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */
   1265 	};
   1266 
   1267 	/*
   1268 	 * The table is proudly stolen from rxvt.
   1269 	 */
   1270 	if (term.trantbl[term.charset] == CS_GRAPHIC0 &&
   1271 	   BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41])
   1272 		utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ);
   1273 
   1274 	if (term.line[y][x].mode & ATTR_WIDE) {
   1275 		if (x+1 < term.col) {
   1276 			term.line[y][x+1].u = ' ';
   1277 			term.line[y][x+1].mode &= ~ATTR_WDUMMY;
   1278 		}
   1279 	} else if (term.line[y][x].mode & ATTR_WDUMMY) {
   1280 		term.line[y][x-1].u = ' ';
   1281 		term.line[y][x-1].mode &= ~ATTR_WIDE;
   1282 	}
   1283 
   1284 	term.dirty[y] = 1;
   1285 	term.line[y][x] = *attr;
   1286 	term.line[y][x].u = u;
   1287 }
   1288 
   1289 void
   1290 tclearregion(int x1, int y1, int x2, int y2)
   1291 {
   1292 	int x, y, temp;
   1293 	Glyph *gp;
   1294 
   1295 	if (x1 > x2)
   1296 		temp = x1, x1 = x2, x2 = temp;
   1297 	if (y1 > y2)
   1298 		temp = y1, y1 = y2, y2 = temp;
   1299 
   1300 	LIMIT(x1, 0, term.col-1);
   1301 	LIMIT(x2, 0, term.col-1);
   1302 	LIMIT(y1, 0, term.row-1);
   1303 	LIMIT(y2, 0, term.row-1);
   1304 
   1305 	for (y = y1; y <= y2; y++) {
   1306 		term.dirty[y] = 1;
   1307 		for (x = x1; x <= x2; x++) {
   1308 			gp = &term.line[y][x];
   1309 			if (selected(x, y))
   1310 				selclear();
   1311 			gp->fg = term.c.attr.fg;
   1312 			gp->bg = term.c.attr.bg;
   1313 			gp->mode = 0;
   1314 			gp->u = ' ';
   1315 		}
   1316 	}
   1317 }
   1318 
   1319 void
   1320 tdeletechar(int n)
   1321 {
   1322 	int dst, src, size;
   1323 	Glyph *line;
   1324 
   1325 	LIMIT(n, 0, term.col - term.c.x);
   1326 
   1327 	dst = term.c.x;
   1328 	src = term.c.x + n;
   1329 	size = term.col - src;
   1330 	line = term.line[term.c.y];
   1331 
   1332 	memmove(&line[dst], &line[src], size * sizeof(Glyph));
   1333 	tclearregion(term.col-n, term.c.y, term.col-1, term.c.y);
   1334 }
   1335 
   1336 void
   1337 tinsertblank(int n)
   1338 {
   1339 	int dst, src, size;
   1340 	Glyph *line;
   1341 
   1342 	LIMIT(n, 0, term.col - term.c.x);
   1343 
   1344 	dst = term.c.x + n;
   1345 	src = term.c.x;
   1346 	size = term.col - dst;
   1347 	line = term.line[term.c.y];
   1348 
   1349 	memmove(&line[dst], &line[src], size * sizeof(Glyph));
   1350 	tclearregion(src, term.c.y, dst - 1, term.c.y);
   1351 }
   1352 
   1353 void
   1354 tinsertblankline(int n)
   1355 {
   1356 	if (BETWEEN(term.c.y, term.top, term.bot))
   1357 		tscrolldown(term.c.y, n, 0);
   1358 }
   1359 
   1360 void
   1361 tdeleteline(int n)
   1362 {
   1363 	if (BETWEEN(term.c.y, term.top, term.bot))
   1364 		tscrollup(term.c.y, n, 0);
   1365 }
   1366 
   1367 int32_t
   1368 tdefcolor(const int *attr, int *npar, int l)
   1369 {
   1370 	int32_t idx = -1;
   1371 	uint r, g, b;
   1372 
   1373 	switch (attr[*npar + 1]) {
   1374 	case 2: /* direct color in RGB space */
   1375 		if (*npar + 4 >= l) {
   1376 			fprintf(stderr,
   1377 				"erresc(38): Incorrect number of parameters (%d)\n",
   1378 				*npar);
   1379 			break;
   1380 		}
   1381 		r = attr[*npar + 2];
   1382 		g = attr[*npar + 3];
   1383 		b = attr[*npar + 4];
   1384 		*npar += 4;
   1385 		if (!BETWEEN(r, 0, 255) || !BETWEEN(g, 0, 255) || !BETWEEN(b, 0, 255))
   1386 			fprintf(stderr, "erresc: bad rgb color (%u,%u,%u)\n",
   1387 				r, g, b);
   1388 		else
   1389 			idx = TRUECOLOR(r, g, b);
   1390 		break;
   1391 	case 5: /* indexed color */
   1392 		if (*npar + 2 >= l) {
   1393 			fprintf(stderr,
   1394 				"erresc(38): Incorrect number of parameters (%d)\n",
   1395 				*npar);
   1396 			break;
   1397 		}
   1398 		*npar += 2;
   1399 		if (!BETWEEN(attr[*npar], 0, 255))
   1400 			fprintf(stderr, "erresc: bad fgcolor %d\n", attr[*npar]);
   1401 		else
   1402 			idx = attr[*npar];
   1403 		break;
   1404 	case 0: /* implemented defined (only foreground) */
   1405 	case 1: /* transparent */
   1406 	case 3: /* direct color in CMY space */
   1407 	case 4: /* direct color in CMYK space */
   1408 	default:
   1409 		fprintf(stderr,
   1410 		        "erresc(38): gfx attr %d unknown\n", attr[*npar]);
   1411 		break;
   1412 	}
   1413 
   1414 	return idx;
   1415 }
   1416 
   1417 void
   1418 tsetattr(const int *attr, int l)
   1419 {
   1420 	int i;
   1421 	int32_t idx;
   1422 
   1423 	for (i = 0; i < l; i++) {
   1424 		switch (attr[i]) {
   1425 		case 0:
   1426 			term.c.attr.mode &= ~(
   1427 				ATTR_BOLD       |
   1428 				ATTR_FAINT      |
   1429 				ATTR_ITALIC     |
   1430 				ATTR_UNDERLINE  |
   1431 				ATTR_BLINK      |
   1432 				ATTR_REVERSE    |
   1433 				ATTR_INVISIBLE  |
   1434 				ATTR_STRUCK     );
   1435 			term.c.attr.fg = defaultfg;
   1436 			term.c.attr.bg = defaultbg;
   1437 			break;
   1438 		case 1:
   1439 			term.c.attr.mode |= ATTR_BOLD;
   1440 			break;
   1441 		case 2:
   1442 			term.c.attr.mode |= ATTR_FAINT;
   1443 			break;
   1444 		case 3:
   1445 			term.c.attr.mode |= ATTR_ITALIC;
   1446 			break;
   1447 		case 4:
   1448 			term.c.attr.mode |= ATTR_UNDERLINE;
   1449 			break;
   1450 		case 5: /* slow blink */
   1451 			/* FALLTHROUGH */
   1452 		case 6: /* rapid blink */
   1453 			term.c.attr.mode |= ATTR_BLINK;
   1454 			break;
   1455 		case 7:
   1456 			term.c.attr.mode |= ATTR_REVERSE;
   1457 			break;
   1458 		case 8:
   1459 			term.c.attr.mode |= ATTR_INVISIBLE;
   1460 			break;
   1461 		case 9:
   1462 			term.c.attr.mode |= ATTR_STRUCK;
   1463 			break;
   1464 		case 22:
   1465 			term.c.attr.mode &= ~(ATTR_BOLD | ATTR_FAINT);
   1466 			break;
   1467 		case 23:
   1468 			term.c.attr.mode &= ~ATTR_ITALIC;
   1469 			break;
   1470 		case 24:
   1471 			term.c.attr.mode &= ~ATTR_UNDERLINE;
   1472 			break;
   1473 		case 25:
   1474 			term.c.attr.mode &= ~ATTR_BLINK;
   1475 			break;
   1476 		case 27:
   1477 			term.c.attr.mode &= ~ATTR_REVERSE;
   1478 			break;
   1479 		case 28:
   1480 			term.c.attr.mode &= ~ATTR_INVISIBLE;
   1481 			break;
   1482 		case 29:
   1483 			term.c.attr.mode &= ~ATTR_STRUCK;
   1484 			break;
   1485 		case 38:
   1486 			if ((idx = tdefcolor(attr, &i, l)) >= 0)
   1487 				term.c.attr.fg = idx;
   1488 			break;
   1489 		case 39:
   1490 			term.c.attr.fg = defaultfg;
   1491 			break;
   1492 		case 48:
   1493 			if ((idx = tdefcolor(attr, &i, l)) >= 0)
   1494 				term.c.attr.bg = idx;
   1495 			break;
   1496 		case 49:
   1497 			term.c.attr.bg = defaultbg;
   1498 			break;
   1499 		default:
   1500 			if (BETWEEN(attr[i], 30, 37)) {
   1501 				term.c.attr.fg = attr[i] - 30;
   1502 			} else if (BETWEEN(attr[i], 40, 47)) {
   1503 				term.c.attr.bg = attr[i] - 40;
   1504 			} else if (BETWEEN(attr[i], 90, 97)) {
   1505 				term.c.attr.fg = attr[i] - 90 + 8;
   1506 			} else if (BETWEEN(attr[i], 100, 107)) {
   1507 				term.c.attr.bg = attr[i] - 100 + 8;
   1508 			} else {
   1509 				fprintf(stderr,
   1510 					"erresc(default): gfx attr %d unknown\n",
   1511 					attr[i]);
   1512 				csidump();
   1513 			}
   1514 			break;
   1515 		}
   1516 	}
   1517 }
   1518 
   1519 void
   1520 tsetscroll(int t, int b)
   1521 {
   1522 	int temp;
   1523 
   1524 	LIMIT(t, 0, term.row-1);
   1525 	LIMIT(b, 0, term.row-1);
   1526 	if (t > b) {
   1527 		temp = t;
   1528 		t = b;
   1529 		b = temp;
   1530 	}
   1531 	term.top = t;
   1532 	term.bot = b;
   1533 }
   1534 
   1535 void
   1536 tsetmode(int priv, int set, const int *args, int narg)
   1537 {
   1538 	int alt; const int *lim;
   1539 
   1540 	for (lim = args + narg; args < lim; ++args) {
   1541 		if (priv) {
   1542 			switch (*args) {
   1543 			case 1: /* DECCKM -- Cursor key */
   1544 				xsetmode(set, MODE_APPCURSOR);
   1545 				break;
   1546 			case 5: /* DECSCNM -- Reverse video */
   1547 				xsetmode(set, MODE_REVERSE);
   1548 				break;
   1549 			case 6: /* DECOM -- Origin */
   1550 				MODBIT(term.c.state, set, CURSOR_ORIGIN);
   1551 				tmoveato(0, 0);
   1552 				break;
   1553 			case 7: /* DECAWM -- Auto wrap */
   1554 				MODBIT(term.mode, set, MODE_WRAP);
   1555 				break;
   1556 			case 0:  /* Error (IGNORED) */
   1557 			case 2:  /* DECANM -- ANSI/VT52 (IGNORED) */
   1558 			case 3:  /* DECCOLM -- Column  (IGNORED) */
   1559 			case 4:  /* DECSCLM -- Scroll (IGNORED) */
   1560 			case 8:  /* DECARM -- Auto repeat (IGNORED) */
   1561 			case 18: /* DECPFF -- Printer feed (IGNORED) */
   1562 			case 19: /* DECPEX -- Printer extent (IGNORED) */
   1563 			case 42: /* DECNRCM -- National characters (IGNORED) */
   1564 			case 12: /* att610 -- Start blinking cursor (IGNORED) */
   1565 				break;
   1566 			case 25: /* DECTCEM -- Text Cursor Enable Mode */
   1567 				xsetmode(!set, MODE_HIDE);
   1568 				break;
   1569 			case 9:    /* X10 mouse compatibility mode */
   1570 				xsetpointermotion(0);
   1571 				xsetmode(0, MODE_MOUSE);
   1572 				xsetmode(set, MODE_MOUSEX10);
   1573 				break;
   1574 			case 1000: /* 1000: report button press */
   1575 				xsetpointermotion(0);
   1576 				xsetmode(0, MODE_MOUSE);
   1577 				xsetmode(set, MODE_MOUSEBTN);
   1578 				break;
   1579 			case 1002: /* 1002: report motion on button press */
   1580 				xsetpointermotion(0);
   1581 				xsetmode(0, MODE_MOUSE);
   1582 				xsetmode(set, MODE_MOUSEMOTION);
   1583 				break;
   1584 			case 1003: /* 1003: enable all mouse motions */
   1585 				xsetpointermotion(set);
   1586 				xsetmode(0, MODE_MOUSE);
   1587 				xsetmode(set, MODE_MOUSEMANY);
   1588 				break;
   1589 			case 1004: /* 1004: send focus events to tty */
   1590 				xsetmode(set, MODE_FOCUS);
   1591 				break;
   1592 			case 1006: /* 1006: extended reporting mode */
   1593 				xsetmode(set, MODE_MOUSESGR);
   1594 				break;
   1595 			case 1034:
   1596 				xsetmode(set, MODE_8BIT);
   1597 				break;
   1598 			case 1049: /* swap screen & set/restore cursor as xterm */
   1599 				if (!allowaltscreen)
   1600 					break;
   1601 				tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
   1602 				/* FALLTHROUGH */
   1603 			case 47: /* swap screen */
   1604 			case 1047:
   1605 				if (!allowaltscreen)
   1606 					break;
   1607 				alt = IS_SET(MODE_ALTSCREEN);
   1608 				if (alt) {
   1609 					tclearregion(0, 0, term.col-1,
   1610 							term.row-1);
   1611 				}
   1612 				if (set ^ alt) /* set is always 1 or 0 */
   1613 					tswapscreen();
   1614 				if (*args != 1049)
   1615 					break;
   1616 				/* FALLTHROUGH */
   1617 			case 1048:
   1618 				tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
   1619 				break;
   1620 			case 2004: /* 2004: bracketed paste mode */
   1621 				xsetmode(set, MODE_BRCKTPASTE);
   1622 				break;
   1623 			/* Not implemented mouse modes. See comments there. */
   1624 			case 1001: /* mouse highlight mode; can hang the
   1625 				      terminal by design when implemented. */
   1626 			case 1005: /* UTF-8 mouse mode; will confuse
   1627 				      applications not supporting UTF-8
   1628 				      and luit. */
   1629 			case 1015: /* urxvt mangled mouse mode; incompatible
   1630 				      and can be mistaken for other control
   1631 				      codes. */
   1632 				break;
   1633 			default:
   1634 				fprintf(stderr,
   1635 					"erresc: unknown private set/reset mode %d\n",
   1636 					*args);
   1637 				break;
   1638 			}
   1639 		} else {
   1640 			switch (*args) {
   1641 			case 0:  /* Error (IGNORED) */
   1642 				break;
   1643 			case 2:
   1644 				xsetmode(set, MODE_KBDLOCK);
   1645 				break;
   1646 			case 4:  /* IRM -- Insertion-replacement */
   1647 				MODBIT(term.mode, set, MODE_INSERT);
   1648 				break;
   1649 			case 12: /* SRM -- Send/Receive */
   1650 				MODBIT(term.mode, !set, MODE_ECHO);
   1651 				break;
   1652 			case 20: /* LNM -- Linefeed/new line */
   1653 				MODBIT(term.mode, set, MODE_CRLF);
   1654 				break;
   1655 			default:
   1656 				fprintf(stderr,
   1657 					"erresc: unknown set/reset mode %d\n",
   1658 					*args);
   1659 				break;
   1660 			}
   1661 		}
   1662 	}
   1663 }
   1664 
   1665 void
   1666 csihandle(void)
   1667 {
   1668 	char buf[40];
   1669 	int len;
   1670 
   1671 	switch (csiescseq.mode[0]) {
   1672 	default:
   1673 	unknown:
   1674 		fprintf(stderr, "erresc: unknown csi ");
   1675 		csidump();
   1676 		/* die(""); */
   1677 		break;
   1678 	case '@': /* ICH -- Insert <n> blank char */
   1679 		DEFAULT(csiescseq.arg[0], 1);
   1680 		tinsertblank(csiescseq.arg[0]);
   1681 		break;
   1682 	case 'A': /* CUU -- Cursor <n> Up */
   1683 		DEFAULT(csiescseq.arg[0], 1);
   1684 		tmoveto(term.c.x, term.c.y-csiescseq.arg[0]);
   1685 		break;
   1686 	case 'B': /* CUD -- Cursor <n> Down */
   1687 	case 'e': /* VPR --Cursor <n> Down */
   1688 		DEFAULT(csiescseq.arg[0], 1);
   1689 		tmoveto(term.c.x, term.c.y+csiescseq.arg[0]);
   1690 		break;
   1691 	case 'i': /* MC -- Media Copy */
   1692 		switch (csiescseq.arg[0]) {
   1693 		case 0:
   1694 			tdump();
   1695 			break;
   1696 		case 1:
   1697 			tdumpline(term.c.y);
   1698 			break;
   1699 		case 2:
   1700 			tdumpsel();
   1701 			break;
   1702 		case 4:
   1703 			term.mode &= ~MODE_PRINT;
   1704 			break;
   1705 		case 5:
   1706 			term.mode |= MODE_PRINT;
   1707 			break;
   1708 		}
   1709 		break;
   1710 	case 'c': /* DA -- Device Attributes */
   1711 		if (csiescseq.arg[0] == 0)
   1712 			ttywrite(vtiden, strlen(vtiden), 0);
   1713 		break;
   1714 	case 'b': /* REP -- if last char is printable print it <n> more times */
   1715 		DEFAULT(csiescseq.arg[0], 1);
   1716 		if (term.lastc)
   1717 			while (csiescseq.arg[0]-- > 0)
   1718 				tputc(term.lastc);
   1719 		break;
   1720 	case 'C': /* CUF -- Cursor <n> Forward */
   1721 	case 'a': /* HPR -- Cursor <n> Forward */
   1722 		DEFAULT(csiescseq.arg[0], 1);
   1723 		tmoveto(term.c.x+csiescseq.arg[0], term.c.y);
   1724 		break;
   1725 	case 'D': /* CUB -- Cursor <n> Backward */
   1726 		DEFAULT(csiescseq.arg[0], 1);
   1727 		tmoveto(term.c.x-csiescseq.arg[0], term.c.y);
   1728 		break;
   1729 	case 'E': /* CNL -- Cursor <n> Down and first col */
   1730 		DEFAULT(csiescseq.arg[0], 1);
   1731 		tmoveto(0, term.c.y+csiescseq.arg[0]);
   1732 		break;
   1733 	case 'F': /* CPL -- Cursor <n> Up and first col */
   1734 		DEFAULT(csiescseq.arg[0], 1);
   1735 		tmoveto(0, term.c.y-csiescseq.arg[0]);
   1736 		break;
   1737 	case 'g': /* TBC -- Tabulation clear */
   1738 		switch (csiescseq.arg[0]) {
   1739 		case 0: /* clear current tab stop */
   1740 			term.tabs[term.c.x] = 0;
   1741 			break;
   1742 		case 3: /* clear all the tabs */
   1743 			memset(term.tabs, 0, term.col * sizeof(*term.tabs));
   1744 			break;
   1745 		default:
   1746 			goto unknown;
   1747 		}
   1748 		break;
   1749 	case 'G': /* CHA -- Move to <col> */
   1750 	case '`': /* HPA */
   1751 		DEFAULT(csiescseq.arg[0], 1);
   1752 		tmoveto(csiescseq.arg[0]-1, term.c.y);
   1753 		break;
   1754 	case 'H': /* CUP -- Move to <row> <col> */
   1755 	case 'f': /* HVP */
   1756 		DEFAULT(csiescseq.arg[0], 1);
   1757 		DEFAULT(csiescseq.arg[1], 1);
   1758 		tmoveato(csiescseq.arg[1]-1, csiescseq.arg[0]-1);
   1759 		break;
   1760 	case 'I': /* CHT -- Cursor Forward Tabulation <n> tab stops */
   1761 		DEFAULT(csiescseq.arg[0], 1);
   1762 		tputtab(csiescseq.arg[0]);
   1763 		break;
   1764 	case 'J': /* ED -- Clear screen */
   1765 		switch (csiescseq.arg[0]) {
   1766 		case 0: /* below */
   1767 			tclearregion(term.c.x, term.c.y, term.col-1, term.c.y);
   1768 			if (term.c.y < term.row-1) {
   1769 				tclearregion(0, term.c.y+1, term.col-1,
   1770 						term.row-1);
   1771 			}
   1772 			break;
   1773 		case 1: /* above */
   1774 			if (term.c.y > 1)
   1775 				tclearregion(0, 0, term.col-1, term.c.y-1);
   1776 			tclearregion(0, term.c.y, term.c.x, term.c.y);
   1777 			break;
   1778 		case 2: /* all */
   1779 			tclearregion(0, 0, term.col-1, term.row-1);
   1780 			break;
   1781 		default:
   1782 			goto unknown;
   1783 		}
   1784 		break;
   1785 	case 'K': /* EL -- Clear line */
   1786 		switch (csiescseq.arg[0]) {
   1787 		case 0: /* right */
   1788 			tclearregion(term.c.x, term.c.y, term.col-1,
   1789 					term.c.y);
   1790 			break;
   1791 		case 1: /* left */
   1792 			tclearregion(0, term.c.y, term.c.x, term.c.y);
   1793 			break;
   1794 		case 2: /* all */
   1795 			tclearregion(0, term.c.y, term.col-1, term.c.y);
   1796 			break;
   1797 		}
   1798 		break;
   1799 	case 'S': /* SU -- Scroll <n> line up */
   1800 		DEFAULT(csiescseq.arg[0], 1);
   1801 		tscrollup(term.top, csiescseq.arg[0], 0);
   1802 		break;
   1803 	case 'T': /* SD -- Scroll <n> line down */
   1804 		DEFAULT(csiescseq.arg[0], 1);
   1805 		tscrolldown(term.top, csiescseq.arg[0], 0);
   1806 		break;
   1807 	case 'L': /* IL -- Insert <n> blank lines */
   1808 		DEFAULT(csiescseq.arg[0], 1);
   1809 		tinsertblankline(csiescseq.arg[0]);
   1810 		break;
   1811 	case 'l': /* RM -- Reset Mode */
   1812 		tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg);
   1813 		break;
   1814 	case 'M': /* DL -- Delete <n> lines */
   1815 		DEFAULT(csiescseq.arg[0], 1);
   1816 		tdeleteline(csiescseq.arg[0]);
   1817 		break;
   1818 	case 'X': /* ECH -- Erase <n> char */
   1819 		DEFAULT(csiescseq.arg[0], 1);
   1820 		tclearregion(term.c.x, term.c.y,
   1821 				term.c.x + csiescseq.arg[0] - 1, term.c.y);
   1822 		break;
   1823 	case 'P': /* DCH -- Delete <n> char */
   1824 		DEFAULT(csiescseq.arg[0], 1);
   1825 		tdeletechar(csiescseq.arg[0]);
   1826 		break;
   1827 	case 'Z': /* CBT -- Cursor Backward Tabulation <n> tab stops */
   1828 		DEFAULT(csiescseq.arg[0], 1);
   1829 		tputtab(-csiescseq.arg[0]);
   1830 		break;
   1831 	case 'd': /* VPA -- Move to <row> */
   1832 		DEFAULT(csiescseq.arg[0], 1);
   1833 		tmoveato(term.c.x, csiescseq.arg[0]-1);
   1834 		break;
   1835 	case 'h': /* SM -- Set terminal mode */
   1836 		tsetmode(csiescseq.priv, 1, csiescseq.arg, csiescseq.narg);
   1837 		break;
   1838 	case 'm': /* SGR -- Terminal attribute (color) */
   1839 		tsetattr(csiescseq.arg, csiescseq.narg);
   1840 		break;
   1841 	case 'n': /* DSR – Device Status Report (cursor position) */
   1842 		if (csiescseq.arg[0] == 6) {
   1843 			len = snprintf(buf, sizeof(buf), "\033[%i;%iR",
   1844 					term.c.y+1, term.c.x+1);
   1845 			ttywrite(buf, len, 0);
   1846 		}
   1847 		break;
   1848 	case 'r': /* DECSTBM -- Set Scrolling Region */
   1849 		if (csiescseq.priv) {
   1850 			goto unknown;
   1851 		} else {
   1852 			DEFAULT(csiescseq.arg[0], 1);
   1853 			DEFAULT(csiescseq.arg[1], term.row);
   1854 			tsetscroll(csiescseq.arg[0]-1, csiescseq.arg[1]-1);
   1855 			tmoveato(0, 0);
   1856 		}
   1857 		break;
   1858 	case 's': /* DECSC -- Save cursor position (ANSI.SYS) */
   1859 		tcursor(CURSOR_SAVE);
   1860 		break;
   1861 	case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */
   1862 		tcursor(CURSOR_LOAD);
   1863 		break;
   1864 	case ' ':
   1865 		switch (csiescseq.mode[1]) {
   1866 		case 'q': /* DECSCUSR -- Set Cursor Style */
   1867 			if (xsetcursor(csiescseq.arg[0]))
   1868 				goto unknown;
   1869 			break;
   1870 		default:
   1871 			goto unknown;
   1872 		}
   1873 		break;
   1874 	}
   1875 }
   1876 
   1877 void
   1878 csidump(void)
   1879 {
   1880 	size_t i;
   1881 	uint c;
   1882 
   1883 	fprintf(stderr, "ESC[");
   1884 	for (i = 0; i < csiescseq.len; i++) {
   1885 		c = csiescseq.buf[i] & 0xff;
   1886 		if (isprint(c)) {
   1887 			putc(c, stderr);
   1888 		} else if (c == '\n') {
   1889 			fprintf(stderr, "(\\n)");
   1890 		} else if (c == '\r') {
   1891 			fprintf(stderr, "(\\r)");
   1892 		} else if (c == 0x1b) {
   1893 			fprintf(stderr, "(\\e)");
   1894 		} else {
   1895 			fprintf(stderr, "(%02x)", c);
   1896 		}
   1897 	}
   1898 	putc('\n', stderr);
   1899 }
   1900 
   1901 void
   1902 csireset(void)
   1903 {
   1904 	memset(&csiescseq, 0, sizeof(csiescseq));
   1905 }
   1906 
   1907 void
   1908 osc4_color_response(int num)
   1909 {
   1910 	int n;
   1911 	char buf[32];
   1912 	unsigned char r, g, b;
   1913 
   1914 	if (xgetcolor(num, &r, &g, &b)) {
   1915 		fprintf(stderr, "erresc: failed to fetch osc4 color %d\n", num);
   1916 		return;
   1917 	}
   1918 
   1919 	n = snprintf(buf, sizeof buf, "\033]4;%d;rgb:%02x%02x/%02x%02x/%02x%02x\007",
   1920 		     num, r, r, g, g, b, b);
   1921 
   1922 	ttywrite(buf, n, 1);
   1923 }
   1924 
   1925 void
   1926 osc_color_response(int index, int num)
   1927 {
   1928 	int n;
   1929 	char buf[32];
   1930 	unsigned char r, g, b;
   1931 
   1932 	if (xgetcolor(index, &r, &g, &b)) {
   1933 		fprintf(stderr, "erresc: failed to fetch osc color %d\n", index);
   1934 		return;
   1935 	}
   1936 
   1937 	n = snprintf(buf, sizeof buf, "\033]%d;rgb:%02x%02x/%02x%02x/%02x%02x\007",
   1938 		     num, r, r, g, g, b, b);
   1939 
   1940 	ttywrite(buf, n, 1);
   1941 }
   1942 
   1943 void
   1944 strhandle(void)
   1945 {
   1946 	char *p = NULL, *dec;
   1947 	int j, narg, par;
   1948 
   1949 	term.esc &= ~(ESC_STR_END|ESC_STR);
   1950 	strparse();
   1951 	par = (narg = strescseq.narg) ? atoi(strescseq.args[0]) : 0;
   1952 
   1953 	switch (strescseq.type) {
   1954 	case ']': /* OSC -- Operating System Command */
   1955 		switch (par) {
   1956 		case 0:
   1957 			if (narg > 1) {
   1958 				xsettitle(strescseq.args[1]);
   1959 				xseticontitle(strescseq.args[1]);
   1960 			}
   1961 			return;
   1962 		case 1:
   1963 			if (narg > 1)
   1964 				xseticontitle(strescseq.args[1]);
   1965 			return;
   1966 		case 2:
   1967 			if (narg > 1)
   1968 				xsettitle(strescseq.args[1]);
   1969 			return;
   1970 		case 52:
   1971 			if (narg > 2 && allowwindowops) {
   1972 				dec = base64dec(strescseq.args[2]);
   1973 				if (dec) {
   1974 					xsetsel(dec);
   1975 					xclipcopy();
   1976 				} else {
   1977 					fprintf(stderr, "erresc: invalid base64\n");
   1978 				}
   1979 			}
   1980 			return;
   1981 		case 10:
   1982 			if (narg < 2)
   1983 				break;
   1984 
   1985 			p = strescseq.args[1];
   1986 
   1987 			if (!strcmp(p, "?"))
   1988 				osc_color_response(defaultfg, 10);
   1989 			else if (xsetcolorname(defaultfg, p))
   1990 				fprintf(stderr, "erresc: invalid foreground color: %s\n", p);
   1991 			else
   1992 				tfulldirt();
   1993 			return;
   1994 		case 11:
   1995 			if (narg < 2)
   1996 				break;
   1997 
   1998 			p = strescseq.args[1];
   1999 
   2000 			if (!strcmp(p, "?"))
   2001 				osc_color_response(defaultbg, 11);
   2002 			else if (xsetcolorname(defaultbg, p))
   2003 				fprintf(stderr, "erresc: invalid background color: %s\n", p);
   2004 			else
   2005 				tfulldirt();
   2006 			return;
   2007 		case 12:
   2008 			if (narg < 2)
   2009 				break;
   2010 
   2011 			p = strescseq.args[1];
   2012 
   2013 			if (!strcmp(p, "?"))
   2014 				osc_color_response(defaultcs, 12);
   2015 			else if (xsetcolorname(defaultcs, p))
   2016 				fprintf(stderr, "erresc: invalid cursor color: %s\n", p);
   2017 			else
   2018 				tfulldirt();
   2019 			return;
   2020 		case 4: /* color set */
   2021 			if (narg < 3)
   2022 				break;
   2023 			p = strescseq.args[2];
   2024 			/* FALLTHROUGH */
   2025 		case 104: /* color reset */
   2026 			j = (narg > 1) ? atoi(strescseq.args[1]) : -1;
   2027 
   2028 			if (p && !strcmp(p, "?"))
   2029 				osc4_color_response(j);
   2030 			else if (xsetcolorname(j, p)) {
   2031 				if (par == 104 && narg <= 1)
   2032 					return; /* color reset without parameter */
   2033 				fprintf(stderr, "erresc: invalid color j=%d, p=%s\n",
   2034 				        j, p ? p : "(null)");
   2035 			} else {
   2036 				/*
   2037 				 * TODO if defaultbg color is changed, borders
   2038 				 * are dirty
   2039 				 */
   2040 				tfulldirt();
   2041 			}
   2042 			return;
   2043 		}
   2044 		break;
   2045 	case 'k': /* old title set compatibility */
   2046 		xsettitle(strescseq.args[0]);
   2047 		return;
   2048 	case 'P': /* DCS -- Device Control String */
   2049 	case '_': /* APC -- Application Program Command */
   2050 	case '^': /* PM -- Privacy Message */
   2051 		return;
   2052 	}
   2053 
   2054 	fprintf(stderr, "erresc: unknown str ");
   2055 	strdump();
   2056 }
   2057 
   2058 void
   2059 strparse(void)
   2060 {
   2061 	int c;
   2062 	char *p = strescseq.buf;
   2063 
   2064 	strescseq.narg = 0;
   2065 	strescseq.buf[strescseq.len] = '\0';
   2066 
   2067 	if (*p == '\0')
   2068 		return;
   2069 
   2070 	while (strescseq.narg < STR_ARG_SIZ) {
   2071 		strescseq.args[strescseq.narg++] = p;
   2072 		while ((c = *p) != ';' && c != '\0')
   2073 			++p;
   2074 		if (c == '\0')
   2075 			return;
   2076 		*p++ = '\0';
   2077 	}
   2078 }
   2079 
   2080 void
   2081 strdump(void)
   2082 {
   2083 	size_t i;
   2084 	uint c;
   2085 
   2086 	fprintf(stderr, "ESC%c", strescseq.type);
   2087 	for (i = 0; i < strescseq.len; i++) {
   2088 		c = strescseq.buf[i] & 0xff;
   2089 		if (c == '\0') {
   2090 			putc('\n', stderr);
   2091 			return;
   2092 		} else if (isprint(c)) {
   2093 			putc(c, stderr);
   2094 		} else if (c == '\n') {
   2095 			fprintf(stderr, "(\\n)");
   2096 		} else if (c == '\r') {
   2097 			fprintf(stderr, "(\\r)");
   2098 		} else if (c == 0x1b) {
   2099 			fprintf(stderr, "(\\e)");
   2100 		} else {
   2101 			fprintf(stderr, "(%02x)", c);
   2102 		}
   2103 	}
   2104 	fprintf(stderr, "ESC\\\n");
   2105 }
   2106 
   2107 void
   2108 strreset(void)
   2109 {
   2110 	strescseq = (STREscape){
   2111 		.buf = xrealloc(strescseq.buf, STR_BUF_SIZ),
   2112 		.siz = STR_BUF_SIZ,
   2113 	};
   2114 }
   2115 
   2116 void
   2117 sendbreak(const Arg *arg)
   2118 {
   2119 	if (tcsendbreak(cmdfd, 0))
   2120 		perror("Error sending break");
   2121 }
   2122 
   2123 void
   2124 tprinter(char *s, size_t len)
   2125 {
   2126 	if (iofd != -1 && xwrite(iofd, s, len) < 0) {
   2127 		perror("Error writing to output file");
   2128 		close(iofd);
   2129 		iofd = -1;
   2130 	}
   2131 }
   2132 
   2133 void
   2134 toggleprinter(const Arg *arg)
   2135 {
   2136 	term.mode ^= MODE_PRINT;
   2137 }
   2138 
   2139 void
   2140 printscreen(const Arg *arg)
   2141 {
   2142 	tdump();
   2143 }
   2144 
   2145 void
   2146 printsel(const Arg *arg)
   2147 {
   2148 	tdumpsel();
   2149 }
   2150 
   2151 void
   2152 tdumpsel(void)
   2153 {
   2154 	char *ptr;
   2155 
   2156 	if ((ptr = getsel())) {
   2157 		tprinter(ptr, strlen(ptr));
   2158 		free(ptr);
   2159 	}
   2160 }
   2161 
   2162 void
   2163 tdumpline(int n)
   2164 {
   2165 	char buf[UTF_SIZ];
   2166 	const Glyph *bp, *end;
   2167 
   2168 	bp = &term.line[n][0];
   2169 	end = &bp[MIN(tlinelen(n), term.col) - 1];
   2170 	if (bp != end || bp->u != ' ') {
   2171 		for ( ; bp <= end; ++bp)
   2172 			tprinter(buf, utf8encode(bp->u, buf));
   2173 	}
   2174 	tprinter("\n", 1);
   2175 }
   2176 
   2177 void
   2178 tdump(void)
   2179 {
   2180 	int i;
   2181 
   2182 	for (i = 0; i < term.row; ++i)
   2183 		tdumpline(i);
   2184 }
   2185 
   2186 void
   2187 tputtab(int n)
   2188 {
   2189 	uint x = term.c.x;
   2190 
   2191 	if (n > 0) {
   2192 		while (x < term.col && n--)
   2193 			for (++x; x < term.col && !term.tabs[x]; ++x)
   2194 				/* nothing */ ;
   2195 	} else if (n < 0) {
   2196 		while (x > 0 && n++)
   2197 			for (--x; x > 0 && !term.tabs[x]; --x)
   2198 				/* nothing */ ;
   2199 	}
   2200 	term.c.x = LIMIT(x, 0, term.col-1);
   2201 }
   2202 
   2203 void
   2204 tdefutf8(char ascii)
   2205 {
   2206 	if (ascii == 'G')
   2207 		term.mode |= MODE_UTF8;
   2208 	else if (ascii == '@')
   2209 		term.mode &= ~MODE_UTF8;
   2210 }
   2211 
   2212 void
   2213 tdeftran(char ascii)
   2214 {
   2215 	static char cs[] = "0B";
   2216 	static int vcs[] = {CS_GRAPHIC0, CS_USA};
   2217 	char *p;
   2218 
   2219 	if ((p = strchr(cs, ascii)) == NULL) {
   2220 		fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii);
   2221 	} else {
   2222 		term.trantbl[term.icharset] = vcs[p - cs];
   2223 	}
   2224 }
   2225 
   2226 void
   2227 tdectest(char c)
   2228 {
   2229 	int x, y;
   2230 
   2231 	if (c == '8') { /* DEC screen alignment test. */
   2232 		for (x = 0; x < term.col; ++x) {
   2233 			for (y = 0; y < term.row; ++y)
   2234 				tsetchar('E', &term.c.attr, x, y);
   2235 		}
   2236 	}
   2237 }
   2238 
   2239 void
   2240 tstrsequence(uchar c)
   2241 {
   2242 	switch (c) {
   2243 	case 0x90:   /* DCS -- Device Control String */
   2244 		c = 'P';
   2245 		break;
   2246 	case 0x9f:   /* APC -- Application Program Command */
   2247 		c = '_';
   2248 		break;
   2249 	case 0x9e:   /* PM -- Privacy Message */
   2250 		c = '^';
   2251 		break;
   2252 	case 0x9d:   /* OSC -- Operating System Command */
   2253 		c = ']';
   2254 		break;
   2255 	}
   2256 	strreset();
   2257 	strescseq.type = c;
   2258 	term.esc |= ESC_STR;
   2259 }
   2260 
   2261 void
   2262 tcontrolcode(uchar ascii)
   2263 {
   2264 	switch (ascii) {
   2265 	case '\t':   /* HT */
   2266 		tputtab(1);
   2267 		return;
   2268 	case '\b':   /* BS */
   2269 		tmoveto(term.c.x-1, term.c.y);
   2270 		return;
   2271 	case '\r':   /* CR */
   2272 		tmoveto(0, term.c.y);
   2273 		return;
   2274 	case '\f':   /* LF */
   2275 	case '\v':   /* VT */
   2276 	case '\n':   /* LF */
   2277 		/* go to first col if the mode is set */
   2278 		tnewline(IS_SET(MODE_CRLF));
   2279 		return;
   2280 	case '\a':   /* BEL */
   2281 		if (term.esc & ESC_STR_END) {
   2282 			/* backwards compatibility to xterm */
   2283 			strhandle();
   2284 		} else {
   2285 			xbell();
   2286 		}
   2287 		break;
   2288 	case '\033': /* ESC */
   2289 		csireset();
   2290 		term.esc &= ~(ESC_CSI|ESC_ALTCHARSET|ESC_TEST);
   2291 		term.esc |= ESC_START;
   2292 		return;
   2293 	case '\016': /* SO (LS1 -- Locking shift 1) */
   2294 	case '\017': /* SI (LS0 -- Locking shift 0) */
   2295 		term.charset = 1 - (ascii - '\016');
   2296 		return;
   2297 	case '\032': /* SUB */
   2298 		tsetchar('?', &term.c.attr, term.c.x, term.c.y);
   2299 		/* FALLTHROUGH */
   2300 	case '\030': /* CAN */
   2301 		csireset();
   2302 		break;
   2303 	case '\005': /* ENQ (IGNORED) */
   2304 	case '\000': /* NUL (IGNORED) */
   2305 	case '\021': /* XON (IGNORED) */
   2306 	case '\023': /* XOFF (IGNORED) */
   2307 	case 0177:   /* DEL (IGNORED) */
   2308 		return;
   2309 	case 0x80:   /* TODO: PAD */
   2310 	case 0x81:   /* TODO: HOP */
   2311 	case 0x82:   /* TODO: BPH */
   2312 	case 0x83:   /* TODO: NBH */
   2313 	case 0x84:   /* TODO: IND */
   2314 		break;
   2315 	case 0x85:   /* NEL -- Next line */
   2316 		tnewline(1); /* always go to first col */
   2317 		break;
   2318 	case 0x86:   /* TODO: SSA */
   2319 	case 0x87:   /* TODO: ESA */
   2320 		break;
   2321 	case 0x88:   /* HTS -- Horizontal tab stop */
   2322 		term.tabs[term.c.x] = 1;
   2323 		break;
   2324 	case 0x89:   /* TODO: HTJ */
   2325 	case 0x8a:   /* TODO: VTS */
   2326 	case 0x8b:   /* TODO: PLD */
   2327 	case 0x8c:   /* TODO: PLU */
   2328 	case 0x8d:   /* TODO: RI */
   2329 	case 0x8e:   /* TODO: SS2 */
   2330 	case 0x8f:   /* TODO: SS3 */
   2331 	case 0x91:   /* TODO: PU1 */
   2332 	case 0x92:   /* TODO: PU2 */
   2333 	case 0x93:   /* TODO: STS */
   2334 	case 0x94:   /* TODO: CCH */
   2335 	case 0x95:   /* TODO: MW */
   2336 	case 0x96:   /* TODO: SPA */
   2337 	case 0x97:   /* TODO: EPA */
   2338 	case 0x98:   /* TODO: SOS */
   2339 	case 0x99:   /* TODO: SGCI */
   2340 		break;
   2341 	case 0x9a:   /* DECID -- Identify Terminal */
   2342 		ttywrite(vtiden, strlen(vtiden), 0);
   2343 		break;
   2344 	case 0x9b:   /* TODO: CSI */
   2345 	case 0x9c:   /* TODO: ST */
   2346 		break;
   2347 	case 0x90:   /* DCS -- Device Control String */
   2348 	case 0x9d:   /* OSC -- Operating System Command */
   2349 	case 0x9e:   /* PM -- Privacy Message */
   2350 	case 0x9f:   /* APC -- Application Program Command */
   2351 		tstrsequence(ascii);
   2352 		return;
   2353 	}
   2354 	/* only CAN, SUB, \a and C1 chars interrupt a sequence */
   2355 	term.esc &= ~(ESC_STR_END|ESC_STR);
   2356 }
   2357 
   2358 /*
   2359  * returns 1 when the sequence is finished and it hasn't to read
   2360  * more characters for this sequence, otherwise 0
   2361  */
   2362 int
   2363 eschandle(uchar ascii)
   2364 {
   2365 	switch (ascii) {
   2366 	case '[':
   2367 		term.esc |= ESC_CSI;
   2368 		return 0;
   2369 	case '#':
   2370 		term.esc |= ESC_TEST;
   2371 		return 0;
   2372 	case '%':
   2373 		term.esc |= ESC_UTF8;
   2374 		return 0;
   2375 	case 'P': /* DCS -- Device Control String */
   2376 	case '_': /* APC -- Application Program Command */
   2377 	case '^': /* PM -- Privacy Message */
   2378 	case ']': /* OSC -- Operating System Command */
   2379 	case 'k': /* old title set compatibility */
   2380 		tstrsequence(ascii);
   2381 		return 0;
   2382 	case 'n': /* LS2 -- Locking shift 2 */
   2383 	case 'o': /* LS3 -- Locking shift 3 */
   2384 		term.charset = 2 + (ascii - 'n');
   2385 		break;
   2386 	case '(': /* GZD4 -- set primary charset G0 */
   2387 	case ')': /* G1D4 -- set secondary charset G1 */
   2388 	case '*': /* G2D4 -- set tertiary charset G2 */
   2389 	case '+': /* G3D4 -- set quaternary charset G3 */
   2390 		term.icharset = ascii - '(';
   2391 		term.esc |= ESC_ALTCHARSET;
   2392 		return 0;
   2393 	case 'D': /* IND -- Linefeed */
   2394 		if (term.c.y == term.bot) {
   2395 			tscrollup(term.top, 1, 1);
   2396 		} else {
   2397 			tmoveto(term.c.x, term.c.y+1);
   2398 		}
   2399 		break;
   2400 	case 'E': /* NEL -- Next line */
   2401 		tnewline(1); /* always go to first col */
   2402 		break;
   2403 	case 'H': /* HTS -- Horizontal tab stop */
   2404 		term.tabs[term.c.x] = 1;
   2405 		break;
   2406 	case 'M': /* RI -- Reverse index */
   2407 		if (term.c.y == term.top) {
   2408 			tscrolldown(term.top, 1, 1);
   2409 		} else {
   2410 			tmoveto(term.c.x, term.c.y-1);
   2411 		}
   2412 		break;
   2413 	case 'Z': /* DECID -- Identify Terminal */
   2414 		ttywrite(vtiden, strlen(vtiden), 0);
   2415 		break;
   2416 	case 'c': /* RIS -- Reset to initial state */
   2417 		treset();
   2418 		resettitle();
   2419 		xloadcols();
   2420 		break;
   2421 	case '=': /* DECPAM -- Application keypad */
   2422 		xsetmode(1, MODE_APPKEYPAD);
   2423 		break;
   2424 	case '>': /* DECPNM -- Normal keypad */
   2425 		xsetmode(0, MODE_APPKEYPAD);
   2426 		break;
   2427 	case '7': /* DECSC -- Save Cursor */
   2428 		tcursor(CURSOR_SAVE);
   2429 		break;
   2430 	case '8': /* DECRC -- Restore Cursor */
   2431 		tcursor(CURSOR_LOAD);
   2432 		break;
   2433 	case '\\': /* ST -- String Terminator */
   2434 		if (term.esc & ESC_STR_END)
   2435 			strhandle();
   2436 		break;
   2437 	default:
   2438 		fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n",
   2439 			(uchar) ascii, isprint(ascii)? ascii:'.');
   2440 		break;
   2441 	}
   2442 	return 1;
   2443 }
   2444 
   2445 void
   2446 tputc(Rune u)
   2447 {
   2448 	char c[UTF_SIZ];
   2449 	int control;
   2450 	int width, len;
   2451 	Glyph *gp;
   2452 
   2453 	control = ISCONTROL(u);
   2454 	if (u < 127 || !IS_SET(MODE_UTF8)) {
   2455 		c[0] = u;
   2456 		width = len = 1;
   2457 	} else {
   2458 		len = utf8encode(u, c);
   2459 		if (!control && (width = wcwidth(u)) == -1)
   2460 			width = 1;
   2461 	}
   2462 
   2463 	if (IS_SET(MODE_PRINT))
   2464 		tprinter(c, len);
   2465 
   2466 	/*
   2467 	 * STR sequence must be checked before anything else
   2468 	 * because it uses all following characters until it
   2469 	 * receives a ESC, a SUB, a ST or any other C1 control
   2470 	 * character.
   2471 	 */
   2472 	if (term.esc & ESC_STR) {
   2473 		if (u == '\a' || u == 030 || u == 032 || u == 033 ||
   2474 		   ISCONTROLC1(u)) {
   2475 			term.esc &= ~(ESC_START|ESC_STR);
   2476 			term.esc |= ESC_STR_END;
   2477 			goto check_control_code;
   2478 		}
   2479 
   2480 		if (strescseq.len+len >= strescseq.siz) {
   2481 			/*
   2482 			 * Here is a bug in terminals. If the user never sends
   2483 			 * some code to stop the str or esc command, then st
   2484 			 * will stop responding. But this is better than
   2485 			 * silently failing with unknown characters. At least
   2486 			 * then users will report back.
   2487 			 *
   2488 			 * In the case users ever get fixed, here is the code:
   2489 			 */
   2490 			/*
   2491 			 * term.esc = 0;
   2492 			 * strhandle();
   2493 			 */
   2494 			if (strescseq.siz > (SIZE_MAX - UTF_SIZ) / 2)
   2495 				return;
   2496 			strescseq.siz *= 2;
   2497 			strescseq.buf = xrealloc(strescseq.buf, strescseq.siz);
   2498 		}
   2499 
   2500 		memmove(&strescseq.buf[strescseq.len], c, len);
   2501 		strescseq.len += len;
   2502 		return;
   2503 	}
   2504 
   2505 check_control_code:
   2506 	/*
   2507 	 * Actions of control codes must be performed as soon they arrive
   2508 	 * because they can be embedded inside a control sequence, and
   2509 	 * they must not cause conflicts with sequences.
   2510 	 */
   2511 	if (control) {
   2512 		tcontrolcode(u);
   2513 		/*
   2514 		 * control codes are not shown ever
   2515 		 */
   2516 		if (!term.esc)
   2517 			term.lastc = 0;
   2518 		return;
   2519 	} else if (term.esc & ESC_START) {
   2520 		if (term.esc & ESC_CSI) {
   2521 			csiescseq.buf[csiescseq.len++] = u;
   2522 			if (BETWEEN(u, 0x40, 0x7E)
   2523 					|| csiescseq.len >= \
   2524 					sizeof(csiescseq.buf)-1) {
   2525 				term.esc = 0;
   2526 				csiparse();
   2527 				csihandle();
   2528 			}
   2529 			return;
   2530 		} else if (term.esc & ESC_UTF8) {
   2531 			tdefutf8(u);
   2532 		} else if (term.esc & ESC_ALTCHARSET) {
   2533 			tdeftran(u);
   2534 		} else if (term.esc & ESC_TEST) {
   2535 			tdectest(u);
   2536 		} else {
   2537 			if (!eschandle(u))
   2538 				return;
   2539 			/* sequence already finished */
   2540 		}
   2541 		term.esc = 0;
   2542 		/*
   2543 		 * All characters which form part of a sequence are not
   2544 		 * printed
   2545 		 */
   2546 		return;
   2547 	}
   2548 	if (selected(term.c.x, term.c.y))
   2549 		selclear();
   2550 
   2551 	gp = &term.line[term.c.y][term.c.x];
   2552 	if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) {
   2553 		gp->mode |= ATTR_WRAP;
   2554 		tnewline(1);
   2555 		gp = &term.line[term.c.y][term.c.x];
   2556 	}
   2557 
   2558 	if (IS_SET(MODE_INSERT) && term.c.x+width < term.col)
   2559 		memmove(gp+width, gp, (term.col - term.c.x - width) * sizeof(Glyph));
   2560 
   2561 	if (term.c.x+width > term.col) {
   2562 		tnewline(1);
   2563 		gp = &term.line[term.c.y][term.c.x];
   2564 	}
   2565 
   2566 	tsetchar(u, &term.c.attr, term.c.x, term.c.y);
   2567 	term.lastc = u;
   2568 
   2569 	if (width == 2) {
   2570 		gp->mode |= ATTR_WIDE;
   2571 		if (term.c.x+1 < term.col) {
   2572 			if (gp[1].mode == ATTR_WIDE && term.c.x+2 < term.col) {
   2573 				gp[2].u = ' ';
   2574 				gp[2].mode &= ~ATTR_WDUMMY;
   2575 			}
   2576 			gp[1].u = '\0';
   2577 			gp[1].mode = ATTR_WDUMMY;
   2578 		}
   2579 	}
   2580 	if (term.c.x+width < term.col) {
   2581 		tmoveto(term.c.x+width, term.c.y);
   2582 	} else {
   2583 		term.c.state |= CURSOR_WRAPNEXT;
   2584 	}
   2585 }
   2586 
   2587 int
   2588 twrite(const char *buf, int buflen, int show_ctrl)
   2589 {
   2590 	int charsize;
   2591 	Rune u;
   2592 	int n;
   2593 
   2594 	for (n = 0; n < buflen; n += charsize) {
   2595 		if (IS_SET(MODE_UTF8)) {
   2596 			/* process a complete utf8 char */
   2597 			charsize = utf8decode(buf + n, &u, buflen - n);
   2598 			if (charsize == 0)
   2599 				break;
   2600 		} else {
   2601 			u = buf[n] & 0xFF;
   2602 			charsize = 1;
   2603 		}
   2604 		if (show_ctrl && ISCONTROL(u)) {
   2605 			if (u & 0x80) {
   2606 				u &= 0x7f;
   2607 				tputc('^');
   2608 				tputc('[');
   2609 			} else if (u != '\n' && u != '\r' && u != '\t') {
   2610 				u ^= 0x40;
   2611 				tputc('^');
   2612 			}
   2613 		}
   2614 		tputc(u);
   2615 	}
   2616 	return n;
   2617 }
   2618 
   2619 void
   2620 tresize(int col, int row)
   2621 {
   2622 	int i, j;
   2623 	int minrow = MIN(row, term.row);
   2624 	int mincol = MIN(col, term.col);
   2625 	int *bp;
   2626 	TCursor c;
   2627 
   2628 	if (col < 1 || row < 1) {
   2629 		fprintf(stderr,
   2630 		        "tresize: error resizing to %dx%d\n", col, row);
   2631 		return;
   2632 	}
   2633 
   2634 	/*
   2635 	 * slide screen to keep cursor where we expect it -
   2636 	 * tscrollup would work here, but we can optimize to
   2637 	 * memmove because we're freeing the earlier lines
   2638 	 */
   2639 	for (i = 0; i <= term.c.y - row; i++) {
   2640 		free(term.line[i]);
   2641 		free(term.alt[i]);
   2642 	}
   2643 	/* ensure that both src and dst are not NULL */
   2644 	if (i > 0) {
   2645 		memmove(term.line, term.line + i, row * sizeof(Line));
   2646 		memmove(term.alt, term.alt + i, row * sizeof(Line));
   2647 	}
   2648 	for (i += row; i < term.row; i++) {
   2649 		free(term.line[i]);
   2650 		free(term.alt[i]);
   2651 	}
   2652 
   2653 	/* resize to new height */
   2654 	term.line = xrealloc(term.line, row * sizeof(Line));
   2655 	term.alt  = xrealloc(term.alt,  row * sizeof(Line));
   2656 	term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
   2657 	term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
   2658 
   2659 	for (i = 0; i < HISTSIZE; i++) {
   2660 		term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph));
   2661 		for (j = mincol; j < col; j++) {
   2662 			term.hist[i][j] = term.c.attr;
   2663 			term.hist[i][j].u = ' ';
   2664 		}
   2665 	}
   2666 
   2667 	/* resize each row to new width, zero-pad if needed */
   2668 	for (i = 0; i < minrow; i++) {
   2669 		term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
   2670 		term.alt[i]  = xrealloc(term.alt[i],  col * sizeof(Glyph));
   2671 	}
   2672 
   2673 	/* allocate any new rows */
   2674 	for (/* i = minrow */; i < row; i++) {
   2675 		term.line[i] = xmalloc(col * sizeof(Glyph));
   2676 		term.alt[i] = xmalloc(col * sizeof(Glyph));
   2677 	}
   2678 	if (col > term.col) {
   2679 		bp = term.tabs + term.col;
   2680 
   2681 		memset(bp, 0, sizeof(*term.tabs) * (col - term.col));
   2682 		while (--bp > term.tabs && !*bp)
   2683 			/* nothing */ ;
   2684 		for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces)
   2685 			*bp = 1;
   2686 	}
   2687 	/* update terminal size */
   2688 	term.col = col;
   2689 	term.row = row;
   2690 	/* reset scrolling region */
   2691 	tsetscroll(0, row-1);
   2692 	/* make use of the LIMIT in tmoveto */
   2693 	tmoveto(term.c.x, term.c.y);
   2694 	/* Clearing both screens (it makes dirty all lines) */
   2695 	c = term.c;
   2696 	for (i = 0; i < 2; i++) {
   2697 		if (mincol < col && 0 < minrow) {
   2698 			tclearregion(mincol, 0, col - 1, minrow - 1);
   2699 		}
   2700 		if (0 < col && minrow < row) {
   2701 			tclearregion(0, minrow, col - 1, row - 1);
   2702 		}
   2703 		tswapscreen();
   2704 		tcursor(CURSOR_LOAD);
   2705 	}
   2706 	term.c = c;
   2707 }
   2708 
   2709 void
   2710 resettitle(void)
   2711 {
   2712 	xsettitle(NULL);
   2713 }
   2714 
   2715 void
   2716 drawregion(int x1, int y1, int x2, int y2)
   2717 {
   2718 	int y;
   2719 
   2720 	for (y = y1; y < y2; y++) {
   2721 		if (!term.dirty[y])
   2722 			continue;
   2723 
   2724 		term.dirty[y] = 0;
   2725 		xdrawline(TLINE(y), x1, y, x2);
   2726 	}
   2727 }
   2728 
   2729 void
   2730 draw(void)
   2731 {
   2732 	int cx = term.c.x, ocx = term.ocx, ocy = term.ocy;
   2733 
   2734 	if (!xstartdraw())
   2735 		return;
   2736 
   2737 	/* adjust cursor position */
   2738 	LIMIT(term.ocx, 0, term.col-1);
   2739 	LIMIT(term.ocy, 0, term.row-1);
   2740 	if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY)
   2741 		term.ocx--;
   2742 	if (term.line[term.c.y][cx].mode & ATTR_WDUMMY)
   2743 		cx--;
   2744 
   2745 	drawregion(0, 0, term.col, term.row);
   2746 	if (term.scr == 0)
   2747 		xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
   2748 				term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
   2749 	term.ocx = cx;
   2750 	term.ocy = term.c.y;
   2751 	xfinishdraw();
   2752 	if (ocx != term.ocx || ocy != term.ocy)
   2753 		xximspot(term.ocx, term.ocy);
   2754 }
   2755 
   2756 void
   2757 redraw(void)
   2758 {
   2759 	tfulldirt();
   2760 	draw();
   2761 }