Princeton University
COS 217: Introduction to Programming Systems

Assignment 7: Supplementary Information

The following are implementation requirements and recommendations for ish:


General

(Required) Write your program so it is modular. Create small functions, each of which does a single well-defined job. 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. Doing so will earn you a better grade. More fundamentally, doing so will help you to write a better program quicker.

(Recommended) See the web page http://www.opengroup.org/onlinepubs/7908799/xsh/unistd.h.html for excellent documentation on the UNIX system calls. You may find that documentation more convenient than the "man" pages.

(Required) Use calls to the assert() macro to validate the parameters of every function in your program. Also use a call to assert() after each call of malloc(), calloc(), or realloc() to make sure the dynamic memory allocation was successful.

(Recommended) Use the DynArray ADT (from early precepts). The source code is available through the course web pages.

(Required) Make sure that your ish prints error messages to the standard error stream, not to the standard output stream.

(Required) Make sure that your ish's error messages begin with "programName: ", where programName is ish's argv[0].

(Required) After a failed call of a function that sets the errno variable, call the perror() or strerror() standard function to print an appropriate error message to the standard error stream.

(Required) Make sure that your ish interprets commands from the .ishrc file when it is first launched. We will test your ish by repeatedly copying command sequences to the .ishrc file and launching your ish. 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 poor.

(Required) Make sure that your ish looks for the .ishrc file in the HOME directory, not in the working directory.

(Required) Make sure that your ish works properly if the .ishrc file does not exist or is not readable.

(Required) Your ish should print the commands from .ishrc. That is, immediately after your ish reads a command from the .ishrc file, it should print that command to the standard output stream. In that manner ish 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.

(Required) Your ish should not print the commands from the standard input stream. That is, when your ish reads a command from the standard input stream, it should not print that command to the standard output stream.


Lexical Analysis

(Recommended) Read each input line using the fgets() function.

(Recommended) Implement your lexical analysis phase as a deterministic finite state automaton.

(Recommended) Create some temporary code that prints the token list created by the lexical analysis phase. Do not proceed to subsequent phases until you test your lexical analysis phase thoroughly.

(Recommended) Test your lexical analysis phase 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

(Required) Make sure that your lexical analysis phase 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

Syntactic Analysis

(Recommended) Do as much validation of the command as you can at the syntactic analysis phase. The more error checking you do in 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 the syntactic analysis phase. Do not proceed to subsequent phases until you test your syntactic analysis phase thoroughly.

(Recommended) Test your syntactic analysis phase 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

Execution

(Required) Call fflush(NULL) before each call of fork() to clear all I/O buffers.

(Required) Use the setenv() UNIX system call to implement the setenv shell built-in command.

(Required) Use the unsetenv() UNIX system call to implement the unsetenv shell built-in command.

(Required) Use the chdir() UNIX system call to implement the cd shell built-in command.


History (for Extra Credit)

(Required) Execute the history command in a child process (alias subshell) so its standard output can be redirected.

(Required) Do not store an input line in the history list if it consists entirely of whitespace characters.

(Required) After expanding an input line containing !commandprefix, print the expanded input line before executing it.

(Required) Match !commandprefix with the most recently executed command that has commandprefix as a prefix.

(Required) Consider all characters that occur in the input line between an exclamation point and a terminator character to be the commandprefix which should be expanded. The terminator characters are space, tab, newline, null, less-than, and greater-than.

(Required) When expanding !commandprefix, consider the double quote character to be an ordinary character, devoid of any special meaning.

(Required) When expanding !commandprefix, if commandprefix is empty, then expand it to nothing.

(Required) Handle multiple instances of !commandprefix within the input line.

(Required) If an input line containing !commandprefix cannot be expanded (that is, if no previous command has commandprefix as its prefix), then print an error message to stderr, and do not store that input line in the history list.

(Recommended) Test your history mechanism using a variety of command sequences involving use of the exclamation point, double quote, less-than, and greater-than characters. For example, make sure it handles this sequence of input lines properly:

INPUT LINE INPUT LINE AFTER EXPANSION
echo one two echo one two
!echo echo one two
echo one two echo one two
!ech>junk echo one two>junk
!ec echo one two>junk
echo one two echo one two
!ec three !e four echo one two three echo one two four
echo one two echo one two
echo three!ec four echo threeecho one two four
echo one two echo one two
!"ech" Error - Event not found
!ech"three" Error - Event not found
!ech "!ech three" echo one two "echo one two three"
! echo echo
!