diff --git a/include/action_list.h b/include/action_list.h index 95c96e7..b18216b 100644 --- a/include/action_list.h +++ b/include/action_list.h @@ -30,5 +30,6 @@ ActionList* new_list(); void add_action(ActionList*, Action); void append_list(ActionList*, ActionList*); void free_action_list(ActionList*); +void sort_action_list(ActionList*); #endif // ACTION_LIST_H diff --git a/include/file.h b/include/file.h index 2003a99..1cb67cc 100644 --- a/include/file.h +++ b/include/file.h @@ -22,6 +22,7 @@ #endif #include "hash.h" +#include "list.h" typedef struct { char** content; @@ -34,6 +35,8 @@ typedef struct { char* hash; } FileInfo; +typedef List FileInfoBuffer; + File* new_empty_file(); File* new_file(const char*); File* from_string(char*); @@ -43,5 +46,10 @@ int insert_line(File*, char*, size_t); int delete_line(File*, size_t); int snapshot_file(char*, char*, size_t, char*); void free_file(File*); +FileInfoBuffer* file_info_buffer_new(); +int file_info_buffer_push(FileInfoBuffer*, FileInfo); +void file_info_buffer_free(FileInfoBuffer*); +void file_info_buffer_sort(FileInfoBuffer*); +FileInfo* file_info_buffer_search(FileInfoBuffer*, const char*); #endif // FILE_H diff --git a/include/list.h b/include/list.h new file mode 100644 index 0000000..a1a5819 --- /dev/null +++ b/include/list.h @@ -0,0 +1,24 @@ +#ifndef LIST_H +#define LIST_H + +#include +#include +#include +#include + +#define LIST_INIT_CAPACITY 4 + +typedef struct { + void** items; + size_t len; + size_t capacity; + size_t item_size; +} List; + +List* list_new(size_t item_size); +int list_push(List*, void*); +int list_remove(List*, const void*, int (*compare)(const void*, const void*)); +void list_free(List*); +void* list_binary_search(List*, const void*, int (*compare)(const void*, const void*)); + +#endif // LIST_H \ No newline at end of file diff --git a/include/object.h b/include/object.h new file mode 100644 index 0000000..65432e6 --- /dev/null +++ b/include/object.h @@ -0,0 +1,32 @@ +#ifndef OBJECT_H +#define OBJECT_H + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#ifndef PATH_MAX +#define PATH_MAX MAX_PATH +#endif +#else +#include +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif +#endif + +#include "hash.h" +#include "file.h" +#include "tree.h" +#include "utilities.h" + +char* get_object(char*, size_t*); +void* parse_object(char*, ObjectType, size_t*); + +#endif // OBJECT_H \ No newline at end of file diff --git a/include/tree.h b/include/tree.h index 2296ffe..ac50552 100644 --- a/include/tree.h +++ b/include/tree.h @@ -2,11 +2,19 @@ #define TREE_H #include +#include -#include "utilities.h" #include "hash.h" +#include "file.h" +#include "myers.h" +#include "utilities.h" + +#define IS_ALPHA(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) +#define IS_DIGIT(c) (c >= '0' && c <= '9') +#define IS_ALNUM(c) (IS_ALPHA(c) || IS_DIGIT(c)) +#define IS_PUNCT(c) ((c >= 33 && c <= 47) || (c >= 58 && c <= 64) || (c >= 91 && c <= 96) || (c >= 123 && c <= 126)) void snapshot_tree(FileInfoBuffer*, char*); int save_tree_diff(FileInfoBuffer*, char*, char*, size_t, char*, char*); - +int read_tree(FileInfoBuffer*, char*); #endif // TREE_H diff --git a/include/utilities.h b/include/utilities.h index 44e9ca6..e42aa53 100644 --- a/include/utilities.h +++ b/include/utilities.h @@ -7,6 +7,7 @@ #include #include "myers.h" +#include "action_list.h" #define IS_ALPHA(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) #define IS_DIGIT(c) (c >= '0' && c <= '9') @@ -18,8 +19,6 @@ #define GREEN_BG "\033[42m" #define BLACK_FG "\033[30m" -#define LIST_INIT_CAPACITY 4 - typedef enum { PT_NOEXIST, PT_FILE, @@ -28,36 +27,17 @@ typedef enum { PT_ERROR } PathType; -typedef struct { - void** items; - size_t len; - size_t capacity; - size_t item_size; -} List; - typedef List StringBuffer; -typedef List FileInfoBuffer; typedef struct { uint32_t insertions; uint32_t deletions; } Changes; -List* list_new(size_t); -int list_push(List*, void*); -int list_remove(List*, const void*, int (*compare)(const void*, const void*)); -void list_free(List*); -void* binary_search(const void*, const void*, size_t, size_t, int (*compare)(const void*, const void*)); -void* list_binary_search(List*, const void*, int (*compare)(const void*, const void*)); StringBuffer* string_buffer_new(); int string_buffer_push(StringBuffer*, char*); void string_buffer_sort(StringBuffer*); char* string_buffer_search(StringBuffer*, char*); -FileInfoBuffer* file_info_buffer_new(); -int file_info_buffer_push(FileInfoBuffer*, FileInfo); -void file_info_buffer_free(FileInfoBuffer*); -void file_info_buffer_sort(FileInfoBuffer*); -FileInfo* file_info_buffer_search(FileInfoBuffer*, const char*); char* find_root(char*); void walk(char*, char*, char*, FileInfoBuffer*, int, char*); char* get_repo_path(char*, char*); @@ -70,9 +50,7 @@ char* get_file_content(char*); char* get_file_content_with_size(char*, size_t*); int create_default_config_file(char*); int save_diff(ActionList*, char*, char*, size_t, char*, File*, int); -int read_diff(char*, char*, size_t, char*, ActionList*); +int read_diff(char*, char*, ActionList*); int save_file_diff(char*, char*, size_t, char*, Changes*); -File* apply_diff(File*, ActionList*); -void sort_action_list(ActionList*); #endif // UTILITIES_H diff --git a/src/action_list.c b/src/action_list.c index d92e9ce..5a52dc7 100644 --- a/src/action_list.c +++ b/src/action_list.c @@ -50,4 +50,29 @@ void free_action_list(ActionList* list) { free(list->actions); // Free the array of actions free(list); // Free the ActionList itself -} \ No newline at end of file +} + +int compare_actions(const void* a, const void* b) { + const Action* action1 = (const Action*)a; + const Action* action2 = (const Action*)b; + + if (action1->type != action2->type) { + return (action1->type == DELETE) ? -1 : 1; + } + + if (action1->type == DELETE) { + if (action1->line_original < action2->line_original) return 1; + if (action1->line_original > action2->line_original) return -1; + return 0; + } else { + if (action1->line_changed < action2->line_changed) return -1; + if (action1->line_changed > action2->line_changed) return 1; + return 0; + } +} + +void sort_action_list(ActionList* actions) { + if (!actions || actions->len <= 1) return; + + qsort(actions->actions, actions->len, sizeof(Action), compare_actions); +} diff --git a/src/file.c b/src/file.c index f03e741..d4748d7 100644 --- a/src/file.c +++ b/src/file.c @@ -291,3 +291,41 @@ void free_file(File* file) { free(file); } + +FileInfoBuffer* file_info_buffer_new() { + return list_new(sizeof(FileInfo)); +} + +int file_info_buffer_push(FileInfoBuffer* buffer, FileInfo info) { + return list_push(buffer, &info); +} + +void file_info_buffer_free(List* buffer) { + if (buffer) { + free(buffer->items); + free(buffer); + } +} + +int compare_file_info(const void* a, const void* b) { + const FileInfo* info1 = (const FileInfo*)a; + const FileInfo* info2 = (const FileInfo*)b; + + return strcmp(info1->name, info2->name); +} + +void file_info_buffer_sort(FileInfoBuffer* buffer) { + if (!buffer || buffer->len <= 1) { + return; + } + + qsort(buffer->items, buffer->len, sizeof(FileInfo), compare_file_info); +} + +FileInfo* file_info_buffer_search(FileInfoBuffer* buffer, const char* filename) { + if (!buffer || !filename) return NULL; + + FileInfo search_key = {.mode = 0, .name = (char*)filename}; + + return (FileInfo*)list_binary_search(buffer, &search_key, compare_file_info); +} \ No newline at end of file diff --git a/src/list.c b/src/list.c new file mode 100644 index 0000000..b8ac3eb --- /dev/null +++ b/src/list.c @@ -0,0 +1,104 @@ +#include "list.h" + +List* list_new(size_t item_size) { + List* list = calloc(1, sizeof(List)); + if (!list) { perror("ERROR: memory allocation in new_list failed!"); return NULL; } + + list->items = calloc(LIST_INIT_CAPACITY, item_size); + if (!list->items) { perror("ERROR: memory allocation in new_list failed!"); free(list); return NULL; } + + list->len = 0; + list->capacity = LIST_INIT_CAPACITY; + list->item_size = item_size; + return list; +} + +int list_push(List* list, void* item) { + if (!list || !item) return 0; + + if (list->len == list->capacity) { + size_t new_capacity = list->capacity * 2; + void* new_items = realloc(list->items, new_capacity * list->item_size); + if (!new_items) { perror("ERROR: memory reallocation failed in list_push!"); return 0; } + + list->items = new_items; + list->capacity = new_capacity; + } + + char* dest = (char*)list->items + (list->len * list->item_size); + memcpy(dest, item, list->item_size); + list->len++; + + return 1; +} + +int list_remove(List* list, const void* key, int (*compare)(const void*, const void*)) { + if (!list || !key || !compare || list->len == 0) { + return 0; + } + + for (size_t i = 0; i < list->len; i++) { + char* current_item = (char*)list->items + (i * list->item_size); + + if (compare(key, current_item) == 0) { + for (size_t j = i; j < list->len - 1; j++) { + char* dest = (char*)list->items + (j * list->item_size); + char* src = (char*)list->items + ((j + 1) * list->item_size); + memcpy(dest, src, list->item_size); + } + + list->len--; + return 1; + } + } + + return 0; +} + +void list_free(List* buffer) { + if (buffer) { + for (size_t i = 0; i < buffer->len; i++) { + free(buffer->items[i]); + } + free(buffer->items); + free(buffer); + } +} + +void* binary_search(const void* key, const void* base, size_t num_elements, size_t element_size, int (*compare)(const void*, const void*)) { + if (!key || !base || !compare || num_elements == 0) { + return NULL; + } + + size_t left = 0; + size_t right = num_elements - 1; + + while (left <= right) { + size_t mid = left + (right - left) / 2; + + const char* mid_element = (const char*)base + (mid * element_size); + + int cmp = compare(key, mid_element); + + if (cmp == 0) { + return (void*)mid_element; + } + else if (cmp < 0) { + if (mid == 0) break; + right = mid - 1; + } + else { + left = mid + 1; + } + } + + return NULL; +} + +void* list_binary_search(List* list, const void* key, int (*compare)(const void*, const void*)) { + if (!list || !key || !compare) { + return NULL; + } + + return binary_search(key, list->items, list->len, list->item_size, compare); +} \ No newline at end of file diff --git a/src/main.c b/src/main.c index 3167a1b..f1f8914 100644 --- a/src/main.c +++ b/src/main.c @@ -6,14 +6,11 @@ #include #include -#include "action_list.h" -#include "file.h" -#include "myers.h" -#include "tree.h" #include "base_file_buffer.h" #include "config.h" #include "commit.h" #include "hash.h" +#include "object.h" static void usage(int exitcode) { printf("usage: merk []\n\ @@ -24,7 +21,7 @@ static void usage(int exitcode) { exit(exitcode); } -int main(int argc, char **argv) { +int main(int argc, char** argv) { if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) { usage(0); } @@ -247,32 +244,25 @@ int main(int argc, char **argv) { return 1; } - char relative[PATH_MAX] = ""; - char* root = find_root(relative); - if (!root) { - free_config(&config); - free(commit_message); - return 1; - } - + char tmp_path[PATH_MAX] = ".merk/BRANCH"; char branch_path[PATH_MAX]; - snprintf(branch_path, sizeof(branch_path), "%s/.merk/BRANCH", root); + realpath(tmp_path, branch_path); char* branch = get_file_content(branch_path); if (!branch) { - free(root); free_config(&config); free(commit_message); return 1; } + char tmp_path2[PATH_MAX]; char info_path[PATH_MAX]; - snprintf(info_path, sizeof(info_path), "%s/.merk/info/%s", root, branch); + snprintf(tmp_path2, sizeof(tmp_path2), ".merk/info/%s", branch); + realpath(tmp_path2, info_path); BaseFileBuffer* base_files = base_file_buffer_new(); if (!base_files) { free(branch); - free(root); free_config(&config); free(commit_message); return 1; @@ -284,7 +274,16 @@ int main(int argc, char **argv) { if (!files) { base_file_buffer_free(base_files); free(branch); - free(root); + free_config(&config); + free(commit_message); + return 1; + } + + char* root = find_root(NULL); + if (!root) { + list_free(files); + base_file_buffer_free(base_files); + free(branch); free_config(&config); free(commit_message); return 1; @@ -297,7 +296,6 @@ int main(int argc, char **argv) { list_free(files); base_file_buffer_free(base_files); free(branch); - free(root); free_config(&config); free(commit_message); return 1; @@ -366,8 +364,8 @@ int main(int argc, char **argv) { } } - StringBuffer* file_hashes = string_buffer_new(); - if (!file_hashes) { + Changes* changes = calloc(1, sizeof(Changes)); + if (!changes) { free(commit_message); list_free(files); base_file_buffer_free(base_files); @@ -377,6 +375,9 @@ int main(int argc, char **argv) { return 1; } + changes->insertions = 0; + changes->deletions = 0; + for (size_t idx = 0; idx < files->len; idx++) { BaseFileInfo* base_file = base_file_buffer_search(base_files, files->items[idx]); if (!base_file) { @@ -385,7 +386,6 @@ int main(int argc, char **argv) { // Compress the files content and put it into the object database char file_hash[41]; snapshot_file(files->items[idx], root, 0, file_hash); - string_buffer_push(file_hashes, file_hash); continue; } size_t base_num = base_file->base_num; @@ -398,17 +398,13 @@ int main(int argc, char **argv) { char id[2 + snprintf(NULL, 0, "%d", base_num) + strlen(files->items[idx])]; snprintf(id, sizeof(id), "%s %d", files->items[idx], base_num); object_hash(BaseFileObject, id, basefile_hash); - - char diff_hash[41]; - save_file_diff(files->items[idx], root, base_num, basefile_hash, diff_hash); - string_buffer_push(file_hashes, diff_hash); + save_file_diff(files->items[idx], root, base_num, basefile_hash, changes); } base_file_buffer_sort(base_files); write_base_file_list(base_files, info_path); FileInfoBuffer* tree = basefilebuffer_to_fileinfobuffer(base_files); if (!tree) { - list_free(file_hashes); free(commit_message); list_free(files); base_file_buffer_free(base_files); @@ -417,6 +413,23 @@ int main(int argc, char **argv) { free_config(&config); return 1; } + + for (size_t idx = 0; idx < tree->len; idx++) { + FileInfo* info = (FileInfo*)tree->items + idx; + BaseFileInfo* base_info = base_file_buffer_search(base_files, info->name); + char hash[41]; + if (base_info->diff_num == 0) { + char id[2 + snprintf(NULL, 0, "%d", base_info->base_num) + strlen(info->name)]; + snprintf(id, sizeof(id), "%s %d", info->name, base_info->base_num); + object_hash(BaseFileObject, id, hash); + } else { + char id[2 + snprintf(NULL, 0, "%d", base_info->diff_num-1) + strlen(info->name)]; + snprintf(id, sizeof(id), "%s %d", info->name, base_info->diff_num-1); + object_hash(FileDiffObject, id, hash); + } + info->hash = strdup(hash); + } + char tree_hash[41]; snapshot_tree(tree, tree_hash); @@ -427,7 +440,6 @@ int main(int argc, char **argv) { if (!log) { file_info_buffer_free(tree); - list_free(file_hashes); free(commit_message); list_free(files); base_file_buffer_free(base_files); @@ -484,7 +496,6 @@ int main(int argc, char **argv) { if (!write_commit_log(log, log_path)) { commit_log_free(log); file_info_buffer_free(tree); - list_free(file_hashes); free(commit_message); list_free(files); base_file_buffer_free(base_files); @@ -497,14 +508,18 @@ int main(int argc, char **argv) { char ref_path[PATH_MAX]; snprintf(ref_path, sizeof(ref_path), "%s/.merk/refs/branches/%s", root, branch); - printf("this is the ref path: %s\n", ref_path); - int ref = open(ref_path, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (ref < 0) return -1; write(ref, commit_hash, 40); close(ref); - // Cleanup + if (changes->insertions == 0 && changes->deletions == 0) { + printf("(\x1b[33;1m%.7s\x1b[0m on \x1b[39;1;4m%s\x1b[0m) %s\n", commit_hash, branch, commit_message); + } + else { + printf("(\x1b[33;1m%.7s\x1b[0m on \x1b[39;1;4m%s\x1b[0m \x1b[32;1m%d+\x1b[0m \x1b[31;1m%d-\x1b[0m) %s\n", commit_hash, branch, changes->insertions, changes->deletions, commit_message); + } + list_free(files); base_file_buffer_free(base_files); free(branch); @@ -541,7 +556,7 @@ int main(int argc, char **argv) { time_t timestamp = atol(commit->committer.timestamp); strftime(human_readable_time, sizeof(human_readable_time), "%Y-%m-%d %H:%M:%S %z", localtime(×tamp)); - printf("\x1b[33;1m%.7s\x1b[0m: %s\n%s <\x1b[38;5;240m\x1b]8;;mailto:%s\x1b\\%s\x1b]8;;\x1b\\\x1b[0m> \x1b[36m%s\x1b[0m\n\n", + printf("\x1b[33;1m%.7s\x1b[0m: %s\n%s <\x1b[38;5;240m\x1b]8;;mailto:%s\x1b\\%s\x1b]8;;\x1b\\\x1b[0m> (\x1b[36m%s\x1b[0m)\n\n", commit->hash, commit->message, commit->committer.name, diff --git a/src/object.c b/src/object.c new file mode 100644 index 0000000..343afa6 --- /dev/null +++ b/src/object.c @@ -0,0 +1,117 @@ +#include "object.h" + +char* get_object(char* hash, size_t* size_out) { + char dir_path[PATH_MAX]; + char repo_path[PATH_MAX]; + char file_path[PATH_MAX]; + snprintf(dir_path, sizeof(dir_path), ".merk/objects/%.2s", hash); + snprintf(repo_path, sizeof(repo_path), "%s/%s", dir_path, hash+2); + realpath(repo_path, file_path); + + size_t compressed_size; + unsigned char* compressed_data = (unsigned char*)get_file_content_with_size(file_path, &compressed_size); + if (!compressed_data || compressed_size == 0) { + free(compressed_data); + return NULL; + } + + size_t idx = 0; + while (idx < compressed_size && IS_DIGIT(compressed_data[idx])) { + idx++; + } + + if (idx == 0) { + perror("ERROR: no length found at start of object!"); + free(compressed_data); + return NULL; + } + + char* size_str = calloc(idx + 1, sizeof(char)); + if (!size_str) { + free(compressed_data); + return NULL; + } + + memcpy(size_str, compressed_data, idx); + size_str[idx] = '\0'; + + char* end; + long original_size = strtol(size_str, &end, 10); + if (end == size_str || *end != '\0') { + perror("ERROR: invalid length in get_object!"); + free(size_str); + free(compressed_data); + return NULL; + } + free(size_str); + + if (idx < compressed_size && compressed_data[idx] == ' ') { + idx++; + } + + char* decompressed = malloc(original_size + 1); + if (!decompressed) { + free(compressed_data); + return NULL; + } + uLongf decompressed_size = original_size; + + if (uncompress((Bytef*)decompressed, &decompressed_size, compressed_data + idx, compressed_size - idx) != Z_OK) { + perror("ERROR: decompression failed in get_object!"); + free(decompressed); + free(compressed_data); + return NULL; + } + + free(compressed_data); + + decompressed[decompressed_size] = '\0'; + if (size_out) { + *size_out = decompressed_size; + } + + return decompressed; +} + +void* parse_object(char* hash, ObjectType type, size_t* size_out) { + size_t line_count; + char* content = get_object(hash, &line_count); + if (!content) return NULL; + if (line_count == 0) { + free(content); + return NULL; + } + + switch (type) { + case BaseFileObject: { + File* file = from_string(content); + if (!file) { + free(content); + return NULL; + } + free(content); + return file; + } + case TreeObject: { + FileInfoBuffer* buffer = file_info_buffer_new(); + if (!buffer) { + free(content); + return NULL; + } + + if (read_tree(buffer, hash) < 0) { + file_info_buffer_free(buffer); + free(content); + return NULL; + } + + free(content); + return buffer; + } + case FileDiffObject: { + + } + + default: free(content); return NULL; + } +} \ No newline at end of file diff --git a/src/tree.c b/src/tree.c index 72e4075..67d6fc8 100644 --- a/src/tree.c +++ b/src/tree.c @@ -416,3 +416,104 @@ int save_tree_diff(FileInfoBuffer* current_tree, char* root, char* branch_name, return 1; } + +int read_tree(FileInfoBuffer* tree_out, char* hash) { + if (!tree_out || !hash) { + return 0; + } + + char repo_location[PATH_MAX]; + snprintf(repo_location, sizeof(repo_location), ".merk/objects/%.2s/%s", hash, hash+2); + + char tree_location[PATH_MAX]; + realpath(repo_location, tree_location); + + size_t compressed_size; + unsigned char* compressed_data = (unsigned char*)get_file_content_with_size(tree_location, &compressed_size); + if (!compressed_data) { + return 0; + } + + size_t idx = 0; + while (idx < compressed_size && IS_DIGIT(compressed_data[idx])) { + idx++; + } + + if (idx == 0) { + perror("ERROR: no length found in tree file!"); + free(compressed_data); + return 0; + } + + char* size_str = calloc(idx + 1, sizeof(char)); + if (!size_str) { + free(compressed_data); + return 0; + } + + memcpy(size_str, compressed_data, idx); + size_str[idx] = '\0'; + + char* end; + long size = strtol(size_str, &end, 10); + if (end == size_str || *end != '\0' || size <= 0) { + perror("ERROR: invalid length in tree file"); + free(size_str); + free(compressed_data); + return 0; + } + + size_t offset = strlen(size_str) + 1; + free(size_str); + + char* tree_content = calloc(size + 1, sizeof(char)); + if (!tree_content) { + free(compressed_data); + return 0; + } + + uLongf dest_len = (uLongf)size; + int result = uncompress((unsigned char*)tree_content, &dest_len, compressed_data + offset, compressed_size - offset); + free(compressed_data); + + if (result != Z_OK) { + perror("ERROR: decompression of tree failed!"); + free(tree_content); + return 0; + } + + tree_content[dest_len] = '\0'; + + char* line = strtok(tree_content, "\n"); + while (line) { + FileInfo file_info; + char mode_str[8]; + if (sscanf(line, "%7s %ms %ms", mode_str, &file_info.name, &file_info.hash) != 3) { + fprintf(stderr, "ERROR: failed to parse file info in tree file! Line: %s\n", line); + free(tree_content); + file_info_buffer_free(tree_out); + return 0; + } + + char* endptr; + file_info.mode = (mode_t)strtol(mode_str, &endptr, 8); + if (endptr == mode_str || *endptr != '\0') { + fprintf(stderr, "ERROR: failed to parse mode as octal! Mode string: %s\n", mode_str); + free(tree_content); + file_info_buffer_free(tree_out); + return 0; + } + + if (!list_push(tree_out, &file_info)) { + fprintf(stderr, "ERROR: Failed to push FileInfo to tree_out!\n"); + free(tree_content); + file_info_buffer_free(tree_out); + return 0; + } + + line = strtok(NULL, "\n"); + } + + free(tree_content); + return 1; +} diff --git a/src/utilities.c b/src/utilities.c index 3e759ab..54dccfd 100644 --- a/src/utilities.c +++ b/src/utilities.c @@ -1,108 +1,5 @@ #include "utilities.h" -List* list_new(size_t item_size) { - List* list = calloc(1, sizeof(List)); - if (!list) { perror("ERROR: memory allocation in new_list failed!"); return NULL; } - - list->items = calloc(LIST_INIT_CAPACITY, item_size); - if (!list->items) { perror("ERROR: memory allocation in new_list failed!"); free(list); return NULL; } - - list->len = 0; - list->capacity = LIST_INIT_CAPACITY; - list->item_size = item_size; - return list; -} - -int list_push(List* list, void* item) { - if (!list || !item) return 0; - - if (list->len == list->capacity) { - size_t new_capacity = list->capacity * 2; - void* new_items = realloc(list->items, new_capacity * list->item_size); - if (!new_items) { perror("ERROR: memory reallocation failed in list_push!"); return 0; } - - list->items = new_items; - list->capacity = new_capacity; - } - - char* dest = (char*)list->items + (list->len * list->item_size); - memcpy(dest, item, list->item_size); - list->len++; - - return 1; -} - -int list_remove(List* list, const void* key, int (*compare)(const void*, const void*)) { - if (!list || !key || !compare || list->len == 0) { - return 0; - } - - for (size_t i = 0; i < list->len; i++) { - char* current_item = (char*)list->items + (i * list->item_size); - - if (compare(key, current_item) == 0) { - for (size_t j = i; j < list->len - 1; j++) { - char* dest = (char*)list->items + (j * list->item_size); - char* src = (char*)list->items + ((j + 1) * list->item_size); - memcpy(dest, src, list->item_size); - } - - list->len--; - return 1; - } - } - - return 0; -} - -void list_free(List* buffer) { - if (buffer) { - for (size_t i = 0; i < buffer->len; i++) { - free(buffer->items[i]); - } - free(buffer->items); - free(buffer); - } -} - -void* binary_search(const void* key, const void* base, size_t num_elements, size_t element_size, int (*compare)(const void*, const void*)) { - if (!key || !base || !compare || num_elements == 0) { - return NULL; - } - - size_t left = 0; - size_t right = num_elements - 1; - - while (left <= right) { - size_t mid = left + (right - left) / 2; - - const char* mid_element = (const char*)base + (mid * element_size); - - int cmp = compare(key, mid_element); - - if (cmp == 0) { - return (void*)mid_element; - } - else if (cmp < 0) { - if (mid == 0) break; - right = mid - 1; - } - else { - left = mid + 1; - } - } - - return NULL; -} - -void* list_binary_search(List* list, const void* key, int (*compare)(const void*, const void*)) { - if (!list || !key || !compare) { - return NULL; - } - - return binary_search(key, list->items, list->len, list->item_size, compare); -} - StringBuffer* string_buffer_new() { return list_new(sizeof(char*)); } @@ -135,44 +32,6 @@ char* string_buffer_search(StringBuffer* buffer, char* path) { return (char*)list_binary_search(buffer, path, compare_strings); } -FileInfoBuffer* file_info_buffer_new() { - return list_new(sizeof(FileInfo)); -} - -int file_info_buffer_push(FileInfoBuffer* buffer, FileInfo info) { - return list_push(buffer, &info); -} - -void file_info_buffer_free(List* buffer) { - if (buffer) { - free(buffer->items); - free(buffer); - } -} - -int compare_file_info(const void* a, const void* b) { - const FileInfo* info1 = (const FileInfo*)a; - const FileInfo* info2 = (const FileInfo*)b; - - return strcmp(info1->name, info2->name); -} - -void file_info_buffer_sort(FileInfoBuffer* buffer) { - if (!buffer || buffer->len <= 1) { - return; - } - - qsort(buffer->items, buffer->len, sizeof(FileInfo), compare_file_info); -} - -FileInfo* file_info_buffer_search(FileInfoBuffer* buffer, const char* filename) { - if (!buffer || !filename) return NULL; - - FileInfo search_key = {.mode = 0, .name = (char*)filename}; - - return (FileInfo*)list_binary_search(buffer, &search_key, compare_file_info); -} - static int is_dot_or_dotdot(const char* s) { return (s[0] == '.' && (s[1] == '\0' || (s[1] == '.' && s[2] == '\0'))); } @@ -697,319 +556,119 @@ int save_diff(ActionList* diff, char* path, char* root, size_t diff_id, char* ba return 1; } -int read_diff(char* path, char* root, size_t diff_id, char* basefile_hash, ActionList* diff_out) { - size_t compressed_size; - unsigned char* compressed_data = (unsigned char*)get_file_content_with_size(path, &compressed_size); - - if (!compressed_data || compressed_size == 0) { - free(compressed_data); - return 1; +int read_diff(char* content, char* basefile_hash, ActionList* diff_out) { + if (!content || !basefile_hash || !diff_out) { + return 0; } size_t idx = 0; - while (idx < compressed_size && IS_DIGIT(compressed_data[idx])) { - idx++; - } - - if (idx == 0) { - perror("ERROR: no length found at start of base file list!"); - free(compressed_data); - return 0; - } - - char* size_str = calloc(idx + 1, sizeof(char)); - if (!size_str) { - free(compressed_data); - return 0; - } - - memcpy(size_str, compressed_data, idx); - size_str[idx] = '\0'; - - char* end; - long original_size = strtol(size_str, &end, 10); - if (end == size_str || *end != '\0') { - perror("ERROR: invalid length in read_diff!"); - free(size_str); - free(compressed_data); - return 0; - } - free(size_str); - - if (idx < compressed_size && compressed_data[idx] == ' ') { - idx++; - } - - char* diff_list = calloc(original_size + 1, sizeof(char)); - if (!diff_list) { - free(compressed_data); - return 0; - } - - uLongf dest_len = (uLongf)original_size; - - int result = uncompress((unsigned char*)diff_list, &dest_len, compressed_data + idx, compressed_size - idx); - free(compressed_data); - - if (result != Z_OK) { - perror("ERROR: decompression of the diff failed!"); - free(diff_list); - return 0; - } - - diff_list[dest_len] = '\0'; - - printf("Decompressed diff content: %s\n", diff_list); - - idx = 0; - strncpy(basefile_hash, diff_list, 40); + strncpy(basefile_hash, content, 40); basefile_hash[40] = '\0'; idx += 41; - while (idx < dest_len && IS_DIGIT(diff_list[idx])) { + while (content[idx] && IS_DIGIT(content[idx])) { idx++; } if (idx == 41) { perror("ERROR: no length found at start of diff!"); - free(diff_list); return 0; } - char* number_of_actions = calloc(idx + 1, sizeof(char)); + char* number_of_actions = calloc(idx - 41 + 1, sizeof(char)); if (!number_of_actions) { - free(diff_list); return 0; } - memcpy(number_of_actions, diff_list + 41, idx-41); - number_of_actions[idx] = '\0'; + memcpy(number_of_actions, content + 41, idx - 41); + number_of_actions[idx - 41] = '\0'; + char* end; long actions = strtol(number_of_actions, &end, 10); - if (end == number_of_actions || *end != '\0') { - perror("ERROR: invalid length in read_diff!"); - free(number_of_actions); - free(diff_list); - return 0; - } - free(number_of_actions); - - if (actions <= 0) { - free(diff_list); - return 1; + + if (end == number_of_actions || *end != '\0' || actions <= 0) { + perror("ERROR: invalid number of actions in read_diff!"); + return 0; } size_t action_idx = 0; - while (idx < dest_len && action_idx < (size_t)actions) { + while (content[idx] && action_idx < (size_t)actions) { idx++; - - if (diff_list[idx] == '0') { + + Action action = {0}; + + if (content[idx] == '0') { idx++; - if (diff_list[idx] == ' ') idx++; - else { + if (content[idx] != ' ') { perror("ERROR: expected space after opbit in read_diff!"); - free(diff_list); return 0; } - size_t start_idx = idx; - - while (idx < dest_len && IS_DIGIT(diff_list[idx])) { - idx++; - } - if (idx == dest_len) { - perror("ERROR: unexpected end of diff in read_diff!"); - free(diff_list); - return 0; - } - char* line_original_str = calloc(idx + 1, sizeof(char)); - if (!line_original_str) { - free(diff_list); - return 0; - } - memcpy(line_original_str, diff_list + start_idx, idx - start_idx); - line_original_str[idx - start_idx] = '\0'; - char* end2; - long line_original = strtol(line_original_str, &end2, 10); - if (end2 == line_original_str || *end2 != '\0') { - perror("ERROR: invalid line number in read_diff!"); - free(line_original_str); - free(diff_list); - return 0; - } - free(line_original_str); - - if (diff_list[idx] == ' ') idx++; - else { - perror("ERROR: expected space after line original in read_diff!"); - free(diff_list); - return 0; - } - start_idx = idx; - while (idx < dest_len && IS_DIGIT(diff_list[idx])) { - idx++; - } - if (idx == dest_len) { - perror("ERROR: unexpected end of diff in read_diff!"); - free(diff_list); - return 0; - } - char* line_changed_str = calloc(idx + 1, sizeof(char)); - if (!line_changed_str) { - free(diff_list); - return 0; - } - memcpy(line_changed_str, diff_list + start_idx, idx - start_idx); - line_changed_str[idx - start_idx] = '\0'; - long line_changed = strtol(line_changed_str, &end2, 10); - if (end2 == line_changed_str || *end2 != '\0') { - perror("ERROR: invalid line number in read_diff!"); - free(line_changed_str); - free(diff_list); - return 0; - } - free(line_changed_str); - - Action action = {.type=DELETE, .line_original=line_original, .line_changed=line_changed, .content=NULL}; - add_action(diff_out, action); - action_idx++; - } - else if (diff_list[idx] == '1') { idx++; - if (diff_list[idx] == ' ') idx++; - else { + + char* endptr; + action.line_original = strtol(content + idx, &endptr, 10); + if (endptr == content + idx || *endptr != ' ') { + perror("ERROR: failed to parse line_original in read_diff!"); + return 0; + } + idx = endptr - content + 1; + + action.line_changed = strtol(content + idx, &endptr, 10); + if (endptr == content + idx || (*endptr != ' ' && *endptr != '\0')) { + perror("ERROR: failed to parse line_changed in read_diff!"); + return 0; + } + idx = endptr - content; + } else if (content[idx] == '1') { + idx++; + if (content[idx] != ' ') { perror("ERROR: expected space after opbit in read_diff!"); - free(diff_list); return 0; } - size_t start_idx = idx; + idx++; - while (idx < dest_len && IS_DIGIT(diff_list[idx])) { - idx++; - } - if (idx == dest_len) { - perror("ERROR: unexpected end of diff in read_diff!"); - free(diff_list); - return 0; - } - char* line_original_str = calloc(idx + 1, sizeof(char)); - if (!line_original_str) { - free(diff_list); - return 0; - } - memcpy(line_original_str, diff_list + start_idx, idx - start_idx); - line_original_str[idx - start_idx] = '\0'; - char* end2; - long line_original = strtol(line_original_str, &end2, 10); - if (end2 == line_original_str || *end2 != '\0') { - perror("ERROR: invalid line number in read_diff!"); - free(line_original_str); - free(diff_list); - return 0; - } - free(line_original_str); - - if (diff_list[idx] == ' ') idx++; - else { - perror("ERROR: expected space after line original in read_diff!"); - free(diff_list); - return 0; - } - start_idx = idx; - while (idx < dest_len && IS_DIGIT(diff_list[idx])) { - idx++; - } - if (idx == dest_len) { - perror("ERROR: unexpected end of diff in read_diff!"); - free(diff_list); - return 0; - } - char* line_changed_str = calloc(idx + 1, sizeof(char)); - if (!line_changed_str) { - free(diff_list); - return 0; - } - memcpy(line_changed_str, diff_list + start_idx, idx - start_idx); - line_changed_str[idx - start_idx] = '\0'; - long line_changed = strtol(line_changed_str, &end2, 10); - if (end2 == line_changed_str || *end2 != '\0') { - perror("ERROR: invalid line number in read_diff!"); - free(line_changed_str); - free(diff_list); - return 0; - } - free(line_changed_str); - - if (diff_list[idx] == ' ') idx++; - else { - perror("ERROR: expected space after line changed in read_diff!"); - free(diff_list); + char* endptr; + action.line_original = strtol(content + idx, &endptr, 10); + if (endptr == content + idx || *endptr != ' ') { + perror("ERROR: failed to parse line_original in read_diff!"); return 0; } + idx = endptr - content + 1; - start_idx = idx; - while (idx < dest_len && IS_DIGIT(diff_list[idx])) { - idx++; - } - if (idx == dest_len) { - perror("ERROR: unexpected end of diff in read_diff!"); - free(diff_list); - return 0; - } - char* content_len_str = calloc(idx + 1, sizeof(char)); - if (!content_len_str) { - free(diff_list); - return 0; - } - memcpy(content_len_str, diff_list + start_idx, idx - start_idx); - content_len_str[idx - start_idx] = '\0'; - long content_len = strtol(content_len_str, &end2, 10); - if (end2 == content_len_str || *end2 != '\0') { - perror("ERROR: invalid line number in read_diff!"); - free(content_len_str); - free(diff_list); - return 0; - } - free(content_len_str); - - if (diff_list[idx] == ' ') idx++; - else { - perror("ERROR: expected space after line original in read_diff!"); - free(diff_list); + action.line_changed = strtol(content + idx, &endptr, 10); + if (endptr == content + idx || *endptr != ' ') { + perror("ERROR: failed to parse line_changed in read_diff!"); return 0; } + idx = endptr - content + 1; - if (idx + content_len > dest_len) { - perror("ERROR: content length exceeds diff length in read_diff!"); - free(diff_list); + size_t content_length = strtol(content + idx, &endptr, 10); + if (endptr == content + idx || *endptr != ' ') { + perror("ERROR: failed to parse content length in read_diff!"); return 0; } + idx = endptr - content + 1; - char* content = calloc(content_len + 1, sizeof(char)); - if (!content) { - free(diff_list); + action.content = strndup(content + idx, content_length); + if (!action.content) { + perror("ERROR: failed to allocate memory for action content in read_diff!"); return 0; } - memcpy(content, diff_list + idx, content_len); - content[content_len] = '\0'; - - idx += content_len; - - Action action = {.type=INSERT, .line_original=line_original, .line_changed=line_changed, .content=content}; - add_action(diff_out, action); - action_idx++; - } - else { - perror("ERROR: invalid opbit in read_diff!"); - free(diff_list); + idx += content_length; + } else { + perror("ERROR: unknown action type in read_diff!"); return 0; } + + add_action(diff_out, action); + + action_idx++; } return 1; } - int save_file_diff(char* path, char* root, size_t diff_id, char* basefile_hash, Changes* changes) { char basefile_location[2+strlen(root)+strlen("/.merk/objects/")+strlen(basefile_hash)]; snprintf(basefile_location, sizeof(basefile_location), "%s/.merk/objects/%.2s/%s", root, basefile_hash, basefile_hash+2); @@ -1135,30 +794,4 @@ File* apply_diff(File* basefile, ActionList* diff) { } return copy; -} - -int compare_actions(const void* a, const void* b) { - const Action* action1 = (const Action*)a; - const Action* action2 = (const Action*)b; - - if (action1->type != action2->type) { - return (action1->type == DELETE) ? -1 : 1; - } - - if (action1->type == DELETE) { - if (action1->line_original < action2->line_original) return 1; - if (action1->line_original > action2->line_original) return -1; - return 0; - } else { - if (action1->line_changed < action2->line_changed) return -1; - if (action1->line_changed > action2->line_changed) return 1; - return 0; - } -} - - -void sort_action_list(ActionList* actions) { - if (!actions || actions->len <= 1) return; - - qsort(actions->actions, actions->len, sizeof(Action), compare_actions); } \ No newline at end of file