2026-01-28 19:34:39 +01:00
// retoor <retoor@molodetz.nl>
# include "tool.h"
# include "r_config.h"
2026-01-29 00:38:21 +01:00
# include "r_diff.h"
2026-01-28 19:34:39 +01:00
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sys/stat.h>
# include <unistd.h>
# include <dirent.h>
# include <glob.h>
# include <errno.h>
# include <time.h>
# include <limits.h>
static char * expand_home_directory ( const char * path ) ;
static const char * get_file_type ( struct stat * st ) ;
static void format_time ( time_t raw_time , char * buffer , size_t size ) ;
static struct json_object * read_file_get_description ( void ) ;
static char * read_file_execute ( tool_t * self , struct json_object * args ) ;
static void read_file_print_action ( const char * name , struct json_object * args ) ;
static struct json_object * write_file_get_description ( void ) ;
static char * write_file_execute ( tool_t * self , struct json_object * args ) ;
static void write_file_print_action ( const char * name , struct json_object * args ) ;
static struct json_object * directory_glob_get_description ( void ) ;
static char * directory_glob_execute ( tool_t * self , struct json_object * args ) ;
static void directory_glob_print_action ( const char * name , struct json_object * args ) ;
static struct json_object * mkdir_get_description ( void ) ;
static char * mkdir_execute ( tool_t * self , struct json_object * args ) ;
static void mkdir_print_action ( const char * name , struct json_object * args ) ;
static struct json_object * chdir_get_description ( void ) ;
static char * chdir_execute ( tool_t * self , struct json_object * args ) ;
static void chdir_print_action ( const char * name , struct json_object * args ) ;
static struct json_object * getpwd_get_description ( void ) ;
static char * getpwd_execute ( tool_t * self , struct json_object * args ) ;
static void getpwd_print_action ( const char * name , struct json_object * args ) ;
static const tool_vtable_t read_file_vtable = {
. get_description = read_file_get_description ,
. execute = read_file_execute ,
. print_action = read_file_print_action
} ;
static const tool_vtable_t write_file_vtable = {
. get_description = write_file_get_description ,
. execute = write_file_execute ,
. print_action = write_file_print_action
} ;
static const tool_vtable_t directory_glob_vtable = {
. get_description = directory_glob_get_description ,
. execute = directory_glob_execute ,
. print_action = directory_glob_print_action
} ;
static const tool_vtable_t mkdir_vtable = {
. get_description = mkdir_get_description ,
. execute = mkdir_execute ,
. print_action = mkdir_print_action
} ;
static const tool_vtable_t chdir_vtable = {
. get_description = chdir_get_description ,
. execute = chdir_execute ,
. print_action = chdir_print_action
} ;
static const tool_vtable_t getpwd_vtable = {
. get_description = getpwd_get_description ,
. execute = getpwd_execute ,
. print_action = getpwd_print_action
} ;
static tool_t read_file_tool = { . vtable = & read_file_vtable , . name = " read_file " } ;
static tool_t write_file_tool = { . vtable = & write_file_vtable , . name = " write_file " } ;
static tool_t directory_glob_tool = { . vtable = & directory_glob_vtable , . name = " directory_glob " } ;
static tool_t mkdir_tool = { . vtable = & mkdir_vtable , . name = " mkdir " } ;
static tool_t chdir_tool = { . vtable = & chdir_vtable , . name = " chdir " } ;
static tool_t getpwd_tool = { . vtable = & getpwd_vtable , . name = " getpwd " } ;
tool_t * tool_read_file_create ( void ) { return & read_file_tool ; }
tool_t * tool_write_file_create ( void ) { return & write_file_tool ; }
tool_t * tool_directory_glob_create ( void ) { return & directory_glob_tool ; }
tool_t * tool_mkdir_create ( void ) { return & mkdir_tool ; }
tool_t * tool_chdir_create ( void ) { return & chdir_tool ; }
tool_t * tool_getpwd_create ( void ) { return & getpwd_tool ; }
static char * expand_home_directory ( const char * path ) {
if ( ! path ) return NULL ;
if ( path [ 0 ] ! = ' ~ ' ) return strdup ( path ) ;
const char * home_dir = getenv ( " HOME " ) ;
if ( ! home_dir ) home_dir = getenv ( " USERPROFILE " ) ;
if ( ! home_dir ) return strdup ( path ) ;
size_t home_len = strlen ( home_dir ) ;
size_t path_len = strlen ( path ) ;
char * expanded = malloc ( home_len + path_len ) ;
if ( ! expanded ) return NULL ;
strcpy ( expanded , home_dir ) ;
strcat ( expanded , path + 1 ) ;
return expanded ;
}
static const char * get_file_type ( struct stat * st ) {
if ( S_ISREG ( st - > st_mode ) ) return " file " ;
if ( S_ISDIR ( st - > st_mode ) ) return " directory " ;
return " other " ;
}
static void format_time ( time_t raw_time , char * buffer , size_t size ) {
struct tm * time_info = localtime ( & raw_time ) ;
strftime ( buffer , size , " %Y-%m-%d %H:%M:%S " , time_info ) ;
}
static char * make_dir_recursive ( const char * path ) {
char temp [ 4096 ] ;
char * p = NULL ;
size_t len = strlen ( path ) ;
if ( len > = sizeof ( temp ) ) return strdup ( " Path too long! " ) ;
memcpy ( temp , path , len + 1 ) ;
if ( temp [ len - 1 ] = = ' / ' ) temp [ len - 1 ] = ' \0 ' ;
for ( p = temp + 1 ; * p ; p + + ) {
if ( * p = = ' / ' ) {
* p = ' \0 ' ;
if ( mkdir ( temp , 0777 ) ! = 0 & & errno ! = EEXIST ) {
return strdup ( " Failed to create directory! " ) ;
}
* p = ' / ' ;
}
}
if ( mkdir ( temp , 0777 ) ! = 0 & & errno ! = EEXIST ) {
return strdup ( " Failed to create directory! " ) ;
}
return strdup ( " Directory successfully created. " ) ;
}
static void ensure_parent_directory_exists ( const char * path_including_file_name ) {
if ( ! path_including_file_name ) return ;
char * path = strdup ( path_including_file_name ) ;
if ( ! path ) return ;
char * last_slash = strrchr ( path , ' / ' ) ;
if ( last_slash ) {
* last_slash = ' \0 ' ;
char * result = make_dir_recursive ( path ) ;
free ( result ) ;
}
free ( path ) ;
}
static void read_file_print_action ( const char * name , struct json_object * args ) {
( void ) name ;
if ( ! args ) return ;
struct json_object * path ;
if ( json_object_object_get_ex ( args , " path " , & path ) ) {
fprintf ( stderr , " -> Reading file: %s \n " , json_object_get_string ( path ) ) ;
}
}
static char * read_file_execute ( tool_t * self , struct json_object * args ) {
( void ) self ;
struct json_object * path_obj ;
if ( ! json_object_object_get_ex ( args , " path " , & path_obj ) ) {
return strdup ( " Error: missing 'path' argument " ) ;
}
char * expanded_path = expand_home_directory ( json_object_get_string ( path_obj ) ) ;
if ( ! expanded_path ) return strdup ( " Failed to expand path! " ) ;
FILE * fp = fopen ( expanded_path , " r " ) ;
free ( expanded_path ) ;
if ( ! fp ) return strdup ( " Failed to open file for reading! " ) ;
fseek ( fp , 0 , SEEK_END ) ;
long size = ftell ( fp ) ;
if ( size < 0 ) {
fclose ( fp ) ;
return strdup ( " Failed to determine file size! " ) ;
}
rewind ( fp ) ;
char * content = malloc ( ( size_t ) size + 1 ) ;
if ( ! content ) {
fclose ( fp ) ;
return strdup ( " Memory allocation failed! " ) ;
}
size_t read_size = fread ( content , 1 , ( size_t ) size , fp ) ;
content [ read_size ] = ' \0 ' ;
fclose ( fp ) ;
return content ;
}
static struct json_object * read_file_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 ( " read_file " ) ) ;
json_object_object_add ( function , " description " ,
json_object_new_string ( " Reads / opens / loads a file and returns its contents as a string. " ) ) ;
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 ( path , " description " ,
json_object_new_string ( " Path to the file to read / open / load. " ) ) ;
json_object_object_add ( properties , " path " , path ) ;
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 ) ;
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 void write_file_print_action ( const char * name , struct json_object * args ) {
( void ) name ;
if ( ! args ) return ;
struct json_object * path ;
if ( json_object_object_get_ex ( args , " path " , & path ) ) {
fprintf ( stderr , " -> Writing file: %s \n " , json_object_get_string ( path ) ) ;
}
}
static char * write_file_execute ( tool_t * self , struct json_object * args ) {
( void ) self ;
struct json_object * path_obj , * content_obj ;
if ( ! json_object_object_get_ex ( args , " path " , & path_obj ) ) {
return strdup ( " Error: missing 'path' argument " ) ;
}
if ( ! json_object_object_get_ex ( args , " content " , & content_obj ) ) {
return strdup ( " Error: missing 'content' argument " ) ;
}
const char * path = json_object_get_string ( path_obj ) ;
2026-01-29 00:38:21 +01:00
const char * new_content = json_object_get_string ( content_obj ) ;
// Read old content for diff
char * old_content = NULL ;
FILE * old_fp = fopen ( path , " r " ) ;
if ( old_fp ) {
fseek ( old_fp , 0 , SEEK_END ) ;
long size = ftell ( old_fp ) ;
rewind ( old_fp ) ;
if ( size > = 0 ) {
old_content = malloc ( ( size_t ) size + 1 ) ;
if ( old_content ) {
size_t rs = fread ( old_content , 1 , ( size_t ) size , old_fp ) ;
old_content [ rs ] = ' \0 ' ;
}
}
fclose ( old_fp ) ;
}
if ( old_content ) {
r_diff_print ( path , old_content , new_content ) ;
free ( old_content ) ;
}
2026-01-28 19:34:39 +01:00
ensure_parent_directory_exists ( path ) ;
FILE * fp = fopen ( path , " w+ " ) ;
if ( ! fp ) return strdup ( " Failed to open file for writing! " ) ;
2026-01-29 00:38:21 +01:00
fwrite ( new_content , 1 , strlen ( new_content ) , fp ) ;
2026-01-28 19:34:39 +01:00
fclose ( fp ) ;
return strdup ( " File successfully written. " ) ;
}
static struct json_object * write_file_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 ( " write_file " ) ) ;
json_object_object_add ( function , " description " ,
json_object_new_string ( " Writes / saves / stores content to a file. Content should be in plain format, not json encoded. " ) ) ;
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 ( path , " description " ,
json_object_new_string ( " Path to the plain file to write / save / store. " ) ) ;
json_object_object_add ( properties , " path " , path ) ;
struct json_object * content = json_object_new_object ( ) ;
json_object_object_add ( content , " type " , json_object_new_string ( " string " ) ) ;
json_object_object_add ( content , " description " ,
json_object_new_string ( " Plain content to write / save / store into the file. " ) ) ;
json_object_object_add ( properties , " content " , content ) ;
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 ( " content " ) ) ;
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 void directory_glob_print_action ( const char * name , struct json_object * args ) {
( void ) name ;
if ( ! args ) return ;
struct json_object * path ;
if ( json_object_object_get_ex ( args , " path " , & path ) ) {
fprintf ( stderr , " -> Listing: %s \n " , json_object_get_string ( path ) ) ;
}
}
static char * directory_glob_execute ( tool_t * self , struct json_object * args ) {
( void ) self ;
struct json_object * path_obj ;
if ( ! json_object_object_get_ex ( args , " path " , & path_obj ) ) {
return strdup ( " Error: missing 'path' argument " ) ;
}
char target_dir [ PATH_MAX ] ;
strncpy ( target_dir , json_object_get_string ( path_obj ) , PATH_MAX - 1 ) ;
target_dir [ PATH_MAX - 1 ] = ' \0 ' ;
if ( strcmp ( target_dir , " . " ) = = 0 ) {
target_dir [ 0 ] = ' * ' ;
target_dir [ 1 ] = ' \0 ' ;
}
glob_t results ;
struct stat file_stat ;
char mod_time [ 20 ] , create_time [ 20 ] ;
if ( glob ( target_dir , GLOB_TILDE , NULL , & results ) ! = 0 ) {
return strdup ( " [] " ) ;
}
json_object * json_array = json_object_new_array ( ) ;
for ( size_t i = 0 ; i < results . gl_pathc ; i + + ) {
const char * file_path = results . gl_pathv [ i ] ;
if ( stat ( file_path , & file_stat ) = = - 1 ) continue ;
format_time ( file_stat . st_mtime , mod_time , sizeof ( mod_time ) ) ;
format_time ( file_stat . st_ctime , create_time , sizeof ( create_time ) ) ;
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 ) ) ;
json_object_array_add ( json_array , json_entry ) ;
}
globfree ( & results ) ;
char * result = strdup ( json_object_to_json_string_ext ( json_array , JSON_C_TO_STRING_PRETTY ) ) ;
json_object_put ( json_array ) ;
return result ;
}
static struct json_object * directory_glob_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 ( " directory_glob " ) ) ;
json_object_object_add ( function , " description " ,
json_object_new_string ( " 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 ) ;
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 void mkdir_print_action ( const char * name , struct json_object * args ) {
( void ) name ;
if ( ! args ) return ;
struct json_object * path ;
if ( json_object_object_get_ex ( args , " path " , & path ) ) {
fprintf ( stderr , " -> Creating directory: %s \n " , json_object_get_string ( path ) ) ;
}
}
static char * mkdir_execute ( tool_t * self , struct json_object * args ) {
( void ) self ;
struct json_object * path_obj ;
if ( ! json_object_object_get_ex ( args , " path " , & path_obj ) ) {
return strdup ( " Error: missing 'path' argument " ) ;
}
return make_dir_recursive ( json_object_get_string ( path_obj ) ) ;
}
static struct json_object * mkdir_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 ( " mkdir " ) ) ;
json_object_object_add ( function , " description " ,
json_object_new_string ( " Creates a new directory with the specified path. " ) ) ;
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 ( path , " description " ,
json_object_new_string ( " Path of the directory to create. " ) ) ;
json_object_object_add ( properties , " path " , path ) ;
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 ) ;
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 void chdir_print_action ( const char * name , struct json_object * args ) {
( void ) name ;
if ( ! args ) return ;
struct json_object * path ;
if ( json_object_object_get_ex ( args , " path " , & path ) ) {
fprintf ( stderr , " -> Changing directory: %s \n " , json_object_get_string ( path ) ) ;
}
}
static char * chdir_execute ( tool_t * self , struct json_object * args ) {
( void ) self ;
struct json_object * path_obj ;
if ( ! json_object_object_get_ex ( args , " path " , & path_obj ) ) {
return strdup ( " Error: missing 'path' argument " ) ;
}
if ( chdir ( json_object_get_string ( path_obj ) ) ! = 0 ) {
return strdup ( " Failed to change directory! " ) ;
}
return strdup ( " Directory successfully changed. " ) ;
}
static struct json_object * chdir_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 ( " chdir " ) ) ;
json_object_object_add ( function , " description " ,
json_object_new_string ( " Changes the current working directory to the specified path. Call this function when `cd` is prompted. " ) ) ;
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 ( path , " description " ,
json_object_new_string ( " Path to change the current working directory to. " ) ) ;
json_object_object_add ( properties , " path " , path ) ;
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 ) ;
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 void getpwd_print_action ( const char * name , struct json_object * args ) {
( void ) name ;
( void ) args ;
fprintf ( stderr , " -> Getting current directory \n " ) ;
}
static char * getpwd_execute ( tool_t * self , struct json_object * args ) {
( void ) self ;
( void ) args ;
char * cwd = malloc ( PATH_MAX ) ;
if ( ! cwd ) return strdup ( " Failed to allocate memory for current working directory! " ) ;
if ( ! getcwd ( cwd , PATH_MAX ) ) {
free ( cwd ) ;
return strdup ( " Failed to get current working directory! " ) ;
}
return cwd ;
}
static struct json_object * getpwd_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 ( " getpwd " ) ) ;
json_object_object_add ( function , " description " ,
json_object_new_string ( " Returns the current working directory as a string. " ) ) ;
2026-01-29 00:38:21 +01:00
struct json_object * parameters = json_object_new_object ( ) ;
json_object_object_add ( parameters , " type " , json_object_new_string ( " object " ) ) ;
json_object_object_add ( parameters , " properties " , json_object_new_object ( ) ) ;
2026-01-29 06:01:05 +01:00
json_object_object_add ( parameters , " required " , json_object_new_array ( ) ) ;
2026-01-29 00:38:21 +01:00
json_object_object_add ( parameters , " additionalProperties " , json_object_new_boolean ( 0 ) ) ;
json_object_object_add ( function , " parameters " , parameters ) ;
2026-01-29 06:01:05 +01:00
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 ) ) ;
}
2026-01-28 19:34:39 +01:00
json_object_object_add ( root , " function " , function ) ;
return root ;
}