The following are implementation requirements and recommendations for your shell program:
(Required) Your program should be modular at the function level. Define small functions, each of which does a single well-defined job.
(Required) Your program should be modular at the source code file level. Define interfaces and implementations, thus splitting your program into multiple files. Define stateless modules, abstract objects, or abstract data types as appropriate. Encapsulate data structures with functions.
(Recommended) Your program may use the DynArray
ADT (from early precepts). The source code is available through the course web pages, and also in the /u/cos217/Assignment7
directory on hats.
(Required) Your program should interpret commands from a configuration file when it is first launched.
(Required) Your program should first look for the configuration file provided as a command-line argument to your program.
(Required) If (and only if) no command-line argument is given, your program should look for the .ishrc
file in the HOME directory. NOTE: the HOME directory is not necessarily the working directory.
(Required) Your program should work properly if the configuration file does not exist or is not readable. It is not an error for the configuration file to not exist or to be unreadable.
We will test your program using both possibilities for the configuration file: command sequences in a file given as a command-line argument and command sequences in the .ishrc
default. If your program does not support reading a configuration file, or does not interpret commands from that file, then it will fail all of our tests. In that unfortunate circumstance, the grade would be reduced substantially.
(Required) Your program should print the commands from the configuration file. That is, immediately after your program reads a command from the configuration file, it should print its "% " prompt and that command to the standard output stream. In that manner your program should generate a transcript that shows each command of the configuration file, followed by the output that results from executing that command. If your program does not print the commands of the configuration file to the standard output stream, then it will be difficult to interpret the transcript. In that unfortunate circumstance, the grade would be reduced substantially.
(Required) Your program should not print the commands from the standard input stream. That is, when your program reads a command from the standard input stream, it should not print that command to the standard output stream.
(Required) Your program should detect and report each programmer error via a call of the assert
macro. A programmer error is one that could not be caused by user input.
(Required) Your program should call the assert
macro to validate the parameters of every function in your program, especially the non-static ones.
(Required) Your program should detect each user error via an if
statement, and should report each user error via a descriptive error message. A user error is one that could be caused by user input.
(Required) Your program should check the result of each attempt to dynamically allocate memory. It should recover from dynamic memory allocation failures as gracefully as possible.
(Required) Your program should print error messages to the standard error stream, not to the standard output stream.
(Required) Your program's error messages should begin with programName:
, where programName
is your program's argv[0]
. Note that the name of your program is not necessarily "ish"; the user might have renamed the executable binary file to something other than "ish".
(Required) After a failed call of a function that sets the errno
variable, your program should call the perror
or strerror
function to print an appropriate error message.
(Recommended) Your program should read each input line by calling the fgets
function.
(Recommended) Implement your lexical analyzer as a deterministic finite state automaton.
(Required) Your program should call the isspace
function to identify white-space characters.
(Recommended) Create some temporary code that prints the token list created by your lexical analyzer. Do not proceed to subsequent phases until you test your lexical analyzer thoroughly.
(Recommended) Test your lexical analyzer by making sure that it handles these example input lines properly:
INPUT LINE | TOKEN ARRAY |
one |
one |
123 |
123 |
one123 |
one123 |
123one |
123one |
@#$%^&*() |
@#$%^&*() |
' |
' |
one two |
one two |
one two |
one two |
one two |
one two |
one > |
one > |
one> |
one > |
>one |
> one |
"one" |
one |
">" |
> (an ordinary token) |
"one two" |
one two |
one"two" |
onetwo |
"one"two |
onetwo |
"one |
ERROR - unmatched quote |
one"two |
ERROR - unmatched quote |
(Recommended) Your program's lexical analyzer should represent tokens so that the difference between quoted and unquoted special characters is adequately captured. For example, these two commands are very different, and that difference should be captured at the lexical analysis phase:
echo one > two echo one ">" two
(Recommended) Your program should do as much validation of the command as possible at the syntactic analysis phase. The more error checking you do during the syntactic analysis phase, the less must be done during the (more complicated) execution phase.
(Recommended) Create some temporary code that prints the command created by your syntactic analyzer. Do not proceed to subsequent phases until you test your syntactic analyzer thoroughly.
(Recommended) Test your syntactic analyzer by making sure that it handles these valid and invalid example input lines properly:
INPUT LINE | VALID/INVALID |
cat |
Valid |
cat file1 |
Valid |
cat < file1 |
Valid |
cat > file1 |
Valid |
cat < file1 > file2 |
Valid |
cat > file1 < file2 |
Valid |
cat file1 > file2 |
Valid |
cat > file2 file1 |
Valid |
< file1 |
Invalid: Missing command name |
cat file1 < |
Invalid: Standard input redirection without file name |
cat file1 > |
Invalid: Standard output redirection without file name |
cat file1 > file2 > file3 |
Invalid: Multiple redirection of standard output |
cat < file1 < file2 |
Invalid: Multiple redirection of standard input |
(Required) Your program should call fflush(NULL)
before each call of fork
to clear all I/O buffers.
(Required) Your program should call the setenv
function to implement the setenv
shell built-in command.
(Required) Your program should call the unsetenv
function to implement the unsetenv
shell built-in command.
(Required) Your program should call the chdir
function to implement the cd
shell built-in command.
(Required) Your program should call exit(0)
to implement the exit
shell built-in command. Or, implement the exit
command by returning 0 from the program's main
function.
(Required) Your program should call the signal
function to install signal handlers.
(Required) Your program should call the alarm
function to control handling of SIGQUIT signals.
(Required) Your program should use the SIG_IGN
and SIG_DFL
arguments to the signal
function, as appropriate.
(Required) Your program should call the sigprocmask
function near the beginning of the main
function to make sure that SIGINT
, SIGQUIT
, and SIGALRM
signals are not blocked.