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

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

View file

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

24
include/list.h Normal file
View file

@ -0,0 +1,24 @@
#ifndef LIST_H
#define LIST_H
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#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

32
include/object.h Normal file
View file

@ -0,0 +1,32 @@
#ifndef OBJECT_H
#define OBJECT_H
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <zlib.h>
#ifdef _WIN32
#include <windows.h>
#ifndef PATH_MAX
#define PATH_MAX MAX_PATH
#endif
#else
#include <limits.h>
#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

View file

@ -2,11 +2,19 @@
#define TREE_H
#include <zlib.h>
#include <errno.h>
#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

View file

@ -7,6 +7,7 @@
#include <fcntl.h>
#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

View file

@ -51,3 +51,28 @@ 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\
@ -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);
if (end == number_of_actions || *end != '\0' || actions <= 0) {
perror("ERROR: invalid number of actions in read_diff!");
return 0;
}
free(number_of_actions);
if (actions <= 0) {
free(diff_list);
return 1;
}
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);
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;
}
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);
idx = endptr - content + 1;
Action action = {.type=DELETE, .line_original=line_original, .line_changed=line_changed, .content=NULL};
add_action(diff_out, action);
action_idx++;
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;
}
else if (diff_list[idx] == '1') {
idx = endptr - content;
} else if (content[idx] == '1') {
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);
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;
}
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);
idx = endptr - content + 1;
if (diff_list[idx] == ' ') idx++;
else {
perror("ERROR: expected space after line changed 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;
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;
action.content = strndup(content + idx, content_length);
if (!action.content) {
perror("ERROR: failed to allocate memory for action content in read_diff!");
return 0;
}
idx += content_length;
} else {
perror("ERROR: unknown action type in read_diff!");
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* 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);
return 0;
}
if (idx + content_len > dest_len) {
perror("ERROR: content length exceeds diff length in read_diff!");
free(diff_list);
return 0;
}
char* content = calloc(content_len + 1, sizeof(char));
if (!content) {
free(diff_list);
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);
return 0;
}
}
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);
@ -1136,29 +795,3 @@ 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);
}