dotfiles/.zsh/zsh-fzf-tab/modules/Src/fzftab.c

540 lines
15 KiB
C

#include "fzftab.mdh"
#include "fzftab.pro"
#include <stdarg.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
const char* get_color(char* file, const struct stat* sb);
const char* colorize_from_mode(char* file, const struct stat* sb);
const char* colorize_from_name(char* file);
int compile_patterns(char* nam, char** list_colors);
char* ftb_strcat(char* dst, int n, ...);
/* Indixes into the terminal string arrays. */
#define COL_NO 0
#define COL_FI 1
#define COL_DI 2
#define COL_LN 3
#define COL_PI 4
#define COL_SO 5
#define COL_BD 6
#define COL_CD 7
#define COL_OR 8
#define COL_MI 9
#define COL_SU 10
#define COL_SG 11
#define COL_TW 12
#define COL_OW 13
#define COL_ST 14
#define COL_EX 15
#define COL_LC 16
#define COL_RC 17
#define COL_EC 18
#define COL_TC 19
#define COL_SP 20
#define COL_MA 21
#define COL_HI 22
#define COL_DU 23
#define COL_SA 24
#define NUM_COLS 25
/* Names of the terminal strings. */
static char* colnames[] = { "no", "fi", "di", "ln", "pi", "so", "bd", "cd", "or", "mi", "su", "sg",
"tw", "ow", "st", "ex", "lc", "rc", "ec", "tc", "sp", "ma", "hi", "du", "sa", NULL };
/* Default values. */
static char* defcols[]
= { "0", "0", "1;31", "1;36", "33", "1;35", "1;33", "1;33", NULL, NULL, "37;41", "30;43",
"30;42", "34;42", "37;44", "1;32", "\033[", "m", NULL, "0", "0", "7", NULL, NULL, "0" };
static char* fzf_tab_module_version;
struct pattern {
Patprog pat;
char color[50];
};
static int opt_list_type = OPT_INVALID;
static int pat_cnt = 0;
static struct pattern* name_color = NULL;
static char mode_color[NUM_COLS][20];
// TODO: use ZLS_COLORS ?
int compile_patterns(char* nam, char** list_colors)
{
int i, j;
// clean old name_color and set pat_cnt = 0
if (pat_cnt != 0) {
while (--pat_cnt) {
freepatprog(name_color[pat_cnt].pat);
}
free(name_color);
}
// initialize mode_color with default value
for (i = 0; i < NUM_COLS; i++) {
if (defcols[i]) {
strcpy(mode_color[i], defcols[i]);
}
}
// empty array, just return
if (list_colors == NULL) {
return 0;
}
// how many pattens?
while (list_colors[pat_cnt] != NULL) {
pat_cnt++;
}
name_color = zshcalloc(pat_cnt * sizeof(struct pattern));
for (i = 0; i < pat_cnt; i++) {
char* pat = ztrdup(list_colors[i]);
char* color = strrchr(pat, '=');
if (color == NULL)
continue;
*color = '\0';
tokenize(pat);
remnulargs(pat);
// mode=color
bool skip = false;
for (j = 0; j < NUM_COLS; j++) {
if (strpfx(colnames[j], list_colors[i])) {
strcpy(mode_color[j], color + 1);
name_color[i].pat = NULL;
skip = true;
}
}
if (skip) {
continue;
}
// name=color
if ((name_color[i].pat = patcompile(pat, PAT_ZDUP, NULL))) {
strcpy(name_color[i].color, color + 1);
}
free(pat);
}
return 0;
}
static char get_suffix(char* file, const struct stat* sb)
{
struct stat sb2;
mode_t filemode = sb->st_mode;
if (S_ISLNK(filemode))
if (strpfx(mode_color[COL_LN], "target")) {
if (stat(file, &sb2) == -1) {
return '@';
}
return get_suffix(file, &sb2);
} else {
return '@';
}
else
return file_type(filemode);
}
const char* get_color(char* file, const struct stat* sb)
{
// no list-colors, return NULL
if (pat_cnt == 0) {
return NULL;
}
const char* ret;
if ((ret = colorize_from_mode(file, sb)) || (ret = colorize_from_name(file))) {
return ret;
}
return mode_color[COL_FI];
}
const char* colorize_from_name(char* file)
{
int i;
for (i = 0; i < pat_cnt; i++) {
if (name_color && name_color[i].pat && pattry(name_color[i].pat, file)) {
return name_color[i].color;
}
}
return NULL;
}
const char* colorize_from_mode(char* file, const struct stat* sb)
{
struct stat sb2;
switch (sb->st_mode & S_IFMT) {
case S_IFDIR:
return mode_color[COL_DI];
case S_IFLNK: {
if (strpfx(mode_color[COL_LN], "target")) {
if (stat(file, &sb2) == -1) {
return mode_color[COL_OR];
}
return get_color(file, &sb2);
}
}
case S_IFIFO:
return mode_color[COL_PI];
case S_IFSOCK:
return mode_color[COL_SO];
case S_IFBLK:
return mode_color[COL_BD];
case S_IFCHR:
return mode_color[COL_CD];
default:
break;
}
if (sb->st_mode & S_ISUID) {
return mode_color[COL_SU];
} else if (sb->st_mode & S_ISGID) {
return mode_color[COL_SG];
// tw ow st ??
} else if (sb->st_mode & S_IXUSR) {
return mode_color[COL_EX];
}
/* Check for suffix alias */
size_t len = strlen(file);
/* shortest valid suffix format is a.b */
if (len > 2) {
const char* suf = file + len - 1;
while (suf > file + 1) {
if (suf[-1] == '.') {
if (sufaliastab->getnode(sufaliastab, suf)) {
return mode_color[COL_SA];
}
break;
}
suf--;
}
}
return NULL;
}
struct {
char** array;
size_t len;
size_t size;
} ftb_compcap;
// Usage:
// initialize fzf-tab-generate-compcap -i
// output to _ftb_compcap fzf-tab-generate-compcap -o
// add a entry fzf-tab-generate-compcap word desc opts
static int bin_fzf_tab_compcap_generate(char* nam, char** args, Options ops, UNUSED(int func))
{
int i;
if (OPT_ISSET(ops, 'o')) {
// write final result to _ftb_compcap
setaparam("_ftb_compcap", ftb_compcap.array);
ftb_compcap.array = NULL;
return 0;
} else if (OPT_ISSET(ops, 'i')) {
// init
if (ftb_compcap.array)
freearray(ftb_compcap.array);
ftb_compcap.array = zshcalloc(1024 * sizeof(char*));
ftb_compcap.len = 0, ftb_compcap.size = 1024;
return 0;
}
if (ftb_compcap.array == NULL) {
zwarnnam(nam, "please initialize it first");
return 1;
}
// get paramaters
char **words = getaparam(args[0]), **dscrs = getaparam(args[1]), *opts = getsparam(args[2]);
if (!words || !dscrs || !opts) {
zwarnnam(nam, "wrong argument");
return 1;
}
char *bs = metafy("\2", 1, META_DUP), *nul = metafy("\0word\0", 6, META_DUP);
size_t dscrs_cnt = arrlen(dscrs);
for (i = 0; words[i]; i++) {
// TODO: replace '\n'
char* dscr = i < dscrs_cnt ? dscrs[i] : words[i];
char *buffer = ftb_strcat(NULL, 5, dscr, bs, opts, nul, words[i]);
ftb_compcap.array[ftb_compcap.len++] = buffer;
if (ftb_compcap.len == ftb_compcap.size) {
ftb_compcap.array
= zrealloc(ftb_compcap.array, (1024 + ftb_compcap.size) * sizeof(char*));
ftb_compcap.size += 1024;
memset(ftb_compcap.array + ftb_compcap.size - 1024, 0, 1024 * sizeof(char*));
}
}
zsfree(bs);
zsfree(nul);
return 0;
}
// cat n string, return the pointer to the new string
// dst will be reallocated if is not big enough
// if dst is NULL, it will be allocated
char* ftb_strcat(char* dst, int n, ...)
{
int i, idx;
va_list valist;
va_start(valist, n);
char* final = dst ? zrealloc(dst, 128) : zalloc(128);
size_t size = 128, max_len = 128 - 1;
dst = final;
for (i = 0, idx = 0; i < n; i++) {
char* src = va_arg(valist, char*);
if (src == NULL)
continue;
for (; *src != '\0'; dst++, src++, idx++) {
if (idx == max_len) {
size += size / 2;
final = zrealloc(final, size);
max_len = size - 1;
dst = &final[idx];
}
*dst = *src;
}
}
*dst = '\0';
va_end(valist);
return final;
}
struct file_color {
char *fc_begin;
char *fc_end;
char *sc;
char *suffix;
};
// accept an
static struct file_color* fzf_tab_colorize(char* file)
{
struct file_color *fc = zalloc(sizeof(struct file_color));
file = unmeta(file);
struct stat sb;
if (lstat(file, &sb) == -1) {
return NULL;
}
char suffix[2] = {0};
if (isset(opt_list_type)) {
suffix[0] = get_suffix(file, &sb);
}
const char* color = get_color(file, &sb);
char* symlink = NULL;
const char* symcolor = "";
if ((sb.st_mode & S_IFMT) == S_IFLNK) {
symlink = zshcalloc(PATH_MAX);
size_t end = readlink(file, symlink, PATH_MAX);
symlink[end] = '\0';
if (stat(file, &sb) == -1) {
symcolor = mode_color[COL_OR];
} else {
char tmp[PATH_MAX];
realpath(file, tmp);
symcolor = get_color(file, &sb);
}
}
if (color != NULL) {
fc->fc_begin = ftb_strcat(NULL, 3, mode_color[COL_LC], color, mode_color[COL_RC]);
fc->fc_end = ftb_strcat(NULL, 3, mode_color[COL_LC], "0", mode_color[COL_RC]);
} else {
fc->fc_begin = ztrdup("");
fc->fc_end = ztrdup("");
}
fc->suffix = ztrdup(suffix);
if (symlink != NULL) {
fc->sc = ftb_strcat(NULL, 7, mode_color[COL_LC], symcolor, mode_color[COL_RC],
symlink, mode_color[COL_LC], "0", mode_color[COL_RC]);
free(symlink);
} else {
fc->sc = ztrdup("");
}
fc->fc_begin = metafy(fc->fc_begin, -1, META_REALLOC);
fc->fc_end = metafy(fc->fc_end, -1, META_REALLOC);
fc->sc = metafy(fc->sc, -1, META_REALLOC);
fc->suffix = metafy(fc->suffix, -1, META_REALLOC);
return fc;
}
static int bin_fzf_tab_candidates_generate(char* nam, char** args, Options ops, UNUSED(int func))
{
int i, j;
if (OPT_ISSET(ops, 'i')) {
// compile list_colors pattern
if (*args == NULL) {
zwarnnam(nam, "please specify an array");
return 1;
} else {
char** array = getaparam(*args);
return compile_patterns(nam, array);
}
}
char **ftb_compcap = getaparam("_ftb_compcap"), **group_colors = getaparam("group_colors"),
*default_color = getsparam("default_color"), *prefix = getsparam("prefix");
size_t group_colors_len = arrlen(group_colors);
size_t ftb_compcap_len = arrlen(ftb_compcap);
char *bs = metafy("\b", 1, META_DUP), *nul = metafy("\0", 1, META_DUP),
*soh = metafy("\2", 1, META_DUP);
char **candidates = zshcalloc(sizeof(char*) * (ftb_compcap_len + 1)),
*filepath = zshcalloc(PATH_MAX * sizeof(char)), *dpre = zshcalloc(128),
*dsuf = zshcalloc(128);
char* first_word = NULL;
int same_word = 1;
for (i = 0; i < ftb_compcap_len; i++) {
char *word = "", *group = NULL, *realdir = NULL;
strcpy(dpre, "");
strcpy(dsuf, "");
// extract all the variables what we need
char** compcap = sepsplit(ftb_compcap[i], soh, 1, 0);
char* desc = compcap[0];
char** info = sepsplit(compcap[1], nul, 1, 0);
for (j = 0; info[j]; j += 2) {
if (!strcmp(info[j], "word")) {
word = info[j + 1];
// unquote word
parse_subst_string(word);
remnulargs(word);
untokenize(word);
} else if (!strcmp(info[j], "group")) {
group = info[j + 1];
} else if (!strcmp(info[j], "realdir")) {
realdir = info[j + 1];
}
}
// check if all the words are the same
if (first_word == NULL) {
first_word = ztrdup(word);
} else if (same_word && strcmp(word, first_word) != 0) {
same_word = 0;
}
// add character and color to describe the type of the files
if (realdir) {
filepath = ftb_strcat(filepath, 2, realdir, word);
struct file_color *fc = fzf_tab_colorize(filepath);
if (fc != NULL) {
dpre = ftb_strcat(dpre, 2, fc->fc_end, fc->fc_begin);
if (fc->sc[0] != '\0') {
dsuf = ftb_strcat(dsuf, 4, fc->fc_end, fc->suffix, " -> ", fc->sc);
} else {
dsuf = ftb_strcat(dsuf, 2, fc->fc_end, fc->suffix);
}
if (dpre[0] != '\0') {
setiparam("colorful", 1);
}
free(fc);
}
}
char* result = zshcalloc(256 * sizeof(char));
// add color to description if they have group index
if (group) {
// use strtol ?
int group_index = atoi(group);
char* color = group_index >= group_colors_len ? "" : group_colors[group_index - 1];
// add prefix
result = ftb_strcat(
result, 11, group, bs, color, prefix, dpre, nul, group, bs, desc, nul, dsuf);
} else {
result = ftb_strcat(result, 6, default_color, dpre, nul, desc, nul, dsuf);
}
// quotedzputs(result, stdout);
// putchar('\n');
candidates[i] = result;
freearray(info);
freearray(compcap);
}
setaparam("tcandidates", candidates);
setiparam("same_word", same_word);
zsfree(dpre);
zsfree(dsuf);
zsfree(filepath);
zsfree(first_word);
return 0;
}
static struct builtin bintab[] = {
BUILTIN("fzf-tab-compcap-generate", 0, bin_fzf_tab_compcap_generate, 0, -1, 0, "io", NULL),
BUILTIN("fzf-tab-candidates-generate", 0, bin_fzf_tab_candidates_generate, 0, -1, 0, "i", NULL),
};
static struct paramdef patab[] = {
STRPARAMDEF("FZF_TAB_MODULE_VERSION", &fzf_tab_module_version),
};
// clang-format off
static struct features module_features = {
bintab, sizeof(bintab) / sizeof(*bintab),
NULL, 0,
NULL, 0,
patab, sizeof(patab) / sizeof(*patab),
0,
};
// clang-format on
int setup_(UNUSED(Module m)) { return 0; }
int features_(Module m, char*** features)
{
*features = featuresarray(m, &module_features);
return 0;
}
int enables_(Module m, int** enables) { return handlefeatures(m, &module_features, enables); }
int boot_(UNUSED(Module m))
{
fzf_tab_module_version = ztrdup("0.2.2");
// different zsh version may have different value of list_type's index
// so query it dynamically
opt_list_type = optlookup("list_types");
return 0;
}
int cleanup_(UNUSED(Module m)) { return setfeatureenables(m, &module_features, NULL); }
int finish_(UNUSED(Module m))
{
printf("fzf-tab module unloaded.\n");
fflush(stdout);
return 0;
}