summaryrefslogtreecommitdiff
path: root/Master/tlpkg/tlperl/lib/IO/AtomicFile.pm
diff options
context:
space:
mode:
Diffstat (limited to 'Master/tlpkg/tlperl/lib/IO/AtomicFile.pm')
-rwxr-xr-xMaster/tlpkg/tlperl/lib/IO/AtomicFile.pm196
1 files changed, 196 insertions, 0 deletions
diff --git a/Master/tlpkg/tlperl/lib/IO/AtomicFile.pm b/Master/tlpkg/tlperl/lib/IO/AtomicFile.pm
new file mode 100755
index 00000000000..89ad6f0e8f5
--- /dev/null
+++ b/Master/tlpkg/tlperl/lib/IO/AtomicFile.pm
@@ -0,0 +1,196 @@
+package IO::AtomicFile;
+
+### DOCUMENTATION AT BOTTOM OF FILE
+
+# Be strict:
+use strict;
+
+# External modules:
+use IO::File;
+
+
+#------------------------------
+#
+# GLOBALS...
+#
+#------------------------------
+use vars qw($VERSION @ISA);
+
+# The package version, both in 1.23 style *and* usable by MakeMaker:
+$VERSION = "2.110";
+
+# Inheritance:
+@ISA = qw(IO::File);
+
+
+#------------------------------
+# new ARGS...
+#------------------------------
+# Class method, constructor.
+# Any arguments are sent to open().
+#
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new();
+ ${*$self}{'io_atomicfile_suffix'} = '';
+ $self->open(@_) if @_;
+ $self;
+}
+
+#------------------------------
+# DESTROY
+#------------------------------
+# Destructor.
+#
+sub DESTROY {
+ shift->close(1); ### like close, but raises fatal exception on failure
+}
+
+#------------------------------
+# open PATH, MODE
+#------------------------------
+# Class/instance method.
+#
+sub open {
+ my ($self, $path, $mode) = @_;
+ ref($self) or $self = $self->new; ### now we have an instance!
+
+ ### Create tmp path, and remember this info:
+ my $temp = "${path}..TMP" . ${*$self}{'io_atomicfile_suffix'};
+ ${*$self}{'io_atomicfile_temp'} = $temp;
+ ${*$self}{'io_atomicfile_path'} = $path;
+
+ ### Open the file! Returns filehandle on success, for use as a constructor:
+ $self->SUPER::open($temp, $mode) ? $self : undef;
+}
+
+#------------------------------
+# _closed [YESNO]
+#------------------------------
+# Instance method, private.
+# Are we already closed? Argument sets new value, returns previous one.
+#
+sub _closed {
+ my $self = shift;
+ my $oldval = ${*$self}{'io_atomicfile_closed'};
+ ${*$self}{'io_atomicfile_closed'} = shift if @_;
+ $oldval;
+}
+
+#------------------------------
+# close
+#------------------------------
+# Instance method.
+# Close the handle, and rename the temp file to its final name.
+#
+sub close {
+ my ($self, $die) = @_;
+ unless ($self->_closed(1)) { ### sentinel...
+ $self->SUPER::close();
+ rename(${*$self}{'io_atomicfile_temp'},
+ ${*$self}{'io_atomicfile_path'})
+ or ($die ? die "close atomic file: $!\n" : return undef);
+ }
+ 1;
+}
+
+#------------------------------
+# delete
+#------------------------------
+# Instance method.
+# Close the handle, and delete the temp file.
+#
+sub delete {
+ my $self = shift;
+ unless ($self->_closed(1)) { ### sentinel...
+ $self->SUPER::close();
+ return unlink(${*$self}{'io_atomicfile_temp'});
+ }
+ 1;
+}
+
+#------------------------------
+# detach
+#------------------------------
+# Instance method.
+# Close the handle, but DO NOT delete the temp file.
+#
+sub detach {
+ my $self = shift;
+ $self->SUPER::close() unless ($self->_closed(1));
+ 1;
+}
+
+#------------------------------
+1;
+__END__
+
+
+=head1 NAME
+
+IO::AtomicFile - write a file which is updated atomically
+
+
+=head1 SYNOPSIS
+
+ use IO::AtomicFile;
+
+ ### Write a temp file, and have it install itself when closed:
+ my $FH = IO::AtomicFile->open("bar.dat", "w");
+ print $FH "Hello!\n";
+ $FH->close || die "couldn't install atomic file: $!";
+
+ ### Write a temp file, but delete it before it gets installed:
+ my $FH = IO::AtomicFile->open("bar.dat", "w");
+ print $FH "Hello!\n";
+ $FH->delete;
+
+ ### Write a temp file, but neither install it nor delete it:
+ my $FH = IO::AtomicFile->open("bar.dat", "w");
+ print $FH "Hello!\n";
+ $FH->detach;
+
+
+=head1 DESCRIPTION
+
+This module is intended for people who need to update files
+reliably in the face of unexpected program termination.
+
+For example, you generally don't want to be halfway in the middle of
+writing I</etc/passwd> and have your program terminate! Even
+the act of writing a single scalar to a filehandle is I<not> atomic.
+
+But this module gives you true atomic updates, via rename().
+When you open a file I</foo/bar.dat> via this module, you are I<actually>
+opening a temporary file I</foo/bar.dat..TMP>, and writing your
+output there. The act of closing this file (either explicitly
+via close(), or implicitly via the destruction of the object)
+will cause rename() to be called... therefore, from the point
+of view of the outside world, the file's contents are updated
+in a single time quantum.
+
+To ensure that problems do not go undetected, the "close" method
+done by the destructor will raise a fatal exception if the rename()
+fails. The explicit close() just returns undef.
+
+You can also decide at any point to trash the file you've been
+building.
+
+
+=head1 AUTHOR
+
+=head2 Primary Maintainer
+
+David F. Skoll (F<dfs@roaringpenguin.com>).
+
+=head2 Original Author
+
+Eryq (F<eryq@zeegee.com>).
+President, ZeeGee Software Inc (F<http://www.zeegee.com>).
+
+
+=head1 REVISION
+
+$Revision: 1.2 $
+
+=cut