1    eval 'exec perl -x -S "$0" ${1+"$@"}'
2	if 0;	# In case running under some shell
3
4require 5;
5use Getopt::Std;
6use Config;
7
8$0 =~ s|.*[/\\]||;
9
10my $usage = <<EOT;
11Usage:  $0 [-h]
12   or:  $0 [-w] [-u] [-a argstring] [-s stripsuffix] [files]
13   or:  $0 [-w] [-u] [-n ntargs] [-o otherargs] [-s stripsuffix] [files]
14        -n ntargs       arguments to invoke perl with in generated file
15                            when run from Windows NT.  Defaults to
16                            '-x -S %0 %*'.
17        -o otherargs    arguments to invoke perl with in generated file
18                            other than when run from Windows NT.  Defaults
19                            to '-x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9'.
20        -a argstring    arguments to invoke perl with in generated file
21                            ignoring operating system (for compatibility
22                            with previous pl2bat versions).
23        -u              update files that may have already been processed
24                            by (some version of) pl2bat.
25        -w              include "-w" on the /^#!.*perl/ line (unless
26                            a /^#!.*perl/ line was already present).
27        -s stripsuffix  strip this suffix from file before appending ".bat"
28                            Not case-sensitive
29                            Can be a regex if it begins with '/'
30                            Defaults to "/\.plx?/"
31        -h              show this help
32EOT
33
34my %OPT = ();
35warn($usage), exit(0) if !getopts('whun:o:a:s:',\%OPT) or $OPT{'h'};
36# NOTE: %0 is already enclosed in double quotes by cmd.exe, as appropriate
37$OPT{'n'} = '-x -S %0 %*' unless exists $OPT{'n'};
38$OPT{'o'} = '-x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9' unless exists $OPT{'o'};
39$OPT{'s'} = '/\\.plx?/' unless exists $OPT{'s'};
40$OPT{'s'} = ($OPT{'s'} =~ m#^/([^/]*[^/\$]|)\$?/?$# ? $1 : "\Q$OPT{'s'}\E");
41
42my $head;
43if(  defined( $OPT{'a'} )  ) {
44    $head = <<EOT;
45	\@rem = '--*-Perl-*--
46	\@echo off
47	perl $OPT{'a'}
48	goto endofperl
49	\@rem ';
50EOT
51} else {
52    $head = <<EOT;
53	\@rem = '--*-Perl-*--
54	\@echo off
55	if "%OS%" == "Windows_NT" goto WinNT
56	perl $OPT{'o'}
57	goto endofperl
58	:WinNT
59	perl $OPT{'n'}
60	if NOT "%COMSPEC%" == "%SystemRoot%\\system32\\cmd.exe" goto endofperl
61	if %errorlevel% == 9009 echo You do not have Perl in your PATH.
62	if errorlevel 1 goto script_failed_so_exit_with_non_zero_val 2>nul
63	goto endofperl
64	\@rem ';
65EOT
66}
67$head =~ s/^\t//gm;
68my $headlines = 2 + ($head =~ tr/\n/\n/);
69my $tail = "\n__END__\n:endofperl\n";
70
71@ARGV = ('-') unless @ARGV;
72
73foreach ( @ARGV ) {
74    process($_);
75}
76
77sub process {
78 my( $file )= @_;
79    my $myhead = $head;
80    my $linedone = 0;
81    my $taildone = 0;
82    my $linenum = 0;
83    my $skiplines = 0;
84    my $line;
85    my $start= $Config{startperl};
86    $start= "#!perl"   unless  $start =~ /^#!.*perl/;
87    open( FILE, '<', $file ) or die "$0: Can't open $file: $!";
88    @file = <FILE>;
89    foreach $line ( @file ) {
90	$linenum++;
91	if ( $line =~ /^:endofperl\b/ ) {
92	    if(  ! exists $OPT{'u'}  ) {
93		warn "$0: $file has already been converted to a batch file!\n";
94		return;
95	    }
96	    $taildone++;
97	}
98	if ( not $linedone and $line =~ /^#!.*perl/ ) {
99	    if(  exists $OPT{'u'}  ) {
100		$skiplines = $linenum - 1;
101		$line .= "#line ".(1+$headlines)."\n";
102	    } else {
103		$line .= "#line ".($linenum+$headlines)."\n";
104	    }
105	    $linedone++;
106	}
107	if ( $line =~ /^#\s*line\b/ and $linenum == 2 + $skiplines ) {
108	    $line = "";
109	}
110    }
111    close( FILE );
112    $file =~ s/$OPT{'s'}$//oi;
113    $file .= '.bat' unless $file =~ /\.bat$/i or $file =~ /^-$/;
114    open( FILE, '>', $file ) or die "Can't open $file: $!";
115    print FILE $myhead;
116    print FILE $start, ( $OPT{'w'} ? " -w" : "" ),
117	       "\n#line ", ($headlines+1), "\n" unless $linedone;
118    print FILE @file[$skiplines..$#file];
119    print FILE $tail unless $taildone;
120    close( FILE );
121}
122__END__
123
124=head1 NAME
125
126pl2bat - wrap perl code into a batch file
127
128=head1 SYNOPSIS
129
130B<pl2bat> B<-h>
131
132B<pl2bat> [B<-w>] S<[B<-a> I<argstring>]> S<[B<-s> I<stripsuffix>]> [files]
133
134B<pl2bat> [B<-w>] S<[B<-n> I<ntargs>]> S<[B<-o> I<otherargs>]> S<[B<-s> I<stripsuffix>]> [files]
135
136=head1 DESCRIPTION
137
138This utility converts a perl script into a batch file that can be
139executed on DOS-like operating systems.  This is intended to allow
140you to use a Perl script like regular programs and batch files where
141you just enter the name of the script [probably minus the extension]
142plus any command-line arguments and the script is found in your B<PATH>
143and run.
144
145=head2 ADVANTAGES
146
147There are several alternatives to this method of running a Perl script.
148They each have disadvantages that help you understand the motivation
149for using B<pl2bat>.
150
151=over
152
153=item 1
154
155    C:> perl x:/path/to/script.pl [args]
156
157=item 2
158
159    C:> perl -S script.pl [args]
160
161=item 3
162
163    C:> perl -S script [args]
164
165=item 4
166
167    C:> ftype Perl=perl.exe "%1" %*
168    C:> assoc .pl=Perl
169    then
170    C:> script.pl [args]
171
172=item 5
173
174    C:> ftype Perl=perl.exe "%1" %*
175    C:> assoc .pl=Perl
176    C:> set PathExt=%PathExt%;.PL
177    then
178    C:> script [args]
179
180=back
181
182B<1> and B<2> are the most basic invocation methods that should work on
183any system [DOS-like or not].  They require extra typing and require
184that the script user know that the script is written in Perl.  This
185is a pain when you have lots of scripts, some written in Perl and some
186not.  It can be quite difficult to keep track of which scripts need to
187be run through Perl and which do not.  Even worse, scripts often get
188rewritten from simple batch files into more powerful Perl scripts in
189which case these methods would require all existing users of the scripts
190be updated.
191
192B<3> works on modern Win32 versions of Perl.  It allows the user to
193omit the ".pl" or ".bat" file extension, which is a minor improvement.
194
195B<4> and B<5> work on some Win32 operating systems with some command
196shells.  One major disadvantage with both is that you can't use them
197in pipelines nor with file redirection.  For example, none of the
198following will work properly if you used method B<4> or B<5>:
199
200    C:> script.pl <infile
201    C:> script.pl >outfile
202    C:> echo y | script.pl
203    C:> script.pl | more
204
205This is due to a Win32 bug which Perl has no control over.  This bug
206is the major motivation for B<pl2bat> [which was originally written
207for DOS] being used on Win32 systems.
208
209Note also that B<5> works on a smaller range of combinations of Win32
210systems and command shells while B<4> requires that the user know
211that the script is a Perl script [because the ".pl" extension must
212be entered].  This makes it hard to standardize on either of these
213methods.
214
215=head2 DISADVANTAGES
216
217There are several potential traps you should be aware of when you
218use B<pl2bat>.
219
220The generated batch file is initially processed as a batch file each
221time it is run.  This means that, to use it from within another batch
222file you should precede it with C<call> or else the calling batch
223file will not run any commands after the script:
224
225    call script [args]
226
227Except under Windows NT, if you specify more than 9 arguments to
228the generated batch file then the 10th and subsequent arguments
229are silently ignored.
230
231Except when using F<CMD.EXE> under Windows NT, if F<perl.exe> is not
232in your B<PATH>, then trying to run the script will give you a generic
233"Command not found"-type of error message that will probably make you
234think that the script itself is not in your B<PATH>.  When using
235F<CMD.EXE> under Windows NT, the generic error message is followed by
236"You do not have Perl in your PATH", to make this clearer.
237
238On most DOS-like operating systems, the only way to exit a batch file
239is to "fall off the end" of the file.  B<pl2bat> implements this by
240doing C<goto :endofperl> and adding C<__END__> and C<:endofperl> as
241the last two lines of the generated batch file.  This means:
242
243=over
244
245=item No line of your script should start with a colon.
246
247In particular, for this version of B<pl2bat>, C<:endofperl>,
248C<:WinNT>, and C<:script_failed_so_exit_with_non_zero_val> should not
249be used.
250
251=item Care must be taken when using C<__END__> and the C<DATA> file handle.
252
253One approach is:
254
255    .  #!perl
256    .  while( <DATA> ) {
257    .	  last   if  /^__END__$/;
258    .	  [...]
259    .  }
260    .  __END__
261    .  lines of data
262    .  to be processed
263    .  __END__
264    .  :endofperl
265
266The dots in the first column are only there to prevent F<cmd.exe> to interpret
267the C<:endofperl> line in this documentation.  Otherwise F<pl2bat.bat> itself
268wouldn't work.  See the previous item. :-)
269
270=item The batch file always "succeeds"
271
272The following commands illustrate the problem:
273
274    C:> echo exit(99); >fail.pl
275    C:> pl2bat fail.pl
276    C:> perl -e "print system('perl fail.pl')"
277    99
278    C:> perl -e "print system('fail.bat')"
279    0
280
281So F<fail.bat> always reports that it completed successfully.  Actually,
282under Windows NT, we have:
283
284    C:> perl -e "print system('fail.bat')"
285    1
286
287So, for Windows NT, F<fail.bat> fails when the Perl script fails, but
288the return code is always C<1>, not the return code from the Perl script.
289
290=back
291
292=head2 FUNCTION
293
294By default, the ".pl" suffix will be stripped before adding a ".bat" suffix
295to the supplied file names.  This can be controlled with the C<-s> option.
296
297The default behavior is to have the batch file compare the C<OS>
298environment variable against C<"Windows_NT">.  If they match, it
299uses the C<%*> construct to refer to all the command line arguments
300that were given to it, so you'll need to make sure that works on your
301variant of the command shell.  It is known to work in the F<CMD.EXE> shell
302under Windows NT.  4DOS/NT users will want to put a C<ParameterChar = *>
303line in their initialization file, or execute C<setdos /p*> in
304the shell startup file.
305
306On Windows95 and other platforms a nine-argument limit is imposed
307on command-line arguments given to the generated batch file, since
308they may not support C<%*> in batch files.
309
310These can be overridden using the C<-n> and C<-o> options or the
311deprecated C<-a> option.
312
313=head1 OPTIONS
314
315=over 8
316
317=item B<-n> I<ntargs>
318
319Arguments to invoke perl with in generated batch file when run from
320Windows NT (or Windows 98, probably).  Defaults to S<'-x -S %0 %*'>.
321
322=item B<-o> I<otherargs>
323
324Arguments to invoke perl with in generated batch file except when
325run from Windows NT (ie. when run from DOS, Windows 3.1, or Windows 95).
326Defaults to S<'-x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9'>.
327
328=item B<-a> I<argstring>
329
330Arguments to invoke perl with in generated batch file.  Specifying
331B<-a> prevents the batch file from checking the C<OS> environment
332variable to determine which operating system it is being run from.
333
334=item B<-s> I<stripsuffix>
335
336Strip a suffix string from file name before appending a ".bat"
337suffix.  The suffix is not case-sensitive.  It can be a regex if
338it begins with '/' (the trailing '/' is optional and a trailing
339C<$> is always assumed).  Defaults to C</.plx?/>.
340
341=item B<-w>
342
343If no line matching C</^#!.*perl/> is found in the script, then such
344a line is inserted just after the new preamble.  The exact line
345depends on C<$Config{startperl}> [see L<Config>].  With the B<-w>
346option, C<" -w"> is added after the value of C<$Config{startperl}>.
347If a line matching C</^#!.*perl/> already exists in the script,
348then it is not changed and the B<-w> option is ignored.
349
350=item B<-u>
351
352If the script appears to have already been processed by B<pl2bat>,
353then the script is skipped and not processed unless B<-u> was
354specified.  If B<-u> is specified, the existing preamble is replaced.
355
356=item B<-h>
357
358Show command line usage.
359
360=back
361
362=head1 EXAMPLES
363
364	C:\> pl2bat foo.pl bar.PM
365	[..creates foo.bat, bar.PM.bat..]
366
367	C:\> pl2bat -s "/\.pl|\.pm/" foo.pl bar.PM
368	[..creates foo.bat, bar.bat..]
369
370	C:\> pl2bat < somefile > another.bat
371
372	C:\> pl2bat > another.bat
373	print scalar reverse "rekcah lrep rehtona tsuj\n";
374	^Z
375	[..another.bat is now a certified japh application..]
376
377	C:\> ren *.bat *.pl
378	C:\> pl2bat -u *.pl
379	[..updates the wrapping of some previously wrapped scripts..]
380
381	C:\> pl2bat -u -s .bat *.bat
382	[..same as previous example except more dangerous..]
383
384=head1 BUGS
385
386C<$0> will contain the full name, including the ".bat" suffix
387when the generated batch file runs.  If you don't like this,
388see runperl.bat for an alternative way to invoke perl scripts.
389
390Default behavior is to invoke Perl with the B<-S> flag, so Perl will
391search the B<PATH> to find the script.   This may have undesirable
392effects.
393
394On really old versions of Win32 Perl, you can't run the script
395via
396
397    C:> script.bat [args]
398
399and must use
400
401    C:> script [args]
402
403A loop should be used to build up the argument list when not on
404Windows NT so more than 9 arguments can be processed.
405
406See also L</DISADVANTAGES>.
407
408=head1 SEE ALSO
409
410perl, perlwin32, runperl.bat
411
412=cut
413
414