Programming Assignment 2 Command Invocation Due Date: by 17:59:59.9 on Friday, May 15, 1998 For this assignment only, you are permitted to work in teams of not more than two people. You must register your team using the groupcon program in the cs2430 workon environment. Both members of the team must be registered by not later than 6:00 PM on Friday, May 8, 1998. If you do not register yourself as part of a team, then it will be assumed that you are working alone. No extra points will be given for people who choose to work alone. EACH MEMBER OF A PROJECT TEAM IS INDIVIDUALLY RESPONSIBLE FOR SEEING THAT A COPY OF THE ASSIGNMENT IS TURNED IN. Readings: Review the material on pages 392-398 of the Weiss book. In addition, look over the material on pointers to functions (pp 135-137), structures (pp 223-234), and how to combine them (pp 245-246). Stub Code: You should have gotten the source code for the Builtin() function with your getjob program for this assignment. The following files should have been distributed: 2README.ps this file (PostScript version) 2README.txt this file (text version) Makefile.jkg creates fsh using jkg's options.c Makefile.opt creates fsh using your own options.c builtin.c contains source code for the Builtin() function The code for the other functions that will be needed ( Parse(), RunCommand(), etc) has already been compiled. The object modules are available in the ~cs2430/pub/fsh/objects directory on Acme. The supplied Makefile knows how to find these, so there is no need to make your own copies of them. You will not have to modify these parts of the code. Note: After completing this part, you will have a working shell! Modifications: You will have to add or modify some shell "builtin" commands. These are commands that are executed directly by the shell, as opposed to being called as external programs. The procedure for adding these commands will be outlined below. You must follow this procedure exactly to get full credit on this part. The actions of each of the builtin commands will be carried out by a single function that is dedicated specifically to that command. The supplied version of builtin.c does this for two commands already - set and variable assignment. A third command (the cd command) is handled directly in the Builtin() function itself (you will have to modify this behavior as part of this assignment). Each function receives two arguments - Argc (a count of the number of arguments passed), and Argv (an array of pointers to the actual argu- ment strings). Argument indices in the Argv[] array range from 0 to Argc-1. Argv[ 0 ] always contains the name of the builtin command ( cd, set, etc). The supplied version of builtin.c uses a series of if-then- else-if ... comparisons to find out what command is being invoked. This is a very cumbersome mechanism that is difficult to modify, particularly if the number of commands to be added is large. A much cleaner way of doing this is to statically declare a global array of command names and their associated functions, where the names are stored as constant strings, and the "functions" are actually stored as pointers (i.e. the addresses where the functions are stored). Here is a sample declaration for an array that contains relevant information about the functions as supplied in builtin.c: #define FNULL ( ( VoidFP ) NULL ) /* pre-cast NULL function ptr */ typedef void ( * VoidFP )( int, char ** ); /* typedef for a pointer to a */ /* function that returns void */ typedef struct { char *Command; /* character string for command name */ VoidFP ComFunc; /* function address for command function */ } CommStruct; static const CommStruct /* struct CommStruct has cmd-func pairs */ BuiltinCmds[] = /* to initialize addresses for builtins */ { { "cd", Cd }, /* "cd" command */ { "set", Set }, /* "set" command */ { NULL, FNULL } /* NULL - end of array */ }; To determine whether or not a command is builtin, all you have to do is write a for loop to step through the BuiltinCmds array until you either find the name string for that command or hit the NULL at the end (similar to the example on page 246 in the Weiss book). If the command name string is found (for example, in location BuiltinCmds[ i ].Command ), then you call the function as BuiltinCmds[ i ].ComFunc( Argc, Argv ). Adding new commands is very simple - just write a function to handle it and add another struct entry to the array. You will have to modify the processing of builtin commands to use the BuiltinCmds array as described above, and then add functions for several new commands. Here are the descriptions (shamelessly copied from the man pages for ksh) for the buil- tin functions that you must provide (in builtin.c): For a "C" (70%) - 1. Add code to utilize the BuiltinCmds structure as described above. 2. Modify the behavior of the set command so that, in addition to what it does now, it also sets options and arguments, just is if those same options and arguments had been passed to the shell from the command line. Note that set must not modify shell argument 0 (i.e. the string that is accessed via $0); argument 0 must always contain the name by which the shell was invoked. 3. Add code for the following builtin commands: cd [ arg ] Changes the current directory to arg. If arg is omitted, then the directory is changed to the home directory, as defined by the HOME shell variable (use getenv(3c) to access the values of shell variables). 37...I PWD echo [ -n ] [ arg ... ] Prints arguments in list (each separated by a single space) to the standard output. The output is terminated with a newline unless the -n option is used. exit [ # ] Causes the shell to exit with the exit status specified by #. If # is omitted, then the exit status is 0. ulimit [ limit ] The maximium size of any files created by a child process is set to limit. Limit is a decimal number that specifies the maximum number of 512 byte blocks that may be allocated for new files. If limit is omitted, then the current file size limit is printed. umask [ mask ] The user file-creation mask is set to mask. Mask is an octal number in the range 0000-0777. If mask is omitted, the current value of the mask is printed. For a "B" (80%) - 1. Do everything required for a "C". 2. Add code for the following builtin commands: shift [ n ] The shell arguments from $n+1 through $# are copied to positions $1 through $#-n in the argument list. The default n is 1. set [ -svx ] [ arg ... ] The options for this command have the following meanings: -s Sort the shell arguments lexicographically. -v Toggles the -v shell command line option. If this option is SET, then it forces printing of each shell input line after it is read. -x Toggles the -x shell command line option. If this option is SET, then it forces printing of each user command (with argu- ments) immediately before it is executed. For an "A" (90%) - 1. Do everything required for a "B". 2. Add code for the following builtin command: nice [ -# ] [ command ] Increment the process priority value for the shell or for command by #. The higher the priority value, the lower the priority of a pro- cess, and the slower it runs. When given, command is always run as a subprocess. If command is omitted, nice increments the value for the current shell. If no increment is speci- fied, nice increments the nice value by 4. The range of nice values is from -20 through 19. Values of # that would cause the nice value to be exceeded are must set the nice value to the upper boundary. exec arg ... The command specified by the arguments is exe- cuted in place of this shell without creating a new process. For an "A+" (100%) - 1. Do everything required for an "A". 2. Modify your shell so that it can be invoked as a "res- tricted" shell. A restricted shell is called by the name rfsh (or stated another way - you can tell that you are in a restricted shell if its $0 argument is rfsh instead of fsh). A restricted shell is typi- cally given to certain users so that the system administrator can maintain complete control over what they can or cannot do from within their shell. In a restricted shell, the following commands or operations are disallowed: o Changing directories o Assigning values to any of the SHELL, ENV, or PATH environment variables o Specifying command names that contain '/' o Redirecting output via '>' If any of these operations are attempted, then your shell (if it was invoked as a restricted shell), must issue an appropriate error message. Extra Credit: 1. Do everything required for an "A+". 2. Modify the umask command to use symbolic values for the file creation mask. See the entry for chmod(1) in the man pages for an explanation of what a symbolic value is. Note that the sense of the operators in the umask command is the opposite of how they're treated by the chmod command. For example, chmod go-w files ... causes write permission for "group" and "other" to be turned off for that file. By contrast, umask go-w causes the corresponding bits in the user mask to be turned on. You should probably mess around some with the umask command in the Korn shell to get some idea of what your program should do. Minimum Functionality - The following features and requirements must be com- pleted, to facilitate testing and grading of future assignments on this project: C1, C2, C3, B2