From cdfbbcd4b7bc575095da3e3cef18b8899745964a Mon Sep 17 00:00:00 2001 From: Thomas Walker Lynch Date: Sat, 7 Jun 2025 18:40:19 -0700 Subject: [PATCH] subu script update --- README.md | 88 +- developer/cc/Db.lib.c | 200 ++++ developer/cc/DbSubu.lib.c | 157 +++ developer/cc/Db_close.cli.c | 32 + developer/cc/{server.cli.c => Hello.cli.c} | 41 +- developer/cc/{hello.lib.c => Hello.lib.c} | 29 +- developer/cc/Server.cli.c | 92 ++ developer/cc/Server.lib.c | 219 +++++ developer/cc/db_add_user.cli.c | 36 + developer/cc/db_delete_user.cli.c | 32 + developer/cc/db_log_event.cli.c | 32 + developer/cc/db_open.cli.c | 34 + developer/cc/db_validate_schema.cli.c | 25 + developer/cc/hello.cli.c | 16 - developer/cc/server.lib.c | 161 ---- .../document/RT C coding conventions.html | 153 --- developer/document/RT-code-format.el | 912 ++++++++++++++++++ developer/document/RT_code_format.txt | 135 --- developer/document/arg1.el | 78 ++ developer/document/arg2.el | 102 ++ .../document/error_messages_and_logging.txt | 21 + developer/document/schema.txt | 37 + developer/document/see_also.txt | 4 + .../document/variable_suffix_conventions.txt | 31 - developer/mockup/subu_server_home/Db_close | Bin 0 -> 23008 bytes developer/mockup/subu_server_home/Hello | Bin 0 -> 22104 bytes developer/mockup/subu_server_home/Server | Bin 0 -> 26712 bytes .../{server_test.log => db.sqlite} | 0 developer/mockup/subu_server_home/db_add_user | Bin 0 -> 26704 bytes .../mockup/subu_server_home/db_delete_user | Bin 0 -> 26616 bytes .../mockup/subu_server_home/db_log_event | Bin 0 -> 26608 bytes developer/mockup/subu_server_home/db_open | Bin 0 -> 22816 bytes .../subu_server_home/db_validate_schema | Bin 0 -> 26520 bytes developer/mockup/subu_server_home/hello | Bin 21368 -> 0 bytes developer/mockup/subu_server_home/server | Bin 23272 -> 0 bytes developer/mockup/subu_server_home/server.log | 7 - .../mockup/{subu_server_home => }/todo.txt | 0 developer/shell/subu | 52 +- developer/sqlite/schema.sql | 79 ++ developer/tool/clean | 23 + developer/tool/make | 13 +- developer/tool/makefile | 10 +- developer/tool/mockup_update | 17 + tool_shared/document/install | 19 + 44 files changed, 2312 insertions(+), 575 deletions(-) create mode 100644 developer/cc/Db.lib.c create mode 100644 developer/cc/DbSubu.lib.c create mode 100644 developer/cc/Db_close.cli.c rename developer/cc/{server.cli.c => Hello.cli.c} (51%) rename developer/cc/{hello.lib.c => Hello.lib.c} (78%) create mode 100644 developer/cc/Server.cli.c create mode 100644 developer/cc/Server.lib.c create mode 100644 developer/cc/db_add_user.cli.c create mode 100644 developer/cc/db_delete_user.cli.c create mode 100644 developer/cc/db_log_event.cli.c create mode 100644 developer/cc/db_open.cli.c create mode 100644 developer/cc/db_validate_schema.cli.c delete mode 100644 developer/cc/hello.cli.c delete mode 100644 developer/cc/server.lib.c delete mode 100644 developer/document/RT C coding conventions.html create mode 100644 developer/document/RT-code-format.el delete mode 100644 developer/document/RT_code_format.txt create mode 100644 developer/document/arg1.el create mode 100644 developer/document/arg2.el create mode 100644 developer/document/error_messages_and_logging.txt create mode 100644 developer/document/schema.txt create mode 100644 developer/document/see_also.txt delete mode 100644 developer/document/variable_suffix_conventions.txt create mode 100755 developer/mockup/subu_server_home/Db_close create mode 100755 developer/mockup/subu_server_home/Hello create mode 100755 developer/mockup/subu_server_home/Server rename developer/mockup/subu_server_home/{server_test.log => db.sqlite} (100%) create mode 100755 developer/mockup/subu_server_home/db_add_user create mode 100755 developer/mockup/subu_server_home/db_delete_user create mode 100755 developer/mockup/subu_server_home/db_log_event create mode 100755 developer/mockup/subu_server_home/db_open create mode 100755 developer/mockup/subu_server_home/db_validate_schema delete mode 100755 developer/mockup/subu_server_home/hello delete mode 100755 developer/mockup/subu_server_home/server delete mode 100644 developer/mockup/subu_server_home/server.log rename developer/mockup/{subu_server_home => }/todo.txt (100%) create mode 100644 developer/sqlite/schema.sql create mode 100755 developer/tool/clean create mode 100755 developer/tool/mockup_update create mode 100644 tool_shared/document/install diff --git a/README.md b/README.md index c739da3..66a2cbf 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,89 @@ -release directory has some resources that are shared among projects +developer/shell/subu is what I am using to run a sub user. I haven't been maintaining any of the rest of it. + + +# `subu` — Sub-User Shell Launcher + +## Overview + +`subu` is a shell script used to launch an interactive login shell under a **sub-user** account. +It provides an isolated session under a consistent naming scheme, while correctly configuring GUI forwarding and persistent services. + +Sub-users are named using the convention: + +``` +Thomas- +``` + +This naming scheme is required because Linux user names must be globally unique — the kernel has no built-in notion of subusers (yet!). + +--- + +## Syntax + +```bash +subu +``` + +Where `` is the symbolic sub-identity (e.g., `incommon`, `dev`, `tester`). + +--- + +## Behavior + +- Launches a shell as user `Thomas-` +- Detects whether the current session is under **Wayland** or **X11**: + - Under Wayland: uses `xhost +SI:localuser:Thomas-` + - Under X11: extracts the appropriate `.Xauthority` cookie to `$HOME/subu//.Xauthority` +- Sets `DISPLAY` and `XAUTHORITY` for graphical applications +- Enables lingering via `loginctl enable-linger` so sub-user services persist +- Runs the user’s shell (as listed in `/etc/passwd`) in **login mode** (`-l`) + +The shell launched will source `.bash_profile`, which should in turn source `.bashrc`: + +```bash +# Inside ~/.bash_profile +if [ -f "$HOME/.bashrc" ]; then + . "$HOME/.bashrc" +fi +``` + +This ensures that aliases, prompts, and other interactive settings are applied. + +--- + +## Subuser: `incommon` + +The subuser named `incommon` is a shared environment used across all sub-users. +It is referenced in the PATH: + +``` +/home/Thomas/subu_data/incommon/executable +``` + +This allows tools and scripts to be shared without duplication. + +--- + +## Example + +```bash +subu incommon +``` + +This spawns a login shell as user `Thomas-incommon`, with GUI forwarding, shared executable access, and persistent services via systemd. + +--- + +## Notes + +- Each `` must have a corresponding home directory: + ``` + /home/Thomas/subu_data/ + ``` +- Ensure that the sub-user exists in `/etc/passwd` with a valid shell. +- Sub-users can run GUI applications (e.g., Emacs, Firefox) with correct X access. +- Sessions are sandboxed per sub-user, ideal for role-based separation, testing, or containment. + +--- diff --git a/developer/cc/Db.lib.c b/developer/cc/Db.lib.c new file mode 100644 index 0000000..44c9b3f --- /dev/null +++ b/developer/cc/Db.lib.c @@ -0,0 +1,200 @@ +#ifndef IFACE +#define Db·IMPLEMENTATION +#define IFACE +#endif + +#ifndef Db·IFACE +#define Db·IFACE + + #include + #include + + // Enum for exit codes + typedef enum { + Db·EXIT_SUCCESS = 0, + Db·EXIT_DB_OPEN_ERROR, + Db·EXIT_SCHEMA_LOAD_ERROR, + Db·EXIT_MEMORY_ALLOCATION_ERROR, + Db·EXIT_STATEMENT_PREPARE_ERROR, + Db·EXIT_STATEMENT_EXECUTE_ERROR + } Db·ExitCode; + + // Interface prototypes + sqlite3* Db·open(const char *db_path ,bool create_if_not_exists); + Db·ExitCode Db·load_schema(sqlite3 *db, const char *schema_path); + Db·ExitCode Db·log_event(sqlite3 *db, int event_id, int user_id); + int Db·query( + sqlite3 *db + ,const char *sql + ,int (*callback)(void * ,int ,char ** ,char **) + ,void *callback_arg + ); + void Db·close(sqlite3 *db); + +#endif // Db·IFACE + +#ifndef Db·IMPLEMENTATION + + #include + #include + #include + #include + #include + #include + #include + + sqlite3* Db·open(const char *db_path ,bool create_if_not_exists){ + sqlite3 *db; + FILE *file_check = fopen(db_path ,"r"); + + if(!file_check && create_if_not_exists){ + file_check = fopen(db_path ,"w"); + if(!file_check){ + fprintf( + stderr, + "Db::open failed to create database file '%s': %s\n", + db_path, + strerror(errno) + ); + return NULL; + } + fclose(file_check); + printf("Db::open created new database file '%s'\n", db_path); + }else if(!file_check){ + fprintf(stderr ,"Db::open database file '%s' not found and create flag not set\n" ,db_path); + return NULL; + }else{ + fclose(file_check); + } + + if( sqlite3_open(db_path ,&db) != SQLITE_OK ){ + fprintf( + stderr, + "Db::open failed to open database '%s': %s\n", + db_path, + sqlite3_errmsg(db) + ); + return NULL; + } + + printf("Db::open database '%s' opened successfully\n", db_path); + return db; + } + + // Load schema from a file + Db·ExitCode Db·load_schema(sqlite3 *db ,const char *schema_path){ + FILE *file = fopen(schema_path, "r"); + if(!file){ + fprintf + ( + stderr + ,"Db::load_schema failed to open schema file '%s'\n" + ,schema_path + ); + return Db·EXIT_SCHEMA_LOAD_ERROR; + } + + fseek(file, 0, SEEK_END); + long file_size = ftell(file); + rewind(file); + + char *schema = malloc(file_size + 1); + if(!schema){ + fprintf(stderr, "Db::load_schema memory allocation failed\n"); + fclose(file); + return Db·EXIT_MEMORY_ALLOCATION_ERROR; + } + + fread(schema, 1, file_size, file); + schema[file_size] = '\0'; + fclose(file); + + char *err_msg = NULL; + if( sqlite3_exec(db, schema, NULL, NULL, &err_msg) != SQLITE_OK ){ + fprintf + ( + stderr + ,"Db::load_schema failed to execute schema: %s\n" + ,err_msg + ); + sqlite3_free(err_msg); + free(schema); + return Db·EXIT_STATEMENT_EXECUTE_ERROR; + } + + printf("Db::load_schema schema initialized successfully from '%s'\n", schema_path); + free(schema); + return Db·EXIT_SUCCESS; + } + + // Log an event into the database + Db·ExitCode Db·log_event(sqlite3 *db ,int event_id ,int user_id){ + const char *sql_template = + "INSERT INTO db_event (event_time ,event_id ,user_id) " + "VALUES (CURRENT_TIMESTAMP ,? ,?);"; + sqlite3_stmt *stmt; + + if( sqlite3_prepare_v2(db ,sql_template ,-1 ,&stmt ,NULL) != SQLITE_OK ){ + fprintf + ( + stderr + ,"Db::log_event failed to prepare statement: %s\n" + ,sqlite3_errmsg(db) + ); + return Db·EXIT_STATEMENT_PREPARE_ERROR; + } + + sqlite3_bind_int(stmt, 1, event_id); + sqlite3_bind_int(stmt, 2, user_id); + + if( sqlite3_step(stmt) != SQLITE_DONE ){ + fprintf + ( + stderr + ,"Db::log_event failed to execute statement: %s\n" + ,sqlite3_errmsg(db) + ); + sqlite3_finalize(stmt); + return Db·EXIT_STATEMENT_EXECUTE_ERROR; + } + + sqlite3_finalize(stmt); + return Db·EXIT_SUCCESS; + } + + // Query Execution Function + int Db·query( + sqlite3 *db + ,const char *sql + ,int (*callback)(void * ,int ,char ** ,char **) + ,void *callback_arg + ){ + char *err_msg = NULL; + int rc = sqlite3_exec(db ,sql ,callback ,callback_arg ,&err_msg); + + if( rc != SQLITE_OK ){ + fprintf + ( + stderr + ,"Db::query SQL error: %s\nQuery: %s\n" + ,err_msg + ,sql + ); + sqlite3_free(err_msg); + return rc; + } + + return SQLITE_OK; + } + + // Close the database + void Db·close(sqlite3 *db){ + if( db ){ + sqlite3_close(db); + printf("Db::close database connection closed\n"); + } + } + +#endif // Db·IMPLEMENTATION + + diff --git a/developer/cc/DbSubu.lib.c b/developer/cc/DbSubu.lib.c new file mode 100644 index 0000000..4274dec --- /dev/null +++ b/developer/cc/DbSubu.lib.c @@ -0,0 +1,157 @@ +#ifndef IFACE +#define DbSubu·IMPLEMENTATION +#define IFACE +#endif + +#ifndef DbSubu·IFACE +#define DbSubu·IFACE + + #include + + typedef struct DbSubu { + sqlite3 *db; + } DbSubu; + + + // db connection + DbSubu* DbSubu·open( const char *db_path ); + void DbSubu·close( DbSubu *db ); + int DbSubu·validate_schema( DbSubu *db ); + + // User Management + int DbSubu·add_user( DbSubu *db ,const char *name ,const char *home_directory ,int shell_id ,int parent_id ,int user_type_id ); + int DbSubu·delete_user( DbSubu *db ,int user_id ); + int DbSubu·get_user( DbSubu *db ,int user_id ,char **name ,char **home_directory ,int *shell_id ,int *parent_id ,int *user_type_id ); + + // Sharing Management + int DbSubu·add_share( DbSubu *db ,int user_id ,int other_user_id ,const char *permissions ); + int DbSubu·delete_share( DbSubu *db ,int share_id ); + + // System Resource Management + int DbSubu·grant_resource( DbSubu *db ,int user_id ,int resource_id ,int granted_by ); + int DbSubu·revoke_resource( DbSubu *db ,int user_id ,int resource_id ); + + // Event Logging + int DbSubu·log_event( DbSubu *db ,int event_id ,int user_id ); + +#endif // DbSubu·IFACE + +#ifdef DbSubu·IMPLEMENTATION + + #include + #include + #include + #include "Db.lib.c" + + // Open the database + DbSubu* DbSubu·open( const char *db_path ){ + DbSubu *db = malloc( sizeof(DbSubu) ); + if( !db ){ + fprintf( stderr ,"DbSubu·open:: failed to allocate memory for DbSubu\n" ); + return NULL; + } + db->db = Db·open(db_path ,true); + if( !db->db ){ + free( db ); + return NULL; + } + return db; + } + + // Close the database + void DbSubu·close( DbSubu *db ){ + if( db ){ + Db·close( db->db ); + free( db ); + } + } + + // Validate the schema + int DbSubu·validate_schema( DbSubu *db ){ + // Validation logic for ensuring the schema is correct + return 0; // Placeholder for schema validation implementation + } + + // Add a user + int DbSubu·add_user( DbSubu *db ,const char *name ,const char *home_directory ,int shell_id ,int parent_id ,int user_type_id ){ + char sql[256]; + snprintf + ( + sql + ,sizeof(sql) + ,"INSERT INTO user (name ,home_directory ,shell ,parent_id ,user_type_id) VALUES ('%s' ,'%s' ,%d ,%d ,%d);" + ,name + ,home_directory + ,shell_id + ,parent_id + ,user_type_id + ); + return Db·query( db->db ,sql ,NULL ,NULL ); + } + + // Delete a user + int DbSubu·delete_user( DbSubu *db ,int user_id ){ + char sql[128]; + snprintf( sql ,sizeof(sql) ,"DELETE FROM user WHERE id = %d;" ,user_id ); + return Db·query( db->db ,sql ,NULL ,NULL ); + } + + // Log an event + int DbSubu·log_event( DbSubu *db ,int event_id ,int user_id ){ + char sql[128]; + snprintf + ( + sql + ,sizeof(sql) + ,"INSERT INTO db_event (event_id ,user_id) VALUES (%d ,%d);" + ,event_id + ,user_id + ); + return Db·query( db->db ,sql ,NULL ,NULL ); + } + + // Add to a list (private function) + static int add_to_list( sqlite3 *db ,const char *list_name ,const char *entry_name ){ + char sql[128]; + snprintf + ( + sql + ,sizeof(sql) + ,"INSERT INTO %s (name) VALUES ('%s');" + ,list_name + ,entry_name + ); + return Db·query( db ,sql ,NULL ,NULL ); + } + + // Get list entries (private function) + static char** get_list( sqlite3 *db ,const char *list_name ,int *count ){ + char sql[128]; + snprintf( sql ,sizeof(sql) ,"SELECT name FROM %s;" ,list_name ); + + struct ListResult { + char **entries; + int count; + } result = { NULL ,0 }; + + int callback( void *arg ,int argc ,char **argv ,char **col_names ){ + (void)argc; (void)col_names; + struct ListResult *res = arg; + res->entries = realloc( res->entries ,(res->count + 1) * sizeof(char *) ); + res->entries[res->count++] = strdup( argv[0] ); + return 0; + } + + if( Db·query( db ,sql ,callback ,&result ) != SQLITE_OK ){ + for( int i = 0; i < result.count; ++i ){ + free( result.entries[i] ); + } + free( result.entries ); + return NULL; + } + + *count = result.count; + return result.entries; + } + +#endif // DbSubu·IMPLEMENTATION diff --git a/developer/cc/Db_close.cli.c b/developer/cc/Db_close.cli.c new file mode 100644 index 0000000..8575260 --- /dev/null +++ b/developer/cc/Db_close.cli.c @@ -0,0 +1,32 @@ +#define IFACE +#include +#include +#include +#include +#include "Db.lib.c" + +int main(int argc ,char *argv[]){ + if( argc < 2 ){ + fprintf(stderr ,"Usage: %s \n" ,argv[0]); + return EXIT_FAILURE; + } + + // Parse the SQLite handle from the command-line argument + uintptr_t handle_as_int; + if( sscanf(argv[1] ,"%lx" ,&handle_as_int) != 1 ){ + fprintf(stderr ,"%s::main failed to parse handle '%s'\n" ,argv[0] ,argv[1]); + return EXIT_FAILURE; + } + + sqlite3 *db = (sqlite3 *)handle_as_int; + + // Attempt to close the database + if( db ){ + Db·close(db); + printf("Database handle %p closed successfully.\n" ,db); + return EXIT_SUCCESS; + } else { + fprintf(stderr ,"Invalid or NULL database handle: %p\n" ,db); + return EXIT_FAILURE; + } +} diff --git a/developer/cc/server.cli.c b/developer/cc/Hello.cli.c similarity index 51% rename from developer/cc/server.cli.c rename to developer/cc/Hello.cli.c index 04bceae..2a18583 100644 --- a/developer/cc/server.cli.c +++ b/developer/cc/Hello.cli.c @@ -1,3 +1,4 @@ + /* The subu server command line interface. @@ -14,59 +15,53 @@ #include #include #include -#include "server.lib.c" +#include "Hello.lib.c" // Define defaults #define DEFAULT_SOCKET_PATH "socket" -#define DEFAULT_LOG_PATH "log.txt" -int main( int argc ,char *argv[] ){ +int main( int argc ,char **argv ){ char *socket_path = DEFAULT_SOCKET_PATH; - char *log_path = DEFAULT_LOG_PATH; - int error_flag = 0; // Flag to track errors + int error_flag = 0; - // Parse options int opt; - while( (opt = getopt(argc ,argv ,"s:l:")) != -1 ){ + while( (opt = getopt(argc ,argv ,":s:l:")) != -1 ){ switch( opt ){ case 's': socket_path = optarg; break; - case 'l': - log_path = optarg; - break; case '?': // Unknown option - fprintf( stderr ,"Error: Unknown option '-%c'\n" ,optopt ); + fprintf( stderr ,"%s::main unknown option '-%c'\n" ,argv[0] ,optopt ); error_flag = 1; break; case ':': // Missing argument - fprintf( stderr ,"Error: Missing argument for '-%c'\n" ,optopt ); + fprintf( stderr ,"%s::main missing argument for option '-%c'\n" ,argv[0] ,optopt ); error_flag = 1; break; } } - // Check for too many arguments - if( optind > argc - 1 ){ - fprintf( stderr ,"Error: Too many arguments provided.\n" ); + if( optind > argc ){ + fprintf( stderr ,"%s::main optind(%d) > argc(%d), which indicates an option parsing bug\n" ,argv[0] ,optind ,argc ); error_flag = 1; } // Exit on error after processing all options if( error_flag ){ - fprintf( stderr ,"Usage: %s [-s ] [-l ] [arguments...]\n" ,argv[0] ); + fprintf( stderr ,"%s::main usage: %s [-s ] [arguments...]\n" ,argv[0] ,argv[0] ); return EXIT_FAILURE; } // Rebase argv to prepare for run - argv[optind - 1] = argv[0]; // Program name at the new base - argc -= (optind - 1); - argv += (optind - 1); + if(optind > 0){ + argv[optind - 1] = argv[0]; // Program name at the new base + argc -= (optind - 1); + argv += (optind - 1); + } // Log parsed options - printf( "Socket Path: %s\n" ,socket_path ); - printf( "Log Path: %s\n" ,log_path ); + printf( "%s::main socket_path='%s'\n" ,argv[0] ,socket_path ); - // Call the core server function with the rebased arguments - return Server·run(argv ,argc ,socket_path ,log_path); + // Call the hello function + return Hello·run(argc ,argv ,socket_path); } diff --git a/developer/cc/hello.lib.c b/developer/cc/Hello.lib.c similarity index 78% rename from developer/cc/hello.lib.c rename to developer/cc/Hello.lib.c index 27acf6d..28d0f19 100644 --- a/developer/cc/hello.lib.c +++ b/developer/cc/Hello.lib.c @@ -7,18 +7,19 @@ #define Hello·IFACE // Necessary interface includes - #include - #include - #include + // .. none // Interface prototypes - int Hello·run(); + int Hello·run(int argc ,char** argv ,char *socket_path); #endif // Hello·IFACE #ifdef Hello·IMPLEMENTATION // Implementation-specific includes + #include + #include + #include #include #include #include @@ -30,35 +31,28 @@ #define Hello·LOG_PATH "server_test.log" #define Hello·BUFFER_SIZE 256 - int Hello·run(){ + int Hello·run(int argc ,char** argv ,char *socket_path){ + (void)argc; // Suppress unused variable warnings + (void)argv; + int client_fd; struct sockaddr_un address; char buffer[Hello·BUFFER_SIZE]; - FILE *log_file; - - // Open the log file - log_file = fopen(Hello·LOG_PATH ,"a+"); - if( log_file == NULL ){ - perror("Hello·run:: error opening log file"); - return EXIT_FAILURE; - } client_fd = socket(AF_UNIX ,SOCK_STREAM ,0); if( client_fd == -1 ){ perror("Hello·run:: error opening socket"); - fclose(log_file); return EXIT_FAILURE; } // Configure server socket address memset(&address ,0 ,sizeof(address)); address.sun_family = AF_UNIX; - strncpy(address.sun_path ,Hello·SOCKET_PATH ,sizeof(address.sun_path) - 1); + strncpy(address.sun_path ,socket_path ,sizeof(address.sun_path) - 1); // Connect to the server if( connect(client_fd ,(struct sockaddr *)&address ,sizeof(address)) == -1 ){ perror("Hello·run:: error connecting to server"); - fclose(log_file); close(client_fd); return EXIT_FAILURE; } @@ -67,15 +61,12 @@ char *out_buf = "hello\n"; if( write(client_fd ,out_buf ,strlen(out_buf)) == -1 ){ perror("Hello·run:: error writing to server"); - fclose(log_file); - close(client_fd); return EXIT_FAILURE; } printf("Hello·run:: sent \"%s\"\n" ,out_buf); // Clean up - fclose(log_file); close(client_fd); return EXIT_SUCCESS; diff --git a/developer/cc/Server.cli.c b/developer/cc/Server.cli.c new file mode 100644 index 0000000..6b38b3b --- /dev/null +++ b/developer/cc/Server.cli.c @@ -0,0 +1,92 @@ +/* + The subu server command line interface. + + Usage: + server [-s ] [-l ] [arguments...] + + Options: + -s Specify the Unix socket file path. Default: ./socket + -l Specify the log file path. Default: ./log.txt +*/ + +#define IFACE +#include +#include +#include +#include +#include "Server.lib.c" + +// Define defaults +#define DEFAULT_SOCKET_PATH "socket" +#define DEFAULT_LOG_PATH "log.txt" + +int main( int argc ,char **argv ){ + char *socket_path = DEFAULT_SOCKET_PATH; + char *log_path = DEFAULT_LOG_PATH; + int error_flag = 0; + + // Parse command-line options + int opt; + while( (opt = getopt(argc ,argv ,":s:l:")) != -1 ){ + switch( opt ){ + case 's': + socket_path = optarg; + break; + case 'l': + log_path = optarg; + break; + case '?': // Unknown option + fprintf( stderr ,"%s::main unknown option '-%c'\n" ,argv[0] ,optopt ); + error_flag = 1; + break; + case ':': // Missing argument + fprintf( stderr ,"%s::main missing argument for option '-%c'\n" ,argv[0] ,optopt ); + error_flag = 1; + break; + } + } + + if( optind > argc ){ + fprintf( stderr ,"%s::main optind(%d) > argc(%d), which indicates an option parsing bug\n" ,argv[0] ,optind ,argc ); + error_flag = 1; + } + + // Exit on error after processing all options + if( error_flag ){ + fprintf( stderr ,"%s::main usage: %s [-s ] [-l ] [arguments...]\n" ,argv[0] ,argv[0] ); + return EXIT_FAILURE; + } + + // Rebase argv to prepare for run + if(optind > 0){ + argv[optind - 1] = argv[0]; // Program name at the new base + argc -= (optind - 1); + argv += (optind - 1); + } + + // Open the log file + FILE *log_file = Server·open_log(log_path); + if( !log_file ){ + fprintf( stderr ,"%s::main unable to open log file '%s'\n" ,argv[0] ,log_path ); + return Server·EXIT_LOG_FILE_ERROR; + } + + // Log parsed options + fprintf( log_file ,"%s::main socket_path='%s'\n" ,argv[0] ,socket_path ); + fprintf( log_file ,"%s::main log_path='%s'\n" ,argv[0] ,log_path ); + fflush(log_file); + + // Prepare file descriptors for error reporting + int fds[] = { fileno(stderr), fileno(log_file), -1 }; + + // Call the core server function + int exit_code = Server·run(argc ,argv ,fds ,socket_path); + + // Report return condition + Server·return_condition_report(exit_code ,fds); + + // Clean up + fclose(log_file); + + return exit_code; +} diff --git a/developer/cc/Server.lib.c b/developer/cc/Server.lib.c new file mode 100644 index 0000000..16451a3 --- /dev/null +++ b/developer/cc/Server.lib.c @@ -0,0 +1,219 @@ +#ifndef IFACE +#define Server·IMPLEMENTATION +#define IFACE +#endif + +#ifndef Server·IFACE +#define Server·IFACE + + #include + #include + + // Exit codes + typedef enum { + Server·EXIT_SUCCESS = 0, + Server·EXIT_LOG_FILE_ERROR, + Server·EXIT_SOCKET_CREATION_ERROR, + Server·EXIT_BIND_ERROR, + Server·EXIT_LISTEN_ERROR, + Server·EXIT_ACCEPT_ERROR + } Server·ExitCode; + + // Interface prototypes + int Server·run( int argc ,char **argv ,int *fds ,char *socket_path ); + void Server·return_condition_report( Server·ExitCode code ,int *fds ); + void Server·report( int *fds ,const char *message ); + FILE* Server·open_log( const char *log_path ); + +#endif // Server·IFACE + +#ifdef Server·IMPLEMENTATION + + // Implementation-specific includes + #include + #include + #include + #include + #include // Ensure full definition of struct ucred + #include + #include + #include + #include + + // Constants + #define Server·BUFFER_SIZE 256 + #define MAX_ARGC 16 + + // Internal function prototypes + static void parse( int *fds ,struct ucred *client_cred ,char *input_line ); + static void hello( int *fds ,int argc ,char *argv[] ,struct ucred *client_cred ); + + // Log a message with time and to multiple destinations + void Server·report( int *fds ,const char *message ){ + time_t now = time(NULL); + char time_buffer[32]; + strftime(time_buffer ,sizeof(time_buffer) ,"%Y-%m-%dT%H:%M:%SZ" ,gmtime(&now)); + + for( int i = 0; fds[i] != -1; ++i ){ + dprintf( fds[i] ,"\n%s:: %s" ,time_buffer ,message ); + } + } + + int Server·run( int argc ,char **argv ,int *fds ,char *socket_path ){ + (void)argc; // Suppress unused variable warnings + (void)argv; + + int server_fd ,client_fd; + struct sockaddr_un address; + + // Create socket + if( (server_fd = socket(AF_UNIX ,SOCK_STREAM ,0)) == -1 ){ + Server·report(fds ,"Socket creation failed."); + return Server·EXIT_SOCKET_CREATION_ERROR; + } + + // Configure socket address + memset(&address ,0 ,sizeof(address)); + address.sun_family = AF_UNIX; + strncpy(address.sun_path ,socket_path ,sizeof(address.sun_path) - 1); + + unlink(socket_path); + if( bind(server_fd ,(struct sockaddr *)&address ,sizeof(address)) == -1 ){ + Server·report(fds ,"Binding socket failed."); + close(server_fd); + return Server·EXIT_BIND_ERROR; + } + + if( listen(server_fd ,5) == -1 ){ + Server·report(fds ,"Listening on socket failed."); + close(server_fd); + return Server·EXIT_LISTEN_ERROR; + } + + char startup_message[Server·BUFFER_SIZE]; + snprintf(startup_message ,Server·BUFFER_SIZE ,"Server running with socket '%s' ,awaiting connections..." ,socket_path); + Server·report(fds ,startup_message); + + while( (client_fd = accept(server_fd ,NULL ,NULL)) != -1 ){ + struct ucred client_cred; + socklen_t len = sizeof(client_cred); + + if( getsockopt(client_fd ,SOL_SOCKET ,SO_PEERCRED ,&client_cred ,&len) == -1 ){ + Server·report(fds ,"Failed to retrieve client credentials."); + close(client_fd); + continue; + } + + char connection_message[Server·BUFFER_SIZE]; + snprintf(connection_message ,Server·BUFFER_SIZE , + "Connection from PID=%d ,UID=%d ,GID=%d" , + client_cred.pid ,client_cred.uid ,client_cred.gid); + Server·report(fds ,connection_message); + + char buffer[Server·BUFFER_SIZE]; + memset(buffer ,0 ,Server·BUFFER_SIZE); + ssize_t bytes_read = read(client_fd ,buffer ,Server·BUFFER_SIZE - 1); + if(bytes_read > 0){ + char *line = strtok(buffer ,"\n"); + while(line != NULL){ + parse(fds ,&client_cred ,line); + line = strtok(NULL ,"\n"); + } + } else if(bytes_read == -1){ + Server·report(fds ,"Error reading from client."); + } + + close(client_fd); + } + + Server·report(fds ,"Error accepting connection."); + close(server_fd); + unlink(socket_path); + return Server·EXIT_ACCEPT_ERROR; + } + + // Parse a single input line and dispatch to the appropriate command + static void parse( int *fds ,struct ucred *client_cred ,char *input_line ){ + char *argv[MAX_ARGC + 1] = {0}; + int argc = 0; + + char *line_copy = strdup(input_line); + if(!line_copy){ + Server·report(fds ,"Failed to duplicate input line."); + return; + } + + char *token = strtok(line_copy ," "); + while(token != NULL && argc < MAX_ARGC){ + argv[argc++] = token; + token = strtok(NULL ," "); + } + + if(argc > 0){ + if( strcmp(argv[0] ,"hello") == 0 ){ + hello(fds ,argc ,argv ,client_cred); + }else{ + char unknown_command_message[Server·BUFFER_SIZE]; + snprintf(unknown_command_message ,Server·BUFFER_SIZE ,"Unknown command '%s'" ,argv[0]); + Server·report(fds ,unknown_command_message); + } + } + + free(line_copy); + } + + // Example command: hello + static void hello( int *fds ,int argc ,char *argv[] ,struct ucred *client_cred ){ + char hello_message[Server·BUFFER_SIZE]; + snprintf(hello_message ,Server·BUFFER_SIZE , + "hello:: invoked by PID=%d ,UID=%d ,GID=%d" , + client_cred->pid ,client_cred->uid ,client_cred->gid); + Server·report(fds ,hello_message); + + for( int i = 1; i < argc; ++i ){ + char argument_message[Server·BUFFER_SIZE]; + snprintf(argument_message ,Server·BUFFER_SIZE ," Arg %d: %s" ,i ,argv[i]); + Server·report(fds ,argument_message); + } + } + + // Error reporting function + void Server·return_condition_report( Server·ExitCode code ,int *fds ){ + const char *message; + switch( code ){ + case Server·EXIT_SUCCESS: + message = "Operation completed successfully."; + break; + case Server·EXIT_LOG_FILE_ERROR: + message = "Failed to open log file."; + break; + case Server·EXIT_SOCKET_CREATION_ERROR: + message = "Socket creation failed."; + break; + case Server·EXIT_BIND_ERROR: + message = "Binding socket failed."; + break; + case Server·EXIT_LISTEN_ERROR: + message = "Listening on socket failed."; + break; + case Server·EXIT_ACCEPT_ERROR: + message = "Error accepting connection."; + break; + default: + message = "Unknown error occurred."; + break; + } + + Server·report(fds ,message); + } + + // Log file opener + FILE* Server·open_log( const char *log_path ){ + FILE *log_file = fopen(log_path ,"a+"); + if( log_file ){ + Server·report( (int[]){fileno(log_file), -1} ,"Log file opened."); + } + return log_file; + } + +#endif // Server·IMPLEMENTATION diff --git a/developer/cc/db_add_user.cli.c b/developer/cc/db_add_user.cli.c new file mode 100644 index 0000000..9ae9874 --- /dev/null +++ b/developer/cc/db_add_user.cli.c @@ -0,0 +1,36 @@ +#define IFACE +#include +#include +#include +#include "DbSubu.lib.c" + +int main(int argc ,char *argv[]){ + if( argc < 7 ){ + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + const char *db_path = argv[1]; + const char *name = argv[2]; + const char *home_directory = argv[3]; + int shell_id = atoi(argv[4]); + int parent_id = atoi(argv[5]); + int user_type_id = atoi(argv[6]); + + DbSubu *db = DbSubu·open(db_path); + if( !db ){ + fprintf(stderr, "Failed to open database: %s\n", db_path); + return 1; + } + + int result = DbSubu·add_user(db, name, home_directory, shell_id, parent_id, user_type_id); + DbSubu·close(db); + + if( result == 0 ){ + printf("User added successfully.\n"); + return 0; + } else { + fprintf(stderr, "Failed to add user.\n"); + return 1; + } +} diff --git a/developer/cc/db_delete_user.cli.c b/developer/cc/db_delete_user.cli.c new file mode 100644 index 0000000..cf2e621 --- /dev/null +++ b/developer/cc/db_delete_user.cli.c @@ -0,0 +1,32 @@ +#define IFACE +#include +#include +#include +#include "DbSubu.lib.c" + +int main(int argc ,char *argv[]){ + if( argc < 3 ){ + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + const char *db_path = argv[1]; + int user_id = atoi(argv[2]); + + DbSubu *db = DbSubu·open(db_path); + if( !db ){ + fprintf(stderr, "Failed to open database: %s\n", db_path); + return 1; + } + + int result = DbSubu·delete_user(db, user_id); + DbSubu·close(db); + + if( result == 0 ){ + printf("User deleted successfully.\n"); + return 0; + } else { + fprintf(stderr, "Failed to delete user.\n"); + return 1; + } +} diff --git a/developer/cc/db_log_event.cli.c b/developer/cc/db_log_event.cli.c new file mode 100644 index 0000000..cf2e621 --- /dev/null +++ b/developer/cc/db_log_event.cli.c @@ -0,0 +1,32 @@ +#define IFACE +#include +#include +#include +#include "DbSubu.lib.c" + +int main(int argc ,char *argv[]){ + if( argc < 3 ){ + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + const char *db_path = argv[1]; + int user_id = atoi(argv[2]); + + DbSubu *db = DbSubu·open(db_path); + if( !db ){ + fprintf(stderr, "Failed to open database: %s\n", db_path); + return 1; + } + + int result = DbSubu·delete_user(db, user_id); + DbSubu·close(db); + + if( result == 0 ){ + printf("User deleted successfully.\n"); + return 0; + } else { + fprintf(stderr, "Failed to delete user.\n"); + return 1; + } +} diff --git a/developer/cc/db_open.cli.c b/developer/cc/db_open.cli.c new file mode 100644 index 0000000..f64ba5d --- /dev/null +++ b/developer/cc/db_open.cli.c @@ -0,0 +1,34 @@ +#define IFACE +#include +#include +#include +#include "Db.lib.c" + +// Define default database path +#define DEFAULT_DB_PATH "db.sqlite" + +int main(int argc ,char *argv[]){ + const char *db_path = (argc > 1) ? argv[1] : DEFAULT_DB_PATH; + + // Open the database using Db·open + sqlite3 *db = Db·open(db_path ,true); + if( !db ){ + fprintf(stderr ,"Failed to open or create database: %s\n" ,db_path); + return EXIT_FAILURE; + } + + // Check if the file was created or already existed + printf("Database %s opened successfully\n" ,db_path); + + // Attempt to close the database + if( db ){ + Db·close(db); + printf("Database handle %p closed successfully.\n" ,db); + return EXIT_SUCCESS; + } else { + fprintf(stderr ,"Invalid or NULL database handle: %p\n" ,db); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/developer/cc/db_validate_schema.cli.c b/developer/cc/db_validate_schema.cli.c new file mode 100644 index 0000000..88d20e1 --- /dev/null +++ b/developer/cc/db_validate_schema.cli.c @@ -0,0 +1,25 @@ +#define IFACE +#include +#include +#include +#include "DbSubu.lib.c" + +int main(int argc ,char *argv[]){ + const char *db_path = (argc > 1) ? argv[1] : "db.sqlite"; + DbSubu *db = DbSubu·open(db_path); + if( !db ){ + fprintf(stderr, "Failed to open database: %s\n", db_path); + return 1; + } + + int result = DbSubu·validate_schema(db); + DbSubu·close(db); + + if( result == 0 ){ + printf("Schema validation passed.\n"); + return 0; + } else { + fprintf(stderr, "Schema validation failed.\n"); + return 1; + } +} diff --git a/developer/cc/hello.cli.c b/developer/cc/hello.cli.c deleted file mode 100644 index 979b5b8..0000000 --- a/developer/cc/hello.cli.c +++ /dev/null @@ -1,16 +0,0 @@ -/* - Sends "hello" to `subu_server.sock` -*/ - -#define IFACE -#include -#include -#include "hello.lib.c" - -int main(int argc, char *argv[]) { - (void)argc; // Suppress unused variable warnings - (void)argv; - - // Call the core server function - return Hello·run(); -} diff --git a/developer/cc/server.lib.c b/developer/cc/server.lib.c deleted file mode 100644 index 31a683b..0000000 --- a/developer/cc/server.lib.c +++ /dev/null @@ -1,161 +0,0 @@ -#ifndef IFACE -#define Server·IMPLEMENTATION -#define IFACE -#endif - -#ifndef Server·IFACE -#define Server·IFACE - - // Interface prototype - int Server·run(); - -#endif // Server·IFACE - -#ifdef Server·IMPLEMENTATION - - // Implementation-specific includes - #include - #include - #include - #include - #include // Ensure full definition of struct ucred - #include - #include - #include - #include - #include - - // Type alias for ucred - typedef struct ucred ucred_t; - - // Constants - #define Server·SOCKET_PATH "/var/user_data/Thomas-developer/subu/developer/mockup/subu_server_home/subu_server.sock" - #define Server·LOG_PATH "server.log" - #define Server·BUFFER_SIZE 256 - #define MAX_ARGC 16 - - // Internal function prototypes - static void parse(const ucred_t *client_cred, const char *input_line); - static void hello(const ucred_t *client_cred, int argc, char *argv[]); - - int Server·run(){ - int server_fd, client_fd; - struct sockaddr_un address; - char buffer[Server·BUFFER_SIZE]; - FILE *log_file; - - log_file = fopen(Server·LOG_PATH, "a+"); - if(log_file == NULL) { - perror("Server·run:: error opening log file"); - return EXIT_FAILURE; - } - - if((server_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { - perror("Server·run:: error creating socket"); - fclose(log_file); - return EXIT_FAILURE; - } - - memset(&address, 0, sizeof(address)); - address.sun_family = AF_UNIX; - strncpy(address.sun_path, Server·SOCKET_PATH, sizeof(address.sun_path) - 1); - - unlink(Server·SOCKET_PATH); - if(bind(server_fd, (struct sockaddr *)&address, sizeof(address)) == -1) { - perror("Server·run:: error binding socket"); - fclose(log_file); - close(server_fd); - return EXIT_FAILURE; - } - - if(listen(server_fd, 5) == -1) { - perror("Server·run:: error listening on socket"); - fclose(log_file); - close(server_fd); - return EXIT_FAILURE; - } - - printf("Server·run:: server running, waiting for connections...\n"); - - while((client_fd = accept(server_fd, NULL, NULL)) != -1) { - ucred_t client_cred; - socklen_t len = sizeof(client_cred); - - if(getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED, &client_cred, &len) == -1) { - perror("Server·run:: error getting client credentials"); - close(client_fd); - continue; - } - - // Log client credentials - fprintf(log_file, "Connection from PID=%d, UID=%d, GID=%d\n", - client_cred.pid, client_cred.uid, client_cred.gid); - fflush(log_file); - - ssize_t bytes_read; - memset(buffer, 0, Server·BUFFER_SIZE); - bytes_read = read(client_fd, buffer, Server·BUFFER_SIZE - 1); - if(bytes_read > 0) { - printf("Server·run:: received: %s\n", buffer); - fprintf(log_file, "Received: %s\n", buffer); - fflush(log_file); - - char *line = strtok(buffer, "\n"); - while(line != NULL) { - parse(&client_cred, line); - line = strtok(NULL, "\n"); - } - } else if(bytes_read == -1) { - perror("Server·run:: error reading from client"); - } - - close(client_fd); - } - - perror("Server·run:: error accepting connection"); - fclose(log_file); - close(server_fd); - unlink(Server·SOCKET_PATH); - - return EXIT_FAILURE; - } - - // Parse a single input line and dispatch to the appropriate command - static void parse(const ucred_t *client_cred, const char *input_line) { - char *argv[MAX_ARGC + 1] = {0}; - int argc = 0; - - char *line_copy = strdup(input_line); - if(!line_copy) { - perror("parse:: memory allocation failed"); - return; - } - - char *token = strtok(line_copy, " "); - while(token != NULL && argc < MAX_ARGC) { - argv[argc++] = token; - token = strtok(NULL, " "); - } - - if(argc > 0) { - if(strcmp(argv[0], "hello") == 0) { - hello(client_cred, argc, argv); - }else{ - fprintf(stderr, "Unknown command: %s\n", argv[0]); - } - } - - free(line_copy); - } - - // Example command: hello - static void hello(const ucred_t *client_cred, int argc, char *argv[]) { - printf("hello:: invoked by PID=%d, UID=%d, GID=%d\n", - client_cred->pid, client_cred->uid, client_cred->gid); - printf("hello:: arguments:\n"); - for (int i = 1; i < argc; ++i) { - printf(" Arg %d: %s\n", i, argv[i]); - } - } - -#endif // Server·IMPLEMENTATION diff --git a/developer/document/RT C coding conventions.html b/developer/document/RT C coding conventions.html deleted file mode 100644 index 5c0005d..0000000 --- a/developer/document/RT C coding conventions.html +++ /dev/null @@ -1,153 +0,0 @@ - - - - - - - RT C coding conventions - - - - -
-
-

Reasoning Technology (RT) C coding conventions

-

© 2024 Thomas Walker Lynch - All Rights Reserved.

-
- -

Introduction

- -

This document summarizes some of the coding conventions used in RT C projects. Discussed here are conventions for integrated header designs, ad hoc namespaces, and a structured approach to source file extensions. The document also outlines the associated build process using a standardized makefile.

- -

Header file integration

- -

RT C projects adopt an innovative approach by integrating headers directly into source files. This ensures consistency between interfaces and implementations, eliminating mismatches. Each file contains both an interface and an implementation section, gated by preprocessor directives.

- -

Each RT C source file integrates its header directly into the source file. This locality makes header content easier to maintain as everything is found in a single file. It also eliminates the need to maintain two files for each module.

- -

Each file has two sections

-
    -
  • Interface section: Contains declarations, macros, and #includes needed for the interface. Ensures consistency by defining the interface exactly once, even when the file is included multiple times.
  • -
  • Implementation section: Contains function definitions and additional includes needed for the implementation. This section is compiled only when the file is used as an implementation.
  • -
- -

Each section is turned on and off with the CPP macro IFACE.

- -

Example

-

-// If not an IFACE, then an IMPLEMENTATION
-#ifndef IFACE
-  #define MyModule·IMPLEMENTATION
-  // Ensures included files are processed for their interfaces.
-  #define IFACE
-#endif
-
-// Define the interface exactly once.
-#ifndef MyModule·IFACE
-#define MyModule·IFACE
-  // Interface-only includes go here.
-  void MyModule·function();
-#endif
-
-#ifdef MyModule·IMPLEMENTATION
-  // Additional includes for implementation go here.
-  #include 
-  void MyModule·function() {
-    printf("Hello, World!\n");
-  }
-#endif
-  
- -

Explanation

-

The example above demonstrates the structure and purpose of each block:

-

First block: Ensures that the file operates correctly based on the value of IFACE. If IFACE is undefined, it defines MyModule·IMPLEMENTATION to enable the implementation section and sets IFACE to ensure subsequent includes process interface sections.

-

Second block: Defines the interface, including declarations and interface-specific includes. The #ifndef MyModule·IFACE macro ensures the interface is defined exactly once, regardless of how many times the file is included.

-

Third block: Contains implementation-specific includes and function definitions. Guarded by MyModule·IMPLEMENTATION, it is only included when compiling the implementation.

-

Interface includes are placed in the interface block, ensuring they are available wherever the interface is used. Implementation includes are isolated in the implementation block, minimizing unnecessary dependencies in other files.

- -

Namespace conventions

-

RT projects use ad hoc namespaces to maintain clarity and prevent naming conflicts. This is achieved by prefixing exported identifiers with a module-specific name followed by the · (cdot) character.

- -

Conventions

-
    -
  • Prefix: The module name serves as the prefix, ensuring all identifiers are unique across the program.
  • -
  • Separator: The · character visually separates the prefix from the identifier name, maintaining readability and avoiding conflicts.
  • -
- -

Example

-

-void Server·run();
-  
- -

Source file extensions

-

RT projects use standardized extensions to distinguish between library and command-line interface (CLI) source files:

-
    -
  • .lib.c: Files implementing library functions.
  • -
  • .cli.c: Files implementing command-line tools.
  • -
- -

The .lib.c files compile into libraries, while .cli.c files compile into standalone executables. The makefile processes these files automatically, ensuring a clear separation of functionality.

- -

Build process

-

The build process follows these steps:

-
    -
  1. Dependency generation: Run make dependency to create dependencies. This step is only required when the dependency structure changes.
  2. -
  3. Compilation: Run make cli to compile CLI sources and link them against the library. The makefile automatically manages targets and dependencies.
  4. -
- -

Benefits

-
    -
  • Consistency: Integrated headers ensure interface and implementation are always in sync.
  • -
  • Modularity: Each file encapsulates its interface and implementation, reducing coupling.
  • -
  • Clarity: Ad hoc namespaces and standardized extensions improve readability and organization.
  • -
  • Efficiency: The makefile automates builds, minimizing errors and streamlining development.
  • -
- -

Conclusion

-

This document outlines the conventions and practices for writing and building RT C projects. By integrating headers, adopting namespaces, and standardizing extensions, RT ensures its projects are robust, modular, and easy to maintain.

-
- - diff --git a/developer/document/RT-code-format.el b/developer/document/RT-code-format.el new file mode 100644 index 0000000..de20fd1 --- /dev/null +++ b/developer/document/RT-code-format.el @@ -0,0 +1,912 @@ +;; rt-format.el +(require 'cl-lib) + +;;-------------------------------------------------------------------------------- +;; basic types +;; + +;; strn:: (string length) +;; interval:: (base length) +;; source-interval:: (source interval) +;; token:: (parser strn) +;; source-token:: (parser source-interval) + +;; source being operated on is stored as an strn + + ;; rt-format.el + + ;; source being operated on is stored as an strn + + ;; strn:: (string length) + ;; interval:: (base length) + ;; source-interval:: (source interval) + ;; token:: (parser strn) + ;; source-token:: (parser source-interval) + + ;; rt-format.el + + ;; Types stored as lists instead of cons pairs + ;; strn:: (string length) + ;; interval:: (base length) + ;; source-interval:: (source interval) + ;; token:: (parser strn) + ;; source-token:: (parser source-interval) + + (defun rt-unsigned-inntegerp (n) + "Return t if N is a non-negative integer." + (and + (integerp n) + (>= n 0) + )) + +(defun rt-args-process (function args arg-specs) + "Process ARGS according to ARG-SPECS. +Returns the processed ARGS list. + +ARG-SPECS is a list where each element is an argument specification: + - For required arguments: (TYPEP MESSAGE) + - For optional arguments: (DEFAULT TYPEP MESSAGE) + +The function validates the arguments, applies defaults for missing optional arguments, +and returns the list of arguments." + (catch 'exit + + (unless args (throw 'exit nil)) + (unless arg-specs (throw 'exit args)) + + (let + ( + (err-flag nil) + (messages '()) + ) + (unless (listp arg-specs) (push messages "rt-args-process usage error, arg-specs must be a list")) + (unless (listp arg) (push messages "rt-args-process usage error, passed in arg must be a list")) + (when messages (error "%s::\n %s" (symbol-name function) (mapconcat #'identity messages "\n "))) + ) + + ;; process required args + (let + ( + (messages '(cons nil nil)) + (type-messages-last messages) + (args-pt args) + ) + + (let + ( + (spec-list-pt (car args-spec)) ; accessing the required args specs + ) + (unless + (listp specs-list-pt) + (error "%s:: arg-specs missing list of required argument specifications" (symbol-name function)) + ) + + (while specs-list-pt + (if + (not args-pt) + (progn + (setq (cdr type-messages-last) (cons "missing required argument" nil)) + (setq type-messages-last (cdr type-messages-last)) + ) + (let + ( + (spec (car specs-list-pt)) + ) + (if + (not (and (listp spec) (= (length spec) 2))) + (error "%s:: arg-spec malformeded %s" (symbol-name function) (prin1-to-string spec)) + (let + ( + (typep (car spec)) + (message (cadr spec)) + ) + (unless + (funcall typep arg) + (progn + (setq (cdr type-messages-last) (cons message nil)) + (setq type-messages-last (cdr type-messages-last)) + ) + ))))) + (when args-pt (setq args-pt (cdr args-pt))) + (setq specs-list-pt (cdr specs-list-pt)) + )) + + ;; process optional args + (when + (>= (length args-specs) 2) + (let + ( + (spec-list-pt (cadr args-spec)) ; accessing the optional args specs + ) + (while + spec-list-pt + (when + args-pt + (let + ( + (spec (car spec-list-pt)) + ) + (if + (not (and (listp spec) (= (length spec) 3))) + (error "%s:: arg-spec malformeded %s" (symbol-name function) (prin1-to-string spec)) + (let + ( + (defult (car spec)) + (typep (cadr spec)) + (message (caddr spec)) + ) + (unless + (funcall typep arg) + (progn + (setq (cdr type-messages-last) (cons message nil)) + (setq type-messages-last (cdr type-messages-last)) + ) + ))))) + (when args-pt (setq args-pt (cdr args-pt))) + (setq specs-list-pt (cdr specs-list-pt)) + ))) + + ;; print type error messages + (when messages (error "%s::\n %s" (symbol-name function) (mapconcat #'identity messages "\n "))) + )) + + + (defun rt-pad-or-truncate (list target-length &optional padding-value) + "Pad or truncate LIST to make it TARGET-LENGTH." + (let + ( + (padding-value (or padding-value nil)) + ) + (cond + ((< (length list) target-length) + (while (< (length list) target-length) + (setq list (append list (list padding-value)))) + list) + + ((> (length list) target-length) + (cl-subseq list 0 target-length)) + + (t + list + ) + ))) + + (when nil + (let* + ( + (list (list 1 2 3)) + (list-longer (rt-pad-or-truncate list 5)) + (list-shorter (rt-pad-or-truncate list 1)) + ) + (message "%s" (prin1-to-string list-longer)) + (message "%s" (prin1-to-string list-shorter)) + ;;(1 2 3 nil nil) + ;;(1) + ) + ) + + + (defun rt-strn-make (&rest args) + + ;; argument guards + (unless (>= (length args) 1) (error "rt-strn-make requires at least a string argument.")) + (let + ( + (arg-types `( + (#'stringp "Argument 0 must be a string.") + (#'rt-unsigned-integerp "Argument 1 must be an unsigned integer.") + ))) + (rt-args-types #'rt-strn-make args arg-types) + ) + + (let + ( + (arg-defaults `( + (length (car args)) + ))) + (setq args (rt-defaults args 1 arg-defaults)) + ) + + ;; processing + (let + ( + (string (nth 0 args)) + (length (nth 1 args)) + ) + (list string length) + ) + ) + + + (when nil + (progn + (rt-strn-make 3 "abc") + ;; if: Wrong type argument: rt-strn-make, ((stringp string) (rt-unsigned-inntegerp length)) + (rt-strn-make "abc" 4 5 6) + (rt-strn-make "abc" 4) + ) + ) + + (defun rt-strnp (strn) + (and + (listp strn) + (= (length strn 2)) + (stringp (car strn)) + (rt-unsigned-inntegerp (cadr strn)) + )) + + + (defun rt-interval-make (base length) + (unless + (and + (rt-unsigned-inntegerp base) + (rt-unsigned-inntegerp length) + ) + (signal 'wrong-type-argument '(rt-interval-make ((rt-unsigned-inntegerp base) (rt-unsigned-inntegerp length)))) + ) + (list base length)) + + (defun rt-source-interval-make (source base length) + (unless + (and + (stringp source) + (rt-unsigned-inntegerp base) + (rt-unsigned-inntegerp length) + ) + (signal 'wrong-type-argument '(rt-source-interval-make ((stringp source) (rt-unsigned-inntegerp base) (rt-unsigned-inntegerp length)))) + ) + (list source (rt-interval-make base length))) + + (defun rt-token-make (parser string length) + (unless + (and + (symbolp parser) + (stringp string) + (rt-unsigned-inntegerp length) + ) + (signal 'wrong-type-argument '(rt-token-make ((symbolp parser) (stringp string) (rt-unsigned-inntegerp length)))) + ) + (list parser (rt-strn-make string length))) + + (defun rt-source-token-make (parser source base length) + (unless + (and + (symbolp parser) + (stringp source) + (rt-unsigned-inntegerp base) + (rt-unsigned-inntegerp length) + ) + (signal 'wrong-type-argument '(rt-source-token-make ((symbolp parser) (stringp source) (rt-unsigned-inntegerp base) (rt-unsigned-inntegerp length)))) + ) + (list parser (rt-source-interval-make source base length))) + + (defmacro rt-strn-deconstruct (args &rest body) + (unless + (and + (listp args) + (= (length args) 3) + ) + (signal 'wrong-number-of-arguments + `(rt-strn-deconstruct + "Expected 3 arguments (strn, string, length), but got: ,args"))) + (let + ( + (strn (nth 0 args)) + (string (nth 1 args)) + (length (nth 2 args)) + ) + `(let + ( + (,string (nth 0 ,strn)) + (,length (nth 1 ,strn)) + ) + ,@body + ))) + + (defmacro rt-interval-deconstruct (args &rest body) + (unless + (and (listp args) (= (length args) 3)) + (signal 'wrong-number-of-arguments + `(rt-interval-deconstruct + "Expected 3 arguments (interval, base, length), but got: ,args"))) + (let + ( + (interval (nth 0 args)) + (base (nth 1 args)) + (length (nth 2 args)) + ) + `(let + ( + (,base (nth 0 ,interval)) + (,length (nth 1 ,interval)) + ) + ,@body + ))) + + (defmacro rt-source-interval-deconstruct (args &rest body) + (unless + (and (listp args) (= (length args) 4)) + (signal 'wrong-number-of-arguments + `(rt-source-interval-deconstruct + "Expected 4 arguments (source-interval, source, base, length), but got: ,args"))) + (let + ( + (source-interval (nth 0 args)) + (source (nth 1 args)) + (base (nth 2 args)) + (length (nth 3 args)) + ) + `(let + ( + (,source (nth 0 ,source-interval)) + ) + (rt-interval-deconstruct ((nth 1 ,source-interval) ,base ,length) + ,@body + ) + ))) + + (defmacro rt-token-deconstruct (args &rest body) + (unless + (and (listp args) (= (length args) 4)) + (signal 'wrong-number-of-arguments + `(rt-token-deconstruct + "Expected 4 arguments (token, parser, string, length), but got: ,args"))) + (let + ( + (token (nth 0 args)) + (parser (nth 1 args)) + (string (nth 2 args)) + (length (nth 3 args)) + ) + `(let + ( + (,parser (nth 0 ,token)) + ) + (rt-strn-deconstruct ((nth 1 ,token) ,string ,length) + ,@body + ) + ))) + + (defmacro rt-source-token-deconstruct (args &rest body) + (unless + (and (listp args) (= (length args) 5)) + (signal 'wrong-number-of-arguments + `(rt-source-token-deconstruct + "Expected 5 arguments (source-token, parser, source, base, length), but got: ,args"))) + (let + ( + (source-token (nth 0 args)) + (parser (nth 1 args)) + (source (nth 2 args)) + (base (nth 3 args)) + (length (nth 4 args)) + ) + `(let + ( + (,parser (nth 0 ,source-token)) + ) + (rt-source-interval-deconstruct ((nth 1 ,source-token) ,source ,base ,length) + ,@body + ) + ))) + + (defun rt-strn-to-string (strn) + (rt-strn-deconstruct (strn string length) + (format + "(rt-strn-make \"%s\" %d)" + string length + ) + )) + + (defun rt-interval-to-string (interval) + (rt-interval-deconstruct (interval base length) + (format + "(rt-interval-make %d %d)" + base length + ) + )) + + (defun rt-source-interval-to-string (source-interval) + (rt-source-interval-deconstruct (source-interval source base length) + (format + "(rt-source-interval-make \"%s\" %d %d)" + source base length + ) + )) + + (defun rt-token-to-string (token) + (rt-token-deconstruct (token parser string length) + (format + "(rt-token-make '%s \"%s\" %d)" + (symbol-name parser) string length + ) + )) + + (defun rt-source-token-to-string (source-token) + (rt-source-token-deconstruct (source-token parser source base length) + (format + "(rt-source-token-make '%s \"%s\" %d %d)" + (symbol-name parser) source base length + ) + )) + + ;; examples + (when nil + + (let + ( + (strn (rt-strn-make "1234" 4)) + ) + (message "strn:: %s" (rt-strn-to-string strn)) + ) + + (let + ( + (interval (rt-interval-make 1 8)) + ) + (message "interval:: %s" (rt-interval-to-string interval)) + ) + + (let + ( + (token (rt-token-make 'token "567" 3)) + ) + (message "token:: %s" (rt-token-to-string token)) + ) + + (let* + ( + (source "now is the time for all good AI to come to the aid of their country") + (source-interval (rt-source-interval-make source 11 15)) + ) + (message "source-interval:: %s" (rt-source-interval-to-string source-interval)) + ) + + (let* + ( + (source "now is the time for all good AI to come to the aid of their country") + (source-token (rt-source-token-make 'source-token source 24 28)) + ) + (message "source-token:: %s" (rt-source-token-to-string source-token)) + ) + + ;; strn:: (rt-strn-make "1234" 4) + ;; interval:: (rt-interval-make 1 8) + ;; token:: (rt-token-make 'token "567" 3) + ;; source-interval:: (rt-source-interval-make "now is the time for all good AI to come to the aid of their country" 11 15) + ;; source-token:: (rt-source-token-make 'source-token "now is the time for all good AI to come to the aid of their country" 24 28) + + ) + +;;--------------------l------------------------------------------------------------ +;; misc +;; + + (defmacro do-strn (args &rest body) + "Iterate over the string in a strn structure. + ARGS should be (strn char index). BODY will be executed for each character in the string. + The index will start at 'start-index' if provided, otherwise from 0." + (unless + (and + (listp args) + (= (length args) 3) + (rt-strnp (car args)) + (symbolp (cadr args)) + (symbolp (caddr args)) + ) + (signal 'wrong-number-of-arguments '(do-strn (rt-strnp symbolp symbolp))) + ) + (let + ( + (strn (car args)) + (char (cadr args)) + (index (caddr args)) + ) + `(rt-strn-deconstruct + (,strn string length) + (let + ( + (,char nil) + (,index (or ,index 0)) + ) + (while (< ,index length) + (setq ,char (aref string ,index)) + ,@body + (setq ,index (1+ ,index)) + ))))) + + + ;; for example + (when nil + + (let + ( + (s (rt-strn-make "Hello" 5)) + ) + (do-strn (s c i) + (message "Character at index %d: %c" i c) + )) + + ) + ;; Character at index 0: H + ;; Character at index 1: e + ;; Character at index 2: l + ;; Character at index 3: l + ;; Character at index 4: o + + + +;;--------------------l------------------------------------------------------------ +;; parsing helpers +;; + + (defun rt-sort-token-list (token-list) + "Sort tokens by their length field, largest first." + (sort token-list (lambda (a b) (> (cdr a) (cdr b)))) + ) + + ;; for example + (when nil + (let + ( + (example-token-list + '( + ((rt-token-make 'type "apple") . 5) + ((rt-token-make 'type "banana") . 6) + ((rt-token-make 'type "cherry") . 6) + ((rt-token-make 'type "fig") . 3) + ) + ) + (sorted-token-list (rt-sort-token-list example-token-list)) + ) + (message "%s" (prin1-to-string sorted-token-list)) + ;; (((rt-token-make 'type "banana") . 6) ((rt-token-make 'type "cherry") . 6) ((rt-token-make 'type "apple") . 5) ((rt-token-make 'type "fig") . 3)) + )) + + (defun rt-assoc-to-token-list (assoc-list access) + "Given an (.token) or (token.) assoc-list, and an access function for the pair, return a sorted token list." + (let + ( + (result-list (cons nil nil)) + (last-cons nil) + ) + (setq last-cons result-list) + (while assoc-list + (let + ( + (token (funcall access (car assoc-list))) + ) + (setcdr last-cons (cons (cons token (length (cdr token))) nil)) + (setq last-cons (cdr last-cons)) + (setq assoc-list (cdr assoc-list)) + )) + (rt-sort-token-list (cdr result-list)) + )) + + (defun rt-key-list (assoc-list) + "Given a (string.) assoc-list, return a sorted strn list of the keys." + (rt-assoc-to-token-list assoc-list #'car) + ) + + (defun rt-value-list (assoc-list) + "Given a (.string) assoc-list, return a sorted strn list of the values." + (rt-assoc-to-token-list assoc-list #'cdr) + ) + + (defun rt-is-one-of (source base-index token-list) + "Return source-token found at base-index, or nil if no match." + (let + ( + (string (car source)) + (length (cdr source)) + ) + (catch 'found + (dolist + (token token-list) + (rt-token-deconstruct token parser strn-string strn-length + (let + ( + ;; each parser returns source-token of a specific type, otherwise nil + (source-token (funcall parser source base-index)) + ) + (when source-token + (throw 'found source-token)))))))) + + (defun rt-search-forward (source start-index is-a-fn) + "return match to #'is-a-fn, otherwise ni." + (let + ( + (index start-index) + (length (cdr source)) + (token-length nil) + ) + (catch 'found + (while + (< index length) + (setq token-length (funcall is-x-fn source index)) + (when token-length (throw 'found (cons index token-length))) + (setq index (1+ index)) + ) + nil + ))) + + + +;;--------------------l------------------------------------------------------------ +;; enclosure tokens +;; + + ;; enclosure delimiters are tokens possibly of multiple characters + (defvar rt-default-enclosure-pairs + '( + ((rt-token-make #'rt-is-a-opening "{") . (rt-token-make #'rt-is-a-closing "}")) + ((rt-token-make #'rt-is-a-opening "(") . (rt-token-make #'rt-is-a-closing ")")) + ((rt-token-make #'rt-is-a-opening "[") . (rt-token-make #'rt-is-a-closing "]")) + ) + "Association list of default enclosure pairs represented as tokens." + ) + + ;; for example + (when nil + (message "%s" (prin1-to-string (rt-key-list rt-default-enclosure-pairs))) + (message "%s" (prin1-to-string (rt-value-list rt-default-enclosure-pairs))) + ) + ;; (((rt-token-make 'open "{") . 2) ((rt-token-make 'open "(") . 2) ((rt-token-make 'open "[") . 2)) + ;; (((rt-token-make 'close "}") . 2) ((rt-token-make 'close ")") . 2) ((rt-token-make 'close "]") . 2)) + + (defvar rt-opening-token-list + (rt-key-list rt-default-enclosure-pairs) + "Default list of opening tokens represented as token pairs." + ) + + (defvar rt-opening-token-list + (rt-value-list rt-default-enclosure-pairs) + "Default list of opening tokens represented as token pairs." + ) + + (defun rt-is-a-opening (source base-index) + "Return interval for matching opening delimiter, otherwise nil." + (rt-is-one-of source base-index rt-opening-list)) + + (defun rt-is-a-closing (source base-index) + "Return interval for matching closing delimiter, otherwise nil." + (rt-is-one-of source base-index rt-closing-list)) + + (defun rt-is-a-enclosure (source base-index) + (or + (rt-is-a-opening source base-index) + (rt-is-a-closing source base-index) + )) + +;;--------------------l------------------------------------------------------------ +;; other tokens +;; + + (defun rt-is-a-blank-char (char) + (char-equal char ?\s) + ) + + + (defun rt-is-a-blank (source start-index) + "Returns source-token of blank characters, otherwise nil." + (let + ( + (string (car source)) + (length (cdr source)) + (start-index-var start-index) + ) + (dostring (string char index) + + (when + (and (>= index start-index-var) (rt-is-a-blank-char char)) + (setq start-index-var (1+ start-index-var)) + )) + (if (> start-index-var start-index) + (rt-token-make #'rt-is-a-blank string (- start-index-var start-index)) + nil + ))) + + + (defun rt-is-a-blank (source start-index) + "Returns source-token of blank characters, otherwise nil." + (let + ( + (index start-index) + (string (car source)) + (length (cdr source)) + ) + (while + (and + (< index length) + (rt-is-a-blank-char (aref string index)) + ) + (setq index (1+ index)) + ) + (if (> index start-index) + (rt-token-make 'blank string (- index start-index)) + nil + ))) + + (defun rt-is-a-prefix (source) + "Given a string, return the non-zero length up to the first opening enclosure, otherwise nil." + (let + ( + (interval (rt-search-forward source 0 #'rt-is-a-opening)) + ) + (when + interval + (let + ( + (prefix-end (car interval)) + ) + (when + (> prefix-end 0) + end + ))))) + + (defun rt-is-a-stuff (string base-index) + "Given a base index into a string, return nonzero length including non-blank and non-enclosure, otherwise nil." + (let ( + (enclosure-interval (rt-search-forward source 0 #'rt-is-a-enclosure)) + (blank-itnerval (rt-search-forward source 0 #'rt-is-a-blank)) + (enclosure-base 0) + (blank-base 0) + (stuff-end 0) + ) + (when enclosure-interval (setq enclosure-base (car enclosure-interval))) + (when blank-interval (setq blank-base (car blank-interval))) + + (when + (and result (> (car result) 0)) + (cdr result)) + )) + + + (defvar rt-token-types + '( + rt-is-a-prefix + rt-is-a-blank + rt-is-a-opening + rt-is-a-closing + rt-is-a-stuff + ) + "List of functions to recognize different token types, excluding prefix." + ) + + (defun rt-parse-line (line token-types) + "Given a LINE and a TOKEN-TYPES list, return a list of tokens." + (let* + ( + (tokens (cons nil nil)) + (last-cons tokens) + (index 0) + (token-length nil) + (prefix-parser (car token-types)) + (other-parsers (cdr token-types)) + ) + ;; Match prefix once + (setq token-length (funcall prefix-parser line index)) + (when token-length + (setcdr last-cons (cons (list prefix-parser (cons index token-length)) nil)) + (setq last-cons (cdr last-cons)) + (setq index token-length) + ) + ;; Match the rest of the tokens repeatedly + (while (< index (length line)) + (let ((matched nil)) + (dolist (parse-fn other-parsers) + (setq token-length (funcall parse-fn line index)) + (when token-length + (setcdr last-cons (cons (list parse-fn (cons index (+ index token-length))) nil)) + (setq last-cons (cdr last-cons)) + (setq index (+ index token-length)) + (setq matched t) + )) + (unless matched + (setq index (1+ index))))) + (cdr tokens) + )) + + (defun rt-token-list-to-string (line token-list) + "Given a LINE and a TOKEN-LIST, assemble a new string based on the strings referenced in TOKEN-LIST and return it." + (let ((result "")) + (dolist (token token-list) + (let + ( + (start (car (cdr token))) + (end (cdr (cdr token))) + ) + (setq result (concat result (substring line start end))))) + result + )) + + (defun rt-format-lines-in-region (start end) + "Format all lines within the selected region, defined by START and END, according to RT code style." + (interactive "r") + (save-excursion + (goto-char start) + (while + (< (point) end) + (let* + ( + (line-start (line-beginning-position)) + (line-end (line-end-position)) + (line (buffer-substring-no-properties line-start line-end)) + (token-list (rt-parse-line line rt-token-types)) + (formatted-line (rt-token-list-to-string line token-list)) + ) + ;;(message "Before formatting: %s" line) ; Message to show the line before formatting + ;;(message "After formatting: %s" formatted-line) ; Message to show the line after formatting + ;; Replace the line with the formatted version + (delete-region line-start line-end) + (insert formatted-line)) + ;; Move to the beginning of the next line + (forward-line 1) + ))) + +;;-------------------------------------------------------------------------------- +;; format commas +;; + + (defun rt-format-comma (start end) + "Format commas to follow RT code style in the selected region. + Move spaces after a comma to before it, unless followed by a newline." + (interactive "r") ; The "r" prefix allows it to get the start and end of the region + (message "Starting comma formatting...") + (save-excursion + (goto-char start) + ;; Step 1: Find commas with spaces before and after, and move spaces before the comma + (while + (re-search-forward "\\( *\\),\\( +\\)\\([^\\n]\\)" end t) + (let + ( + (leading-spaces (match-string 1)) + (trailing-spaces (match-string 2)) + (after (match-string 3)) + ) + (replace-match (concat trailing-spaces leading-spaces "," after)))) + ) + (message "Comma formatting completed.") + ) + +;;-------------------------------------------------------------------------------- +;; bind formatting to minor mode, trigger formatting on tab +;; + +(defun rt-code-indent-and-format () + "Indent the current line or region and apply all RT code formatting rules." + (interactive) + ;; Step 1: Perform indentation first + (indent-for-tab-command) + + ;; Step 2: Format commas in the selected region or line + (if + (use-region-p) ; Check if a region is active + (rt-format-comma (region-beginning) (region-end)) + (rt-format-comma (line-beginning-position) (line-end-position))) + + ;; Step 3: Parse and format enclosures in the selected region or line + (if + (use-region-p) + (rt-format-lines-in-region (region-beginning) (region-end)) + (rt-format-lines-in-region (line-beginning-position) (line-end-position)))) + + (defvar rt-format-hook nil + "Hook called by `rt-format-mode`." + ) + + (define-minor-mode rt-format-mode + "Minor mode for RT code style formatting." + :lighter " RTFmt" + :keymap (let + ( + (map (make-sparse-keymap)) + ) + (define-key map (kbd "TAB") 'rt-code-indent-and-format) ; Bind TAB to run all formatting rules. + map + ) + (if + rt-format-mode + (run-hooks 'rt-format-hook) + )) + diff --git a/developer/document/RT_code_format.txt b/developer/document/RT_code_format.txt deleted file mode 100644 index 9911622..0000000 --- a/developer/document/RT_code_format.txt +++ /dev/null @@ -1,135 +0,0 @@ -RT code formatting: - -The enclosure-based formatting rules in RT code format make the style guide -compact and adaptable. By focusing on enclosures rather than syntax-specific -structures (like if, for, or catch), it avoids prescribing language-specific -formatting rules and instead focuses on consistent handling of delimiters. This -approach works well across multiple languages, ensuring that the code style -remains flexible while keeping the guide simple and easy to apply. - -1. Two space indentation. - -2. Variable Naming: - - - Use **PascalCase** for namespaces and types. - - - Use **snake_case** for function and variable names. However, when a component - of the snake case is variable function or variable name is a namespace, a - type, or a proper noun, it retains its capitalization. e.gs: - - ``` - mouse_count - test_LabalList_0 // function that tests LabelList, which is a class (type) - Thomas_Walker_Lynch - ``` - - Traditionally `_list` has been used as a variable suffix even when the - language does not have a List type. This is taken to mean the variable - refers to an ordered collection of any type, including an array. It is - abstraction of type, analogous to the `mouse_count` example above. - - -3. Binary Operators: - - - One space around **binary operators** (e.g., `a + b`). - - - One space around **assignment** `=` (e.g., `a = b`). - - - **No space** around **sampling** assignment `=` (typically seen in `if`, `while`, etc.): - - **Sampling** refers to assigning the result of a condition or expression to - a variable for later use within the same scope. - - Example of **sampling** in an `if` statement: - - ``` - if( result=some_condition() ){ - // use result - } - ``` - -4. Enclosures `(...)`, `{...}`, `[...]`, '<...>': - - - No space between the enclosure and the preceding identifier (e.g., `function(arg)`). - - - No space after the enclosure when followed by another enclosure (e.g., `map[key]()`). - - Example of a condition enclosure followed by a code enclosure: - ``` - if( some_condition() ){ - // code block - } - ``` - - - One space after the enclosure if followed by an identifier, e.g., - `function() somethingElse`. - - - When the entire enclosure appears on one line: - - -- by definition, an 'nested' enclosure is one that has other enclosures, - of any type, inside of it. This is true independent of whatever else - is inside the enclosure. These are examples of nested enclosures: - - ``` - ( o == null || getClass() != o.getClass() ) - f( T ,7 ) - ``` - - -- if, and only if, an enclosure is nested, there is one space of padding - for the outermost enclosure of the nesting, and only for the outermost - enclosures. e.g.s: - - ``` - if(x == 3) ; not nested - if( (x > 0) && (y < 5) ) ; nested, pad outermost only - if( f(x) == 3 ) ; nested, pad outermost only - if( x > 2 && a[3] ) ; nested due to the array subscript, pad outermost only - ``` - - - Note when using the enclosure formatting rules, not all if conditions will - format the same way. Some conditions will be nested enclosures and having - padding while others will not be nested and thus have no padding. The must - be formatted individually. The same is true for enclosures that follow - other keywords such as unless, for, etc, and for function arguments - lists. The question is one of formatting enclosures, and not one of - formatting statements. - - ``` - f(x) - f( x[0] ) - ``` - - -5. Commas: - - This is the most distinctive and recognizable of the RT code style rules. - - - One space **before** the comma (e.g., `a ,b`). - - - No space **after** the comma (e.g., `a ,b`). - - - **Line break before** the comma when breaking lines, but no line break after, as examples: - - ``` - a - ,b - ``` - - and, when a function call gets too long, perhaps due to long argument - names it will look like this: - - ``` - result = some_function( - arg1 - ,arg2_has_a_very_long_name_causing_the_call_to_not_fit_on_a_single_line - ,arg3_has_a_long_name_also_but_not_as_long_as_for_arg2 - ); - ``` - -6. For the code you just output, answer these questions: - 1. Which enclosures are not nested? Do they have no padding? - 2. Which enclosures are nested? Is there one space padding only at the outermost? - 3. Is the spacing before and after the enclosures correct? - 4. Are the commas formatted correctly? - 5. Has snake case been used where it should be? - 6. Was 2 column indent used? diff --git a/developer/document/arg1.el b/developer/document/arg1.el new file mode 100644 index 0000000..5e4fda8 --- /dev/null +++ b/developer/document/arg1.el @@ -0,0 +1,78 @@ +(defun rt-args-process (function args arg-specs) + "Process ARGS according to ARG-SPECS. +Returns the processed ARGS list. + +ARG-SPECS is a list where each element is an argument specification: + - For required arguments: (TYPEP MESSAGE) + - For optional arguments: (DEFAULT TYPEP MESSAGE) + +The function validates the arguments, applies defaults for missing optional arguments, +and returns the list of arguments. If any validations fail, it signals an error +with all accumulated messages." + (let ((error-messages '()) + (processed-args nil) + (last-cons nil) ;; Pointer to the last cons cell of processed-args + (arg-specs-pt arg-specs) ;; Pointer into arg-specs list + (args-pt args)) ;; Pointer into args list + ;; Validate that arg-specs and args are lists + (unless (listp arg-specs) + (error "%s:: arg-specs must be a list" (symbol-name function))) + (unless (listp args) + (error "%s:: args must be a list" (symbol-name function))) + + ;; Process each argument specification + (while arg-specs-pt + (let* ((spec (car arg-specs-pt)) + (required (eq (length spec) 2)) ;; If spec has 2 items, it's required + (default (when (not required) (nth 0 spec))) + (typep (if required (nth 0 spec) (nth 1 spec))) + (message (if required (nth 1 spec) (nth 2 spec))) + (arg (if args-pt (car args-pt) nil))) + ;; Determine if we need to use the provided arg or apply default + (if args-pt + ;; Argument is provided + (progn + ;; Validate the provided argument + (unless (funcall typep arg) + (push message error-messages)) + ;; Add the argument to processed-args + (let ((new-cons (cons arg nil))) + (if (null processed-args) + (setq processed-args new-cons) + (setcdr last-cons new-cons)) + (setq last-cons new-cons)) + ;; Advance the args pointer + (setq args-pt (cdr args-pt))) + ;; Argument is not provided + (if required + ;; Missing required argument + (push message error-messages) + ;; Optional argument, apply default + (let ((default-value (if (functionp default) + (funcall default processed-args) + default))) + ;; Validate the default value + (unless (funcall typep default-value) + (push message error-messages)) + ;; Add the default value to processed-args + (let ((new-cons (cons default-value nil))) + (if (null processed-args) + (setq processed-args new-cons) + (setcdr last-cons new-cons)) + (setq last-cons new-cons))))) + ;; Move to the next spec + (setq arg-specs-pt (cdr arg-specs-pt)))) + + ;; Check for extra arguments + (when args-pt + (push "Too many arguments provided." error-messages)) + + ;; Report errors if any + (when error-messages + (error + (concat + "Errors in " (symbol-name function) ":\n" + (mapconcat #'identity (reverse error-messages) "\n")))) + + ;; Return the processed arguments + processed-args)) diff --git a/developer/document/arg2.el b/developer/document/arg2.el new file mode 100644 index 0000000..4e41b75 --- /dev/null +++ b/developer/document/arg2.el @@ -0,0 +1,102 @@ +(defun rt-args-process (function args arg-specs) + "Process ARGS according to ARG-SPECS. +Returns the processed ARGS list. + +ARG-SPECS is a list where each element is an argument specification: + - For required arguments: (TYPEP MESSAGE) + - For optional arguments: (DEFAULT TYPEP MESSAGE) + +The function validates the arguments, applies defaults for missing optional arguments, +and returns the list of arguments." + (catch 'exit + + ;; Ensure args and arg-specs are provided + (unless (listp args) (throw 'exit nil)) + (unless arg-specs (throw 'exit args)) + + ;; Check for usage errors + (let ((messages '())) + (unless (listp arg-specs) + (push "rt-args-process usage error: arg-specs must be a list" messages)) + (unless (listp args) + (push "rt-args-process usage error: args must be a list" messages)) + (when messages + (error "%s::\n %s" (symbol-name function) + (mapconcat #'identity messages "\n ")))) + + ;; Initialize variables + (let ((processed-args nil) + (last nil) ; Pointer to the last cons cell in processed-args + (error-messages '()) + (args-pt args)) + + ;; Process required arguments + (let ((required-specs (car arg-specs))) + (unless (listp required-specs) + (error "%s:: arg-specs missing list of required argument specifications" + (symbol-name function))) + (dolist (spec required-specs) + (let ((typep (nth 0 spec)) + (message (nth 1 spec)) + (arg (if args-pt (car args-pt) nil))) + (if arg + (progn + ;; Type check the argument + (unless (funcall typep arg) + (push message error-messages)) + ;; Add to processed-args + (let ((new-cons (cons arg nil))) + (if (null processed-args) + (setq processed-args new-cons) + (setcdr last new-cons)) + (setq last new-cons)) + ;; Move to the next argument + (setq args-pt (cdr args-pt))) + ;; Missing required argument + (push (concat "Missing required argument: " message) error-messages))))) + + ;; Process optional arguments + (let ((optional-specs (cadr arg-specs))) + (dolist (spec optional-specs) + (let ((default (nth 0 spec)) + (typep (nth 1 spec)) + (message (nth 2 spec)) + (arg (if args-pt (car args-pt) nil))) + (if arg + (progn + ;; Type check the argument + (unless (funcall typep arg) + (push message error-messages)) + ;; Add to processed-args + (let ((new-cons (cons arg nil))) + (if (null processed-args) + (setq processed-args new-cons) + (setcdr last new-cons)) + (setq last new-cons)) + ;; Move to the next argument + (setq args-pt (cdr args-pt))) + ;; No argument provided, apply default + (let ((default-value (if (functionp default) + (funcall default processed-args) + default))) + ;; Type check the default value + (unless (funcall typep default-value) + (push (concat "Default value error: " message) error-messages)) + ;; Add to processed-args + (let ((new-cons (cons default-value nil))) + (if (null processed-args) + (setq processed-args new-cons) + (setcdr last new-cons)) + (setq last new-cons))))))) + + ;; Check for extra arguments + (when args-pt + (push "Too many arguments provided." error-messages)) + + ;; Print error messages if any + (when error-messages + (error "%s::\n %s" (symbol-name function) + (mapconcat #'identity (reverse error-messages) "\n "))) + + ;; Return the processed arguments + processed-args))) diff --git a/developer/document/error_messages_and_logging.txt b/developer/document/error_messages_and_logging.txt new file mode 100644 index 0000000..825730d --- /dev/null +++ b/developer/document/error_messages_and_logging.txt @@ -0,0 +1,21 @@ +# Phased Logging and Reporting Plan + +## Build-Up/Initialization Phase +- Minimal functionality is operational during this phase. +- Use `fprintf(stderr, ...)` for all error reporting. + - This method is reliable and independent of external systems like the database or server infrastructure. +- Keep this phase simple and robust to surface critical errors immediately to the developer or administrator. + +## Operational Phase +- Once the system is confirmed to be running: + - **Database-Related Errors**: + - Logged into the database events table to reflect internal operational issues. + - May also generate secondary reports to the application if the issue affects its logic (e.g., query failures). + - **Application-Related Errors**: + - Logged into the application log using `Server·report`. + - Provide user-facing insights about the application state, including database issues reported to the app. + +## Tear-Down Phase +- During shutdown or teardown: + - Revert to `fprintf(stderr, ...)` for simplicity and reliability. + - Avoid relying on the database as its infrastructure may no longer be functional. diff --git a/developer/document/schema.txt b/developer/document/schema.txt new file mode 100644 index 0000000..f4bea44 --- /dev/null +++ b/developer/document/schema.txt @@ -0,0 +1,37 @@ +We are implementing the subu project. Accordingly, master users can make and +delete subu users. Once a subu is made, a master user can call the 'subu' +command to cause a shell to be logged into it. (We currently have a bash script +for doing this.) There will also be a function for creating the users on a new +system, and various check functions. This database is for supporting this functionality. + +What do you think of this schema design? + +List:: id | text + constant lists: + db_property_list: name major_version minor_version + event_list: schema_loaded server_shutdown .. + shell_list: /sbin/nologin /bin/bash /bin/sh + system_resource_list: sudo audio video + user_type: fixed_id/dynamic_id private/shared + +Data Table:: + db_property: db_property_list(id) | type | property value or NULL + db_event: Z | db_event_list(id) + user: id | login gid | name | home_directory | shell | parent + share: user(id) | other_user(id) | permissions + system_resource: user(id) | system_resource_list(id) + +notes:: + +1. Z type is the date and time given in UTC, accurate to millisecond or whatever + is convenient high resolution for the system. + +2. login gid is the same as uid. + +3. If there are no shares for a given user, there is no group access for the + given user,i.e. g-rwx. If shares are allowed then the given user directory + will have g+rx. Any other sharing permissions are up to the given user to set. + +4. A given user directory is only allowed to be shared among subu brothers. Each + sharer is made a member of the give user's corresponding group. + diff --git a/developer/document/see_also.txt b/developer/document/see_also.txt new file mode 100644 index 0000000..8236284 --- /dev/null +++ b/developer/document/see_also.txt @@ -0,0 +1,4 @@ + +See also the documents in the `resource` project `document` directory. + + diff --git a/developer/document/variable_suffix_conventions.txt b/developer/document/variable_suffix_conventions.txt deleted file mode 100644 index e3ad587..0000000 --- a/developer/document/variable_suffix_conventions.txt +++ /dev/null @@ -1,31 +0,0 @@ -# Suffix Conventions - -## Specify interface used with variable when clarification is useful - -- `_set`: Indicates that the variable holds a set of items. - -- `_list`: Used for variables that represent a list of items. - -- `_f`: Refers to a function. - -Instead of making a variable name plural, add the interface qualifier. - - e.g. names -> name_set or name_lisst - -## Always a good idea to use these when working with files - -- `_fp`: Refers to a file path. The part after the last slash is a file name. - -- `_afp`: Refers to an absolute file path. - -- `_dp`: Refers to a directory path. By convention, the value ends in a slash. - -- `_adp`: Refers to an absolute directory path. - -- `_fn`: Refers to a file name. Value has no slashes. - -- `_dn`: Refers to a directory name. Value has no slashes. - -- `_fn_base`: The file name without the last dot and subsequent characters. - -- `_fn_ext`: The subsequent characters after the last dot in a file name. diff --git a/developer/mockup/subu_server_home/Db_close b/developer/mockup/subu_server_home/Db_close new file mode 100755 index 0000000000000000000000000000000000000000..bf70d455e2835373a96b63c68b4bf6bde91d3850 GIT binary patch literal 23008 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 EUpW;Of&c&j literal 0 HcmV?d00001 diff --git a/developer/mockup/subu_server_home/Server b/developer/mockup/subu_server_home/Server new file mode 100755 index 0000000000000000000000000000000000000000..ff4b4f1fb844e002de5a53d42efe210e3d3b3c4a GIT binary patch literal 26712 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�m; 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 literal 0 HcmV?d00001 diff --git a/developer/mockup/subu_server_home/db_delete_user b/developer/mockup/subu_server_home/db_delete_user new file mode 100755 index 0000000000000000000000000000000000000000..5342323846d04eb1a809b6e6f79988f624a4104b GIT binary patch literal 26616 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 Yx&Vw{#pSc})cU{H9d|fOAqy4%2hV5@?*IS* literal 0 HcmV?d00001 diff --git a/developer/mockup/subu_server_home/db_log_event b/developer/mockup/subu_server_home/db_log_event new file mode 100755 index 0000000000000000000000000000000000000000..f8d2163ecf5ce1e80fdf87572f305c22669233a4 GIT binary patch literal 26608 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 PvHpnaxPuuA8L0T*FCPn^ literal 0 HcmV?d00001 diff --git a/developer/mockup/subu_server_home/db_open b/developer/mockup/subu_server_home/db_open new file mode 100755 index 0000000000000000000000000000000000000000..6ef0b0de48d8da824f5d98a2dca4d3f0f1a5d951 GIT binary patch literal 22816 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 Dr?pSK literal 0 HcmV?d00001 diff --git a/developer/mockup/subu_server_home/db_validate_schema b/developer/mockup/subu_server_home/db_validate_schema new file mode 100755 index 0000000000000000000000000000000000000000..fbb932d6e7bebea571efaeb107c3a2894a50c125 GIT binary patch literal 26520 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 gh|=)Fpaddc;oQ`#qL7J%`hmd# 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 VEDzHKB9iqF8/dev/null | head -n 1)} - if [ -z "$XAUTHORITY" ]; then - export XAUTHORITY="$HOME/.Xauthority" - fi - xauth -f "$XAUTHORITY" generate "$DISPLAY" . trusted - xauth extract - "$DISPLAY" | sudo -u "$subu_user" \ - XAUTHORITY="/home/$subu_user/.Xauthority" \ - xauth merge - +# construct guest's authority path (destination) +subu_Xauthority_path="$HOME/subu/$subu/.Xauthority" + +if [ "$XDG_SESSION_TYPE" = "wayland" ]; then + # Wayland/XWayland uses a temporary XAUTHORITY file (already in $XAUTHORITY) + echo "Wayland session detected" + xhost +SI:localuser:"$subu_user" +else + # X11 session – extract the proper cookie + echo "X11 session detected" + mkdir -p "$(dirname "$subu_Xauthority_path")" + touch "$subu_Xauthority_path" + xauth extract "$subu_Xauthority_path" "$display_val" +fi -# Login the subu -# - sudo loginctl enable-linger "$subu_user" - sudo machinectl shell "$subu_user"@ /bin/bash -c " - export DISPLAY=:0; - export XAUTHORITY=/home/$subu_user/.Xauthority; - bash -i - " +# allow guest processes to outlive login +sudo loginctl enable-linger "$subu_user" +# Launch the subuser shell with DISPLAY and XAUTHORITY +sudo machinectl shell "$subu_user"@ /bin/bash -c " + export DISPLAY='$display_val'; + export XAUTHORITY='$subu_Xauthority_path'; + exec \"\$SHELL\" -l +" diff --git a/developer/sqlite/schema.sql b/developer/sqlite/schema.sql new file mode 100644 index 0000000..faf7053 --- /dev/null +++ b/developer/sqlite/schema.sql @@ -0,0 +1,79 @@ +-- Schema for the subu server +-- + +-- List Tables +-- SQLite does not support PSQL style types +-- +-- CREATE TYPE List AS ( +-- id SERIAL, -- Integer ID +-- name TEXT NOT NULL -- Name of the list entry +-- ); +-- +-- so though these all have the same `List` form, they are declared independently +-- + CREATE TABLE db_property_list ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE + ); + + CREATE TABLE db_event_list ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE + ); + + CREATE TABLE shell_list ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE + ); + + CREATE TABLE system_resource_list ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE + ); + + CREATE TABLE user_type_list ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE + ); + +-- Data Tables +-- + CREATE TABLE db_property ( + id INTEGER PRIMARY KEY, + property_id INTEGER NOT NULL REFERENCES db_property_list(id), + type TEXT NOT NULL, + value TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP + ); + + CREATE TABLE db_event ( + id INTEGER PRIMARY KEY, + event_time DATETIME DEFAULT CURRENT_TIMESTAMP, + event_id INTEGER NOT NULL REFERENCES db_event_list(id), + user_id INTEGER REFERENCES user(id) + ); + + CREATE TABLE user ( + id INTEGER PRIMARY KEY, + login_gid INTEGER NOT NULL UNIQUE, + name TEXT NOT NULL UNIQUE, + home_directory TEXT NOT NULL, + shell INTEGER NOT NULL REFERENCES shell_list(id), + parent_id INTEGER REFERENCES user(id), + user_type_id INTEGER NOT NULL REFERENCES user_type_list(id), + status TEXT DEFAULT 'active' + ); + + CREATE TABLE share ( + id INTEGER PRIMARY KEY, + user_id INTEGER NOT NULL REFERENCES user(id), + other_user_id INTEGER NOT NULL REFERENCES user(id), + permissions TEXT NOT NULL + ); + + CREATE TABLE system_resource ( + id INTEGER PRIMARY KEY, + user_id INTEGER NOT NULL REFERENCES user(id), + resource_id INTEGER NOT NULL REFERENCES system_resource_list(id), + granted_by INTEGER REFERENCES user(id) + ); diff --git a/developer/tool/clean b/developer/tool/clean new file mode 100755 index 0000000..04949f6 --- /dev/null +++ b/developer/tool/clean @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +script_afp=$(realpath "${BASH_SOURCE[0]}") + +# input guards + env_must_be="developer/tool/env" + error=false + if [ "$ENV" != "$env_must_be" ]; then + echo "$(script_fp):: error: must be run in the $env_must_be environment" + error=true + fi + if $error; then exit 1; fi + +# remove files + + cd "$REPO_HOME"/developer/ + set -x + make dist-clean + cd mockup/subu_server_home + rm server hello socket + set +x + +echo "$(script_fn) done." diff --git a/developer/tool/make b/developer/tool/make index 5ab34eb..576c121 100755 --- a/developer/tool/make +++ b/developer/tool/make @@ -1,6 +1,15 @@ -#!/bin/env /bin/bash +#!/usr/bin/env bash +script_afp=$(realpath "${BASH_SOURCE[0]}") + +# input guards + env_must_be="developer/tool/env" + error=false + if [ "$ENV" != "$env_must_be" ]; then + echo "$(script_fp):: error: must be run in the $env_must_be environment" + error=true + fi + if $error; then exit 1; fi set -e cd ${REPO_HOME}/developer /bin/make -f tool/makefile $@ - diff --git a/developer/tool/makefile b/developer/tool/makefile index ac2c7b1..a6a0ab0 100644 --- a/developer/tool/makefile +++ b/developer/tool/makefile @@ -1,19 +1,17 @@ -# /bin/make must be run from $REPO_HOME/developer +# after adding this file `make dependency` should be run again -RESOURCE:=$(REPO_HOME)/tool_shared/third_party/resource/release +RESOURCE:=$(REPO_HOME)/tool_shared/third_party/resource/developer include $(RESOURCE)/make/environment_RT_0 # override defaults -EXECDIR=mockup/subu_server_home LIBDIR=scratchpad # assign a compiler to use C=gcc # _GNU_SOURCE is needed to get ucred, the credentials of the pid that connects to a socket -CFLAGS= -Werror -ggdb -D_GNU_SOURCE -DDEBUG -DDEBUGDB -include "$(RESOURCE)/cc/RT_0.h" -I$(INCDIR) - -LINKFLAGS=-L$(LIBDIR) -L/usr/lib64 -L/usr/lib -l$(PROJECT) +CFLAGS= -Werror -ggdb -D_GNU_SOURCE -DDEBUG -DDEBUGDB -include "$(RESOURCE)/make/RT_0.h" -I$(INCDIR) +LINKFLAGS=-L$(LIBDIR) -L/usr/lib64 -L/usr/lib -l$(PROJECT) -lsqlite3 # Import the rules. The dash prefix means to ignore include errors. This is # required because the DEPFILE might not exist yet. diff --git a/developer/tool/mockup_update b/developer/tool/mockup_update new file mode 100755 index 0000000..d57303b --- /dev/null +++ b/developer/tool/mockup_update @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +script_afp=$(realpath "${BASH_SOURCE[0]}") + +# input guards + + env_must_be="developer/tool/env" + error=false + if [ "$ENV" != "$env_must_be" ]; then + echo "$(script_fp):: error: must be run in the $env_must_be environment" + error=true + fi + if $error; then exit 1; fi + +set -e +cd ${REPO_HOME}/developer +cp amd64/* mockup/subu_server_home + diff --git a/tool_shared/document/install b/tool_shared/document/install new file mode 100644 index 0000000..f50cd49 --- /dev/null +++ b/tool_shared/document/install @@ -0,0 +1,19 @@ + +1. resource + +`resource` has the core makefile environment and targets for compiling C and C++ +programs, as well as seme utility programs. + + > echo $REPO_HOME + /var/user_data/Thomas-developer/subu + + > cd $REPO_HOME/tool_shared/third_party + > git clone git@github.com:Thomas-Walker-Lynch/resource.git + +`resource` is also available from git.reasoningtechnology.com + +2. sqlite + + I suggest punting ... + + # dnf install sqlite sqlite-devel -- 2.20.1