123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- // @file main.c ~ main source file for in
- // @author Original: Stephen Brennan ~ Maintained and customized
- // my Chris Dorman, 2020+ LGPLv2
- // @desc CDSH, Chris Dorman's SHell! Simplistic, but functional
- // posix unix shell.
- #include "main.h"
- #include "parse.h"
- /*
- List of builtin commands, followed by their corresponding functions.
- */
- char *builtin_str[] = {
- "cd",
- "help",
- "exit"
- };
- int (*builtin_func[]) (char **) = {
- &cdsh_cd,
- &cdsh_help,
- &cdsh_exit
- };
- int cdsh_num_builtins() {
- return sizeof(builtin_str) / sizeof(char *);
- }
- /*
- Builtin function implementations.
- */
- /*
- @brief Bultin 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 cdsh_cd(char **args)
- {
- if (args[1] == NULL) {
- fprintf(stderr, "cdsh: expected argument to \"cd\"\n");
- } else {
- if (chdir(args[1]) != 0) {
- perror("cdsh");
- }
- }
-
- return 1;
- }
- /*
- @brief Builtin command: print help.
- @param args List of args. Not examined.
- @return Always returns 1, to continue executing.
- */
- int cdsh_help(char **args)
- {
- int i;
- printf("Chris Dorman's SHell\n");
- printf("Type program names and arguments, and hit enter.\n");
- printf("The following are built in:\n");
- for (i = 0; i < cdsh_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 cdsh_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 cdsh_launch(char **args)
- {
- pid_t pid;
- int status;
- pid = fork();
- if (pid == 0) {
- // Child process
- if (execvp(args[0], args) == -1) {
- perror("cdsh");
- }
-
- exit(EXIT_FAILURE);
-
- } else if (pid < 0) {
- // Error forking
- perror("cdsh");
- } 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 cdsh_execute(char **args)
- {
- int i;
- if (args[0] == NULL) {
- // An empty command was entered.
- return 1;
- }
- for (i = 0; i < cdsh_num_builtins(); i++) {
- if (strcmp(args[0], builtin_str[i]) == 0) {
- return (*builtin_func[i])(args);
- }
- }
-
- return cdsh_launch(args);
- }
- /*
- @brief Read a line of input from stdin.
- @return The line from stdin.
- */
- char *cdsh_read_line(void)
- {
- #ifdef CDSH_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 recieved an EOF
- } else {
- perror("cdsh: getline\n");
- exit(EXIT_FAILURE);
- }
- }
- return line;
- #else
- #define CDSH_RL_BUFSIZE 1024
- int bufsize = CDSH_RL_BUFSIZE;
- int position = 0;
- char *buffer = malloc(sizeof(char) * bufsize);
- int c;
- if (!buffer) {
- fprintf(stderr, "cdsh: 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 += CDSH_RL_BUFSIZE;
- buffer = realloc(buffer, bufsize);
- if (!buffer) {
- fprintf(stderr, "cdsh: allocation error\n");
- exit(EXIT_FAILURE);
- }
- }
- }
- #endif
- }
- #define CDSH_TOK_BUFSIZE 64
- #define CDSH_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 **cdsh_split_line(char *line)
- {
- int bufsize = CDSH_TOK_BUFSIZE, position = 0;
- char **tokens = malloc(bufsize * sizeof(char*));
- char *token, **tokens_backup;
- if (!tokens) {
- fprintf(stderr, "cdsh: allocation error\n");
- exit(EXIT_FAILURE);
- }
- token = strtok(line, CDSH_TOK_DELIM);
- while (token != NULL) {
- tokens[position] = token;
- position++;
- if (position >= bufsize) {
- bufsize += CDSH_TOK_BUFSIZE;
- tokens_backup = tokens;
- tokens = realloc(tokens, bufsize * sizeof(char*));
- if (!tokens) {
- free(tokens_backup);
- fprintf(stderr, "cdsh: allocation error\n");
- exit(EXIT_FAILURE);
- }
- }
- token = strtok(NULL, CDSH_TOK_DELIM);
- }
- tokens[position] = NULL;
- return tokens;
- }
- /*
- @brief Loop getting input and executing it.
- */
- void cdsh_loop(void)
- {
- char *line;
- char **args;
- int status;
- do {
- printf("$ ");
- line = cdsh_read_line();
- args = cdsh_split_line(line);
- status = cdsh_execute(args);
- free(line);
- free(args);
- } while (status);
- }
- /**
- @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.
- if (argc == 1) {
- // If cdsh is called by itself, just pull prompt up
- cdsh_loop();
- } else if(argc > 2 || !strcmp(argv[1], "-?") || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
- printf("usage: cdsh [file location]\n"
- "Example: cdsh /path/to/script.sh \n");
- fflush(stdout);
- cdsh_exit(0); // exit
- } else if (argc == 2 && file_exists(argv[1])==0) {
- parse(argv[1]);
- }
- // Perform any shutdown/cleanup.
- return EXIT_SUCCESS;
- }
|