Hacking Mac OS X Panther
Pages: 1, 2, 3
Add a Dab of GUI to Unix Scripts
Build a graphical dialog front-end to your Unix scripts with just a snippet of Perl or Python glue code.
When it comes to combining Unix scripting and GUI, more often than not, a little dab will do. Such was the case with the Mac OS X Installer I was building for Blosxom (http://www.blosxom.com), my designed-for-Mac-yet-runs-anywhere weblog [Hack #30] application.
I'd built a nice Mac OS X package (http://developer.apple.com/documentation/DeveloperTools/Conceptual/SoftwareDistribution), which turned my otherwise rather Unix-y application into a double-clickable install. However, I still needed to set a configuration setting or three and didn't want to require my users to resort to editing Perl scripts by hand. So, I set about divining an elaborate—and not a little bit grotty—scheme involving one part shell scripting, two parts Perl, and a pinch of AppleScript.
The Grotty Way
As part of its standard procedure, the Mac OS X Installer runs any postflight shell script it finds in a package at the end of the installation process—thus the name postflight (http://developer.apple.com/documentation/DeveloperTools /Conceptual/SoftwareDistribution/Concepts/sd_pre_post_processing.html #//apple_ref/doc/uid/20001945/TPXREF19). This gives the package maker a chance to perform any last-minute reshuffling, fix permissions, run additional scripts, and generally do anything you can do on and from the command line. I slipped a call to a Perl script, configure.pl, into Blosxom's postflight script like so:
echo Running perl/applescript configuration scripts...
sudo /usr/bin/perl "$PACKAGE_PATH/Contents/Resources/configure.pl"
TIP: The output generated by
echoin the preceding shell snippet and
One of the things this
configure.pl Perl script does is call an
AppleScript script, configure.scpt, to prompt
the user through a series of dialog boxes for the answers to various
configuration questions and retrieve the responses as a string of
key/value pairs (stored in $options), later to be
applied automagically to the Blosxom application itself. Calling the
AppleScript script itself is just a matter of passing the script to
the osascript command-line utility that takes care
of actually running it for me:
print "... prompting for answers to configuration questions\n";
my $options = `/usr/bin/osascript
$PACKAGE_PATH/Contents/Resources/configure.scpt 2>&1`;
...
# Do something magical with the contents of $options
...
Finally we come to the GUI bit itself. The
configure.scpt AppleScript consists of nothing
more than a series of display dialog statements,
each prompting for a single configuration setting and saving the
user's responses:
tell application "Finder"
activate
display dialog "You're mere moments away from your very own blog." & return
& return & "Before letting you get on with it, however, let's get a few last
settings taken care of for you." & return & return & "To skip this step,
click Cancel." & return with icon 1
display dialog "What would you like to call your blog?" default answer
"blosxom"
set blog_title to (text returned of result)
display dialog "How would you describe your blog (keep it short)?" default
answer "yet another blosxom blog"
set blog_description to (text returned of result)
display dialog "What language code would you like to associate with
your blog (e.g. en=English, fr=French, de=German)?" default answer "en"
set blog_language to (text returned of result)
display dialog "How many entries would you like displayed on your
blog's home page?" default answer "40"
set blog_num_entries to (text returned of result)
display dialog "What URL should be used for your blog (leave blank
to have Blosxom figure this out for itself)?" default answer ""
set blog_url to (text returned of result)
set result to ("$$$blog_title=" & blog_title & "$$$blog_description=" &
blog_description & "$$$blog_language=" & blog_language & "$$$num_entries="
& blog_num_entries & "$$$url=" & blog_url)
end tell
For instance, line 1 displays prompts for a blog name, as shown in
Figure 2-24, while line 2 saves the result to the
AppleScript variable blog_title.

Figure 2-24. One of a series of AppleScript dialogs
Line 3 strings all the various responses together to be returned to the calling configure.pl Perl script.
While this actually does work remarkably well, it is overly involved (albeit not particularly complex) for the rather simple requirements it has to meet. Also, it has quite a staccato feel to it from the user's point of view, with dialog boxes bouncing up and down one after another. Unfortunately, there was just no way, without doing quite a bit more programming, that I could get around sending the user (very possibly a newbie) to the command line—a no-no in my book.
The Nifty Way
Thankfully, I stumbled upon Carsten Blüm's Pashua (http://www.bluem.net/downloads/pashua_en; donateware), a rather nifty tool for creating simple, native Aqua GUIs from Perl, Python, PHP, Tcl, Rexx, and AppleScript. Pashua's capabilities are limited to dialog windows, but it does provide a nice collection of widgets: labels, tool tips, text fields, password fields, checkboxes, radio buttons, pop-up and combo boxes, file selectors, and buttons.
I can now replace that staccato series of dialog boxes with a single dialog box that sports all the configuration questions I need—at least one window's worth.
Installation
Installing Pashua is just a matter of downloading the disk image and copying the contents across to your hard drive. To have the Pashua.app itself readily accessible from anywhere you care to use it, you might consider dropping it into your Applications folder. You'll also want to make sure that the associated Perl or Python module (Pashua.pm and Pashua.py, respectively) you'll be using in your code is somewhere Perl or Python will expect to find it.
My preference is to keep all of the Pashua bits together, simply creating a Pashuafolder in my Documents/Code directory (where I do all of my coding).
You just need to be sure that the Pashua.app application and appropriate Pashua.pm or Pashua.py module is available to your script. The simplest way to do this is to copy these into the same folder as your script and distribute the entire folder to anyone who'll be using your software.
The Basics
Pasha scripts are simply wrappers around a configuration string that's fed to the Pashua application. This string defines the dialog window itself and the various GUI widgets you'd like Pashua to display.
While I'll not delve into every widget in turn, each definition looks a little something like this:
title_type=textfield
title_label=What do you want to call your blog?
title_width=200
title_default=My Blosxom
title_tooltip=Keep it simple.
title is the name I've given to
this particular widget; I could have used any string of letters or
numbers (e.g., title1, thingy,
or wotsit22). Each widget is described by using
attributes, prefixed with an underscore (_) and
appended to the widget's name. Most widgets take
four attributes: type
(textfield, button,
checkbox, etc.), label (what
appears alongside the checkbox or on the button),
width (in pixels), and a
default (which radio button is initially selected,
the initial value of a textfield, etc.). So, this
widget is a 200-pixel-wide textfield named
title, labeled What do you want to
callyour blog?, and with an initial
value of My Blosxom.
TIP: It's all much like designing an HTML form, except that attributes are listed as
name_attribute, one per line, rather than in an HTML tag. The just-describedtextfieldmight look something like this in an HTML form:What do you want to call your blog? <input type="text" name="title" size="200" value="My blosxom" />
Here's another:
future_type=radiobutton
future_label=Should I show entries from the future?
future_option=No
future_option=Yes
future_default=No
This one is a radiobutton group named
future, labeled Should I showentries from the future?, with
No and Yes options, the former
selected by default.
As for the dialog window itself, there's not much to fiddle with:
# Set window title
windowtitle=Blosxom Configuration Wizard
# Set transparency: 0 is transparent, 1 is opaque
transparency=0.95
# Set the window to brushed metal; the default is regular Aqua
appearance=metal
This window is titled Blosxom Configuration Wizard
and sports a 95% opaque (5% transparent) brushed-metal look.
You'll find complete documentation for these directives in the Pasha package itself (Pashua/Documentation/documentation.html) and various hints in the code and comments of the examples used in this hack.
Simple Example
Let's start out with a simple example, one that displays a single dialog window with some number of GUI widgets representing the minimal configuration settings Blosxom requires.
The code
While I could have chosen any of the supported languages, I thought I'd stick to Perl, because that's what Blosxom is written in and I can write Perl in my sleep (although, admittedly, I'm more of a Python devotee at heart).
To a folder named Simple (Documents/Code/Pashua/Simple), I copied Pashua.app and Pashua.pm. I then wrote the following script, borrowing the basic framework from the example code (Pashua/Examples/example.pl) found in the Pashua distribution. I saved the file as Simple.pl:
# !/usr/bin/perl -w
# Simple.pl
# A simple Pashua example
# Add the local directory to the queue of places to look for Pashua.pm
BEGIN {
use File::Basename;
unshift @INC, dirname($0);
}
use strict;
use Pashua;
# Define what the dialog should be like
# Take a look at Pashua's Readme file for more info on the syntax
my $conf = <<EOCONF;
# Lines starting with a hash character are
# comments, empty lines are ignored
# Set transparency: 0 is transparent, 1 is opaque
transparency=0.95
# Set window title
windowtitle=Blosxom Configuration Wizard
txt_type=text
txt_text=Welcome to Blosxom.[return][return]You're mere moments
away from your very own blog.[return][return]This wizard will
take you through some last minute configuration settings. If
you're in need of some details, take a gander at the gory
details on the Blosxom Configuration page at
http://www.blosxom.com/documentation/users/configure/.
title_type=textfield
title_label=What do you want to call your blog?
title_width=200
title_default=My Blosxom
description_type=textfield
description_label=How would you describe your blog?
description_width=400
description_default=Yet another blosxom blog.
language_type=textfield
language_label=What will be your primary written language? (e.g. en=English)
language_width=25
language_default=en
future_type=radiobutton
future_label=Should I show entries from the future (i.e. post-dated entries)?
future_option=No
future_option=Yes
future_default=No
# Add a cancel button - if you like, you can set the
# button label by uncommenting the second line below
cncl_type=cancelbutton
#cncl_label=If you click here, no values will be returned
# A default button is added automatically - if you want to
# change the button title, you should uncomment the next
# two lines to override the "built-in" default button
#default_type=defaultbutton
#default_label=Click here to return the values
EOCONF
# Pass the configuration string to the Pashua module
my %result = Pashua::run($conf);
if (%result) {
print " Pashua returned the following hash keys and values:\n";
while (my($k, $v) = each(%result)) {
print " $k = $v\n";
}
}
else {
print " No result returned. Looks like
the 'Cancel' button has been pressed.\n";
}
Lines 7-12 make the Pashua.pm Perl module in the current folder available to the script.
The heart of the script is a long configuration string that defines
the various aspects of the dialog window itself (lines 20-24) and
component GUI widgets (lines 26-59) stashed in a
$conf variable.
Line 63 is where the actual work gets done: the
$conf configuration string is passed to the
Pashua::run() method of the
Pashua.pm Perl module, which, in turn, asks the
Pashua.app application to display a dialog box
that contains the various described widgets.
Lines 65-73 print the results to the Terminal window, although you'll most likely want to do something far more useful with them in any application you're building with Pashua.
Running the code
Run the Perl script on the command line, like so:
$ perl Simple.pl
You'll see the Pashua application bounce into being in your Dock and your dialog pop up on the screen, as shown in Figure 2-25.

Figure 2-25. A simple Pashua-generated dialog
Click the OK button or just hit the Return key on your keyboard to send the values of the various dialog widgets to the Terminal:
$ perl Simple.pl
Pashua returned the following hash keys and values:
cncl = 0
title = My Blosxom
future = No
description = Yet another blosxom blog.
language = en
Hacking the hack
Since the lion's share of your script is Pashua configuration, the process is just about identical regardless of the language you choose. Here's the same thing (the configuration is abbreviated) in Python:
#!/usr/bin/env pythonimport Pashuaconf = """
# Lines starting with a hash character are
# comments, empty lines are ignored
# Set transparency: 0 is transparent, 1 is opaque
transparency=0.95
# Set window title
windowtitle=Blosxom Configuration Wizard
...
# two lines to override the "built-in" default button
# default_type=defaultbutton
# default_label=Click here to return the values
"""
if Result['cncl'] is '0':
print " Pashua returned the following dictionary keys and values:"
for Key in Result.keys( ):
print " %s = %s" % (Key, Result[Key])
else:
print " No result returned. Looks like the 'Cancel' button has been pressed.";
print
Wizard Example
I created a new folder named Wizard(Documents/Code/Pashua/Wizard) and again copied Pashua.app and Pashua.pm into it.
The code
The following script, Wizard.pl, defines two
separate dialog window configurations, $general
and $configurestatic, each to be fed serially to
the Pashua engine (additions and alterations from the code in the
preceding "Simple Example"
section—mostly the addition of a second screen
configuration—are called out in bold):
#!/usr/bin/perl -w
# Wizard.pl
# A multi-screen Pashua Wizard example
BEGIN {
use File::Basename;
unshift @INC, dirname($0);
}
use strict;
use Pashua;
# Define the Wizard's first screen
my $general = <<GENERAL;
# Lines starting with a hash character are
# comments, empty lines are ignored
# Set transparency: 0 is transparent, 1 is opaque
transparency=0.95
# Set window title
windowtitle=Blosxom Configuration Wizard: General
txt_type=text
txt_text=Welcome to Blosxom.[return][return]You're mere
moments away from your very own blog.[return][return]This
wizard will take you through some last minute configuration
settings. If you're in need of some details, take a gander
at the gory details on the Blosxom Configuration page at
http://www.blosxom.com/documentation/users/configure/.
title_type=textfield
title_label=What do you want to call your blog?
title_width=200
title_default=My Blosxom
description_type=textfield
description_label=How would you describe your blog?
description_width=400
description_default=Yet another blosxom blog.
language_type=textfield
language_label=What will be your primary written language? (e.g. en=English)
language_width=25
language_default=en
future_type=radiobutton
future_label=Should I show entries from the future (i.e. post-dated entries)?
future_option=No
future_option=Yes
future_default=No
cncl_type=cancelbutton
# A default button is added automatically - if you want to
# change the button title, you should uncomment the next
# two lines to override the "built-in" default button
default_type=defaultbutton
default_label=Next
GENERAL
# Define the Wizard's second screen
my $configurestatic = <<CONFIGURESTATIC;
windowtitle=Blosxom Configuration Wizard: Static Settings
staticdir_type=openbrowser
staticdir_label=Where would you like the static version of you blog to live?
staticdir_width=400
staticdir_default=/Library/WebServer/Documents
staticpwd_label=What would you like to use as your static rendering password?
staticpwd_type=password
staticpwd_width=200
staticpwd_default=
staticentries_type=radiobutton
staticentries_label=Would you like to statically render individual entries?
staticentries_option=No
staticentries_option=Yes
staticentries_default=No
editor_type=popup
editor_label=When would you like static rendering to run?
editor_width=200
editor_option=Manually
editor_option=Every 1/2 Hour
editor_option=Every 1 Hour
editor_option=Every 2 Hours
editor_option=Every 3 Hours
editor_option=Every 4 Hours
editor_option=Every 5 Hours
editor_option=Every 6 Hours
editor_option=Every 12 Hours
editor_option=Every 24 Hours
editor_default=Manually
cncl_type=cancelbutton
cncl_label=Cancel
default_type=defaultbutton
default_label=Next
CONFIGURESTATIC
# Define the rest of the wizard's screens
# ...
# Pass each configuration string in turn to the Pashua module to create
# a Wizard-like screen-by-screen interface and gather the results along
# the way
my %result = Pashua::run($general));
%result = (%result, Pashua::run($configurestatic));
# All the rest of the screens go here in the same manner
# %result = (%result, Pashua::run($shareware));
if (%result) {
print " Pashua returned the following hash keys and values:\n";
while (my($k, $v) = each(%result)) {
print " $k = $v\n";
}
}
else {
print " No result returned. Looks like the
'Cancel' button has been pressed.\n";
}
Line 104 runs Pashua::run(), feeding it the first
screen configuration held in the $general
variable. Line 181 calls Pashua::run() again,
this time feeding it the second screen, as described in
$configurestatic.
Running the code
Run the Perl script on the command line, like so:
$ perl Wizard.pl
Up comes the first screen of your wizard. Click the Next button to move on to the next screen, shown in Figure 2-26.

Figure 2-26. The second screen of a Pashua-generated wizard
Click the Next button again (in your final wizard, a Finish button is probably more appropriate for the last screen) or just hit the Return key on your keyboard to send the values of the various dialog widgets across all the wizard's screens to the Terminal:
$ perl Wizard.pl
Pashua returned the following hash keys and values:
title = My Blosxom
default = 1
description = Yet another blosxom blog.
future = No
staticdir = /Library/WebServer/Documents
staticentries = No
staticpwd = s33kr1t
cncl = 0
editor = Manually
language = en
WARNING Be sure to choose different names for your various variables across wizard screens, because all the key/value pairs are stored in a single hash (at least in this example) and a second widget of the same name on another screen will overwrite the value of the first.
And this gets me to right where I wanted to be: a multiscreen configuration wizard that's capable of being called as a script by the Mac OS X Installer.
But if you try this yourself, you'll notice something slightly irritating: for each screen, the Pashua app is brought to life and killed off, over and over. While not quite as staccato as the grotty way I started out with and far more feature-filled and flexible, it still feels a mite bit rough around the edges. And what if the user wanted to rerun it? She'd have to visit the command line and invoke it from there—still a no-no.
Double-Clickable Example
Wouldn't it be nice to make a self-contained, double-clickable application of this, turning your simple Unix script into a GUI wizard that's virtually indistinguishable (except for the more limited form-driven-only functionality) from a first-class application?
The Pashua distribution includes just such a thing in the form of Doubleclickable Example (Pashua/Examples/Doubleclickable Example.app).
Option-drag a copy of Doubleclickable Example somewhere for editing (leaving the original as it is, in case you need it again as a starting point); my copy is Documents/Code/Pashua/Doubleclickable Example.
Control- or right-click the application and select Show Package Contents from the context menu, as shown in Figure 2-27. Browse through the app until you get to Doubleclickable Example/Contents/MacOS. Notice that both the Pashua app and Pashua.pm Perl module are baked right into the application's contents so that they're readily available to the core script. The script itself has the same name as the outer application, Doubleclickable Example.

Figure 2-27. Show Package Contents
Otherwise, open the script in your favorite plain text editor [Hack #78] and edit to your heart's content.
The code
Here's what is essentially the same code as appeared in the preceding "Wizard Example" section. I've added in a few more screens and put in some basic logic for which screens to show, given the input provided on the previous screen and so forth:
#!/usr/bin/perl -w
# Doubleclickable Example
# A double-clickable Pashua Wizard example
BEGIN {
use File::Basename;
unshift @INC, dirname($0);
}
use strict;
use Pashua;
#!/usr/bin/perl -w
use strict;
use Pashua;
my $general = <<GENERAL;
windowtitle=Blosxom Configuration Wizard: General Settings
txt_type=text
txt_text=Welcome to Blosxom.[return][return]You're mere
moments away from your very own blog.[return][return]This
wizard will take you through some last minute configuration
settings. If you're in need of some details, take a gander
at the gory details on the Blosxom Configuration page at
http://www.blosxom.com/documentation/users/configure/.
title_type=textfield
title_label=What do you want to call your blog?
title_width=200
title_default=My Blosxom
description_type=textfield
description_label=How would you describe your blog?
description_width=400
description_default=Yet another blosxom blog.
language_type=textfield
language_label=What will be your primary written language? (e.g. en=English)
language_width=25
language_default=en
future_type=radiobutton
future_label=Should I show entries from the future (i.e. post-dated entries)?
future_option=No
future_option=Yes
future_default=No
cncl_type=cancelbutton
cncl_label=Cancel
default_type=defaultbutton
default_label=Next
GENERAL
my $staticordynamic = <<STATICORDYNAMIC;
windowtitle=Blosxom Configuration Wizard: Dynamic or Static
txt_type=text
txt_text=Blosxom runs either dynamically (on-the-fly) or
statically, rendering the main index, category indexes,
date indexes, and (optionally) individual entries as regular
files on your filesystem. Static rendering is useful for
those who either prefer to or are only able to serve up
files from their Web server. You can render your blog from
locally and then move them to a mounted WebDAV drive, .Mac
iDisk, or FTP/rsync them up to your ISP's server.[return][return]
For more details on static rendering, visit the Blosxom Static
page at http://www.blosxom.com/documentation/users/configure/static.html.
staticdynamic_type=radiobutton
staticdynamic_label=Would you prefer to run your blog in static or dynamic mode?
staticdynamic_option=Dynamic
staticdynamic_option=Static
staticdynamic_default=Dynamic
cncl_type=cancelbutton
cncl_label=Cancel
default_type=defaultbutton
default_label=Next
STATICORDYNAMIC
my $configurestatic = <<CONFIGURESTATIC;
windowtitle=Blosxom Configuration Wizard: Static Settings
staticdir_type=openbrowser
staticdir_label=Where would you like the static version of you blog to live?
staticdir_width=400
staticdir_default=/Library/WebServer/Documents
staticpwd_label=What would you like to use as your static rendering password?
staticpwd_type=password
staticpwd_width=200
staticpwd_default=
staticentries_type=radiobutton
staticentries_label=Would you like to statically render individual entries?
staticentries_option=No
staticentries_option=Yes
staticentries_default=No
editor_type=popup
editor_label=When would you like static rendering to run?
editor_width=200
editor_option=Manually
editor_option=Every 1/2 Hour
editor_option=Every 1 Hour
editor_option=Every 2 Hours
editor_option=Every 3 Hours
editor_option=Every 4 Hours
editor_option=Every 5 Hours
editor_option=Every 6 Hours
editor_option=Every 12 Hours
editor_option=Every 24 Hours
editor_default=Manually
cncl_type=cancelbutton
cncl_label=Cancel
default_type=defaultbutton
default_label=Next
CONFIGURESTATIC
my $shareware = <<SHAREWARE;
windowtitle=Blosxom Configuration Wizard: Shareware
txt_type=text
txt_text=Blosxom is free for the taking and sharing. That said,
it does take a considerable amount of not-so-free time and loving
care. This Blosxom Installer for Mac OS X is shareware; when you've
yourself situated and have a moment, please do pay the one-time $15
shareware fee at:[return][return]http://www.amazon.com/paypage/P13LC7VUIVY0N .
cncl_type=cancelbutton
cncl_label=Cancel
default_type=defaultbutton
default_label=Next
SHAREWARE
my $finish = <<FINISH;
windowtitle=Blosxom Configuration Wizard: Finishing Up
txt_type=text
txt_text=That's all there is to it. Click the Finish button, let the
installer finish up, and you'll be whisked away to your brand new blog.
cncl_type=cancelbutton
cncl_label=Cancel
default_type=defaultbutton
default_label=Finish
FINISH
my %result;
%result = Pashua::run($general);
$result{cncl} and exit;
%result = (%result, Pashua::run($staticordynamic));
$result{cncl} and exit;
$result{staticdynamic} eq 'Static' and %result =
(%result, Pashua::run($configurestatic));
$result{cncl} and exit;
%result = (%result, Pashua::run($shareware));
$result{cncl} and exit;
%result = (%result, Pashua::run($finish));
# Do something useful with the results (or just save them
# somewhere for now)
open OUT, "> /tmp/pashua.out";
print OUT " Pashua returned the following hash keys and values:\n";
while (my($k, $v) = each(%result)) {
print OUT " $k = $v\n";
}
close OUT;
There are some slight changes—aside from the whole thing running as a first-class, double-clickable application, that is—worth pointing out here.
Line 2 is a cosmetic change, replacing the Next button of the previous screens with a more appropriate Finish button.
Lines 3 through 12 call the Pashua::run() method
for each screen and store the results in a %result
hash. I have added some logic to check after each screen that the
user isn't trying to get out of the wizard by
hitting the Cancel button (Lines 5, 7, 9, and 11). If so, we
terminate the script on the spot.
Now, of course, we could simply have dropped all the screen names into a loop and iterated over them one by one, like so:
my %result;
foreach my $screen ( ($general, $staticordynamic, $configurestatic,
$shareware, $finish) ) {
%result = (%result, Pashua::run($screen));
$result{cncl} and exit;
}
Line 8 (requiring an exception) is why I chose to do things manually.
We offer the user a choice of configuring Blosxom for dynamic or
static rendering. Since only the latter requires any kind of
additional configuration, the $configurestatic
screen is called only if the user selects the Static radio button in
Line 1.
The last few lines of the script do something useful with the results—or, in this example, simply stash them somewhere for now. You can't just print the values out to the Terminal (as in the case in the Simple or Wizard examples), because this script runs as a double-clickable app and doesn't involve a visit to the Terminal at all.
Running the code
Double-click the Doubleclickable Example application icon in the Finder to open your dialog window. Click the Next button to jump from screen to screen. Be sure to select Static mode on the Dynamic or Static screen; otherwise, you'll never get to see the Static Settings (as well you shouldn't) shown in Figure 2-28. At the end, click the Finish button to finish up. You can quit the app at any time by clicking the Cancel button or hitting the Esc key on your keyboard.

Figure 2-28. The Static Settings screen of a Pashua-powered, double-clickable application
Notice that the Pashua app no longer ebbs and flows between each screen, making for a nice, smooth experience for the end user.
If you're interested in what shows up in the temporary output file, open a Terminal window and type the following command:
$ less /tmp/pashua.out
Pashua returned the following hash keys and values:
description = Yet another blosxom blog.
editor = Every 1 Hour
staticpwd =
language = en
cncl = 0
title = My Blosxom
staticdir = /Library/WebServer/Documents
future = No
staticentries = No
staticdynamic = Static
default = 1
/tmp/pashua.out (END)
Hacking the hack
While the documentation says that to change the name of the app you need to change both the name of the outer application, Doubleclickable Example.app, and inner Doubleclickable Example Perl script, I found that changing the name of the outer without touching the inner worked like a charm; indeed, changing the inner script's name caused the app not to run at all.



