xref: /onnv-gate/usr/src/cmd/perl/5.8.4/distrib/lib/File/Temp.pm (revision 0:68f95e015346)
1*0Sstevel@tonic-gatepackage File::Temp;
2*0Sstevel@tonic-gate
3*0Sstevel@tonic-gate=head1 NAME
4*0Sstevel@tonic-gate
5*0Sstevel@tonic-gateFile::Temp - return name and handle of a temporary file safely
6*0Sstevel@tonic-gate
7*0Sstevel@tonic-gate=begin __INTERNALS
8*0Sstevel@tonic-gate
9*0Sstevel@tonic-gate=head1 PORTABILITY
10*0Sstevel@tonic-gate
11*0Sstevel@tonic-gateThis module is designed to be portable across operating systems
12*0Sstevel@tonic-gateand it currently supports Unix, VMS, DOS, OS/2, Windows and
13*0Sstevel@tonic-gateMac OS (Classic). When
14*0Sstevel@tonic-gateporting to a new OS there are generally three main issues
15*0Sstevel@tonic-gatethat have to be solved:
16*0Sstevel@tonic-gate
17*0Sstevel@tonic-gate=over 4
18*0Sstevel@tonic-gate
19*0Sstevel@tonic-gate=item *
20*0Sstevel@tonic-gate
21*0Sstevel@tonic-gateCan the OS unlink an open file? If it can not then the
22*0Sstevel@tonic-gateC<_can_unlink_opened_file> method should be modified.
23*0Sstevel@tonic-gate
24*0Sstevel@tonic-gate=item *
25*0Sstevel@tonic-gate
26*0Sstevel@tonic-gateAre the return values from C<stat> reliable? By default all the
27*0Sstevel@tonic-gatereturn values from C<stat> are compared when unlinking a temporary
28*0Sstevel@tonic-gatefile using the filename and the handle. Operating systems other than
29*0Sstevel@tonic-gateunix do not always have valid entries in all fields. If C<unlink0> fails
30*0Sstevel@tonic-gatethen the C<stat> comparison should be modified accordingly.
31*0Sstevel@tonic-gate
32*0Sstevel@tonic-gate=item *
33*0Sstevel@tonic-gate
34*0Sstevel@tonic-gateSecurity. Systems that can not support a test for the sticky bit
35*0Sstevel@tonic-gateon a directory can not use the MEDIUM and HIGH security tests.
36*0Sstevel@tonic-gateThe C<_can_do_level> method should be modified accordingly.
37*0Sstevel@tonic-gate
38*0Sstevel@tonic-gate=back
39*0Sstevel@tonic-gate
40*0Sstevel@tonic-gate=end __INTERNALS
41*0Sstevel@tonic-gate
42*0Sstevel@tonic-gate=head1 SYNOPSIS
43*0Sstevel@tonic-gate
44*0Sstevel@tonic-gate  use File::Temp qw/ tempfile tempdir /;
45*0Sstevel@tonic-gate
46*0Sstevel@tonic-gate  $dir = tempdir( CLEANUP => 1 );
47*0Sstevel@tonic-gate  ($fh, $filename) = tempfile( DIR => $dir );
48*0Sstevel@tonic-gate
49*0Sstevel@tonic-gate  ($fh, $filename) = tempfile( $template, DIR => $dir);
50*0Sstevel@tonic-gate  ($fh, $filename) = tempfile( $template, SUFFIX => '.dat');
51*0Sstevel@tonic-gate
52*0Sstevel@tonic-gate  $fh = tempfile();
53*0Sstevel@tonic-gate
54*0Sstevel@tonic-gateObject interface:
55*0Sstevel@tonic-gate
56*0Sstevel@tonic-gate  require File::Temp;
57*0Sstevel@tonic-gate  use File::Temp ();
58*0Sstevel@tonic-gate
59*0Sstevel@tonic-gate  $fh = new File::Temp($template);
60*0Sstevel@tonic-gate  $fname = $fh->filename;
61*0Sstevel@tonic-gate
62*0Sstevel@tonic-gate  $tmp = new File::Temp( UNLINK => 0, SUFFIX => '.dat' );
63*0Sstevel@tonic-gate  print $tmp "Some data\n";
64*0Sstevel@tonic-gate  print "Filename is $tmp\n";
65*0Sstevel@tonic-gate
66*0Sstevel@tonic-gate
67*0Sstevel@tonic-gateMkTemp family:
68*0Sstevel@tonic-gate
69*0Sstevel@tonic-gate  use File::Temp qw/ :mktemp  /;
70*0Sstevel@tonic-gate
71*0Sstevel@tonic-gate  ($fh, $file) = mkstemp( "tmpfileXXXXX" );
72*0Sstevel@tonic-gate  ($fh, $file) = mkstemps( "tmpfileXXXXXX", $suffix);
73*0Sstevel@tonic-gate
74*0Sstevel@tonic-gate  $tmpdir = mkdtemp( $template );
75*0Sstevel@tonic-gate
76*0Sstevel@tonic-gate  $unopened_file = mktemp( $template );
77*0Sstevel@tonic-gate
78*0Sstevel@tonic-gatePOSIX functions:
79*0Sstevel@tonic-gate
80*0Sstevel@tonic-gate  use File::Temp qw/ :POSIX /;
81*0Sstevel@tonic-gate
82*0Sstevel@tonic-gate  $file = tmpnam();
83*0Sstevel@tonic-gate  $fh = tmpfile();
84*0Sstevel@tonic-gate
85*0Sstevel@tonic-gate  ($fh, $file) = tmpnam();
86*0Sstevel@tonic-gate  $fh = tmpfile();
87*0Sstevel@tonic-gate
88*0Sstevel@tonic-gate
89*0Sstevel@tonic-gateCompatibility functions:
90*0Sstevel@tonic-gate
91*0Sstevel@tonic-gate  $unopened_file = File::Temp::tempnam( $dir, $pfx );
92*0Sstevel@tonic-gate
93*0Sstevel@tonic-gate=head1 DESCRIPTION
94*0Sstevel@tonic-gate
95*0Sstevel@tonic-gateC<File::Temp> can be used to create and open temporary files in a safe
96*0Sstevel@tonic-gateway.  There is both a function interface and an object-oriented
97*0Sstevel@tonic-gateinterface.  The File::Temp constructor or the tempfile() function can
98*0Sstevel@tonic-gatebe used to return the name and the open filehandle of a temporary
99*0Sstevel@tonic-gatefile.  The tempdir() function can be used to create a temporary
100*0Sstevel@tonic-gatedirectory.
101*0Sstevel@tonic-gate
102*0Sstevel@tonic-gateThe security aspect of temporary file creation is emphasized such that
103*0Sstevel@tonic-gatea filehandle and filename are returned together.  This helps guarantee
104*0Sstevel@tonic-gatethat a race condition can not occur where the temporary file is
105*0Sstevel@tonic-gatecreated by another process between checking for the existence of the
106*0Sstevel@tonic-gatefile and its opening.  Additional security levels are provided to
107*0Sstevel@tonic-gatecheck, for example, that the sticky bit is set on world writable
108*0Sstevel@tonic-gatedirectories.  See L<"safe_level"> for more information.
109*0Sstevel@tonic-gate
110*0Sstevel@tonic-gateFor compatibility with popular C library functions, Perl implementations of
111*0Sstevel@tonic-gatethe mkstemp() family of functions are provided. These are, mkstemp(),
112*0Sstevel@tonic-gatemkstemps(), mkdtemp() and mktemp().
113*0Sstevel@tonic-gate
114*0Sstevel@tonic-gateAdditionally, implementations of the standard L<POSIX|POSIX>
115*0Sstevel@tonic-gatetmpnam() and tmpfile() functions are provided if required.
116*0Sstevel@tonic-gate
117*0Sstevel@tonic-gateImplementations of mktemp(), tmpnam(), and tempnam() are provided,
118*0Sstevel@tonic-gatebut should be used with caution since they return only a filename
119*0Sstevel@tonic-gatethat was valid when function was called, so cannot guarantee
120*0Sstevel@tonic-gatethat the file will not exist by the time the caller opens the filename.
121*0Sstevel@tonic-gate
122*0Sstevel@tonic-gate=cut
123*0Sstevel@tonic-gate
124*0Sstevel@tonic-gate# 5.6.0 gives us S_IWOTH, S_IWGRP, our and auto-vivifying filehandls
125*0Sstevel@tonic-gate# People would like a version on 5.005 so give them what they want :-)
126*0Sstevel@tonic-gateuse 5.005;
127*0Sstevel@tonic-gateuse strict;
128*0Sstevel@tonic-gateuse Carp;
129*0Sstevel@tonic-gateuse File::Spec 0.8;
130*0Sstevel@tonic-gateuse File::Path qw/ rmtree /;
131*0Sstevel@tonic-gateuse Fcntl 1.03;
132*0Sstevel@tonic-gateuse Errno;
133*0Sstevel@tonic-gaterequire VMS::Stdio if $^O eq 'VMS';
134*0Sstevel@tonic-gate
135*0Sstevel@tonic-gate# Need the Symbol package if we are running older perl
136*0Sstevel@tonic-gaterequire Symbol if $] < 5.006;
137*0Sstevel@tonic-gate
138*0Sstevel@tonic-gate### For the OO interface
139*0Sstevel@tonic-gateuse base qw/ IO::Handle /;
140*0Sstevel@tonic-gateuse overload '""' => "STRINGIFY";
141*0Sstevel@tonic-gate
142*0Sstevel@tonic-gate
143*0Sstevel@tonic-gate# use 'our' on v5.6.0
144*0Sstevel@tonic-gateuse vars qw($VERSION @EXPORT_OK %EXPORT_TAGS $DEBUG);
145*0Sstevel@tonic-gate
146*0Sstevel@tonic-gate$DEBUG = 0;
147*0Sstevel@tonic-gate
148*0Sstevel@tonic-gate# We are exporting functions
149*0Sstevel@tonic-gate
150*0Sstevel@tonic-gateuse base qw/Exporter/;
151*0Sstevel@tonic-gate
152*0Sstevel@tonic-gate# Export list - to allow fine tuning of export table
153*0Sstevel@tonic-gate
154*0Sstevel@tonic-gate@EXPORT_OK = qw{
155*0Sstevel@tonic-gate	      tempfile
156*0Sstevel@tonic-gate	      tempdir
157*0Sstevel@tonic-gate	      tmpnam
158*0Sstevel@tonic-gate	      tmpfile
159*0Sstevel@tonic-gate	      mktemp
160*0Sstevel@tonic-gate	      mkstemp
161*0Sstevel@tonic-gate	      mkstemps
162*0Sstevel@tonic-gate	      mkdtemp
163*0Sstevel@tonic-gate	      unlink0
164*0Sstevel@tonic-gate		};
165*0Sstevel@tonic-gate
166*0Sstevel@tonic-gate# Groups of functions for export
167*0Sstevel@tonic-gate
168*0Sstevel@tonic-gate%EXPORT_TAGS = (
169*0Sstevel@tonic-gate		'POSIX' => [qw/ tmpnam tmpfile /],
170*0Sstevel@tonic-gate		'mktemp' => [qw/ mktemp mkstemp mkstemps mkdtemp/],
171*0Sstevel@tonic-gate	       );
172*0Sstevel@tonic-gate
173*0Sstevel@tonic-gate# add contents of these tags to @EXPORT
174*0Sstevel@tonic-gateExporter::export_tags('POSIX','mktemp');
175*0Sstevel@tonic-gate
176*0Sstevel@tonic-gate# Version number
177*0Sstevel@tonic-gate
178*0Sstevel@tonic-gate$VERSION = '0.14';
179*0Sstevel@tonic-gate
180*0Sstevel@tonic-gate# This is a list of characters that can be used in random filenames
181*0Sstevel@tonic-gate
182*0Sstevel@tonic-gatemy @CHARS = (qw/ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
183*0Sstevel@tonic-gate	         a b c d e f g h i j k l m n o p q r s t u v w x y z
184*0Sstevel@tonic-gate	         0 1 2 3 4 5 6 7 8 9 _
185*0Sstevel@tonic-gate	     /);
186*0Sstevel@tonic-gate
187*0Sstevel@tonic-gate# Maximum number of tries to make a temp file before failing
188*0Sstevel@tonic-gate
189*0Sstevel@tonic-gateuse constant MAX_TRIES => 10;
190*0Sstevel@tonic-gate
191*0Sstevel@tonic-gate# Minimum number of X characters that should be in a template
192*0Sstevel@tonic-gateuse constant MINX => 4;
193*0Sstevel@tonic-gate
194*0Sstevel@tonic-gate# Default template when no template supplied
195*0Sstevel@tonic-gate
196*0Sstevel@tonic-gateuse constant TEMPXXX => 'X' x 10;
197*0Sstevel@tonic-gate
198*0Sstevel@tonic-gate# Constants for the security level
199*0Sstevel@tonic-gate
200*0Sstevel@tonic-gateuse constant STANDARD => 0;
201*0Sstevel@tonic-gateuse constant MEDIUM   => 1;
202*0Sstevel@tonic-gateuse constant HIGH     => 2;
203*0Sstevel@tonic-gate
204*0Sstevel@tonic-gate# OPENFLAGS. If we defined the flag to use with Sysopen here this gives
205*0Sstevel@tonic-gate# us an optimisation when many temporary files are requested
206*0Sstevel@tonic-gate
207*0Sstevel@tonic-gatemy $OPENFLAGS = O_CREAT | O_EXCL | O_RDWR;
208*0Sstevel@tonic-gate
209*0Sstevel@tonic-gateunless ($^O eq 'MacOS') {
210*0Sstevel@tonic-gate  for my $oflag (qw/ FOLLOW BINARY LARGEFILE EXLOCK NOINHERIT /) {
211*0Sstevel@tonic-gate    my ($bit, $func) = (0, "Fcntl::O_" . $oflag);
212*0Sstevel@tonic-gate    no strict 'refs';
213*0Sstevel@tonic-gate    $OPENFLAGS |= $bit if eval {
214*0Sstevel@tonic-gate      # Make sure that redefined die handlers do not cause problems
215*0Sstevel@tonic-gate      # eg CGI::Carp
216*0Sstevel@tonic-gate      local $SIG{__DIE__} = sub {};
217*0Sstevel@tonic-gate      local $SIG{__WARN__} = sub {};
218*0Sstevel@tonic-gate      $bit = &$func();
219*0Sstevel@tonic-gate      1;
220*0Sstevel@tonic-gate    };
221*0Sstevel@tonic-gate  }
222*0Sstevel@tonic-gate}
223*0Sstevel@tonic-gate
224*0Sstevel@tonic-gate# On some systems the O_TEMPORARY flag can be used to tell the OS
225*0Sstevel@tonic-gate# to automatically remove the file when it is closed. This is fine
226*0Sstevel@tonic-gate# in most cases but not if tempfile is called with UNLINK=>0 and
227*0Sstevel@tonic-gate# the filename is requested -- in the case where the filename is to
228*0Sstevel@tonic-gate# be passed to another routine. This happens on windows. We overcome
229*0Sstevel@tonic-gate# this by using a second open flags variable
230*0Sstevel@tonic-gate
231*0Sstevel@tonic-gatemy $OPENTEMPFLAGS = $OPENFLAGS;
232*0Sstevel@tonic-gateunless ($^O eq 'MacOS') {
233*0Sstevel@tonic-gate  for my $oflag (qw/ TEMPORARY /) {
234*0Sstevel@tonic-gate    my ($bit, $func) = (0, "Fcntl::O_" . $oflag);
235*0Sstevel@tonic-gate    no strict 'refs';
236*0Sstevel@tonic-gate    $OPENTEMPFLAGS |= $bit if eval {
237*0Sstevel@tonic-gate      # Make sure that redefined die handlers do not cause problems
238*0Sstevel@tonic-gate      # eg CGI::Carp
239*0Sstevel@tonic-gate      local $SIG{__DIE__} = sub {};
240*0Sstevel@tonic-gate      local $SIG{__WARN__} = sub {};
241*0Sstevel@tonic-gate      $bit = &$func();
242*0Sstevel@tonic-gate      1;
243*0Sstevel@tonic-gate    };
244*0Sstevel@tonic-gate  }
245*0Sstevel@tonic-gate}
246*0Sstevel@tonic-gate
247*0Sstevel@tonic-gate# INTERNAL ROUTINES - not to be used outside of package
248*0Sstevel@tonic-gate
249*0Sstevel@tonic-gate# Generic routine for getting a temporary filename
250*0Sstevel@tonic-gate# modelled on OpenBSD _gettemp() in mktemp.c
251*0Sstevel@tonic-gate
252*0Sstevel@tonic-gate# The template must contain X's that are to be replaced
253*0Sstevel@tonic-gate# with the random values
254*0Sstevel@tonic-gate
255*0Sstevel@tonic-gate#  Arguments:
256*0Sstevel@tonic-gate
257*0Sstevel@tonic-gate#  TEMPLATE   - string containing the XXXXX's that is converted
258*0Sstevel@tonic-gate#           to a random filename and opened if required
259*0Sstevel@tonic-gate
260*0Sstevel@tonic-gate# Optionally, a hash can also be supplied containing specific options
261*0Sstevel@tonic-gate#   "open" => if true open the temp file, else just return the name
262*0Sstevel@tonic-gate#             default is 0
263*0Sstevel@tonic-gate#   "mkdir"=> if true, we are creating a temp directory rather than tempfile
264*0Sstevel@tonic-gate#             default is 0
265*0Sstevel@tonic-gate#   "suffixlen" => number of characters at end of PATH to be ignored.
266*0Sstevel@tonic-gate#                  default is 0.
267*0Sstevel@tonic-gate#   "unlink_on_close" => indicates that, if possible,  the OS should remove
268*0Sstevel@tonic-gate#                        the file as soon as it is closed. Usually indicates
269*0Sstevel@tonic-gate#                        use of the O_TEMPORARY flag to sysopen.
270*0Sstevel@tonic-gate#                        Usually irrelevant on unix
271*0Sstevel@tonic-gate
272*0Sstevel@tonic-gate# Optionally a reference to a scalar can be passed into the function
273*0Sstevel@tonic-gate# On error this will be used to store the reason for the error
274*0Sstevel@tonic-gate#   "ErrStr"  => \$errstr
275*0Sstevel@tonic-gate
276*0Sstevel@tonic-gate# "open" and "mkdir" can not both be true
277*0Sstevel@tonic-gate# "unlink_on_close" is not used when "mkdir" is true.
278*0Sstevel@tonic-gate
279*0Sstevel@tonic-gate# The default options are equivalent to mktemp().
280*0Sstevel@tonic-gate
281*0Sstevel@tonic-gate# Returns:
282*0Sstevel@tonic-gate#   filehandle - open file handle (if called with doopen=1, else undef)
283*0Sstevel@tonic-gate#   temp name  - name of the temp file or directory
284*0Sstevel@tonic-gate
285*0Sstevel@tonic-gate# For example:
286*0Sstevel@tonic-gate#   ($fh, $name) = _gettemp($template, "open" => 1);
287*0Sstevel@tonic-gate
288*0Sstevel@tonic-gate# for the current version, failures are associated with
289*0Sstevel@tonic-gate# stored in an error string and returned to give the reason whilst debugging
290*0Sstevel@tonic-gate# This routine is not called by any external function
291*0Sstevel@tonic-gatesub _gettemp {
292*0Sstevel@tonic-gate
293*0Sstevel@tonic-gate  croak 'Usage: ($fh, $name) = _gettemp($template, OPTIONS);'
294*0Sstevel@tonic-gate    unless scalar(@_) >= 1;
295*0Sstevel@tonic-gate
296*0Sstevel@tonic-gate  # the internal error string - expect it to be overridden
297*0Sstevel@tonic-gate  # Need this in case the caller decides not to supply us a value
298*0Sstevel@tonic-gate  # need an anonymous scalar
299*0Sstevel@tonic-gate  my $tempErrStr;
300*0Sstevel@tonic-gate
301*0Sstevel@tonic-gate  # Default options
302*0Sstevel@tonic-gate  my %options = (
303*0Sstevel@tonic-gate		 "open" => 0,
304*0Sstevel@tonic-gate		 "mkdir" => 0,
305*0Sstevel@tonic-gate		 "suffixlen" => 0,
306*0Sstevel@tonic-gate		 "unlink_on_close" => 0,
307*0Sstevel@tonic-gate		 "ErrStr" => \$tempErrStr,
308*0Sstevel@tonic-gate		);
309*0Sstevel@tonic-gate
310*0Sstevel@tonic-gate  # Read the template
311*0Sstevel@tonic-gate  my $template = shift;
312*0Sstevel@tonic-gate  if (ref($template)) {
313*0Sstevel@tonic-gate    # Use a warning here since we have not yet merged ErrStr
314*0Sstevel@tonic-gate    carp "File::Temp::_gettemp: template must not be a reference";
315*0Sstevel@tonic-gate    return ();
316*0Sstevel@tonic-gate  }
317*0Sstevel@tonic-gate
318*0Sstevel@tonic-gate  # Check that the number of entries on stack are even
319*0Sstevel@tonic-gate  if (scalar(@_) % 2 != 0) {
320*0Sstevel@tonic-gate    # Use a warning here since we have not yet merged ErrStr
321*0Sstevel@tonic-gate    carp "File::Temp::_gettemp: Must have even number of options";
322*0Sstevel@tonic-gate    return ();
323*0Sstevel@tonic-gate  }
324*0Sstevel@tonic-gate
325*0Sstevel@tonic-gate  # Read the options and merge with defaults
326*0Sstevel@tonic-gate  %options = (%options, @_)  if @_;
327*0Sstevel@tonic-gate
328*0Sstevel@tonic-gate  # Make sure the error string is set to undef
329*0Sstevel@tonic-gate  ${$options{ErrStr}} = undef;
330*0Sstevel@tonic-gate
331*0Sstevel@tonic-gate  # Can not open the file and make a directory in a single call
332*0Sstevel@tonic-gate  if ($options{"open"} && $options{"mkdir"}) {
333*0Sstevel@tonic-gate    ${$options{ErrStr}} = "doopen and domkdir can not both be true\n";
334*0Sstevel@tonic-gate    return ();
335*0Sstevel@tonic-gate  }
336*0Sstevel@tonic-gate
337*0Sstevel@tonic-gate  # Find the start of the end of the  Xs (position of last X)
338*0Sstevel@tonic-gate  # Substr starts from 0
339*0Sstevel@tonic-gate  my $start = length($template) - 1 - $options{"suffixlen"};
340*0Sstevel@tonic-gate
341*0Sstevel@tonic-gate  # Check that we have at least MINX x X (eg 'XXXX") at the end of the string
342*0Sstevel@tonic-gate  # (taking suffixlen into account). Any fewer is insecure.
343*0Sstevel@tonic-gate
344*0Sstevel@tonic-gate  # Do it using substr - no reason to use a pattern match since
345*0Sstevel@tonic-gate  # we know where we are looking and what we are looking for
346*0Sstevel@tonic-gate
347*0Sstevel@tonic-gate  if (substr($template, $start - MINX + 1, MINX) ne 'X' x MINX) {
348*0Sstevel@tonic-gate    ${$options{ErrStr}} = "The template must contain at least ".
349*0Sstevel@tonic-gate      MINX . " 'X' characters\n";
350*0Sstevel@tonic-gate    return ();
351*0Sstevel@tonic-gate  }
352*0Sstevel@tonic-gate
353*0Sstevel@tonic-gate  # Replace all the X at the end of the substring with a
354*0Sstevel@tonic-gate  # random character or just all the XX at the end of a full string.
355*0Sstevel@tonic-gate  # Do it as an if, since the suffix adjusts which section to replace
356*0Sstevel@tonic-gate  # and suffixlen=0 returns nothing if used in the substr directly
357*0Sstevel@tonic-gate  # and generate a full path from the template
358*0Sstevel@tonic-gate
359*0Sstevel@tonic-gate  my $path = _replace_XX($template, $options{"suffixlen"});
360*0Sstevel@tonic-gate
361*0Sstevel@tonic-gate
362*0Sstevel@tonic-gate  # Split the path into constituent parts - eventually we need to check
363*0Sstevel@tonic-gate  # whether the directory exists
364*0Sstevel@tonic-gate  # We need to know whether we are making a temp directory
365*0Sstevel@tonic-gate  # or a tempfile
366*0Sstevel@tonic-gate
367*0Sstevel@tonic-gate  my ($volume, $directories, $file);
368*0Sstevel@tonic-gate  my $parent; # parent directory
369*0Sstevel@tonic-gate  if ($options{"mkdir"}) {
370*0Sstevel@tonic-gate    # There is no filename at the end
371*0Sstevel@tonic-gate    ($volume, $directories, $file) = File::Spec->splitpath( $path, 1);
372*0Sstevel@tonic-gate
373*0Sstevel@tonic-gate    # The parent is then $directories without the last directory
374*0Sstevel@tonic-gate    # Split the directory and put it back together again
375*0Sstevel@tonic-gate    my @dirs = File::Spec->splitdir($directories);
376*0Sstevel@tonic-gate
377*0Sstevel@tonic-gate    # If @dirs only has one entry (i.e. the directory template) that means
378*0Sstevel@tonic-gate    # we are in the current directory
379*0Sstevel@tonic-gate    if ($#dirs == 0) {
380*0Sstevel@tonic-gate      $parent = File::Spec->curdir;
381*0Sstevel@tonic-gate    } else {
382*0Sstevel@tonic-gate
383*0Sstevel@tonic-gate      if ($^O eq 'VMS') {  # need volume to avoid relative dir spec
384*0Sstevel@tonic-gate        $parent = File::Spec->catdir($volume, @dirs[0..$#dirs-1]);
385*0Sstevel@tonic-gate        $parent = 'sys$disk:[]' if $parent eq '';
386*0Sstevel@tonic-gate      } else {
387*0Sstevel@tonic-gate
388*0Sstevel@tonic-gate	# Put it back together without the last one
389*0Sstevel@tonic-gate	$parent = File::Spec->catdir(@dirs[0..$#dirs-1]);
390*0Sstevel@tonic-gate
391*0Sstevel@tonic-gate	# ...and attach the volume (no filename)
392*0Sstevel@tonic-gate	$parent = File::Spec->catpath($volume, $parent, '');
393*0Sstevel@tonic-gate      }
394*0Sstevel@tonic-gate
395*0Sstevel@tonic-gate    }
396*0Sstevel@tonic-gate
397*0Sstevel@tonic-gate  } else {
398*0Sstevel@tonic-gate
399*0Sstevel@tonic-gate    # Get rid of the last filename (use File::Basename for this?)
400*0Sstevel@tonic-gate    ($volume, $directories, $file) = File::Spec->splitpath( $path );
401*0Sstevel@tonic-gate
402*0Sstevel@tonic-gate    # Join up without the file part
403*0Sstevel@tonic-gate    $parent = File::Spec->catpath($volume,$directories,'');
404*0Sstevel@tonic-gate
405*0Sstevel@tonic-gate    # If $parent is empty replace with curdir
406*0Sstevel@tonic-gate    $parent = File::Spec->curdir
407*0Sstevel@tonic-gate      unless $directories ne '';
408*0Sstevel@tonic-gate
409*0Sstevel@tonic-gate  }
410*0Sstevel@tonic-gate
411*0Sstevel@tonic-gate  # Check that the parent directories exist
412*0Sstevel@tonic-gate  # Do this even for the case where we are simply returning a name
413*0Sstevel@tonic-gate  # not a file -- no point returning a name that includes a directory
414*0Sstevel@tonic-gate  # that does not exist or is not writable
415*0Sstevel@tonic-gate
416*0Sstevel@tonic-gate  unless (-d $parent) {
417*0Sstevel@tonic-gate    ${$options{ErrStr}} = "Parent directory ($parent) is not a directory";
418*0Sstevel@tonic-gate    return ();
419*0Sstevel@tonic-gate  }
420*0Sstevel@tonic-gate  unless (-w _) {
421*0Sstevel@tonic-gate    ${$options{ErrStr}} = "Parent directory ($parent) is not writable\n";
422*0Sstevel@tonic-gate      return ();
423*0Sstevel@tonic-gate  }
424*0Sstevel@tonic-gate
425*0Sstevel@tonic-gate
426*0Sstevel@tonic-gate  # Check the stickiness of the directory and chown giveaway if required
427*0Sstevel@tonic-gate  # If the directory is world writable the sticky bit
428*0Sstevel@tonic-gate  # must be set
429*0Sstevel@tonic-gate
430*0Sstevel@tonic-gate  if (File::Temp->safe_level == MEDIUM) {
431*0Sstevel@tonic-gate    my $safeerr;
432*0Sstevel@tonic-gate    unless (_is_safe($parent,\$safeerr)) {
433*0Sstevel@tonic-gate      ${$options{ErrStr}} = "Parent directory ($parent) is not safe ($safeerr)";
434*0Sstevel@tonic-gate      return ();
435*0Sstevel@tonic-gate    }
436*0Sstevel@tonic-gate  } elsif (File::Temp->safe_level == HIGH) {
437*0Sstevel@tonic-gate    my $safeerr;
438*0Sstevel@tonic-gate    unless (_is_verysafe($parent, \$safeerr)) {
439*0Sstevel@tonic-gate      ${$options{ErrStr}} = "Parent directory ($parent) is not safe ($safeerr)";
440*0Sstevel@tonic-gate      return ();
441*0Sstevel@tonic-gate    }
442*0Sstevel@tonic-gate  }
443*0Sstevel@tonic-gate
444*0Sstevel@tonic-gate
445*0Sstevel@tonic-gate  # Now try MAX_TRIES time to open the file
446*0Sstevel@tonic-gate  for (my $i = 0; $i < MAX_TRIES; $i++) {
447*0Sstevel@tonic-gate
448*0Sstevel@tonic-gate    # Try to open the file if requested
449*0Sstevel@tonic-gate    if ($options{"open"}) {
450*0Sstevel@tonic-gate      my $fh;
451*0Sstevel@tonic-gate
452*0Sstevel@tonic-gate      # If we are running before perl5.6.0 we can not auto-vivify
453*0Sstevel@tonic-gate      if ($] < 5.006) {
454*0Sstevel@tonic-gate	$fh = &Symbol::gensym;
455*0Sstevel@tonic-gate      }
456*0Sstevel@tonic-gate
457*0Sstevel@tonic-gate      # Try to make sure this will be marked close-on-exec
458*0Sstevel@tonic-gate      # XXX: Win32 doesn't respect this, nor the proper fcntl,
459*0Sstevel@tonic-gate      #      but may have O_NOINHERIT. This may or may not be in Fcntl.
460*0Sstevel@tonic-gate      local $^F = 2;
461*0Sstevel@tonic-gate
462*0Sstevel@tonic-gate      # Store callers umask
463*0Sstevel@tonic-gate      my $umask = umask();
464*0Sstevel@tonic-gate
465*0Sstevel@tonic-gate      # Set a known umask
466*0Sstevel@tonic-gate      umask(066);
467*0Sstevel@tonic-gate
468*0Sstevel@tonic-gate      # Attempt to open the file
469*0Sstevel@tonic-gate      my $open_success = undef;
470*0Sstevel@tonic-gate      if ( $^O eq 'VMS' and $options{"unlink_on_close"} ) {
471*0Sstevel@tonic-gate        # make it auto delete on close by setting FAB$V_DLT bit
472*0Sstevel@tonic-gate	$fh = VMS::Stdio::vmssysopen($path, $OPENFLAGS, 0600, 'fop=dlt');
473*0Sstevel@tonic-gate	$open_success = $fh;
474*0Sstevel@tonic-gate      } else {
475*0Sstevel@tonic-gate	my $flags = ( $options{"unlink_on_close"} ?
476*0Sstevel@tonic-gate		      $OPENTEMPFLAGS :
477*0Sstevel@tonic-gate		      $OPENFLAGS );
478*0Sstevel@tonic-gate	$open_success = sysopen($fh, $path, $flags, 0600);
479*0Sstevel@tonic-gate      }
480*0Sstevel@tonic-gate      if ( $open_success ) {
481*0Sstevel@tonic-gate
482*0Sstevel@tonic-gate	# Reset umask
483*0Sstevel@tonic-gate	umask($umask) if defined $umask;
484*0Sstevel@tonic-gate
485*0Sstevel@tonic-gate	# Opened successfully - return file handle and name
486*0Sstevel@tonic-gate	return ($fh, $path);
487*0Sstevel@tonic-gate
488*0Sstevel@tonic-gate      } else {
489*0Sstevel@tonic-gate	# Reset umask
490*0Sstevel@tonic-gate	umask($umask) if defined $umask;
491*0Sstevel@tonic-gate
492*0Sstevel@tonic-gate	# Error opening file - abort with error
493*0Sstevel@tonic-gate	# if the reason was anything but EEXIST
494*0Sstevel@tonic-gate	unless ($!{EEXIST}) {
495*0Sstevel@tonic-gate	  ${$options{ErrStr}} = "Could not create temp file $path: $!";
496*0Sstevel@tonic-gate	  return ();
497*0Sstevel@tonic-gate	}
498*0Sstevel@tonic-gate
499*0Sstevel@tonic-gate	# Loop round for another try
500*0Sstevel@tonic-gate
501*0Sstevel@tonic-gate      }
502*0Sstevel@tonic-gate    } elsif ($options{"mkdir"}) {
503*0Sstevel@tonic-gate
504*0Sstevel@tonic-gate      # Store callers umask
505*0Sstevel@tonic-gate      my $umask = umask();
506*0Sstevel@tonic-gate
507*0Sstevel@tonic-gate      # Set a known umask
508*0Sstevel@tonic-gate      umask(066);
509*0Sstevel@tonic-gate
510*0Sstevel@tonic-gate      # Open the temp directory
511*0Sstevel@tonic-gate      if (mkdir( $path, 0700)) {
512*0Sstevel@tonic-gate	# created okay
513*0Sstevel@tonic-gate	# Reset umask
514*0Sstevel@tonic-gate	umask($umask) if defined $umask;
515*0Sstevel@tonic-gate
516*0Sstevel@tonic-gate	return undef, $path;
517*0Sstevel@tonic-gate      } else {
518*0Sstevel@tonic-gate
519*0Sstevel@tonic-gate	# Reset umask
520*0Sstevel@tonic-gate	umask($umask) if defined $umask;
521*0Sstevel@tonic-gate
522*0Sstevel@tonic-gate	# Abort with error if the reason for failure was anything
523*0Sstevel@tonic-gate	# except EEXIST
524*0Sstevel@tonic-gate	unless ($!{EEXIST}) {
525*0Sstevel@tonic-gate	  ${$options{ErrStr}} = "Could not create directory $path: $!";
526*0Sstevel@tonic-gate	  return ();
527*0Sstevel@tonic-gate	}
528*0Sstevel@tonic-gate
529*0Sstevel@tonic-gate	# Loop round for another try
530*0Sstevel@tonic-gate
531*0Sstevel@tonic-gate      }
532*0Sstevel@tonic-gate
533*0Sstevel@tonic-gate    } else {
534*0Sstevel@tonic-gate
535*0Sstevel@tonic-gate      # Return true if the file can not be found
536*0Sstevel@tonic-gate      # Directory has been checked previously
537*0Sstevel@tonic-gate
538*0Sstevel@tonic-gate      return (undef, $path) unless -e $path;
539*0Sstevel@tonic-gate
540*0Sstevel@tonic-gate      # Try again until MAX_TRIES
541*0Sstevel@tonic-gate
542*0Sstevel@tonic-gate    }
543*0Sstevel@tonic-gate
544*0Sstevel@tonic-gate    # Did not successfully open the tempfile/dir
545*0Sstevel@tonic-gate    # so try again with a different set of random letters
546*0Sstevel@tonic-gate    # No point in trying to increment unless we have only
547*0Sstevel@tonic-gate    # 1 X say and the randomness could come up with the same
548*0Sstevel@tonic-gate    # file MAX_TRIES in a row.
549*0Sstevel@tonic-gate
550*0Sstevel@tonic-gate    # Store current attempt - in principal this implies that the
551*0Sstevel@tonic-gate    # 3rd time around the open attempt that the first temp file
552*0Sstevel@tonic-gate    # name could be generated again. Probably should store each
553*0Sstevel@tonic-gate    # attempt and make sure that none are repeated
554*0Sstevel@tonic-gate
555*0Sstevel@tonic-gate    my $original = $path;
556*0Sstevel@tonic-gate    my $counter = 0;  # Stop infinite loop
557*0Sstevel@tonic-gate    my $MAX_GUESS = 50;
558*0Sstevel@tonic-gate
559*0Sstevel@tonic-gate    do {
560*0Sstevel@tonic-gate
561*0Sstevel@tonic-gate      # Generate new name from original template
562*0Sstevel@tonic-gate      $path = _replace_XX($template, $options{"suffixlen"});
563*0Sstevel@tonic-gate
564*0Sstevel@tonic-gate      $counter++;
565*0Sstevel@tonic-gate
566*0Sstevel@tonic-gate    } until ($path ne $original || $counter > $MAX_GUESS);
567*0Sstevel@tonic-gate
568*0Sstevel@tonic-gate    # Check for out of control looping
569*0Sstevel@tonic-gate    if ($counter > $MAX_GUESS) {
570*0Sstevel@tonic-gate      ${$options{ErrStr}} = "Tried to get a new temp name different to the previous value $MAX_GUESS times.\nSomething wrong with template?? ($template)";
571*0Sstevel@tonic-gate      return ();
572*0Sstevel@tonic-gate    }
573*0Sstevel@tonic-gate
574*0Sstevel@tonic-gate  }
575*0Sstevel@tonic-gate
576*0Sstevel@tonic-gate  # If we get here, we have run out of tries
577*0Sstevel@tonic-gate  ${ $options{ErrStr} } = "Have exceeded the maximum number of attempts ("
578*0Sstevel@tonic-gate    . MAX_TRIES . ") to open temp file/dir";
579*0Sstevel@tonic-gate
580*0Sstevel@tonic-gate  return ();
581*0Sstevel@tonic-gate
582*0Sstevel@tonic-gate}
583*0Sstevel@tonic-gate
584*0Sstevel@tonic-gate# Internal routine to return a random character from the
585*0Sstevel@tonic-gate# character list. Does not do an srand() since rand()
586*0Sstevel@tonic-gate# will do one automatically
587*0Sstevel@tonic-gate
588*0Sstevel@tonic-gate# No arguments. Return value is the random character
589*0Sstevel@tonic-gate
590*0Sstevel@tonic-gate# No longer called since _replace_XX runs a few percent faster if
591*0Sstevel@tonic-gate# I inline the code. This is important if we are creating thousands of
592*0Sstevel@tonic-gate# temporary files.
593*0Sstevel@tonic-gate
594*0Sstevel@tonic-gatesub _randchar {
595*0Sstevel@tonic-gate
596*0Sstevel@tonic-gate  $CHARS[ int( rand( $#CHARS ) ) ];
597*0Sstevel@tonic-gate
598*0Sstevel@tonic-gate}
599*0Sstevel@tonic-gate
600*0Sstevel@tonic-gate# Internal routine to replace the XXXX... with random characters
601*0Sstevel@tonic-gate# This has to be done by _gettemp() every time it fails to
602*0Sstevel@tonic-gate# open a temp file/dir
603*0Sstevel@tonic-gate
604*0Sstevel@tonic-gate# Arguments:  $template (the template with XXX),
605*0Sstevel@tonic-gate#             $ignore   (number of characters at end to ignore)
606*0Sstevel@tonic-gate
607*0Sstevel@tonic-gate# Returns:    modified template
608*0Sstevel@tonic-gate
609*0Sstevel@tonic-gatesub _replace_XX {
610*0Sstevel@tonic-gate
611*0Sstevel@tonic-gate  croak 'Usage: _replace_XX($template, $ignore)'
612*0Sstevel@tonic-gate    unless scalar(@_) == 2;
613*0Sstevel@tonic-gate
614*0Sstevel@tonic-gate  my ($path, $ignore) = @_;
615*0Sstevel@tonic-gate
616*0Sstevel@tonic-gate  # Do it as an if, since the suffix adjusts which section to replace
617*0Sstevel@tonic-gate  # and suffixlen=0 returns nothing if used in the substr directly
618*0Sstevel@tonic-gate  # Alternatively, could simply set $ignore to length($path)-1
619*0Sstevel@tonic-gate  # Don't want to always use substr when not required though.
620*0Sstevel@tonic-gate
621*0Sstevel@tonic-gate  if ($ignore) {
622*0Sstevel@tonic-gate    substr($path, 0, - $ignore) =~ s/X(?=X*\z)/$CHARS[ int( rand( $#CHARS ) ) ]/ge;
623*0Sstevel@tonic-gate  } else {
624*0Sstevel@tonic-gate    $path =~ s/X(?=X*\z)/$CHARS[ int( rand( $#CHARS ) ) ]/ge;
625*0Sstevel@tonic-gate  }
626*0Sstevel@tonic-gate
627*0Sstevel@tonic-gate  return $path;
628*0Sstevel@tonic-gate}
629*0Sstevel@tonic-gate
630*0Sstevel@tonic-gate# internal routine to check to see if the directory is safe
631*0Sstevel@tonic-gate# First checks to see if the directory is not owned by the
632*0Sstevel@tonic-gate# current user or root. Then checks to see if anyone else
633*0Sstevel@tonic-gate# can write to the directory and if so, checks to see if
634*0Sstevel@tonic-gate# it has the sticky bit set
635*0Sstevel@tonic-gate
636*0Sstevel@tonic-gate# Will not work on systems that do not support sticky bit
637*0Sstevel@tonic-gate
638*0Sstevel@tonic-gate#Args:  directory path to check
639*0Sstevel@tonic-gate#       Optionally: reference to scalar to contain error message
640*0Sstevel@tonic-gate# Returns true if the path is safe and false otherwise.
641*0Sstevel@tonic-gate# Returns undef if can not even run stat() on the path
642*0Sstevel@tonic-gate
643*0Sstevel@tonic-gate# This routine based on version written by Tom Christiansen
644*0Sstevel@tonic-gate
645*0Sstevel@tonic-gate# Presumably, by the time we actually attempt to create the
646*0Sstevel@tonic-gate# file or directory in this directory, it may not be safe
647*0Sstevel@tonic-gate# anymore... Have to run _is_safe directly after the open.
648*0Sstevel@tonic-gate
649*0Sstevel@tonic-gatesub _is_safe {
650*0Sstevel@tonic-gate
651*0Sstevel@tonic-gate  my $path = shift;
652*0Sstevel@tonic-gate  my $err_ref = shift;
653*0Sstevel@tonic-gate
654*0Sstevel@tonic-gate  # Stat path
655*0Sstevel@tonic-gate  my @info = stat($path);
656*0Sstevel@tonic-gate  unless (scalar(@info)) {
657*0Sstevel@tonic-gate    $$err_ref = "stat(path) returned no values";
658*0Sstevel@tonic-gate    return 0;
659*0Sstevel@tonic-gate  };
660*0Sstevel@tonic-gate  return 1 if $^O eq 'VMS';  # owner delete control at file level
661*0Sstevel@tonic-gate
662*0Sstevel@tonic-gate  # Check to see whether owner is neither superuser (or a system uid) nor me
663*0Sstevel@tonic-gate  # Use the real uid from the $< variable
664*0Sstevel@tonic-gate  # UID is in [4]
665*0Sstevel@tonic-gate  if ($info[4] > File::Temp->top_system_uid() && $info[4] != $<) {
666*0Sstevel@tonic-gate
667*0Sstevel@tonic-gate    Carp::cluck(sprintf "uid=$info[4] topuid=%s \$<=$< path='$path'",
668*0Sstevel@tonic-gate		File::Temp->top_system_uid());
669*0Sstevel@tonic-gate
670*0Sstevel@tonic-gate    $$err_ref = "Directory owned neither by root nor the current user"
671*0Sstevel@tonic-gate      if ref($err_ref);
672*0Sstevel@tonic-gate    return 0;
673*0Sstevel@tonic-gate  }
674*0Sstevel@tonic-gate
675*0Sstevel@tonic-gate  # check whether group or other can write file
676*0Sstevel@tonic-gate  # use 066 to detect either reading or writing
677*0Sstevel@tonic-gate  # use 022 to check writability
678*0Sstevel@tonic-gate  # Do it with S_IWOTH and S_IWGRP for portability (maybe)
679*0Sstevel@tonic-gate  # mode is in info[2]
680*0Sstevel@tonic-gate  if (($info[2] & &Fcntl::S_IWGRP) ||   # Is group writable?
681*0Sstevel@tonic-gate      ($info[2] & &Fcntl::S_IWOTH) ) {  # Is world writable?
682*0Sstevel@tonic-gate    # Must be a directory
683*0Sstevel@tonic-gate    unless (-d _) {
684*0Sstevel@tonic-gate      $$err_ref = "Path ($path) is not a directory"
685*0Sstevel@tonic-gate      if ref($err_ref);
686*0Sstevel@tonic-gate      return 0;
687*0Sstevel@tonic-gate    }
688*0Sstevel@tonic-gate    # Must have sticky bit set
689*0Sstevel@tonic-gate    unless (-k _) {
690*0Sstevel@tonic-gate      $$err_ref = "Sticky bit not set on $path when dir is group|world writable"
691*0Sstevel@tonic-gate	if ref($err_ref);
692*0Sstevel@tonic-gate      return 0;
693*0Sstevel@tonic-gate    }
694*0Sstevel@tonic-gate  }
695*0Sstevel@tonic-gate
696*0Sstevel@tonic-gate  return 1;
697*0Sstevel@tonic-gate}
698*0Sstevel@tonic-gate
699*0Sstevel@tonic-gate# Internal routine to check whether a directory is safe
700*0Sstevel@tonic-gate# for temp files. Safer than _is_safe since it checks for
701*0Sstevel@tonic-gate# the possibility of chown giveaway and if that is a possibility
702*0Sstevel@tonic-gate# checks each directory in the path to see if it is safe (with _is_safe)
703*0Sstevel@tonic-gate
704*0Sstevel@tonic-gate# If _PC_CHOWN_RESTRICTED is not set, does the full test of each
705*0Sstevel@tonic-gate# directory anyway.
706*0Sstevel@tonic-gate
707*0Sstevel@tonic-gate# Takes optional second arg as scalar ref to error reason
708*0Sstevel@tonic-gate
709*0Sstevel@tonic-gatesub _is_verysafe {
710*0Sstevel@tonic-gate
711*0Sstevel@tonic-gate  # Need POSIX - but only want to bother if really necessary due to overhead
712*0Sstevel@tonic-gate  require POSIX;
713*0Sstevel@tonic-gate
714*0Sstevel@tonic-gate  my $path = shift;
715*0Sstevel@tonic-gate  print "_is_verysafe testing $path\n" if $DEBUG;
716*0Sstevel@tonic-gate  return 1 if $^O eq 'VMS';  # owner delete control at file level
717*0Sstevel@tonic-gate
718*0Sstevel@tonic-gate  my $err_ref = shift;
719*0Sstevel@tonic-gate
720*0Sstevel@tonic-gate  # Should Get the value of _PC_CHOWN_RESTRICTED if it is defined
721*0Sstevel@tonic-gate  # and If it is not there do the extensive test
722*0Sstevel@tonic-gate  my $chown_restricted;
723*0Sstevel@tonic-gate  $chown_restricted = &POSIX::_PC_CHOWN_RESTRICTED()
724*0Sstevel@tonic-gate    if eval { &POSIX::_PC_CHOWN_RESTRICTED(); 1};
725*0Sstevel@tonic-gate
726*0Sstevel@tonic-gate  # If chown_resticted is set to some value we should test it
727*0Sstevel@tonic-gate  if (defined $chown_restricted) {
728*0Sstevel@tonic-gate
729*0Sstevel@tonic-gate    # Return if the current directory is safe
730*0Sstevel@tonic-gate    return _is_safe($path,$err_ref) if POSIX::sysconf( $chown_restricted );
731*0Sstevel@tonic-gate
732*0Sstevel@tonic-gate  }
733*0Sstevel@tonic-gate
734*0Sstevel@tonic-gate  # To reach this point either, the _PC_CHOWN_RESTRICTED symbol
735*0Sstevel@tonic-gate  # was not avialable or the symbol was there but chown giveaway
736*0Sstevel@tonic-gate  # is allowed. Either way, we now have to test the entire tree for
737*0Sstevel@tonic-gate  # safety.
738*0Sstevel@tonic-gate
739*0Sstevel@tonic-gate  # Convert path to an absolute directory if required
740*0Sstevel@tonic-gate  unless (File::Spec->file_name_is_absolute($path)) {
741*0Sstevel@tonic-gate    $path = File::Spec->rel2abs($path);
742*0Sstevel@tonic-gate  }
743*0Sstevel@tonic-gate
744*0Sstevel@tonic-gate  # Split directory into components - assume no file
745*0Sstevel@tonic-gate  my ($volume, $directories, undef) = File::Spec->splitpath( $path, 1);
746*0Sstevel@tonic-gate
747*0Sstevel@tonic-gate  # Slightly less efficient than having a function in File::Spec
748*0Sstevel@tonic-gate  # to chop off the end of a directory or even a function that
749*0Sstevel@tonic-gate  # can handle ../ in a directory tree
750*0Sstevel@tonic-gate  # Sometimes splitdir() returns a blank at the end
751*0Sstevel@tonic-gate  # so we will probably check the bottom directory twice in some cases
752*0Sstevel@tonic-gate  my @dirs = File::Spec->splitdir($directories);
753*0Sstevel@tonic-gate
754*0Sstevel@tonic-gate  # Concatenate one less directory each time around
755*0Sstevel@tonic-gate  foreach my $pos (0.. $#dirs) {
756*0Sstevel@tonic-gate    # Get a directory name
757*0Sstevel@tonic-gate    my $dir = File::Spec->catpath($volume,
758*0Sstevel@tonic-gate				  File::Spec->catdir(@dirs[0.. $#dirs - $pos]),
759*0Sstevel@tonic-gate				  ''
760*0Sstevel@tonic-gate				  );
761*0Sstevel@tonic-gate
762*0Sstevel@tonic-gate    print "TESTING DIR $dir\n" if $DEBUG;
763*0Sstevel@tonic-gate
764*0Sstevel@tonic-gate    # Check the directory
765*0Sstevel@tonic-gate    return 0 unless _is_safe($dir,$err_ref);
766*0Sstevel@tonic-gate
767*0Sstevel@tonic-gate  }
768*0Sstevel@tonic-gate
769*0Sstevel@tonic-gate  return 1;
770*0Sstevel@tonic-gate}
771*0Sstevel@tonic-gate
772*0Sstevel@tonic-gate
773*0Sstevel@tonic-gate
774*0Sstevel@tonic-gate# internal routine to determine whether unlink works on this
775*0Sstevel@tonic-gate# platform for files that are currently open.
776*0Sstevel@tonic-gate# Returns true if we can, false otherwise.
777*0Sstevel@tonic-gate
778*0Sstevel@tonic-gate# Currently WinNT, OS/2 and VMS can not unlink an opened file
779*0Sstevel@tonic-gate# On VMS this is because the O_EXCL flag is used to open the
780*0Sstevel@tonic-gate# temporary file. Currently I do not know enough about the issues
781*0Sstevel@tonic-gate# on VMS to decide whether O_EXCL is a requirement.
782*0Sstevel@tonic-gate
783*0Sstevel@tonic-gatesub _can_unlink_opened_file {
784*0Sstevel@tonic-gate
785*0Sstevel@tonic-gate  if ($^O eq 'MSWin32' || $^O eq 'os2' || $^O eq 'VMS' || $^O eq 'dos' || $^O eq 'MacOS') {
786*0Sstevel@tonic-gate    return 0;
787*0Sstevel@tonic-gate  } else {
788*0Sstevel@tonic-gate    return 1;
789*0Sstevel@tonic-gate  }
790*0Sstevel@tonic-gate
791*0Sstevel@tonic-gate}
792*0Sstevel@tonic-gate
793*0Sstevel@tonic-gate# internal routine to decide which security levels are allowed
794*0Sstevel@tonic-gate# see safe_level() for more information on this
795*0Sstevel@tonic-gate
796*0Sstevel@tonic-gate# Controls whether the supplied security level is allowed
797*0Sstevel@tonic-gate
798*0Sstevel@tonic-gate#   $cando = _can_do_level( $level )
799*0Sstevel@tonic-gate
800*0Sstevel@tonic-gatesub _can_do_level {
801*0Sstevel@tonic-gate
802*0Sstevel@tonic-gate  # Get security level
803*0Sstevel@tonic-gate  my $level = shift;
804*0Sstevel@tonic-gate
805*0Sstevel@tonic-gate  # Always have to be able to do STANDARD
806*0Sstevel@tonic-gate  return 1 if $level == STANDARD;
807*0Sstevel@tonic-gate
808*0Sstevel@tonic-gate  # Currently, the systems that can do HIGH or MEDIUM are identical
809*0Sstevel@tonic-gate  if ( $^O eq 'MSWin32' || $^O eq 'os2' || $^O eq 'cygwin' || $^O eq 'dos' || $^O eq 'MacOS' || $^O eq 'mpeix') {
810*0Sstevel@tonic-gate    return 0;
811*0Sstevel@tonic-gate  } else {
812*0Sstevel@tonic-gate    return 1;
813*0Sstevel@tonic-gate  }
814*0Sstevel@tonic-gate
815*0Sstevel@tonic-gate}
816*0Sstevel@tonic-gate
817*0Sstevel@tonic-gate# This routine sets up a deferred unlinking of a specified
818*0Sstevel@tonic-gate# filename and filehandle. It is used in the following cases:
819*0Sstevel@tonic-gate#  - Called by unlink0 if an opened file can not be unlinked
820*0Sstevel@tonic-gate#  - Called by tempfile() if files are to be removed on shutdown
821*0Sstevel@tonic-gate#  - Called by tempdir() if directories are to be removed on shutdown
822*0Sstevel@tonic-gate
823*0Sstevel@tonic-gate# Arguments:
824*0Sstevel@tonic-gate#   _deferred_unlink( $fh, $fname, $isdir );
825*0Sstevel@tonic-gate#
826*0Sstevel@tonic-gate#   - filehandle (so that it can be expclicitly closed if open
827*0Sstevel@tonic-gate#   - filename   (the thing we want to remove)
828*0Sstevel@tonic-gate#   - isdir      (flag to indicate that we are being given a directory)
829*0Sstevel@tonic-gate#                 [and hence no filehandle]
830*0Sstevel@tonic-gate
831*0Sstevel@tonic-gate# Status is not referred to since all the magic is done with an END block
832*0Sstevel@tonic-gate
833*0Sstevel@tonic-gate{
834*0Sstevel@tonic-gate  # Will set up two lexical variables to contain all the files to be
835*0Sstevel@tonic-gate  # removed. One array for files, another for directories
836*0Sstevel@tonic-gate  # They will only exist in this block
837*0Sstevel@tonic-gate  # This means we only have to set up a single END block to remove all files
838*0Sstevel@tonic-gate  # @files_to_unlink contains an array ref with the filehandle and filename
839*0Sstevel@tonic-gate  my (@files_to_unlink, @dirs_to_unlink);
840*0Sstevel@tonic-gate
841*0Sstevel@tonic-gate  # Set up an end block to use these arrays
842*0Sstevel@tonic-gate  END {
843*0Sstevel@tonic-gate    # Files
844*0Sstevel@tonic-gate    foreach my $file (@files_to_unlink) {
845*0Sstevel@tonic-gate      # close the filehandle without checking its state
846*0Sstevel@tonic-gate      # in order to make real sure that this is closed
847*0Sstevel@tonic-gate      # if its already closed then I dont care about the answer
848*0Sstevel@tonic-gate      # probably a better way to do this
849*0Sstevel@tonic-gate      close($file->[0]);  # file handle is [0]
850*0Sstevel@tonic-gate
851*0Sstevel@tonic-gate      if (-f $file->[1]) {  # file name is [1]
852*0Sstevel@tonic-gate	unlink $file->[1] or warn "Error removing ".$file->[1];
853*0Sstevel@tonic-gate      }
854*0Sstevel@tonic-gate    }
855*0Sstevel@tonic-gate    # Dirs
856*0Sstevel@tonic-gate    foreach my $dir (@dirs_to_unlink) {
857*0Sstevel@tonic-gate      if (-d $dir) {
858*0Sstevel@tonic-gate	rmtree($dir, $DEBUG, 0);
859*0Sstevel@tonic-gate      }
860*0Sstevel@tonic-gate    }
861*0Sstevel@tonic-gate
862*0Sstevel@tonic-gate  }
863*0Sstevel@tonic-gate
864*0Sstevel@tonic-gate  # This is the sub called to register a file for deferred unlinking
865*0Sstevel@tonic-gate  # This could simply store the input parameters and defer everything
866*0Sstevel@tonic-gate  # until the END block. For now we do a bit of checking at this
867*0Sstevel@tonic-gate  # point in order to make sure that (1) we have a file/dir to delete
868*0Sstevel@tonic-gate  # and (2) we have been called with the correct arguments.
869*0Sstevel@tonic-gate  sub _deferred_unlink {
870*0Sstevel@tonic-gate
871*0Sstevel@tonic-gate    croak 'Usage:  _deferred_unlink($fh, $fname, $isdir)'
872*0Sstevel@tonic-gate      unless scalar(@_) == 3;
873*0Sstevel@tonic-gate
874*0Sstevel@tonic-gate    my ($fh, $fname, $isdir) = @_;
875*0Sstevel@tonic-gate
876*0Sstevel@tonic-gate    warn "Setting up deferred removal of $fname\n"
877*0Sstevel@tonic-gate      if $DEBUG;
878*0Sstevel@tonic-gate
879*0Sstevel@tonic-gate    # If we have a directory, check that it is a directory
880*0Sstevel@tonic-gate    if ($isdir) {
881*0Sstevel@tonic-gate
882*0Sstevel@tonic-gate      if (-d $fname) {
883*0Sstevel@tonic-gate
884*0Sstevel@tonic-gate	# Directory exists so store it
885*0Sstevel@tonic-gate	# first on VMS turn []foo into [.foo] for rmtree
886*0Sstevel@tonic-gate	$fname = VMS::Filespec::vmspath($fname) if $^O eq 'VMS';
887*0Sstevel@tonic-gate	push (@dirs_to_unlink, $fname);
888*0Sstevel@tonic-gate
889*0Sstevel@tonic-gate      } else {
890*0Sstevel@tonic-gate	carp "Request to remove directory $fname could not be completed since it does not exist!\n" if $^W;
891*0Sstevel@tonic-gate      }
892*0Sstevel@tonic-gate
893*0Sstevel@tonic-gate    } else {
894*0Sstevel@tonic-gate
895*0Sstevel@tonic-gate      if (-f $fname) {
896*0Sstevel@tonic-gate
897*0Sstevel@tonic-gate	# file exists so store handle and name for later removal
898*0Sstevel@tonic-gate	push(@files_to_unlink, [$fh, $fname]);
899*0Sstevel@tonic-gate
900*0Sstevel@tonic-gate      } else {
901*0Sstevel@tonic-gate	carp "Request to remove file $fname could not be completed since it is not there!\n" if $^W;
902*0Sstevel@tonic-gate      }
903*0Sstevel@tonic-gate
904*0Sstevel@tonic-gate    }
905*0Sstevel@tonic-gate
906*0Sstevel@tonic-gate  }
907*0Sstevel@tonic-gate
908*0Sstevel@tonic-gate
909*0Sstevel@tonic-gate}
910*0Sstevel@tonic-gate
911*0Sstevel@tonic-gate=head1 OO INTERFACE
912*0Sstevel@tonic-gate
913*0Sstevel@tonic-gateThis is the primary interface for interacting with
914*0Sstevel@tonic-gateC<File::Temp>. Using the OO interface a temporary file can be created
915*0Sstevel@tonic-gatewhen the object is constructed and the file can be removed when the
916*0Sstevel@tonic-gateobject is no longer required.
917*0Sstevel@tonic-gate
918*0Sstevel@tonic-gateNote that there is no method to obtain the filehandle from the
919*0Sstevel@tonic-gateC<File::Temp> object. The object itself acts as a filehandle. Also,
920*0Sstevel@tonic-gatethe object is configured such that it stringifies to the name of the
921*0Sstevel@tonic-gatetemporary file.
922*0Sstevel@tonic-gate
923*0Sstevel@tonic-gate=over 4
924*0Sstevel@tonic-gate
925*0Sstevel@tonic-gate=item B<new>
926*0Sstevel@tonic-gate
927*0Sstevel@tonic-gateCreate a temporary file object.
928*0Sstevel@tonic-gate
929*0Sstevel@tonic-gate  my $tmp = new File::Temp();
930*0Sstevel@tonic-gate
931*0Sstevel@tonic-gateby default the object is constructed as if C<tempfile>
932*0Sstevel@tonic-gatewas called without options, but with the additional behaviour
933*0Sstevel@tonic-gatethat the temporary file is removed by the object destructor
934*0Sstevel@tonic-gateif UNLINK is set to true (the default).
935*0Sstevel@tonic-gate
936*0Sstevel@tonic-gateSupported arguments are the same as for C<tempfile>: UNLINK
937*0Sstevel@tonic-gate(defaulting to true), DIR and SUFFIX. Additionally, the filename
938*0Sstevel@tonic-gatetemplate is specified using the TEMPLATE option. The OPEN option
939*0Sstevel@tonic-gateis not supported (the file is always opened).
940*0Sstevel@tonic-gate
941*0Sstevel@tonic-gate $tmp = new File::Temp( TEMPLATE => 'tempXXXXX',
942*0Sstevel@tonic-gate                        DIR => 'mydir',
943*0Sstevel@tonic-gate                        SUFFIX => '.dat');
944*0Sstevel@tonic-gate
945*0Sstevel@tonic-gateArguments are case insensitive.
946*0Sstevel@tonic-gate
947*0Sstevel@tonic-gate=cut
948*0Sstevel@tonic-gate
949*0Sstevel@tonic-gatesub new {
950*0Sstevel@tonic-gate  my $proto = shift;
951*0Sstevel@tonic-gate  my $class = ref($proto) || $proto;
952*0Sstevel@tonic-gate
953*0Sstevel@tonic-gate  # read arguments and convert keys to upper case
954*0Sstevel@tonic-gate  my %args = @_;
955*0Sstevel@tonic-gate  %args = map { uc($_), $args{$_} } keys %args;
956*0Sstevel@tonic-gate
957*0Sstevel@tonic-gate  # see if they are unlinking (defaulting to yes)
958*0Sstevel@tonic-gate  my $unlink = (exists $args{UNLINK} ? $args{UNLINK} : 1 );
959*0Sstevel@tonic-gate  delete $args{UNLINK};
960*0Sstevel@tonic-gate
961*0Sstevel@tonic-gate  # template (store it in an error so that it will
962*0Sstevel@tonic-gate  # disappear from the arg list of tempfile
963*0Sstevel@tonic-gate  my @template = ( exists $args{TEMPLATE} ? $args{TEMPLATE} : () );
964*0Sstevel@tonic-gate  delete $args{TEMPLATE};
965*0Sstevel@tonic-gate
966*0Sstevel@tonic-gate  # Protect OPEN
967*0Sstevel@tonic-gate  delete $args{OPEN};
968*0Sstevel@tonic-gate
969*0Sstevel@tonic-gate  # Open the file and retain file handle and file name
970*0Sstevel@tonic-gate  my ($fh, $path) = tempfile( @template, %args );
971*0Sstevel@tonic-gate
972*0Sstevel@tonic-gate  print "Tmp: $fh - $path\n" if $DEBUG;
973*0Sstevel@tonic-gate
974*0Sstevel@tonic-gate  # Store the filename in the scalar slot
975*0Sstevel@tonic-gate  ${*$fh} = $path;
976*0Sstevel@tonic-gate
977*0Sstevel@tonic-gate  # Store unlink information in hash slot (plus other constructor info)
978*0Sstevel@tonic-gate  %{*$fh} = %args;
979*0Sstevel@tonic-gate  ${*$fh}{UNLINK} = $unlink;
980*0Sstevel@tonic-gate
981*0Sstevel@tonic-gate  bless $fh, $class;
982*0Sstevel@tonic-gate
983*0Sstevel@tonic-gate  return $fh;
984*0Sstevel@tonic-gate}
985*0Sstevel@tonic-gate
986*0Sstevel@tonic-gate=item B<filename>
987*0Sstevel@tonic-gate
988*0Sstevel@tonic-gateReturn the name of the temporary file associated with this object.
989*0Sstevel@tonic-gate
990*0Sstevel@tonic-gate  $filename = $tmp->filename;
991*0Sstevel@tonic-gate
992*0Sstevel@tonic-gateThis method is called automatically when the object is used as
993*0Sstevel@tonic-gatea string.
994*0Sstevel@tonic-gate
995*0Sstevel@tonic-gate=cut
996*0Sstevel@tonic-gate
997*0Sstevel@tonic-gatesub filename {
998*0Sstevel@tonic-gate  my $self = shift;
999*0Sstevel@tonic-gate  return ${*$self};
1000*0Sstevel@tonic-gate}
1001*0Sstevel@tonic-gate
1002*0Sstevel@tonic-gatesub STRINGIFY {
1003*0Sstevel@tonic-gate  my $self = shift;
1004*0Sstevel@tonic-gate  return $self->filename;
1005*0Sstevel@tonic-gate}
1006*0Sstevel@tonic-gate
1007*0Sstevel@tonic-gate=item B<DESTROY>
1008*0Sstevel@tonic-gate
1009*0Sstevel@tonic-gateWhen the object goes out of scope, the destructor is called. This
1010*0Sstevel@tonic-gatedestructor will attempt to unlink the file (using C<unlink1>)
1011*0Sstevel@tonic-gateif the constructor was called with UNLINK set to 1 (the default state
1012*0Sstevel@tonic-gateif UNLINK is not specified).
1013*0Sstevel@tonic-gate
1014*0Sstevel@tonic-gateNo error is given if the unlink fails.
1015*0Sstevel@tonic-gate
1016*0Sstevel@tonic-gate=cut
1017*0Sstevel@tonic-gate
1018*0Sstevel@tonic-gatesub DESTROY {
1019*0Sstevel@tonic-gate  my $self = shift;
1020*0Sstevel@tonic-gate  if (${*$self}{UNLINK}) {
1021*0Sstevel@tonic-gate    print "# --------->   Unlinking $self\n" if $DEBUG;
1022*0Sstevel@tonic-gate
1023*0Sstevel@tonic-gate    # The unlink1 may fail if the file has been closed
1024*0Sstevel@tonic-gate    # by the caller. This leaves us with the decision
1025*0Sstevel@tonic-gate    # of whether to refuse to remove the file or simply
1026*0Sstevel@tonic-gate    # do an unlink without test. Seems to be silly
1027*0Sstevel@tonic-gate    # to do this when we are trying to be careful
1028*0Sstevel@tonic-gate    # about security
1029*0Sstevel@tonic-gate    unlink1( $self, $self->filename )
1030*0Sstevel@tonic-gate      or unlink($self->filename);
1031*0Sstevel@tonic-gate  }
1032*0Sstevel@tonic-gate}
1033*0Sstevel@tonic-gate
1034*0Sstevel@tonic-gate=back
1035*0Sstevel@tonic-gate
1036*0Sstevel@tonic-gate=head1 FUNCTIONS
1037*0Sstevel@tonic-gate
1038*0Sstevel@tonic-gateThis section describes the recommended interface for generating
1039*0Sstevel@tonic-gatetemporary files and directories.
1040*0Sstevel@tonic-gate
1041*0Sstevel@tonic-gate=over 4
1042*0Sstevel@tonic-gate
1043*0Sstevel@tonic-gate=item B<tempfile>
1044*0Sstevel@tonic-gate
1045*0Sstevel@tonic-gateThis is the basic function to generate temporary files.
1046*0Sstevel@tonic-gateThe behaviour of the file can be changed using various options:
1047*0Sstevel@tonic-gate
1048*0Sstevel@tonic-gate  ($fh, $filename) = tempfile();
1049*0Sstevel@tonic-gate
1050*0Sstevel@tonic-gateCreate a temporary file in  the directory specified for temporary
1051*0Sstevel@tonic-gatefiles, as specified by the tmpdir() function in L<File::Spec>.
1052*0Sstevel@tonic-gate
1053*0Sstevel@tonic-gate  ($fh, $filename) = tempfile($template);
1054*0Sstevel@tonic-gate
1055*0Sstevel@tonic-gateCreate a temporary file in the current directory using the supplied
1056*0Sstevel@tonic-gatetemplate.  Trailing `X' characters are replaced with random letters to
1057*0Sstevel@tonic-gategenerate the filename.  At least four `X' characters must be present
1058*0Sstevel@tonic-gateat the end of the template.
1059*0Sstevel@tonic-gate
1060*0Sstevel@tonic-gate  ($fh, $filename) = tempfile($template, SUFFIX => $suffix)
1061*0Sstevel@tonic-gate
1062*0Sstevel@tonic-gateSame as previously, except that a suffix is added to the template
1063*0Sstevel@tonic-gateafter the `X' translation.  Useful for ensuring that a temporary
1064*0Sstevel@tonic-gatefilename has a particular extension when needed by other applications.
1065*0Sstevel@tonic-gateBut see the WARNING at the end.
1066*0Sstevel@tonic-gate
1067*0Sstevel@tonic-gate  ($fh, $filename) = tempfile($template, DIR => $dir);
1068*0Sstevel@tonic-gate
1069*0Sstevel@tonic-gateTranslates the template as before except that a directory name
1070*0Sstevel@tonic-gateis specified.
1071*0Sstevel@tonic-gate
1072*0Sstevel@tonic-gate  ($fh, $filename) = tempfile($template, UNLINK => 1);
1073*0Sstevel@tonic-gate
1074*0Sstevel@tonic-gateReturn the filename and filehandle as before except that the file is
1075*0Sstevel@tonic-gateautomatically removed when the program exits. Default is for the file
1076*0Sstevel@tonic-gateto be removed if a file handle is requested and to be kept if the
1077*0Sstevel@tonic-gatefilename is requested. In a scalar context (where no filename is
1078*0Sstevel@tonic-gatereturned) the file is always deleted either on exit or when it is closed.
1079*0Sstevel@tonic-gate
1080*0Sstevel@tonic-gateIf the template is not specified, a template is always
1081*0Sstevel@tonic-gateautomatically generated. This temporary file is placed in tmpdir()
1082*0Sstevel@tonic-gate(L<File::Spec>) unless a directory is specified explicitly with the
1083*0Sstevel@tonic-gateDIR option.
1084*0Sstevel@tonic-gate
1085*0Sstevel@tonic-gate  $fh = tempfile( $template, DIR => $dir );
1086*0Sstevel@tonic-gate
1087*0Sstevel@tonic-gateIf called in scalar context, only the filehandle is returned
1088*0Sstevel@tonic-gateand the file will automatically be deleted when closed (see
1089*0Sstevel@tonic-gatethe description of tmpfile() elsewhere in this document).
1090*0Sstevel@tonic-gateThis is the preferred mode of operation, as if you only
1091*0Sstevel@tonic-gatehave a filehandle, you can never create a race condition
1092*0Sstevel@tonic-gateby fumbling with the filename. On systems that can not unlink
1093*0Sstevel@tonic-gatean open file or can not mark a file as temporary when it is opened
1094*0Sstevel@tonic-gate(for example, Windows NT uses the C<O_TEMPORARY> flag)
1095*0Sstevel@tonic-gatethe file is marked for deletion when the program ends (equivalent
1096*0Sstevel@tonic-gateto setting UNLINK to 1). The C<UNLINK> flag is ignored if present.
1097*0Sstevel@tonic-gate
1098*0Sstevel@tonic-gate  (undef, $filename) = tempfile($template, OPEN => 0);
1099*0Sstevel@tonic-gate
1100*0Sstevel@tonic-gateThis will return the filename based on the template but
1101*0Sstevel@tonic-gatewill not open this file.  Cannot be used in conjunction with
1102*0Sstevel@tonic-gateUNLINK set to true. Default is to always open the file
1103*0Sstevel@tonic-gateto protect from possible race conditions. A warning is issued
1104*0Sstevel@tonic-gateif warnings are turned on. Consider using the tmpnam()
1105*0Sstevel@tonic-gateand mktemp() functions described elsewhere in this document
1106*0Sstevel@tonic-gateif opening the file is not required.
1107*0Sstevel@tonic-gate
1108*0Sstevel@tonic-gateOptions can be combined as required.
1109*0Sstevel@tonic-gate
1110*0Sstevel@tonic-gate=cut
1111*0Sstevel@tonic-gate
1112*0Sstevel@tonic-gatesub tempfile {
1113*0Sstevel@tonic-gate
1114*0Sstevel@tonic-gate  # Can not check for argument count since we can have any
1115*0Sstevel@tonic-gate  # number of args
1116*0Sstevel@tonic-gate
1117*0Sstevel@tonic-gate  # Default options
1118*0Sstevel@tonic-gate  my %options = (
1119*0Sstevel@tonic-gate		 "DIR"    => undef,  # Directory prefix
1120*0Sstevel@tonic-gate                "SUFFIX" => '',     # Template suffix
1121*0Sstevel@tonic-gate                "UNLINK" => 0,      # Do not unlink file on exit
1122*0Sstevel@tonic-gate                "OPEN"   => 1,      # Open file
1123*0Sstevel@tonic-gate		);
1124*0Sstevel@tonic-gate
1125*0Sstevel@tonic-gate  # Check to see whether we have an odd or even number of arguments
1126*0Sstevel@tonic-gate  my $template = (scalar(@_) % 2 == 1 ? shift(@_) : undef);
1127*0Sstevel@tonic-gate
1128*0Sstevel@tonic-gate  # Read the options and merge with defaults
1129*0Sstevel@tonic-gate  %options = (%options, @_)  if @_;
1130*0Sstevel@tonic-gate
1131*0Sstevel@tonic-gate  # First decision is whether or not to open the file
1132*0Sstevel@tonic-gate  if (! $options{"OPEN"}) {
1133*0Sstevel@tonic-gate
1134*0Sstevel@tonic-gate    warn "tempfile(): temporary filename requested but not opened.\nPossibly unsafe, consider using tempfile() with OPEN set to true\n"
1135*0Sstevel@tonic-gate      if $^W;
1136*0Sstevel@tonic-gate
1137*0Sstevel@tonic-gate  }
1138*0Sstevel@tonic-gate
1139*0Sstevel@tonic-gate  if ($options{"DIR"} and $^O eq 'VMS') {
1140*0Sstevel@tonic-gate
1141*0Sstevel@tonic-gate      # on VMS turn []foo into [.foo] for concatenation
1142*0Sstevel@tonic-gate      $options{"DIR"} = VMS::Filespec::vmspath($options{"DIR"});
1143*0Sstevel@tonic-gate  }
1144*0Sstevel@tonic-gate
1145*0Sstevel@tonic-gate  # Construct the template
1146*0Sstevel@tonic-gate
1147*0Sstevel@tonic-gate  # Have a choice of trying to work around the mkstemp/mktemp/tmpnam etc
1148*0Sstevel@tonic-gate  # functions or simply constructing a template and using _gettemp()
1149*0Sstevel@tonic-gate  # explicitly. Go for the latter
1150*0Sstevel@tonic-gate
1151*0Sstevel@tonic-gate  # First generate a template if not defined and prefix the directory
1152*0Sstevel@tonic-gate  # If no template must prefix the temp directory
1153*0Sstevel@tonic-gate  if (defined $template) {
1154*0Sstevel@tonic-gate    if ($options{"DIR"}) {
1155*0Sstevel@tonic-gate
1156*0Sstevel@tonic-gate      $template = File::Spec->catfile($options{"DIR"}, $template);
1157*0Sstevel@tonic-gate
1158*0Sstevel@tonic-gate    }
1159*0Sstevel@tonic-gate
1160*0Sstevel@tonic-gate  } else {
1161*0Sstevel@tonic-gate
1162*0Sstevel@tonic-gate    if ($options{"DIR"}) {
1163*0Sstevel@tonic-gate
1164*0Sstevel@tonic-gate      $template = File::Spec->catfile($options{"DIR"}, TEMPXXX);
1165*0Sstevel@tonic-gate
1166*0Sstevel@tonic-gate    } else {
1167*0Sstevel@tonic-gate
1168*0Sstevel@tonic-gate      $template = File::Spec->catfile(File::Spec->tmpdir, TEMPXXX);
1169*0Sstevel@tonic-gate
1170*0Sstevel@tonic-gate    }
1171*0Sstevel@tonic-gate
1172*0Sstevel@tonic-gate  }
1173*0Sstevel@tonic-gate
1174*0Sstevel@tonic-gate  # Now add a suffix
1175*0Sstevel@tonic-gate  $template .= $options{"SUFFIX"};
1176*0Sstevel@tonic-gate
1177*0Sstevel@tonic-gate  # Determine whether we should tell _gettemp to unlink the file
1178*0Sstevel@tonic-gate  # On unix this is irrelevant and can be worked out after the file is
1179*0Sstevel@tonic-gate  # opened (simply by unlinking the open filehandle). On Windows or VMS
1180*0Sstevel@tonic-gate  # we have to indicate temporary-ness when we open the file. In general
1181*0Sstevel@tonic-gate  # we only want a true temporary file if we are returning just the
1182*0Sstevel@tonic-gate  # filehandle - if the user wants the filename they probably do not
1183*0Sstevel@tonic-gate  # want the file to disappear as soon as they close it.
1184*0Sstevel@tonic-gate  # For this reason, tie unlink_on_close to the return context regardless
1185*0Sstevel@tonic-gate  # of OS.
1186*0Sstevel@tonic-gate  my $unlink_on_close = ( wantarray ? 0 : 1);
1187*0Sstevel@tonic-gate
1188*0Sstevel@tonic-gate  # Create the file
1189*0Sstevel@tonic-gate  my ($fh, $path, $errstr);
1190*0Sstevel@tonic-gate  croak "Error in tempfile() using $template: $errstr"
1191*0Sstevel@tonic-gate    unless (($fh, $path) = _gettemp($template,
1192*0Sstevel@tonic-gate				    "open" => $options{'OPEN'},
1193*0Sstevel@tonic-gate				    "mkdir"=> 0 ,
1194*0Sstevel@tonic-gate                                    "unlink_on_close" => $unlink_on_close,
1195*0Sstevel@tonic-gate				    "suffixlen" => length($options{'SUFFIX'}),
1196*0Sstevel@tonic-gate				    "ErrStr" => \$errstr,
1197*0Sstevel@tonic-gate				   ) );
1198*0Sstevel@tonic-gate
1199*0Sstevel@tonic-gate  # Set up an exit handler that can do whatever is right for the
1200*0Sstevel@tonic-gate  # system. This removes files at exit when requested explicitly or when
1201*0Sstevel@tonic-gate  # system is asked to unlink_on_close but is unable to do so because
1202*0Sstevel@tonic-gate  # of OS limitations.
1203*0Sstevel@tonic-gate  # The latter should be achieved by using a tied filehandle.
1204*0Sstevel@tonic-gate  # Do not check return status since this is all done with END blocks.
1205*0Sstevel@tonic-gate  _deferred_unlink($fh, $path, 0) if $options{"UNLINK"};
1206*0Sstevel@tonic-gate
1207*0Sstevel@tonic-gate  # Return
1208*0Sstevel@tonic-gate  if (wantarray()) {
1209*0Sstevel@tonic-gate
1210*0Sstevel@tonic-gate    if ($options{'OPEN'}) {
1211*0Sstevel@tonic-gate      return ($fh, $path);
1212*0Sstevel@tonic-gate    } else {
1213*0Sstevel@tonic-gate      return (undef, $path);
1214*0Sstevel@tonic-gate    }
1215*0Sstevel@tonic-gate
1216*0Sstevel@tonic-gate  } else {
1217*0Sstevel@tonic-gate
1218*0Sstevel@tonic-gate    # Unlink the file. It is up to unlink0 to decide what to do with
1219*0Sstevel@tonic-gate    # this (whether to unlink now or to defer until later)
1220*0Sstevel@tonic-gate    unlink0($fh, $path) or croak "Error unlinking file $path using unlink0";
1221*0Sstevel@tonic-gate
1222*0Sstevel@tonic-gate    # Return just the filehandle.
1223*0Sstevel@tonic-gate    return $fh;
1224*0Sstevel@tonic-gate  }
1225*0Sstevel@tonic-gate
1226*0Sstevel@tonic-gate
1227*0Sstevel@tonic-gate}
1228*0Sstevel@tonic-gate
1229*0Sstevel@tonic-gate=item B<tempdir>
1230*0Sstevel@tonic-gate
1231*0Sstevel@tonic-gateThis is the recommended interface for creation of temporary directories.
1232*0Sstevel@tonic-gateThe behaviour of the function depends on the arguments:
1233*0Sstevel@tonic-gate
1234*0Sstevel@tonic-gate  $tempdir = tempdir();
1235*0Sstevel@tonic-gate
1236*0Sstevel@tonic-gateCreate a directory in tmpdir() (see L<File::Spec|File::Spec>).
1237*0Sstevel@tonic-gate
1238*0Sstevel@tonic-gate  $tempdir = tempdir( $template );
1239*0Sstevel@tonic-gate
1240*0Sstevel@tonic-gateCreate a directory from the supplied template. This template is
1241*0Sstevel@tonic-gatesimilar to that described for tempfile(). `X' characters at the end
1242*0Sstevel@tonic-gateof the template are replaced with random letters to construct the
1243*0Sstevel@tonic-gatedirectory name. At least four `X' characters must be in the template.
1244*0Sstevel@tonic-gate
1245*0Sstevel@tonic-gate  $tempdir = tempdir ( DIR => $dir );
1246*0Sstevel@tonic-gate
1247*0Sstevel@tonic-gateSpecifies the directory to use for the temporary directory.
1248*0Sstevel@tonic-gateThe temporary directory name is derived from an internal template.
1249*0Sstevel@tonic-gate
1250*0Sstevel@tonic-gate  $tempdir = tempdir ( $template, DIR => $dir );
1251*0Sstevel@tonic-gate
1252*0Sstevel@tonic-gatePrepend the supplied directory name to the template. The template
1253*0Sstevel@tonic-gateshould not include parent directory specifications itself. Any parent
1254*0Sstevel@tonic-gatedirectory specifications are removed from the template before
1255*0Sstevel@tonic-gateprepending the supplied directory.
1256*0Sstevel@tonic-gate
1257*0Sstevel@tonic-gate  $tempdir = tempdir ( $template, TMPDIR => 1 );
1258*0Sstevel@tonic-gate
1259*0Sstevel@tonic-gateUsing the supplied template, create the temporary directory in
1260*0Sstevel@tonic-gatea standard location for temporary files. Equivalent to doing
1261*0Sstevel@tonic-gate
1262*0Sstevel@tonic-gate  $tempdir = tempdir ( $template, DIR => File::Spec->tmpdir);
1263*0Sstevel@tonic-gate
1264*0Sstevel@tonic-gatebut shorter. Parent directory specifications are stripped from the
1265*0Sstevel@tonic-gatetemplate itself. The C<TMPDIR> option is ignored if C<DIR> is set
1266*0Sstevel@tonic-gateexplicitly.  Additionally, C<TMPDIR> is implied if neither a template
1267*0Sstevel@tonic-gatenor a directory are supplied.
1268*0Sstevel@tonic-gate
1269*0Sstevel@tonic-gate  $tempdir = tempdir( $template, CLEANUP => 1);
1270*0Sstevel@tonic-gate
1271*0Sstevel@tonic-gateCreate a temporary directory using the supplied template, but
1272*0Sstevel@tonic-gateattempt to remove it (and all files inside it) when the program
1273*0Sstevel@tonic-gateexits. Note that an attempt will be made to remove all files from
1274*0Sstevel@tonic-gatethe directory even if they were not created by this module (otherwise
1275*0Sstevel@tonic-gatewhy ask to clean it up?). The directory removal is made with
1276*0Sstevel@tonic-gatethe rmtree() function from the L<File::Path|File::Path> module.
1277*0Sstevel@tonic-gateOf course, if the template is not specified, the temporary directory
1278*0Sstevel@tonic-gatewill be created in tmpdir() and will also be removed at program exit.
1279*0Sstevel@tonic-gate
1280*0Sstevel@tonic-gate=cut
1281*0Sstevel@tonic-gate
1282*0Sstevel@tonic-gate# '
1283*0Sstevel@tonic-gate
1284*0Sstevel@tonic-gatesub tempdir  {
1285*0Sstevel@tonic-gate
1286*0Sstevel@tonic-gate  # Can not check for argument count since we can have any
1287*0Sstevel@tonic-gate  # number of args
1288*0Sstevel@tonic-gate
1289*0Sstevel@tonic-gate  # Default options
1290*0Sstevel@tonic-gate  my %options = (
1291*0Sstevel@tonic-gate		 "CLEANUP"    => 0,  # Remove directory on exit
1292*0Sstevel@tonic-gate		 "DIR"        => '', # Root directory
1293*0Sstevel@tonic-gate		 "TMPDIR"     => 0,  # Use tempdir with template
1294*0Sstevel@tonic-gate		);
1295*0Sstevel@tonic-gate
1296*0Sstevel@tonic-gate  # Check to see whether we have an odd or even number of arguments
1297*0Sstevel@tonic-gate  my $template = (scalar(@_) % 2 == 1 ? shift(@_) : undef );
1298*0Sstevel@tonic-gate
1299*0Sstevel@tonic-gate  # Read the options and merge with defaults
1300*0Sstevel@tonic-gate  %options = (%options, @_)  if @_;
1301*0Sstevel@tonic-gate
1302*0Sstevel@tonic-gate  # Modify or generate the template
1303*0Sstevel@tonic-gate
1304*0Sstevel@tonic-gate  # Deal with the DIR and TMPDIR options
1305*0Sstevel@tonic-gate  if (defined $template) {
1306*0Sstevel@tonic-gate
1307*0Sstevel@tonic-gate    # Need to strip directory path if using DIR or TMPDIR
1308*0Sstevel@tonic-gate    if ($options{'TMPDIR'} || $options{'DIR'}) {
1309*0Sstevel@tonic-gate
1310*0Sstevel@tonic-gate      # Strip parent directory from the filename
1311*0Sstevel@tonic-gate      #
1312*0Sstevel@tonic-gate      # There is no filename at the end
1313*0Sstevel@tonic-gate      $template = VMS::Filespec::vmspath($template) if $^O eq 'VMS';
1314*0Sstevel@tonic-gate      my ($volume, $directories, undef) = File::Spec->splitpath( $template, 1);
1315*0Sstevel@tonic-gate
1316*0Sstevel@tonic-gate      # Last directory is then our template
1317*0Sstevel@tonic-gate      $template = (File::Spec->splitdir($directories))[-1];
1318*0Sstevel@tonic-gate
1319*0Sstevel@tonic-gate      # Prepend the supplied directory or temp dir
1320*0Sstevel@tonic-gate      if ($options{"DIR"}) {
1321*0Sstevel@tonic-gate
1322*0Sstevel@tonic-gate        $template = File::Spec->catdir($options{"DIR"}, $template);
1323*0Sstevel@tonic-gate
1324*0Sstevel@tonic-gate      } elsif ($options{TMPDIR}) {
1325*0Sstevel@tonic-gate
1326*0Sstevel@tonic-gate	# Prepend tmpdir
1327*0Sstevel@tonic-gate	$template = File::Spec->catdir(File::Spec->tmpdir, $template);
1328*0Sstevel@tonic-gate
1329*0Sstevel@tonic-gate      }
1330*0Sstevel@tonic-gate
1331*0Sstevel@tonic-gate    }
1332*0Sstevel@tonic-gate
1333*0Sstevel@tonic-gate  } else {
1334*0Sstevel@tonic-gate
1335*0Sstevel@tonic-gate    if ($options{"DIR"}) {
1336*0Sstevel@tonic-gate
1337*0Sstevel@tonic-gate      $template = File::Spec->catdir($options{"DIR"}, TEMPXXX);
1338*0Sstevel@tonic-gate
1339*0Sstevel@tonic-gate    } else {
1340*0Sstevel@tonic-gate
1341*0Sstevel@tonic-gate      $template = File::Spec->catdir(File::Spec->tmpdir, TEMPXXX);
1342*0Sstevel@tonic-gate
1343*0Sstevel@tonic-gate    }
1344*0Sstevel@tonic-gate
1345*0Sstevel@tonic-gate  }
1346*0Sstevel@tonic-gate
1347*0Sstevel@tonic-gate  # Create the directory
1348*0Sstevel@tonic-gate  my $tempdir;
1349*0Sstevel@tonic-gate  my $suffixlen = 0;
1350*0Sstevel@tonic-gate  if ($^O eq 'VMS') {  # dir names can end in delimiters
1351*0Sstevel@tonic-gate    $template =~ m/([\.\]:>]+)$/;
1352*0Sstevel@tonic-gate    $suffixlen = length($1);
1353*0Sstevel@tonic-gate  }
1354*0Sstevel@tonic-gate  if ( ($^O eq 'MacOS') && (substr($template, -1) eq ':') ) {
1355*0Sstevel@tonic-gate    # dir name has a trailing ':'
1356*0Sstevel@tonic-gate    ++$suffixlen;
1357*0Sstevel@tonic-gate  }
1358*0Sstevel@tonic-gate
1359*0Sstevel@tonic-gate  my $errstr;
1360*0Sstevel@tonic-gate  croak "Error in tempdir() using $template: $errstr"
1361*0Sstevel@tonic-gate    unless ((undef, $tempdir) = _gettemp($template,
1362*0Sstevel@tonic-gate				    "open" => 0,
1363*0Sstevel@tonic-gate				    "mkdir"=> 1 ,
1364*0Sstevel@tonic-gate				    "suffixlen" => $suffixlen,
1365*0Sstevel@tonic-gate				    "ErrStr" => \$errstr,
1366*0Sstevel@tonic-gate				   ) );
1367*0Sstevel@tonic-gate
1368*0Sstevel@tonic-gate  # Install exit handler; must be dynamic to get lexical
1369*0Sstevel@tonic-gate  if ( $options{'CLEANUP'} && -d $tempdir) {
1370*0Sstevel@tonic-gate    _deferred_unlink(undef, $tempdir, 1);
1371*0Sstevel@tonic-gate  }
1372*0Sstevel@tonic-gate
1373*0Sstevel@tonic-gate  # Return the dir name
1374*0Sstevel@tonic-gate  return $tempdir;
1375*0Sstevel@tonic-gate
1376*0Sstevel@tonic-gate}
1377*0Sstevel@tonic-gate
1378*0Sstevel@tonic-gate=back
1379*0Sstevel@tonic-gate
1380*0Sstevel@tonic-gate=head1 MKTEMP FUNCTIONS
1381*0Sstevel@tonic-gate
1382*0Sstevel@tonic-gateThe following functions are Perl implementations of the
1383*0Sstevel@tonic-gatemktemp() family of temp file generation system calls.
1384*0Sstevel@tonic-gate
1385*0Sstevel@tonic-gate=over 4
1386*0Sstevel@tonic-gate
1387*0Sstevel@tonic-gate=item B<mkstemp>
1388*0Sstevel@tonic-gate
1389*0Sstevel@tonic-gateGiven a template, returns a filehandle to the temporary file and the name
1390*0Sstevel@tonic-gateof the file.
1391*0Sstevel@tonic-gate
1392*0Sstevel@tonic-gate  ($fh, $name) = mkstemp( $template );
1393*0Sstevel@tonic-gate
1394*0Sstevel@tonic-gateIn scalar context, just the filehandle is returned.
1395*0Sstevel@tonic-gate
1396*0Sstevel@tonic-gateThe template may be any filename with some number of X's appended
1397*0Sstevel@tonic-gateto it, for example F</tmp/temp.XXXX>. The trailing X's are replaced
1398*0Sstevel@tonic-gatewith unique alphanumeric combinations.
1399*0Sstevel@tonic-gate
1400*0Sstevel@tonic-gate=cut
1401*0Sstevel@tonic-gate
1402*0Sstevel@tonic-gate
1403*0Sstevel@tonic-gate
1404*0Sstevel@tonic-gatesub mkstemp {
1405*0Sstevel@tonic-gate
1406*0Sstevel@tonic-gate  croak "Usage: mkstemp(template)"
1407*0Sstevel@tonic-gate    if scalar(@_) != 1;
1408*0Sstevel@tonic-gate
1409*0Sstevel@tonic-gate  my $template = shift;
1410*0Sstevel@tonic-gate
1411*0Sstevel@tonic-gate  my ($fh, $path, $errstr);
1412*0Sstevel@tonic-gate  croak "Error in mkstemp using $template: $errstr"
1413*0Sstevel@tonic-gate    unless (($fh, $path) = _gettemp($template,
1414*0Sstevel@tonic-gate				    "open" => 1,
1415*0Sstevel@tonic-gate				    "mkdir"=> 0 ,
1416*0Sstevel@tonic-gate				    "suffixlen" => 0,
1417*0Sstevel@tonic-gate				    "ErrStr" => \$errstr,
1418*0Sstevel@tonic-gate				   ) );
1419*0Sstevel@tonic-gate
1420*0Sstevel@tonic-gate  if (wantarray()) {
1421*0Sstevel@tonic-gate    return ($fh, $path);
1422*0Sstevel@tonic-gate  } else {
1423*0Sstevel@tonic-gate    return $fh;
1424*0Sstevel@tonic-gate  }
1425*0Sstevel@tonic-gate
1426*0Sstevel@tonic-gate}
1427*0Sstevel@tonic-gate
1428*0Sstevel@tonic-gate
1429*0Sstevel@tonic-gate=item B<mkstemps>
1430*0Sstevel@tonic-gate
1431*0Sstevel@tonic-gateSimilar to mkstemp(), except that an extra argument can be supplied
1432*0Sstevel@tonic-gatewith a suffix to be appended to the template.
1433*0Sstevel@tonic-gate
1434*0Sstevel@tonic-gate  ($fh, $name) = mkstemps( $template, $suffix );
1435*0Sstevel@tonic-gate
1436*0Sstevel@tonic-gateFor example a template of C<testXXXXXX> and suffix of C<.dat>
1437*0Sstevel@tonic-gatewould generate a file similar to F<testhGji_w.dat>.
1438*0Sstevel@tonic-gate
1439*0Sstevel@tonic-gateReturns just the filehandle alone when called in scalar context.
1440*0Sstevel@tonic-gate
1441*0Sstevel@tonic-gate=cut
1442*0Sstevel@tonic-gate
1443*0Sstevel@tonic-gatesub mkstemps {
1444*0Sstevel@tonic-gate
1445*0Sstevel@tonic-gate  croak "Usage: mkstemps(template, suffix)"
1446*0Sstevel@tonic-gate    if scalar(@_) != 2;
1447*0Sstevel@tonic-gate
1448*0Sstevel@tonic-gate
1449*0Sstevel@tonic-gate  my $template = shift;
1450*0Sstevel@tonic-gate  my $suffix   = shift;
1451*0Sstevel@tonic-gate
1452*0Sstevel@tonic-gate  $template .= $suffix;
1453*0Sstevel@tonic-gate
1454*0Sstevel@tonic-gate  my ($fh, $path, $errstr);
1455*0Sstevel@tonic-gate  croak "Error in mkstemps using $template: $errstr"
1456*0Sstevel@tonic-gate    unless (($fh, $path) = _gettemp($template,
1457*0Sstevel@tonic-gate				    "open" => 1,
1458*0Sstevel@tonic-gate				    "mkdir"=> 0 ,
1459*0Sstevel@tonic-gate				    "suffixlen" => length($suffix),
1460*0Sstevel@tonic-gate				    "ErrStr" => \$errstr,
1461*0Sstevel@tonic-gate				   ) );
1462*0Sstevel@tonic-gate
1463*0Sstevel@tonic-gate  if (wantarray()) {
1464*0Sstevel@tonic-gate    return ($fh, $path);
1465*0Sstevel@tonic-gate  } else {
1466*0Sstevel@tonic-gate    return $fh;
1467*0Sstevel@tonic-gate  }
1468*0Sstevel@tonic-gate
1469*0Sstevel@tonic-gate}
1470*0Sstevel@tonic-gate
1471*0Sstevel@tonic-gate=item B<mkdtemp>
1472*0Sstevel@tonic-gate
1473*0Sstevel@tonic-gateCreate a directory from a template. The template must end in
1474*0Sstevel@tonic-gateX's that are replaced by the routine.
1475*0Sstevel@tonic-gate
1476*0Sstevel@tonic-gate  $tmpdir_name = mkdtemp($template);
1477*0Sstevel@tonic-gate
1478*0Sstevel@tonic-gateReturns the name of the temporary directory created.
1479*0Sstevel@tonic-gateReturns undef on failure.
1480*0Sstevel@tonic-gate
1481*0Sstevel@tonic-gateDirectory must be removed by the caller.
1482*0Sstevel@tonic-gate
1483*0Sstevel@tonic-gate=cut
1484*0Sstevel@tonic-gate
1485*0Sstevel@tonic-gate#' # for emacs
1486*0Sstevel@tonic-gate
1487*0Sstevel@tonic-gatesub mkdtemp {
1488*0Sstevel@tonic-gate
1489*0Sstevel@tonic-gate  croak "Usage: mkdtemp(template)"
1490*0Sstevel@tonic-gate    if scalar(@_) != 1;
1491*0Sstevel@tonic-gate
1492*0Sstevel@tonic-gate  my $template = shift;
1493*0Sstevel@tonic-gate  my $suffixlen = 0;
1494*0Sstevel@tonic-gate  if ($^O eq 'VMS') {  # dir names can end in delimiters
1495*0Sstevel@tonic-gate    $template =~ m/([\.\]:>]+)$/;
1496*0Sstevel@tonic-gate    $suffixlen = length($1);
1497*0Sstevel@tonic-gate  }
1498*0Sstevel@tonic-gate  if ( ($^O eq 'MacOS') && (substr($template, -1) eq ':') ) {
1499*0Sstevel@tonic-gate    # dir name has a trailing ':'
1500*0Sstevel@tonic-gate    ++$suffixlen;
1501*0Sstevel@tonic-gate  }
1502*0Sstevel@tonic-gate  my ($junk, $tmpdir, $errstr);
1503*0Sstevel@tonic-gate  croak "Error creating temp directory from template $template\: $errstr"
1504*0Sstevel@tonic-gate    unless (($junk, $tmpdir) = _gettemp($template,
1505*0Sstevel@tonic-gate					"open" => 0,
1506*0Sstevel@tonic-gate					"mkdir"=> 1 ,
1507*0Sstevel@tonic-gate					"suffixlen" => $suffixlen,
1508*0Sstevel@tonic-gate					"ErrStr" => \$errstr,
1509*0Sstevel@tonic-gate				       ) );
1510*0Sstevel@tonic-gate
1511*0Sstevel@tonic-gate  return $tmpdir;
1512*0Sstevel@tonic-gate
1513*0Sstevel@tonic-gate}
1514*0Sstevel@tonic-gate
1515*0Sstevel@tonic-gate=item B<mktemp>
1516*0Sstevel@tonic-gate
1517*0Sstevel@tonic-gateReturns a valid temporary filename but does not guarantee
1518*0Sstevel@tonic-gatethat the file will not be opened by someone else.
1519*0Sstevel@tonic-gate
1520*0Sstevel@tonic-gate  $unopened_file = mktemp($template);
1521*0Sstevel@tonic-gate
1522*0Sstevel@tonic-gateTemplate is the same as that required by mkstemp().
1523*0Sstevel@tonic-gate
1524*0Sstevel@tonic-gate=cut
1525*0Sstevel@tonic-gate
1526*0Sstevel@tonic-gatesub mktemp {
1527*0Sstevel@tonic-gate
1528*0Sstevel@tonic-gate  croak "Usage: mktemp(template)"
1529*0Sstevel@tonic-gate    if scalar(@_) != 1;
1530*0Sstevel@tonic-gate
1531*0Sstevel@tonic-gate  my $template = shift;
1532*0Sstevel@tonic-gate
1533*0Sstevel@tonic-gate  my ($tmpname, $junk, $errstr);
1534*0Sstevel@tonic-gate  croak "Error getting name to temp file from template $template: $errstr"
1535*0Sstevel@tonic-gate    unless (($junk, $tmpname) = _gettemp($template,
1536*0Sstevel@tonic-gate					 "open" => 0,
1537*0Sstevel@tonic-gate					 "mkdir"=> 0 ,
1538*0Sstevel@tonic-gate					 "suffixlen" => 0,
1539*0Sstevel@tonic-gate					 "ErrStr" => \$errstr,
1540*0Sstevel@tonic-gate					 ) );
1541*0Sstevel@tonic-gate
1542*0Sstevel@tonic-gate  return $tmpname;
1543*0Sstevel@tonic-gate}
1544*0Sstevel@tonic-gate
1545*0Sstevel@tonic-gate=back
1546*0Sstevel@tonic-gate
1547*0Sstevel@tonic-gate=head1 POSIX FUNCTIONS
1548*0Sstevel@tonic-gate
1549*0Sstevel@tonic-gateThis section describes the re-implementation of the tmpnam()
1550*0Sstevel@tonic-gateand tmpfile() functions described in L<POSIX>
1551*0Sstevel@tonic-gateusing the mkstemp() from this module.
1552*0Sstevel@tonic-gate
1553*0Sstevel@tonic-gateUnlike the L<POSIX|POSIX> implementations, the directory used
1554*0Sstevel@tonic-gatefor the temporary file is not specified in a system include
1555*0Sstevel@tonic-gatefile (C<P_tmpdir>) but simply depends on the choice of tmpdir()
1556*0Sstevel@tonic-gatereturned by L<File::Spec|File::Spec>. On some implementations this
1557*0Sstevel@tonic-gatelocation can be set using the C<TMPDIR> environment variable, which
1558*0Sstevel@tonic-gatemay not be secure.
1559*0Sstevel@tonic-gateIf this is a problem, simply use mkstemp() and specify a template.
1560*0Sstevel@tonic-gate
1561*0Sstevel@tonic-gate=over 4
1562*0Sstevel@tonic-gate
1563*0Sstevel@tonic-gate=item B<tmpnam>
1564*0Sstevel@tonic-gate
1565*0Sstevel@tonic-gateWhen called in scalar context, returns the full name (including path)
1566*0Sstevel@tonic-gateof a temporary file (uses mktemp()). The only check is that the file does
1567*0Sstevel@tonic-gatenot already exist, but there is no guarantee that that condition will
1568*0Sstevel@tonic-gatecontinue to apply.
1569*0Sstevel@tonic-gate
1570*0Sstevel@tonic-gate  $file = tmpnam();
1571*0Sstevel@tonic-gate
1572*0Sstevel@tonic-gateWhen called in list context, a filehandle to the open file and
1573*0Sstevel@tonic-gatea filename are returned. This is achieved by calling mkstemp()
1574*0Sstevel@tonic-gateafter constructing a suitable template.
1575*0Sstevel@tonic-gate
1576*0Sstevel@tonic-gate  ($fh, $file) = tmpnam();
1577*0Sstevel@tonic-gate
1578*0Sstevel@tonic-gateIf possible, this form should be used to prevent possible
1579*0Sstevel@tonic-gaterace conditions.
1580*0Sstevel@tonic-gate
1581*0Sstevel@tonic-gateSee L<File::Spec/tmpdir> for information on the choice of temporary
1582*0Sstevel@tonic-gatedirectory for a particular operating system.
1583*0Sstevel@tonic-gate
1584*0Sstevel@tonic-gate=cut
1585*0Sstevel@tonic-gate
1586*0Sstevel@tonic-gatesub tmpnam {
1587*0Sstevel@tonic-gate
1588*0Sstevel@tonic-gate   # Retrieve the temporary directory name
1589*0Sstevel@tonic-gate   my $tmpdir = File::Spec->tmpdir;
1590*0Sstevel@tonic-gate
1591*0Sstevel@tonic-gate   croak "Error temporary directory is not writable"
1592*0Sstevel@tonic-gate     if $tmpdir eq '';
1593*0Sstevel@tonic-gate
1594*0Sstevel@tonic-gate   # Use a ten character template and append to tmpdir
1595*0Sstevel@tonic-gate   my $template = File::Spec->catfile($tmpdir, TEMPXXX);
1596*0Sstevel@tonic-gate
1597*0Sstevel@tonic-gate   if (wantarray() ) {
1598*0Sstevel@tonic-gate       return mkstemp($template);
1599*0Sstevel@tonic-gate   } else {
1600*0Sstevel@tonic-gate       return mktemp($template);
1601*0Sstevel@tonic-gate   }
1602*0Sstevel@tonic-gate
1603*0Sstevel@tonic-gate}
1604*0Sstevel@tonic-gate
1605*0Sstevel@tonic-gate=item B<tmpfile>
1606*0Sstevel@tonic-gate
1607*0Sstevel@tonic-gateReturns the filehandle of a temporary file.
1608*0Sstevel@tonic-gate
1609*0Sstevel@tonic-gate  $fh = tmpfile();
1610*0Sstevel@tonic-gate
1611*0Sstevel@tonic-gateThe file is removed when the filehandle is closed or when the program
1612*0Sstevel@tonic-gateexits. No access to the filename is provided.
1613*0Sstevel@tonic-gate
1614*0Sstevel@tonic-gateIf the temporary file can not be created undef is returned.
1615*0Sstevel@tonic-gateCurrently this command will probably not work when the temporary
1616*0Sstevel@tonic-gatedirectory is on an NFS file system.
1617*0Sstevel@tonic-gate
1618*0Sstevel@tonic-gate=cut
1619*0Sstevel@tonic-gate
1620*0Sstevel@tonic-gatesub tmpfile {
1621*0Sstevel@tonic-gate
1622*0Sstevel@tonic-gate  # Simply call tmpnam() in a list context
1623*0Sstevel@tonic-gate  my ($fh, $file) = tmpnam();
1624*0Sstevel@tonic-gate
1625*0Sstevel@tonic-gate  # Make sure file is removed when filehandle is closed
1626*0Sstevel@tonic-gate  # This will fail on NFS
1627*0Sstevel@tonic-gate  unlink0($fh, $file)
1628*0Sstevel@tonic-gate    or return undef;
1629*0Sstevel@tonic-gate
1630*0Sstevel@tonic-gate  return $fh;
1631*0Sstevel@tonic-gate
1632*0Sstevel@tonic-gate}
1633*0Sstevel@tonic-gate
1634*0Sstevel@tonic-gate=back
1635*0Sstevel@tonic-gate
1636*0Sstevel@tonic-gate=head1 ADDITIONAL FUNCTIONS
1637*0Sstevel@tonic-gate
1638*0Sstevel@tonic-gateThese functions are provided for backwards compatibility
1639*0Sstevel@tonic-gatewith common tempfile generation C library functions.
1640*0Sstevel@tonic-gate
1641*0Sstevel@tonic-gateThey are not exported and must be addressed using the full package
1642*0Sstevel@tonic-gatename.
1643*0Sstevel@tonic-gate
1644*0Sstevel@tonic-gate=over 4
1645*0Sstevel@tonic-gate
1646*0Sstevel@tonic-gate=item B<tempnam>
1647*0Sstevel@tonic-gate
1648*0Sstevel@tonic-gateReturn the name of a temporary file in the specified directory
1649*0Sstevel@tonic-gateusing a prefix. The file is guaranteed not to exist at the time
1650*0Sstevel@tonic-gatethe function was called, but such guarantees are good for one
1651*0Sstevel@tonic-gateclock tick only.  Always use the proper form of C<sysopen>
1652*0Sstevel@tonic-gatewith C<O_CREAT | O_EXCL> if you must open such a filename.
1653*0Sstevel@tonic-gate
1654*0Sstevel@tonic-gate  $filename = File::Temp::tempnam( $dir, $prefix );
1655*0Sstevel@tonic-gate
1656*0Sstevel@tonic-gateEquivalent to running mktemp() with $dir/$prefixXXXXXXXX
1657*0Sstevel@tonic-gate(using unix file convention as an example)
1658*0Sstevel@tonic-gate
1659*0Sstevel@tonic-gateBecause this function uses mktemp(), it can suffer from race conditions.
1660*0Sstevel@tonic-gate
1661*0Sstevel@tonic-gate=cut
1662*0Sstevel@tonic-gate
1663*0Sstevel@tonic-gatesub tempnam {
1664*0Sstevel@tonic-gate
1665*0Sstevel@tonic-gate  croak 'Usage tempnam($dir, $prefix)' unless scalar(@_) == 2;
1666*0Sstevel@tonic-gate
1667*0Sstevel@tonic-gate  my ($dir, $prefix) = @_;
1668*0Sstevel@tonic-gate
1669*0Sstevel@tonic-gate  # Add a string to the prefix
1670*0Sstevel@tonic-gate  $prefix .= 'XXXXXXXX';
1671*0Sstevel@tonic-gate
1672*0Sstevel@tonic-gate  # Concatenate the directory to the file
1673*0Sstevel@tonic-gate  my $template = File::Spec->catfile($dir, $prefix);
1674*0Sstevel@tonic-gate
1675*0Sstevel@tonic-gate  return mktemp($template);
1676*0Sstevel@tonic-gate
1677*0Sstevel@tonic-gate}
1678*0Sstevel@tonic-gate
1679*0Sstevel@tonic-gate=back
1680*0Sstevel@tonic-gate
1681*0Sstevel@tonic-gate=head1 UTILITY FUNCTIONS
1682*0Sstevel@tonic-gate
1683*0Sstevel@tonic-gateUseful functions for dealing with the filehandle and filename.
1684*0Sstevel@tonic-gate
1685*0Sstevel@tonic-gate=over 4
1686*0Sstevel@tonic-gate
1687*0Sstevel@tonic-gate=item B<unlink0>
1688*0Sstevel@tonic-gate
1689*0Sstevel@tonic-gateGiven an open filehandle and the associated filename, make a safe
1690*0Sstevel@tonic-gateunlink. This is achieved by first checking that the filename and
1691*0Sstevel@tonic-gatefilehandle initially point to the same file and that the number of
1692*0Sstevel@tonic-gatelinks to the file is 1 (all fields returned by stat() are compared).
1693*0Sstevel@tonic-gateThen the filename is unlinked and the filehandle checked once again to
1694*0Sstevel@tonic-gateverify that the number of links on that file is now 0.  This is the
1695*0Sstevel@tonic-gateclosest you can come to making sure that the filename unlinked was the
1696*0Sstevel@tonic-gatesame as the file whose descriptor you hold.
1697*0Sstevel@tonic-gate
1698*0Sstevel@tonic-gate  unlink0($fh, $path) or die "Error unlinking file $path safely";
1699*0Sstevel@tonic-gate
1700*0Sstevel@tonic-gateReturns false on error. The filehandle is not closed since on some
1701*0Sstevel@tonic-gateoccasions this is not required.
1702*0Sstevel@tonic-gate
1703*0Sstevel@tonic-gateOn some platforms, for example Windows NT, it is not possible to
1704*0Sstevel@tonic-gateunlink an open file (the file must be closed first). On those
1705*0Sstevel@tonic-gateplatforms, the actual unlinking is deferred until the program ends and
1706*0Sstevel@tonic-gategood status is returned. A check is still performed to make sure that
1707*0Sstevel@tonic-gatethe filehandle and filename are pointing to the same thing (but not at
1708*0Sstevel@tonic-gatethe time the end block is executed since the deferred removal may not
1709*0Sstevel@tonic-gatehave access to the filehandle).
1710*0Sstevel@tonic-gate
1711*0Sstevel@tonic-gateAdditionally, on Windows NT not all the fields returned by stat() can
1712*0Sstevel@tonic-gatebe compared. For example, the C<dev> and C<rdev> fields seem to be
1713*0Sstevel@tonic-gatedifferent.  Also, it seems that the size of the file returned by stat()
1714*0Sstevel@tonic-gatedoes not always agree, with C<stat(FH)> being more accurate than
1715*0Sstevel@tonic-gateC<stat(filename)>, presumably because of caching issues even when
1716*0Sstevel@tonic-gateusing autoflush (this is usually overcome by waiting a while after
1717*0Sstevel@tonic-gatewriting to the tempfile before attempting to C<unlink0> it).
1718*0Sstevel@tonic-gate
1719*0Sstevel@tonic-gateFinally, on NFS file systems the link count of the file handle does
1720*0Sstevel@tonic-gatenot always go to zero immediately after unlinking. Currently, this
1721*0Sstevel@tonic-gatecommand is expected to fail on NFS disks.
1722*0Sstevel@tonic-gate
1723*0Sstevel@tonic-gate=cut
1724*0Sstevel@tonic-gate
1725*0Sstevel@tonic-gatesub unlink0 {
1726*0Sstevel@tonic-gate
1727*0Sstevel@tonic-gate  croak 'Usage: unlink0(filehandle, filename)'
1728*0Sstevel@tonic-gate    unless scalar(@_) == 2;
1729*0Sstevel@tonic-gate
1730*0Sstevel@tonic-gate  # Read args
1731*0Sstevel@tonic-gate  my ($fh, $path) = @_;
1732*0Sstevel@tonic-gate
1733*0Sstevel@tonic-gate  cmpstat($fh, $path) or return 0;
1734*0Sstevel@tonic-gate
1735*0Sstevel@tonic-gate  # attempt remove the file (does not work on some platforms)
1736*0Sstevel@tonic-gate  if (_can_unlink_opened_file()) {
1737*0Sstevel@tonic-gate    # XXX: do *not* call this on a directory; possible race
1738*0Sstevel@tonic-gate    #      resulting in recursive removal
1739*0Sstevel@tonic-gate    croak "unlink0: $path has become a directory!" if -d $path;
1740*0Sstevel@tonic-gate    unlink($path) or return 0;
1741*0Sstevel@tonic-gate
1742*0Sstevel@tonic-gate    # Stat the filehandle
1743*0Sstevel@tonic-gate    my @fh = stat $fh;
1744*0Sstevel@tonic-gate
1745*0Sstevel@tonic-gate    print "Link count = $fh[3] \n" if $DEBUG;
1746*0Sstevel@tonic-gate
1747*0Sstevel@tonic-gate    # Make sure that the link count is zero
1748*0Sstevel@tonic-gate    # - Cygwin provides deferred unlinking, however,
1749*0Sstevel@tonic-gate    #   on Win9x the link count remains 1
1750*0Sstevel@tonic-gate    # On NFS the link count may still be 1 but we cant know that
1751*0Sstevel@tonic-gate    # we are on NFS
1752*0Sstevel@tonic-gate    return ( $fh[3] == 0 or $^O eq 'cygwin' ? 1 : 0);
1753*0Sstevel@tonic-gate
1754*0Sstevel@tonic-gate  } else {
1755*0Sstevel@tonic-gate    _deferred_unlink($fh, $path, 0);
1756*0Sstevel@tonic-gate    return 1;
1757*0Sstevel@tonic-gate  }
1758*0Sstevel@tonic-gate
1759*0Sstevel@tonic-gate}
1760*0Sstevel@tonic-gate
1761*0Sstevel@tonic-gate=item B<cmpstat>
1762*0Sstevel@tonic-gate
1763*0Sstevel@tonic-gateCompare C<stat> of filehandle with C<stat> of provided filename.  This
1764*0Sstevel@tonic-gatecan be used to check that the filename and filehandle initially point
1765*0Sstevel@tonic-gateto the same file and that the number of links to the file is 1 (all
1766*0Sstevel@tonic-gatefields returned by stat() are compared).
1767*0Sstevel@tonic-gate
1768*0Sstevel@tonic-gate  cmpstat($fh, $path) or die "Error comparing handle with file";
1769*0Sstevel@tonic-gate
1770*0Sstevel@tonic-gateReturns false if the stat information differs or if the link count is
1771*0Sstevel@tonic-gategreater than 1.
1772*0Sstevel@tonic-gate
1773*0Sstevel@tonic-gateOn certain platofms, eg Windows, not all the fields returned by stat()
1774*0Sstevel@tonic-gatecan be compared. For example, the C<dev> and C<rdev> fields seem to be
1775*0Sstevel@tonic-gatedifferent in Windows.  Also, it seems that the size of the file
1776*0Sstevel@tonic-gatereturned by stat() does not always agree, with C<stat(FH)> being more
1777*0Sstevel@tonic-gateaccurate than C<stat(filename)>, presumably because of caching issues
1778*0Sstevel@tonic-gateeven when using autoflush (this is usually overcome by waiting a while
1779*0Sstevel@tonic-gateafter writing to the tempfile before attempting to C<unlink0> it).
1780*0Sstevel@tonic-gate
1781*0Sstevel@tonic-gateNot exported by default.
1782*0Sstevel@tonic-gate
1783*0Sstevel@tonic-gate=cut
1784*0Sstevel@tonic-gate
1785*0Sstevel@tonic-gatesub cmpstat {
1786*0Sstevel@tonic-gate
1787*0Sstevel@tonic-gate  croak 'Usage: cmpstat(filehandle, filename)'
1788*0Sstevel@tonic-gate    unless scalar(@_) == 2;
1789*0Sstevel@tonic-gate
1790*0Sstevel@tonic-gate  # Read args
1791*0Sstevel@tonic-gate  my ($fh, $path) = @_;
1792*0Sstevel@tonic-gate
1793*0Sstevel@tonic-gate  warn "Comparing stat\n"
1794*0Sstevel@tonic-gate    if $DEBUG;
1795*0Sstevel@tonic-gate
1796*0Sstevel@tonic-gate  # Stat the filehandle - which may be closed if someone has manually
1797*0Sstevel@tonic-gate  # closed the file. Can not turn off warnings without using $^W
1798*0Sstevel@tonic-gate  # unless we upgrade to 5.006 minimum requirement
1799*0Sstevel@tonic-gate  my @fh;
1800*0Sstevel@tonic-gate  {
1801*0Sstevel@tonic-gate    local ($^W) = 0;
1802*0Sstevel@tonic-gate    @fh = stat $fh;
1803*0Sstevel@tonic-gate  }
1804*0Sstevel@tonic-gate  return unless @fh;
1805*0Sstevel@tonic-gate
1806*0Sstevel@tonic-gate  if ($fh[3] > 1 && $^W) {
1807*0Sstevel@tonic-gate    carp "unlink0: fstat found too many links; SB=@fh" if $^W;
1808*0Sstevel@tonic-gate  }
1809*0Sstevel@tonic-gate
1810*0Sstevel@tonic-gate  # Stat the path
1811*0Sstevel@tonic-gate  my @path = stat $path;
1812*0Sstevel@tonic-gate
1813*0Sstevel@tonic-gate  unless (@path) {
1814*0Sstevel@tonic-gate    carp "unlink0: $path is gone already" if $^W;
1815*0Sstevel@tonic-gate    return;
1816*0Sstevel@tonic-gate  }
1817*0Sstevel@tonic-gate
1818*0Sstevel@tonic-gate  # this is no longer a file, but may be a directory, or worse
1819*0Sstevel@tonic-gate  unless (-f _) {
1820*0Sstevel@tonic-gate    confess "panic: $path is no longer a file: SB=@fh";
1821*0Sstevel@tonic-gate  }
1822*0Sstevel@tonic-gate
1823*0Sstevel@tonic-gate  # Do comparison of each member of the array
1824*0Sstevel@tonic-gate  # On WinNT dev and rdev seem to be different
1825*0Sstevel@tonic-gate  # depending on whether it is a file or a handle.
1826*0Sstevel@tonic-gate  # Cannot simply compare all members of the stat return
1827*0Sstevel@tonic-gate  # Select the ones we can use
1828*0Sstevel@tonic-gate  my @okstat = (0..$#fh);  # Use all by default
1829*0Sstevel@tonic-gate  if ($^O eq 'MSWin32') {
1830*0Sstevel@tonic-gate    @okstat = (1,2,3,4,5,7,8,9,10);
1831*0Sstevel@tonic-gate  } elsif ($^O eq 'os2') {
1832*0Sstevel@tonic-gate    @okstat = (0, 2..$#fh);
1833*0Sstevel@tonic-gate  } elsif ($^O eq 'VMS') { # device and file ID are sufficient
1834*0Sstevel@tonic-gate    @okstat = (0, 1);
1835*0Sstevel@tonic-gate  } elsif ($^O eq 'dos') {
1836*0Sstevel@tonic-gate    @okstat = (0,2..7,11..$#fh);
1837*0Sstevel@tonic-gate  } elsif ($^O eq 'mpeix') {
1838*0Sstevel@tonic-gate    @okstat = (0..4,8..10);
1839*0Sstevel@tonic-gate  }
1840*0Sstevel@tonic-gate
1841*0Sstevel@tonic-gate  # Now compare each entry explicitly by number
1842*0Sstevel@tonic-gate  for (@okstat) {
1843*0Sstevel@tonic-gate    print "Comparing: $_ : $fh[$_] and $path[$_]\n" if $DEBUG;
1844*0Sstevel@tonic-gate    # Use eq rather than == since rdev, blksize, and blocks (6, 11,
1845*0Sstevel@tonic-gate    # and 12) will be '' on platforms that do not support them.  This
1846*0Sstevel@tonic-gate    # is fine since we are only comparing integers.
1847*0Sstevel@tonic-gate    unless ($fh[$_] eq $path[$_]) {
1848*0Sstevel@tonic-gate      warn "Did not match $_ element of stat\n" if $DEBUG;
1849*0Sstevel@tonic-gate      return 0;
1850*0Sstevel@tonic-gate    }
1851*0Sstevel@tonic-gate  }
1852*0Sstevel@tonic-gate
1853*0Sstevel@tonic-gate  return 1;
1854*0Sstevel@tonic-gate}
1855*0Sstevel@tonic-gate
1856*0Sstevel@tonic-gate=item B<unlink1>
1857*0Sstevel@tonic-gate
1858*0Sstevel@tonic-gateSimilar to C<unlink0> except after file comparison using cmpstat, the
1859*0Sstevel@tonic-gatefilehandle is closed prior to attempting to unlink the file. This
1860*0Sstevel@tonic-gateallows the file to be removed without using an END block, but does
1861*0Sstevel@tonic-gatemean that the post-unlink comparison of the filehandle state provided
1862*0Sstevel@tonic-gateby C<unlink0> is not available.
1863*0Sstevel@tonic-gate
1864*0Sstevel@tonic-gate  unlink1($fh, $path) or die "Error closing and unlinking file";
1865*0Sstevel@tonic-gate
1866*0Sstevel@tonic-gateUsually called from the object destructor when using the OO interface.
1867*0Sstevel@tonic-gate
1868*0Sstevel@tonic-gateNot exported by default.
1869*0Sstevel@tonic-gate
1870*0Sstevel@tonic-gate=cut
1871*0Sstevel@tonic-gate
1872*0Sstevel@tonic-gatesub unlink1 {
1873*0Sstevel@tonic-gate  croak 'Usage: unlink1(filehandle, filename)'
1874*0Sstevel@tonic-gate    unless scalar(@_) == 2;
1875*0Sstevel@tonic-gate
1876*0Sstevel@tonic-gate  # Read args
1877*0Sstevel@tonic-gate  my ($fh, $path) = @_;
1878*0Sstevel@tonic-gate
1879*0Sstevel@tonic-gate  cmpstat($fh, $path) or return 0;
1880*0Sstevel@tonic-gate
1881*0Sstevel@tonic-gate  # Close the file
1882*0Sstevel@tonic-gate  close( $fh ) or return 0;
1883*0Sstevel@tonic-gate
1884*0Sstevel@tonic-gate  # remove the file
1885*0Sstevel@tonic-gate  return unlink($path);
1886*0Sstevel@tonic-gate}
1887*0Sstevel@tonic-gate
1888*0Sstevel@tonic-gate=back
1889*0Sstevel@tonic-gate
1890*0Sstevel@tonic-gate=head1 PACKAGE VARIABLES
1891*0Sstevel@tonic-gate
1892*0Sstevel@tonic-gateThese functions control the global state of the package.
1893*0Sstevel@tonic-gate
1894*0Sstevel@tonic-gate=over 4
1895*0Sstevel@tonic-gate
1896*0Sstevel@tonic-gate=item B<safe_level>
1897*0Sstevel@tonic-gate
1898*0Sstevel@tonic-gateControls the lengths to which the module will go to check the safety of the
1899*0Sstevel@tonic-gatetemporary file or directory before proceeding.
1900*0Sstevel@tonic-gateOptions are:
1901*0Sstevel@tonic-gate
1902*0Sstevel@tonic-gate=over 8
1903*0Sstevel@tonic-gate
1904*0Sstevel@tonic-gate=item STANDARD
1905*0Sstevel@tonic-gate
1906*0Sstevel@tonic-gateDo the basic security measures to ensure the directory exists and
1907*0Sstevel@tonic-gateis writable, that the umask() is fixed before opening of the file,
1908*0Sstevel@tonic-gatethat temporary files are opened only if they do not already exist, and
1909*0Sstevel@tonic-gatethat possible race conditions are avoided.  Finally the L<unlink0|"unlink0">
1910*0Sstevel@tonic-gatefunction is used to remove files safely.
1911*0Sstevel@tonic-gate
1912*0Sstevel@tonic-gate=item MEDIUM
1913*0Sstevel@tonic-gate
1914*0Sstevel@tonic-gateIn addition to the STANDARD security, the output directory is checked
1915*0Sstevel@tonic-gateto make sure that it is owned either by root or the user running the
1916*0Sstevel@tonic-gateprogram. If the directory is writable by group or by other, it is then
1917*0Sstevel@tonic-gatechecked to make sure that the sticky bit is set.
1918*0Sstevel@tonic-gate
1919*0Sstevel@tonic-gateWill not work on platforms that do not support the C<-k> test
1920*0Sstevel@tonic-gatefor sticky bit.
1921*0Sstevel@tonic-gate
1922*0Sstevel@tonic-gate=item HIGH
1923*0Sstevel@tonic-gate
1924*0Sstevel@tonic-gateIn addition to the MEDIUM security checks, also check for the
1925*0Sstevel@tonic-gatepossibility of ``chown() giveaway'' using the L<POSIX|POSIX>
1926*0Sstevel@tonic-gatesysconf() function. If this is a possibility, each directory in the
1927*0Sstevel@tonic-gatepath is checked in turn for safeness, recursively walking back to the
1928*0Sstevel@tonic-gateroot directory.
1929*0Sstevel@tonic-gate
1930*0Sstevel@tonic-gateFor platforms that do not support the L<POSIX|POSIX>
1931*0Sstevel@tonic-gateC<_PC_CHOWN_RESTRICTED> symbol (for example, Windows NT) it is
1932*0Sstevel@tonic-gateassumed that ``chown() giveaway'' is possible and the recursive test
1933*0Sstevel@tonic-gateis performed.
1934*0Sstevel@tonic-gate
1935*0Sstevel@tonic-gate=back
1936*0Sstevel@tonic-gate
1937*0Sstevel@tonic-gateThe level can be changed as follows:
1938*0Sstevel@tonic-gate
1939*0Sstevel@tonic-gate  File::Temp->safe_level( File::Temp::HIGH );
1940*0Sstevel@tonic-gate
1941*0Sstevel@tonic-gateThe level constants are not exported by the module.
1942*0Sstevel@tonic-gate
1943*0Sstevel@tonic-gateCurrently, you must be running at least perl v5.6.0 in order to
1944*0Sstevel@tonic-gaterun with MEDIUM or HIGH security. This is simply because the
1945*0Sstevel@tonic-gatesafety tests use functions from L<Fcntl|Fcntl> that are not
1946*0Sstevel@tonic-gateavailable in older versions of perl. The problem is that the version
1947*0Sstevel@tonic-gatenumber for Fcntl is the same in perl 5.6.0 and in 5.005_03 even though
1948*0Sstevel@tonic-gatethey are different versions.
1949*0Sstevel@tonic-gate
1950*0Sstevel@tonic-gateOn systems that do not support the HIGH or MEDIUM safety levels
1951*0Sstevel@tonic-gate(for example Win NT or OS/2) any attempt to change the level will
1952*0Sstevel@tonic-gatebe ignored. The decision to ignore rather than raise an exception
1953*0Sstevel@tonic-gateallows portable programs to be written with high security in mind
1954*0Sstevel@tonic-gatefor the systems that can support this without those programs failing
1955*0Sstevel@tonic-gateon systems where the extra tests are irrelevant.
1956*0Sstevel@tonic-gate
1957*0Sstevel@tonic-gateIf you really need to see whether the change has been accepted
1958*0Sstevel@tonic-gatesimply examine the return value of C<safe_level>.
1959*0Sstevel@tonic-gate
1960*0Sstevel@tonic-gate  $newlevel = File::Temp->safe_level( File::Temp::HIGH );
1961*0Sstevel@tonic-gate  die "Could not change to high security"
1962*0Sstevel@tonic-gate      if $newlevel != File::Temp::HIGH;
1963*0Sstevel@tonic-gate
1964*0Sstevel@tonic-gate=cut
1965*0Sstevel@tonic-gate
1966*0Sstevel@tonic-gate{
1967*0Sstevel@tonic-gate  # protect from using the variable itself
1968*0Sstevel@tonic-gate  my $LEVEL = STANDARD;
1969*0Sstevel@tonic-gate  sub safe_level {
1970*0Sstevel@tonic-gate    my $self = shift;
1971*0Sstevel@tonic-gate    if (@_) {
1972*0Sstevel@tonic-gate      my $level = shift;
1973*0Sstevel@tonic-gate      if (($level != STANDARD) && ($level != MEDIUM) && ($level != HIGH)) {
1974*0Sstevel@tonic-gate	carp "safe_level: Specified level ($level) not STANDARD, MEDIUM or HIGH - ignoring\n" if $^W;
1975*0Sstevel@tonic-gate      } else {
1976*0Sstevel@tonic-gate	# Dont allow this on perl 5.005 or earlier
1977*0Sstevel@tonic-gate	if ($] < 5.006 && $level != STANDARD) {
1978*0Sstevel@tonic-gate	  # Cant do MEDIUM or HIGH checks
1979*0Sstevel@tonic-gate	  croak "Currently requires perl 5.006 or newer to do the safe checks";
1980*0Sstevel@tonic-gate	}
1981*0Sstevel@tonic-gate	# Check that we are allowed to change level
1982*0Sstevel@tonic-gate	# Silently ignore if we can not.
1983*0Sstevel@tonic-gate        $LEVEL = $level if _can_do_level($level);
1984*0Sstevel@tonic-gate      }
1985*0Sstevel@tonic-gate    }
1986*0Sstevel@tonic-gate    return $LEVEL;
1987*0Sstevel@tonic-gate  }
1988*0Sstevel@tonic-gate}
1989*0Sstevel@tonic-gate
1990*0Sstevel@tonic-gate=item TopSystemUID
1991*0Sstevel@tonic-gate
1992*0Sstevel@tonic-gateThis is the highest UID on the current system that refers to a root
1993*0Sstevel@tonic-gateUID. This is used to make sure that the temporary directory is
1994*0Sstevel@tonic-gateowned by a system UID (C<root>, C<bin>, C<sys> etc) rather than
1995*0Sstevel@tonic-gatesimply by root.
1996*0Sstevel@tonic-gate
1997*0Sstevel@tonic-gateThis is required since on many unix systems C</tmp> is not owned
1998*0Sstevel@tonic-gateby root.
1999*0Sstevel@tonic-gate
2000*0Sstevel@tonic-gateDefault is to assume that any UID less than or equal to 10 is a root
2001*0Sstevel@tonic-gateUID.
2002*0Sstevel@tonic-gate
2003*0Sstevel@tonic-gate  File::Temp->top_system_uid(10);
2004*0Sstevel@tonic-gate  my $topid = File::Temp->top_system_uid;
2005*0Sstevel@tonic-gate
2006*0Sstevel@tonic-gateThis value can be adjusted to reduce security checking if required.
2007*0Sstevel@tonic-gateThe value is only relevant when C<safe_level> is set to MEDIUM or higher.
2008*0Sstevel@tonic-gate
2009*0Sstevel@tonic-gate=back
2010*0Sstevel@tonic-gate
2011*0Sstevel@tonic-gate=cut
2012*0Sstevel@tonic-gate
2013*0Sstevel@tonic-gate{
2014*0Sstevel@tonic-gate  my $TopSystemUID = 10;
2015*0Sstevel@tonic-gate  sub top_system_uid {
2016*0Sstevel@tonic-gate    my $self = shift;
2017*0Sstevel@tonic-gate    if (@_) {
2018*0Sstevel@tonic-gate      my $newuid = shift;
2019*0Sstevel@tonic-gate      croak "top_system_uid: UIDs should be numeric"
2020*0Sstevel@tonic-gate        unless $newuid =~ /^\d+$/s;
2021*0Sstevel@tonic-gate      $TopSystemUID = $newuid;
2022*0Sstevel@tonic-gate    }
2023*0Sstevel@tonic-gate    return $TopSystemUID;
2024*0Sstevel@tonic-gate  }
2025*0Sstevel@tonic-gate}
2026*0Sstevel@tonic-gate
2027*0Sstevel@tonic-gate=head1 WARNING
2028*0Sstevel@tonic-gate
2029*0Sstevel@tonic-gateFor maximum security, endeavour always to avoid ever looking at,
2030*0Sstevel@tonic-gatetouching, or even imputing the existence of the filename.  You do not
2031*0Sstevel@tonic-gateknow that that filename is connected to the same file as the handle
2032*0Sstevel@tonic-gateyou have, and attempts to check this can only trigger more race
2033*0Sstevel@tonic-gateconditions.  It's far more secure to use the filehandle alone and
2034*0Sstevel@tonic-gatedispense with the filename altogether.
2035*0Sstevel@tonic-gate
2036*0Sstevel@tonic-gateIf you need to pass the handle to something that expects a filename
2037*0Sstevel@tonic-gatethen, on a unix system, use C<"/dev/fd/" . fileno($fh)> for arbitrary
2038*0Sstevel@tonic-gateprograms, or more generally C<< "+<=&" . fileno($fh) >> for Perl
2039*0Sstevel@tonic-gateprograms.  You will have to clear the close-on-exec bit on that file
2040*0Sstevel@tonic-gatedescriptor before passing it to another process.
2041*0Sstevel@tonic-gate
2042*0Sstevel@tonic-gate    use Fcntl qw/F_SETFD F_GETFD/;
2043*0Sstevel@tonic-gate    fcntl($tmpfh, F_SETFD, 0)
2044*0Sstevel@tonic-gate        or die "Can't clear close-on-exec flag on temp fh: $!\n";
2045*0Sstevel@tonic-gate
2046*0Sstevel@tonic-gate=head2 Temporary files and NFS
2047*0Sstevel@tonic-gate
2048*0Sstevel@tonic-gateSome problems are associated with using temporary files that reside
2049*0Sstevel@tonic-gateon NFS file systems and it is recommended that a local filesystem
2050*0Sstevel@tonic-gateis used whenever possible. Some of the security tests will most probably
2051*0Sstevel@tonic-gatefail when the temp file is not local. Additionally, be aware that
2052*0Sstevel@tonic-gatethe performance of I/O operations over NFS will not be as good as for
2053*0Sstevel@tonic-gatea local disk.
2054*0Sstevel@tonic-gate
2055*0Sstevel@tonic-gate=head1 HISTORY
2056*0Sstevel@tonic-gate
2057*0Sstevel@tonic-gateOriginally began life in May 1999 as an XS interface to the system
2058*0Sstevel@tonic-gatemkstemp() function. In March 2000, the OpenBSD mkstemp() code was
2059*0Sstevel@tonic-gatetranslated to Perl for total control of the code's
2060*0Sstevel@tonic-gatesecurity checking, to ensure the presence of the function regardless of
2061*0Sstevel@tonic-gateoperating system and to help with portability.
2062*0Sstevel@tonic-gate
2063*0Sstevel@tonic-gate=head1 SEE ALSO
2064*0Sstevel@tonic-gate
2065*0Sstevel@tonic-gateL<POSIX/tmpnam>, L<POSIX/tmpfile>, L<File::Spec>, L<File::Path>
2066*0Sstevel@tonic-gate
2067*0Sstevel@tonic-gateSee L<IO::File> and L<File::MkTemp> for different implementations of
2068*0Sstevel@tonic-gatetemporary file handling.
2069*0Sstevel@tonic-gate
2070*0Sstevel@tonic-gate=head1 AUTHOR
2071*0Sstevel@tonic-gate
2072*0Sstevel@tonic-gateTim Jenness E<lt>tjenness@cpan.orgE<gt>
2073*0Sstevel@tonic-gate
2074*0Sstevel@tonic-gateCopyright (C) 1999-2003 Tim Jenness and the UK Particle Physics and
2075*0Sstevel@tonic-gateAstronomy Research Council. All Rights Reserved.  This program is free
2076*0Sstevel@tonic-gatesoftware; you can redistribute it and/or modify it under the same
2077*0Sstevel@tonic-gateterms as Perl itself.
2078*0Sstevel@tonic-gate
2079*0Sstevel@tonic-gateOriginal Perl implementation loosely based on the OpenBSD C code for
2080*0Sstevel@tonic-gatemkstemp(). Thanks to Tom Christiansen for suggesting that this module
2081*0Sstevel@tonic-gateshould be written and providing ideas for code improvements and
2082*0Sstevel@tonic-gatesecurity enhancements.
2083*0Sstevel@tonic-gate
2084*0Sstevel@tonic-gate=cut
2085*0Sstevel@tonic-gate
2086*0Sstevel@tonic-gate1;
2087