From c372a5f8bc5db6f1ae3933647d9c59d3756c6893 Mon Sep 17 00:00:00 2001 From: lisk77 Date: Wed, 20 Aug 2025 21:44:04 +0200 Subject: [PATCH] feat: added a simple filesystem tree walk and the status cli command to get a list of untracked files --- include/action_list.h | 8 +-- include/tree.h | 24 +++++++++ src/action_list.c | 2 +- src/main.c | 25 ++++++++- src/tree.c | 122 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 174 insertions(+), 7 deletions(-) create mode 100644 include/tree.h create mode 100644 src/tree.c diff --git a/include/action_list.h b/include/action_list.h index 390f052..eab87e8 100644 --- a/include/action_list.h +++ b/include/action_list.h @@ -14,15 +14,15 @@ typedef enum { // Represents a deletion or insertion into the files text typedef struct { ActionType type; - uint64_t line_original; - uint64_t line_changed; + size_t line_original; + size_t line_changed; } Action; // Dynamic array of Actions typedef struct { Action* actions; - uint64_t capacity; - uint64_t len; + size_t capacity; + size_t len; } ActionList; ActionList* new_list(); diff --git a/include/tree.h b/include/tree.h new file mode 100644 index 0000000..702b2c5 --- /dev/null +++ b/include/tree.h @@ -0,0 +1,24 @@ +#ifndef TREE_H +#define TREE_H + +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + char** paths; + size_t len; + size_t capacity; +} PathBuffer; + +PathBuffer* new_path_buffer(); +void add_path(PathBuffer*, char*); +void free_path_buffer(PathBuffer*); +char* find_root(const char*); +void walk(const char*, const char*, PathBuffer*); + +#endif // TREE_H diff --git a/src/action_list.c b/src/action_list.c index ef4be48..be0638e 100644 --- a/src/action_list.c +++ b/src/action_list.c @@ -25,7 +25,7 @@ void add_action(ActionList* list, Action action) { // Concatenate two ActionLists. Modifies list1 in place void append_list(ActionList* list1, ActionList* list2) { - uint64_t available_space = list1->capacity - list1->len; + size_t available_space = list1->capacity - list1->len; if (available_space < list2->len) { list1->capacity += list2->capacity; list1->actions = realloc(list1->actions, list1->capacity*sizeof(Action)); diff --git a/src/main.c b/src/main.c index 71a38b5..6164175 100644 --- a/src/main.c +++ b/src/main.c @@ -9,6 +9,7 @@ #include "file.h" #include "myers.h" #include "utilities.h" +#include "tree.h" static void usage(int exitcode) { printf("usage: merk []\n\n init Initializes a repository in the current directory\n diff Displays the difference between the given two files\n"); @@ -22,7 +23,7 @@ int main(int argc, char **argv) { const char* subcmd = argv[1]; - if (strcmp(subcmd, "diff") != 0 && strcmp(subcmd, "init") != 0) { + if (strcmp(subcmd, "diff") != 0 && strcmp(subcmd, "init") != 0 && strcmp(subcmd, "status") != 0) { fprintf(stderr, "ERROR: Unknown subcommand: '%s'\n", subcmd); usage(2); } @@ -55,7 +56,7 @@ int main(int argc, char **argv) { // Prints out a visual representation of the diff of the files provided else if (strcmp(subcmd, "diff") == 0) { if (argc != 4) { - printf("ERROR: too many/too little arguments given!\n"); + printf("ERROR: too many/little arguments given!\n"); printf("Usage: merk diff "); exit(1); } @@ -82,6 +83,26 @@ int main(int argc, char **argv) { if (!actions) printf("ERROR: something went wrong while taking the diff!"); else visualize_diff(file1, file2, actions); } + // TODO: Make it repo specific and not universal + // Gives a list of untracked files in the filesystem + else if (strcmp(subcmd, "status") == 0) { + if (argc != 2) { + printf("ERROR: too many arguments given!\n"); + printf("Usage: merk status"); + exit(1); + } + + printf("Untracked files:\n\n"); + PathBuffer* files = new_path_buffer(); + walk(".", "./", files); + + for (size_t i = 0; i < files->len; i++) { + printf("%s\n", files->paths[i]); + } + + free_path_buffer(files); + return 0; + } else { usage(1); } diff --git a/src/tree.c b/src/tree.c new file mode 100644 index 0000000..bb64b59 --- /dev/null +++ b/src/tree.c @@ -0,0 +1,122 @@ +#include "tree.h" + +PathBuffer* new_path_buffer() { + PathBuffer* buffer = calloc(1, sizeof(PathBuffer)); + if (!buffer) { + perror("ERROR: Failed to allocate PathBuffer"); + exit(1); + } + + buffer->capacity = 10; + buffer->len = 0; + + buffer->paths = calloc(buffer->capacity, sizeof(char*)); + if (!buffer->paths) { + perror("ERROR: Failed to allocate paths array"); + free(buffer); + exit(1); + } + + return buffer; +} + +void add_path(PathBuffer* buffer, char* path) { + if (buffer->len >= buffer->capacity) { + buffer->capacity *= 2; + char** new_paths = realloc(buffer->paths, buffer->capacity * sizeof(char*)); + if (!new_paths) { + perror("ERROR: PathBuffer realloc failed!"); + exit(1); + } + buffer->paths = new_paths; + } + + char* path_copy = strdup(path); + if (!path_copy) { + perror("ERROR: strdup failed in add_path!"); + exit(1); + } + + buffer->paths[buffer->len++] = path_copy; +} + +void free_path_buffer(PathBuffer* buffer) { + if (buffer) { + for (size_t i = 0; i < buffer->len; i++) { + free(buffer->paths[i]); + } + free(buffer->paths); + free(buffer); + } +} + +static int is_dot_or_dotdot(const char* s) { + return (s[0] == '.' && (s[1] == '\0' || (s[1] == '.' && s[2] == '\0'))); +} + +static char* join_path(const char* a, const char* b) { + size_t la = strlen(a); + size_t lb = strlen(b); + int need_sep = (la > 0 && a[la - 1] != '/'); + + char* s = calloc(la + (need_sep ? 1 : 0) + lb + 1, sizeof(char)); + if (!s) { + perror("ERROR: calloc failed in join_path!"); + exit(1); + } + + memcpy(s, a, la); + size_t p = la; + if (need_sep) s[p++] = '/'; + memcpy(s + p, b, lb + 1); + + return s; +} + +void walk(const char* base, const char* rel, PathBuffer* paths) { + char* dir_full = rel[0] ? join_path(base, rel) : strdup(base); + if (!dir_full) { + perror("ERROR: memory allocation failed in walk!"); + exit(1); + } + + DIR* dir = opendir(dir_full); + if (!dir) { + free(dir_full); + return; + } + + struct dirent* de; + while ((de = readdir(dir)) != NULL) { + if (is_dot_or_dotdot(de->d_name)) continue; + + char* child_rel = rel[0] ? join_path(rel, de->d_name) : strdup(de->d_name); + if (!child_rel) { + perror("ERROR: memory allocation failed in walk!"); + closedir(dir); + free(dir_full); + exit(1); + } + + char* child_full = join_path(base, child_rel); + + struct stat st; + if (lstat(child_full, &st) != 0) { + free(child_full); + free(child_rel); + continue; + } + + if (S_ISDIR(st.st_mode)) { + walk(base, child_rel, paths); + } else { + add_path(paths, child_rel); + } + + free(child_rel); + free(child_full); + } + + closedir(dir); + free(dir_full); +}