/* mush.c * a Minimally Usable SHell * rewritten by Douglas Jones for MP2 in CS:3620, Feb 13, 2018 * originally by Douglas Jones */ #include /* needed for getchar, putchar, NULL */ #include /* needed for exit() */ #include /* needed for fork() */ #include /* needed for wait() */ #include /* needed for strcmp() */ #define LINE_LENGTH 73 char command[LINE_LENGTH]; #define MAX_ARGS ((LINE_LENGTH/2)+1) char *argv[MAX_ARGS]; /* the definition of MAX_ARGS means we never need to do any bounds checking on argv; each arg is at least 1 character followed by a NUL, and we allow a trailing NULL at the end of the argument list. */ void getcommand() /* Read a command line from stdin, the standard input file global: command, filled with the null-terminated text of the line note: this is never called when feof(stdin) is true */ { int ch; /* this must be into because EOF is outside the char range */ int i = 0; putchar( '>' ); /* the prompt */ do { /* get the line */ ch = getchar(); command[i] = ch; i++; } while ((ch != '\n') && (ch != EOF) && (i < LINE_LENGTH)); if ((ch == '\n') || (ch == EOF)) { /* normal case */ command[i - 1] = '\0'; } else { /* error case */ command[LINE_LENGTH - 1] = '\0'; fputs( "overlength line: ", stderr ); while ((ch != '\n') && (ch != EOF)) { putchar( ch ); ch = getchar(); } putchar( '\n' ); } } void splitargv() /* Split the command line into its constituent arguments global: command, the line, a null terminated string that is broken into many argv, filled with pointers to the successive arguments in command note: NUL chars replace the delimiter after each argument in command See the definition of MAX_ARGS for why no array bounds checking here */ { int i = 0; int j = 0; for (;;) { while (command[j] == ' ') j++; if (command[j] == '\0') break; argv[i] = &command[j]; i++; while ((command[j] != ' ') && (command[j] != '\0')) { j++; } if (command[j] == '\0') break; command[j] = '\0'; j++; } argv[i] = NULL; } void launch() /* Execute the command global: argv, the command (in argv[0]) and its arguments (argv[1] and up) command, not explicitly referenced, but holds the argument text note: Except for built-in commands, this uses fork-exec to launch applications */ { /* built-in commands */ if (argv[0] == NULL) { /* we need this to prevent strcmp() from segmentation fault */ return; } if (!strcmp( argv[0], "exit" )) { /* this could get exit succes/failure code from argv[1] */ exit( EXIT_SUCCESS ); } /* control only reaches here if the command is not built-in */ if (fork() == 0) { /*child*/ /* we could pass on the environment here (see HW3 prob 1) */ execve( argv[0], argv, NULL ); /* we could put code to walk down the search path here */ printf( "no such command\n" ); exit( EXIT_FAILURE ); } else { /*parent*/ wait( NULL ); } } int main() /* The main program note: repeatedly reads commands, splits them up, and executes them */ { while (!feof( stdin )) { getcommand(); splitargv(); launch(); } }