// retoor #include "tool.h" #include "r_config.h" #include "r_diff.h" #include #include #include #include #include static struct json_object *file_line_replace_get_description(void); static char *file_line_replace_execute(tool_t *self, struct json_object *args); static void file_line_replace_print_action(const char *name, struct json_object *args); static struct json_object *file_apply_patch_get_description(void); static char *file_apply_patch_execute(tool_t *self, struct json_object *args); static void file_apply_patch_print_action(const char *name, struct json_object *args); static const tool_vtable_t file_line_replace_vtable = { .get_description = file_line_replace_get_description, .execute = file_line_replace_execute, .print_action = file_line_replace_print_action }; static const tool_vtable_t file_apply_patch_vtable = { .get_description = file_apply_patch_get_description, .execute = file_apply_patch_execute, .print_action = file_apply_patch_print_action }; static tool_t file_line_replace_tool = { .vtable = &file_line_replace_vtable, .name = "file_line_replace" }; static tool_t file_apply_patch_tool = { .vtable = &file_apply_patch_vtable, .name = "file_apply_patch" }; tool_t *tool_file_line_replace_create(void) { return &file_line_replace_tool; } tool_t *tool_file_apply_patch_create(void) { return &file_apply_patch_tool; } static char *read_full_file(const char *path) { FILE *fp = fopen(path, "r"); if (!fp) return NULL; fseek(fp, 0, SEEK_END); long size = ftell(fp); rewind(fp); char *content = malloc(size + 1); if (content) { size_t rs = fread(content, 1, size, fp); content[rs] = '\0'; } fclose(fp); return content; } static void file_line_replace_print_action(const char *name, struct json_object *args) { (void)name; struct json_object *path; if (json_object_object_get_ex(args, "path", &path)) { fprintf(stderr, " -> Replacing lines in: %s\n", json_object_get_string(path)); } } static char *file_line_replace_execute(tool_t *self, struct json_object *args) { (void)self; struct json_object *path_obj, *start_obj, *end_obj, *replacement_obj; if (!json_object_object_get_ex(args, "path", &path_obj) || !json_object_object_get_ex(args, "start_line", &start_obj) || !json_object_object_get_ex(args, "end_line", &end_obj) || !json_object_object_get_ex(args, "replacement", &replacement_obj)) { return strdup("Error: missing arguments for file_line_replace"); } const char *path = json_object_get_string(path_obj); int start_line = json_object_get_int(start_obj); int end_line = json_object_get_int(end_obj); const char *replacement = json_object_get_string(replacement_obj); char *old_content = read_full_file(path); if (!old_content) return strdup("Error: could not read file"); // Split into lines char **lines = NULL; int count = 0; char *copy = strdup(old_content); char *saveptr; char *line = strtok_r(copy, "\n", &saveptr); while (line) { char **expanded = realloc(lines, sizeof(char *) * (size_t)(count + 1)); if (!expanded) break; lines = expanded; lines[count++] = strdup(line); line = strtok_r(NULL, "\n", &saveptr); } free(copy); if (start_line < 1 || start_line > count || end_line < start_line || end_line > count) { for (int i = 0; i < count; i++) free(lines[i]); free(lines); free(old_content); return strdup("Error: invalid line range"); } // Build new content size_t new_size = 1024 * 1024; // start with 1MB char *new_content = malloc(new_size); new_content[0] = '\0'; size_t current_len = 0; for (int i = 1; i <= count; i++) { const char *to_add = NULL; if (i < start_line || i > end_line) { to_add = lines[i - 1]; } else if (i == start_line) { to_add = replacement; } if (to_add) { size_t add_len = strlen(to_add); if (current_len + add_len + 2 >= new_size) { new_size *= 2; char *expanded_content = realloc(new_content, new_size); if (!expanded_content) break; new_content = expanded_content; } memcpy(new_content + current_len, to_add, add_len); current_len += add_len; new_content[current_len] = '\n'; current_len += 1; new_content[current_len] = '\0'; } } r_diff_print(path, old_content, new_content); FILE *fp = fopen(path, "w"); if (fp) { fputs(new_content, fp); fclose(fp); snapshot_live_record(path, new_content); } for (int i = 0; i < count; i++) free(lines[i]); free(lines); free(old_content); free(new_content); return strdup("Lines replaced successfully."); } static void file_apply_patch_print_action(const char *name, struct json_object *args) { (void)name; struct json_object *path; if (json_object_object_get_ex(args, "path", &path)) { fprintf(stderr, " -> Applying patch to: %s\n", json_object_get_string(path)); } } static char *file_apply_patch_execute(tool_t *self, struct json_object *args) { (void)self; struct json_object *path_obj, *patch_obj; if (!json_object_object_get_ex(args, "path", &path_obj) || !json_object_object_get_ex(args, "patch", &patch_obj)) { return strdup("Error: missing arguments for file_apply_patch"); } const char *path = json_object_get_string(path_obj); const char *patch = json_object_get_string(patch_obj); char *old_content = read_full_file(path); char patch_file[] = "/tmp/r_patch_XXXXXX"; int fd = mkstemp(patch_file); if (fd == -1) return strdup("Error: could not create temp patch file"); ssize_t written = write(fd, patch, strlen(patch)); close(fd); if (written < (ssize_t)strlen(patch)) { unlink(patch_file); return strdup("Error: could not write full patch to temp file"); } pid_t pid = fork(); int res = -1; if (pid == 0) { execl("/usr/bin/patch", "patch", path, patch_file, (char *)NULL); _exit(127); } else if (pid > 0) { int status = 0; waitpid(pid, &status, 0); res = WIFEXITED(status) ? WEXITSTATUS(status) : -1; } unlink(patch_file); if (res != 0) { free(old_content); return strdup("Error: patch application failed"); } char *new_content = read_full_file(path); if (old_content && new_content) { r_diff_print(path, old_content, new_content); } if (new_content) { snapshot_live_record(path, new_content); } free(old_content); free(new_content); return strdup("Patch applied successfully."); } static struct json_object *file_line_replace_get_description(void) { struct json_object *root = json_object_new_object(); json_object_object_add(root, "type", json_object_new_string("function")); struct json_object *function = json_object_new_object(); json_object_object_add(function, "name", json_object_new_string("file_line_replace")); json_object_object_add(function, "description", json_object_new_string("Replace a range of lines in a file with new content.")); struct json_object *parameters = json_object_new_object(); json_object_object_add(parameters, "type", json_object_new_string("object")); struct json_object *properties = json_object_new_object(); struct json_object *path = json_object_new_object(); json_object_object_add(path, "type", json_object_new_string("string")); json_object_object_add(properties, "path", path); struct json_object *start = json_object_new_object(); json_object_object_add(start, "type", json_object_new_string("integer")); json_object_object_add(properties, "start_line", start); struct json_object *end = json_object_new_object(); json_object_object_add(end, "type", json_object_new_string("integer")); json_object_object_add(properties, "end_line", end); struct json_object *repl = json_object_new_object(); json_object_object_add(repl, "type", json_object_new_string("string")); json_object_object_add(properties, "replacement", repl); json_object_object_add(parameters, "properties", properties); struct json_object *required = json_object_new_array(); json_object_array_add(required, json_object_new_string("path")); json_object_array_add(required, json_object_new_string("start_line")); json_object_array_add(required, json_object_new_string("end_line")); json_object_array_add(required, json_object_new_string("replacement")); json_object_object_add(parameters, "required", required); json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0)); json_object_object_add(function, "parameters", parameters); r_config_handle cfg = r_config_get_instance(); if (r_config_use_strict(cfg)) { json_object_object_add(function, "strict", json_object_new_boolean(1)); } json_object_object_add(root, "function", function); return root; } static struct json_object *file_apply_patch_get_description(void) { struct json_object *root = json_object_new_object(); json_object_object_add(root, "type", json_object_new_string("function")); struct json_object *function = json_object_new_object(); json_object_object_add(function, "name", json_object_new_string("file_apply_patch")); json_object_object_add(function, "description", json_object_new_string("Apply a unified diff patch to a file.")); struct json_object *parameters = json_object_new_object(); json_object_object_add(parameters, "type", json_object_new_string("object")); struct json_object *properties = json_object_new_object(); struct json_object *path = json_object_new_object(); json_object_object_add(path, "type", json_object_new_string("string")); json_object_object_add(properties, "path", path); struct json_object *patch = json_object_new_object(); json_object_object_add(patch, "type", json_object_new_string("string")); json_object_object_add(properties, "patch", patch); json_object_object_add(parameters, "properties", properties); struct json_object *required = json_object_new_array(); json_object_array_add(required, json_object_new_string("path")); json_object_array_add(required, json_object_new_string("patch")); json_object_object_add(parameters, "required", required); json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0)); json_object_object_add(function, "parameters", parameters); r_config_handle cfg = r_config_get_instance(); if (r_config_use_strict(cfg)) { json_object_object_add(function, "strict", json_object_new_boolean(1)); } json_object_object_add(root, "function", function); return root; }