JShell  (Andrew Van Kirk)

Version 1.0

A Unix-Like Shell Written in Java

 

 

 

Programmer’s Guide

 

Welcome to Jshell! This is the Programmer’s Guide.  If you like running programs a lot, you may want the User's Guide.  This is where you learn how to program JShell.

 

 

JShell Structure

The JShell Program can be broken up into three main parts:

 

JShell:                       This is the .class file that is actually run to start the program.  It handles user input and runs the commands, as well as does some basic error handling.

JShellParser:          This class parses the user input line.  Because there is only one instance of this parser instantiated by JShell, all of its fields are private.  This is VERY important, because JShellParser not only holds the map of Strings to Command classes, but also holds such data as the current directory and the map of environment variables.  Though this may seem awkward, it really works out quite elegantly.  For example, because all commends that deal with traversing the directory structure, such as “cd” and “pwd” are constructed with the same directory stack from JShellParser, and change to the current directory by one command changes the current directory in all the other commands.

Commands:             The heart and soul of JShell.  Each command runs itself and returns its output, if any, on the StringBuffer that it is passed when method runCommand(StringBuffer) is invoked.  Every command inherits from Command, but there is also some deeper inheritance. See the Class Documentation for a clear picture of this inheritance.

 

 

Adding to JShell

There are several rules that should be followed when adding to JShell

1)     DO NOT modify JShell.java.  It is not necessary unless major modifications are being made to program flow.

2)     Add only one line to JShellParser, in method void putCommands(), that adds your command and the corresponding user input string to the map of commands.

3)     Rule 2 may be broken if you are implementing a new series of command that required the same data to be accessible to all commands in the series.  The directory transversing commands, as described above, are such a series.  In that case, add a field to JShellParser that will hold this date, and construct all commands in this series with that field.

4)     If you’re doing a task in your command that seems like it might have been done before, check the classes that you inherit from to see if that method is already implemented.  For example, if your command needs to read in a series of filenames and run on those files, then you can use the workWithFiles method in command to read in the files, and then put the code to deal with your file in makeOutput().  This not only saves redundant code, but you can also be sure that the file names are being dealt with correctly.    

 

The rules above hint at how simple it is to add commands in JShell.  It takes just three easy steps.

1)     Implement your command in its own *.java file, inheriting from the lowest parent possible.

2)     Add your command to the map in JShellParser

3)     Compile

4)     Use your new command.  This really means test it.

 

Two large test files are included. 

testfile.in

testfile2.in

Feel free to add input to those files to test your commands.  Also make sure you didn’t break anything.

 

Notes:

It is always a good idea to call the bool doPipedInput() that is in Command (and thus accessible to all commands).  If your command doesn’t take piped input, this call will make sure that the StringBuffer to which you will write your output is empty (which it might not be if the user accidentally pipes data to a command that doesn’t take piped data).

 

If you use the methods inherited from Command, then exceptions generated by those actions will be caught.  Otherwise, catch your own.

 

Don’t forget to append the String variable sep for line separators instead of “\”

 

 

Problems:

No program is perfect, JShell is no exception.  Here are some of the known issues:

 

printenv does not meet spec: right now printenv takes a string as an argument, and looks up the environment variable that corresponds to that string.  Spec is to print all environment variables out.  The fix would be simple…but it’s too late now.

 

Directory commands not portable: through most of the code, when system dependent items came up, the System.getProperty() values were used to achieve platform independence.  However, all the directory commands rest on the fact that “/” in Unix is the root of the file system.  Drive letters, which are roots in Windows, are not supported.

 

Basic file system shortcuts are not implemented: Though cd .. will succeed, it doesn’t work (the path returned is “oldpath/..”, because .. exists as a file in Unix.  None of the standard shortcuts “.”, “..”, “~”, etc. will work.

 

I’m sure that’s not all.  In fact, I’m really feeling like I forgot something.  But I can’t remember.

 

 

Wanna Program:

Good places to start on JShell would be

·        any of the problems above

·        more commands

·        make prompt dynamic, dependent on user and current directory

·        tab completion

·        up arrow cycle through history

 

 

Thank you for using JShell, v1.0

 

Last modified Oct. 22, 2002