NAME
slashstyle - Slash Style Guide
INTRODUCTION
All code and documentation that is submitted to be included in the Slash
distribution should follow the style in this document. This is not to
try to stifle your creativity, but to make life easier for everybody who
has to work with your code, and to aid those who are not quite sure how
to do something.
These conventions below apply to perl modules, web programs, and
command-line programs, specifically, but also might apply to some degree
to any Perl code written for use in Slash.
Note that these are all guidelines, not unbreakable rules. If you have a
really good need to break one of the rules herein, however, then it is
best to ask the core Slash team first.
Note that with much of this document, it is not so much the Right Way as
it is Our Way. We need to have conventions in order to make life easier
for everyone. So don't gripe, and just follow it, because you didn't get
a good grade in "Plays Well With Others" in kindergarten and you want to
make up for it now.
If you have any questions, please ask us on the slashcode-development
mailing list at SourceForge.
http://sf.net/projects/slashcode/
We don't always follow this guide. We are making changes throughout our
code to be in line with it. But just because we didn't do it yet, that
is no excuse. Do it anyway. :-)
This document is subject to change at the whims of the core Slash team.
We hope to add any significant changes at the bottom of the document.
CODING PRINCIPLES
Perl Version
We code everything to perl 5.005_03. Some day we may switch to take
advantage of perl 5.6 features. Regardless, all code should run on perl
5.005_03 or any later version of perl 5. All of the core Slash code has
been tested on perl 5.005_03 and perl 5.6.0, though it has probably been
used more on perl 5.6.0.
Documentation
All modules will be documented using the POD examples in the module
boilerplate. The function, purpose, use of the module will be explained,
and each public API will be documented with name, description, inputs,
outputs, side effects, etc.
If an array or hash reference is returned, document the size of the
array (including what each element is, as appropriate) and name each key
in the hash. For complex data structures, map out the structure as
appropriate (e.g., name each field returned for each column from a DB
call; yes, this means you shouldn't use "SELECT *", which you shouldn't
use anyway).
Also document what kind of data returned values are. Is it an integer, a
block of HTML, a boolean?
All command-line program options will be documented using the
boilerplate code for command-line programs. Each available function,
switch, etc. should be documented, along with a statement of function,
purpose, use of the program. Do not use the same options as another
program, for a different purpose.
All web programs should be documented with a statement of function,
purpose, and use in the comments of the program.
Any external documents, and documentation for command-line programs and
modules, should be written in POD, where appropriate. From there, they
can be translated to many formats with the various pod2* translators.
Read the perlpod manpage before writing any POD, because although POD is
not difficult, it is not what most people are used to. It is not a
regular markup language; it is just a way to make easy documentation for
translating to other formats. Read, and understand, the perlpod manpage,
and ask us or someone else who knows if you have any questions.
Version
Use the boilerplate code for versions of modules, web programs, and
command-line programs. The $VERSION of the module will then reflect the
CVS revision. The exception to this is the main module of any given
distribution (e.g., Slash.pm) which should have the $VERSION reflect the
distribution version. Also, XS modules should probably have $VERSION
also reflect the distribution, or else you'll need to recompile the
shared library every time you make a change to the file, which is really
a pain to do during development.
Our distribution versions use tuples, where the first number is the
major revision, the second number is the version, and third number is
the subversion. Odd-numbered versions are development versions.
Examples:
1.0.0 First release of Slash 1
1.0.1 Second release of Slash 1.0
1.0.10 etc.
1.1.0 First development release of Slash 1.2 (or 2.0)
2.0.0 First release of Slash 2
Versions can be modified with a hyphen followed by some text, for
special versions, or to give extra information. Examples:
1.1.4-bender Notes that this is a bender release
2.0.0-pre1 Notes that this is not final, but preview
In perl 5.6.0, you can have versions like `v2.0.0', but this is not
allowed in previous versions of perl. So to convert a tuple version
string to a string to use with $VERSION, use a regular integer for the
revision, and three digits for version and subversion. Examples:
1.1.6 -> 1.001006
2.0.0 -> 2.000000
This way, perl can use the version strings in greater-than and less-than
comparisons.
Comments
All code should be self-documenting as much as possible. Only include
necessary comments. Use names like "$story_count", so you don't need to
do something like:
# story count
my $sc = 0;
Include any comments that are, or might be, necessary in order for
someone else to understand the code. Sometimes a simple one-line comment
is good to explain what the purpose of the following code is for.
Sometimes each line needs to be commented because of a complex
algorithm. Read Kernighan & Pike's *Practice of Programming* about
commenting. Good stuff, Maynard.
Warnings and Strict
All code must compile and run cleanly with "use strict" enabled and the
perl "-w" (warnings) option on. If you must do something that -w or
strict complains about, there are workarounds, but the chances that you
really need to do it that way are remote.
The one exception is the "Use of uninitialized variable" warnings, which
we hate. We have those disabled in Slash.pm, so by including "use Slash"
you are disabling that warning in your code, too, and you don't need to
worry about them.
Lexical Variables
Use only lexical variables, except for special global variables
($VERSION, %ENV, @ISA, $!, etc.) or very special circumstances (see
@Slash::DB::ISAMySQL and $Slash::Display::CONTEXT). Global variables for
regular use are never appropriate. When necessary, "declare" globals
with "use vars", not with our() (our() was introduced in perl 5.6).
A lexical variable is created with my(). A global variable is
pre-existing (if it is a special variable), or it pops into existence
when it is used. local() is used to tell perl to assign a temporary
value to a variable. This should only be used with special variables,
like $/, or in special circumstances. If you must assign to any global
variable, consider whether or not you should use local().
local() may also be used on elements of arrays and hashes, though there
is seldom a need to do it, and you shouldn't.
Exporting
Except for the universal Slash API, which is currently implemented by
Slash and Slash::Utility (and one function in Slash::Display), do not
export anything from a module by default. Feel free to put anything you
want to in @EXPORT_OK, so users of your modules can explicitly ask for
symbols (e.g., "use Slash::Something qw(getFoo setFoo)"), but do not
export them by default.
Pass by Reference
Arrays and hashes should be passed to and from functions by reference
only. Note that a list and an array are NOT the same thing. This is
perfectly fine:
return($user, $form, $constants);
An exception might be a temporary array of discrete arguments:
my @return = ($user, $form);
push @return, $constants if $flag;
return @return;
Although, usually, this is better (faster, easier to read, etc.):
if ($flag) {
return($user, $form, $constants);
} else {
return($user, $form);
}
Garbage Collection
Perl does pretty good garbage collection for you. It will automatically
clean up lexical variables that have gone out of scope and objects whose
references have gone away. Normally you don't need to worry about
cleaning up after yourself, if using lexicals.
However, some glue code, code compiled in C and linked to Perl, might
not automatically clean up for you. In such cases, clean up for
yourself. If there is a method in that glue to dispose or destruct, then
use it as appropriate.
Also, if you have a long-running function that has a large data
structure in it, it is polite to free up the memory as soon as you are
done with it, if possible.
my $huge_data_structure = get_huge_data_structure();
do_something_with($huge_data_structure);
undef $huge_data_structure;
DESTROY
All object classes must provide a DESTROY method. If it won't do
anything, provide it anyway:
sub DESTROY { }
__END__ and __DATA__ and __PACKAGE__
Do not use __END__ or __DATA__ in web programs. They break mod_perl.
Also, __PACKAGE__ will likely not return the value you expect in web
programs. These are all fine for modules.
die() and exit()
Don't do it. Do not die() or exit() from a web program or module. Do not
call `kill 9, $$'. Don't do it.
In command-line programs, do as you please.
shift
Do not use shift. Use @_. shift is slower, and Brian has an allergic
reaction to it.
my $var = shift; # wrong
my($var) = @_; # right
sub foo { uc $_[0] } # OK
my($var1, $var2) = (shift, shift); # Um, no.
Tests
Modules should provide test code, with documentation on how to use it.
STDIN/STDOUT
Always report errors using errorLog(), from Slash::Utility. Never print
directly to STDERR. Do not print directly to STDOUT, unless you need to
print directly to the user's browser; never print anything directly to
the browser except for the result of slashDisply().
In command-line programs, feel free to print to STDERR and STDOUT as
needed.
Files and Globs
Always use the gensym() function to create a new filehandle instead of
more traditional methods. gensym() is exported by the Slash module, via
the Symbol module (which comes with Perl).
use Slash; # or "use Symbol;"
my $fh = gensym();
open $fh, "< $file" or die $!;
It works just like a regular filehandle, like `FILE', but there are
several advantages to this. Because `$fh' is a lexical variable above,
you don't have to explicitly close() it when the current block ends. It
is also easier to pass to other functions, as it is just a regular
variable containing a reference to a filehandle.
It is still best to use close() explicitly, however, because you will
want to check the return value to make sure that it closed cleanly. See
the next step.
For constructing and parsing file paths, use File::Spec::Functions and
File::Basename. For creating or removing paths, use File::Path. If you
don't know how to use these modules, learn.
my $path = "$dir/$file"; # wrong
my $path = catfile($dir, $file); # right
my $dir = "."; # wrong
my $dir = curdir(); # right
mkdir("/path"), mkdir("/path/to"), ... # wrong
`mkdir /path`; `mkdir /path/to`, ... # very wrong
mkpath("/path/to/my/dir", 0, 0775); # right
Do not use the glob operator (`glob('*')' or `<*>'). Use opendir() with
readdir() instead. Note that glob() is much more portable in perl 5.6
than it was in previous versions of perl, but its behavior is still
unreliable, as each perl installation can choose to implement perl using
local conventions instead of the default, which is via the File::Glob
module.
Do not use symbol table globs (not the same kind of glob as above!) like
`*foo' for anything, except for when direct symbol table manipulation is
necessary, which it almost never is (the only place it is currently used
is in Slash::Display::Plugin, for a very specific pupose).
System Calls
Always check return values from system calls, including open(), close(),
mkdir(), or anything else that talks directly to the system. Perl
built-in system calls return the error in $!; some functions in modules
might return an error in $@ or some other way, so read the module's
documentation if you don't know. Always do something, even if it is just
calling errorLog(), when the return value is not what you'd expect.
Pass-through
No module should provide pass-through behavior to another module, except
as an inheritance model dictates. We break this rule on rare occasions,
such as with Slash.pm exporting Symbol.pm's gensym(), but only for very
specific reasons.
Data Encoding
There are some special functions provided by Slash::Utility that aid in
how to encode data that is displayed to the user. These functions *must
be used* for security reasons, and for functional reasons.
There are two ways to use these. One is in Perl:
my $subject_strip = strip_attribute($subject);
$html .= "";
The other is in a Template, using FILTER, which is the more common
usage:
You should not ever need to think about what data will be in "$subject"
in these examples. Just use the functions/filters, and you won't need to
care or worry about it. The only time you can reasonably NOT use the
appropriate filter/function is if you know exactly what the data is
going to be, and that there is no possible chance it will break. For
example:
user.uid will always be a number, and will never have data that could
conflict with the HTML. However, if it were form.uid, then it could be
any value at all, and you would need to filter it.
Take care not to run the same data through more than one function, or
the same function multiple times.
Descriptions of the various functions are below. You must read and
understand all of them.
Basic Encoding
These are basic encodings to be used to filter certain kinds of
text.
strip_nohtml
This strips out all HTML, and adds extra whitespace into long
strings of non-whitespace characters. It removes < and > and
converts & to &.
It is to be used when you have text where HTML is not allowed.
This could be, for example, a comment subject. It must be used
whenever user-supplied text is to be printed where HTML needs to
be stripped out.
strip_notags
This strips out all HTML tags, and adds extra whitespace into
long strings of non-whitespace characters. It removes < and >
and converts & to & unless the & is part of an HTML entity.
It is to be used when you have text where HTML tags are not
allowed, but HTML encoding is (for example, in TITLE tags).
strip_attribute
This converts the text to print literally inside an HTML
attribute value (e.g., ); this means that <, >,
&, and " will be converted to their respective HTML entities.
This must be used when placing *any* unknown text -- no matter
what it is, or where it came from -- inside an HTML attribute
value. Example:
$subject = getSubject($cid); # this is "my life!"
$html .= ""; # WRONG
Without using strip_attribute, the HTML is now broken, because
there will be extra " inside the tag.
strip_literal
This converts the text to print literally in HTML; e.g., < and >
and & are converted to their HTML equivalents. It does not add
extra whitespace.
It is to be used whenever you want to display some text
literally as it originally appeared. The most common use of this
is for TEXTAREA form elements. All text put into a TEXTAREA
element must be run through strip_literal.
fudgeurl
This takes a URL and strips out bad things. It encodes illegal
characters to the proper escape sequences, it strips out
JavaScript, etc. It should be used on *every* URL that we get
from outside Slash, whether supplied by a user, or an RSS file,
or anything else. Example:
$url = fudgeurl($user->{homepage});
fixurl
The function called by `fudgeurl' that does just the encoding of
illegal characters to the proper escape sequences. It should be
used on every URL that we get from *inside* Slash that might
contain unknown data. For example:
$url = fixurl($url);
This should generally not be called; normally, either fudgeurl
is called internally, or fixparam is called for portions of
URLs. Since fixurl will not encode things like & etc., that are
not allowed to appear in the path part of the URL, often
fixparam() will be the best bet, since it encodes almost
everything.
fixparam
This is similar to fixurl, but it only encodes characters that
are in the parameter portion of a URL, instead of the rest of
the URL. Example:
$url = "http://www.example.com/script.pl?op=" . fixparam($data);
It should be used on *every* datum that we put into a parameter
of a URL that we get from outside of Slash.
User-Supplied Encodings
User-supplied encodings are used in comment posting. They are
normally called with the strip_mode() function, which is like the
other functions, but takes a mode as a second argument (strip_mode
*only* works with user-supplied encodings).
strip_extrans
This is similar to literal, except that formatting is preserved
somewhat by converting successive whitespace to HTML entities,
and multiple newlines into BR tags. Also, whitespace is inserted
into long strings of non-whitespace characters.
strip_code
Similar to extrans, except that it is wrapped in CODE tags.
strip_html
All this does is strip out bad/disallowed/broken HTML.
strip_plaintext
This strips out bad HTML like the html mode, and preserves
whitespace/newlines like extrans.
XML Encoding
When putting text into XML, such as RSS, we normally assume that the
text is HTML going in, and HTML coming out. That is, except for
>link< elements, which are assumed to be usable URLs, not
necessarily text that is usable in HTML. That would be the
difference between:
http://example.com/foo?bar=1&baz=2
And:
http://example.com/foo?bar=1&baz=2
There are two functions for encoding text into XML, and one for
decoding from XML.
xmlencode
Standard function for encoding text into XML, which assumes text
is in HTML.
xmlencode_plain
To be used only when the text is, quite specifically, to be able
to be used directly as-is, with no conversion to or from HTML.
xmldecode
Decodes text from XML. To decode HTML text in the XML, use
HTML::Entities::decode_entities() after xmldecode().
STYLE
Much of the style section is taken from the perlsyle manpage. We make
some changes to it here, but it wouldn't be a bad idea to read that
document, too.
Terminology
Slash vs. slash vs. slashcode
"Slash" is the name of the project. "slash" is, optionally, the
specific name for the actual file distribution. That's it.
"slashcode.com" or "Slashcode" or "/code" is the name of the site.
There is no "Slash1" or "Slash2". The name of the project is
"Slash". To specify a version, use "Slash 2.0".
function vs. sub(routine) vs. method
Just because it is the Perl Way (not necessarily right for all
languages, but the documented terminology in the perl
documentation), "method" should be used only to refer to a
subroutine that are object methods or class methods; that is, these
are functions that are used with OOP that always take either an
object or a class as the first argument. Regular subroutines, ones
that are not object or class methods, are functions. Class methods
that create and return an object are optionally called constructors.
Users
"users" are normally users of Slash, the ones hitting the site; if
using it in any other context, specify. "virtual users" are the
users entered into the text of the DBIx::Password module. "system
users" are user names on the operating system. "database users" are
the user names in the database server. None of these needs to be
capitalized.
Names
Don't use single-character variables, except as iterator variables.
Don't use two-character variables just to spite us over the above rule.
Constants are in all caps; these are variables whose value will *never*
change during the course of the program.
$Minimum = 10; # wrong
$MAXIMUM = 50; # right
Other variables are lowercase, with underscores separating the words.
They words used should, in general, form a noun (usually singular),
unless the variable is a flag used to denote some action that should be
taken, in which case they should be verbs (or gerunds, as appropriate)
describing that action.
$thisVar = 'foo'; # wrong
$this_var = 'foo'; # right
$work_hard = 1; # right, verb, boolean flag
$running_fast = 0; # right, gerund, boolean flag
Arrays and hashes should be plural nouns, whether as regular arrays and
hashes or array and hash references. Do not name references with "ref"
or the data type in the name.
@stories = (1, 2, 3); # right
$comment_ref = [4, 5, 6]; # wrong
$comments = [4, 5, 6]; # right
$comment = $comments->[0]; # right
Make the name descriptive. Don't use variables like "$sc" when you could
call it "$story_count". See the section on "Comments".
There are several variables in slash that are used throughout the code,
that you should use in your code. Do not use these variable names for
anything other than how they are normally used, and do not use any other
variable names in their place. Some of these are:
$slashdb # getCurrentDB
$user # getCurrentUser
$form # getCurrentForm
$constants # getCurrentStatic
$self # first named argument in object method
Subroutines (except for special cases, like AUTOLOAD) begin with a verb,
with words following to complete the action. The first word is all
lowercase, while successive words begin with a capital letter. They
should as clearly as possible describe the activity to be peformed, and
the data to be returned.
getStory(); # good
setStoryByName(); # good
getStoryByID(); # good
Subroutines beginning with `_' are special: they are not to be used
outside the current file. There is not to be enforced by the code
itself, but by someone named Guido.
For large for() loops, do not use $_, but name the variable. Do not use
$_ (or assume it) except for when it is absolutely clear what is going
on, or when it is required (such as with map() and grep()).
for (@list) {
print; # OK; everyone knows this one
print uc; # wrong; few people know this
print uc $_; # better
}
Note that the special variable `_' *should* be used when possible. It is
a placeholder that can be passed to stat() and the file test operators,
that saves perl a trip to re-stat the file. In the example below, using
`$file' over for each file test, instead of `_' for subsequent uses, is
a performance hit. You should be careful that the last-tested file is
what you think it is, though.
if (-d $file) { # $file is a directory
# ...
} elsif (-l _) { # $file is a symlink
# ...
}
Package names begin with a capital letter in each word, followed by
lower case letters (for the most part). Try to avoid multiple words in
one part of the package.
Slash::DB # good
Slash::MySQL # proper name
Slash::Display::Provider # good
Slash::MainCode # not so good, but OK
Plugin modules should begin with "Slash::", followed by the name of the
plugin. Direct database functions should be put into a separate module
for that database.
Slash::MyPlugin
Slash::MyPlugin::MySQL # OK
Slash::MyPlugin::DB::MySQL # OK
Indents and Blank Space
All indents should be tabs. Set your tab stops whatever you want them to
be; I use 8 spaces per tabs.
No space before a semicolon that closes a statement.
foo(@bar) ; # wrong
foo(@bar); # right
Line up corresponding items vertically.
my $foo = 1;
my $bar = 2;
my $xyzzy = 3;
open(FILE, $fh) or die $!;
open(FILE2, $fh2) or die $!;
$rot13 =~ tr[abcedfghijklmnopqrstuvwxyz]
[nopqrstuvwxyzabcdefghijklm];
# note we use a-mn-z instead of a-z,
# for readability
$rot13 =~ tr[a-mn-z]
[n-za-m];
Put blank lines between groups of code that do different things. Put
blank lines after your variable declarations. Put a blank line before a
final return() statement. Put a blank line following a block (and
before, with the exception of comment lines).
An example:
# this is my function!
sub foo {
my(@data) = @_;
my $obj = new Constructor;
my($var1, $var2);
$obj->setFoo($data[1]);
$var1 = $obj->getFoo(1);
$var2 = $obj->getFoo($var1);
display($var1, $var2);
return($data[0]);
}
print 1;
Parentheses
For control structures, there is a space between the keyword and opening
parenthesis. For functions, there is not.
for(@list) # wrong
for (@list) # right
my ($ref) # wrong
my($ref) # right
Be careful about list vs. scalar context with parentheses!
my @array = ('a', 'b', 'c');
my($first_element) = @array; # a
my($first_element) = ('a', 'b', 'c'); # a
my $element_count = @array; # 3
my $last_element = ('a', 'b', 'c'); # c
Always include parentheses after functions, even if there are no
arguments. There are some exceptions, such as list operators (like
print) and unary operators (like undef, delete, uc).
There is no space inside the parentheses, unless it is needed for
readability.
for ( map { [ $_, 1 ] } @list ) # OK
for ( @list ) # not really OK, not horrible
On multi-line expressions, match up the closing parenthesis with either
the opening statement, or the opening parenthesis, whichever works best.
Examples:
@list = qw(
bar
baz
); # right
if ($foo && $bar && $baz
&& $buz && $xyzzy
) {
print $foo;
}
Whether or not there is space following a closing parenthesis is
dependent on what it is that follows.
print foo(@bar), baz(@buz) if $xyzzy;
Note also that parentheses around single-statement control expressions,
as in `if $xyzzy', are optional (and discouraged) `if' it is
*absolutely* clear -- to a programmer -- what is going on. There is
absolutely no need for parentheses around `$xyzzy' above, so leaving
them out enhances readability. Use your best discretion. Better to
include them, if there is any question.
The same essentially goes for perl's built-in functions, when there is
nothing confusing about what is going on (for example, there is only one
function call in the statement, or the function call is separated by a
flow control operator). User-supplied functions must always include
parentheses.
print 1, 2, 3; # good
delete $hash{key} if isAnon($uid); # good
However, if there is any possible confusion at all, then include the
parentheses. Remember the words of Larry Wall in the perlstyle manpage:
When in doubt, parenthesize. At the very least it will
let some poor schmuck bounce on the % key in vi.
Even if you aren't in doubt, consider the mental welfare
of the person who has to maintain the code after you, and
who will probably put parens in the wrong place.
So leave them out when it is absoutely clear to a programmer, but if
there is any question, leave them in.
Braces
(This is about control braces, not hash/data structure braces.)
There is always a space befor the opening brace.
while (<$fh>){ # wrong
while (<$fh>) { # right
A one-line block may be put on one line, and the semicolon may be
omitted.
for (@list) { print }
Otherwise, finish each statement with a semicolon, put the keyword and
opening curly on the first line, and the ending curly lined up with the
keyword at the end.
for (@list) {
print;
smell();
}
perlstyle likes to have "uncuddled elses":
if ($foo) {
print;
}
else {
die;
}
But our style is to "cuddle" them:
if ($foo) {
print;
} else {
die;
}
Operators
Put space around most operators. The primary exception is the for
aesthetics; e.g., sometimes the space around "**" is ommitted, and there
is never a space before a ",", but always after.
print $x , $y; # wrong
print $x, $y; # right
$x = 2 >> 1; # good
$y = 2**2; # ok
Note that "&&" and "||" have a higher precedence than "and" and "or".
Other than that, they are exactly the same. It is best to use the lower
precedence version for control, and the higher for testing/returning
values. Examples:
$bool = $flag1 or $flag2; # WRONG (doesn't work)
$value = $foo || $bar; # right
open(FILE, $file) or die $!;
$true = foo($bar) && baz($buz);
foo($bar) and baz($buz);
Note that "and" is seldom ever used, because the statement above is
better written using "if":
baz($buz) if foo($bar);
Most of the time, the confusion between and/&&, or/|| can be alleviated
by using parentheses. If you want to leave off the parentheses then you
*must* use the proper operator. But if you use parentheses -- and
normally, you should, if there is any question at all -- then it doesn't
matter which you use. Use whichever is most readable and aesthetically
pleasing to you at the time, and be consistent within your block of
code.
Break long lines AFTER operators, except for "and", "or", "&&", "||".
Try to keep the two parts to a binary operator (an operator that has two
operands) together when possible.
print "foo" . "bar" . "baz"
. "buz"; # wrong
print "foo" . "bar" . "baz" .
"buz"; # right
print $foo unless $x == 3 && $y ==
4 && $z == 5; # wrong
print $foo unless $x == 3 && $y == 4
&& $z == 5; # right
Other
Put space around a complex subscript inside the brackets or braces.
$foo{$bar{baz}{buz}}; # OK
$foo{ $bar{baz}{buz} }; # better
In general, use single-quotes around literals, and double-quotes when
the text needs to be interpolated.
It is OK to omit quotes around names in braces and when using the =>
operator, but be careful not to use a name that doubles as a function;
in that case, quote.
$what{'time'}{it}{is} = time();
When making compound statements, put the primary action first.
open(FILE, $fh) or die $!; # right
die $! unless open(FILE, $fh); # wrong
print "Starting\n" if $verbose; # right
$verbose && print "Starting\n"; # wrong
Use here-docs instead of repeated print statements.
print <