summaryrefslogtreecommitdiff
path: root/lst.c
diff options
context:
space:
mode:
Diffstat (limited to 'lst.c')
-rw-r--r--lst.c204
1 files changed, 204 insertions, 0 deletions
diff --git a/lst.c b/lst.c
new file mode 100644
index 0000000..bbc5081
--- /dev/null
+++ b/lst.c
@@ -0,0 +1,204 @@
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/ttydefaults.h>
+#include <termios.h>
+#include <unistd.h>
+
+#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;
+
+int current = -1; /* currently selected item */
+int x, y; /* 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;
+}
+
+/* select item */
+int item(int n) {
+ if (n < 1 || n > lines_c) return -1;
+ if (current != -1)
+ prn("%s", lines[current - 1]);
+ current = n;
+ prn("%s%d;%dH", CSI, y + n - 1, 0);
+ prn(CSI "7m");
+ prn("%s", lines[n - 1]);
+ prn(CSI "0m");
+ prn("%s%d;%dH", CSI, y + n - 1, 0);
+ return 0;
+}
+
+int up() { return item(current-1); }
+int down() { return item(current+1); }
+int right() {}
+int left() {}
+
+int main() {
+ char c, *line, *p, *phrase, *tmp;
+ int i, line_s, r;
+ struct winsize w;
+ phrase = NULL;
+
+ ttyfd = open("/dev/tty", O_RDWR);
+ if (ttyfd == -1) rerr(1, "open");
+
+ /* read from standard input */
+
+ 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(&x, &y);
+ 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, &w);
+ if (r == -1) rerr(1, "ioctl");
+
+ /* correct cursor position if original cursor was near bottom */
+ r = y + lines_c - w.ws_row;
+ if (r > 0)
+ y = y - r;
+
+ /* restore original cursor position (CUP) */
+ prn("%s%d;%dH", CSI, y, x);
+
+ /* select first item */
+ item(1);
+
+ while (read(ttyfd, &c, 1) != 0) {
+ switch (c) {
+ case 'q':
+ goto quit;
+ break;
+ case '\033':
+ 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 13: /* enter */
+ phrase = lines[current - 1];
+ goto quit;
+ }
+ }
+quit:
+ prn("%s%d;%dH", CSI, y, x);
+ prn(CSI "J"); /* delete from cursor to end of display (ED) */
+ raw(false);
+ if (phrase != NULL)
+ printf("%s\n", phrase);
+ return 0;
+die:
+ raw(false);
+ puts("died");
+ return 0;
+}