summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSadeep Madurange <sadeep@asciimx.com>2026-03-14 19:45:55 +0800
committerSadeep Madurange <sadeep@asciimx.com>2026-03-14 19:49:37 +0800
commitc39a32d111e7cccb9ef2063d6723d91ba3755565 (patch)
treecfc69cdd08342fa2795f04ec59798c652fd1bb3a
parent3eccf3989f17668676fb42b73843578b87eef569 (diff)
downloadcvn-c39a32d111e7cccb9ef2063d6723d91ba3755565.tar.gz
Implement status command in C.
-rw-r--r--.gitignore2
-rw-r--r--Makefile2
-rw-r--r--main.c234
-rw-r--r--mem.c22
-rw-r--r--mem.h10
5 files changed, 173 insertions, 97 deletions
diff --git a/.gitignore b/.gitignore
index 6714024..d1c9982 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,6 @@
cvn
.cvn/
-test_env/
+test_env*/
**/*.o
**/*.out
diff --git a/Makefile b/Makefile
index c0e08fb..9104cae 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/main.c b/main.c
index 65ec01d..cea633b 100644
--- a/main.c
+++ b/main.c
@@ -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;
+}
+
diff --git a/mem.c b/mem.c
deleted file mode 100644
index c1b1216..0000000
--- a/mem.c
+++ /dev/null
@@ -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;
-}
diff --git a/mem.h b/mem.h
deleted file mode 100644
index 546b004..0000000
--- a/mem.h
+++ /dev/null
@@ -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 */