refactor: split up the utilities file into smaller modules
This commit is contained in:
parent
abaa6e12fc
commit
ff71a92249
13 changed files with 572 additions and 488 deletions
|
|
@ -50,4 +50,29 @@ void free_action_list(ActionList* list) {
|
|||
|
||||
free(list->actions); // Free the array of actions
|
||||
free(list); // Free the ActionList itself
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
38
src/file.c
38
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);
|
||||
}
|
||||
104
src/list.c
Normal file
104
src/list.c
Normal file
|
|
@ -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);
|
||||
}
|
||||
81
src/main.c
81
src/main.c
|
|
@ -6,14 +6,11 @@
|
|||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
||||
#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 <command> [<args>]\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,
|
||||
|
|
|
|||
117
src/object.c
Normal file
117
src/object.c
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
101
src/tree.c
101
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;
|
||||
}
|
||||
|
|
|
|||
489
src/utilities.c
489
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);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue