diff options
| author | Nakidai <nakidai@disroot.org> | 2025-06-11 18:47:21 +0300 |
|---|---|---|
| committer | Nakidai <nakidai@disroot.org> | 2025-06-11 18:47:21 +0300 |
| commit | b2e86523e97b343639a74ce64063bf566c939ddd (patch) | |
| tree | e030ef3f3de8bf58d3d50689c9a2cef6eeaaf7f9 | |
| download | fp-b2e86523e97b343639a74ce64063bf566c939ddd.tar.gz fp-b2e86523e97b343639a74ce64063bf566c939ddd.zip | |
Add code
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | LICENSE | 10 | ||||
| -rw-r--r-- | Makefile | 3 | ||||
| -rw-r--r-- | README | 15 | ||||
| -rw-r--r-- | fp.1 | 26 | ||||
| -rw-r--r-- | fp.c | 167 |
6 files changed, 222 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5b08531 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +fp diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7ae8865 --- /dev/null +++ b/LICENSE @@ -0,0 +1,10 @@ +Permission to use, copy, modify, and/or distribute this software for +any purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE +FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN +AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f4f4cbb --- /dev/null +++ b/Makefile @@ -0,0 +1,3 @@ +all: fp +clean: + rm -f fp diff --git a/README b/README new file mode 100644 index 0000000..81176a5 --- /dev/null +++ b/README @@ -0,0 +1,15 @@ +FP(1) General Commands Manual FP(1) + +NAME + fp - interactive file picker + +SYNOPSIS + fp path + +DESCRIPTION + fp searches for files from path. User can type something and this will + be visible on the screen. fp prints all the interactive stuff into + stderr and the result into stdout, so this means you can easily pipe this + program into another one. + +Linux 6.14.7-arch2-1 June 11, 2025 Linux 6.14.7-arch2-1 diff --git a/fp.1 b/fp.1 new file mode 100644 index 0000000..9e1a228 --- /dev/null +++ b/fp.1 @@ -0,0 +1,26 @@ +.Dd June 11, 2025 +.Dt FP 1 +.Os +. +.Sh NAME +.Nm fp +.Nd interactive file picker +. +.Sh SYNOPSIS +.Nm +.Ar path +. +.Sh DESCRIPTION +.Nm +searches for files from +.Ar path . +User can type something +and this will be visible on the screen. +.Nm +prints all the interactive stuff +into stderr +and the result +into stdout, +so this means +you can easily pipe this program +into another one. diff --git a/fp.c b/fp.c new file mode 100644 index 0000000..ac9ef2d --- /dev/null +++ b/fp.c @@ -0,0 +1,167 @@ +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <signal.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <dirent.h> +#include <termios.h> +#include <unistd.h> + + +struct result +{ + struct collision + { + size_t offset, length, fullness; + } c; + char name[NAME_MAX+1]; +} *results = 0; +size_t amount = 0; +struct termios state; + +int getch() +{ + int buf; + struct termios old; + + fflush(stdout); + + int res = tcgetattr(0, &old); + old.c_lflag &= ~ICANON & ~ECHO; + old.c_cc[VMIN] = 1; + old.c_cc[VTIME] = 0; + res = tcsetattr(0, TCSANOW, &old); + buf = getchar(); + old.c_lflag |= ICANON | ECHO; + res = tcsetattr(0, TCSANOW, &old); + return buf; +} + +void restore(void) +{ + tcsetattr(0, TCSANOW, &state); +} + +void getchhnd(int sig) +{ + exit(1); +} + +struct collision strsub(const char *outside, const char *inside) +{ + struct collision res = {0, 0}; + + size_t outlen = strlen(outside); + size_t inlen = strlen(inside); + + for (size_t i = 0; i < outlen; ++i) + { + size_t j; + for (j = 0; j < inlen && j < outlen - i; ++j) + if (outside[i + j] != inside[j]) + break; + if (res.length < j) + res.offset = i, + res.length = j, + res.fullness = outlen - j; + } + return res; +} + +/* it is inverted for generating descending array */ +int colcmp(const void *_left, const void *_right) +{ + const struct result *left = _left, *right = _right; + if (left->c.length != right->c.length) + return left->c.length < right->c.length ? 1 : -1; + if (left->c.fullness != right->c.fullness) + return left->c.fullness < right->c.fullness ? -1 : 1; + if (left->c.offset != right->c.offset) + return left->c.offset < right->c.offset ? 1 : -1; + return -strcmp(left->name, right->name); +} + +void loadpath(const char *path) +{ + errno = 0; + DIR *dir = opendir(path); + for (struct dirent *d; (d = readdir(dir));) + { + results = realloc(results, sizeof(*results) * ++amount); + if (!results) + err(1, "realloc()"); + strlcpy(results[amount - 1].name, d->d_name, sizeof(results->name)); + } + if (errno) + err(1, "readdir()"); +} + +void sortresults(const char *search) +{ + for (size_t i = 0; i < amount; ++i) + results[i].c = strsub(results[i].name, search); + qsort(results, amount, sizeof(struct result), colcmp); +} + +void render(const char *buf, size_t bufi) +{ + sortresults(buf); + fprintf(stderr, "\r: %s\n", buf); + for (size_t i = 0; i < 5; ++i) + fprintf(stderr, "`%s'\n", results[i].name); + fprintf(stderr, "\033[6A\033[%dC", 2 + bufi); + fflush(stderr); +} + +int main(int argc, char **argv) +{ + if (argc != 2 || !*argv[1]) + { + fprintf(stderr, "usage: %s path\n", *argv); + return 1; + } + + tcgetattr(0, &state); + for (int sig = 1; sig <= SIGRTMAX; ++sig) + signal(sig, getchhnd); + atexit(restore); + + loadpath(argv[1]); + + char buf[NAME_MAX+1] = {0}; + size_t bufi = 0; + + render("", 0); + for (int ch; (ch = getch());) + { + switch (ch) + { + case -1: case 4: case '\n': case '\r': + goto end; + case 127: + { + if (!bufi) + goto cont; + buf[--bufi] = 0; + } break; + default: + { + if (bufi == NAME_MAX) + goto cont; + buf[bufi++] = ch; + } break; + } + render(buf, bufi); +cont: + } +end: + fprintf(stderr, "\033[6B\rYou've chosen `", results[0].name); + fflush(stderr); + fprintf(stdout, "%s", results[0].name); + fflush(stdout); + fprintf(stderr, "'!\n"); +} |