refactor: split up the utilities file into smaller modules

This commit is contained in:
lisk77 2025-09-19 03:48:08 +02:00
parent abaa6e12fc
commit ff71a92249
13 changed files with 572 additions and 488 deletions

View file

@ -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);
}

View file

@ -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
View 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);
}

View file

@ -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(&timestamp));
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
View 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;
}
}

View file

@ -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;
}

View file

@ -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);
}