Princeton University
COS 217:  Introduction to Programming Systems

Assignment 6:  ish - An Interactive UNIX Shell

Purpose

The purpose of this assignment is to help you learn about UNIX system calls.  It will also give you ample opportunity to define interfaces, implementations, and ADTs; in that sense the assignment is a capstone for the course.

Background

A UNIX shell is a program that makes the facilities of the operating system available to interactive users.  There are several popular UNIX shells:  sh (the Bourne shell), csh (the C shell), and bash (the Bourne Again shell) are a few.

Your Task

Your task in this assignment is to create a program named ishish should be a minimal but realistic interactive UNIX shell.

Initialization and Termination

When first started, ish should read and interpret lines from the file ~/.ishrc, provided that the file exists and is readable.  Note the the file name is .ishrc (not ishrc), and that it resides in the user's HOME directory (not the current directory).  Typically, the ~/.ishrc file contains commands to specify the terminal type and environment.

To facilitate your debugging and our testing, ish should print each line that it reads from ~/.ishrc immediately after reading it.  ish should print a percent sign and a space (% ) before each such line.

ish should terminate when the user types Control-D or exit.

Interactive Operation

After startup processing, ish should read lines from the terminal, prompting with a precent sign and a space (% ).  Specifically, ish repeatedly should perform the these actions: 
  • Read a line from standard input.
  • Lexically analyze the line to form a list of tokens.
  • Syntactically analyze (i.e. parse) the token list to form a pipeline of commands.
  • Execute the pipeline.
  • Lexical Analysis

    Informally, a token should be a word.  More formally, a token should consist of a sequence of non-whitespace characters that is separated from other tokens by whitespace characters.  There should be two exceptions: ish should assume that no line of standard input is longer than 1024 characters.  If a line of standard input is longer than 1024 characters, then ish need not handle it properly; but it should not crash.

    Syntactic Analysis

    A command should be a sequence of tokens, the first of which specifies the command-name.  

    The '<' token should indicate that the subsequent token is the name of a file.  ish should redirect the command's standard input from that file.  It should be an error to redirect a command's standard input more than once.  

    The '>' token should indicate that the subsequent token is the name of a file.  ish should redirect the command's standard output to that file.  If the file does not already exist, then ish should create it; if the file does already exist, then ish should destroy its contents and rewrite the file from scratch.  ish should set the permissions of the new file by examining the user's umask setting.  It should be an error to redirect a command's standard output more than once.  

    A pipeline should be a sequence of one or more commands separated by '|' tokens.  ish should redirect the standard output of the command that precedes the '|' token to the standard input of the command that follows.  It should acceptable to redirect the standard input of the first command of a pipeline via the '<' token; it should be erroneous to redirect the standard input of any other command of a pipeline via the '<' token.  Similarly it should be acceptable to redirect the standard output of the last command of a pipeline via the '>' token; it should be erroneous to redirect the standard output of any other command of a pipeline via the '>' token.

    Execution

    If the command is an ish built-in, then ish should execute it directly (i.e. without forking a child process).  ish should interpret four shell built-in commands:
    setenv var [value] If environment variable var does not exist, then create it.  Set the value of var to value, or to the empty string if value is omitted.  Note:  Initially, ish inherits environment variables from its parent.  ish modifies the value of an existing environment variable or creates a new environment variable via the setenv command.  ish can set the value of any environment variable; but the only environment variables that it uses itself are HOME and PATH.
    unsetenv var Destroy the environment variable var.  
    cd [dir] Change ish's working directory to dir, or to the HOME directory if dir is omitted.
    exit Exit ish.

    Note that those built-in commands should neither read from standard input nor write to standard output.  Thus ish need not support file redirection with its built-in commands.  Nor does ish need to support built-in commands within pipelines.

    If the command is not an ish built-in, then ish should consider the command-name to be the name of a file that contains executable binary code.  ish's first task should be to determine the filename to be passed to the execv or execl system call.  How it should do so depends upon whether the given filename contains a slash (/) or not:

    After ish determines the appropriate filename, ish should fork a child process and pass the filename, along with its arguments, to the OS using the execv or execl system call.  (ish should not use the execvp or execlp calls; ish is responsible for searching the PATH.)  If the attempt to execute the file fails, then ish should print an error message indicating the reason for the failure.

    ish should print its prompt for the next standard input line only when all commands in the current pipeline are completed.  If any command of a pipeline fails to execute, ish should simply lets all of the other commands execute to completion.

    Process Control

    All children processes forked by ish should run in the foreground; ish need not support background process control.  However, the user must be able to kill the current children processes using Control-C.  Control-C should not kill ish itself.

    Error Handling

    ish should handle an erroneous line gracefully by rejecting the line and writing a descriptive error message to standard error.  ish should handle all user errors; it should be impossible for the user's input to cause ish to crash.

    History Mechanism (for extra credit)

    ish should support a primitive history mechanism that includes the history built-in command, and the ability to re-execute a past command by typing !prefix. (ish need not support editing of the previous command.)  Note that the history command should write data to standard output, and so ish should allow it to be used with file redirection or within a pipeline.  Also note that ish should allow use of the !prefix syntax with file redirection or within a pipeline.

    Logistics

    An implementation requirements and recommendations page is available.

    Develop on arizona.  Use Emacs to create source code.  Use the "make" tool to automate the build process.  Use gdb to debug.

    An executable version of the assignment solution is available in /u/cs217/Assignment6/sampleish.  You should use it to resolve any issues concerning the desired functionality of ish.

    The file /u/cs217/Assignment6/sample_ishrc.txt is a sample initialization file.  It also serves as a minimal test case.  The expectation is that you will devote substantial effort to creating and running additional test cases.  

    You should create a makefile.  When the user types "make" (with no command-line arguments), your makefile should instruct the make program to build your ish program.

    You should create a readme file.  As always, the readme file should contain your name and any information that will help us to grade your work in the most favorable light.  In particular you should describe all known bugs in your readme file.

    Submit your work electronically via the command u/cs217/bin/submit 6 makefile readme sourcecodefiles

    Grading

    Your work will be graded on correctness, understandability, and design.  An important aspect of design is proper modularity through the definition and use of interfaces, implementations, and ADTs.   To encourage good coding practices, you will lose points for any warning messages generated by gcc during the compilation of your work.

    Your solution is due by 11:59 P.M. on the Dean's Day.  That is a hard deadline; no work will be accepted after that time.