Audit a Perl 5.003 Script for Perl 5.004 Changes
You will finish with a small, repeatable audit for a Perl 5.003-era script before moving it to the Perl 5.004 behaviour described by perl5004delta. The checks cover startup options, warnings, taint mode, and the semantic changes most likely to alter an old program. Allow 20 to 30 minutes for one script, longer if it has modules or setuid behaviour.
The route
Jump straight to the step you need, or tick off Done means at the end.
This is a compatibility review, not an instruction to install Perl 5.004. The installed manual on this machine comes from Perl 5.38.2 and documents the historical 5.003 to 5.004 change. The examples use the current interpreter for safe syntax and smoke checks; they do not pretend that Perl 5.38 reproduces every old release.
1. Record the interpreter and copy the script
Work on a copy of the script and record the interpreter that will run the audit. This is an ordinary, read-only check:
$ perl -v | sed -n '1,5p'
$ cp -- ./old-script.pl /tmp/old-script.pl.audit
$ perl -c /tmp/old-script.pl.audit
Expected output ends with syntax OK. If compilation fails, fix or record that failure before interpreting any compatibility result. Do not replace a production script in place while testing.
Checkpoint
You should now have the exact source under review and a recorded Perl version.
2. Check options supplied through the environment
Perl 5.004 introduced PERL5OPT. Unless taint checks are active, its contents are treated like options from a #!perl line, with only -D, -I, -M, -U, -d and -m permitted. This makes an inherited environment part of a script's startup contract.
Inspect the value without changing it:
$ printf 'PERL5OPT=%s\n' "${PERL5OPT-}"
For a clean compatibility run, unset it for the child process, then add options explicitly:
$ env -u PERL5OPT perl -w -c /tmp/old-script.pl.audit
/tmp/old-script.pl.audit syntax OK
If the script relies on PERL5OPT, document that dependency and test each permitted option separately. Never copy an unreviewed value into a service environment. A surprising -I can change which module is loaded.
3. Put taint mode in the right place
The manual tightened taint checks and the placement of -T. On Unix, put -T first in the interpreter arguments on the shebang line:
#!/usr/bin/perl -T -w
use strict;
use warnings;
The documented ordering matters for an executable script invoked by name. A line with -w -T may fail, and perl scriptname cannot rely on finding -T in the script after the interpreter has started. Test the copied file explicitly:
$ env -u PERL5OPT perl -T -c /tmp/old-script.pl.audit
/tmp/old-script.pl.audit syntax OK
Security boundary
Taint mode is not a substitute for quoting, validation, least privilege, or safe process execution. Perl 5.004 also rejected some tainted globbing and spawning conditions involving CDPATH, ENV, BASH_ENV, PATH, IFS and unsafe TERM. Treat a new failure as evidence that the old script depended on unsafe input handling, not as a reason to disable -T.
4. Run with warnings and inspect the likely traps
The document recommends trying -w again because warning behaviour became more precise. Run the copied script with its normal harmless arguments and save diagnostics separately:
$ env -u PERL5OPT perl -w /tmp/old-script.pl.audit >/tmp/perl-audit.out 2>/tmp/perl-audit.err
$ sed -n '1,80p' /tmp/perl-audit.err
Do not treat an empty diagnostic file as proof of compatibility. Review code that uses inherited non-method AUTOLOAD, the old %OVERLOAD interface, or interpolation such as $$0. The 5.004 document says to use the overload pragma, and says non-method autoloading should be made explicit when it depended on a base class.
5. Test context-sensitive code deliberately
Several changes affect results without producing a syntax error. Perl 5.004 can return an undefined value from wantarray when a caller ignores the result. Code that performs expensive work should distinguish list, scalar and void context:
sub report_context {
return 'list' if wantarray;
return 'scalar' if defined wantarray;
return 'void';
}
print report_context(), "\n";
my $value = report_context();
report_context();
Also review string-built eval. The documented 5.004 rule determines the value of the expression in scalar context before executing it in the surrounding context. Prefer a block eval for error handling where possible, and add a regression test around any code that expects an array-valued string expression.
Finally, check code that relies on side effects from reading an undefined array or hash element as a subroutine argument. In 5.004 the element is created only when the subroutine modifies that argument. Check existence explicitly rather than relying on an older incidental side effect:
my @a;
sub inspect { return defined $_[0] ? 'defined' : 'undefined' }
print inspect($a[2]), "\n";
print exists $a[2] ? "exists\n" : "absent\n";
6. Finish with a small regression record
Write down the interpreter version, environment variables, command-line options, warnings, taint result, and outputs for the context-sensitive tests. If the script uses regex captures, filehandle reopening, group identity, globbing, or embedded interpreters, add a focused test for that area too: the manual lists changed behaviour for each.
There is nothing to undo in this workflow. The copy and temporary logs can be removed when you have retained the audit record:
$ rm -- /tmp/old-script.pl.audit /tmp/perl-audit.out /tmp/perl-audit.err
Done means:
- the script was compiled with
PERL5OPTcontrolled; - the shebang and
-Tplacement were checked; - warning output was reviewed rather than ignored;
- taint,
AUTOLOAD,%OVERLOAD,wantarray,evaland argument side effects were assessed; - the observed interpreter version and any required fixes are recorded.