#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 *types; /* type of each character in source text (see enums below) */ enum { NORMAL = 0 << 0, BREAK = 1 << 1, /* last character of visual line */ HIDDEN = 1 << 2, BOLD = 1 << 3, ITALIC = 1 << 4 }; int x, y; /* current cursor position */ int w, h; /* terminal width, height */ 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"); types[src_l] = BREAK; x = 1; if (y == h) yorig--; else y++; prn(CSI "1G"); } void hide() { types[src_l] = HIDDEN; } 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(types, src_s * sizeof(int)); if (types == NULL) err(1, "malloc"); types = tmp2; for (i = src_s - 50; i < src_s; i++) types[i] = 0; } src[++src_l] = c; types[src_l] = 0; src[src_l + 1] = '\0'; } void delc() { src[src_l--] = '\0'; } int main() { bool bold, dot, italic; 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; types = malloc(src_s * sizeof(int)); if (types == NULL) err(1, "malloc"); for (i = 0; i < src_s; i++) types[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;1H", yorig); x = 1; y = yorig; bold = false; dot = false; italic = false; while (read(ttyfd, &c, 1) != 0) { switch (c) { case '\033': /* escape */ read(ttyfd, &c, 1); switch (c) { case 'b': bold: if (bold && italic) { bold = false; italic = false; goto italic; } if (bold && !italic) goto roman; addc('\\'); hide(); addc('f'); hide(); if (italic) { addc('('); hide(); addc('B'); hide(); addc('I'); hide(); } else { addc('B'); hide(); } bold = true; break; italic: case 'i': if (italic && bold) { italic = false; bold = false; goto bold; } if (italic && !bold) goto roman; addc('\\'); hide(); addc('f'); hide(); if (bold) { addc('('); hide(); addc('B'); hide(); addc('I'); hide(); } else { addc('I'); hide(); } italic = true; break; roman: case 'r': case ' ': if (!bold && !italic) break; addc('\\'); hide(); addc('f'); hide(); addc('R'); hide(); prn(CSI "0m"); bold = false; italic = false; break; case '[': read(ttyfd, &c, 1); if (c == 'A') up(); if (c == 'B') down(); if (c == 'C') right(); if (c == 'D') left(); break; } break; case 3: /* ctrl-c */ goto quit; break; case 13: /* enter */ addc('\n'); breakline(); dot = false; break; case 127: /* backspace */ if (x == 1) break; prn("\b"); delc(); if (src[src_l-2] == '\\' && src[src_l-1] == 'f') { if (src[src_l] = 'B') bold = false; if (src[src_l] = 'I') italic = false; if (src[src_l] = 'R') { if ((types[src_l-3] & BOLD) == BOLD) bold = true; if ((types[src_l-3] & ITALIC) == ITALIC) italic = true; } } while (types[src_l] == HIDDEN) delc(); x--; if (x == 1) dot = false; break; default: if (iscntrl(c)) break; prn(CSI "0m"); types[src_l] &= ~BOLD; types[src_l] &= ~ITALIC; if (x == 1 && c == '.') dot = true; addc(c); if (x > w) breakline(); if (dot) prn(CSI "2m"); if (bold) { prn(CSI "1m"); types[src_l] |= BOLD; } if (italic) { prn(CSI "3m"); types[src_l] |= ITALIC; } 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; }