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 FC010.
(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
stdin
is redirected. Similarly the "Command stdout" line must be present if and only if the command'sstdout
is redirected.
(Required) Your programs must call fflush(stdout)
immediately after writing the input line to stdout
. Doing so makes sure that the input line is written to stdout
before any error messages related to that line are written to stderr
-- thus avoiding sequencing problems when both stdout
and stderr
are redirected to the same file. 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) 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(NULL)
before each call of fork
to clear all I/O buffers.
(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.
(Required) Your ish
must call the sigprocmask
function to make sure that SIGINT
and SIGALRM
signals are not blocked.