December 95 - MPW Tips and Tricks: ToolServer Caveats and Carping
MPW Tips and Tricks:
ToolServer Caveats and Carping
Tim Maroney
MPW comes with dozens of useful tools and scripts. They're handy for a lot of
things besides programming -- or would be, if you were willing to keep the
MPW Shell open all the time, and if they weren't based on command lines.
Fortunately, the Shell is not the only way to use them: a small application
known as ToolServer makes it possible to run MPW commands in a standalone mode.
You can write double-clickable MPW scripts, give MPW commands from AppleScript,
and write front ends to tools in high-level programming languages.
Using ToolServer isn't exactly like using the MPW Shell. There are caveats if
you want to write scripts and tools that will work in both environments. We'll
first take a look at these issues and then explore how to package commands for
use with ToolServer.
MODULARITY AND FACTORING
Shell scripting languages such as sh and csh in UNIX
reg., as well as
MPW, have always taken a rather cavalier approach to code organization. Most
configuration is achieved with a global namespace of environment variables.
This is a problem with ToolServer, because you don't want to load your entire
set of MPW startup scripts every time you run a command. Even if you wanted to,
you couldn't -- ToolServer doesn't have text editing, menu bar customization,
or other user interface elements of the MPW Shell, so it's missing several
built-in commands. Your existing startup scripts won't work, and some utility
commands may also fail.
Three principles from structured software design are useful here:
- Separate user interface code from core code.
- Use the "include" mechanism to provide modularity.
- Reduce dependencies between modules.
Let's take a concrete example.
Many of us cut our teeth as programmers on UNIX. Initiates of this brilliant
but byzantine operating system tend to grow fond of its command set, in much
the same way that cabalists become attached to bizarre metaphysical formulas
purporting to explain the universe. A UNIX wizard's MPW startup script usually
contains a list of aliases to translate between UNIX and MPW: Alias ls Files,
Alias cp Duplicate, and so on. These commands are then used in all the wizard's
utility scripts as well. This creates a problem with ToolServer: it can't use
these startup scripts because they also customize the user interface with
commands like AddMenu and SetKey. Without the aliases, though, the utility
scripts won't run.
One solution to this problem combines the first two principles listed above.
First, separate the aliases from the user interface setup code, yielding two
different startup files. Both files are invoked by the MPW Shell startup
process but neither is invoked at ToolServer startup. Second, instead of
assuming a particular global configuration, make each utility script explicitly
include whatever setup files it may require. MPW's analog of the
#include directive of C is Execute, which executes a file in the current
namespace.
We can apply common C bracketing conventions to avoid multiple inclusion of the
same file. Assuming that our UNIX wizard has split off his or her aliases into
a file named UNIXAliases, a script using these aliases would start -- after the
header comment -- as follows:
if {__UNIXALIASES__} == ""
execute UNIXAliases
end
The
script file UNIXAliases would set the variable __UNIXALIASES__ to something
other than the empty string, and decline to execute itself again if it had
already been executed, like so:
if {__UNIXALIASES__} == ""
set __UNIXALIASES__ "true"
... # the aliases go here
end # __UNIXALIASES__
A
different solution to the same problem involves the third principle, reducing
dependencies between modules. Utility scripts don't really need to use csh
commands, after all: the aliases are there mostly so that the wizard can type
them into the MPW Shell, his or her fingers having long ago locked into an
inflexible pattern of TTY interaction. If scripts don't assume the availability
of a different command set -- that is, if they stick with the MPW command
names -- the aliases need not be included at all.
Independence is a good idea for another reason: you may give your ToolServer
scripts to other people at some point in your long and happy life. The more
your commands depend on the global environment, including ToolServer startup
files, the more likely they are to conflict with another user's environment.
INPUT AND OUTPUT
ToolServer implements most of the MPW Shell's I/O system, which is based on the
stdio library and UNIX-style redirection. However, it doesn't read keyboard
input or display text output. All of its I/O channels are ultimately files,
pipes, or the pseudodevice Dev:Null.
The only mechanisms for interacting with the user in ToolServer are commands
like Alert and Confirm that display dialog boxes, and interface tools you write
yourself. Even these must be used with caution, since ToolServer can run
remotely over a network, and hanging a server machine by bringing up a dialog
box is often regarded as undesirable.
It helps to separate user interface code from core code, as already discussed.
Commands you intend to run with ToolServer should not have a user interface:
they should perform an action that's completely specified by their command
line. An outermost user interface script can present choices to the user, then
invoke an innermost command that has no user interface. The outermost script is
just for ToolServer; the inner script or tool is suitable for both ToolServer
and the MPW Shell.
You can detect when a command is running under ToolServer and squelch its user
interface by looking at the environment variable BackgroundShell. This is the
empty string when running under the MPW Shell, but it's nonempty under
ToolServer. Most user interactions in MPW commands are just confirmation
alerts, so if execution reaches a Confirm command and BackgroundShell is set,
assume that the user would answer "no." All commands that require confirmation
should support the -y and -n options, which provide answers on the command line, and these options should be
provided when the commands are used from ToolServer.
Some MPW commands, such as Make and Backup, write output to the Worksheet, and
the user then selects and executes the output. This model doesn't apply to the
ToolServer environment since it has no Worksheet. The easiest solution is to
redirect the command output to a temporary file, execute that file, and then
delete it. This is less selective than using the Worksheet, which allows the
user to decide which lines to execute. If selectivity is important, you can
write a command that presents the lines of output to the user and allows them
to be independently accepted or rejected.
We don't live in the best of all possible worlds, St. Thomas Aquinas and Dr.
Pangloss to the contrary, and so commands often return errors. These generate
text that's directed to the standard error channel. In the MPW Shell, error
text goes to the frontmost window by default, but in ToolServer, the default is
a file named command.err in the folder containing the command file. This is
very antisocial behavior, especially since commands invoke other commands and
the error file could wind up buried at some arbitrary-seeming place in your
folder tree. Redirect the standard error channel to save yourself from
Sisyphean levels of frustration whenever something goes just a little bit
wrong.
There are two ways to redirect errors. First, you can use the standard MPW
error redirection characters >=, >=>=, [[Sigma]], and
[[Sigma]][[Sigma]] in your outermost user interface script. For instance, the
script line
Veeblefetzer >= "{Boot}"Veeblefetzer.Errors
would
redirect errors to the file Veeblefetzer.Errors at the top level of your
startup disk. This does little or nothing to bring the errors to your
attention, though, so your outermost script should look something like this:
Set ErrorFile "{TempFolder}"MyUtility.Errors
Delete -i "{ErrorFile}"
Set Exit 0 # Don't bomb quietly on errors
Potrzebie >=>= "{ErrorFile}"
if {Status} == 0
Veeblefetzer >=>= "{ErrorFile}"
end
if `Exists "{ErrorFile}"`
Alert `Catenate "{ErrorFile}"`
Delete -i "{ErrorFile}"
end
The
other way to redirect errors is to set the ToolServer built-in variable
BackgroundErr to the name of a file. This will create that file whenever
there's an error. This is somewhat less flexible than redirection, but it can
be set once and for all in a ToolServer startup script. That would make the
script above read like this:
Delete -i "{BackgroundErr}"
Set Exit 0 # Don't bomb quietly on errors
Potrzebie
if {Status} == 0
Veeblefetzer
end
if `Exists "{BackgroundErr}"`
Alert `Catenate "{BackgroundErr}"`
Delete -i "{BackgroundErr}"
end
Standard
output can be controlled similarly, using either redirection characters or the
environment variable BackgroundOut.
FORMS OF TOOLS
There are several ways to package commands for use with ToolServer. The most
basic and boring ways are:
- Use the Execute Script command in ToolServer's File menu to select and
execute a script file.
- Drop a script file on the ToolServer application icon.
- Give the "ToolServer [script ... ]" command in the MPW
Shell.
There are more interesting deployment modes, but they require a bit
more explanation.
Standalone scripts. If you change the creator of a script file to 'MPSX',
double-clicking it in the Finder will launch ToolServer and send it an Open
Document event, causing it to be executed. Use this approach for your outermost
user interface scripts. To change the creator, use MPW's "SetFile -c 'MPSX'
file" command.
There is, alas, no such thing as a standalone tool, but you can write a
one-line script that invokes a tool with any parameters or none.
AppleScript. ToolServer is fully scriptable. Aside from the four required
commands, it has only one scripting command, the dreaded DoScript. This takes a
command written in another scripting language -- MPW command language in
this case -- and passes the command to its script interpreter. DoScript is
discouraged in new applications because it's unstructured, but it's very useful
for pre-AppleScript applications that have their own languages.
A simple AppleScript script confers few benefits over a standalone ToolServer
script. In fact, it's better to avoid mixing scripting languages if you can.
However, using FaceSpan or another AppleScript authoring tool, you can use
AppleScript to set up a conventional application that relies on ToolServer as a
workhorse. Simply pass DoScript commands in response to user actions,
redirecting errors and output to temporary files that you interpret in your
AppleScript code.
Apple events. Finally, you can take AppleScript one step further, driving
ToolServer directly with Apple events generated from compiled software. This
delivers the maximum in flexibility and performance. You could even write a
project-file development system based on MPW compilers. Another possibility
would be HyperCard XCMDs, allowing MPW commands to be invoked from HyperTalk.
An Apple event front end could be created for a particular MPW tool, allowing
it to be cleanly invoked from other scripts or compiled software; this might
also provide a simple user interface for controlling it with dialogs and
menus.
ToolServer accepts the required Apple events, as well as DoScript and some
special-purpose events related to status checking, command canceling, and error
and output redirection. These are documented in Chapter 4 of the ToolServer
Reference manual that comes with MPW. In this column in the last issue of
develop, I provided sample code for interacting with SourceServer (another
Apple event-driven MPW Shell subset), and that code can easily be adapted for
ToolServer.
TOOLS FOR THE FUTURE?
Because it's tied to a command-line interface, the MPW toolset has come to seem
rather archaic, but there's life in the old girl yet. ToolServer's support for
Apple events and AppleScript allows innumerable improvements in its interface.
In the future, we may see friendly front ends for various MPW tools, as well as
deeper support for compilation and other kinds of file processing with MPW
tools in third-party development systems.
Ultimately, MPW's command-line interface is destined to become a fading memory.
Although it confers some advantages in power, it must give way to friendlier
approaches in the end. However, if we fail to move its toolset forward into the
post-command-line world, we will be poorer for the loss.
TIM MARONEY was discovered on the Isle of Wight by seal farmers in the Year of
Our Lord 1394, and again seventy years later by Tasmanian basket twirlers out
for a stroll in the Yukon. The little tyke pursued a happy life of fun,
freedom, and quantum mechanics. He resurfaced in 1961, in the town of Holyoke,
Massachusetts. Tim played a magician in bondage in a class play in the second
grade, which may have prepared him for the contract work he's now doing at
Apple.
Thanks to Deeje Cooley, Arno Gourdol, and Rick Mann for reviewing this
column.