From 2cb9fbfe059354d06c088e090b6af7238b4fa806 Mon Sep 17 00:00:00 2001 From: "Andrey V.Kosteltsev" Date: Sat, 24 Sep 2022 16:49:18 +0300 Subject: Initial commit --- doc/.inputrc | 100 +++++++++++++++ meson.build | 20 +++ src/Makefile | 28 +++++ src/commands.c | 267 ++++++++++++++++++++++++++++++++++++++++ src/commands.h | 60 +++++++++ src/completion.c | 88 ++++++++++++++ src/completion.h | 18 +++ src/main.c | 365 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.h | 4 + src/utils.c | 52 ++++++++ src/utils.h | 23 ++++ 11 files changed, 1025 insertions(+) create mode 100644 doc/.inputrc create mode 100644 meson.build create mode 100644 src/Makefile create mode 100644 src/commands.c create mode 100644 src/commands.h create mode 100644 src/completion.c create mode 100644 src/completion.h create mode 100644 src/main.c create mode 100644 src/main.h create mode 100644 src/utils.c create mode 100644 src/utils.h diff --git a/doc/.inputrc b/doc/.inputrc new file mode 100644 index 0000000..14cb0bf --- /dev/null +++ b/doc/.inputrc @@ -0,0 +1,100 @@ + +# This file controls the behaviour of line input editing for +# programs that use the GNU Readline library. Existing +# programs include FTP, Bash, and GDB. +# +# You can re-read the inputrc file with C-x C-r. +# Lines beginning with '#' are comments. +# +# First, include any system-wide bindings and variable +# assignments from /etc/Inputrc +$include /etc/Inputrc + +# +# Set various bindings for emacs mode. + +set editing-mode emacs + +$if mode=emacs + +Meta-Control-h: backward-kill-word Text after the function name is ignored + +# +# Arrow keys in keypad mode +# +#"\M-OD": backward-char +#"\M-OC": forward-char +#"\M-OA": previous-history +#"\M-OB": next-history +# +# Arrow keys in ANSI mode +# +"\M-[D": backward-char +"\M-[C": forward-char +"\M-[A": previous-history +"\M-[B": next-history +# +# Arrow keys in 8 bit keypad mode +# +#"\M-\C-OD": backward-char +#"\M-\C-OC": forward-char +#"\M-\C-OA": previous-history +#"\M-\C-OB": next-history +# +# Arrow keys in 8 bit ANSI mode +# +#"\M-\C-[D": backward-char +#"\M-\C-[C": forward-char +#"\M-\C-[A": previous-history +#"\M-\C-[B": next-history + +C-q: quoted-insert + +$endif + +# An old-style binding. This happens to be the default. +TAB: complete + +# Macros that are convenient for shell interaction +$if Bash +# edit the path +"\C-xp": "PATH=${PATH}\e\C-e\C-a\ef\C-f" +# prepare to type a quoted word -- +# insert open and close double quotes +# and move to just after the open quote +"\C-x\"": "\"\"\C-b" +# insert a backslash (testing backslash escapes +# in sequences and macros) +"\C-x\\": "\\" +# Quote the current or previous word +"\C-xq": "\eb\"\ef\"" +# Add a binding to refresh the line, which is unbound +"\C-xr": redraw-current-line +# Edit variable on current line. +"\M-\C-v": "\C-a\C-k$\C-y\M-\C-e\C-a\C-y=" +$endif + +# use a visible bell if one is available +set bell-style visible + +# don't strip characters to 7 bits when reading +set input-meta on + +# allow iso-latin1 characters to be inserted rather +# than converted to prefix-meta sequences +set convert-meta off + +# display characters with the eighth bit set directly +# rather than as meta-prefixed characters +set output-meta on + +# if there are 150 or more possible completions for a word, +# ask whether or not the user wants to see all of them +set completion-query-items 150 + +# For FTP +$if Ftp +"\C-xg": "get \M-?" +"\C-xt": "put \M-?" +"\M-.": yank-last-arg +$endif diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..774a871 --- /dev/null +++ b/meson.build @@ -0,0 +1,20 @@ + +project('sila-shell', 'c') + +incdir = include_directories('src', '/usr/include') + +src = [ + 'src/main.c', + 'src/commands.c', + 'src/completion.c', + 'src/utils.c' +] + +readline = dependency('readline') + +executable('sila-shell', + sources : src, + include_directories: incdir, + dependencies : readline, + install : true, + install_dir : '/bin' ) diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..3cd22f3 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,28 @@ + + +CC = gcc +CFLAGS = -O3 -Wall -I/usr/include -I. -fsanitize=address +DEFS = -D__DEBUG__ + +LDFLAGS = -L/usr/lib64 +LIBS = -lasan -lm -lreadline + + +SOURCES = main.c commands.c completion.c utils.c +OBJECTS = main.o commands.o completion.o utils.o + + +.SUFFIXES: +.SUFFIXES: .o .c +%.o : %.c + $(CC) -g $(CFLAGS) $(DEFS) $(INCLUDIES) -c $< -o $@ + + +all: main + +main: $(OBJECTS) + $(CC) $(LDFLAGS) -o main $(OBJECTS) $(LIBS) + + +clean: + rm -f *.o main diff --git a/src/commands.c b/src/commands.c new file mode 100644 index 0000000..64ea3e3 --- /dev/null +++ b/src/commands.c @@ -0,0 +1,267 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +/* Function which tells you that you can't do this. */ +void too_dangerous( char *caller ) +{ + fprintf( stderr, + "%s: Too dangerous for me to distribute. Write it yourself.\n", + caller ); +} + +/* Return non-zero if ARG is a valid argument for CALLER, else print + an error message and return zero. */ +int valid_argument( char *caller, char *arg ) +{ + if( !arg || !*arg ) + { + fprintf( stderr, "%s: Argument required.\n", caller ); + return( 0 ); + } + + return( 1 ); +} + +/* **************************************************************** */ +/* */ +/* SILA Shell Commands */ +/* */ +/* **************************************************************** */ + +/* String to pass to system (). This is for the LIST, VIEW and RENAME + commands. */ +static char syscom[PATH_MAX]; + +/* List the file(s) named in arg. */ +int com_ls( char *arg ) +{ + if( !arg ) + arg = ""; + + sprintf( syscom, "ls -FClg %s", arg ); + return( system( syscom ) ); +} + +int com_more( char *arg ) +{ + if( !valid_argument( "more", arg ) ) + return( 1 ); + + sprintf (syscom, "more %s", arg); + return( system( syscom ) ); +} + +int com_vi( char *arg ) +{ + if( !valid_argument( "vi", arg ) ) + return( 1 ); + + sprintf (syscom, "vi %s", arg); + return( system( syscom ) ); +} + +int com_rename( char *arg ) +{ + too_dangerous( "rename" ); + return( 1 ); +} + +int com_stat( char *arg ) +{ + struct stat finfo; + + if( !valid_argument( "stat", arg ) ) + return( 1 ); + + if( stat( arg, &finfo ) == -1 ) + { + perror( arg ); + return( 1 ); + } + + printf( "Statistics for `%s':\n", arg ); + + printf( "%s has %ld link%s, and is %ld byte%s in length.\n", arg, + finfo.st_nlink, + (finfo.st_nlink == 1) ? "" : "s", + finfo.st_size, + (finfo.st_size == 1) ? "" : "s"); + printf( "Inode Last Change at: %s", ctime( &finfo.st_ctime ) ); + printf( " Last access at: %s", ctime( &finfo.st_atime ) ); + printf( " Last modified at: %s", ctime( &finfo.st_mtime ) ); + return( 0 ); +} + +int com_delete( char *arg ) +{ + too_dangerous( "delete" ); + return( 1 ); +} + +/* Print out help for ARG, or for all of the commands if ARG is + not present. */ +int com_help( char *arg ) +{ + register int i; + int printed = 0; + + for( i = 0; current->list[i].name; ++i ) + { + if( !*arg || (strcmp( arg, current->list[i].name ) == 0) ) + { + printf( " %-12s\t- %s.\n", current->list[i].name, current->list[i].doc ); + printed++; + } + } + + if( printed ) + printf( "\n" ); + + if( !printed ) + { + printf( "No commands match `%s'. Possibilties are:\n", arg ); + + for( i = 0; current->list[i].name; ++i ) + { + /* Print in six columns. */ + if( printed == 6 ) + { + printed = 0; + printf( "\n" ); + } + + printf( "%s\t", current->list[i].name ); + printed++; + } + + if( printed ) + printf( "\n" ); + } + return( 0 ); +} + +/* Change to the directory ARG. */ +int com_cd( char *arg ) +{ + if( !strcmp( arg, "~" ) ) + { + arg = getenv( "HOME" ); + } + + if( chdir( arg ) == -1 ) + { + perror( arg ); + return( 1 ); + } + + com_pwd( "" ); + return( 0 ); +} + +/* Print out the current working directory. */ +int com_pwd( char *ignore ) +{ + char dir[PATH_MAX], *s; + + s = getcwd( dir, PATH_MAX ); + if( s == 0 ) + { + printf( "Error getting pwd: %s\n", dir ); + return( 1 ); + } + + printf( "Current directory is: %s\n", dir ); + return( 0 ); +} + +int com_ping( char *arg ) +{ + if( !valid_argument( "ping", arg ) ) + return( 1 ); + + sprintf (syscom, "ping %s", arg); + return( system( syscom ) ); +} + +int com_useradd( char *arg ) +{ + if( !valid_argument( "useradd", arg ) ) + return( 1 ); + + sprintf (syscom, "useradd %s", arg); + return( system( syscom ) ); +} + +int com_userdel( char *arg ) +{ + if( !valid_argument( "userdel", arg ) ) + return( 1 ); + + sprintf (syscom, "userdel %s", arg); + return( system( syscom ) ); +} + +int com_userlist( char *arg ) +{ + if( !arg ) arg = ""; + + sprintf (syscom, "cat /etc/passwd | cut -f1 -d':' %s", arg); + return( system( syscom ) ); +} + + +/* The user wishes to quit using this program. Just set DONE non-zero. */ +int com_quit( char *arg ) +{ + done = 1; + return( 0 ); +} + + + +int com_shell( char *arg ) +{ + if( !arg ) arg = ""; + + current = &shell; + + initialize_readline(); + return( 0 ); +} + +int com_users( char *arg ) +{ + if( !arg ) arg = ""; + + current = &users; + + initialize_readline(); + return( 0 ); +} + +int com_top( char *arg ) +{ + if( !arg ) arg = ""; + + current = ⊤ + + initialize_readline(); + return( 0 ); +} diff --git a/src/commands.h b/src/commands.h new file mode 100644 index 0000000..c9d975d --- /dev/null +++ b/src/commands.h @@ -0,0 +1,60 @@ + +#ifndef __COMMANDS_H +#define __COMMANDS_H + + +#ifdef __cplusplus +extern "C" { +#endif + +/* + A structure which contains information on the commands + this program can understand. + */ +typedef struct { + char *name; /* User printable name of the function. */ + rl_icpfunc_t *func; /* Function to call to do the job. */ + char *doc; /* Documentation for this function. */ +} COMMAND; + +typedef struct { + char *name; + COMMAND *list; +} COMMAND_LIST; + + +extern COMMAND_LIST top; +extern COMMAND_LIST shell; +extern COMMAND_LIST users; + +extern COMMAND_LIST *current; + + +extern int com_ls( char *arg ); +extern int com_more( char *arg ); +extern int com_vi( char *arg ); +extern int com_rename( char *arg ); +extern int com_stat( char *arg ); +extern int com_delete( char *arg ); +extern int com_help( char *arg ); +extern int com_cd( char *arg ); +extern int com_ping( char *arg ); +extern int com_pwd( char *ignore ); + +extern int com_useradd( char *arg ); +extern int com_userdel( char *arg ); +extern int com_userlist( char *arg ); + +extern int com_quit( char *arg ); + +extern int com_shell( char *arg ); +extern int com_users( char *arg ); +extern int com_top( char *arg ); + + +#ifdef __cplusplus +} +#endif + +#endif /* __COMMANDS_H */ + diff --git a/src/completion.c b/src/completion.c new file mode 100644 index 0000000..b90f249 --- /dev/null +++ b/src/completion.c @@ -0,0 +1,88 @@ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +/* **************************************************************** */ +/* */ +/* Interface to Readline Completion */ +/* */ +/* **************************************************************** */ + +/* + Generator function for command completion. STATE lets us know whether + to start from scratch; without any state (i.e. STATE == 0), then we + start at the top of the list. + */ +char *command_generator( const char *text, int state ) +{ + static int list_index, len; + char *name; + + /* + If this is a new word to complete, initialize now. This includes + saving the length of TEXT for efficiency, and initializing the index + variable to 0. + */ + if( !state ) + { + list_index = 0; + len = strlen( text ); + } + + /* Return the next name which partially matches from the command list. */ + while( (name = current->list[list_index].name) ) + { + list_index++; + + if( strncmp(name, text, len) == 0 ) + return( dupstr(name) ); + } + + /* If no names matched, then return NULL. */ + return( (char *)NULL ); +} + +/* + Attempt to complete on the contents of TEXT. START and END show the + region of TEXT that contains the word to complete. We can use the + entire line in case we want to do some simple parsing. Return the + array of matches, or NULL if there aren't any. + */ +char **sila_completion( char *text, int start, int end ) +{ + char **matches; + + matches = (char **)NULL; + + /* If this word is at the start of the line, then it is a command + to complete. Otherwise it is the name of a file in the current + directory. */ + if (start == 0) + matches = rl_completion_matches (text, command_generator); + + return (matches); +} + +/* + Tell the GNU Readline library how to complete. We want to try to complete + on command names if this is the first word in the line, or on filenames + if not. + */ +void initialize_readline() +{ + /* Allow conditional parsing of the ~/.inputrc file. */ + rl_readline_name = "sila"; + + /* Tell the completer that we want a crack first. */ + rl_attempted_completion_function = (rl_completion_func_t *)sila_completion; +} + diff --git a/src/completion.h b/src/completion.h new file mode 100644 index 0000000..583ba3b --- /dev/null +++ b/src/completion.h @@ -0,0 +1,18 @@ + +#ifndef __COMPLETION_H +#define __COMPLETION_H + + +#ifdef __cplusplus +extern "C" { +#endif + + +extern void initialize_readline( void ); + + +#ifdef __cplusplus +} +#endif + +#endif /* __COMPLETION_H */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..c950f40 --- /dev/null +++ b/src/main.c @@ -0,0 +1,365 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + + +char *progname; /* The name of this program, as taken from argv[0]. */ +int done; /* When non-zero, this global means the user is done using this program. */ +char pwd[PATH_MAX], home[PATH_MAX]; + +static sigset_t blockmask; + + +COMMAND top_admin_list[] = { + { "help", com_help, "Display this text" }, + { "?", com_help, "Synonym for `help'" }, + { "shell", com_shell, "Activate submenu shell" }, + { "users", com_users, "Activate submenu users" }, + { "quit", com_quit, "Quit using SILA Shell" }, + { (char *)NULL, (rl_icpfunc_t *)NULL, (char *)NULL } +}; + +COMMAND top_operator_list[] = { + { "help", com_help, "Display this text" }, + { "?", com_help, "Synonym for `help'" }, + { "shell", com_shell, "Activate submenu shell" }, + { "users", com_users, "Activate submenu users" }, + { "quit", com_quit, "Quit using SILA Shell" }, + { (char *)NULL, (rl_icpfunc_t *)NULL, (char *)NULL } +}; + +COMMAND top_user_list[] = { + { "help", com_help, "Display this text" }, + { "?", com_help, "Synonym for `help'" }, + { "shell", com_shell, "Activate submenu shell" }, + { "quit", com_quit, "Quit using SILA Shell" }, + { (char *)NULL, (rl_icpfunc_t *)NULL, (char *)NULL } +}; + +COMMAND_LIST top; + + +COMMAND shell_admin_list[] = { + { "help", com_help, "Display this text" }, + { "?", com_help, "Synonym for `help'" }, + { "cd", com_cd, "Change to directory DIR" }, + { "delete", com_delete, "Delete FILE" }, + { "list", com_ls, "List files in DIR" }, + { "ls", com_ls, "Synonym for `list'" }, + { "ping", com_ping, "Ping some host" }, + { "pwd", com_pwd, "Print the current working directory" }, + { "rename", com_rename, "Rename FILE to NEWNAME" }, + { "stat", com_stat, "Print out statistics on FILE" }, + { "more", com_more, "View the contents of FILE" }, + { "vi", com_vi, "Edit the contents of text FILE" }, + { "..", com_top, "Return to top menu" }, + { "quit", com_quit, "Quit using SILA Shell" }, + { (char *)NULL, (rl_icpfunc_t *)NULL, (char *)NULL } +}; + +COMMAND shell_operator_list[] = { + { "help", com_help, "Display this text" }, + { "?", com_help, "Synonym for `help'" }, + { "cd", com_cd, "Change to directory DIR" }, + { "delete", com_delete, "Delete FILE" }, + { "list", com_ls, "List files in DIR" }, + { "ls", com_ls, "Synonym for `list'" }, + { "ping", com_ping, "Ping some host" }, + { "pwd", com_pwd, "Print the current working directory" }, + { "rename", com_rename, "Rename FILE to NEWNAME" }, + { "stat", com_stat, "Print out statistics on FILE" }, + { "more", com_more, "View the contents of FILE" }, + { "vi", com_vi, "Edit the contents of text FILE" }, + { "..", com_top, "Return to top menu" }, + { "quit", com_quit, "Quit using SILA Shell" }, + { (char *)NULL, (rl_icpfunc_t *)NULL, (char *)NULL } +}; + +COMMAND shell_user_list[] = { + { "help", com_help, "Display this text" }, + { "?", com_help, "Synonym for `help'" }, + { "cd", com_cd, "Change to directory DIR" }, + { "delete", com_delete, "Delete FILE" }, + { "list", com_ls, "List files in DIR" }, + { "ls", com_ls, "Synonym for `list'" }, + { "ping", com_ping, "Ping some host" }, + { "pwd", com_pwd, "Print the current working directory" }, + { "rename", com_rename, "Rename FILE to NEWNAME" }, + { "stat", com_stat, "Print out statistics on FILE" }, + { "more", com_more, "View the contents of FILE" }, + { "vi", com_vi, "Edit the contents of text FILE" }, + { "..", com_top, "Return to top menu" }, + { "quit", com_quit, "Quit using SILA Shell" }, + { (char *)NULL, (rl_icpfunc_t *)NULL, (char *)NULL } +}; + +COMMAND_LIST shell; + + +COMMAND users_admin_list[] = { + { "help", com_help, "Display this text" }, + { "?", com_help, "Synonym for `help'" }, + { "useradd", com_useradd, "Register new user" }, + { "userdel", com_userdel, "Delete user" }, + { "..", com_top, "Return to top menu" }, + { "quit", com_quit, "Quit using SILA Shell" }, + { (char *)NULL, (rl_icpfunc_t *)NULL, (char *)NULL } +}; + +COMMAND users_operator_list[] = { + { "help", com_help, "Display this text" }, + { "?", com_help, "Synonym for `help'" }, + { "useradd", com_useradd, "Register new user" }, + { "userdel", com_userdel, "Delete user" }, + { "..", com_top, "Return to top menu" }, + { "quit", com_quit, "Quit using SILA Shell" }, + { (char *)NULL, (rl_icpfunc_t *)NULL, (char *)NULL } +}; + +COMMAND users_user_list[] = { + { "help", com_help, "Display this text" }, + { "?", com_help, "Synonym for `help'" }, + { "list", com_userlist, "List users" }, + { "..", com_top, "Return to top menu" }, + { "quit", com_quit, "Quit using SILA Shell" }, + { (char *)NULL, (rl_icpfunc_t *)NULL, (char *)NULL } +}; + + +COMMAND_LIST users; + +COMMAND_LIST *current; + + +void cmd_lists_init( gid_t gid ) +{ + gid_t admin_gid = 0, operator_gid = 0, user_gid = 0; + struct group *grp = NULL; + + grp = getgrnam( "priv-admin" ); + if( grp != NULL ) + admin_gid = grp->gr_gid; + grp = getgrnam( "priv-operator" ); + if( grp != NULL ) + operator_gid = grp->gr_gid; + grp = getgrnam( "priv-user" ); + if( grp != NULL ) + user_gid = grp->gr_gid; + + top.name = "top"; + shell.name = "shell"; + users.name = "users"; + + if( admin_gid != 0 && (gid == admin_gid || gid == 0) ) + { + top.list = &top_admin_list[0]; + shell.list = &shell_admin_list[0]; + users.list = &users_admin_list[0]; + } + else if( operator_gid != 0 && gid == operator_gid ) + { + top.list = &top_operator_list[0]; + shell.list = &shell_operator_list[0]; + users.list = &users_operator_list[0]; + } + else if( user_gid != 0 && gid == user_gid ) + { + top.list = &top_user_list[0]; + shell.list = &shell_user_list[0]; + users.list = &users_user_list[0]; + } + else + { + top.list = &top_user_list[0]; + shell.list = &shell_user_list[0]; + users.list = &users_user_list[0]; + } + + current = ⊤ +} + +/* + Strip whitespace from the start and end of STRING. + Return a pointer into STRING. + */ +char *stripwhite( char *string ) +{ + register char *s, *t; + + for( s = string; whitespace (*s); ++s ) + ; + + if( *s == 0 ) + return( s ); + + t = s + strlen (s) - 1; + while( t > s && whitespace( *t ) ) + t--; + *++t = '\0'; + + return s; +} + +/* + Look up NAME as the name of a command, and return a pointer to + that command. Return a NULL pointer if NAME isn't a command name. + */ +COMMAND *find_command( char *name ) +{ + register int i; + + for( i = 0; current->list[i].name; i++ ) + if( strcmp( name, current->list[i].name ) == 0 ) + return( ¤t->list[i] ); + + return( (COMMAND *)NULL ); +} + +/* + Execute a command line. + */ +int execute_line( char *line ) +{ + register int i; + COMMAND *command; + char *word; + + /* Isolate the command word. */ + i = 0; + while( line[i] && whitespace( line[i] ) ) + i++; + word = line + i; + + while( line[i] && !whitespace( line[i] ) ) + i++; + + if( line[i] ) + line[i++] = '\0'; + + command = find_command( word ); + + if( !command ) + { + if( strcmp( current->name, "top" ) ) + fprintf( stderr, "%s: No such command for SILA[%s] Shell.\n", word, current->name ); + else + fprintf( stderr, "%s: No such command for SILA Shell.\n", word ); + return( -1 ); + } + + /* Get argument to command, if any. */ + while( whitespace( line[i] ) ) + i++; + + word = line + i; + + /* Call the function. */ + return( (*(command->func))(word) ); +} + +void sigint( int signum ) +{ + if( signum == SIGTERM ) + { + /* free resourses and exit */ + exit( 0 ); + } + else + { + /* Ignore SIGINT */ + return; + } +} + +static void set_signal_handlers() +{ + struct sigaction sa; + sigset_t set; + + memset( &sa, 0, sizeof( sa ) ); + sa.sa_handler = sigint; /* TERM, INT */ + sa.sa_flags = SA_RESTART; + sigemptyset( &set ); + sigaddset( &set, SIGTERM ); + sigaddset( &set, SIGINT ); + sa.sa_mask = set; + sigaction( SIGTERM, &sa, NULL ); + sigaction( SIGINT, &sa, NULL ); + + /* на случай блокировки сигналов с помощью sigprocmask(): */ + sigemptyset( &blockmask ); + sigaddset( &blockmask, SIGTERM ); + sigaddset( &blockmask, SIGINT ); +} + + + +int main( int argc, char **argv ) +{ + char *line, *s, *home, *curdir; + char prompt[PATH_MAX]; + + set_signal_handlers(); + + cmd_lists_init( getgid() ); + + progname = argv[0]; + + initialize_readline(); /* Bind our completer. */ + + /* Loop reading and executing lines until the user quits. */ + for( ; done == 0; ) + { + home = getenv( "HOME" ); + curdir = getcwd( pwd, PATH_MAX ); + + if( home && curdir == strstr( curdir, home ) ) + { + curdir[strlen( home ) - 1] = '~'; + curdir += strlen( home ) - 1; + } + if( strcmp( current->name, "top" ) ) + sprintf( prompt, "%s[%s]:%s$ ", "sila", current->name, curdir ); + else + sprintf( prompt, "%s:%s$ ", "sila", curdir ); + + line = readline( prompt ); + + if( !line ) + { + //continue; /* for non-priviledged users */ + break; /* for admin */ + } + + /* + Remove leading and trailing whitespace from the line. + Then, if there is anything left, add it to the history list + and execute it. + */ + s = stripwhite( line ); + + if( *s ) + { + add_history( s ); + execute_line( s ); + } + + free( line ); + } + exit( 0 ); +} diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..f0e017c --- /dev/null +++ b/src/main.h @@ -0,0 +1,4 @@ + + +extern int done; /* When non-zero, this global means the user is done using this program. */ + diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..05844da --- /dev/null +++ b/src/utils.c @@ -0,0 +1,52 @@ + +#include +#include +#include +#include +#include + +#include + +void no_space( void ) +{ + char buf[MAX_ERROR_MSG_SIZE]; + char *format = "%s: Cannot allocate memory"; + + snprintf( buf, MAX_ERROR_MSG_SIZE, format, "sila" ); + + fprintf( stderr, "%s", buf ); +// FATAL_ERROR( "%s", buf ); + +// ++errors; +// exit( 1 ); +} + +void *xmalloc( size_t n ) +{ + void *p = NULL; + + p = malloc( n ); + if( !p ) no_space(); + bzero( p, n ); + + return( p ); +} + +void *xrealloc( void *b, size_t n ) +{ + void *p = NULL; + + p = realloc( b , n ); + if( !p ) no_space(); + + return( p ); +} + +char *dupstr( char *s ) +{ + char *r; + + r = xmalloc (strlen (s) + 1); + strcpy (r, s); + return (r); +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..e56fe14 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,23 @@ + +#ifndef __UTILS_H +#define __UTILS_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_ERROR_MSG_SIZE PATH_MAX + + +extern void *xmalloc( size_t ); +extern void *xrealloc( void *, size_t ); + +extern char *dupstr( char *s ); + + +#ifdef __cplusplus +} +#endif + +#endif /* __UTILS_H */ -- cgit v1.2.3