diff --git a/tools.h b/tools.h index 24e520a..e53fe29 100644 --- a/tools.h +++ b/tools.h @@ -23,6 +23,8 @@ struct json_object *tool_description_linux_terminal(); struct json_object *tool_description_directory_glob(); struct json_object *tool_description_read_file(); struct json_object *tool_description_write_file(); +struct json_object *tool_description_directory_rglob(); + struct json_object *tools_descriptions() { struct json_object *root = json_object_new_array(); @@ -31,7 +33,7 @@ struct json_object *tools_descriptions() { json_object_array_add(root, tool_description_directory_glob()); json_object_array_add(root, tool_description_read_file()); json_object_array_add(root, tool_description_write_file()); - + json_object_array_add(root, tool_description_directory_rglob()); return root; } @@ -75,6 +77,42 @@ char * tool_function_linux_terminal(char * command){ return output ? output : strdup(""); } +struct json_object *tool_description_directory_rglob() { + 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("directory_rglob")); + json_object_object_add(function, "description", json_object_new_string("Recursively list the contents of a specified directory in glob format. " + "Result is a json array containing objects with keys: name, modification_date(iso), creation_date(iso), type and size_bytes.")); + + 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 *directory = json_object_new_object(); + json_object_object_add(directory, "type", json_object_new_string("string")); + json_object_object_add(directory, "description", json_object_new_string("Path to the directory to list in glob format.")); + json_object_object_add(properties, "path", directory); + + 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_object_add(parameters, "required", required); + + json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0)); + + json_object_object_add(function, "parameters", parameters); + json_object_object_add(function, "strict", json_object_new_boolean(1)); + + json_object_object_add(root, "function", function); + + return root; +} + + + struct json_object *tool_description_read_file() { struct json_object *root = json_object_new_object(); json_object_object_add(root, "type", json_object_new_string("function")); @@ -203,6 +241,171 @@ void format_time(time_t raw_time, char *buffer, size_t size) { strftime(buffer, size, "%Y-%m-%d %H:%M:%S", time_info); } + + + +void recursive_glob(const char *pattern, glob_t *results) { + // First, find all matching files in the current scope + glob_t current_matches; + int ret = glob(pattern, GLOB_NOSORT | GLOB_TILDE, NULL, ¤t_matches); + + // Handle errors + if (ret != 0 && ret != GLOB_NOMATCH) { + // Copy error state to results + results->gl_pathc = 0; + results->gl_pathv = NULL; + results->gl_offs = 0; + return; + } + + // Initialize results if this is the first call + if (results->gl_pathv == NULL) { + memset(results, 0, sizeof(glob_t)); + } + + // Add found paths to results + for (size_t i = 0; i < current_matches.gl_pathc; i++) { + char *path = current_matches.gl_pathv[i]; + + // Add the path to results + if (results->gl_pathc == 0) { + // First result + results->gl_pathc = 1; + results->gl_pathv = malloc(sizeof(char *)); + results->gl_pathv[0] = strdup(path); + } else { + // Additional result + results->gl_pathc++; + results->gl_pathv = realloc(results->gl_pathv, + results->gl_pathc * sizeof(char *)); + results->gl_pathv[results->gl_pathc - 1] = strdup(path); + } + } + + // Now look for directories to recurse into + DIR *dir; + struct dirent *entry; + struct stat statbuf; + + // Extract directory part from pattern + char *pattern_copy = strdup(pattern); + char *last_slash = strrchr(pattern_copy, '/'); + char *dir_path; + char *file_pattern; + + if (last_slash) { + *last_slash = '\0'; + dir_path = pattern_copy; + file_pattern = last_slash + 1; + } else { + // No directory part in the pattern + dir_path = "."; + file_pattern = pattern_copy; + } + + if ((dir = opendir(dir_path)) != NULL) { + while ((entry = readdir(dir)) != NULL) { + // Skip . and .. + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; + } + + // Construct full path - ensure sufficient buffer space + char full_path[PATH_MAX]; + int path_len; + + if (strcmp(dir_path, ".") == 0) { + path_len = snprintf(full_path, PATH_MAX, "%s", entry->d_name); + } else { + path_len = snprintf(full_path, PATH_MAX, "%s/%s", dir_path, entry->d_name); + } + + // Check if snprintf truncated the output + if (path_len >= PATH_MAX) { + // Path too long, skip this entry + continue; + } + + // Check if it's a directory + if (stat(full_path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) { + // Create new pattern for recursion - ensure sufficient buffer space + char new_pattern[PATH_MAX]; + int new_pattern_len = snprintf(new_pattern, PATH_MAX, "%s/%s", full_path, file_pattern); + + // Check if snprintf truncated the output + if (new_pattern_len >= PATH_MAX) { + // Pattern too long, skip this recursion + continue; + } + + // Recurse + recursive_glob(new_pattern, results); + } + } + closedir(dir); + } + + // Free temporary resources + free(pattern_copy); + globfree(¤t_matches); +} + + + + + + + +char *tool_function_directory_rglob(char *target_dir) { + fprintf(stderr, "Tools directory_rglob: %s\n", target_dir); + glob_t results; + results.gl_pathc = 0; + struct stat file_stat; + char mod_time[20], create_time[20]; + + // Perform glob search recursively + recursive_glob(target_dir, &results); + + // Create a JSON array to store results + json_object *json_array = json_object_new_array(); + + // Iterate through the matched files + for (size_t i = 0; i < results.gl_pathc; i++) { + const char *file_path = results.gl_pathv[i]; + + // Get file stats + if (stat(file_path, &file_stat) == -1) { + perror("stat failed"); + continue; + } + + // Format timestamps + format_time(file_stat.st_mtime, mod_time, sizeof(mod_time)); + format_time(file_stat.st_ctime, create_time, sizeof(create_time)); // Creation time is unreliable on Linux + + // Create JSON object for each file + json_object *json_entry = json_object_new_object(); + json_object_object_add(json_entry, "name", json_object_new_string(file_path)); + json_object_object_add(json_entry, "modification_date", json_object_new_string(mod_time)); + json_object_object_add(json_entry, "creation_date", json_object_new_string(create_time)); + json_object_object_add(json_entry, "type", json_object_new_string(get_file_type(&file_stat))); + json_object_object_add(json_entry, "size_bytes", json_object_new_int64(file_stat.st_size)); + + // Add to JSON array + json_object_array_add(json_array, json_entry); + } + + // Free glob results + globfree(&results); + char *result = strdup(json_object_to_json_string_ext(json_array, JSON_C_TO_STRING_PRETTY)); + + // Cleanup + json_object_put(json_array); + + return result; +} + + char * tool_function_directory_glob(char *target_dir) { fprintf(stderr, "Tools directory_glob: %s\n", target_dir); glob_t results; @@ -457,6 +660,20 @@ struct json_object *tools_execute(struct json_object *tools_array) { } } } + if (!strcmp(function_name, "directory_rglob")) { + struct json_object *arguments_obj; + if (json_object_object_get_ex(function_obj, "arguments", &arguments_obj)) { + struct json_object *arguments = json_tokener_parse(json_object_get_string(arguments_obj)); + struct json_object *path_obj; + if (json_object_object_get_ex(arguments, "path", &path_obj)) { + char *path = (char *)json_object_get_string(path_obj); + char *listing_result = tool_function_directory_rglob(path); + json_object_object_add(tool_result, "content", json_object_new_string(listing_result)); + free(listing_result); + } + } +} + json_object_array_add(tools_result_messages, tool_result); } }