Commando
Volume Number: 6
Issue Number: 7
Column Tag: Programmer's Workshop
Commando Programming 
By W. G. Powell, Salt Lake City, UT
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
With its combination of a programmable text processor, a mouse-based
interactive text editor and a general command interpreter, the MPW shell is one of the
most powerful and flexible development environments available on any computer. One
feature of this environment is particularly Macintoshish: the ability to replace the
text of a shell command with a more graphic modal dialog. The Commando tool,
supplied with MPW version 2.0 and later, controls such dialogs according to behaviors
specified by the programmer in resources of type ‘cmdo’.
In this article I’d like to introduce some tricks and techniques I’ve found useful
for building these commando interfaces. Since commandos are fairly flexible, some
examples presented here use commando as a means of convenient data entry beyond the
direct application to command generation. A previous article on the MPW shell showed
the effects of launching applications from within shell command scripts; in this one
we’ll see the used of Commando from within shell scripts.
Commands via Commando
The conventional use of commando is to replace the typing of a command and its
arguments with a more user-friendly graphical equivalent. The commando dialog is an
intuitive way to illustrate the modes of an action and the choices available to the user.
Commando interfaces are helpful in two ways. First, presentation of all the options on
the screen relieves the user of the burden of memorizing syntax and options. Second,
the use of commando dependency rules (discussed later) can increase the correctness
and success of commands initiated by the user, thereby enhancing the user’s
productivity.
Commando interfaces are available for the commands built into the shell
application itself. A commando interface may also be added to any MPW tool or shell
script (TEXT) file with ‘cmdo’ resources. A small shell script command with a few
simple options will serve as a basic example of designing commando interfaces.
Example 1: A Scratch File Generator
Shell scripts of moderate complexity frequently require scratch files or windows
for intermediate results and working space. As I wrote more shell scripts, I became
less and less confident that I could code unique scratch file names into scripts, and
feared I would ultimately overwrite some critical file by accident. A command which
opens a scratch file and guarantees that it has a unique name was the obvious solution.
The shell script “NewTempFile”, shown in the listings which follow, performs this
function.
The command and its behavior
The NewTempFile command script works together with a few supporting
facilities. A special directory is created, where all temporary files will reside. A
shell variable defined in the MPW “Startup” or “UserStartup” scripts maintains the
path to this directory. By convention, a temporary or scratch file is no longer needed
when the command which creates the file terminates. When the user Quits the MPW
shell, no commands are still active, so the “Quit” script contains commands to delete
the contents of the temporary file directory. The appropriate commands are shown in
the listings.
The first half of the script file for the NewTempFile command is a loop which
reads, interprets and checks the command line arguments. The second half of the file is
a loop which generates a unique file name and opens the file. The file name is formed
by appending a number to a base name. A default base name is supplied unless the user
provides an alternative as a command line argument. The first trial name is created by
appending the count of files in the temporary file directory to the base name. If a file
with that name already exists, a new trial name is formed by incrementing the number
part by one. The incrementing process continues until an unused name is found. This
method has always worked quickly for me in practice, but pathological cases exist
which can cause a large number of trials before success. Readers are welcome to try
better schemes.
Several command line options can be used to control the behavior of the command.
As mentioned earlier, the user can specify a base name for the file on the command
line. Normally, when a unique name is successfully found, it is opened as the active
window; the -t option on the command line causes the file to be opened as the target
window. NewTempFile usually produces no standard output, although error messages
(if any) go to the Standard Diagnostic stream. Options -e or -q will echo the name of
the new file on the Standard Output stream. Option -q will quote the name if it contains
spaces or shell special characters, -e never quotes, and at most one of -e and -q may
be specified (you can’t have things both ways!).
Examining the script in detail, one may wonder why I test the file name with the
“Exists” and then use “New” to open the file when a name is found. Since the “New”
command must also test for the existence of the file anyway, and returns a nonzero
shell {Status} if the file exists, it looks like I am doing the same test twice. Actually,
my first attempt at writing NewTempFile tried the command “New” with each trial
name, using a {Status} value 2 to indicate a need for incrementing and trying again.
Careful testing uncovered the flaw in this technique. Under “ordinary” use the script
worked fine, but a test where I place the command inside an infinite loop failed. The
“New” command returns a {Status} value of 2 when the command fails because a file
of the same name already exists, but it also returns a value of 2 when too many
windows are already open. Once I opened enough windows to reach my system’s limit,
NewTempFile became an inefficient code to count to very high numbers instead of a
convenient way to open new files. Thus the slightly more redundant, but less
ambiguous test with “Exists” was added.
I digress to present this poignant example of one of my failures because it
illustrates a common problem with devising shell command scripts. The description in
the MPW reference manual of command status results is quite terse, and sometimes
does not make clear all the cases which may produce the particular status value. The
nature of the status results must be fully defined when they are used to control a
script. As in any software development, successful scripting requires careful
validation through testing.
The -e and -q options illustrate another useful detail of shell programming
worthy of a short digression. After calling NewTempFile within a script, we will
usually want to get the name of the new window for use in subsequent commands. Here
are two code fragments that set the variable {WName} to the name of a window opened
by NewTempFile.
#1
Set WName ‘NewTempFile -q ‘
or
#2
NewTempFile
If {Status} != 0
Echo “Error”>>Dev:StdErr
Else
Set WName “{Active}”
End
If NewTempFile fails, the first example sets {WName} to the empty string, and
the second does not set {WName} at all. These two code snippets illustrate what I call
the MPW Uncertainty Principle. You can use the standard output of a command in a
script (via command substitution), OR you can use the status value returned by the
command, but you can’t use both. In an article on MPW scripting in MacTutor [April
87, p. 37], F. Alviani presents several shell scripts with constructs like:
#3
Set SomeVar “`Request ‘some text’`”
If {Status} != 0
etc.
The intention was apparently to use the “If” statement to take different actions
depending upon whether the user hit the “OK” or “Cancel” button in the “Request”
dialog. In reality, if the Request is cancelled, there is no standard output and the
{Status} variable is set to value 2. Then the set command (successfully) sets the
variable {SomeVar} to the empty string. Finally, the {Status} of 0 returned by the
successful “Set” command is the value tested in the “If” expression. So the “If” sees
a status of 0 no matter what status value is returned by the “Request” command. This
script used the standard output of “Request”, so it couldn’t use the status of the
“Request” command.
Now that we have a command, and have defined its command line arguments, let’s
look in detail at the construction of its commando interface.
The command’s commando
In discussing the commando interface for NewTempFile, I will refer frequently to
the Rez input for the ‘cmdo’ resource (file NewTempFile.Cmdo.R in the listings). This
discussion will lead sequentially through the Rez code, so you may want to mark the
page and follow along there. The appearance of the resulting dialog is shown in Figure
1.
The dialog of Figure 1 will be generally familiar to everyone who has already
used the Commando tool. The dialog is divided into three rectangular regions. The
uppermost “options” rectangle contains the programmed controls particular to this
command and defined in the ‘cmdo’ resource. The middle rectangle shows the
equivalent command and argument text that fits the options selected by the user in the
dialog; this text is changed automatically by Commando as the user changes controls in
the options area. The third rectangle, labelled “Help”, shows descriptive text about
the command and its options; the help text is changed to describe the appropriate
options as the user clicks on the various controls. The placement and action of the
controls, the command line options corresponding to the various controls, and the help
text are all summarized in the ‘cmdo’ resource.
The Rez input for the ‘cmdo’ resource begins with the type and resource ID
number of the resource. The specific resource data are then contained within braces.
The resource description begins with an integer representing the height of the dialog
box in pixels, and the default string of help text that appears in the Help box when no
controls are being clicked by the user. The dialog box width is fixed at 480 pixels and
the MPW Reference Manual recommends that the height be less than 295 pixels to
insure visibility on the original size Macintosh screen. Since only the programmable
controls in the options area vary in size, the options determine the necessary size of
the dialog window. Letting h represent the height specified in the Rez description, the
rectangle drawn around the options area in the dialog is {7, 5, h - 105, 475}. After
the default help text, another set of braces encloses input for the variable number of
items in the options area of the dialog.
The first item in the NewTempFile commando options is a set of radio buttons,
which appear in the upper left of the dialog in Figure 1. This pair of radio buttons
allows the user to toggle between causing the temporary window to open as the active
or target window. For each button in the set, the Rez input lists the bounds rectangle
for the control and its label in the dialog window, the label text to show in the dialog,
the command line option corresponding to the control (if any), the help text explaining
the control and its option, and the initial setting of the control. Note that when the
button labelled “Target” is selected, the -t option is added to the command line, but
when the “Active” button is selected, no option is added since this is the default case.
Entering the empty string in the command line option field for this button signifies the
absence of corresponding text on the command line. The behavior of radio buttons