add vidir rewrite

This commit is contained in:
2025-12-08 08:26:51 -05:00
parent 9f3f4a83b5
commit fef96fbff0
2 changed files with 202 additions and 1 deletions

View File

@@ -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)

198
vidir.c Normal file
View File

@@ -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, 0755); // best-effort
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("'%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("'%s' => '%s'\n", old, p);
}
free(items[num - 1].path);
items[num - 1].path = strdup(p);
}
}
fclose(tmp);
unlink(tmpname);
return error_flag;
}