/* mp3.c * a Minimally Usable SHell * by Douglas Jones, extended to solve MP2, Sept 29, 2013 * by Douglas Jones, extended to solve MP3, Oct 7, 2013 \***\ * all lines changed to solve MP3 are marked on the right \***\ * * this solution has a reduced 'intellectual footprint' in that * solutions using less code require understanding more of the * tools in the C standard library. */ #include #include #include #include extern char **environ; /* the environment */ char command[100]; /* the command line */ char * argv[100]; /* result of parsing the command line */ int argc; /* the number of arguments */ /***/ bool errors; /* were there any errors in it */ /***/ char * path; /* the search path extracted from the environment */ char * patha[100]; /* result of parsing the path */ void getcommand() { char ch; int i = 0; putchar( '>' ); do { ch = getchar(); command[i] = ch; i++; } while (ch != '\n'); command[i - 1] = '\000'; } void parseargv() { int i = 0; int j = 0; for (;;) { while (command[j] == ' ') j++; if (command[j] == '\000') break; argv[i] = &command[j]; i++; while ((command[j] != ' ') && (command[j] != '\000')) { j++; } if (command[j] == '\000') break; command[j] = '\000'; j++; } argc = i - 1; /***/ argv[i] = NULL; } void dollarsigns() { /***/ /* process shell variable substitutions */ /***/ int i = 0; /* loop index */ /***/ /***/ while (argv[i] != NULL) { /***/ if (argv[i][0] == '$') { /* ref to a shell variable */ /***/ char * value; /* value of the variable */ /***/ /***/ value = getenv( &argv[i][1] ); /***/ if (value != NULL) { /* value is defined */ /***/ argv[i] = value; /***/ } else { /* value is not defined */ /***/ printf( "no such variable: %s\n", /***/ argv[i] ); /***/ errors = true; /***/ } /***/ } /***/ i++; /***/ } /***/ } /***/ void getparsepath() { /* get the path and pick it apart into patha */ int i = 0; /* index into patha, array of path part pointers */ int j = 0; /* index into path, the text of the value of $PATH */ /* get a pointer to $PATH in the environ */ path = getenv( "PATH" ); if (path != NULL) { /* $PATH might not be defined in environ */ /* make a copy of path so we can still pass environ */ path = strcpy( malloc( strlen( path ) ), path ); /* pick out components of path, index them in patha */ /* this logic was borrowed from parseargv */ for (;;) { if (path[j] == '\000') break; patha[i] = &path[j]; i++; while ((path[j] != ':') && (path[j] != '\000')) { j++; } if (path[j] == '\000') break; path[j] = '\000'; j++; } } patha[i] = NULL; } char * gluepath( char * left, char * right ) { /* concatenate left and right, with a / between them */ char * result; /* how long is result string? including / and null terminator */ result = malloc( strlen( left ) + 1 + strlen( right ) + 1 ); strcpy( result, left ); strcat( result, "/" ); strcat( result, right ); return result; } void launch() { if (fork() == 0) { /*child*/ int i; /* first try it with the literal command */ execve( argv[0], argv, environ ); /* if that fails, try with successive path components */ getparsepath(); i = 0; while (patha[i] != NULL) { execve( gluepath( patha[i], argv[0] ), argv, environ ); i++; } /* if that fails, we are lost */ printf( "no such command\n" ); exit( EXIT_FAILURE ); } else { /*parent*/ wait( NULL ); } } void docommand() { /***/ /* process built-in commands or launch an application */ /***/ /***/ if ( argv[0] == NULL ) { /***/ /* prevent segmentation fault on blank lines */ /***/ /***/ } else if (!strcmp( argv[0], "exit" )) { /***/ exit( EXIT_SUCCESS ); /***/ /***/ } else if (!strcmp( argv[0], "setenv" )) { /***/ if (argc < 1) { /***/ printf( "setenv with missing argument\n" ); /***/ } else if (argc == 1) { /***/ /* setenv without a specified value */ /***/ int ret = setenv( argv[1], "", 1 ); /***/ if (ret != 0) { /***/ printf( "bad variable name\n" ); /***/ } /***/ } else if (argc == 2) { /***/ /* the normal setenv with a value specified */ /***/ int ret = setenv( argv[1], argv[2], 1 ); /***/ if (ret != 0) { /***/ printf( "bad variable name\n" ); /***/ } /***/ } else { /* argc > 2 */ /***/ printf( "setenv with extra arguments\n" ); /***/ } /***/ /***/ } else { /* not a built-in command */ /***/ launch(); /***/ } /***/ } /***/ main(){ for (;;) { errors = false; /***/ getcommand(); parseargv(); dollarsigns(); /***/ if (!errors) docommand(); /***/ } }