The following are implementation requirements and recommendations for your shell program:
(Required) Write your program so it is modular at the function level. Create small functions, each of which does a single well-defined job.
(Required) Write your program so it is modular at the source code file level. Define interfaces and implementations, thus splitting your program into multiple files. Define either abstract objects or abstract data types as appropriate. Encapsulate data structures with functions.
(Required) Call the assert
macro to validate the parameters of every function in your program, especially the non-static ones.
(Required) Check the result of each attempt to dynamically allocate memory. Recover from dynamic memory allocation failures as gracefully as possible.
(Recommended) 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) Make sure that your program prints error messages to the standard error stream, not to the standard output stream.
(Required) Make sure that your program's error messages begin with programName:
, where programName
is your program's argv[0]
.
(Required) After a failed call of a function that sets the errno
variable, call the perror
or strerror
function to print an appropriate error message to the standard error stream.
(Required) Make sure that your program interprets commands from the .ishrc
file when it is first launched. We will test your program by repeatedly copying command sequences to the .ishrc
file and launching your program. If your program does not interpret commands from that file, then it will fail all of our tests. In that unfortunate circumstance, the grade would be penalized substantially.
(Required) Make sure that your program looks for the .ishrc
file in the HOME directory, not in the working directory.
(Required) Make sure that your program works properly if the .ishrc
file does not exist or is not readable.
(Required) Your program should print the commands from .ishrc
. That is, immediately after your program reads a command from the .ishrc
file, it should print that command to the standard output stream. In that manner your program should generate a transcript that shows each command of .ishrc
, followed by the output that results from executing that command. If your program does not print the commands of .ishrc
to the standard output stream, then it will be difficult to interpret the transcript. In that unfortunate circumstance, the grade would be penalized 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.
(Recommended) Read each input line using the fgets
function.
(Recommended) Implement your lexical analyzer as a deterministic finite state automaton.
(Required) 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
|
one two
|
one
|
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) Make sure your lexical analyzer represents 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 must be captured at the lexical analysis phase:
echo one > two echo one ">" two
(Recommended) Do as much validation of the command as you can 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) Call fflush(NULL)
before each call of fork
to clear all I/O buffers.
(Required) Call the setenv
function to implement the setenv
shell built-in command.
(Required) Call the unsetenv
function to implement the unsetenv
shell built-in command.
(Required) Call the chdir
function to implement the cd
shell built-in command.
(Recommended) Call the strtol
function to convert the exit
command's argument from a string to an integer.
(Required) Call the signal
and alarm
functions, as appropriate.
(Required) Use the SIG_IGN
and SIG_DFL
arguments to the signal
function, as appropriate.
(Required) Call the sigprocmask
function near the beginning of the main
function to make sure that SIGINT
, SIGQUIT
, and SIGALRM
signals are not blocked.