diff --git a/include/tree.h b/include/tree.h index 702b2c5..e5276da 100644 --- a/include/tree.h +++ b/include/tree.h @@ -18,7 +18,7 @@ typedef struct { 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*); +char* find_root(char*); +void walk(const char*, const char*, const char*, PathBuffer*); #endif // TREE_H diff --git a/include/utilities.h b/include/utilities.h index 08b5a0d..795707c 100644 --- a/include/utilities.h +++ b/include/utilities.h @@ -1,8 +1,13 @@ #ifndef UTILITIES_H #define UTILITIES_H -#include +#include +#include +#include #include +#include +#include +#include #include "file.h" #include "action_list.h" diff --git a/src/main.c b/src/main.c index 6164175..1354654 100644 --- a/src/main.c +++ b/src/main.c @@ -12,7 +12,7 @@ #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"); + printf("usage: merk []\n\n init Initializes a repository in the current directory\n diff Displays the difference between the given two files\n status Displays a list of modified and untracked files in the repository\n"); exit(exitcode); } @@ -83,8 +83,7 @@ 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 + // Gives a list of untracked files in the repo else if (strcmp(subcmd, "status") == 0) { if (argc != 2) { printf("ERROR: too many arguments given!\n"); @@ -92,15 +91,23 @@ int main(int argc, char **argv) { exit(1); } - printf("Untracked files:\n\n"); PathBuffer* files = new_path_buffer(); - walk(".", "./", files); + char relative[PATH_MAX] = ""; + char* root = find_root(relative); + if (!root) { + free_path_buffer(files); + return 1; + } + walk(root, relative, relative, files); + + printf("Untracked files:\n\n"); for (size_t i = 0; i < files->len; i++) { printf("%s\n", files->paths[i]); } free_path_buffer(files); + free(root); return 0; } else { diff --git a/src/tree.c b/src/tree.c index bb64b59..4d6ea72 100644 --- a/src/tree.c +++ b/src/tree.c @@ -73,16 +73,97 @@ static char* join_path(const char* a, const char* b) { 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); +char* find_root(char* relative) { + char* current_dir = calloc(PATH_MAX, sizeof(char)); + if (!current_dir) { + perror("ERROR: calloc in find_root failed!"); + return NULL; } - DIR* dir = opendir(dir_full); + if (!getcwd(current_dir, PATH_MAX)) { + perror("ERROR: getcwd in find_root failed!"); + free(current_dir); + return NULL; + } + + char* search_dir = strdup(current_dir); + if (!search_dir) { + perror("ERROR: strdup in find_root failed!"); + free(current_dir); + return NULL; + } + + while (1) { + size_t dir_len = strlen(search_dir); + size_t merk_path_len = dir_len + strlen("/.merk") + 1; + char* merk_path = calloc(merk_path_len, sizeof(char)); + if (!merk_path) { + perror("ERROR: calloc in find_root failed!"); + free(current_dir); + free(search_dir); + return NULL; + } + + snprintf(merk_path, merk_path_len, "%s/.merk", search_dir); + + struct stat st; + if (stat(merk_path, &st) == 0 && S_ISDIR(st.st_mode)) { + free(current_dir); + free(merk_path); + char* result = strdup(search_dir); + free(search_dir); + return result; + } + + free(merk_path); + + if (strcmp(search_dir, "/") == 0) { + break; + } + + char temp[PATH_MAX]; + snprintf(temp, sizeof(temp), "../%s", relative); + strcpy(relative, temp); + + char* parent = strrchr(search_dir, '/'); + if (parent == search_dir) { + search_dir[1] = '\0'; + } else if (parent) { + *parent = '\0'; + } else { + break; + } + } + free(current_dir); + free(search_dir); + printf("ERROR: you are in no merk repository!\nCreate a new repository with merk init\n"); + return NULL; +} + +void normalize_path(char* path, char* rel) { + if (strlen(rel) == 0) return; + + size_t path_len = strlen(path); + size_t rel_len = strlen(rel); + int consumers = rel_len / 3; + int counter = 0; + size_t latest = 0; + + for (size_t idx = rel_len; idx < path_len; idx++) { + if (path[idx] == '/') { + counter++; + latest = idx; + if (counter == consumers) break; + } + if (path[idx] == '\0') break; + } + + strcpy(path, path+latest+(counter > 0 ? 1 : 0)); +} + +void walk(const char* base, const char* base_rel, const char* rel, PathBuffer* paths) { + DIR* dir = opendir(base); if (!dir) { - free(dir_full); return; } @@ -90,33 +171,58 @@ void walk(const char* base, const char* rel, PathBuffer* paths) { 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) { + char* child_full = join_path(base, de->d_name); + if (!child_full) { 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); + char* new_rel; + if (rel && rel[0]) { + new_rel = join_path(rel, de->d_name); + } else { + new_rel = strdup(de->d_name); + } + + if (!new_rel) { + perror("ERROR: memory allocation failed in walk!"); + free(child_full); + closedir(dir); + exit(1); + } + + walk(child_full, base_rel, new_rel, paths); + free(new_rel); } else { - add_path(paths, child_rel); + char* relative_path; + if (rel && rel[0]) { + relative_path = join_path(rel, de->d_name); + } else { + relative_path = strdup(de->d_name); + } + + if (!relative_path) { + perror("ERROR: memory allocation failed in walk!"); + free(child_full); + closedir(dir); + exit(1); + } + + normalize_path(relative_path, base_rel); + add_path(paths, relative_path); + free(relative_path); } - free(child_rel); free(child_full); } closedir(dir); - free(dir_full); }