commit d9c8d6daf3aacdf7b12b6c27a3503d794d8660e5
parent d3cdda678aaaee5540f8ff2e80d9d5c2d2ba44ef
Author: amrfti <andrew@kloet.net>
Date: Mon, 8 Dec 2025 09:12:18 -0500
add vidir
Diffstat:
| M | Makefile | | | 5 | ++++- |
| A | vidir.c | | | 198 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
2 files changed, 202 insertions(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
@@ -2,12 +2,15 @@ CC = cc
CFLAGS = -Wall -Wextra -O3
CURL_LIBS = -lcurl
-SOURCES = sb-cpu.c sb-date.c sb-memory.c sb-time.c sb-weather.c sb-battery.c
+SOURCES = vidir.c sb-cpu.c sb-date.c sb-memory.c sb-time.c sb-weather.c sb-battery.c
EXECUTABLES = $(SOURCES:.c=)
all: $(EXECUTABLES)
+vidir: vidir.c
+ $(CC) $(CFLAGS) $< -o $@
+
sb-weather: sb-weather.c
$(CC) $(CFLAGS) $< -o $@ $(CURL_LIBS)
diff --git a/vidir.c b/vidir.c
@@ -0,0 +1,198 @@
+// vidir.c - rewrite of Joey Hess's vidir in C
+// usage: ./vidir [--verbose] [dir|file|-]
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#define MAX_FILES 10000
+
+typedef struct {
+ int id;
+ char *path;
+} Item;
+
+static int verbose = 0;
+static Item items[MAX_FILES];
+static int n_items = 0;
+static int error_flag = 0;
+
+void die(const char *msg) {
+ perror(msg);
+ exit(1);
+}
+
+void add_item(const char *path) {
+ if (n_items >= MAX_FILES) {
+ fprintf(stderr, "too many files\n");
+ exit(1);
+ }
+ items[n_items].id = n_items + 1;
+ items[n_items].path = strdup(path);
+ n_items++;
+}
+
+void read_dir(const char *dirname) {
+ DIR *dir = opendir(dirname);
+ if (!dir) {
+ fprintf(stderr, "cannot read %s: %s\n", dirname, strerror(errno));
+ exit(1);
+ }
+ struct dirent *ent;
+ while ((ent = readdir(dir))) {
+ if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
+ continue;
+ char path[PATH_MAX];
+ snprintf(path, sizeof(path), "%s/%s", dirname, ent->d_name);
+ add_item(path);
+ }
+ closedir(dir);
+}
+
+int rm_path(const char *p) {
+ struct stat st;
+ if (lstat(p, &st) != 0)
+ return -1;
+ if (S_ISDIR(st.st_mode))
+ return rmdir(p);
+ else
+ return unlink(p);
+}
+
+int main(int argc, char *argv[]) {
+ char tmpname[] = "/tmp/vidirXXXXXX";
+ int fd;
+ FILE *tmp;
+ char line[PATH_MAX + 64];
+ char *editor;
+ char buf[PATH_MAX + 64];
+
+ // parse args
+ for (int i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "--verbose") || !strcmp(argv[i], "-v")) {
+ verbose = 1;
+ } else if (!strcmp(argv[i], "-")) {
+ // read stdin list
+ while (fgets(line, sizeof(line), stdin)) {
+ line[strcspn(line, "\r\n")] = 0;
+ if (*line)
+ add_item(line);
+ }
+ } else {
+ struct stat st;
+ if (stat(argv[i], &st) == 0 && S_ISDIR(st.st_mode))
+ read_dir(argv[i]);
+ else
+ add_item(argv[i]);
+ }
+ }
+ if (n_items == 0)
+ read_dir(".");
+
+ // write numbered list
+ fd = mkstemp(tmpname);
+ if (fd < 0)
+ die("mkstemp");
+ tmp = fdopen(fd, "w");
+ if (!tmp)
+ die("fdopen");
+ int digits = snprintf(NULL, 0, "%d", n_items);
+ for (int i = 0; i < n_items; i++)
+ fprintf(tmp, "%0*d\t%s\n", digits, items[i].id, items[i].path);
+ fclose(tmp);
+
+ // pick editor
+ editor = getenv("VISUAL");
+ if (!editor)
+ editor = getenv("EDITOR");
+ if (!editor)
+ editor = "vi";
+
+ snprintf(buf, sizeof(buf), "%s %s", editor, tmpname);
+ int ret = system(buf);
+ if (ret != 0) {
+ fprintf(stderr, "editor exited nonzero, aborting\n");
+ unlink(tmpname);
+ exit(1);
+ }
+
+ // re-read edited list
+ tmp = fopen(tmpname, "r");
+ if (!tmp)
+ die("fopen tmp");
+
+ char newname[PATH_MAX];
+ while (fgets(line, sizeof(line), tmp)) {
+ line[strcspn(line, "\r\n")] = 0;
+ if (strlen(line) == 0)
+ continue;
+ int num;
+ if (sscanf(line, "%d%[^\n]", &num, newname) < 1)
+ continue;
+ char *p = newname;
+ while (isspace((unsigned char)*p))
+ p++;
+ if (num <= 0 || num > n_items)
+ continue;
+ char *old = items[num - 1].path;
+ if (*p == '\0') {
+ // deleted line -> remove file
+ if (rm_path(old) != 0) {
+ fprintf(stderr, "failed to remove %s: %s\n", old, strerror(errno));
+ error_flag = 1;
+ } else if (verbose) {
+ printf("removed '%s'\n", old);
+ }
+ } else if (strcmp(p, old) != 0) {
+ // rename
+ char dirbuf[PATH_MAX];
+ strcpy(dirbuf, p);
+ char *d = dirname(dirbuf);
+ mkdir(d, 0777);
+ if (access(p, F_OK) == 0) {
+ char tmpname[PATH_MAX + 16]; // allow space for suffixes
+ int suffix = 0;
+ snprintf(tmpname, sizeof(tmpname), "%s~", p);
+ while (access(tmpname, F_OK) == 0) {
+ suffix++;
+ snprintf(tmpname, sizeof(tmpname), "%s~%d", p, suffix);
+ }
+ if (rename(p, tmpname) != 0) {
+ fprintf(stderr, "failed to rename '%s' -> '%s': %s\n", p, tmpname,
+ strerror(errno));
+ error_flag = 1;
+ } else if (verbose) {
+ printf("renamed '%s' -> '%s'\n", p, tmpname);
+ }
+ // update internal mapping so future renames see the new tmp location
+ for (int j = 0; j < n_items; j++) {
+ if (strcmp(items[j].path, p) == 0) {
+ free(items[j].path);
+ items[j].path = strdup(tmpname);
+ }
+ }
+ }
+
+ // now perform the intended rename
+ if (rename(old, p) != 0) {
+ fprintf(stderr, "failed to rename %s -> %s: %s\n", old, p,
+ strerror(errno));
+ error_flag = 1;
+ } else if (verbose) {
+ printf("renamed '%s' -> '%s'\n", old, p);
+ }
+ free(items[num - 1].path);
+ items[num - 1].path = strdup(p);
+ }
+ }
+ fclose(tmp);
+ unlink(tmpname);
+ return error_flag;
+}