From 25b3356b926e71d25fddef998021e66d7c1a7557 Mon Sep 17 00:00:00 2001 From: NPScript Date: Mon, 20 Dec 2021 18:19:17 +0100 Subject: init commit --- Makefile | 22 ++++ README.md | 3 + luis | Bin 0 -> 27568 bytes luis.c | 339 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ luis.o | Bin 0 -> 16616 bytes tui.c | 207 ++++++++++++++++++++++++++++++++++++++ tui.h | 52 ++++++++++ tui.o | Bin 0 -> 7928 bytes 8 files changed, 623 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100755 luis create mode 100644 luis.c create mode 100644 luis.o create mode 100644 tui.c create mode 100644 tui.h create mode 100644 tui.o 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 Binary files /dev/null and b/luis 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 Binary files /dev/null and b/luis.o 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 +#include +#include +#include +#include + +#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 + +#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 Binary files /dev/null and b/tui.o differ -- cgit v1.2.3-70-g09d2