The following are implementation requirements and recommendations for your programs:
(Required) Your programs must be modular at the function level. Define small functions, each of which does a single well-defined job.
(Required) Your programs must be modular at the source code file level. Define interfaces and implementations, thus splitting your programs into multiple files. As noted in the primary assignment specification page, your ish must consists of a top-level module, a lexical analyzer module, a syntactic analyzer module, and a DynArray module; but it may -- and indeed must -- consist of additional modules.
(Required) Define stateless modules as appropriate. A stateless module is one that does not have any associated data. For example, your Str module from Assignment 2 is a stateless module; it consists of a group of related functions, but does not encapsulate any data. It is fine for a stateless module to declare only a single function in its interface.
(Required) Define abstract objects and abstract data types as appropriate, thus encapsulating data structures with functions.
(Required) 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 armlab.
(Required) Your programs must write the lines that it reads from stdin. That is, immediately after your programs read a line from stdin, they must write that line to stdout. If your programs do not do that, then it will be difficult for you and your grader to interpret the output of your programs when stdout and stderr are redirected to a file.
(Required) Your programs must make no assumptions about the maximum number of characters in each line read from stdin, the maximum length of the name of the given command, the maximum length of any of the command-line arguments of the given command, or the maximum number of command-line arguments of the given command.
(Recommended) Define the readLine function given in the dfa.c precept example program. Call that function to read each line from stdin.
(Required) ishlex must write each token to stdout on a distinct line. The token must be preceded by "Token: ". If the token is an ordinary token, then it must be followed by " (ordinary)". If the token is a special token, then it must be followed by " (special)".
(Required) ishsyn must write each command to stdout using this format:
Command name: commandname Command arg: argument Command arg: argument ... Command stdin: filename Command stdout: filenameThe "Command name" line always must be present. There must be one "Command arg" line for each command-line argument. The "Command stdin" line must be present if and only if the command's
stdinis redirected. Similarly the "Command stdout" line must be present if and only if the command'sstdoutis redirected.
(Required) Your programs must call fflush(stdout) immediately after writing the input line to stdout. More generally, your programs should call fflush(stdout) immediately after writing any data to stdout. Explanation: The stdout stream is buffered; the stderr stream is not. So if (1) both stdout and stderr are bound to the same destination, (2) your program writes X to stdout and then writes Y to stderr, and (3) your program does not call fflush(stdout) immediately after writing X to stdout, then under some circumstances X could appear in the destination after Y.
(Required) A programmer error is one that could not possibly be caused by user input. Informally, a programmer error is an error that should never happen. Your programs must detect each programmer error via an if statement, report the error via a descriptive error message written to stderr, and then exit with EXIT_FAILURE as the status code.
(Required) Invalid function parameter is a special-case programmer error. Your programs must call the assert macro to handle that error. Of course the assert macro detects the error, reports the error by writing a message to stderr, and exits.
(Required) A user error is one that could be caused by user input. Informally, a user error is one that could happen. Your programs must detect each user error via an if statement, and report the error via a descriptive error message written to stderr. Then your programs must reject the offending input line and continue.
(Required) In general heap exhaustion (that is, a failed call of malloc, calloc or realloc) could be caused by user input, and so must be treated as a user error. However in these particular programs (1) there is no reasonable user input that could cause heap exhaustion, and (2) if heap exhaustion does occur, then there is no reasonable way for your programs to recover from it and continue. So your programs must treat heap exhaustion as a programmer error. That is, your programs must detect heap exhaustion via an if statement, report the error via a descriptive error message written to stderr, and then exit with EXIT_FAILURE as the status code.
(Required) Your programs' error messages must begin with "programName: ", where programName is argv[0]. Note that the names of your programs are not necessarily ishlex, ishsyn, and ish; the user might have renamed the executable binary files to something else.
(Required) After each failed call of a function that sets the errno variable, your programs must call the perror or strerror function to write an appropriate error message to stderr.
(Recommended) Implement your lexical analyzer as a deterministic finite state automaton.
(Required) Your lexical analyzer must call the isspace function to identify white-space characters.
(Required) Your lexical analyzer must represent tokens so that the difference between special and ordinary tokens 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) Test your lexical analyzer by making sure that your ishlex program handles these example test cases properly:
INPUT OUTPUT echo Token: echo (ordinary) echo 123 Token: echo (ordinary) Token: 123 (ordinary) echo one123 Token: echo (ordinary) Token: one123 (ordinary) echo 123one Token: echo (ordinary) Token: 123one (ordinary) echo @#$%^&*() Token: echo (ordinary) Token: @#$%^&*() (ordinary) echo ' Token: echo (ordinary) Token: ' (ordinary) echo one two Token: echo (ordinary) Token: one (ordinary) Token: two (ordinary) echo one two Token: echo (ordinary) Token: one (ordinary) Token: two (ordinary) echo one > Token: echo (ordinary) Token: one (ordinary) Token: > (special) echo one> Token: echo (ordinary) Token: one (ordinary) Token: > (special) echo>one Token: echo (ordinary) Token: > (special) Token: one (ordinary) rm one Token: rm (ordinary) Token: one (ordinary) echo "one" Token: echo (ordinary) Token: one (ordinary) echo ">" Token: echo (ordinary) Token: > (ordinary) echo "one two" Token: echo (ordinary) Token: one two (ordinary) echo one"two" Token: echo (ordinary) Token: onetwo (ordinary) echo "one"two Token: echo (ordinary) Token: onetwo (ordinary) echo "one ./ishlex: unmatched quote echo one"two ./ishlex: unmatched quote
(Recommended) Test your syntactic analyzer by making sure that your ishsyn program handles these example test cases properly:
INPUT OUTPUT pwd Command name: pwd pwd > file1 Command name: pwd Command stdout: file1 cat file1 Command name: cat Command arg: file1 cat < file1 Command name: cat Command stdin: file1 cat file1 > file2 Command name: cat Command arg: file1 Command stdout: file2 cat < file1 > file2 Command name: cat Command stdin: file1 Command stdout: file2 cat > file1 < file2 Command name: cat Command stdin: file2 Command stdout: file1 cat > file2 file1 Command name: cat Command arg: file1 Command stdout: file2 < file1 ./ishsyn: missing command name cat file1 < ./ishsyn: standard input redirection without file name cat file1 > ./ishsyn: standard output redirection without file name cat file1 > file2 > file3 ./ishsyn: multiple redirection of standard output cat < file1 < file2 ./ishsyn: multiple redirection of standard input rm file1 file2 Command name: rm Command arg: file1 Command arg: file2
(Required) Your ish must call fflush before each call of fork to flush the buffers of all open streams. That is, your ish must call fflush(stdin) and fflush(stdout) before each call of fork.
(Required) Your ish must call the setenv function to implement the setenv shell built-in command.
(Required) Your ish must call the unsetenv function to implement the unsetenv shell built-in command.
(Required) Your ish must call the chdir function to implement the cd shell built-in command.
(Required) Your ish must call exit(0) to implement the exit shell built-in command. Or, your ish must implement the exit command by returning 0 from the program's main function.
(Required) Your ish must not call the system function.
(Required) Your ish must call the signal function to install signal handlers.
(Required) Your ish must call the alarm function when handling signals, as appropriate.
(Required) Your ish must use the SIG_IGN and/or SIG_DFL arguments to the signal function, as appropriate.