From 3daa54df98b27066f9fcf9aab85d375dee744655 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 30 Sep 2019 17:06:27 -0700 Subject: Moved config.h -> bb.h and did some cleanup, moving function declarations and constants out of bb.c and into bb.h. Also re-alphabetized the functions and updated the docs. --- bb.h | 301 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 301 insertions(+) create mode 100644 bb.h (limited to 'bb.h') diff --git a/bb.h b/bb.h new file mode 100644 index 0000000..d702ad0 --- /dev/null +++ b/bb.h @@ -0,0 +1,301 @@ +/* + * Bitty Browser (bb) + * Copyright 2019 Bruce Hill + * Released under the MIT license + * + * This file contains definitions and customization for `bb`. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bterm.h" + +// Macros: +#define BB_VERSION "0.16.0" + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +#define MAX_COLS 12 +#define MAX_SORT (2*MAX_COLS) +#define HASH_SIZE 1024 +#define HASH_MASK (HASH_SIZE - 1) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#define MIN(a,b) ((a) > (b) ? (b) : (a)) +#define IS_SELECTED(p) (((p)->selected.atme) != NULL) +#define IS_VIEWED(p) ((p)->index >= 0) +#define LOWERCASE(c) ('A' <= (c) && (c) <= 'Z' ? ((c) + 'a' - 'A') : (c)) +#define E_ISDIR(e) (S_ISDIR(S_ISLNK((e)->info.st_mode) ? (e)->linkedmode : (e)->info.st_mode)) +#define ONSCREEN (termheight - 3) + +#ifdef __APPLE__ +#define mtime(s) (s).st_mtimespec +#define atime(s) (s).st_atimespec +#define ctime(s) (s).st_ctimespec +#else +#define mtime(s) (s).st_mtim +#define atime(s) (s).st_atim +#define ctime(s) (s).st_ctim +#endif + +#define err(...) do { \ + cleanup(); \ + fprintf(stderr, __VA_ARGS__); \ + if (errno) fprintf(stderr, "\n%s", strerror(errno)); \ + fprintf(stderr, "\n"); \ + exit(EXIT_FAILURE); \ +} while (0) + +// Types: +typedef struct { + int key; + char *script; + char *description; +} binding_t; + +typedef struct { + int width; + const char *name; +} column_t; + +typedef enum { + COL_NONE = 0, + COL_NAME = 'n', + COL_SIZE = 's', + COL_PERM = 'p', + COL_MTIME = 'm', + COL_CTIME = 'c', + COL_ATIME = 'a', + COL_RANDOM = 'r', + COL_SELECTED = '*', +} column_e; + +/* entry_t uses intrusive linked lists. This means entries can only belong to + * one list at a time, in this case the list of selected entries. 'atme' is an + * indirect pointer to either the 'next' field of the previous list member, or + * the variable that points to the first list member. In other words, + * item->next->atme == &item->next and firstitem->atme == &firstitem. + */ +typedef struct { + struct entry_s *next, **atme; +} llnode_t; + +typedef struct entry_s { + llnode_t selected, hash; + char *name, *linkname; + struct stat info; + mode_t linkedmode; + int no_esc : 1; + int link_no_esc : 1; + int shufflepos; + int index; + char fullname[1]; + // ------- fullname must be last! -------------- + // When entries are allocated, extra space on the end is reserved to fill + // in fullname. +} entry_t; + +typedef struct bb_s { + entry_t *hash[HASH_SIZE]; + entry_t **files; + entry_t *firstselected; + char path[PATH_MAX]; + char prev_path[PATH_MAX]; + int nfiles; + int scroll, cursor; + + char sort[MAX_SORT+1]; + char columns[MAX_COLS+1]; + unsigned int dirty : 1; + unsigned int show_dotdot : 1; + unsigned int show_dot : 1; + unsigned int show_dotfiles : 1; + unsigned int interleave_dirs : 1; + unsigned int should_quit : 1; +} bb_t; + +typedef enum { BB_OK = 0, BB_INVALID, BB_QUIT } bb_result_t; + +// Configurable options: +#define SCROLLOFF MIN(5, (termheight-4)/2) +#define CMDFILE_FORMAT "/tmp/bb.XXXXXX" +#define SORT_INDICATOR "↓" +#define RSORT_INDICATOR "↑" +#define SELECTED_INDICATOR " \033[31;7m \033[0m" +#define NOT_SELECTED_INDICATOR " " +// Colors (using ANSI escape sequences): +#define TITLE_COLOR "\033[37;1m" +#define NORMAL_COLOR "\033[37m" +#define CURSOR_COLOR "\033[43;30;1m" +#define LINK_COLOR "\033[35m" +#define DIR_COLOR "\033[34m" +#define EXECUTABLE_COLOR "\033[31m" + +#define MAX_BINDINGS 1024 + +binding_t bindings[MAX_BINDINGS]; + +// Column widths and titles: +const column_t columns[] = { + ['*'] = {2, "*"}, + ['a'] = {21, " Accessed"}, + ['c'] = {21, " Created"}, + ['m'] = {21, " Modified"}, + ['n'] = {40, "Name"}, + ['p'] = {5, "Permissions"}, + ['r'] = {2, "Random"}, + ['s'] = {9, " Size"}, +}; + +// Functions +void bb_browse(bb_t *bb, const char *path); +static int cd_to(bb_t *bb, const char *path); +static void cleanup(void); +static void cleanup_and_exit(int sig); +static const char* color_of(mode_t mode); +#ifdef __APPLE__ +static int compare_files(void *v, const void *v1, const void *v2); +#else +static int compare_files(const void *v1, const void *v2, void *v); +#endif +static int fputs_escaped(FILE *f, const char *str, const char *color); +static void init_term(void); +static int is_simple_bbcmd(const char *s); +static entry_t* load_entry(bb_t *bb, const char *path, int clear_dots); +static void* memcheck(void *p); +static void normalize_path(const char *root, const char *path, char *pbuf, int clear_dots); +static void populate_files(bb_t *bb, int samedir); +static void print_bindings(int fd); +static bb_result_t process_cmd(bb_t *bb, const char *cmd); +static void render(bb_t *bb); +static void restore_term(const struct termios *term); +static int run_script(bb_t *bb, const char *cmd); +static void set_cursor(bb_t *bb, int i); +static void set_selected(bb_t *bb, entry_t *e, int selected); +static void set_scroll(bb_t *bb, int i); +static void set_sort(bb_t *bb, const char *sort); +static void sort_files(bb_t *bb); +static char *trim(char *s); +static int try_free_entry(entry_t *e); +static void update_term_size(int sig); + +// Constants +static const char *T_ENTER_BBMODE = T_OFF(T_SHOW_CURSOR ";" T_WRAP) T_ON(T_ALT_SCREEN ";" T_MOUSE_XY ";" T_MOUSE_CELL ";" T_MOUSE_SGR); +static const char *T_LEAVE_BBMODE = T_OFF(T_MOUSE_XY ";" T_MOUSE_CELL ";" T_MOUSE_SGR ";" T_ALT_SCREEN) T_ON(T_SHOW_CURSOR ";" T_WRAP); +static const char *T_LEAVE_BBMODE_PARTIAL = T_OFF(T_MOUSE_XY ";" T_MOUSE_CELL ";" T_MOUSE_SGR) T_ON(T_WRAP); +static const struct termios default_termios = { + .c_iflag = ICRNL, + .c_oflag = OPOST | ONLCR | NL0 | CR0 | TAB0 | BS0 | VT0 | FF0, + .c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE, + .c_cflag = CS8 | CREAD, + .c_cc[VINTR] = '', + .c_cc[VQUIT] = '', + .c_cc[VERASE] = 127, + .c_cc[VKILL] = '', + .c_cc[VEOF] = '', + .c_cc[VSTART] = '', + .c_cc[VSTOP] = '', + .c_cc[VSUSP] = '', + .c_cc[VREPRINT] = '', + .c_cc[VWERASE] = '', + .c_cc[VLNEXT] = '', + .c_cc[VDISCARD] = '', + .c_cc[VMIN] = 1, + .c_cc[VTIME] = 0, +}; + +// Shell functions +static const char *bbcmdfn = "bb() {\n" +" if test $# -eq 0; then cat >> $BBCMD; return; fi\n" +" for arg; do\n" +" shift;\n" +" if echo \"$arg\" | grep \"^+[^:]*:$\" >/dev/null 2>/dev/null; then\n" +" if test $# -gt 0; then printf \"%s\\0\" \"$arg\" \"$@\" >> $BBCMD\n" +" else sed \"s/\\([^\\x00]\\+\\)/$arg\\1/g\" >> $BBCMD; fi\n" +" return\n" +" fi\n" +" printf \"%s\\0\" \"$arg\" >> $BBCMD\n" +" done\n" +"}\n" +"ask() {\n" +#ifdef ASK +ASK ";\n" +#else +" printf \"\033[1m%s\033[0m\" \"$2\" >/dev/tty;\n" +" read $1 /dev/tty\n" +#endif +"}\n" +"ask1() {\n" +#ifdef ASK1 +ASK1 ";\n" +#else +" printf \"\033[?25l\" >/dev/tty;\n" +" printf \"\033[1m%s\033[0m\" \"$2\" >/dev/tty;\n" +" stty -icanon -echo >/dev/tty;\n" +" eval \"$1=\\$(dd bs=1 count=1 2>/dev/null /dev/tty;\n" +" printf \"\033[?25h\" >/dev/tty;\n" +#endif +"}\n" +"confirm() {\n" +#ifdef CONFIRM +CONFIRM ";\n" +#else +" ask1 REPLY \"\033[1mIs that okay? [y/N] \" && [ \"$REPLY\" = 'y' ];\n" +#endif +"}\n" +"pause() {\n" +#ifdef PAUSE +PAUSE ";\n" +#else +" ask1 REPLY \"\033[2mPress any key to continue...\033[0m\";" +#endif +"}\n" +"pick() {\n" +#ifdef PICK +PICK ";\n" +#else +" ask query \"$1\" && awk '{print length, $1}' | sort -n | cut -d' ' -f2- |\n" +" grep -i -m1 \"$(echo \"$query\" | sed 's;.;[^/&]*[&];g')\";\n" +#endif +"}\n" +"spin() {\n" +#ifdef SPIN +SPIN ";\n" +#else +" eval \"$@\" &\n" +" pid=$!;\n" +" spinner='-\\|/';\n" +" sleep 0.01;\n" +" while kill -0 $pid 2>/dev/null; do\n" +" printf '%c\\033[D' \"$spinner\" >/dev/tty;\n" +" spinner=\"$(echo $spinner | sed 's/\\(.\\)\\(.*\\)/\\2\\1/')\";\n" +" sleep 0.1;\n" +" done;\n" +" wait $pid;\n" +#endif +"}\n" +#ifdef SH +"alias sh=" SH";\n" +#else +#define SH "sh" +#endif +; + +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1 -- cgit v1.2.3