aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2020-01-18 17:18:42 +0200
committerSergey Poznyakoff <gray@gnu.org>2020-01-19 09:45:55 +0200
commite3d9037c37a934718c9bc8fb196006fa7c002ac0 (patch)
tree29a36b983d14afe9ba89e7d4128ce00a8cec09e9
downloadfile-backup-e3d9037c37a934718c9bc8fb196006fa7c002ac0.tar.gz
file-backup-e3d9037c37a934718c9bc8fb196006fa7c002ac0.tar.bz2
Initial commit
-rw-r--r--.gitignore14
-rw-r--r--MANIFEST.SKIP63
-rw-r--r--Makefile.PL34
-rw-r--r--lib/File/Backup.pm117
-rw-r--r--t/00simple.t19
-rw-r--r--t/01numbered.t19
-rw-r--r--t/02auto.t22
-rw-r--r--t/03backup.t32
-rw-r--r--t/04envar.t33
-rw-r--r--t/TestBackup.pm40
10 files changed, 393 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8666cce
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,14 @@
1*~
2\#*#
3.#*
4*.bak
5*.tar*
6.emacs.*
7/tmp/
8/debug.sh
9core
10/MANIFEST
11/MYMETA.*
12/Makefile
13/blib
14/pm_to_blib
diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP
new file mode 100644
index 0000000..58696be
--- /dev/null
+++ b/MANIFEST.SKIP
@@ -0,0 +1,63 @@
1# Avoid version control files.
2\bRCS\b
3\bCVS\b
4\bSCCS\b
5,v$
6\B\.svn\b
7\B\.git\b
8\B\.gitignore\b
9\b_darcs\b
10\B\.cvsignore$
11
12# Avoid VMS specific MakeMaker generated files
13\bDescrip.MMS$
14\bDESCRIP.MMS$
15\bdescrip.mms$
16
17# Avoid Makemaker generated and utility files.
18\bMANIFEST\.bak
19\bMakefile$
20\bblib/
21\bMakeMaker-\d
22\bpm_to_blib\.ts$
23\bpm_to_blib$
24\bblibdirs\.ts$ # 6.18 through 6.25 generated this
25
26# Avoid Module::Build generated and utility files.
27\bBuild$
28\b_build/
29\bBuild.bat$
30\bBuild.COM$
31\bBUILD.COM$
32\bbuild.com$
33
34# Avoid temp and backup files.
35~$
36\.old$
37\#$
38\b\.#
39\.bak$
40\.tmp$
41\.#
42\.rej$
43
44# Avoid OS-specific files/dirs
45# Mac OSX metadata
46\B\.DS_Store
47# Mac OSX SMB mount metadata files
48\B\._
49
50# Avoid Devel::Cover and Devel::CoverX::Covered files.
51\bcover_db\b
52\bcovered\b
53
54# Avoid MYMETA files
55^MYMETA\.
56
57^debug.sh
58^tmp
59^buildreq
60^\.emacs\.*
61
62\.tar$
63\.tar\.gz$
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644
index 0000000..84b1f32
--- /dev/null
+++ b/Makefile.PL
@@ -0,0 +1,34 @@
1use strict;
2use warnings;
3use ExtUtils::MakeMaker;
4use Module::Metadata;
5
6WriteMakefile(
7 NAME => 'File::Backup',
8 VERSION_FROM => 'lib/File/Backup.pm',
9 ABSTRACT_FROM => 'lib/File/Backup.pm',
10 LICENSE => 'gpl_3',
11 AUTHOR => 'Sergey Poznyakoff <gray@gnu.org>',
12 MIN_PERL_VERSION => 5.006,
13 PREREQ_PM => {
14 'File::Temp' => 0.23
15 },
16 TEST_REQUIRES => {
17 'File::Cmp' => 1.07
18 },
19 META_MERGE => {
20 'meta-spec' => { version => 2 },
21 resources => {
22 repository => {
23 type => 'git',
24 url => 'git://git.gnu.org.ua/file-backup.git',
25 web => 'http://git.gnu.org.ua/cgit/file-backup.git/',
26 },
27 },
28 provides => Module::Metadata->provides(version => '1.4',
29 dir => 'lib')
30 }
31);
32
33
34
diff --git a/lib/File/Backup.pm b/lib/File/Backup.pm
new file mode 100644
index 0000000..8aed3bb
--- /dev/null
+++ b/lib/File/Backup.pm
@@ -0,0 +1,117 @@
1package File::Backup;
2use strict;
3use warnings;
4use File::Copy;
5use File::Temp qw(tempdir);
6use File::Basename;
7use Exporter;
8use re '/aa';
9use Carp;
10
11our $VERSION = '1.00';
12our @ISA = qw(Exporter);
13our @EXPORT = qw(BACKUP_NONE
14 BACKUP_SINGLE
15 BACKUP_SIMPLE
16 BACKUP_NUMBERED
17 BACKUP_AUTO
18 backup
19 backup_simple
20 backup_numbered
21 backup_auto);
22
23use constant {
24 BACKUP_NONE => 0, # No backups at all (none,off)
25 BACKUP_SINGLE => 1, # Always make single backups (never,simple)
26 BACKUP_SIMPLE => 1,
27 BACKUP_NUMBERED => 2, # Always make numbered backups (t,numbered)
28 BACKUP_AUTO => 3 # Make numbered if numbered backups exist,
29 # simple otherwise (nil,existing)
30};
31
32my %envtrans = (
33 none => BACKUP_NONE,
34 off => BACKUP_NONE,
35 never => BACKUP_SIMPLE,
36 simple => BACKUP_SIMPLE,
37 t => BACKUP_NUMBERED,
38 numbered => BACKUP_NUMBERED,
39 nil => BACKUP_AUTO,
40 existing => BACKUP_AUTO
41);
42
43my %backup_func = (
44 BACKUP_NONE() => sub {},
45 BACKUP_SIMPLE() => \&backup_simple,
46 BACKUP_NUMBERED() => \&backup_numbered,
47 BACKUP_AUTO() => \&backup_auto
48);
49
50sub backup {
51 my ($file, $type) = @_;
52 unless (defined($type)) {
53 my $v = $ENV{VERSION_CONTROL} || BACKUP_AUTO;
54 if (exists($envtrans{$v})) {
55 $type = $envtrans{$v};
56 } else {
57 $type = BACKUP_AUTO;
58 }
59 }
60 &{$backup_func{$type}}($file);
61}
62
63sub backup_simple {
64 my ($file_name) = @_;
65 my $backup_name = $file_name . '~';
66 copy($file_name, $backup_name)
67 or croak "failed to copy $file_name to $backup_name: $!";
68 return $backup_name;
69}
70
71sub backup_numbered_opt {
72 my ($file_name, $if_exists) = @_;
73
74 my $dir = tempdir(DIR => dirname($file_name), CLEANUP => 1);
75 my $fh = File::Temp->new(DIR => $dir);
76 copy($file_name, $fh) or
77 croak "failed to make a temporary copy of $file_name: $!";
78
79 my $num = (sort { $b <=> $a }
80 map {
81 if (/.+\.~(\d+)~$/) {
82 $1
83 } else {
84 ()
85 }
86 } glob("$file_name.~*~"))[0];
87
88 if (!defined($num)) {
89 return backup_simple($file_name) if $if_exists;
90 $num = '1';
91 }
92
93 my $backup_name;
94 while (1) {
95 $backup_name = "$file_name.~$num~";
96 last if symlink($fh->filename, $backup_name);
97 ++$num;
98 }
99
100 unless (rename($fh->filename, $backup_name)) {
101 croak "can't rename temporary file to $backup_name: $!";
102 }
103 return $backup_name;
104}
105
106sub backup_numbered {
107 my ($file_name) = @_;
108 backup_numbered_opt($file_name, 0);
109}
110
111sub backup_auto {
112 my ($file_name) = @_;
113 backup_numbered_opt($file_name, 1);
114}
115
1161;