summaryrefslogtreecommitdiffstats
path: root/main.c
diff options
context:
space:
mode:
authorSadeep Madurange <sadeep@asciimx.com>2026-04-04 11:55:08 +0800
committerSadeep Madurange <sadeep@asciimx.com>2026-04-04 14:52:02 +0800
commitcf421aeff4faa15a2a80495345ae76023830ca86 (patch)
treed417dd85f75fc4c532bb0954ed59bea87d350914 /main.c
parent3af329abee56900dd42cb305bd7a431ab3ded90a (diff)
downloadcvn-cf421aeff4faa15a2a80495345ae76023830ca86.tar.gz
Unit tests and fix hash collisions, deletions.
Diffstat (limited to 'main.c')
-rw-r--r--main.c508
1 files changed, 0 insertions, 508 deletions
diff --git a/main.c b/main.c
deleted file mode 100644
index 5202715..0000000
--- a/main.c
+++ /dev/null
@@ -1,508 +0,0 @@
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <fnmatch.h>
-#include <ftw.h>
-#include <limits.h>
-#include <sha1.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/wait.h>
-
-#define VCX_DIR ".vcx"
-#define HEAD VCX_DIR "/head"
-#define TMP_DIR VCX_DIR "/tmp"
-#define OBJ_DIR VCX_DIR "/obj"
-
-#define EXCLUDE_PATHS ".vcxignore"
-
-#define MAX_DEPTH 256
-#define BUF_LEN 8192
-#define PATH_LEN PATH_MAX
-#define HASH_LEN SHA1_DIGEST_STRING_LENGTH
-
-#define KIND_DEL 'D'
-#define KIND_MOD 'M'
-#define KIND_NEW 'N'
-
-#define MALLOC(s) _xmalloc((s), __FILE__, __LINE__)
-#define REALLOC(p, s) _xrealloc((p), (s), __FILE__, __LINE__)
-
-static inline void init(int argc, char *argv[]);
-static inline void status(int argc, char *argv[]);
-static inline void add(int argc, char *argv[]);
-
-static inline int cmplink(const char *link1, const char *link2);
-static inline int cmpfile(const char *file1, const char *file2);
-
-static inline void stage_init(void);
-static inline void stage_entry(const char *path, char kind, int islnk);
-static inline void print_status(const char *file, char kind, int islnk);
-
-static inline int scan_head(const char * path, const struct stat *st, int flag, struct FTW *ftwbuf);
-static inline int scan_tree(const char * path, const struct stat *st, int flag, struct FTW *ftwbuf);
-
-static inline void mkcopy(const char *path, char *dst);
-static inline void mklink(const char *src, const char *obj);
-
-static inline void mkdirs(const char *path);
-static inline void copy_link(const char *src);
-static inline void copy_file(const char *src, const char *dst);
-
-static inline int lnklen(const char *lnk);
-static inline char *format_path(const char *fmt, ...);
-
-static inline void *_xmalloc(size_t s, const char *file, int line);
-static inline void *_xrealloc(void *ptr, size_t s, const char *file, int line);
-
-struct command {
- char *name;
- void (*func)(int argc, char *argv[]);
-};
-
-struct command cmd[] = {
- {"init", init},
- {"add", add},
- {"status", status},
- {NULL, NULL}
-};
-
-int main(int argc, char *argv[])
-{
- if (argc < 2)
- errx(1, "Usage: %s <command> [<args>]", argv[0]);
-
- for (int i = 0; cmd[i].name != NULL; i++) {
- if (strcmp(argv[1], cmd[i].name) == 0) {
- cmd[i].func(argc - 1, argv + 1);
- return 0;
- }
- }
- return 0;
-}
-
-static inline void init(int argc, char *argv[])
-{
- if (mkdir(VCX_DIR, 0755) == -1) {
- if (errno != EEXIST)
- err(1, "Failed to create %s", VCX_DIR);
- }
-
- if (mkdir(HEAD, 0755) == -1) {
- if (errno != EEXIST)
- err(1, "Failed to create %s", HEAD);
- }
-
- if (mkdir(OBJ_DIR, 0755) == -1) {
- if (errno != EEXIST)
- err(1, "Failed to create %s", OBJ_DIR);
- }
-
- printf("Repository ready\n");
-}
-
-static void (*scan_res_cb)(const char *file, char kind, int islnk) = NULL;
-
-static inline void status(int argc, char *argv[])
-{
- scan_res_cb = print_status;
-
- if (nftw(HEAD, scan_head, MAX_DEPTH, FTW_PHYS) == -1)
- err(1, "Failed to scan head");
-
- if (nftw(".", scan_tree, MAX_DEPTH, FTW_PHYS) == -1)
- err(1, "Failed to scan work tree");
-}
-
-static inline void add(int argc, char *argv[])
-{
- size_t i;
- struct stat st;
- char *wt_path, *hd_path;
-
- if (argc < 2)
- errx(1, "Usage: %s [<files>]", argv[0]);
-
- stage_init();
- scan_res_cb = stage_entry;
-
- for (i = 1; i < argc; i++) {
- wt_path = argv[i];
- if (lstat(wt_path, &st) == -1)
- err(1, "%s", wt_path);
-
- hd_path = format_path("%s/%s", HEAD, wt_path);
-
- if (nftw(hd_path, scan_head, MAX_DEPTH, FTW_PHYS) == -1)
- err(1, "Failed to scan head");
-
- if (nftw(wt_path, scan_tree, MAX_DEPTH, FTW_PHYS) == -1)
- err(1, "Failed to scan work tree");
- }
-}
-
-int scan_head(const char * path, const struct stat *sb, int flag,
- struct FTW *ftwbuf)
-{
- char *wt_path;
- struct stat wt_stat;
-
- if (flag == FTW_F) {
- const char *filename = path + ftwbuf->base;
- wt_path = format_path("./%s", filename);
-
- if (lstat(wt_path, &wt_stat) == -1) {
- if (errno == ENOENT) {
- // file/link doesn't matter for deletions
- scan_res_cb(wt_path, KIND_DEL, 0);
- return 0;
- }
- else
- err(1, "lstat failed for %s", wt_path);
- }
-
- if (wt_stat.st_size == sb->st_size &&
- wt_stat.st_mtime == sb->st_mtime)
- return 0; // no change
-
- if (S_ISREG(wt_stat.st_mode)) {
- if (cmpfile(path, wt_path) == 1)
- scan_res_cb(wt_path, KIND_MOD, 0);
- } else if (S_ISLNK(wt_stat.st_mode)) {
- if (cmplink(path, wt_path) == 1)
- scan_res_cb(wt_path, KIND_MOD, 1);
- }
- }
-
- return 0;
-}
-
-static inline int scan_tree(const char * path, const struct stat *st,
- int flag, struct FTW *ftwbuf)
-{
- char *hd_path;
-
- if (fnmatch("./.vcx/*", path, 0) == 0)
- return 0;
-
- if (flag == FTW_F) {
- const char *filename = path + ftwbuf->base;
- hd_path = format_path("%s/%s", HEAD, filename);
- if (access(hd_path, F_OK) != 0)
- scan_res_cb(path, KIND_NEW, S_ISLNK(st->st_mode) != 0);
- }
-
- return 0;
-}
-
-static inline int cmpfile(const char *file1, const char *file2)
-{
- int rc;
- int fd1, fd2;
- ssize_t r1, r2;
- char buf1[BUF_LEN], buf2[BUF_LEN];
-
- if ((fd1 = open(file1, O_RDONLY)) < 0)
- err(1, "Couldn't open %s to compare", file1);
-
- if ((fd2 = open(file2, O_RDONLY)) < 0)
- err(1, "Couldn't open %s to compare", file2);
-
- rc = 0;
- do {
- r1 = read(fd1, buf1, BUF_LEN);
- r2 = read(fd2, buf2, BUF_LEN);
-
- if (r1 != r2 || memcmp(buf1, buf2, r1) != 0) {
- rc = 1;
- break;
- }
- } while (r1 > 0);
-
- close(fd1);
- close(fd2);
- return rc;
-}
-
-static inline int cmplink(const char *link1, const char *link2)
-{
- ssize_t len1, len2;
- int buflen1, buflen2;
-
- buflen1 = lnklen(link1);
- char buf1[buflen1];
- len1 = readlink(link1, buf1, sizeof(buf1) - 1);
-
- buflen2 = lnklen(link2);
- char buf2[buflen2];
- len2 = readlink(link2, buf2, sizeof(buf2) - 1);
-
- if (len1 < 0 || len2 < 0)
- err(1, "readlink error on %s or %s", link1, link2);
-
- if (len1 != len2)
- return 1;
-
- buf1[len1] = '\0';
- buf2[len2] = '\0';
-
- return strcmp(buf1, buf2);
-}
-
-static inline void stage_init(void)
-{
- pid_t rm_pid, cp_pid;
-
- rm_pid = fork();
- if (rm_pid == 0) {
- if (access(TMP_DIR, F_OK) == 0) {
- execlp("rm", "rm", "-rf", TMP_DIR, NULL);
- err(1, "Failed to delete %s", TMP_DIR);
- }
- _exit(0);
- } else {
- waitpid(rm_pid, NULL, 0);
- cp_pid = fork();
- if (cp_pid == 0) {
- execlp("cp", "cp", "-RP", HEAD, TMP_DIR, NULL);
- err(1, "Failed to copy %s to %s", HEAD, TMP_DIR);
- }
- waitpid(cp_pid, NULL, 0);
- }
-}
-
-static inline void stage_entry(const char *path, char kind, int islnk)
-{
- char objname[HASH_LEN];
-
- switch (kind) {
- case KIND_MOD:
- break;
- case KIND_NEW:
- if (!islnk) {
- mkcopy(path, objname);
- mklink(path, objname);
- } else
- copy_link(path);
- break;
- case KIND_DEL:
- break;
- default:
- break;
- }
-}
-
-static inline void mkcopy(const char *path, char *dst)
-{
- int len;
- char *obj_path;
-
- len = strlen(path);
-
- if (SHA1Data((const uint8_t *)path, len, dst) == NULL)
- err(1, "Failed to compute hash for %s", path);
-
- obj_path = format_path("%s/%s.tmp", OBJ_DIR, dst);
- copy_file(path, obj_path);
-}
-
-static inline void mklink(const char *src, const char *obj)
-{
- char *lnk, *dst;
-
- lnk = format_path("../obj/%s.tmp", obj);
- dst = format_path("%s/%s", TMP_DIR, src);
-
- mkdirs(dst);
-
- if (symlink(lnk, dst) != 0)
- err(1, "symlink %s", src);
-}
-
-static inline void print_status(const char *file, char kind, int islnk)
-{
- printf("[%c] %s\n", kind, file);
-}
-
-static inline int lnklen(const char *lnk)
-{
- int len;
- struct stat st;
-
- if (lstat(lnk, &st) != 0)
- err(1, "lstat %s", lnk);
-
- len = st.st_size + 1;
- if (PATH_LEN < len)
- errx(1, "Link too long: %s", lnk);
-
- return len;
-}
-
-static inline int diff(const char *file1, const char *file2)
-{
- pid_t pid;
- int null_fd, status, rc;
-
- pid = fork();
-
- if (pid == 0) {
- null_fd = open("/dev/null", O_WRONLY);
- if (null_fd == -1)
- err(1, "open() failed on /dev/null");
-
- dup2(null_fd, STDOUT_FILENO);
- dup2(null_fd, STDERR_FILENO);
- close(null_fd);
-
- execlp("diff", "diff", "-q", file1, file2, (char *)NULL);
- err(1, "execlp failed for diff");
- } else if (pid > 0) {
- waitpid(pid, &status, 0);
- // diff returns 0 if files are same, 1 if different, >1 if error
- rc = WEXITSTATUS(status);
- if (rc > 1)
- err(1, "diff error");
- return rc;
- }
-
- err(1, "fork");
-}
-
-static inline void copy_file(const char *src, const char *dst)
-{
- int fdin, fdout;
- struct stat st;
- char buf[BUF_LEN], *outptr;
- ssize_t nread, nwrite, res;
-
- if ((fdin = open(src, O_RDONLY)) < 0)
- err(1, "open failed for %s", src);
-
- if (fstat(fdin, &st) < 0) {
- close(fdin);
- err(1, "fstat failed for %s", src);
- }
-
- mkdirs(dst);
-
- fdout = open(dst, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode & 0777);
- if (fdout < 0) {
- close(fdin);
- err(1, "open failed for %s", dst);
- }
-
- while ((nread = read(fdin, buf, sizeof(buf))) > 0) {
- nwrite = 0;
- outptr = buf;
-
- // Handle partial writes (possible with large files/slow disks)
- while (nwrite < nread) {
- res = write(fdout, outptr + nwrite, nread - nwrite);
- if (res < 0) {
- if (errno == EINTR) // interrupted by a signal - retry
- continue;
- close(fdin);
- close(fdout);
- err(1, "Copy error on %s", src); // bad error: e.g, disk full
- }
- nwrite += res;
- }
- }
-
- close(fdin);
- close(fdout);
-}
-
-static inline void copy_link(const char *src)
-{
- int len;
- ssize_t n;
- char *dst;
-
- // todo: use path_buf
- len = lnklen(src);
- char target[len];
- n = readlink(src, target, len);
- if (n == -1)
- err(1, "readlink %s", src);
-
- target[n] = '\0';
-
- dst = format_path("%s/%s", TMP_DIR, src);
- mkdirs(dst);
-
- if (symlink(target, dst) != 0)
- err(1, "Link copy error %s", src);
-}
-
-static inline void mkdirs(const char *path)
-{
- char *p;
- int pathlen;
-
- static char buf[PATH_LEN];
-
- pathlen = strlen(path);
- if (PATH_LEN < pathlen + 1)
- errx(1, "Path too long: %s", path);
-
- buf[0] = '\0';
- strcpy(buf, path);
-
- for (p = buf; *p; p++) {
- if (*p == '/') {
- *p = '\0';
- if (mkdir(buf, 0755) != 0) {
- if (errno != EEXIST)
- err(1, "mkdir failed for %s", buf);
- }
- *p = '/';
- }
- }
-}
-
-static char *path_buf[2][PATH_LEN];
-
-static inline char *format_path(const char *fmt, ...)
-{
- int rc;
- va_list args;
- static uint8_t i = 1;
-
- i ^= 1;
- va_start(args, fmt);
- rc = vsnprintf((char *)path_buf[i], PATH_LEN, fmt, args);
- va_end(args);
-
- if (rc < 0)
- err(1, "vsnprintf");
-
- if (rc >= PATH_LEN)
- errx(1, "Path too long");
-
- return (char *)path_buf[i];
-}
-
-static inline void *_xmalloc(size_t s, const char *file, int line)
-{
- void *p;
-
- if (!(p = malloc(s)))
- err(1, "%s:%d: malloc", file, line);
- return p;
-}
-
-static inline void *_xrealloc(void *ptr, size_t s, const char *file,
- int line)
-{
- void *p;
-
- if (!(p = realloc(ptr, s)))
- err(1, "%s:%d: realloc", file, line);
- return p;
-}
-