========================================================================== CS 311 - Assignment III Name: Student ID: Email: Name: Student ID: Email: Name: Student ID: Email: Wed Nov 8 23:44:15 PST 2000 SunOS eel.ENGR.ORST.EDU 5.7 Generic_106541-12 sun4u sparc SUNW,Ultra-1 lundberg ========================================================================== -------------------------------------------------------------------------- Source File: smallsh.h /* * smallsh.h * File Revision 1 * Primary header for smallsh. * (c) 1999 Addison Wesley Longman, Inc. * (c) 2000 Jacob Lundberg, jacob@chaos2.org */ /* * 2000.11.05 Acquire initial code * Adjust style and values */ #ifndef __SMALLSH_H__ #define __SMALLSH_H__ #include #include #include #include /* * boolean logic * We provide a form that gcc will accept by default. However, it isn't * strictly correct to define arbitrary values for both members of an enum * so unless we know we're compiling with a GNU C compiler we'll refrain. */ #ifdef __GNUC__ typedef enum boolean { false = 0, true = !false } bool; #else /* def __GNUC__ */ typedef int bool; #define false 0 #define true !false #endif /* def __GNUC__ */ /* Some boxen define pid_t in odd places, so... */ //#ifndef pid_t //typedef __pid_t pid_t; //#endif /* ndef pid_t */ /* Symbol types reading from the commandline. */ typedef enum _sh_symbol { SH_SYM_AMP, /* & (ampersand) */ SH_SYM_ARG, /* (regular word) */ SH_SYM_EOL, /* (end of line) */ SH_SYM_GT, /* > (greater than) */ SH_SYM_LT, /* < (less than) */ SH_SYM_SC, /* ; (semicolon) */ } sh_symbol; /* Maximum number of command args. */ #define MAXARG 1023 /* Maximum length of input lines. */ #define MAXBUF 1023 /* Task types. */ typedef enum _sh_task_whence { SH_TASK_FORE, /* foreground tasks */ SH_TASK_BACK /* background tasks */ } sh_task_whence; /* Exported functional prototypes. */ int runcommand(char **cline, sh_task_whence where, char *inp, char *oup); int userin(char *p); void procline(void); #endif /* ndef __SMALLSH_H__ */ -------------------------------------------------------------------------- Source File: main.c /* * main.c * File Revision 1 * Main loop for smallsh. * (c) 1999 Addison Wesley Longman, Inc. * (c) 2000 Jacob Lundberg, jacob@chaos2.org */ /* * 2000.11.05 Acquire initial code * Adjust style and prompt */ #include #include "smallsh.h" int main() { /* * main() * Loop doing nothing */ char *prompt = ": "; /* Ignore ^C signals. */ signal(SIGINT, SIG_IGN); /* Loop on user input. */ while(userin(prompt) != EOF) { procline(); } return(0); } -------------------------------------------------------------------------- Source File: procline.c /* * procline.c * File Revision 1 * Commandline processing for smallsh. * (c) 1999 Addison Wesley Longman, Inc. * (c) 2000 Jacob Lundberg, jacob@chaos2.org */ /* * 2000.11.05 Acquire initial code * Adjust style */ #include #include "smallsh.h" /* Program buffers and work pointers */ static char inpbuf[MAXBUF], tokbuf[2 * MAXBUF], *ptr = inpbuf, *tok = tokbuf; static char special[] = {' ', '\t', '<', '>', '&', ';', '\n', '\0' }; void doprompt(char *p) { /* * doprompt() * Push the prompt on out to the console. */ pid_t pid; int status; /* Check for information on backgrounded processes. */ while((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { if(WIFEXITED(status)) printf("background pid %li is done: exit value %i\n", pid, WEXITSTATUS(status)); else if(WIFSIGNALED(status)) printf("background pid %li is done: terminated by signal %i\n", pid, WTERMSIG(status)); else if(WIFSTOPPED(status)) printf("background pid %li has been stopped by signal %i\n", pid, WSTOPSIG(status)); } /* Display the prompt itself. */ printf("%s", p); /* Make sure it gets out now. */ fflush(stdout); return; } int userin(char *p) { /* * userin() * Print the prompt and read a line. */ char c; int count; /* Initialization for later routines. */ ptr = inpbuf; tok = tokbuf; /* Display the prompt. */ doprompt(p); count = 0; while(true) { c = getchar(); if(c == EOF) { return(EOF); } if(count < MAXBUF) inpbuf[count++] = c; if(c == '\n' && count < MAXBUF) { inpbuf[count] = '\0'; return(count); } /* If line too long, restart. */ if(c == '\n') { count = 0; printf("smallsh: input line too long\n"); doprompt(p); } } } int inarg(char c) { /* * inarg() * Determine if a character can be part of an ordinary argument or special character. */ char *wrk; for(wrk = special; *wrk; ++wrk) { if(c == *wrk) return(false); } return(true); } int gettok(char **outptr) { /* * gettok() * Tokenize the string reading process. */ int type; /* set the outptr string to tok */ *outptr = tok; /* strip white space from the buffer containing the tokens */ while(*ptr == '\t' || *ptr == ' ') ++ptr; *tok++ = *ptr; /* set the variable depending on the token in the buffer */ switch(*ptr++) { case '\n': type = SH_SYM_EOL; break; case '<': type = SH_SYM_LT; break; case '>': type = SH_SYM_GT; break; case '&': type = SH_SYM_AMP; break; case ';': type = SH_SYM_SC; break; default: type = SH_SYM_ARG; /* keep reading valid ordinary characters */ while(inarg(*ptr)) *tok++ = *ptr++; } *tok++ = '\0'; return(type); } void procline(void) { /* * procline() * Parse a command line using gettok(), and construct an argument list. * Call docommand() when it reaches a semicolon or newline. The command * must have alredy been read by userin before calling this function. */ char *arg[MAXARG + 1]; /* pointer array for runcommand */ sh_task_whence type; /* SH_TASK_FORE or SH_TASK_BACK */ int narg = 0; /* number of arguments so far */ int toktype; /* type of token in command */ char *oup = NULL; /* output redirection file */ char *inp = NULL; /* input redirection file */ while(true) { /* Take action according to token type. */ toktype = gettok(&arg[narg]); type = SH_TASK_FORE; switch(toktype) { case SH_SYM_ARG: if(narg < MAXARG) ++narg; break; case SH_SYM_AMP: type = SH_TASK_BACK; case SH_SYM_EOL: case SH_SYM_SC: if(narg != 0) { arg[narg] = NULL; runcommand(arg, type, inp, oup); } if(toktype == SH_SYM_EOL) return; narg = 0; break; case SH_SYM_LT: gettok(&inp); break; case SH_SYM_GT: gettok(&oup); break; } } } -------------------------------------------------------------------------- Source File: runcommand.c /* * runcommand.c * File Revision 1 * Sub-command execution for smallsh. * (c) 1999 Addison Wesley Longman, Inc. * (c) 2000 Jacob Lundberg, jacob@chaos2.org */ /* * 2000.11.05 Acquire initial code * Adjust style */ #include #include #include "smallsh.h" int run_external(char **cline, sh_task_whence where, char *inp, char *oup) { /* * run_external() * Execute an external command with an optional wait. */ pid_t pid; int status; pid = fork(); switch(pid) { case -1: /* fork error */ perror("smallsh:runcommand:fork"); return(-1); case 0: /* code for child */ if(where == SH_TASK_BACK && inp == NULL) freopen("/dev/null", "r", stdin); if(inp != NULL && freopen(inp, "r", stdin) == NULL) { fprintf(stderr, "smallsh: unable to open input file\n"); fflush(stderr); exit(1); } if(oup != NULL && freopen(oup, "w", stdout) == NULL) { fprintf(stderr, "smallsh: unable to open output file\n"); fflush(stderr); exit(1); } /* Stop trapping signals. */ signal(SIGINT, SIG_DFL); /* Execute the child. */ execvp(*cline, cline); perror(*cline); exit(1); } /* code for parent */ if(where == SH_TASK_BACK) { /* If background process, print pid and return. */ printf("background pid is %lu\n", pid); fflush(stdout); return(0); } /* wait until process pid exits */ if(waitpid(pid, &status, 0) == -1) return(-1); else return(status); } int run_exit(char **cline) { /* * run_exit() * Exit smallsh. */ /* Exit the shell. */ exit(0); return(0); } int run_cd(char **cline) { /* * run_cd() * Change the present working directory. */ /* Default to the user's home directory. */ if(cline[1] == NULL) cline[1] = getenv("HOME"); /* Try and change the dir. */ if(chdir(cline[1])) { fprintf(stderr, "smallsh: unable to cd to %s\n", cline[1]); fflush(stderr); return(1); } else return(0); } int run_pwd(char **cline, char *oup) { /* * run_pwd() * List the present working directory. */ FILE *newout; char pwd[1024] = ""; /* Open the new output stream (if needed). */ if(oup != NULL) { newout = fopen(oup, "w"); if(newout == NULL) { fprintf(stderr, "smallsh: unable to open output file\n"); fflush(stderr); exit(1); } } /* Output the pwd. */ getcwd(pwd, 1024); pwd[1023] = '\0'; fprintf(oup == NULL ? stdout : newout, "%s\n", pwd); /* Close the file stream. */ if(oup != NULL) fclose(newout); return(0); } int run_status(char **cline, char *oup, int status) { /* * run_status() * Show the exit conditions of the last foreground process. */ FILE *newout; /* Open the new output stream (if needed). */ if(oup != NULL) { newout = fopen(oup, "w"); if(newout == NULL) { fprintf(stderr, "smallsh: unable to open output file\n"); fflush(stderr); exit(1); } } if(status == -1) fprintf(oup == NULL ? stdout : newout, "no foreground processes yet\n"); else if(WIFEXITED(status)) fprintf(oup == NULL ? stdout : newout, "exit value %i\n", WEXITSTATUS(status)); else if(WIFSIGNALED(status)) fprintf(oup == NULL ? stdout : newout, "terminated by signal %i\n", WTERMSIG(status)); else if(WIFSTOPPED(status)) fprintf(oup == NULL ? stdout : newout, "stopped by signal %i\n", WSTOPSIG(status)); /* Close the file stream. */ if(oup != NULL) fclose(newout); return(0); } int runcommand(char **cline, sh_task_whence where, char *inp, char *oup) { /* * runcommand() * Run a command. */ /* Exit value info for foreground processes. */ static int status = -1; /* Determine how to handle the command (internally / externally). */ if(!strncmp(cline[0], "#", 1)) return(0); else if(!strncmp(cline[0], "cd", 2)) return(run_cd(cline)); else if(!strncmp(cline[0], "exit", 4)) return(run_exit(cline)); else if(!strncmp(cline[0], "pwd", 3)) return(run_pwd(cline, oup)); else if(!strncmp(cline[0], "status", 6)) return(run_status(cline, oup, status)); else return(status = run_external(cline, where, inp, oup)); } -------------------------------------------------------------------------- Building Test Program -------------------------------------------------------------------------- Testing smallsh : ------------------- : Single Command Test : : ls : bkgjunk junk minitest minitest.c smallsh : : ps : PID TTY TIME CMD 29909 pts/12 0:00 test 29926 pts/12 0:00 smallsh 29322 pts/12 0:00 tcsh : : : ------------------- : Argument Test : : arg 0: |./minitest| arg 1: |1| arg 2: |2| arg 3: |3| arg 4: |4| arg 5: |five| arg 6: |six| arg 7: |seven| arg 8: |eight| arg 9: |9| arg 10: |10| arg 11: |11| arg 12: |12| arg 13: |13| arg 14: |14| arg 15: |15| arg 16: |16| arg 17: |17| arg 18 is NULL : : : ------------------- : Exit Status Test : : exiting with value 0 : exit value 0 : : exiting with value 17 : exit value 17 : : sending signal 1 : terminated by signal 1 : : sending SIGINT (2) : terminated by signal 2 : : : ------------------- : Redirection Test : : ls out junk1 : : : ls : bkgjunk junk junk1 minitest minitest.c smallsh : : cat junk1 : bkgjunk junk junk1 minitest minitest.c smallsh : : tr m A in junk : sAallsh is a Ainiature shell : : tr m B in junk out junk2 : : cat junk2 : sBallsh is a Biniature shell : : tr M C out junk3 in junk : : cat junk3 : sCallsh is a Ciniature shell : : : ------------------- : Input/Output Test : : this is stderr stdin is readable this is stdout : : minitest 2 in junk out junk4 : this is stderr : cat junk4 : stdin is readable this is stdout : : : ------------------- : Bad Command Test : : PID TTY TIME CMD 29909 pts/12 0:00 test 29926 pts/12 0:00 smallsh 29322 pts/12 0:00 tcsh : : funnybadcommand: No such file or directory : : PID TTY TIME CMD 29909 pts/12 0:00 test 29926 pts/12 0:00 smallsh 29322 pts/12 0:00 tcsh : : : ------------------- : Bad Redirection Test : : cat in badfile : smallsh: unable to open input file : : chmod 0 junk - ls out junk : : smallsh: unable to open output file : : PID TTY TIME CMD 29909 pts/12 0:00 test 29926 pts/12 0:00 smallsh 29322 pts/12 0:00 tcsh : : : ------------------- : System Test - should fail : : ls | wc : |: No such file or directory wc: No such file or directory : rm junk* : junk*: No such file or directory : : : ------------------- : Background Test : : sleep 5 background : background pid is 36 : : : PID TTY TIME CMD 36 pts/12 0:00 sleep 29909 pts/12 0:00 test 29926 pts/12 0:00 smallsh 29322 pts/12 0:00 tcsh : : background pid 36 is done: exit value 0 : : PID TTY TIME CMD 29909 pts/12 0:00 test 29926 pts/12 0:00 smallsh 29322 pts/12 0:00 tcsh : : : ------------------- : Background Input/Output Test : : background pid is 49 : this is stderr stdin is readable this is stdout background pid 49 is done: exit value 0 : : background pid is 54 : read returns 0, char read is X background pid 54 is done: exit value 0 : : cat in bkgjunk background : background pid is 58 : smallsh is a miniature shell that can run commands in the background background pid 58 is done: exit value 0 : : ------------------- : Background Redirection Test : : tr m M in bkgjunk out bkgjunk2 background : background pid is 65 : background pid 65 is done: exit value 0 : : cat bkgjunk2 : sMallsh is a Miniature shell that can run coMMands in the background : : : ------------------- : Background Exit Status Test : : background pid is 75 : exiting with value 0 background pid 75 is done: exit value 0 : : background pid is 78 : exiting with value 17 background pid 78 is done: exit value 17 : : background pid is 81 : sending signal 1 background pid 81 is done: terminated by signal 1 : : background pid is 84 : sending SIGINT (2) background pid 84 is done: terminated by signal 2 : : : ------------------- : Background Check PS : : PID TTY TIME CMD 29909 pts/12 0:00 test 29926 pts/12 0:00 smallsh 29322 pts/12 0:00 tcsh : : : ------------------- : CD Test : : : cd subdir : : /nfs/stak/u3/l/lundberg/cs311temp/assign3.test.29909/subdir : : cd .. : : /nfs/stak/u3/l/lundberg/cs311temp/assign3.test.29909 : : cd : : /nfs/stak/u3/l/lundberg : : cd /nfs/stak/u3/l/lundberg/cs311temp/assign3.test.29909 : : /nfs/stak/u3/l/lundberg/cs311temp/assign3.test.29909 : : : ------------------- : Comment Test : : : : : ------------------- : Blank Line Test : : : : : : ------------------- : Exit Test : : -------------------------------------------------------------------------- Shell SIGINT Test PID TTY TIME CMD 29909 pts/12 0:00 test 29322 pts/12 0:00 tcsh PS While Running PID TTY TIME CMD 29909 pts/12 0:00 test 141 pts/12 0:00 sleep 138 pts/12 0:00 smallsh 29322 pts/12 0:00 tcsh PS After SIGINT PID TTY TIME CMD 29909 pts/12 0:00 test 141 pts/12 0:00 sleep 138 pts/12 0:00 smallsh 29322 pts/12 0:00 tcsh 138 Killed