diff options
| -rw-r--r-- | Makefile | 22 | ||||
| -rw-r--r-- | README.md | 3 | ||||
| -rwxr-xr-x | luis | bin | 0 -> 27568 bytes | |||
| -rw-r--r-- | luis.c | 339 | ||||
| -rw-r--r-- | luis.o | bin | 0 -> 16616 bytes | |||
| -rw-r--r-- | tui.c | 207 | ||||
| -rw-r--r-- | tui.h | 52 | ||||
| -rw-r--r-- | tui.o | bin | 0 -> 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*. Binary files differ@@ -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; +} Binary files differ@@ -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; + } +} @@ -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 Binary files differ |