228 lines
5.8 KiB
C
228 lines
5.8 KiB
C
#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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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) {
|
|
return;
|
|
}
|
|
|
|
struct dirent* de;
|
|
while ((de = readdir(dir)) != NULL) {
|
|
if (is_dot_or_dotdot(de->d_name)) continue;
|
|
|
|
char* child_full = join_path(base, de->d_name);
|
|
if (!child_full) {
|
|
perror("ERROR: memory allocation failed in walk!");
|
|
closedir(dir);
|
|
exit(1);
|
|
}
|
|
|
|
struct stat st;
|
|
if (lstat(child_full, &st) != 0) {
|
|
free(child_full);
|
|
continue;
|
|
}
|
|
|
|
if (S_ISDIR(st.st_mode)) {
|
|
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 {
|
|
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_full);
|
|
}
|
|
|
|
closedir(dir);
|
|
}
|