#include "file.h" File* new_empty_file() { File* file = calloc(1, sizeof(File)); file->content = NULL; file->lines = 0; return file; } File* new_file(const char* path) { if (!path) return NULL; FILE* f = fopen(path, "rb"); if (!f) return NULL; if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return NULL; } long sz = ftell(f); if (sz < 0) { fclose(f); return NULL; } if (fseek(f, 0, SEEK_SET) != 0) { fclose(f); return NULL; } size_t n = (size_t)sz; char* buf = (char *)calloc(n + 1, 1); if (!buf) { fclose(f); return NULL; } size_t got = fread(buf, 1, n, f); fclose(f); buf[got] = '\0'; size_t count = 1; for (size_t i = 0; i < got; ++i) if (buf[i] == '\n') count++; if (buf[got-1] == '\n') count--; char** lines = count ? (char**)malloc(count * sizeof *lines) : NULL; if (count && !lines) return NULL; size_t idx = 0; lines[idx++] = buf; for (size_t i = 0; i < got; ++i) { if (buf[i] == '\n') { if (i > 0 && buf[i-1] == '\r') buf[i-1] = '\0'; buf[i] = '\0'; if (i+1 < got) lines[idx++] = &buf[i+1]; } } if (got > 0 && buf[got-1] == '\r') buf[got-1] = '\0'; File* file = calloc(1, sizeof(File)); file->content = lines; file->lines = idx; return file; } File* from_string(char* string) { if (!string) return NULL; size_t len = strlen(string); if (len == 0) return new_empty_file(); char* buf = malloc(len + 1); if (!buf) return NULL; strcpy(buf, string); size_t count = 1; for (size_t i = 0; i < len; ++i) { if (buf[i] == '\n') count++; } if (buf[len-1] == '\n') count--; char** lines = count ? (char**)malloc(count * sizeof *lines) : NULL; if (count && !lines) { free(buf); return NULL; } size_t idx = 0; lines[idx++] = buf; for (size_t i = 0; i < len; ++i) { if (buf[i] == '\n') { if (i > 0 && buf[i-1] == '\r') buf[i-1] = '\0'; buf[i] = '\0'; if (i+1 < len) lines[idx++] = &buf[i+1]; } } if (len > 0 && buf[len-1] == '\r') buf[len-1] = '\0'; File* file = calloc(1, sizeof(File)); if (!file) { free(lines); free(buf); return NULL; } file->content = lines; file->lines = idx; return file; } File* slice_file(File* original, uint64_t start, uint64_t end) { if (!original || (end < start) || (end > original->lines)) return NULL; File* slice = calloc(1, sizeof(File)); if (!slice) return NULL; uint64_t lines = end-start; if (!original->content || lines == 0) return NULL; slice->content = original->content + start; slice->lines = lines; return slice; } File* copy_file(File* original) { if (!original) return NULL; File* copy = calloc(1, sizeof(File)); if (!copy) return NULL; copy->lines = original->lines; if (original->lines == 0) { copy->content = NULL; return copy; } copy->content = malloc(original->lines * sizeof(char*)); if (!copy->content) { free(copy); return NULL; } for (size_t i = 0; i < original->lines; i++) { copy->content[i] = strdup(original->content[i]); if (!copy->content[i]) { for (size_t j = 0; j < i; j++) { free(copy->content[j]); } free(copy->content); free(copy); return NULL; } } return copy; } int insert_line(File* file, char* line, size_t idx) { if (!file || !line || idx > file->lines) return 0; char** new_content = realloc(file->content, (file->lines + 1) * sizeof(char*)); if (!new_content) return 0; file->content = new_content; for (size_t i = file->lines; i > idx; i--) { file->content[i] = file->content[i - 1]; } file->content[idx] = strdup(line); file->lines++; return 1; } int delete_line(File* file, size_t idx) { if (!file || idx >= file->lines) return 0; free(file->content[idx]); for (size_t i = idx; i < file->lines - 1; i++) { file->content[i] = file->content[i + 1]; } char** new_content = realloc(file->content, (file->lines - 1) * sizeof(char*)); if (file->lines - 1 > 0 && !new_content) return 0; file->content = new_content; file->lines--; return 1; } int snapshot_file(char* path, char* root, size_t basefile_id, char* hash) { File* file = new_file(path); if (!file) return 1; char** content = file->content; size_t lines = file->lines; size_t total_len = 0; for (size_t i = 0; i < lines; i++) { total_len += strlen(content[i]); if (i < lines - 1) { total_len++; } } total_len += 1; char* concat_file = calloc(total_len, sizeof(char)); if (!concat_file) { free_file(file); return 1; } strcpy(concat_file, content[0]); for (size_t i = 1; i < lines; i++) { strcat(concat_file, "\n"); strcat(concat_file, content[i]); } const char* basefile_prefix = "basefile "; size_t prefix_len = strlen(basefile_prefix); size_t content_len = prefix_len + total_len; char* final_content = calloc(content_len, sizeof(char)); if (!final_content) { free(concat_file); concat_file = NULL; free_file(file); return 1; } strcat(final_content, basefile_prefix); strcat(final_content, concat_file); free(concat_file); concat_file = NULL; char id[2 + snprintf(NULL, 0, "%d", basefile_id) + strlen(path)]; snprintf(id, sizeof(id), "%s %d", path, basefile_id); object_hash(BaseFileObject, id, hash); char dir_path[PATH_MAX]; char file_path[PATH_MAX]; snprintf(dir_path, sizeof(dir_path), "%s/.merk/objects/%.2s", root, hash); mkdir(dir_path, 0755); snprintf(file_path, sizeof(file_path), "%s/%s", dir_path, hash + 2); FILE* fp = fopen(file_path, "wb"); if (!fp) { perror("ERROR: cannot open path in snapshot_file!\n"); free(final_content); final_content = NULL; free_file(file); return 0; } uLong originalLen = strlen(final_content) + 1; uLong compressedLen = compressBound(originalLen); Bytef* compressed = malloc(compressedLen); if (!compressed) { fclose(fp); free(final_content); final_content = NULL; free_file(file); return 0; } if (compress(compressed, &compressedLen, (const Bytef*)final_content, originalLen) != Z_OK) { perror("ERROR: compression failed in snapshot_file!"); free(compressed); fclose(fp); free(final_content); final_content = NULL; free_file(file); return 0; } fprintf(fp, "%lu ", (unsigned long)originalLen); fwrite(compressed, 1, compressedLen, fp); fclose(fp); chmod(file_path, S_IRUSR | S_IRGRP | S_IROTH); free(compressed); free(final_content); final_content = NULL; free_file(file); return 1; } void free_file(File* file) { if (!file) return; if (file->content) { free(file->content[0]); free(file->content); } 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); }