unit module File::Temp:ver<0.0.8>; use File::Directory::Tree; # Characters used to create temporary file/directory names my @filechars = flat('a'..'z', 'A'..'Z', 0..9, '_'); constant MAX-RETRIES = 10; my %roster = (); my %keptfd = (); my Lock $roster-lock; my Lock $keptfd-lock; BEGIN { # Because --doc runs END $roster-lock = Lock.new; $keptfd-lock = Lock.new; } my role File::Temp::AutoUnlink { submethod DESTROY { given self.path { if $_.path.IO ~~ :f { # Workaround, should just be $_ ~~ :f self.close; my $did; $roster-lock.protect: { $did = %roster{$_.path}:delete; $_.unlink if $did; }; unless $did { # Something (probably END) already unlinked it # We could have a debug/testing flag do something here. } } # Directories will not get here yet } } } sub make-temp($type, $template, $tempdir, $prefix, $suffix, $unlink) { my $count = MAX-RETRIES; while ($count--) { my $tempfile = $template; $tempfile ~~ s/ '*' ** 4..* /{ @filechars.roll($/.chars).join }/; my $name = $*SPEC.catfile($tempdir,"$prefix$tempfile$suffix"); next if $name.IO ~~ :e; my $fh; if $type eq 'file' { $fh = try { CATCH { next }; open $name, :rw, :exclusive; }; chmod(0o600,$name); } else { try { CATCH { next }; mkdir($name, 0o700) }; } if $unlink { $roster-lock.protect: { %roster{$name} = $fh; }; $fh &&= $fh does File::Temp::AutoUnlink; } elsif ($type eq 'file') { $keptfd-lock.protect: { %keptfd{$name} = $fh; } } return $type eq 'file' ?? ($name,$fh) !! $name; } fail "Unable to open temporary $type after {MAX-RETRIES} attempts within \"$tempdir\""; } sub tempfile ( $tmpl? = '*' x 10, # positional template :$tempdir? = $*TMPDIR, # where to create these temp files :$prefix? = '', # filename prefix :$suffix? = '', # filename suffix :$unlink? = 1, # remove when program exits? :$template = $tmpl # required named template ) is export { return make-temp('file', $template, $tempdir, $prefix, $suffix, $unlink); } our sub tempdir ( $tmpl? = '*' x 10, # positional template :$tempdir? = $*TMPDIR, # where to create tempdir :$prefix? = '', # directory prefix :$suffix? = '', # directory suffix :$unlink? = 1, # remove when program exits? :$template = $tmpl # required named template ) is export { return make-temp('dir', $template, $tempdir, $prefix, $suffix, $unlink); } END { $roster-lock.protect: { # Workaround -- directly using %roster.keys not reliable under stress. my @rk = %roster.keys; for @rk -> $fn { if $fn.IO ~~ :f { %roster{$fn}.close; unlink($fn); } elsif $fn.IO ~~ :d { rmtree($fn); } } %roster = (); } $keptfd-lock.protect: { my @kk = %keptfd.keys; for @kk -> $fn { %keptfd{$fn}.close; } %keptfd = (); } } =begin pod =NAME File::Temp =SYNOPSIS # Generate a temp file in a temp dir my ($filename,$filehandle) = tempfile; # specify a template for the filename # * are replaced with random characters my ($filename,$filehandle) = tempfile("******"); # Automatically unlink files at end of program (this is the default) my ($filename,$filehandle) = tempfile("******", :unlink); # Specify the directory where the tempfile will be created my ($filename,$filehandle) = tempfile(:tempdir("/path/to/my/dir")); # don't unlink this one my ($filename,$filehandle) = tempfile(:tempdir('.'), :!unlink); # specify a prefix and suffix for the filename my ($filename,$filehandle) = tempfile(:prefix('foo'), :suffix(".txt")); =DESCRIPTION This module exports two routines: =item tempfile =item tempdir =AUTHOR Jonathan Scott Duff =end pod