diff options
| author | Sadeep Madurange <sadeep@asciimx.com> | 2026-03-14 19:45:55 +0800 |
|---|---|---|
| committer | Sadeep Madurange <sadeep@asciimx.com> | 2026-03-14 19:49:37 +0800 |
| commit | c39a32d111e7cccb9ef2063d6723d91ba3755565 (patch) | |
| tree | cfc69cdd08342fa2795f04ec59798c652fd1bb3a | |
| parent | 3eccf3989f17668676fb42b73843578b87eef569 (diff) | |
| download | cvn-c39a32d111e7cccb9ef2063d6723d91ba3755565.tar.gz | |
Implement status command in C.
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | main.c | 234 | ||||
| -rw-r--r-- | mem.c | 22 | ||||
| -rw-r--r-- | mem.h | 10 |
5 files changed, 173 insertions, 97 deletions
@@ -1,6 +1,6 @@ cvn .cvn/ -test_env/ +test_env*/ **/*.o **/*.out @@ -1,7 +1,7 @@ CC = cc TARGET = cvn -SRC = main.c mem.c +SRC = main.c OBJ = $(SRC:.c=.o) CFLAGS = -std=c99 -O3 -Wall -I/usr/local/include @@ -1,38 +1,54 @@ #include <err.h> #include <errno.h> +#include <fcntl.h> +#include <fnmatch.h> #include <ftw.h> +#include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/wait.h> -#define PATCH_DIR ".cvn" -#define BASE PATCH_DIR "/base" -#define EXCLUDE_PATHS PATCH_DIR "ignore" +#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 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 int cmp_links(const char *link1, const char *link2); +static inline int cmp_content(const char *file1, const char *file2); +static inline char *concat_path(const char *dir, const char *file); + +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 *_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}, - {"status", status}, - {NULL, NULL} + {"init", init}, {"status", status}, {NULL, NULL} }; int main(int argc, char *argv[]) { - int i; - if (argc < 2) errx(1, "Usage: %s <command> [<args>]", argv[0]); - for (i = 0; cmd[i].name != NULL; i++) { + 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; @@ -43,80 +59,172 @@ int main(int argc, char *argv[]) static inline void init(int argc, char *argv[]) { - int opt; - char *branch; - - optind = 1; - while ((opt = getopt(argc, argv, "b:")) != -1) { - switch (opt) { - case 'b': - branch = optarg; - break; - default: - break; - } + 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(PATCH_DIR, 0755) == -1) { + if (mkdir(OBJ_DIR, 0755) == -1) { if (errno != EEXIST) - err(1, "Failed to create repository"); + err(1, "Failed to create %s", OBJ_DIR); } - if (mkdir(BASE, 0755) == -1) { + if (mkdir(TMP_DIR, 0755) == -1) { if (errno != EEXIST) - err(1, "Failed to create base directory."); + err(1, "Failed to create %s", TMP_DIR); } - printf("Ready\n"); + printf("Repository ready\n"); } static inline void status(int argc, char *argv[]) { - FILE *fp; - pid_t pid; - int status; - int pipefd[2]; - char line[1024]; + if (nftw(HEAD, scan_head, MAX_DEPTH, FTW_PHYS) == -1) + err(1, "scan_head"); - if (pipe(pipefd) == -1) - err(1, "pipe()"); + if (nftw(".", scan_tree, MAX_DEPTH, FTW_PHYS) == -1) + err(1, "scan_tree"); +} - if ((pid = fork()) == -1) - err(1, "fork()"); +static char *path_buf = NULL; +static size_t path_buflen = 1024; - if (pid == 0) { - close(pipefd[0]); - - dup2(pipefd[1], STDOUT_FILENO); - close(pipefd[1]); - - if (access(EXCLUDE_PATHS, F_OK) == 0) { - char *args[] = {"diff", "-x", PATCH_DIR, "-X", - EXCLUDE_PATHS, "-rq", BASE, ".", NULL}; - execvp("diff", args); - } else { - char *args[] = {"diff", "-x", PATCH_DIR, "-rq", BASE, ".", NULL}; - execvp("diff", args); +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 = concat_path(".", filename); + if (lstat(wt_path, &wt_stat) == -1) { + if (errno == ENOENT) { + printf("[D] %s\n", wt_path); + return 0; + } + else + err(1, "lstat failed for %s", wt_path); } - err(1, "execvp"); - } else { - waitpid(pid, &status, 0); - close(pipefd[1]); - - if (!(fp = fdopen(pipefd[0], "r"))) - err(1, "fdopen()"); - const char *src = "Only in .: "; - const char *rep = "(N) "; + if (wt_stat.st_size == sb->st_size && + wt_stat.st_mtime == sb->st_mtime) + return 0; // no change - while (fgets(line, sizeof(line), fp) != NULL) { - if (strncmp(line, src, strlen(src)) == 0) - printf("%s%s", rep, line + strlen(src)); - else - printf("%s", line); + if (S_ISREG(wt_stat.st_mode)) { + if (cmp_content(path, wt_path) == 1) + printf("[M] %s\n", wt_path); + } else if (S_ISLNK(wt_stat.st_mode)) { + if (cmp_links(path, wt_path) == 1) + printf("[M] %s\n", wt_path); } + } + 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, FNM_PATHNAME) == 0) + return 0; - fclose(fp); + if (flag == FTW_F) { + const char *filename = path + ftwbuf->base; + hd_path = concat_path(HEAD, filename); + if (access(hd_path, F_OK) != 0) + printf("[N] %s\n", path); + } + return 0; +} + +static inline int cmp_content(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 int cmp_links(const char *link1, const char *link2) +{ + ssize_t len1, len2; + char buf1[PATH_MAX], buf2[PATH_MAX]; + + len1 = readlink(link1, buf1, sizeof(buf1) - 1); + 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 char *concat_path(const char *dir, const char *file) +{ + size_t path_len; + + if (!path_buf) + path_buf = MALLOC(sizeof(path_buf[0]) * path_buflen); + + path_len = strlen(dir) + strlen(file) + 2; + if (path_len > path_buflen) { + path_buflen <<= 1; + path_buf = REALLOC(path_buf, sizeof(path_buf[0]) * path_buflen); + } + + snprintf(path_buf, path_buflen, "%s/%s", dir, file); + return path_buf; +} + +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; +} + @@ -1,22 +0,0 @@ -#include <err.h> -#include <stdlib.h> - -#include "mem.h" - -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; -} - -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; -} @@ -1,10 +0,0 @@ -#ifndef MEM_H -#define MEM_H - -#define MALLOC(s) _xmalloc((s), __FILE__, __LINE__) -#define REALLOC(p, s) _xrealloc((p), (s), __FILE__, __LINE__) - -void *_xmalloc(size_t s, const char *file, int line); -void *_xrealloc(void *ptr, size_t s, const char *file, int line); - -#endif /* MEM_H */ |
