/* mp3.c */ /************************************************* * A minimally usable shell * * Author: Douglas W. Jones * * Revised by: Douglas W. Jones * * as mp2.c, to conform to manual of C style * * as mp3.c, to support the following: * * a) quotation marks around parameters * * b) pound is comment delimiter * * c) dollar sign environment variables * * d) builtin commands exit and setenv * * e) pass environment to launched app * *************************************************/ #include #include extern char **environ; /* e */ /* string terminator character */ #define NUL '\000' /* maximum line length */ #define COMMANDLEN 100 /* maximum number of arguments, worst case: one char each separted by blanks */ #define ARGVLEN (COMMANDLEN / 2) /* the command line, a character string, later a collection of strings initialized by getcommand modified by parseargv indirectly used by launch */ char command[COMMANDLEN]; /* the argument vector, an array of strings initialized by parseargv c modified by dollarsubs used by launch */ char *argv[ARGVLEN]; void getcommand() /* read one command line from standard input note: result returned in the global variable command */ { char ch; /* one character from standard input */ int i = 0; /* a count of the number of characters on the line */ /* prompt for input */ putchar('>'); /* collect the input line, one character at a time */ do { ch = getchar(); if (i < COMMANDLEN) { command[i] = ch; i++; } } while (ch != '\n'); /* replace the newline at the end with a string termiantor */ command[i - 1] = NUL; } void parseargv() /* parse the command line into a sequence of arguments note: result returned in the global variable argv chops the string stored in the global variable command into substrings */ { int i = 0; /* index of the current argument in argv */ int j = 0; /* index of the current character in the command */ for (;;) { /* iterate once per argument */ /* look for the start of an argument */ while (command[j] == ' ') j++; if (command[j] == NUL) break; /* b */ if (command[j] == '#') break; /* a */ if ((command[j] != '\'') /* a */ && (command[j] != '"')) { /* regular parameter */ /* mark the start of the argument */ if (i < (ARGVLEN - 1)) { argv[i] = &command[j]; i++; } /* find the end of the argument */ while ((command[j] != ' ') &&(command[j] != NUL) /* b */ &&(command[j] != '#')) { j++; } if (command[j] == NUL) break; /* b */ if (command[j] == '#') { /* b */ command[j] = NUL; /* b */ break; /* b */ } /* a */ } else { /* quoted parameter */ /* a */ char quotemark = command[j]; /* a */ j++; /* skip over opening quote */ /* a */ /* a */ /* mark the start of the argument */ /* a */ if (i < (ARGVLEN - 1)) { /* a */ argv[i] = &command[j]; /* a */ i++; /* a */ } /* a */ /* a */ /* find the end of the argument */ /* a */ while (command[j] != quotemark) { /* a */ if (command[j] == NUL) { /* a */ printf( "missing closing quote\n" ); /* a */ break; /* a */ } /* a */ j++; /* a */ } /* a */ } /* mark end of command */ command[j] = NUL; j++; } /* mark the end of argv */ argv[i] = NULL; } void dollarsubs() /* handle dollar sign substitution in the global variable argv */ { /* c */ int i = 0; /* c */ char * arg; /* c */ for (;;) { /* for each argument */ /* c */ arg = argv[i]; /* c */ if (arg == NULL) break; /* c */ /* c */ if (arg[0] == '$') { /* argument begins with $ */ /* c */ char * v = getenv( &arg[1] ); /* c */ if (v != NULL) { /* there is such a variable */ /* c */ argv[i] = v; /* c */ } else { /* c */ printf( "no such environment variable\n" ); /* c */ } /* c */ } /* c */ /* c */ i++; /* c */ } } void launch() /* launch the application required to execute the command note: uses the global variable argv for the command name and arguments */ { /* d */ char * command = argv[0]; /* d */ /* d */ if ( argv[0] == NULL ) return; /* do nothing for blank lines */ /* d */ /* d */ if ( strcmp( command, "exit" ) == 0 ) { /* handle built in exit */ /* d */ if (argv[1] != NULL) { /* d */ printf( "too many arguments\n" ); /* d */ } /* d */ exit( EXIT_SUCCESS ); /* d */ } /* d */ /* d */ if ( strcmp( command, "setenv" ) == 0 ) { /* handle built in setenv */ /* d */ char * name = argv[1]; /* d */ char * value = argv[2]; /* d */ if ((name == NULL) || (value == NULL)) { /* d */ printf( "missing arguments\n" ); /* d */ } else { /* d */ setenv( name, value, 1 ); /* d */ } /* d */ return; /* d */ } /* d */ if (fork() == 0) { /*child*/ /* try to launch the application */ /* e */ execve( command, argv, environ ); /* if launch fails, error message */ printf( "no such command\n" ); exit( EXIT_FAILURE ); } else { /*parent*/ /* wait for command to finish */ wait( NULL ); } } int main() /* repeatedly read, parse and execute commands from standard input note: communication between subsidiary routines is through global variables */ { for (;;) { getcommand(); parseargv(); /* c */ dollarsubs(); launch(); } }