summaryrefslogblamecommitdiff
path: root/src/user/lib/libc/std/readline.c
blob: c2237b69907be59602ceb5c25666cf0adc5b48f1 (plain) (tree)







































                                                                                          
                                              

































































































                                                                                          
#include <readline.h>
#include <stdlib.h>

char* freadln(FILE f) {
	fprint(f, "\x1b[e");	// enable keyboard echo
	int i;

	char *p = (char*)malloc(256);
	char *b = p;

	while (1) {
		int l = read(f, 0, 255, b);
		if (l < 0) {
			free(b);
			return 0;
		}

		for (i = 0; i < l; i++) {
			if (b[i] == '\n') {
				b[i+1] = 0;
				return p;
			} else if (b[i] == 27) {		// ignore escape sequences
				b[i] = 0;
				l = i;
			}
		}

		int d = b - p + l;

		char* newp = (char*)malloc(d + 256);
		memcpy(newp, p, d);
		free(p);
		p = newp;
		b = p + d;
	}
}


// ** READLINE

char *freadline(FILE f, readline_history *h) {
	int i;
	fprint(f, "\x1b[h");	// disable keyboard echo

	if (h->str == 0) {
		h->str = (char**)malloc(h->max * sizeof(char*));
		for (i = 0; i < h->max; i++) h->str[i] = 0;
		h->n = 0;
	}

	int lid;
	if (h->n < h->max) {
		lid = h->n++;
	} else {
		free(h->str[0]);
		int i;
		for (i = 0; i < h->max - 1; i++) {
			h->str[i] = h->str[i+1];
		}
		lid = h->max - 1;
		h->str[lid] = 0;
	}

	if (h->str[lid] == 0) h->str[lid] = (char*)malloc(256);

	char *str = h->str[lid];
	str[0] = 0;
	char *cur = str;	// position in string
	int len = 0;		// string length

	while (1) {
		int finished = 0;

		int te = cur - str;

		char buf[16];
		int l = read(f, 0, 16, buf);
		if (l < 0) return 0;

		if (buf[0] == 27) {
			if (buf[1] == '[') {
				if (buf[2] == 'A') {			// up
					if (lid > 0) {
						lid--;
						str = h->str[lid];
						len = strlen(str);
						cur = str + len;
					}
				} else if (buf[2] == 'B') {		// down
					if (lid < h->n - 1) {
						lid++;
						str = h->str[lid];
						len = strlen(str);
						cur = str + len;
					}
				} else if (buf[2] == 'C') {		// forward (right)
					if (cur < str + len) cur++;
				} else if (buf[2] == 'D') {		// back (left)
					if (cur > str) cur--;
				}
			} else {
				// ALT sequence - ignore
			}
		} else if (buf[0] == '\n') {
			finished = 1;
			cur = str + len;
		} else if (buf[0] == '\b') {
			if (cur > str) {
				cur--;
				len--;
				char* c;
				for (c = cur; *c != 0; c++) c[0] = c[1];
			}
		} else if (buf[0] == '\t') {
			// ignore tabs -- todo: tab completion???? haha
		} else if (buf[0] >= ' ') {
			if (l < 255) {
				char* c;
				for (c = cur + len; c >= cur; c--) c[1] = c[0];
				cur[0] = buf[0];
				cur++;
				len++;
			}
		}

		// ASSUMPTION : everything will fit on one line...
		if (te > 0) fprintf(f, "\x1b[%iD", te);
		fprintf(f, "\x1b[K%s", str);
		te = len - (cur - str);
		if (te > 0) fprintf(f, "\x1b[%iD", te);
		
		
		if (finished) {
			fprintf(f, "\n");
			if (h->str[h->n-1][0] == 0) h->n--;
			return str;
		}
	}
}