aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNPScript <nathan@reinerweb.ch>2021-12-20 18:19:17 +0100
committerNPScript <nathan@reinerweb.ch>2021-12-20 18:19:17 +0100
commit25b3356b926e71d25fddef998021e66d7c1a7557 (patch)
tree7e5dc325907a4802d74233998db6cde6605d4b0a
init commit
-rw-r--r--Makefile22
-rw-r--r--README.md3
-rwxr-xr-xluisbin0 -> 27568 bytes
-rw-r--r--luis.c339
-rw-r--r--luis.obin0 -> 16616 bytes
-rw-r--r--tui.c207
-rw-r--r--tui.h52
-rw-r--r--tui.obin0 -> 7928 bytes
8 files changed, 623 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..162a76c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,22 @@
+CC=gcc
+SRC=$(wildcard *.c)
+OBJ=${SRC:.c=.o}
+LDFLAGS=-lmagic
+
+all: luis
+
+options:
+ @echo "CC = ${CC}"
+ @echo "SRC = ${SRC}"
+ @echo "OBJ = ${OBJ}"
+ @echo "LDFLAGS = ${LDFLAGS}"
+
+.c.o:
+ ${CC} -c $<
+
+luis: ${OBJ}
+ ${CC} -o $@ ${OBJ} ${LDFLAGS}
+
+clean:
+ rm ${OBJ} test
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d8e4ee4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# luis - A example for [tui](https://github.com/NPScript/libnut/tree/master/tui)
+
+*luis* is just a little filemanager which uses *tui* instead of *ncurses*.
diff --git a/luis b/luis
new file mode 100755
index 0000000..d43211e
--- /dev/null
+++ b/luis
Binary files differ
diff --git a/luis.c b/luis.c
new file mode 100644
index 0000000..f14d1b8
--- /dev/null
+++ b/luis.c
@@ -0,0 +1,339 @@
+#include "tui.h"
+#include <dirent.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <magic.h>
+#include <limits.h>
+#include <unistd.h>
+
+const char DTC[] = {
+ [DT_BLK] = 'b',
+ [DT_CHR] = 'c',
+ [DT_DIR] = 'd',
+ [DT_FIFO] = 'F',
+ [DT_LNK] = 'l',
+ [DT_REG] = 'f',
+ [DT_SOCK] = 's',
+ [DT_UNKNOWN] = '?',
+};
+
+const char * SUNIT[] = {"B", "KiB", "MiB", "GiB", "TiB"};
+
+magic_t magic_cookie;
+
+int (*current_filter)(const struct dirent *);
+int selected_item = 0;
+char current_path[PATH_MAX] = {0};
+
+int filter_hidden(const struct dirent * e) {
+ return e->d_name[0] != '.';
+}
+
+int filter_twodot_dir(const struct dirent * e) {
+ return strcmp(e->d_name, "..") && strcmp(e->d_name, ".");
+}
+
+int number_of_dir_entries(const char * dpath) {
+ struct dirent * entry;
+ DIR * dir = opendir(dpath);
+ int n = 0;
+
+ while ((entry = readdir(dir)) != NULL)
+ n += current_filter(entry);
+
+ closedir(dir);
+
+ return n;
+}
+
+void get_selection_name(const char * dpath, char * name) {
+ struct dirent ** namelist;
+ int n = scandir(dpath, &namelist, current_filter, alphasort);
+ strcpy(name, namelist[selected_item]->d_name);
+
+ while (n--)
+ free(namelist[n]);
+ free(namelist);
+}
+
+void write_dir_content_to_win(Window * win, const char * dpath, unsigned select, unsigned offset) {
+ struct dirent ** namelist;
+ int n = scandir(dpath, &namelist, current_filter, alphasort);
+
+ if (n == 0) {
+ SET_COLOR(180, 100, 100);
+ printfxy_to_window(win, 0, offset, "empty");
+ RESET_VIDEO();
+ }
+
+ for (unsigned i = 0; i < n && i < get_term_height(); ++i) {
+ if (select == i) {
+ SET_BG(60, 103, 84);
+ SET_COLOR(0, 0, 0);
+ }
+
+ printfxy_to_window(win, 0, i + offset, " %c %s%*s", DTC[namelist[i]->d_type], namelist[i]->d_name, win->width - 5 - strlen(namelist[i]->d_name), "");
+
+ if (select == i)
+ RESET_VIDEO();
+
+ free(namelist[i]);
+ }
+
+ free(namelist);
+}
+
+void switch_filter() {
+ current_filter = current_filter == filter_hidden ? filter_twodot_dir : filter_hidden;
+}
+
+void select_move(int n) {
+ if (selected_item + n >= 0 && selected_item + n < number_of_dir_entries(current_path))
+ selected_item += n;
+}
+
+void enter_parent() {
+ unsigned n = strlen(current_path) - 1;
+
+ if (!n)
+ return;
+
+ while (current_path[--n]) {
+ if (current_path[n] == '/') {
+ current_path[n + 1] = 0;
+ break;
+ }
+ }
+
+ selected_item = 0;
+
+}
+
+void enter_directory() {
+ get_selection_name(current_path, current_path + strlen(current_path));
+ strcat(current_path, "/");
+ int s = selected_item;
+
+ DIR * dir = opendir(current_path);
+
+ if (dir) {
+ selected_item = 0;
+ closedir(dir);
+
+ struct dirent ** namelist;
+ int n = scandir(current_path, &namelist, current_filter, alphasort);;
+
+ for (int i = 0; i < n; ++i)
+ free(namelist[i]);
+ free(namelist);
+
+ if (n == 0) {
+ enter_parent();
+ selected_item = s;
+ }
+
+ return;
+ } else {
+ showecho(1);
+ showcursor(1);
+ char open_with[2048];
+
+ printfxy(1, 1, "open %s with: ", current_path);
+ if (fgets(open_with, 256, stdin)) {
+ open_with[strlen(open_with) - 1] = 0;
+ current_path[strlen(current_path) - 1] = 0;
+ endtui();
+ system(strcat(strcat(open_with, " "), current_path));
+ inittui();
+ current_path[strlen(current_path) - 1] = '/';
+ }
+
+ showcursor(0);
+ showecho(0);
+ }
+
+ closedir(dir);
+ enter_parent();
+ selected_item = s;
+}
+
+void help() {
+ Window help;
+ help.x = get_term_width() / 4;
+ help.y = get_term_height() / 4;
+ help.width = get_term_width() - 2 * help.x;
+ help.height = get_term_height() - 2 * help.y;
+ help.has_borders = 1;
+
+ draw_window(&help);
+
+ const char helptext[] = " H : Toggle Hidden\n"
+ " j : Move down\n"
+ " k : Move up\n"
+ " h : Move back\n"
+ " l : Enter Directory\n"
+ " o : Open\n"
+ " ? : Show this dialog\n";
+
+ printfxy_to_window(&help, help.width / 2 - 4, 0, "- Help -");
+ printfxy_to_window(&help, 0, 1, "%s", helptext);
+ printfxy_to_window(&help, help.width - 16, help.height - 3, "[c]lose window");
+
+ while (getch() != 'c');
+}
+
+char * readable_size(long size, char * dest) {
+ int n = 0;
+
+ while (size > 1000) {
+ size /= 1024;
+ ++n;
+ }
+
+ sprintf(dest, "%d %s", size, SUNIT[n]);
+
+ return dest;
+}
+
+void print_preview(Window * win) {
+ struct stat selstat;
+ char filepath[2048];
+ char size[128];
+ const char * magic_full;
+
+ strcpy(filepath, current_path);
+ get_selection_name(current_path, filepath + strlen(filepath));
+
+ stat(filepath, &selstat);
+
+ magic_full = magic_file(magic_cookie, filepath);
+
+ SET_COLOR(100, 100, 100);
+ printfxy_to_window(win, 0, 0, "%s\nLast modified %sSize %s\nType: %s",
+ filepath + strlen(current_path), ctime(&selstat.st_mtime), readable_size(selstat.st_size, size), magic_full);
+ RESET_VIDEO();
+
+ if (strstr(magic_full, "text")) {
+ char buffer[4096] = "";
+ FILE * fp = fopen(filepath, "r");
+
+ fread(buffer, 4096, 1, fp);
+
+ printfxy_to_window(win, 0, 6, "%s", buffer);
+
+ fclose(fp);
+ } else if (strstr(magic_full, "directory")) {
+ SET_COLOR(100, 100, 100);
+ printfxy_to_window(win, 0, 5, "Content");
+ RESET_VIDEO();
+ write_dir_content_to_win(win, filepath, -1, 7);
+ }
+}
+
+int main(int argc, char ** argv) {
+ Window mainwin;
+ Window preview;
+
+ mainwin.x = 1;
+ mainwin.y = 2;
+ mainwin.has_borders = 1;
+
+ preview.y = 2;
+ preview.has_borders = 1;
+
+ if (argc == 2) {
+ realpath(argv[1], current_path);
+
+ printf(current_path);
+
+ if (current_path[strlen(current_path) - 1] != '/') {
+ current_path[strlen(current_path)] = '/';
+ }
+
+ DIR * dir = opendir(current_path);
+
+ if (dir == NULL) {
+ if (getcwd(current_path, sizeof(current_path)) == NULL) {
+ fprintf(stderr, "getcwd error\n");
+ return -1;
+ }
+ strcat(current_path, "/");
+ }
+
+ closedir(dir);
+
+ } else {
+ if (getcwd(current_path, sizeof(current_path)) == NULL) {
+ fprintf(stderr, "getcwd error\n");
+ return -1;
+ }
+ strcat(current_path, "/");
+ }
+
+ magic_cookie = magic_open(MAGIC_MIME_TYPE);
+
+ if (!magic_cookie) {
+ fprintf(stderr, "cannot initialize magic library\n");
+ return -1;
+ }
+
+ if (magic_load(magic_cookie, NULL)) {
+ fprintf(stderr, "cannot load magic database: %s\n", magic_error(magic_cookie));
+ magic_close(magic_cookie);
+ return -1;
+ }
+
+ inittui();
+ showcursor(0);
+ showecho(0);
+
+ char c = 0;
+ current_filter = filter_hidden;
+
+ do {
+
+ switch(c) {
+ case 'H': switch_filter(); break;
+ case 'j': select_move(1); break;
+ case 'k': select_move(-1); break;
+ case 'l': enter_directory(); break;
+ case 'h': enter_parent(); break;
+ case 'o': enter_directory(); break;
+ case '?': help(); break;
+ default: break;
+ }
+
+ mainwin.width = get_term_width() / 2;
+ mainwin.height = get_term_height() - 1;
+
+ preview.width = get_term_width() - mainwin.width + 1;
+ preview.height = mainwin.height;
+ preview.x = mainwin.width;
+
+ printfxy(1, 1, "%s%*s[?] for help", current_path, get_term_width() - strlen(current_path) - 12, "");
+
+ SET_COLOR(40, 74, 53);
+ draw_window(&preview);
+ BOLD();
+ SET_COLOR(60, 103, 84);
+ draw_window(&mainwin);
+ RESET_VIDEO();
+
+ write_dir_content_to_win(&mainwin, current_path, selected_item, 0);
+
+ print_preview(&preview);
+
+ } while ((c = getch()) != 'q');
+
+
+ showecho(1);
+ showcursor(1);
+
+ endtui();
+ magic_close(magic_cookie);
+ return 0;
+}
diff --git a/luis.o b/luis.o
new file mode 100644
index 0000000..22829fb
--- /dev/null
+++ b/luis.o
Binary files differ
diff --git a/tui.c b/tui.c
new file mode 100644
index 0000000..37bb4c6
--- /dev/null
+++ b/tui.c
@@ -0,0 +1,207 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <termios.h>
+
+#include "tui.h"
+
+struct termios term;
+
+unsigned get_term_width() {
+ struct winsize w;
+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
+
+ return w.ws_col;
+}
+
+unsigned get_term_height() {
+ struct winsize w;
+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
+
+ return w.ws_row;
+}
+
+unsigned get_cursor_x_position() {
+ printf("\033[6n");
+ char buf[32];
+ unsigned n;
+ unsigned m;
+ unsigned x;
+
+ for (char * c = buf; c < buf + 32; ++c) {
+ *c = getch();
+
+ if (*c == 'R') {
+ m = c - buf;
+ break;
+ } else if (*c == ';') {
+ n = c - buf;
+ }
+ }
+
+ x = 0;
+
+ for (int i = m - 1; i > n; --i) {
+ int p = 1;
+
+ for (int j = 0; j < m - 1 - i; ++j)
+ p *= 10;
+
+ x += ((int)buf[i] - 48) * p;
+ }
+
+ return x;
+}
+
+unsigned get_cursor_y_position() {
+ printf("\033[6n");
+ char buf[32];
+ unsigned n;
+ unsigned y;
+ unsigned was_echo = is_echo();
+
+ showecho(0);
+
+ for (char * c = buf; c < buf + 32; ++c) {
+ *c = getch();
+
+ if (*c == 'R') {
+ break;
+ } else if (*c == ';') {
+ n = c - buf;
+ }
+ }
+
+ showecho(was_echo);
+
+ y = 0;
+
+ for (int i = n - 1; i > 1; --i) {
+ int p = 1;
+
+ for (int j = 0; j < n - 1 - i; ++j)
+ p *= 10;
+
+ y += ((int)buf[i] - 48) * p;
+ }
+
+ return y;
+}
+
+void inittui() {
+ printf("\033[?1049h");
+ tcgetattr(fileno(stdin), &term);
+}
+
+void endtui() {
+ printf("\033[?1049l");
+}
+
+char getch() {
+ char c;
+ term.c_lflag &= ~ICANON;
+
+ tcsetattr(fileno(stdin), TCSANOW, &term);
+ c = getc(stdin);
+
+ term.c_lflag |= ICANON;
+ tcsetattr(fileno(stdin), TCSANOW, &term);
+ return c;
+}
+
+void showecho(int echo) {
+ if (!echo)
+ term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+ else
+ term.c_lflag |= ECHO | ECHOE | ECHOK | ECHONL;
+ tcsetattr(fileno(stdin), TCSANOW, &term);
+}
+
+int is_echo() {
+ return term.c_iflag & ECHO;
+}
+
+void showcursor(int show) {
+ printf("\033[?25%c", show ? 'h' : 'l');
+}
+
+void printfxy(unsigned x, unsigned y, const char * fmt, ...) {
+ va_list list;
+
+ printf("\033[%d;%dH", y, x);
+
+ va_start(list, fmt);
+ vprintf(fmt, list);
+ va_end(list);
+}
+
+void draw_window(Window * win) {
+ for (int i = 0; i < win->height; ++i)
+ printfxy(win->x, win->y + i, "%*s", win->width, "");
+
+ if (win->has_borders) {
+ printfxy(win->x, win->y, "%s", TLCORNER);
+ for (int i = 1; i < win->width - 1; ++i)
+ printfxy(win->x + i, win->y, "%s", HLINE);
+ printfxy(win->x + win->width - 1, win->y, "%s", TRCORNER);
+
+ printfxy(win->x, win->y + win->height - 1, "%s", BLCORNER);
+ for (int i = 1; i < win->width - 1; ++i)
+ printfxy(win->x + i, win->y + win->height - 1, "%s", HLINE);
+ printfxy(win->x + win->width - 1, win->y + win->height - 1, "%s", BRCORNER);
+
+ for (int i = 1; i < win->height - 1; ++i)
+ printfxy(win->x, win->y + i, "%s", VLINE);
+
+ for (int i = 1; i < win->height - 1; ++i)
+ printfxy(win->x + win->width - 1, win->y + i, "%s", VLINE);
+ }
+
+ fflush(stdout);
+}
+
+void printfxy_to_window(Window * win, int x, int y, const char * fmt, ...) {
+ va_list list;
+ char buf[BUFSIZ];
+ int cx;
+ int cy;
+ int tabstop = 0;
+
+ va_start(list, fmt);
+ vsprintf(buf, fmt, list);
+ va_end(list);
+
+ cx = x + win->x + win->has_borders;
+ cy = y + win->y + win->has_borders;
+
+ if (cy >= (int)(win->y + win->height - win->has_borders))
+ return;
+
+ printf("\033[%d;%dH", cy, cx);
+
+ for (char * c = buf; *c;) {
+ if (*c == '\t') {
+ tabstop = 3;
+ *c = ' ';
+ }
+
+ if (*c == '\n' || cx == win->x + win->width - win->has_borders) {
+ if (++cy >= (int)(win->y + win->height - win->has_borders))
+ break;
+ cx = win->x + win->has_borders;
+ printf("\033[%d;%dH", cy, cx);
+ }
+
+ if (*c != '\n') {
+ if (cy >= (int)(win->y + win->has_borders))
+ putc(*c, stdout);
+ ++cx;
+ }
+
+ if (tabstop)
+ --tabstop;
+ else
+ ++c;
+ }
+}
diff --git a/tui.h b/tui.h
new file mode 100644
index 0000000..d625478
--- /dev/null
+++ b/tui.h
@@ -0,0 +1,52 @@
+#ifndef TUI_H
+#define TUI_H
+
+#include <stdio.h>
+
+#define VLINE "│"
+#define HLINE "─"
+#define TLCORNER "┌"
+#define BLCORNER "└"
+#define TRCORNER "┐"
+#define BRCORNER "┘"
+#define LTEE "├"
+#define RTEE "┤"
+#define BTEE "┴"
+#define TTEE "┬"
+#define PLUS "─"
+#define RGB(t, r, g, b) "\033[38;2;" #r ";" #g ";" #b "m" t "\033[0m"
+#define SET_COLOR(r, g, b) printf("\033[38;2;" #r ";" #g ";" #b "m")
+#define RESET_VIDEO() printf("\033[0m")
+#define INVERT() printf("\033[7m")
+#define BOLD() printf("\033[1m")
+#define SET_BG(r, g, b) printf("\033[48;2;" #r ";" #g ";" #b "m")
+
+typedef struct {
+ unsigned width;
+ unsigned height;
+ unsigned x;
+ unsigned y;
+ unsigned has_borders;
+} Window;
+
+unsigned get_term_width();
+unsigned get_term_height();
+
+unsigned get_cursor_x_position();
+unsigned get_cursor_y_position();
+
+void inittui();
+void endtui();
+
+char getch();
+
+void printfxy(unsigned x, unsigned y, const char * fmt, ...);
+
+void printfxy_to_window(Window * win, int x, int y, const char * fmt, ...);
+void draw_window(Window * win);
+
+int is_echo();
+void showecho(int echo);
+void showcursor(int show);
+
+#endif
diff --git a/tui.o b/tui.o
new file mode 100644
index 0000000..492dd73
--- /dev/null
+++ b/tui.o
Binary files differ