st

suckless terminal - with my changes
git clone https://pi.duncano.de/git/st.git

st.c (56901B)


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