-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-<subuser>
+```
+
+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 <subuser>
+```
+
+Where `<subuser>` is the symbolic sub-identity (e.g., `incommon`, `dev`, `tester`).
+
+---
+
+## Behavior
+
+- Launches a shell as user `Thomas-<subuser>`
+- Detects whether the current session is under **Wayland** or **X11**:
+ - Under Wayland: uses `xhost +SI:localuser:Thomas-<subuser>`
+ - Under X11: extracts the appropriate `.Xauthority` cookie to `$HOME/subu/<subuser>/.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 `<subuser>` must have a corresponding home directory:
+ ```
+ /home/Thomas/subu_data/<subuser>
+ ```
+- 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.
+
+---
--- /dev/null
+#ifndef IFACE
+#define Db·IMPLEMENTATION
+#define IFACE
+#endif
+
+#ifndef Db·IFACE
+#define Db·IFACE
+
+ #include <sqlite3.h>
+ #include <stdbool.h>
+
+ // 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 <sqlite3.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <errno.h>
+ #include <time.h>
+ #include <string.h>
+
+ 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
+
+
--- /dev/null
+#ifndef IFACE
+#define DbSubu·IMPLEMENTATION
+#define IFACE
+#endif
+
+#ifndef DbSubu·IFACE
+#define DbSubu·IFACE
+
+ #include <sqlite3.h>
+
+ 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 <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #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
--- /dev/null
+#define IFACE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include "Db.lib.c"
+
+int main(int argc ,char *argv[]){
+ if( argc < 2 ){
+ fprintf(stderr ,"Usage: %s <sqlite_handle_as_hex>\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;
+ }
+}
--- /dev/null
+
+/*
+ The subu server command line interface.
+
+ Usage:
+ server [-s <socket_path>] [-l <log_path>] [arguments...]
+
+ Options:
+ -s <socket_path> Specify the Unix socket file path. Default: ./socket
+ -l <log_path> Specify the log file path. Default: ./log.txt
+*/
+
+#define IFACE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "Hello.lib.c"
+
+// Define defaults
+#define DEFAULT_SOCKET_PATH "socket"
+
+int main( int argc ,char **argv ){
+ char *socket_path = DEFAULT_SOCKET_PATH;
+ int error_flag = 0;
+
+ int opt;
+ while( (opt = getopt(argc ,argv ,":s:l:")) != -1 ){
+ switch( opt ){
+ case 's':
+ socket_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 <socket_path>] [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);
+ }
+
+ // Log parsed options
+ printf( "%s::main socket_path='%s'\n" ,argv[0] ,socket_path );
+
+ // Call the hello function
+ return Hello·run(argc ,argv ,socket_path);
+}
--- /dev/null
+#ifndef IFACE
+#define Hello·IMPLEMENTATION
+#define IFACE
+#endif
+
+#ifndef Hello·IFACE
+#define Hello·IFACE
+
+ // Necessary interface includes
+ // .. none
+
+ // Interface prototypes
+ int Hello·run(int argc ,char** argv ,char *socket_path);
+
+#endif // Hello·IFACE
+
+#ifdef Hello·IMPLEMENTATION
+
+ // Implementation-specific includes
+ #include <sys/socket.h>
+ #include <sys/un.h>
+ #include <stddef.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <unistd.h>
+ #include <errno.h>
+
+ // Constants
+ #define Hello·SOCKET_PATH "/var/user_data/Thomas-developer/subu/developer/mockup/subu_server_home/subu_server.sock"
+ #define Hello·LOG_PATH "server_test.log"
+ #define Hello·BUFFER_SIZE 256
+
+ 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];
+
+ client_fd = socket(AF_UNIX ,SOCK_STREAM ,0);
+ if( client_fd == -1 ){
+ perror("Hello·run:: error opening socket");
+ return EXIT_FAILURE;
+ }
+
+ // Configure server socket address
+ memset(&address ,0 ,sizeof(address));
+ address.sun_family = AF_UNIX;
+ 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");
+ close(client_fd);
+ return EXIT_FAILURE;
+ }
+
+ // Send message to the server
+ char *out_buf = "hello\n";
+ if( write(client_fd ,out_buf ,strlen(out_buf)) == -1 ){
+ perror("Hello·run:: error writing to server");
+ return EXIT_FAILURE;
+ }
+
+ printf("Hello·run:: sent \"%s\"\n" ,out_buf);
+
+ // Clean up
+ close(client_fd);
+
+ return EXIT_SUCCESS;
+ }
+
+#endif // Hello·IMPLEMENTATION
--- /dev/null
+/*
+ The subu server command line interface.
+
+ Usage:
+ server [-s <socket_path>] [-l <log_path>] [arguments...]
+
+ Options:
+ -s <socket_path> Specify the Unix socket file path. Default: ./socket
+ -l <log_path> Specify the log file path. Default: ./log.txt
+*/
+
+#define IFACE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#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 <socket_path>] [-l <log_path>] [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;
+}
--- /dev/null
+#ifndef IFACE
+#define Server·IMPLEMENTATION
+#define IFACE
+#endif
+
+#ifndef Server·IFACE
+#define Server·IFACE
+
+ #include <stdio.h>
+ #include <time.h>
+
+ // 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 <sys/socket.h>
+ #include <sys/un.h>
+ #include <stddef.h>
+ #include <sys/types.h>
+ #include <bits/socket.h> // Ensure full definition of struct ucred
+ #include <stdlib.h>
+ #include <string.h>
+ #include <unistd.h>
+ #include <errno.h>
+
+ // 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
--- /dev/null
+#define IFACE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "DbSubu.lib.c"
+
+int main(int argc ,char *argv[]){
+ if( argc < 7 ){
+ fprintf(stderr, "Usage: %s <db_path> <name> <home_directory> <shell_id> <parent_id> <user_type_id>\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;
+ }
+}
--- /dev/null
+#define IFACE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "DbSubu.lib.c"
+
+int main(int argc ,char *argv[]){
+ if( argc < 3 ){
+ fprintf(stderr, "Usage: %s <db_path> <user_id>\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;
+ }
+}
--- /dev/null
+#define IFACE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "DbSubu.lib.c"
+
+int main(int argc ,char *argv[]){
+ if( argc < 3 ){
+ fprintf(stderr, "Usage: %s <db_path> <user_id>\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;
+ }
+}
--- /dev/null
+#define IFACE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#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;
+}
--- /dev/null
+#define IFACE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#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;
+ }
+}
+++ /dev/null
-/*
- Sends "hello" to `subu_server.sock`
-*/
-
-#define IFACE
-#include <stdio.h>
-#include <stdlib.h>
-#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();
-}
+++ /dev/null
-#ifndef IFACE
-#define Hello·IMPLEMENTATION
-#define IFACE
-#endif
-
-#ifndef Hello·IFACE
-#define Hello·IFACE
-
- // Necessary interface includes
- #include <sys/socket.h>
- #include <sys/un.h>
- #include <stddef.h>
-
- // Interface prototypes
- int Hello·run();
-
-#endif // Hello·IFACE
-
-#ifdef Hello·IMPLEMENTATION
-
- // Implementation-specific includes
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <errno.h>
-
- // Constants
- #define Hello·SOCKET_PATH "/var/user_data/Thomas-developer/subu/developer/mockup/subu_server_home/subu_server.sock"
- #define Hello·LOG_PATH "server_test.log"
- #define Hello·BUFFER_SIZE 256
-
- int Hello·run(){
- 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);
-
- // 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;
- }
-
- // Send message to the server
- 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;
- }
-
-#endif // Hello·IMPLEMENTATION
+++ /dev/null
-/*
- The subu server command line interface.
-
- Usage:
- server [-s <socket_path>] [-l <log_path>] [arguments...]
-
- Options:
- -s <socket_path> Specify the Unix socket file path. Default: ./socket
- -l <log_path> Specify the log file path. Default: ./log.txt
-*/
-
-#define IFACE
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#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; // Flag to track errors
-
- // Parse 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 ,"Error: Unknown option '-%c'\n" ,optopt );
- error_flag = 1;
- break;
- case ':': // Missing argument
- fprintf( stderr ,"Error: Missing argument for '-%c'\n" ,optopt );
- error_flag = 1;
- break;
- }
- }
-
- // Check for too many arguments
- if( optind > argc - 1 ){
- fprintf( stderr ,"Error: Too many arguments provided.\n" );
- error_flag = 1;
- }
-
- // Exit on error after processing all options
- if( error_flag ){
- fprintf( stderr ,"Usage: %s [-s <socket_path>] [-l <log_path>] [arguments...]\n" ,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);
-
- // Log parsed options
- printf( "Socket Path: %s\n" ,socket_path );
- printf( "Log Path: %s\n" ,log_path );
-
- // Call the core server function with the rebased arguments
- return Server·run(argv ,argc ,socket_path ,log_path);
-}
+++ /dev/null
-#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 <sys/socket.h>
- #include <sys/un.h>
- #include <stddef.h>
- #include <sys/types.h>
- #include <bits/socket.h> // Ensure full definition of struct ucred
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <errno.h>
-
- // 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
+++ /dev/null
-<!DOCTYPE html>
-<html lang="en">
-<head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&display=swap" rel="stylesheet">
- <title>RT C coding conventions</title>
- <style>
- body {
- font-family: 'Noto Sans JP', Arial, sans-serif;
- background-color: hsl(0, 0%, 0%);
- color: hsl(42, 100%, 80%);
- padding: 2rem;
- }
- .page {
- padding: 3rem;
- margin: 1.25rem auto;
- max-width: 46.875rem;
- background-color: hsl(0, 0%, 0%);
- box-shadow: 0 0 0.625rem hsl(42, 100%, 50%);
- }
- h1 {
- font-size: 1.5rem;
- text-align: center;
- color: hsl(42, 100%, 84%);
- text-transform: uppercase;
- margin-top: 1.5rem;
- }
- h2 {
- font-size: 1.25rem;
- color: hsl(42, 100%, 84%);
- text-align: center;
- margin-top: 2rem;
- }
- h3 {
- font-size: 1.125rem;
- color: hsl(42, 100%, 75%);
- margin-top: 1.5rem;
- }
- p, li {
- color: hsl(42, 100%, 90%);
- text-align: justify;
- margin-bottom: 1rem;
- }
- code {
- font-family: 'Courier New', Courier, monospace;
- background-color: hsl(0, 0%, 25%);
- padding: 0.125rem 0.25rem;
- color: hsl(42, 100%, 90%);
- }
- </style>
-</head>
-
-<body>
-<div class="page">
- <header>
- <h1>Reasoning Technology (RT) C coding conventions</h1>
- <p>© 2024 Thomas Walker Lynch - All Rights Reserved.</p>
- </header>
-
- <h2>Introduction</h2>
-
- <p>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.</p>
-
- <h2>Header file integration</h2>
-
- <p>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.</p>
-
- <p>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.</p>
-
- <h3>Each file has two sections</h3>
- <ul>
- <li><strong>Interface section:</strong> Contains declarations, macros, and <code>#includes</code> needed for the interface. Ensures consistency by defining the interface exactly once, even when the file is included multiple times.</li>
- <li><strong>Implementation section:</strong> Contains function definitions and additional includes needed for the implementation. This section is compiled only when the file is used as an implementation.</li>
- </ul>
-
- <p>Each section is turned on and off with the CPP macro <code>IFACE</code>.</p>
-
- <h3>Example</h3>
- <pre><code>
-// 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 <stdio.h>
- void MyModule·function() {
- printf("Hello, World!\n");
- }
-#endif
- </code></pre>
-
- <h3>Explanation</h3>
- <p>The example above demonstrates the structure and purpose of each block:</p>
- <p><strong>First block:</strong> Ensures that the file operates correctly based on the value of <code>IFACE</code>. If <code>IFACE</code> is undefined, it defines <code>MyModule·IMPLEMENTATION</code> to enable the implementation section and sets <code>IFACE</code> to ensure subsequent includes process interface sections.</p>
- <p><strong>Second block:</strong> Defines the interface, including declarations and interface-specific includes. The <code>#ifndef MyModule·IFACE</code> macro ensures the interface is defined exactly once, regardless of how many times the file is included.</p>
- <p><strong>Third block:</strong> Contains implementation-specific includes and function definitions. Guarded by <code>MyModule·IMPLEMENTATION</code>, it is only included when compiling the implementation.</p>
- <p>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.</p>
-
- <h2>Namespace conventions</h2>
- <p>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 <code>·</code> (cdot) character.</p>
-
- <h3>Conventions</h3>
- <ul>
- <li><strong>Prefix:</strong> The module name serves as the prefix, ensuring all identifiers are unique across the program.</li>
- <li><strong>Separator:</strong> The <code>·</code> character visually separates the prefix from the identifier name, maintaining readability and avoiding conflicts.</li>
- </ul>
-
- <h3>Example</h3>
- <pre><code>
-void Server·run();
- </code></pre>
-
- <h2>Source file extensions</h2>
- <p>RT projects use standardized extensions to distinguish between library and command-line interface (CLI) source files:</p>
- <ul>
- <li><strong><code>.lib.c</code>:</strong> Files implementing library functions.</li>
- <li><strong><code>.cli.c</code>:</strong> Files implementing command-line tools.</li>
- </ul>
-
- <p>The <code>.lib.c</code> files compile into libraries, while <code>.cli.c</code> files compile into standalone executables. The makefile processes these files automatically, ensuring a clear separation of functionality.</p>
-
- <h3>Build process</h3>
- <p>The build process follows these steps:</p>
- <ol>
- <li><strong>Dependency generation:</strong> Run <code>make dependency</code> to create dependencies. This step is only required when the dependency structure changes.</li>
- <li><strong>Compilation:</strong> Run <code>make cli</code> to compile CLI sources and link them against the library. The makefile automatically manages targets and dependencies.</li>
- </ol>
-
- <h2>Benefits</h2>
- <ul>
- <li><strong>Consistency:</strong> Integrated headers ensure interface and implementation are always in sync.</li>
- <li><strong>Modularity:</strong> Each file encapsulates its interface and implementation, reducing coupling.</li>
- <li><strong>Clarity:</strong> Ad hoc namespaces and standardized extensions improve readability and organization.</li>
- <li><strong>Efficiency:</strong> The makefile automates builds, minimizing errors and streamlining development.</li>
- </ul>
-
- <h2>Conclusion</h2>
- <p>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.</p>
-</div>
-</body>
-</html>
--- /dev/null
+;; 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 (<X>.token) or (token.<X>) 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.<X>) 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 (<X>.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)
+ ))
+
+++ /dev/null
-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<x> ,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?
--- /dev/null
+(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))
--- /dev/null
+(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)))
--- /dev/null
+# 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.
--- /dev/null
+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 .. <various possible error messages>
+ 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.
+
--- /dev/null
+
+See also the documents in the `resource` project `document` directory.
+
+
+++ /dev/null
-# 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.
+++ /dev/null
-Received: hello
-
-Received: hello
-
-Connection from PID=7196, UID=2005, GID=2005
-Received: hello
-
+++ /dev/null
-
-1. automatic create server log if it does not exist.
-2. option to pass in the home directory
-3. command processing of course
-4. proper permissions, group member can rx home directory, and w the socket. Can not do
-anything to other files.
-
-
-
-
--- /dev/null
+
+1. automatic create server log if it does not exist.
+2. option to pass in the home directory
+3. command processing of course
+4. proper permissions, group member can rx home directory, and w the socket. Can not do
+anything to other files.
+
+
+
+
-#!/bin/env /bin/bash
-# set -x
+#!/bin/bash
+set -x
subu="$1"
if [ -z "$subu" ]; then
echo "No subuser name supplied"
exit 1
fi
+
+# actual subu user name and subu home directory
subu_user="Thomas-$subu"
+subu_dir="/home/Thomas/subu_data/$subu"
+
+# detect current display and authority
+display_val="${DISPLAY:-:0}"
+authority_val="${XAUTHORITY:-$HOME/.Xauthority}"
-# share the X display
-#
- export DISPLAY=:0
- export XAUTHORITY=${XAUTHORITY:-$(ls -1 /run/user/$(id -u)/.mutter-Xwaylandauth.* 2>/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
+"
--- /dev/null
+-- 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)
+ );
--- /dev/null
+#!/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."
-#!/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 $@
-
-# /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.
--- /dev/null
+#!/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
+
--- /dev/null
+
+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