commit e6d25d99f2fd2c1e13b06c7fa71784fa1bca93a4
parent 0c59ffc0c205b45ee88b8a0b1840b8831e51e6ae
Author: Louis Burda <quent.burda@gmail.com>
Date: Sun, 18 Jun 2023 04:17:21 +0200
Refactor for readability and fix warnings
Diffstat:
M | Makefile | | | 2 | ++ |
M | linkup.c | | | 233 | +++++++++++++++++++++++++++++++++++++++++-------------------------------------- |
2 files changed, 123 insertions(+), 112 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,6 +1,8 @@
PREFIX ?= /usr/local
BINDIR ?= /bin
+CFLAGS = -Wunused-variable -Wunused-function -Wuninitialized -Wconversion
+
all: linkup
clean:
diff --git a/linkup.c b/linkup.c
@@ -3,27 +3,37 @@
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
-#include <err.h>
#include <errno.h>
#include <string.h>
+#include <stdarg.h>
#include <stdbool.h>
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
-static const char usage[] = "linkup [-h] [-x FILE..] SOURCE DEST";
-
-static bool force;
+static bool force = true;
+static bool install = true;
-static const char **ignored;
-static int ignorecnt;
-static int ignorecap;
+static const char **ignored = NULL;
+static size_t ignorecnt = 0;
+static size_t ignorecap = 0;
-bool
-exists(int dirfd, const char *path)
+static void
+die(const char *fmt, ...)
{
- struct stat attr;
+ va_list ap;
+
+ va_start(ap, fmt);
+ fputs("linkup: ", stderr);
+ vfprintf(stderr, fmt, ap);
+ if (*fmt && fmt[strlen(fmt)-1] == ':') {
+ fputc(' ', stderr);
+ perror(NULL);
+ } else {
+ fputc('\n', stderr);
+ }
+ va_end(ap);
- return !fstatat(dirfd, path, &attr, AT_SYMLINK_NOFOLLOW);
+ exit(1);
}
void
@@ -31,40 +41,41 @@ normalize(const char *src, char *dst)
{
const char *sep, *tok;
char *end;
- int len;
+ size_t len;
end = dst;
- tok = src;
*end = '\0';
- do {
+
+ tok = src;
+ while (tok) {
sep = strchr(tok, '/');
- len = sep ? sep - tok : strlen(tok);
- if (!len || !strncmp(tok, ".", len))
- goto next;
- if (!strncmp(tok, "..", len)) {
- end = strrchr(dst, '/');
- if (!end) end = dst;
- *end = '\0';
- } else {
- *end++ = '/';
- strncpy(end, tok, len);
- end += len;
- *end = '\0';
+ len = sep ? (size_t) (sep - tok) : strlen(tok);
+ if (len && strncmp(tok, ".", len)) {
+ if (!strncmp(tok, "..", len)) {
+ end = strrchr(dst, '/');
+ if (!end) end = dst;
+ *end = '\0';
+ } else {
+ *end++ = '/';
+ strncpy(end, tok, len);
+ end += len;
+ *end = '\0';
+ }
}
-next:
- tok = sep + 1;
- } while (sep);
+ tok = sep ? sep + 1 : NULL;
+ }
}
-void
+
+static void
realparent(int dirfd, const char *dirpath, const char *file, char *parent)
{
- char tmpbuf[PATH_MAX+1];
+ char tmpbuf[PATH_MAX];
ssize_t len;
char *sep;
- if ((len = readlinkat(dirfd, file, tmpbuf, PATH_MAX)) == -1)
- err(1, "readlink");
+ if ((len = readlinkat(dirfd, file, tmpbuf, PATH_MAX-1)) == -1)
+ die("readlink '%s/%s':", dirpath, file);
tmpbuf[len] = '\0';
if (tmpbuf[0] != '/') { /* relative path */
@@ -77,147 +88,145 @@ realparent(int dirfd, const char *dirpath, const char *file, char *parent)
*sep = '\0';
}
-void
-do_install(const char *spath, const char *dpath)
+static void
+ignore(const char *arg)
{
- char spbuf[PATH_MAX], dpbuf[PATH_MAX];
+ if (ignorecnt == ignorecap) {
+ ignorecap = (ignorecap + 1) * 2;
+ ignored = realloc(ignored, ignorecap * sizeof(const char *));
+ }
+ ignored[ignorecnt] = arg;
+ ignorecnt++;
+}
+
+static void
+do_install(const char *src_path, const char *dst_path)
+{
+ char src_fpath[PATH_MAX], dst_fpath[PATH_MAX];
struct dirent *ent;
struct stat attr;
- DIR *sdir, *ddir;
- char *sep;
+ DIR *src_dir, *dst_dir;
int i;
- sdir = opendir(spath);
- if (!sdir) err(1, "opendir src");
+ src_dir = opendir(src_path);
+ if (!src_dir) die("opendir '%s'", src_path);
- ddir = opendir(dpath);
- if (!ddir) err(1, "opendir dst");
+ dst_dir = opendir(dst_path);
+ if (!dst_dir) die("opendir '%s'", dst_path);
+
+ while ((ent = readdir(src_dir))) {
+ if (!strcmp(ent->d_name, "."))
+ continue;
+ if (!strcmp(ent->d_name, ".."))
+ continue;
- while ((ent = readdir(sdir))) {
- /* skip ignored files */
for (i = 0; i < ignorecnt; i++) {
if (!strcmp(ignored[i], ent->d_name))
break;
}
if (i != ignorecnt) continue;
- /* skip default directories */
- if (!strcmp(ent->d_name, "."))
- continue;
- if (!strcmp(ent->d_name, ".."))
- continue;
-
- snprintf(spbuf, PATH_MAX, "%s/%s", spath, ent->d_name);
- snprintf(dpbuf, PATH_MAX, "%s/%s", dpath, ent->d_name);
+ snprintf(src_fpath, PATH_MAX, "%s/%s", src_path, ent->d_name);
+ snprintf(dst_fpath, PATH_MAX, "%s/%s", dst_path, ent->d_name);
/* ensure link destination does not exist */
- if (!fstatat(dirfd(ddir), ent->d_name,
+ if (!fstatat(dirfd(dst_dir), ent->d_name,
&attr, AT_SYMLINK_NOFOLLOW)) {
if ((attr.st_mode & S_IFMT) != S_IFLNK && !force)
- errx(1, "dst %s already exists", ent->d_name);
- if (remove(dpbuf))
- err(1, "remove dst %s", ent->d_name);
+ die("dst '%s' already exists", dst_fpath);
+ if (remove(dst_fpath))
+ die("remove '%s' failed:", dst_path);
}
- /* create link */
- if (symlink(spbuf, dpbuf) < 0)
- err(1, "link");
+ if (symlink(src_fpath, dst_fpath) < 0)
+ die("linking '%s' failed:", dst_fpath);
}
- /* clean dangling symlinks */
- while ((ent = readdir(ddir))) {
+ /* cleanup dangling symlinks */
+ while ((ent = readdir(dst_dir))) {
if (ent->d_type != DT_LNK)
continue;
/* must point into src */
- realparent(dirfd(ddir), dpath, ent->d_name, dpbuf);
- if (strcmp(dpbuf, spath))
+ realparent(dirfd(dst_dir), dst_path, ent->d_name, dst_fpath);
+ if (strcmp(dst_fpath, src_path))
continue;
/* with missing destination */
- if (fstatat(dirfd(ddir), ent->d_name, &attr, 0) < 0)
- unlinkat(dirfd(ddir), ent->d_name, 0);
+ if (faccessat(dirfd(dst_dir), ent->d_name, F_OK, 0))
+ unlinkat(dirfd(dst_dir), ent->d_name, 0);
}
- closedir(ddir);
- closedir(sdir);
+ closedir(dst_dir);
+ closedir(src_dir);
}
-void
-do_uninstall(const char *spath, const char *dpath)
+static void
+do_uninstall(const char *src_path, const char *dst_path)
{
struct dirent *ent;
- char dpbuf[PATH_MAX];
- char *sep;
- DIR *ddir;
+ char dst_fpath[PATH_MAX];
+ DIR *dst_dir;
- ddir = opendir(dpath);
- if (!ddir) err(1, "opendir dst");
+ dst_dir = opendir(dst_path);
+ if (!dst_dir) die("opendir '%s':", dst_path);
- while ((ent = readdir(ddir))) {
+ while ((ent = readdir(dst_dir))) {
if (ent->d_type != DT_LNK)
continue;
- realparent(dirfd(ddir), dpath, ent->d_name, dpbuf);
- if (!strcmp(dpbuf, spath))
- unlinkat(dirfd(ddir), ent->d_name, 0);
+ /* must point into src */
+ realparent(dirfd(dst_dir), dst_path, ent->d_name, dst_fpath);
+ if (strcmp(dst_fpath, src_path))
+ continue;
+
+ if (unlinkat(dirfd(dst_dir), ent->d_name, 0))
+ die("failed to remove '%s/%s':", dst_path, ent->d_name);
}
- closedir(ddir);
+ closedir(dst_dir);
}
int
main(int argc, const char **argv)
{
- char spath[PATH_MAX], dpath[PATH_MAX];
- const char *sarg, *darg;
+ char src_path[PATH_MAX], dst_path[PATH_MAX];
+ const char *src_arg, *dst_arg;
const char **arg;
- bool install;
- int i;
-
- ignored = NULL;
- ignorecap = 0;
- ignorecnt = 0;
- force = false;
- install = true;
- sarg = darg = NULL;
+ src_arg = dst_arg = NULL;
for (arg = &argv[1]; *arg; arg++) {
if (!strcmp(*arg, "-x")) {
- if (ignorecnt == ignorecap) {
- ignorecap = (ignorecap + 1) * 2;
- ignored = realloc(ignored,
- ignorecap * sizeof(const char *));
- }
- ignored[ignorecnt] = *arg;
- ignorecnt++;
+ ignore(*arg);
} else if (!strcmp(*arg, "-u")) {
install = false;
} else if (!strcmp(*arg, "-f")) {
force = true;
- } else if (!sarg) {
- sarg = *arg;
- } else if (!darg) {
- darg = *arg;
+ } else if (!src_arg) {
+ src_arg = *arg;
+ } else if (!dst_arg) {
+ dst_arg = *arg;
} else {
- printf("Usage: %s\n", usage);
- return 0;
+ break;
}
}
- if (!sarg || !darg) {
- fprintf(stderr, "Usage: %s\n", usage);
+ if (!src_arg || !dst_arg || *arg) {
+ fprintf(stderr, "Usage: linkup [-h] [-x FILE..] SRC DST\n");
return 1;
}
- if (!realpath(sarg, spath))
- err(1, "realpath src");
+ if (!realpath(src_arg, src_path))
+ die("realpath '%s':", src_path);
- if (!realpath(darg, dpath))
- err(1, "realpath dst");
+ if (!realpath(dst_arg, dst_path))
+ die("realpath '%s':", dst_path);
- if (install) do_install(spath, dpath);
- else do_uninstall(spath, dpath);
+ if (install) {
+ do_install(src_path, dst_path);
+ } else {
+ do_uninstall(src_path, dst_path);
+ }
free(ignored);
}