# mush.shar # Shell archive made by dwjones on Thu Apr 12 16:57:02 CDT 2018 # To install this software on a UNIX system: # 1) create a directory (e.g. with the shell command mkdir stuff) # 2) change to that directory (e.g. with the command cd stuff), # 3) direct the remainder of this text to sh (e.g. sh < ../savedmail). # This will make sh create files in the new directory; it will do # nothing else (if you're paranoid, you should scan the following text # to verify this before you follow these directions). Then read README # in the new directory for additional instructions. cat > README <<\xxxxxxxxxx README by Douglas Jones, Apr. 12, 2018 This is a distribution of the version of mush (the minimally usable shell) written to launch MP6 (but not solve) in the Spring 2018 offering of CS:3620 The component files are: README -- this file globals.h -- the global declarations main.c -- the main program getcommand.c -- read a command from stdin splitargv.c -- divide the command into arguments substitute.c -- do command line substitutions trypath.c -- try to exec app in each segment of the search path launch.c -- launch the application or do built-in commands Makefile -- build mush from the above Read the Makefile for more information xxxxxxxxxx cat > globals.h <<\xxxxxxxxxx /* globals.h * Global variables and definitions for a Minimally Usable SHell (MUSH) * modified by Douglas Jones for MP4, Mar 2, 2018 * extracted from mush.c, Feb 25, 2018 * modified by Douglas Jones for MP6 in CS:3620, Apr 12, 2018 * rewritten by Douglas Jones for MP2 in CS:3620, Feb 13, 2018 * originally by Douglas Jones */ /* the program name, used to compose error messages, initialized by main */ char * progname; /* the input file (usually the same as stdin), initialized by main */ FILE * infile; /* the input line */ #define LINE_LENGTH 73 char command[LINE_LENGTH]; /* the argument vector extracted from the input line */ #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. */ /* interface specs for the components of mush */ void getcommand(); void splitargv(); void substitute(); void trypath( char * name, char ** argv, char ** environ ); void launch(); xxxxxxxxxx cat > main.c <<\xxxxxxxxxx /* main.c * Main program for a Minimally Usable SHell (MUSH) * modified by Douglas Jones to complete MP4, Mar 27, 2018 * modified by Douglas Jones to start MP4, Mar 2, 2018 * extracted from mush.c, Feb 25, 2018. * rewritten by Douglas Jones for MP2 in CS:3620, Feb 13, 2018 * originally by Douglas Jones */ #include /* needed for fopen(), fputs(stderr), feof(stdin) */ #include /* needed for exit(EXIT_FAILURE) */ #include "globals.h" /* global variables of mush, including infile */ int main( int argc, char * argv[] ) /* repeatedly reads commands, splits them up, and executes them */ { infile = stdin; /* default */ progname = argv[0]; if (progname == NULL) progname = "mush"; /* rare need for a default */ if (argc >= 2) { /* argv[1] exists */ /* first, open the input file */ infile = fopen( argv[1], "r" ); if (infile == NULL) { fputs( progname, stderr ); fputs( ": cannot read \"", stderr ); fputs( argv[1], stderr ); fputs( "\"\n", stderr ); exit( EXIT_FAILURE ); } /* then transcribe the arguments into the environment */ /* argv[1] is script name, argv[2] and up are args to script */ int i; char argname[3]; /* needed so each arg is a string */ argname[1] = '\0'; /* arg names ends with NUL char */ for( i = 1; i < argc; i++ ) { if (i <= 10) { /* create names 0 through 9 */ argname[0] = '0' + (i - 1); argname[1] = '\0'; } else { /* create names 10 through 99 */ argname[0] = '0' + ((i - 1) % 10); argname[1] = '0' + ((i - 1) / 10); argname[2] = '\0'; } /* works up to 99 args */ setenv( argname, argv[i], 1 ); } } while (!feof( infile )) { getcommand(); splitargv(); substitute(); launch(); } } xxxxxxxxxx cat > getcommand.c <<\xxxxxxxxxx /* getcommand.c * Get one command from stdin for a Minimally Usable SHell (MUSH) * modified by Douglas Jones to start MP4, Mar 2, 2018 * extracted from mush.c, Feb 25, 2018 * rewritten by Douglas Jones for MP2 in CS:3620, Feb 13, 2018 * originally by Douglas Jones */ #include /* needed for getc, putchar, EOF */ #include "globals.h" /* global variables of mush */ void getcommand() /* Read a command line from stdin, the standard input file global: command, filled with the null-terminated text of the line infile, the input text stream note: this is never called when feof(stdin) is true */ { int ch; /* type is int because EOF is outside the char range */ int i = 0; /* where on the line are we */ /* only prompt if reading from stdin */ if (infile == stdin) putc( '>', stdout ); /* get as much of the line as possible */ do { /* get the line */ ch = getc( infile ); command[i] = ch; i++; } while ((ch != '\n') && (ch != EOF) && (i < LINE_LENGTH)); /* deal with nonstandard end of line conditions */ 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)) { putc( ch, stderr ); ch = getc( infile ); } putc( '\n', stderr ); } } xxxxxxxxxx cat > splitargv.c <<\xxxxxxxxxx /* splitargv.c * Split the command line into argv for a Minimally Usable SHell (MUSH) * extracted from mush.c, Feb 25, 2018 * rewritten by Douglas Jones for MP2 in CS:3620, Feb 13, 2018 * originally by Douglas Jones */ #include /* needed for NULL */ #include "globals.h" /* global variables of mush */ 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; } xxxxxxxxxx cat > substitute.c <<\xxxxxxxxxx /* substitute.c * Make command line substitutions for a Minimally Usable SHell (MUSH) * extracted from launch.c by Douglas Jones, Apr 12, 2018 */ #include /* needed for NULL */ #include /* needed for getenv() */ #include "globals.h" /* global variables of mush */ extern char** environ; /* environment passed here by whoever exec'd this */ void substitute() /* do any $environment substitutions on the command line in argv global: argv, an array of strings, any starting with $ will be substituted environ, the values of variables */ { int i; for (i = 0; argv[i] != NULL; i++) { /* for each argument */ if (argv[i][0] == '$') { /* argument subject to substitution */ char * variable = argv[i] + 1; /* what was after $ */ char * value = getenv( variable ); if (value != NULL) { argv[i] = value; } else { argv[i] = ""; /* undefined variables become empty strings */ } } } } xxxxxxxxxx cat > trypath.c <<\xxxxxxxxxx /* trypath.c * Utility used by launcher to traversesearch path in MUSH * extracted by Douglas Jones from launch.c, Apr 12, 2018 * originally by Douglas Jones */ #include /* needed for fputs, NULL */ #include /* needed for exit(), getenv() */ #include /* needed for execve() */ #include /* needed for strcat() */ void trypath( char * name, char ** argv, char ** environ ) /* try to launch the command using each element of the search path parameters: identical to execve note: launch() calls this, and it calls it only in the child fork. */ { char * path = getenv( "PATH" ); int namelen = strlen( name ); /* get this just once in advance */ if (path != NULL) { /* if there's no path, don't even try */ /* make a copy of the path for strtok to scribble on */ path = strcpy( malloc( strlen( path ) + 1 ), path ); /* find the first path element and setup for future strtoks */ char * element = strtok( path, ":" ); /* for each path element */ while (element != NULL) { /* construct a trial file name */ int len = strlen( element ) + namelen + 2; char * filename = strcpy( malloc( len ), element ); strcat( filename, "/" ); strcat( filename, name ); /* try this path element */ execve( filename, argv, environ ); /* get next element (this is how strtok works!) */ element = strtok( NULL, ":" ); } } } xxxxxxxxxx cat > launch.c <<\xxxxxxxxxx /* launch.c * Application launcher and built-ins for a Minimally Usable SHell (MUSH) * modified by Douglas Jones moving substitute() to a new file, Apr 12, 2018 * modified by Douglas Jones to start MP4, Mar 2, 2018 * extracted from mush.c, Feb 25, 2018 * rewritten by Douglas Jones for MP2 in CS:3620, Feb 13, 2018 * originally by Douglas Jones */ #include /* needed for fputs, NULL */ #include /* needed for exit(), getenv() */ #include /* needed for fork() */ #include /* needed for wait() */ #include /* needed for strcmp() */ #include "globals.h" /* global variables of mush */ extern char** environ; /* environment passed here by whoever exec'd this */ 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, uses fork-exec to launch applications */ { /* built-in commands */ if (argv[0] == NULL) return; /* blank command line */ if (argv[0][0] == '#') return; /* comment */ 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*/ /* first try the literal file name given */ execve( argv[0], argv, environ ); /* if that doesn't work, try prefixes from the search path */ trypath( argv[0], argv, environ ); /* if that doesn't work, give up */ fputs( progname, stderr ); fputs( ": no such command \"", stderr ); fputs( argv[0], stderr ); fputs( "\"\n", stderr ); exit( EXIT_FAILURE ); } else { /*parent*/ wait( NULL ); } } xxxxxxxxxx cat > Makefile <<\xxxxxxxxxx # Makefile # make mush -- makes the minimally usable shell # make clean -- deletes all files created by make # # revised by Douglas Jones for MP6 in CS:3620, Apr. 12, 2018 # by Douglas Jones for MP3 in CS:3620, Feb. 25, 2018 # primary make target OBJFILES = main.o getcommand.o splitargv.o substitute.o trypath.o launch.o mush: $(OBJFILES) cc -o mush $(OBJFILES) # subsidiary make targets main.o: main.c globals.h cc -c main.c getcommand.o: getcommand.c globals.h cc -c getcommand.c splitargv.o: splitargv.c globals.h cc -c splitargv.c substitute.o: substitute.c globals.h cc -c substitute.c trypath.o: trypath.c globals.h cc -c trypath.c launch.o: launch.c globals.h cc -c launch.c # utility make targets clean: rm -f *.o mush xxxxxxxxxx