summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey V.Kosteltsev <AKosteltsev@IBS.RU>2022-09-24 16:49:18 +0300
committerAndrey V.Kosteltsev <AKosteltsev@IBS.RU>2022-09-24 16:49:18 +0300
commit2cb9fbfe059354d06c088e090b6af7238b4fa806 (patch)
tree7c0e72790e63c4befccb3b250e5a1257f362df4d
downloadsila-shell-2cb9fbfe059354d06c088e090b6af7238b4fa806.tar.xz
Initial commit
-rw-r--r--doc/.inputrc100
-rw-r--r--meson.build20
-rw-r--r--src/Makefile28
-rw-r--r--src/commands.c267
-rw-r--r--src/commands.h60
-rw-r--r--src/completion.c88
-rw-r--r--src/completion.h18
-rw-r--r--src/main.c365
-rw-r--r--src/main.h4
-rw-r--r--src/utils.c52
-rw-r--r--src/utils.h23
11 files changed, 1025 insertions, 0 deletions
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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <limits.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include <main.h>
+#include <commands.h>
+#include <completion.h>
+#include <utils.h>
+
+/* 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 = &top;
+
+ 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 <stdio.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include <utils.h>
+#include <commands.h>
+
+/* **************************************************************** */
+/* */
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <limits.h>
+#include <unistd.h>
+#include <signal.h>
+#include <grp.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include <utils.h>
+#include <completion.h>
+#include <commands.h>
+
+
+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 = &top;
+}
+
+/*
+ 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( &current->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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include <utils.h>
+
+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 */