master xplshn/aruu / shared / libredline / redline.c
   1/* see license file for copyright and license details */
   2
   3#include "redline.h"
   4
   5#include <ctype.h>
   6#include <dirent.h>
   7#include <errno.h>
   8#include <fcntl.h>
   9#include <signal.h>
  10#include <stdint.h>
  11#include <stdio.h>
  12#include <stdlib.h>
  13#include <string.h>
  14#include <sys/ioctl.h>
  15#include <sys/stat.h>
  16#include <sys/types.h>
  17#include <termios.h>
  18#include <unistd.h>
  19
  20#define REDLINE_DEFAULT_HISTORY_MAX_LEN 100
  21#define REDLINE_MAX_LINE (1024*1024)
  22#define REDLINE_INITIAL_BUFLEN 4096
  23
  24#define ENTER 13
  25#define CTRL_A 1
  26#define CTRL_B 2
  27#define CTRL_C 3
  28#define CTRL_D 4
  29#define CTRL_E 5
  30#define CTRL_F 6
  31#define CTRL_H 8
  32#define CTRL_K 11
  33#define CTRL_L 12
  34#define CTRL_N 14
  35#define CTRL_P 16
  36#define CTRL_T 20
  37#define CTRL_U 21
  38#define CTRL_W 23
  39#define CTRL_Y 25
  40#define BACKSPACE 127
  41#define ESC 27
  42
  43struct redlineState {
  44	int in_completion;
  45	int ifd;
  46	int ofd;
  47	char *buf;
  48	size_t buflen;
  49	const char *prompt;
  50	size_t plen;
  51	size_t pos;
  52	size_t oldpos;
  53	size_t len;
  54	size_t cols;
  55	size_t oldrows;
  56	int oldrpos;
  57	int history_index;
  58};
  59
  60
  61struct abuf {
  62	char *b;
  63	int len;
  64};
  65
  66static struct termios orig_termios;
  67static int rawmode = 0;
  68static int mlmode = 0;
  69static int history_max_len = REDLINE_DEFAULT_HISTORY_MAX_LEN;
  70static int history_len = 0;
  71static char **history = NULL;
  72static char *kill_buffer = NULL;
  73static volatile sig_atomic_t winch_received = 0;
  74static struct sigaction orig_sigwinch;
  75
  76static void (*completionCallback)(const char *, struct redlineCompletions *) = NULL;
  77
  78/* return the number of bytes that compose the utf-8 character starting at c */
  79static int
  80utf8ByteLen(char c)
  81{
  82	unsigned char uc = (unsigned char)c;
  83	if ((uc & 0x80) == 0)    return 1;
  84	if ((uc & 0xE0) == 0xC0) return 2;
  85	if ((uc & 0xF0) == 0xE0) return 3;
  86	if ((uc & 0xF8) == 0xF0) return 4;
  87	return 1;
  88}
  89
  90/* decode character starting at s */
  91static uint32_t
  92utf8DecodeChar(const char *s, size_t *len)
  93{
  94	uint32_t cp = 0;
  95	int l = utf8ByteLen(*s);
  96	*len = l;
  97	if (l == 1) {
  98		cp = ((unsigned char)*s);
  99	} else if (l == 2) {
 100		cp = ((unsigned char)*s & 0x1F) << 6;
 101		cp |= ((unsigned char)*(s+1) & 0x3F);
 102	} else if (l == 3) {
 103		cp = ((unsigned char)*s & 0x0F) << 12;
 104		cp |= ((unsigned char)*(s+1) & 0x3F) << 6;
 105		cp |= ((unsigned char)*(s+2) & 0x3F);
 106	} else if (l == 4) {
 107		cp = ((unsigned char)*s & 0x07) << 18;
 108		cp |= ((unsigned char)*(s+1) & 0x3F) << 12;
 109		cp |= ((unsigned char)*(s+2) & 0x3F) << 6;
 110		cp |= ((unsigned char)*(s+3) & 0x3F);
 111	}
 112	return cp;
 113}
 114
 115static int
 116isZWJ(uint32_t cp)
 117{
 118	return cp == 0x200D;
 119}
 120
 121static int
 122isCombiningMark(uint32_t cp)
 123{
 124	return (cp >= 0x0300 && cp <= 0x036F) || (cp >= 0x1DC0 && cp <= 0x1DFF) ||
 125	       (cp >= 0x20D0 && cp <= 0x20FF) || (cp >= 0xFE20 && cp <= 0xFE2F);
 126}
 127
 128static int
 129isVariationSelector(uint32_t cp)
 130{
 131	return (cp >= 0xFE00 && cp <= 0xFE0F) || (cp >= 0xE0100 && cp <= 0xE01EF);
 132}
 133
 134static int
 135isSkinToneModifier(uint32_t cp)
 136{
 137	return cp >= 0x1F3FB && cp <= 0x1F3FF;
 138}
 139
 140
 141static int
 142isGraphemeExtend(uint32_t cp)
 143{
 144	return isCombiningMark(cp) || isVariationSelector(cp) || isSkinToneModifier(cp);
 145}
 146
 147/* decode character going backward from pos */
 148static uint32_t
 149utf8DecodePrev(const char *buf, size_t pos, size_t *cplen)
 150{
 151	size_t i = 1;
 152	while (pos >= i && i <= 4) {
 153		unsigned char uc = (unsigned char)buf[pos-i];
 154		if ((uc & 0x80) == 0) {
 155			if (i == 1) {
 156				*cplen = 1;
 157				return uc;
 158			}
 159			break;
 160		}
 161		if ((uc & 0xC0) == 0xC0) {
 162			int l = utf8ByteLen(buf[pos-i]);
 163			if ((size_t)l == i) {
 164				*cplen = i;
 165				return utf8DecodeChar(buf + pos - i, cplen);
 166			}
 167			break;
 168		}
 169		i++;
 170	}
 171	*cplen = 1;
 172	return (unsigned char)buf[pos-1];
 173}
 174
 175/* calculate width of utf-8 char pos */
 176static size_t
 177utf8PrevCharLen(const char *buf, size_t pos)
 178{
 179	size_t len = 0;
 180	size_t next_len = 0;
 181	uint32_t cp;
 182	if (pos == 0) return 0;
 183	cp = utf8DecodePrev(buf, pos, &len);
 184	pos -= len;
 185	while (pos > 0 && isGraphemeExtend(cp)) {
 186		cp = utf8DecodePrev(buf, pos, &next_len);
 187		len += next_len;
 188		pos -= next_len;
 189	}
 190	if (pos > 0 && isZWJ(cp)) {
 191		size_t j = utf8PrevCharLen(buf, pos);
 192		if (j > 0) len += j;
 193	}
 194	return len;
 195}
 196
 197/* calculate width of next utf-8 char */
 198static size_t
 199utf8NextCharLen(const char *buf, size_t pos, size_t len)
 200{
 201	size_t clen = 0;
 202	size_t offset = 0;
 203	uint32_t cp;
 204	if (pos >= len) return 0;
 205	cp = utf8DecodeChar(buf + pos, &clen);
 206	offset = clen;
 207	while (pos + offset < len) {
 208		size_t next_len = 0;
 209		uint32_t next_cp = utf8DecodeChar(buf + pos + offset, &next_len);
 210		if (isGraphemeExtend(next_cp)) {
 211			offset += next_len;
 212		} else if (isZWJ(cp)) {
 213			offset += next_len;
 214			cp = next_cp;
 215		} else {
 216			break;
 217		}
 218	}
 219	return offset;
 220}
 221
 222/* get columns needed to display char */
 223static int
 224utf8CharWidth(uint32_t cp)
 225{
 226	if (cp == 0) return 0;
 227	if (cp < 0x20 || (cp >= 0x7f && cp < 0xa0)) return 0;
 228	if ((cp >= 0x1100 && cp <= 0x115f) ||
 229	    (cp >= 0x2e80 && cp <= 0xa4cf && cp != 0x303f) ||
 230	    (cp >= 0xac00 && cp <= 0xd7a3) ||
 231	    (cp >= 0xf900 && cp <= 0xfaff) ||
 232	    (cp >= 0xfe10 && cp <= 0xfe19) ||
 233	    (cp >= 0xfe30 && cp <= 0xfe6f) ||
 234	    (cp >= 0xff00 && cp <= 0xff60) ||
 235	    (cp >= 0xffe0 && cp <= 0xffe6) ||
 236	    (cp >= 0x20000 && cp <= 0x2fffd) ||
 237	    (cp >= 0x30000 && cp <= 0x3fffd)) {
 238		return 2;
 239	}
 240	return 1;
 241}
 242
 243/* get ansi escape sequence length */
 244static size_t
 245ansiEscapeLen(const char *s, size_t len)
 246{
 247	size_t i = 0;
 248	if (len < 2 || s[0] != '\x1b' || s[1] != '[') return 0;
 249	i = 2;
 250	while (i < len) {
 251		char c = s[i];
 252		if ((c >= '0' && c <= '9') || c == ';' || c == '?' || c == '"') {
 253			i++;
 254		} else if (c >= 'A' && c <= 'Z') {
 255			return i + 1;
 256		} else if (c >= 'a' && c <= 'z') {
 257			return i + 1;
 258		} else {
 259			break;
 260		}
 261	}
 262	return 0;
 263}
 264
 265/* calculate width of string */
 266static size_t
 267utf8StrWidth(const char *s, size_t len)
 268{
 269	size_t width = 0;
 270	size_t i = 0;
 271	while (i < len) {
 272		size_t elen = ansiEscapeLen(s + i, len - i);
 273		if (elen > 0) {
 274			i += elen;
 275			continue;
 276		}
 277		size_t clen = 0;
 278		uint32_t cp = utf8DecodeChar(s + i, &clen);
 279		width += utf8CharWidth(cp);
 280		i += clen;
 281	}
 282	return width;
 283}
 284
 285/* get single character width */
 286static int
 287utf8SingleCharWidth(const char *s, size_t len)
 288{
 289	size_t clen = 0;
 290	uint32_t cp = utf8DecodeChar(s, &clen);
 291	(void)len;
 292	return utf8CharWidth(cp);
 293}
 294
 295static int
 296isUnsupportedTerm(void)
 297{
 298	char *term = getenv("TERM");
 299	int i;
 300	static char *unsupported[] = {"dumb", "cons25", "emacs", NULL};
 301	if (term == NULL) return 0;
 302	for (i = 0; unsupported[i]; i++) {
 303		if (strcasecmp(term, unsupported[i]) == 0)
 304			return 1;
 305	}
 306	return 0;
 307}
 308
 309static void
 310sigwinchHandler(int sig)
 311{
 312	(void)sig;
 313	winch_received = 1;
 314}
 315
 316static int
 317enableRawMode(int fd)
 318{
 319	struct termios raw;
 320	struct sigaction sa;
 321
 322	if (!isatty(STDIN_FILENO))
 323		return -1;
 324	if (tcgetattr(fd, &orig_termios) == -1)
 325		return -1;
 326
 327	raw = orig_termios;
 328	raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
 329	raw.c_oflag &= ~(OPOST);
 330	raw.c_cflag |= (CS8);
 331	raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
 332	raw.c_cc[VMIN] = 1;
 333	raw.c_cc[VTIME] = 0;
 334
 335	if (tcsetattr(fd, TCSAFLUSH, &raw) < 0)
 336		return -1;
 337
 338	rawmode = 1;
 339
 340	/* register sigwinch handler */
 341	sa.sa_handler = sigwinchHandler;
 342	sigemptyset(&sa.sa_mask);
 343	sa.sa_flags = 0;
 344	sigaction(SIGWINCH, &sa, &orig_sigwinch);
 345
 346	return 0;
 347}
 348
 349static void
 350disableRawMode(int fd)
 351{
 352	if (rawmode) {
 353		tcsetattr(fd, TCSAFLUSH, &orig_termios);
 354		sigaction(SIGWINCH, &orig_sigwinch, NULL);
 355		rawmode = 0;
 356	}
 357}
 358
 359static int
 360getCursorPosition(int ifd, int ofd)
 361{
 362	char buf[32];
 363	int cols, rows;
 364	unsigned int i = 0;
 365
 366	if (write(ofd, "\x1b[6n", 4) != 4) return -1;
 367
 368	while (i < sizeof(buf)-1) {
 369		if (read(ifd,buf+i,1) != 1) break;
 370		if (buf[i] == 'R') break;
 371		i++;
 372	}
 373	buf[i] = '\0';
 374
 375	if (buf[0] != 27 || buf[1] != '[') return -1;
 376	if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1;
 377	return cols;
 378}
 379
 380static int
 381getColumns(int ifd, int ofd)
 382{
 383	struct winsize ws;
 384	char *cols_env;
 385	int tty_fd;
 386	int cols = 0;
 387
 388	if (ioctl(ofd, TIOCGWINSZ, &ws) == 0 && ws.ws_col >= 20)
 389		return ws.ws_col;
 390	if (ioctl(ifd, TIOCGWINSZ, &ws) == 0 && ws.ws_col >= 20)
 391		return ws.ws_col;
 392	if (ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_col >= 20)
 393		return ws.ws_col;
 394	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_col >= 20)
 395		return ws.ws_col;
 396	if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_col >= 20)
 397		return ws.ws_col;
 398
 399	tty_fd = open("/dev/tty", O_RDWR | O_NOCTTY);
 400	if (tty_fd >= 0) {
 401		if (ioctl(tty_fd, TIOCGWINSZ, &ws) == 0 && ws.ws_col >= 20) {
 402			cols = ws.ws_col;
 403		}
 404		close(tty_fd);
 405		if (cols >= 20) return cols;
 406	}
 407
 408	cols_env = getenv("COLUMNS");
 409	if (cols_env) {
 410		cols = atoi(cols_env);
 411		if (cols >= 20) return cols;
 412	}
 413
 414	/* fallback to cursor position query */
 415	int start;
 416
 417	if (!isatty(ifd) || !isatty(ofd)) return 80;
 418
 419	start = getCursorPosition(ifd,ofd);
 420	if (start == -1) return 80;
 421
 422	if (write(ofd,"\x1b[999C",6) != 6) return 80;
 423	cols = getCursorPosition(ifd,ofd);
 424	if (cols == -1) return 80;
 425
 426	if (cols > start) {
 427		char seq[32];
 428		snprintf(seq,sizeof(seq),"\x1b[%dD",cols-start);
 429		if (write(ofd,seq,strlen(seq)) == -1) {}
 430	}
 431	if (cols < 20) return 80;
 432	return cols;
 433}
 434
 435static void
 436redlineBeep(void)
 437{
 438	fprintf(stderr, "\x7");
 439	fflush(stderr);
 440}
 441
 442static void
 443freeCompletions(struct redlineCompletions *lc)
 444{
 445	size_t i;
 446	if (lc->cvec) {
 447		for (i = 0; i < lc->len; i++) {
 448			free(lc->cvec[i]);
 449		}
 450		free(lc->cvec);
 451	}
 452}
 453
 454static size_t
 455longestCommonPrefix(struct redlineCompletions *lc)
 456{
 457	size_t i, j;
 458	if (lc->len == 0) return 0;
 459	for (i = 0; ; i++) {
 460		char c = lc->cvec[0][i];
 461		if (c == '\0') return i;
 462		for (j = 1; j < lc->len; j++) {
 463			if (lc->cvec[j][i] != c) {
 464				return i;
 465			}
 466		}
 467	}
 468}
 469
 470static void
 471printCompletions(struct redlineState *ls, struct redlineCompletions *lc)
 472{
 473	size_t max_len = 0;
 474	size_t i, j, k;
 475	size_t col_width, num_cols, num_rows;
 476	size_t sp, len, idx;
 477	const char *comp;
 478	const char *name;
 479
 480	for (i = 0; i < lc->len; i++) {
 481		comp = lc->cvec[i];
 482		sp = strlen(comp);
 483		if (sp > 0 && comp[sp-1] == ' ') {
 484			sp--;
 485		}
 486		while (sp > 0 && comp[sp-1] != ' ') {
 487			sp--;
 488		}
 489		len = strlen(comp + sp);
 490		if (len > 0 && (comp + sp)[len-1] == ' ') {
 491			len--;
 492		}
 493		if (len > max_len) {
 494			max_len = len;
 495		}
 496	}
 497
 498	col_width = max_len + 2;
 499	num_cols = ls->cols / col_width;
 500	if (num_cols == 0) num_cols = 1;
 501	num_rows = (lc->len + num_cols - 1) / num_cols;
 502
 503	if (write(ls->ofd, "\r\n", 2) == -1) {}
 504	for (i = 0; i < num_rows; i++) {
 505		for (j = 0; j < num_cols; j++) {
 506			idx = j * num_rows + i;
 507			if (idx < lc->len) {
 508				comp = lc->cvec[idx];
 509				sp = strlen(comp);
 510				if (sp > 0 && comp[sp-1] == ' ') {
 511					sp--;
 512				}
 513				while (sp > 0 && comp[sp-1] != ' ') {
 514					sp--;
 515				}
 516				name = comp + sp;
 517				len = strlen(name);
 518				if (len > 0 && name[len-1] == ' ') {
 519					len--;
 520				}
 521				if (write(ls->ofd, name, len) == -1) {}
 522				if (j < num_cols - 1) {
 523					for (k = len; k < col_width; k++) {
 524						if (write(ls->ofd, " ", 1) == -1) {}
 525					}
 526				}
 527			}
 528		}
 529		if (write(ls->ofd, "\r\n", 2) == -1) {}
 530	}
 531}
 532
 533static void
 534abInit(struct abuf *ab)
 535{
 536	ab->b = NULL;
 537	ab->len = 0;
 538}
 539
 540static void
 541abAppend(struct abuf *ab, const char *s, int len)
 542{
 543	char *new = realloc(ab->b,ab->len+len);
 544	if (new == NULL) return;
 545	memcpy(new+ab->len,s,len);
 546	ab->b = new;
 547	ab->len += len;
 548}
 549
 550static void
 551abFree(struct abuf *ab)
 552{
 553	free(ab->b);
 554}
 555
 556static void
 557refreshSingleLine(struct redlineState *l, int flags)
 558{
 559	char seq[64];
 560	size_t pwidth = utf8StrWidth(l->prompt, l->plen);
 561	int fd = l->ofd;
 562	char *buf = l->buf;
 563	size_t len = l->len;
 564	size_t pos = l->pos;
 565	size_t poscol;
 566	size_t lencol;
 567	struct abuf ab;
 568
 569	poscol = utf8StrWidth(buf, pos);
 570	lencol = utf8StrWidth(buf, len);
 571
 572	while (pwidth + poscol >= l->cols) {
 573		size_t clen = utf8NextCharLen(buf, 0, len);
 574		int cwidth = utf8SingleCharWidth(buf, clen);
 575		buf += clen;
 576		len -= clen;
 577		pos -= clen;
 578		poscol -= cwidth;
 579		lencol -= cwidth;
 580	}
 581
 582	while (pwidth + lencol > l->cols) {
 583		size_t clen = utf8PrevCharLen(buf, len);
 584		int cwidth = utf8SingleCharWidth(buf + len - clen, clen);
 585		len -= clen;
 586		lencol -= cwidth;
 587	}
 588
 589	abInit(&ab);
 590	snprintf(seq,sizeof(seq),"\r");
 591	abAppend(&ab,seq,strlen(seq));
 592
 593	if (flags & 1) {
 594		abAppend(&ab,l->prompt,l->plen);
 595		abAppend(&ab,buf,len);
 596	}
 597
 598	snprintf(seq,sizeof(seq),"\x1b[0K");
 599	abAppend(&ab,seq,strlen(seq));
 600
 601	if (flags & 1) {
 602		snprintf(seq,sizeof(seq),"\r\x1b[%dC", (int)(poscol+pwidth));
 603		abAppend(&ab,seq,strlen(seq));
 604	}
 605
 606	if (write(fd,ab.b,ab.len) == -1) {}
 607	abFree(&ab);
 608}
 609
 610static void
 611refreshMultiLine(struct redlineState *l, int flags)
 612{
 613	char seq[64];
 614	size_t pwidth = utf8StrWidth(l->prompt, l->plen);
 615	size_t bufwidth;
 616	size_t poswidth;
 617	int rows;
 618	int rpos2;
 619	int col;
 620	int old_rows = l->oldrows;
 621	int rpos = l->oldrpos;
 622	int fd = l->ofd, j;
 623	struct abuf ab;
 624
 625	(void)flags;
 626
 627	bufwidth = utf8StrWidth(l->buf, l->len);
 628	poswidth = utf8StrWidth(l->buf, l->pos);
 629	rows = (pwidth+bufwidth+l->cols-1)/l->cols;
 630	l->oldrows = rows;
 631
 632	abInit(&ab);
 633
 634	/* move cursor up to the first row, column 0 of the input area */
 635	if (rpos > 1) {
 636		snprintf(seq, 64, "\r\x1b[%dA", rpos - 1);
 637		abAppend(&ab, seq, strlen(seq));
 638	} else {
 639		abAppend(&ab, "\r", 1);
 640	}
 641
 642	/* clear all old rows */
 643	for (j = 0; j < old_rows; j++) {
 644		abAppend(&ab, "\x1b[0K", 4);
 645		if (j < old_rows - 1) {
 646			abAppend(&ab, "\n\r", 2);
 647		}
 648	}
 649
 650	/* move cursor back to the first row, column 0 */
 651	if (old_rows > 1) {
 652		snprintf(seq, 64, "\r\x1b[%dA", old_rows - 1);
 653		abAppend(&ab, seq, strlen(seq));
 654	} else {
 655		abAppend(&ab, "\r", 1);
 656	}
 657
 658	/* print prompt and new buffer */
 659	abAppend(&ab, l->prompt, l->plen);
 660	abAppend(&ab, l->buf, l->len);
 661
 662	/* if cursor is at the end of the line and wraps, print a newline */
 663	if (l->pos && l->pos == l->len && (poswidth+pwidth) % l->cols == 0) {
 664		abAppend(&ab, "\n\r", 2);
 665		rows++;
 666		if (rows > (int)l->oldrows) l->oldrows = rows;
 667	}
 668
 669	/* calculate cursor row and column */
 670	rpos2 = (pwidth+poswidth+l->cols)/l->cols;
 671	col = (pwidth+poswidth) % l->cols;
 672
 673	/* move cursor to the correct row and column */
 674	if (rows - rpos2 > 0) {
 675		snprintf(seq, 64, "\x1b[%dA", rows - rpos2);
 676		abAppend(&ab, seq, strlen(seq));
 677	}
 678	if (col) {
 679		snprintf(seq, 64, "\r\x1b[%dC", col);
 680		abAppend(&ab, seq, strlen(seq));
 681	} else {
 682		abAppend(&ab, "\r", 1);
 683	}
 684
 685	l->oldpos = l->pos;
 686	l->oldrpos = rpos2;
 687
 688	if (write(fd, ab.b, ab.len) == -1) {}
 689	abFree(&ab);
 690}
 691
 692static void
 693refreshLineWithFlags(struct redlineState *l, int flags)
 694{
 695	if (mlmode)
 696		refreshMultiLine(l,flags);
 697	else
 698		refreshSingleLine(l,flags);
 699}
 700
 701static void
 702refreshLine(struct redlineState *l)
 703{
 704	refreshLineWithFlags(l, 1);
 705}
 706
 707static int
 708completeLine(struct redlineState *ls, int keypressed)
 709{
 710	struct redlineCompletions lc = { 0, NULL };
 711	size_t lcp_len;
 712	int c = keypressed;
 713	int proceed = 1;
 714	char query[128];
 715	char answer = 0;
 716
 717	if (c != 9) {
 718		ls->in_completion = 0;
 719		return c;
 720	}
 721
 722	completionCallback(ls->buf, &lc);
 723	if (lc.len == 0) {
 724		redlineBeep();
 725		ls->in_completion = 0;
 726		c = 0;
 727	} else if (lc.len == 1) {
 728		size_t nwritten = snprintf(ls->buf, ls->buflen, "%s", lc.cvec[0]);
 729		ls->len = ls->pos = nwritten;
 730		refreshLine(ls);
 731		ls->in_completion = 0;
 732		c = 0;
 733	} else {
 734		lcp_len = longestCommonPrefix(&lc);
 735		if (lcp_len > ls->len) {
 736			size_t nwritten = snprintf(ls->buf, ls->buflen, "%.*s", (int)lcp_len, lc.cvec[0]);
 737			ls->len = ls->pos = nwritten;
 738			refreshLine(ls);
 739			ls->in_completion = 1;
 740			c = 0;
 741		} else {
 742			/* prefix cannot be expanded further */
 743			if (ls->in_completion == 0) {
 744				/* first tab: beep and wait for the second tab */
 745				redlineBeep();
 746				ls->in_completion = 1;
 747				c = 0;
 748			} else {
 749				/* second tab: display possibilities */
 750				if (lc.len > 100) {
 751					snprintf(query, sizeof(query), "\r\nDisplay all %d possibilities? (y or n) ", (int)lc.len);
 752					if (write(ls->ofd, query, strlen(query)) == -1) {}
 753					while (1) {
 754						if (read(ls->ifd, &answer, 1) != 1) {
 755							proceed = 0;
 756							break;
 757						}
 758						if (answer == 'y' || answer == 'Y' || answer == ' ' || answer == '\t') {
 759							proceed = 1;
 760							break;
 761						}
 762						if (answer == 'n' || answer == 'N' || answer == 27 || answer == 3 || answer == 4) {
 763							proceed = 0;
 764							break;
 765						}
 766						redlineBeep();
 767					}
 768				}
 769				if (proceed) {
 770					printCompletions(ls, &lc);
 771				} else {
 772					if (write(ls->ofd, "\r\n", 2) == -1) {}
 773				}
 774				ls->oldrows = 0;
 775				refreshLine(ls);
 776				ls->in_completion = 0;
 777				c = 0;
 778			}
 779		}
 780	}
 781
 782	freeCompletions(&lc);
 783	return c;
 784}
 785
 786static int
 787redlineEditInsert(struct redlineState *l, const char *c, int clen)
 788{
 789	if (l->len + clen >= l->buflen) {
 790		return 0;
 791	}
 792	if (l->len == l->pos) {
 793		memcpy(l->buf + l->pos, c, clen);
 794		l->pos += clen;
 795		l->len += clen;
 796		l->buf[l->len] = '\0';
 797		refreshLine(l);
 798	} else {
 799		memmove(l->buf + l->pos + clen, l->buf + l->pos, l->len - l->pos);
 800		memcpy(l->buf + l->pos, c, clen);
 801		l->pos += clen;
 802		l->len += clen;
 803		l->buf[l->len] = '\0';
 804		refreshLine(l);
 805	}
 806	return 1;
 807}
 808
 809static void
 810redlineEditBackspace(struct redlineState *l)
 811{
 812	if (l->pos > 0 && l->len > 0) {
 813		size_t clen = utf8PrevCharLen(l->buf, l->pos);
 814		memmove(l->buf+l->pos-clen, l->buf+l->pos, l->len-l->pos);
 815		l->pos -= clen;
 816		l->len -= clen;
 817		l->buf[l->len] = '\0';
 818		refreshLine(l);
 819	}
 820}
 821
 822static void
 823redlineEditDelete(struct redlineState *l)
 824{
 825	if (l->len > 0 && l->pos < l->len) {
 826		size_t clen = utf8NextCharLen(l->buf, l->pos, l->len);
 827		memmove(l->buf+l->pos, l->buf+l->pos+clen, l->len-l->pos-clen);
 828		l->len -= clen;
 829		l->buf[l->len] = '\0';
 830		refreshLine(l);
 831	}
 832}
 833
 834static void
 835redlineEditMoveLeft(struct redlineState *l)
 836{
 837	if (l->pos > 0) {
 838		l->pos -= utf8PrevCharLen(l->buf, l->pos);
 839		refreshLine(l);
 840	}
 841}
 842
 843static void
 844redlineEditMoveRight(struct redlineState *l)
 845{
 846	if (l->pos != l->len) {
 847		l->pos += utf8NextCharLen(l->buf, l->pos, l->len);
 848		refreshLine(l);
 849	}
 850}
 851
 852static void
 853redlineEditMoveHome(struct redlineState *l)
 854{
 855	if (l->pos != 0) {
 856		l->pos = 0;
 857		refreshLine(l);
 858	}
 859}
 860
 861static void
 862redlineEditMoveEnd(struct redlineState *l)
 863{
 864	if (l->pos != l->len) {
 865		l->pos = l->len;
 866		refreshLine(l);
 867	}
 868}
 869
 870static void
 871redlineEditMoveWordLeft(struct redlineState *l)
 872{
 873	if (l->pos > 0) {
 874		while (l->pos > 0 && l->buf[l->pos-1] == ' ')
 875			l->pos -= utf8PrevCharLen(l->buf, l->pos);
 876		while (l->pos > 0 && l->buf[l->pos-1] != ' ')
 877			l->pos -= utf8PrevCharLen(l->buf, l->pos);
 878		refreshLine(l);
 879	}
 880}
 881
 882static void
 883redlineEditMoveWordRight(struct redlineState *l)
 884{
 885	if (l->pos < l->len) {
 886		while (l->pos < l->len && l->buf[l->pos] == ' ')
 887			l->pos += utf8NextCharLen(l->buf, l->pos, l->len);
 888		while (l->pos < l->len && l->buf[l->pos] != ' ')
 889			l->pos += utf8NextCharLen(l->buf, l->pos, l->len);
 890		refreshLine(l);
 891	}
 892}
 893
 894static void
 895killBufferSave(const char *text, size_t len)
 896{
 897	free(kill_buffer);
 898	kill_buffer = malloc(len + 1);
 899	if (kill_buffer) {
 900		memcpy(kill_buffer, text, len);
 901		kill_buffer[len] = '\0';
 902	}
 903}
 904
 905static void
 906redlineEditDeleteWordRight(struct redlineState *l)
 907{
 908	size_t old_pos = l->pos;
 909	size_t diff;
 910	if (l->pos < l->len) {
 911		while (l->pos < l->len && l->buf[l->pos] == ' ')
 912			l->pos += utf8NextCharLen(l->buf, l->pos, l->len);
 913		while (l->pos < l->len && l->buf[l->pos] != ' ')
 914			l->pos += utf8NextCharLen(l->buf, l->pos, l->len);
 915		diff = l->pos - old_pos;
 916		l->pos = old_pos;
 917		killBufferSave(l->buf + l->pos, diff);
 918		memmove(l->buf + l->pos, l->buf + l->pos + diff, l->len - l->pos - diff + 1);
 919		l->len -= diff;
 920		refreshLine(l);
 921	}
 922}
 923
 924static void
 925redlineEditDeletePrevWord(struct redlineState *l)
 926{
 927	size_t old_pos = l->pos;
 928	size_t diff;
 929	if (l->pos > 0) {
 930		while (l->pos > 0 && l->buf[l->pos-1] == ' ')
 931			l->pos -= utf8PrevCharLen(l->buf, l->pos);
 932		while (l->pos > 0 && l->buf[l->pos-1] != ' ')
 933			l->pos -= utf8PrevCharLen(l->buf, l->pos);
 934		diff = old_pos - l->pos;
 935		killBufferSave(l->buf + l->pos, diff);
 936		memmove(l->buf + l->pos, l->buf + old_pos, l->len - old_pos + 1);
 937		l->len -= diff;
 938		refreshLine(l);
 939	}
 940}
 941
 942void
 943redlineHistoryAdd(const char *line)
 944{
 945	char *linecopy;
 946	if (history_max_len == 0) return;
 947	if (history == NULL) {
 948		history = malloc(sizeof(char*) * history_max_len);
 949		if (history == NULL) return;
 950		memset(history,0,sizeof(char*)*history_max_len);
 951	}
 952	if (history_len && strcmp(history[history_len-1], line) == 0) return;
 953	linecopy = strdup(line);
 954	if (!linecopy) return;
 955	if (history_len == history_max_len) {
 956		free(history[0]);
 957		memmove(history,history+1,sizeof(char*)*(history_max_len-1));
 958		history_len--;
 959	}
 960	history[history_len] = linecopy;
 961	history_len++;
 962}
 963
 964void
 965redlineHistorySetMaxLen(int len)
 966{
 967	char **new;
 968	if (len < 1) return;
 969	if (history) {
 970		int tocopy = history_len;
 971		new = malloc(sizeof(char*)*len);
 972		if (new == NULL) return;
 973		if (len < tocopy) {
 974			int j;
 975			for (j = 0; j < tocopy-len; j++) free(history[j]);
 976			tocopy = len;
 977		}
 978		memset(new,0,sizeof(char*)*len);
 979		memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy);
 980		free(history);
 981		history = new;
 982	}
 983	history_max_len = len;
 984	if (history_len > history_max_len)
 985		history_len = history_max_len;
 986}
 987
 988int
 989redlineHistorySave(const char *filename)
 990{
 991	mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
 992	FILE *fp;
 993	int j;
 994
 995	fp = fopen(filename,"w");
 996	umask(old_umask);
 997	if (fp == NULL) return -1;
 998	chmod(filename,S_IRUSR|S_IWUSR);
 999	for (j = 0; j < history_len; j++) {
1000		fprintf(fp,"%s\n",history[j]);
1001	}
1002	fclose(fp);
1003	return 0;
1004}
1005
1006int
1007redlineHistoryLoad(const char *filename)
1008{
1009	FILE *fp = fopen(filename,"r");
1010	char buf[REDLINE_INITIAL_BUFLEN];
1011	if (fp == NULL) return -1;
1012	while (fgets(buf,sizeof(buf),fp) != NULL) {
1013		char *p = strchr(buf,'\r');
1014		if (!p) p = strchr(buf,'\n');
1015		if (p) *p = '\0';
1016		redlineHistoryAdd(buf);
1017	}
1018	fclose(fp);
1019	return 0;
1020}
1021
1022char *
1023redlineHistoryGet(int idx)
1024{
1025	if (idx >= 0 && idx < history_len)
1026		return history[idx];
1027	return NULL;
1028}
1029
1030int
1031redlineHistoryLen(void)
1032{
1033	return history_len;
1034}
1035
1036static void
1037redlineEditHistoryNext(struct redlineState *l, int dir)
1038{
1039	if (history_len > 1) {
1040		const char *src;
1041		size_t len;
1042		free(history[history_len - 1 - l->history_index]);
1043		history[history_len - 1 - l->history_index] = strdup(l->buf);
1044		l->history_index += (dir == 1) ? 1 : -1;
1045		if (l->history_index < 0) {
1046			l->history_index = 0;
1047			return;
1048		} else if (l->history_index >= history_len) {
1049			l->history_index = history_len - 1;
1050			return;
1051		}
1052		src = history[history_len - 1 - l->history_index];
1053		len = strlen(src);
1054		if (len >= l->buflen) len = l->buflen - 1;
1055		memcpy(l->buf, src, len);
1056		l->buf[len] = '\0';
1057		l->len = l->pos = len;
1058		refreshLine(l);
1059	}
1060}
1061
1062static char *
1063redlineReadLine(FILE *fp)
1064{
1065	char *line = NULL;
1066	size_t len = 0, cap = 0;
1067	while (1) {
1068		if (len+1 >= cap) {
1069			size_t newcap = cap ? cap*2 : 16;
1070			char *new = realloc(line, newcap);
1071			if (!new) {
1072				free(line);
1073				return NULL;
1074			}
1075			line = new;
1076			cap = newcap;
1077		}
1078		int c = fgetc(fp);
1079		if (c == EOF || c == '\n') {
1080			if (c == EOF && len == 0) {
1081				free(line);
1082				return NULL;
1083			}
1084			line[len] = '\0';
1085			return line;
1086		}
1087		line[len++] = c;
1088	}
1089}
1090
1091static char *
1092redlineNoTTY(void)
1093{
1094	return redlineReadLine(stdin);
1095}
1096
1097void
1098redlineClearScreen(void)
1099{
1100	if (write(STDOUT_FILENO, "\x1b[H\x1b[2J", 7) == -1) {}
1101}
1102
1103static char *
1104redlineEditFeed(struct redlineState *l)
1105{
1106	char c;
1107	int nread;
1108	char seq[3];
1109	char param[8];
1110	size_t plen;
1111	char final;
1112	char p;
1113	int is_word_jump;
1114	char tmp[32];
1115	size_t prevlen;
1116	size_t currlen;
1117	size_t prevstart;
1118	char utf8[4];
1119	int utf8len;
1120	int i;
1121
1122	if (!isatty(l->ifd) && !getenv("REDLINE_ASSUME_TTY")) return redlineNoTTY();
1123
1124	while (1) {
1125		nread = read(l->ifd, &c, 1);
1126		if (nread < 0) {
1127			if (errno == EINTR) {
1128				if (winch_received) {
1129					winch_received = 0;
1130					l->cols = getColumns(l->ifd, l->ofd);
1131					refreshLine(l);
1132				}
1133				continue;
1134			}
1135			return (errno == EAGAIN || errno == EWOULDBLOCK) ? "more" : NULL;
1136		} else if (nread == 0) {
1137			return NULL;
1138		}
1139		break;
1140	}
1141
1142	if ((l->in_completion || c == 9) && completionCallback != NULL) {
1143		int retval = completeLine(l, c);
1144		if (retval == 0) return "more";
1145		c = retval;
1146	}
1147
1148	switch (c) {
1149	case ENTER:
1150		if (mlmode) redlineEditMoveEnd(l);
1151		return strdup(l->buf);
1152	case CTRL_C:
1153		errno = EAGAIN;
1154		return NULL;
1155	case BACKSPACE:
1156	case 8:
1157		redlineEditBackspace(l);
1158		break;
1159	case CTRL_D:
1160		if (l->len > 0) {
1161			redlineEditDelete(l);
1162		} else {
1163			errno = ENOENT;
1164			return NULL;
1165		}
1166		break;
1167	case CTRL_T:
1168		if (l->pos > 0 && l->pos < l->len) {
1169			prevlen = utf8PrevCharLen(l->buf, l->pos);
1170			currlen = utf8NextCharLen(l->buf, l->pos, l->len);
1171			prevstart = l->pos - prevlen;
1172			if (prevlen > sizeof(tmp) || currlen > sizeof(tmp)) break;
1173			memcpy(tmp, l->buf + l->pos, currlen);
1174			memmove(l->buf + prevstart + currlen, l->buf + prevstart, prevlen);
1175			memcpy(l->buf + prevstart, tmp, currlen);
1176			if (l->pos + currlen <= l->len) l->pos += currlen;
1177			refreshLine(l);
1178		}
1179		break;
1180	case CTRL_B:
1181		redlineEditMoveLeft(l);
1182		break;
1183	case CTRL_F:
1184		redlineEditMoveRight(l);
1185		break;
1186	case CTRL_P:
1187		redlineEditHistoryNext(l, 1);
1188		break;
1189	case CTRL_N:
1190		redlineEditHistoryNext(l, 0);
1191		break;
1192	case ESC:
1193		if (read(l->ifd, seq, 1) == -1) break;
1194		if (seq[0] == '[' || seq[0] == 'O') {
1195			if (read(l->ifd, seq+1, 1) == -1) break;
1196			if (seq[0] == '[') {
1197				if (seq[1] >= '0' && seq[1] <= '9') {
1198					plen = 1;
1199					final = 0;
1200					param[0] = seq[1];
1201					while (plen < sizeof(param)) {
1202						if (read(l->ifd, &p, 1) != 1) break;
1203						if ((p >= '0' && p <= '9') || p == ';') {
1204							param[plen++] = p;
1205						} else {
1206							final = p;
1207							break;
1208						}
1209					}
1210					if (final == '~') {
1211						if (plen == 1 && param[0] == '3') {
1212							redlineEditDelete(l);
1213						}
1214					} else if (final == 'D' || final == 'C') {
1215						is_word_jump = 0;
1216						if (plen == 3 && param[0] == '1' && param[1] == ';' && (param[2] == '5' || param[2] == '3')) {
1217							is_word_jump = 1;
1218						} else if (plen == 1 && (param[0] == '5' || param[0] == '3')) {
1219							is_word_jump = 1;
1220						}
1221						if (is_word_jump) {
1222							if (final == 'D') {
1223								redlineEditMoveWordLeft(l);
1224							} else {
1225								redlineEditMoveWordRight(l);
1226							}
1227						}
1228					}
1229				} else {
1230					switch (seq[1]) {
1231					case 'A':
1232						redlineEditHistoryNext(l, 1);
1233						break;
1234					case 'B':
1235						redlineEditHistoryNext(l, 0);
1236						break;
1237					case 'C':
1238						redlineEditMoveRight(l);
1239						break;
1240					case 'D':
1241						redlineEditMoveLeft(l);
1242						break;
1243					case 'H':
1244						redlineEditMoveHome(l);
1245						break;
1246					case 'F':
1247						redlineEditMoveEnd(l);
1248						break;
1249					}
1250				}
1251			} else if (seq[0] == 'O') {
1252				switch (seq[1]) {
1253				case 'H':
1254					redlineEditMoveHome(l);
1255					break;
1256				case 'F':
1257					redlineEditMoveEnd(l);
1258					break;
1259				}
1260			}
1261		} else {
1262			if (seq[0] == 'b' || seq[0] == 'B') {
1263				redlineEditMoveWordLeft(l);
1264			} else if (seq[0] == 'f' || seq[0] == 'F') {
1265				redlineEditMoveWordRight(l);
1266			} else if (seq[0] == 'd' || seq[0] == 'D') {
1267				redlineEditDeleteWordRight(l);
1268			} else if (seq[0] == 127 || seq[0] == 8) {
1269				redlineEditDeletePrevWord(l);
1270			}
1271		}
1272		break;
1273	default:
1274		utf8len = utf8ByteLen(c);
1275		utf8[0] = c;
1276		if (utf8len > 1) {
1277			for (i = 1; i < utf8len; i++) {
1278				if (read(l->ifd, utf8+i, 1) != 1) break;
1279			}
1280		}
1281		if (redlineEditInsert(l, utf8, utf8len) == 0) return NULL;
1282		break;
1283	case CTRL_U:
1284		killBufferSave(l->buf, l->pos);
1285		memmove(l->buf, l->buf + l->pos, l->len - l->pos + 1);
1286		l->len -= l->pos;
1287		l->pos = 0;
1288		refreshLine(l);
1289		break;
1290	case CTRL_K:
1291		killBufferSave(l->buf + l->pos, l->len - l->pos);
1292		l->buf[l->pos] = '\0';
1293		l->len = l->pos;
1294		refreshLine(l);
1295		break;
1296	case CTRL_A:
1297		redlineEditMoveHome(l);
1298		break;
1299	case CTRL_E:
1300		redlineEditMoveEnd(l);
1301		break;
1302	case CTRL_L:
1303		redlineClearScreen();
1304		refreshLine(l);
1305		break;
1306	case CTRL_W:
1307		redlineEditDeletePrevWord(l);
1308		break;
1309	case CTRL_Y:
1310		if (kill_buffer) {
1311			redlineEditInsert(l, kill_buffer, strlen(kill_buffer));
1312		}
1313		break;
1314	}
1315	return "more";
1316}
1317
1318static int
1319redlineEditStart(struct redlineState *l, int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt)
1320{
1321	l->in_completion = 0;
1322	l->ifd = stdin_fd;
1323	l->ofd = stdout_fd;
1324	l->buf = buf;
1325	l->buflen = buflen;
1326	l->prompt = prompt;
1327	l->plen = strlen(prompt);
1328	l->pos = 0;
1329	l->oldpos = 0;
1330	l->len = 0;
1331	l->cols = getColumns(stdin_fd, stdout_fd);
1332	l->oldrows = 0;
1333	l->oldrpos = 0;
1334	l->history_index = 0;
1335	l->buf[0] = '\0';
1336
1337	if (enableRawMode(l->ifd) == -1) return -1;
1338	refreshLine(l);
1339	return 0;
1340}
1341
1342static void
1343redlineEditStop(struct redlineState *l)
1344{
1345	if (!isatty(l->ifd) && !getenv("REDLINE_ASSUME_TTY")) return;
1346	disableRawMode(l->ifd);
1347	printf("\n");
1348}
1349
1350char *
1351redline(const char *prompt)
1352{
1353	struct redlineState l;
1354	char *buf;
1355	char *res;
1356
1357	if (!isatty(STDIN_FILENO) || isUnsupportedTerm()) {
1358		if (write(STDOUT_FILENO, prompt, strlen(prompt)) == -1) {}
1359		return redlineNoTTY();
1360	}
1361
1362	buf = malloc(REDLINE_INITIAL_BUFLEN);
1363	if (buf == NULL) return NULL;
1364	if (redlineEditStart(&l, STDIN_FILENO, STDOUT_FILENO, buf, REDLINE_INITIAL_BUFLEN, prompt) == -1) {
1365		free(buf);
1366		return NULL;
1367	}
1368	redlineHistoryAdd("");
1369	while (1) {
1370		res = redlineEditFeed(&l);
1371		if (res == NULL || strcmp(res, "more") != 0) {
1372			break;
1373		}
1374	}
1375	redlineEditStop(&l);
1376	if (history_len > 0) {
1377		history_len--;
1378		free(history[history_len]);
1379	}
1380	free(l.buf);
1381	return res;
1382}
1383
1384void
1385redlineSetCompletionCallback(void (*cb)(const char *, struct redlineCompletions *))
1386{
1387	completionCallback = cb;
1388}
1389
1390void
1391redlineAddCompletion(struct redlineCompletions *lc, const char *str)
1392{
1393	size_t len = strlen(str);
1394	char *copy, **cvec;
1395
1396	copy = malloc(len+1);
1397	if (copy == NULL) return;
1398	memcpy(copy,str,len+1);
1399	cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));
1400	if (cvec == NULL) {
1401		free(copy);
1402		return;
1403	}
1404	lc->cvec = cvec;
1405	lc->cvec[lc->len++] = copy;
1406}
1407
1408void
1409redlineSetMultiLine(int ml)
1410{
1411	mlmode = ml;
1412}