-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7d38f8d
commit 3c5a87e
Showing
1 changed file
with
283 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,283 @@ | ||
/***************************************************************************//** | ||
@file main.c | ||
@author Stephen Brennan | ||
@date Thursday, 8 January 2015 | ||
@brief LSH (Libstephen SHell) | ||
*******************************************************************************/ | ||
|
||
#include <sys/wait.h> | ||
#include <sys/types.h> | ||
#include <unistd.h> | ||
#include <stdlib.h> | ||
#include <stdio.h> | ||
#include <string.h> | ||
|
||
/* | ||
Function Declarations for builtin shell commands: | ||
*/ | ||
int lsh_cd(char **args); | ||
int lsh_help(char **args); | ||
int lsh_exit(char **args); | ||
|
||
/* | ||
List of builtin commands, followed by their corresponding functions. | ||
*/ | ||
char *builtin_str[] = { | ||
"cd", | ||
"help", | ||
"exit" | ||
}; | ||
|
||
int (*builtin_func[]) (char **) = { | ||
&lsh_cd, | ||
&lsh_help, | ||
&lsh_exit | ||
}; | ||
|
||
int lsh_num_builtins() { | ||
return sizeof(builtin_str) / sizeof(char *); | ||
} | ||
|
||
/* | ||
Builtin function implementations. | ||
*/ | ||
|
||
/** | ||
@brief Builtin command: change directory. | ||
@param args List of args. args[0] is "cd". args[1] is the directory. | ||
@return Always returns 1, to continue executing. | ||
*/ | ||
int lsh_cd(char **args) | ||
{ | ||
if (args[1] == NULL) { | ||
fprintf(stderr, "lsh: expected argument to \"cd\"\n"); | ||
} else { | ||
if (chdir(args[1]) != 0) { | ||
perror("lsh"); | ||
} | ||
} | ||
return 1; | ||
} | ||
|
||
/** | ||
@brief Builtin command: print help. | ||
@param args List of args. Not examined. | ||
@return Always returns 1, to continue executing. | ||
*/ | ||
int lsh_help(char **args) | ||
{ | ||
int i; | ||
printf("Stephen Brennan's LSH\n"); | ||
printf("Type program names and arguments, and hit enter.\n"); | ||
printf("The following are built in:\n"); | ||
|
||
for (i = 0; i < lsh_num_builtins(); i++) { | ||
printf(" %s\n", builtin_str[i]); | ||
} | ||
|
||
printf("Use the man command for information on other programs.\n"); | ||
return 1; | ||
} | ||
|
||
/** | ||
@brief Builtin command: exit. | ||
@param args List of args. Not examined. | ||
@return Always returns 0, to terminate execution. | ||
*/ | ||
int lsh_exit(char **args) | ||
{ | ||
return 0; | ||
} | ||
|
||
/** | ||
@brief Launch a program and wait for it to terminate. | ||
@param args Null terminated list of arguments (including program). | ||
@return Always returns 1, to continue execution. | ||
*/ | ||
int lsh_launch(char **args) | ||
{ | ||
pid_t pid; | ||
int status; | ||
|
||
pid = fork(); | ||
if (pid == 0) { | ||
// Child process | ||
if (execvp(args[0], args) == -1) { | ||
perror("lsh"); | ||
} | ||
exit(EXIT_FAILURE); | ||
} else if (pid < 0) { | ||
// Error forking | ||
perror("lsh"); | ||
} else { | ||
// Parent process | ||
do { | ||
waitpid(pid, &status, WUNTRACED); | ||
} while (!WIFEXITED(status) && !WIFSIGNALED(status)); | ||
} | ||
|
||
return 1; | ||
} | ||
|
||
/** | ||
@brief Execute shell built-in or launch program. | ||
@param args Null terminated list of arguments. | ||
@return 1 if the shell should continue running, 0 if it should terminate | ||
*/ | ||
int lsh_execute(char **args) | ||
{ | ||
int i; | ||
|
||
if (args[0] == NULL) { | ||
// An empty command was entered. | ||
return 1; | ||
} | ||
|
||
for (i = 0; i < lsh_num_builtins(); i++) { | ||
if (strcmp(args[0], builtin_str[i]) == 0) { | ||
return (*builtin_func[i])(args); | ||
} | ||
} | ||
|
||
return lsh_launch(args); | ||
} | ||
|
||
/** | ||
@brief Read a line of input from stdin. | ||
@return The line from stdin. | ||
*/ | ||
char *lsh_read_line(void) | ||
{ | ||
#ifdef LSH_USE_STD_GETLINE | ||
char *line = NULL; | ||
ssize_t bufsize = 0; // have getline allocate a buffer for us | ||
if (getline(&line, &bufsize, stdin) == -1) { | ||
if (feof(stdin)) { | ||
exit(EXIT_SUCCESS); // We received an EOF | ||
} else { | ||
perror("lsh: getline\n"); | ||
exit(EXIT_FAILURE); | ||
} | ||
} | ||
return line; | ||
#else | ||
#define LSH_RL_BUFSIZE 1024 | ||
int bufsize = LSH_RL_BUFSIZE; | ||
int position = 0; | ||
char *buffer = malloc(sizeof(char) * bufsize); | ||
int c; | ||
|
||
if (!buffer) { | ||
fprintf(stderr, "lsh: allocation error\n"); | ||
exit(EXIT_FAILURE); | ||
} | ||
|
||
while (1) { | ||
// Read a character | ||
c = getchar(); | ||
|
||
if (c == EOF) { | ||
exit(EXIT_SUCCESS); | ||
} else if (c == '\n') { | ||
buffer[position] = '\0'; | ||
return buffer; | ||
} else { | ||
buffer[position] = c; | ||
} | ||
position++; | ||
|
||
// If we have exceeded the buffer, reallocate. | ||
if (position >= bufsize) { | ||
bufsize += LSH_RL_BUFSIZE; | ||
buffer = realloc(buffer, bufsize); | ||
if (!buffer) { | ||
fprintf(stderr, "lsh: allocation error\n"); | ||
exit(EXIT_FAILURE); | ||
} | ||
} | ||
} | ||
#endif | ||
} | ||
|
||
#define LSH_TOK_BUFSIZE 64 | ||
#define LSH_TOK_DELIM " \t\r\n\a" | ||
/** | ||
@brief Split a line into tokens (very naively). | ||
@param line The line. | ||
@return Null-terminated array of tokens. | ||
*/ | ||
char **lsh_split_line(char *line) | ||
{ | ||
int bufsize = LSH_TOK_BUFSIZE, position = 0; | ||
char **tokens = malloc(bufsize * sizeof(char*)); | ||
char *token, **tokens_backup; | ||
|
||
if (!tokens) { | ||
fprintf(stderr, "lsh: allocation error\n"); | ||
exit(EXIT_FAILURE); | ||
} | ||
|
||
token = strtok(line, LSH_TOK_DELIM); | ||
while (token != NULL) { | ||
tokens[position] = token; | ||
position++; | ||
|
||
if (position >= bufsize) { | ||
bufsize += LSH_TOK_BUFSIZE; | ||
tokens_backup = tokens; | ||
tokens = realloc(tokens, bufsize * sizeof(char*)); | ||
if (!tokens) { | ||
free(tokens_backup); | ||
fprintf(stderr, "lsh: allocation error\n"); | ||
exit(EXIT_FAILURE); | ||
} | ||
} | ||
|
||
token = strtok(NULL, LSH_TOK_DELIM); | ||
} | ||
tokens[position] = NULL; | ||
return tokens; | ||
} | ||
|
||
/** | ||
@brief Loop getting input and executing it. | ||
*/ | ||
void lsh_loop(void) | ||
{ | ||
char *line; | ||
char **args; | ||
int status; | ||
|
||
do { | ||
printf("> "); | ||
line = lsh_read_line(); | ||
args = lsh_split_line(line); | ||
status = lsh_execute(args); | ||
|
||
free(line); | ||
free(args); | ||
} while (status); | ||
}s | ||
|
||
/** | ||
@brief Main entry point. | ||
@param argc Argument count. | ||
@param argv Argument vector. | ||
@return status code | ||
*/ | ||
int main(int argc, char **argv) | ||
{ | ||
// Load config files, if any. | ||
|
||
// Run command loop. | ||
lsh_loop(); | ||
|
||
// Perform any shutdown/cleanup. | ||
|
||
return EXIT_SUCCESS; | ||
} |