geadline(prompt) {
	local capacity = 64,
	      size = 0,
	      i = 0,
	      c,
	      a,
	      buf = galloc(capacity);
	if (prompt)
		putstr(prompt);
	if (buf == NULL)
		return NULL;
	[buf] = 0;

	loop {
		red &c;
		if ((c == 0xffff) | (c == 0x04)) {
			if ((size == 0) | (c == 0xffff)) {
				free(buf);
				return 0;
			}
		} else if (c == 0x1b) {
			// ESC code
			red &c; // skip [
			red &c; // value

			if ((c == 'A') & (size > 0)) {
				loop {
					if (i == 0)
						break;
					i = i - 1;
					esccode('D');
				}
			} else if ((c == 'B') & (i < size)) {
				loop {
					if (i >= size)
						break;
					i = i + 1;
					esccode('C');
				}
			} else if ((c == 'C') & (i < size)) {
				i = i + 1;
				esccode('C');
			} else if ((c == 'D') & (i > 0)) {
				i = i - 1;
				esccode('D');
			}
				wrt '\a';
		} else if (c == 0x7f) {
			if (i) {
				a = i - 1;
				loop {
					if (a >= size)
						break;
					[buf + a] = [buf + a + 1];
					a = a + 1;
				}
				size = size - 1;
				i = i - 1;
				[buf + size] = 0;

				wrt '\r';
				if (prompt)
					putstr(prompt);
				putstr(buf);
				wrt ' ';
				esccode('D');
				a = size - i;
				loop {
					if (a == 0)
						break;
					a = a - 1;
					esccode('D');
				}
			}
		} else {
			size = size + 1;
			if (size >= capacity) {
				buf = realloc(buf, capacity * 2);
				if (buf == NULL)
					return NULL;
				capacity = capacity * 2;
			}
			a = size - i - 1;
			loop {
				if ((a == 0xffff) | (a == 0))
					break;
				[buf + i + a] = [buf + i + a - 1];
				a = a - 1;
			}
			[buf + i] = c;
			[buf + size] = 0;
			putstr(buf + i);
			a = strlen(buf + i) - 1;
			loop {
				if ((a == 0xffff) | (a == 0))
					break;
				a = a - 1;
				esccode('D');
			}
			i = i + 1;
			if (c == '\n')
				return buf;
		}
	}
}

esccode(c) {
	wrt 0x1b;
	wrt '[';
	wrt c;
}