#include #include #include #include #include #include #include #include #include #include #include #define ESC "\033" #define CSI ESC "[" #define prn(...) dprintf(ttyfd, __VA_ARGS__) #define rerr(...) do { raw(false); err(__VA_ARGS__); } while (0) char **lines; int lines_c; int lines_s; char *src; /* source text */ int src_l; int src_s; int *breaks; /* positions of visual line breaks in source text */ /* i.e. breaks[src index] = 1 if line broken before, else 0 */ int x, y; /* current cursor position */ int w, h; /* terminal width, height */ int margin = 10; int current = -1; /* currently selected item */ int xorig, yorig; /* original cursor position in cols, rows */ int ttyfd; /* enable/disable "raw" mode */ void raw(bool enable) { int r; static struct termios orig; struct termios raw; if (enable) { r = tcgetattr(ttyfd, &orig); if (r == -1) err(1, "tcgetattr"); raw = orig; raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); raw.c_oflag &= ~OPOST; raw.c_cflag |= CS8; raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); r = tcsetattr(ttyfd, TCSAFLUSH, &raw); if (r == -1) err(1, "tcsetattr"); } else tcsetattr(ttyfd, TCSAFLUSH, &orig); } /* cursor position report */ int cpr(int *x, int *y) { char c, xbuf[4], ybuf[4]; int i; prn(CSI "6n"); /* get CSI */ read(ttyfd, &c, 1); if (c != '\033') return -1; read(ttyfd, &c, 1); if (c != '[') return -1; /* get x */ i = 0; while (read(ttyfd, &c, 1) && c != ';') { if (i > 3 || !isdigit(c)) return -1; ybuf[i++] = c; } if (i == 0) return -1; ybuf[i] = '\0'; /* get y */ i = 0; while (read(ttyfd, &c, 1) && c != 'R') { if (i > 3 || !isdigit(c)) return -1; xbuf[i++] = c; } if (i == 0) return -1; xbuf[i] = '\0'; *x = atoi(xbuf); *y = atoi(ybuf); return 0; } int up() { } int down() { } int right() {} int left() {} void breakline() { prn("\r\n"); breaks[src_l + 1] = 1; x = margin; if (y == h) yorig--; else y++; prn(CSI "%d;%dH", y, margin); } void addc(char c) { char *tmp; int i, *tmp2; if (src_l + 2 > src_s) { src_s += 50; tmp = realloc(src, (src_s + 1) * sizeof(char)); if (tmp == NULL) rerr(1, "realloc"); src = tmp; tmp2 = realloc(breaks, src_s * sizeof(int)); if (breaks == NULL) err(1, "malloc"); breaks = tmp2; for (i = src_s - 50; i < src_s; i++) breaks[i] = 0; } src[++src_l] = c; src[src_l + 1] = '\0'; } void delc() { src[src_l--] = '\0'; } int main() { bool dot; char c, *line, *p, *tmp; int i, line_s, r; struct winsize ws; src_s = 100; src = malloc((src_s + 1) * sizeof(char)); if (src == NULL) err(1, "malloc"); src_l = -1; breaks = malloc(src_s * sizeof(int)); if (breaks == NULL) err(1, "malloc"); for (i = 0; i < src_s; i++) breaks[i] = 0; ttyfd = open("/dev/tty", O_RDWR); if (ttyfd == -1) rerr(1, "open"); /* read file */ /* line_s = 100; line = malloc((line_s + 1) * sizeof(char)); if (line == NULL) err(1, "malloc"); lines_c = 0; lines_s = 50; lines = malloc(lines_s * sizeof(char *)); if (lines == NULL) err(1, "malloc"); i = 0; while (read(STDIN_FILENO, &c, 1) != 0) { if (c == '\n') { line[i] = '\0'; lines[lines_c] = malloc((strlen(line) + 1) * sizeof(char)); if (lines[lines_c] == NULL) err(1, "malloc"); strcpy(lines[lines_c], line); lines_c++; i = 0; } else { if (i > line_s) { line_s += 50; tmp = realloc(line, (line_s + 1) * sizeof(char)); if (tmp == NULL) err(1, "realloc"); line = tmp; } line[i++] = c; } } */ raw(true); /* save original cursor position */ r = cpr(&xorig, &yorig); if (r == -1) { fprintf(stderr, "could not retrieve cursor position"); goto quit; } /* print output */ for (i = 0; i < lines_c; i++) prn("%s\r\n", lines[i]); /* get height of terminal */ r = ioctl(ttyfd, TIOCGWINSZ, &ws); if (r == -1) rerr(1, "ioctl"); w = ws.ws_col; h = ws.ws_row; /* correct cursor position if original cursor was near bottom */ r = y + lines_c - h; if (r > 0) y = y - r; /* restore original cursor position (CUP) */ prn(CSI "%d;%dH", yorig, margin); x = margin; y = yorig; dot = false; while (read(ttyfd, &c, 1) != 0) { switch (c) { case '\033': /* escape */ read(ttyfd, &c, 1); if (c != '[') break; read(ttyfd, &c, 1); if (c == 'A') up(); if (c == 'B') down(); if (c == 'C') right(); if (c == 'D') left(); break; case 3: /* ctrl-c */ goto quit; break; case 13: /* enter */ addc('\n'); breakline(); prn(CSI "0m"); break; case 127: /* backspace */ if (x == margin || x == 1) break; prn("\b"); delc(); x--; if (x == 1) { prn(CSI "0m"); for (i = 1; i < margin; i++) prn(" "); x = margin; } break; default: if (iscntrl(c)) break; if (x == margin && c == '.') { prn(CSI "%d;%dH", y, 1); x = 1; prn(CSI "2m"); } addc(c); if (x + 1 > w) breakline(); prn("%c", c); x++; break; } } quit: prn(CSI "%d;%dH", yorig, xorig); prn(CSI "J"); /* delete from cursor to end of display (ED) */ prn(CSI "0m"); raw(false); printf("%s", src); return 0; die: raw(false); puts("died"); return 0; }