Add `rdbl-d64`, Git textconv filter for .d64 files (1541 floppy for C64)
[sunny256-utils.git] / Utv / afv_move-selective_move
blobc081168f3d7a8f1f85543e10ad450ce0d26aebfb
1 #!/usr/bin/env perl
3 #=======================================================================
4 # $Id$
5 # File ID: f6e12fcc-fa51-11dd-b506-000475e441b9
6 # Reads file names from stdin or files and places them into a directory
7 # structure based on a date in the file name or the modification time.
9 # Character set: UTF-8
10 # ©opyleft 2004– Øyvind A. Holm <sunny@sunbase.org>
11 # License: GNU General Public License, see end of file for legal stuff.
12 #=======================================================================
14 use strict;
15 use warnings;
16 use File::Copy;
17 use File::Path;
18 use Digest::MD5;
19 use Getopt::Std;
21 $| = 1;
23 our $Debug = 0;
25 our ($opt_c, $opt_d, $opt_h, $opt_l, $opt_L, $opt_m, $opt_o, $opt_O) =
26 ( 0, "", 0, 0, 0, 0, "", 0);
27 our ($opt_q, $opt_s, $opt_S, $opt_v) =
28 ( 0, 0, 0, 0);
29 getopts('cd:hlLmo:OqsSv') || die("Option error. Use -h for help.\n");
31 my $VERSION = "0.5";
33 our $progname = $0;
34 $progname =~ s#^.*/(.*?)$#$1#;
36 my $rcs_id = '$Id$';
37 my $id_date = $rcs_id;
38 $id_date =~ s/^.*?\d+ (\d\d\d\d-.*?\d\d:\d\d:\d\d\S+).*/$1/;
40 my $DEFAULT_DIR = "%Y/%m/%d";
41 my $skip_dirs = $opt_s ? 1 : 0;
42 my $skip_files = ($opt_s | $opt_S) ? 1 : 0;
44 if ($opt_o =~ /[^b]/) {
45 die("Uknown value in -o option. Use -h for help.\n");
47 my $replace_if_bigger = ($opt_o =~ /b/) ? 1 : 0;
49 D(<<END);
51 replace_if_bigger = "$replace_if_bigger"
52 skip_files = "$skip_files"
53 skip_dirs = "$skip_dirs"
54 opt_o = "$opt_o"
56 END
58 my $simul_str = $skip_files ? " (simulating)" : "";
60 $opt_h && usage(0);
62 if ($opt_c + $opt_l + $opt_L > 1) {
63 die("$0: Can’t mix the \"-c\", \"-l\" or \"-L\" options, " .
64 "only one or none allowed.\n");
67 LOOP: while (<>) {
68 my ($Path, $File) =
69 ("", "" );
71 chomp();
72 if ($opt_l && ($_ !~ /^\//)) {
73 warn("$_: Pathname is not absolute\n");
74 next LOOP;
76 if (/\//) {
77 if (/^(.*)\/([^\/]+?)$/) {
78 ($Path, $File) =
79 ($1, $2 );
81 } else {
82 $Path = ".";
83 $File = $_;
85 if ($opt_m || $File =~ /^(.*?)\b(\d\d\d\d)-?(\d\d)-?(\d\d)T(\d\d):?(\d\d):?(\d\d)Z\b(.+)/) {
86 # {{{
87 my ($Pre, $Year, $Mon, $Day, $Hour, $Min, $Sec, $Rest);
88 unless ($opt_m) {
89 ($Pre, $Year, $Mon, $Day, $Hour, $Min, $Sec, $Rest) =
90 ( $1, $2, $3, $4, $5, $6, $7, $8);
92 my $From = "$Path/$File";
93 if (-e $From) {
94 if (-f $From) {
95 # {{{
96 if ($opt_m) {
97 my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev,
98 $size, $atime, $mtime, $ctime, $blksize, $blocks) =
99 stat($From);
100 my @TA = gmtime($mtime);
101 ( $Year, $Mon, $Day, $Hour, $Min, $Sec) =
102 ($TA[5]+1900, $TA[4]+1, $TA[3], $TA[2], $TA[1], $TA[0]);
103 $Year = sprintf("%04u", $Year);
104 $Mon = sprintf("%02u", $Mon);
105 $Day = sprintf("%02u", $Day);
106 $Hour = sprintf("%02u", $Hour);
107 $Min = sprintf("%02u", $Min);
108 $Sec = sprintf("%02u", $Sec);
111 my $Dir = length($opt_d) ? $opt_d : $DEFAULT_DIR;
112 $Dir =~ s/%Y/$Year/g;
113 $Dir =~ s/%m/$Mon/g;
114 $Dir =~ s/%d/$Day/g;
115 $Dir =~ s/%H/$Hour/g;
116 $Dir =~ s/%M/$Min/g;
117 $Dir =~ s/%S/$Sec/g;
118 $Dir =~ s/%%/%/g;
120 my $Dest = "$Dir/$File";
121 my $write_ok = 1;
123 if (!$opt_O && -e $Dest) {
124 if (length($opt_o)) {
125 D("!\$opt_O && -e \$Dest\n");
126 my ($f_dev, $f_ino, $f_mode, $f_nlink, $f_uid,
127 $f_gid, $f_rdev, $f_size, $f_atime, $f_mtime,
128 $f_ctime, $f_blksize, $f_blocks) = stat($From);
129 my ($d_dev, $d_ino, $d_mode, $d_nlink, $d_uid,
130 $d_gid, $d_rdev, $d_size, $d_atime, $d_mtime,
131 $d_ctime, $d_blksize, $d_blocks) = stat($Dest);
132 D("f_size = \"$f_size\", d_size = \"$d_size\", " .
133 "replace_if_bigger = $replace_if_bigger\n");
134 if ($replace_if_bigger && ($f_size <= $d_size)) {
135 warn("\"$From\" is not bigger than \"$Dest\", " .
136 "will not overwrite\n");
137 # unlink($From) || warn("$From: Can’t delete file: $!\n");
138 next LOOP;
140 } else {
141 $write_ok = 0;
145 if ($write_ok) { # FIXME
146 D("Inside \$write_ok\n");
147 $skip_dirs ||
148 -d $Dir ||
149 mkpath($Dir, $opt_v ? 1 : 0, 0777) ||
150 die("mkpath(\"$Dir\", 0, 0777): $!");
151 if ($opt_c) {
152 $opt_v &&
153 print("Copying \"$From\" to " .
154 "\"$Dest\"$simul_str...");
155 $skip_files ||
156 copy($From, $Dest) ||
157 die("\ncopy(\"$From\", \"$Dest\"): $!");
158 $opt_v && print("OK\n");
159 } elsif ($opt_L) {
160 $opt_v &&
161 print("Linking \"$From\" to " .
162 "\"$Dest\"$simul_str...");
163 $skip_files ||
164 link($From, $Dest) ||
165 die("\nlink(\"$From\", \"$Dest\"): $!");
166 $opt_v && print("OK\n");
167 } elsif ($opt_l) {
168 $opt_v &&
169 print("Symlinking \"$From\" to " .
170 "\"$Dest\"$simul_str...");
171 $skip_files ||
172 symlink($From, $Dest) ||
173 die("\nsymlink(\"$From\", " .
174 "\"$Dest\"): $!");
175 $opt_v && print("OK\n");
176 } else {
177 $opt_v &&
178 print("Moving \"$From\" to " .
179 "\"$Dest\"$simul_str...");
180 $skip_files ||
181 move($From, $Dest) ||
182 die("\nmove(\"$From\", \"$Dest\"): $!");
183 $opt_v && print("OK\n");
185 # }}}
186 } else {
187 $opt_q || warn("$Dest: File already exists, will not overwrite\n");
189 # }}}
190 } else {
191 $opt_q || warn("Ignoring non-regular file $From\n");
193 } else {
194 warn("$From: File not found\n");
196 # }}}
200 sub D {
201 chomp(my $Txt = shift);
202 my @call_info = caller;
203 $Debug && print(STDERR "DEBUG $call_info[2]: $Txt\n");
204 } # D()
206 sub md5sum {
207 my $File = shift;
208 open(FILE, $File) || die("$File: $!");
209 binmode(FILE);
211 return (Digest::MD5->new->addfile(*FILE)->hexdigest, " $File\n");
212 } # md5sum()
214 sub usage {
215 # Send the help message to stdout {{{
216 my $Retval = shift;
218 print(<<END);
220 $progname v$VERSION -- $id_date
222 Syntax: $0 [options] [file_with_filenames [...]]
224 The program reads file names from stdin or from the files on the command
225 line and moves or copies the files into a directory structure defined by
226 the user. It can also create soft or hard links if the file system
227 allows it. The file name has to contain a date on the format
229 yyyymmddThhmmssZ
231 which is the date specified in UTC.
233 Options:
235 -c Copy files instead of move
236 -d X Place files under directory X
237 Use the following modifiers for subtree layout:
239 %Y Year with four digits
240 %m Month (00..12)
241 %d Day of month (00..31)
242 %H Hour (00..23)
243 %M Minutes (00..59)
244 %S Seconds (00..61)
245 %% Regular percent sign
247 If the -d option is not specified, "$DEFAULT_DIR" will be used.
249 -h Show this help.
250 -l Create symlinks instead of moving or copying files. The file names
251 in the input has to contain an absolute path to prevent creating
252 dead links. File names not starting with "/" will be ignored.
253 -L Create hard links to the files instead of copying or moving.
254 -m Use the file modification time instead of date found in the file
255 name. All files will be affected, not only those with a date in
256 the file name.
257 -o X Overwrite file if certain conditions is met:
259 i File has identical data
260 b New file is bigger
261 s New file is smaller
262 o New file is older
263 n New file is newer
265 Examples:
267 -o bn
268 File is bigger or newer
269 -o i
270 File is identical
271 -osi
272 File is smaller or identical
274 (This is a TODO, only the 'b' option is implemented.)
276 -O Always overwrite.
277 -q Be quiet, suppress non-critical messages.
278 -s Simulate, don't really move or copy files.
279 -S Semisimulate. Don’t touch the files, only create the directory
280 structure. Useful for running tests with big amounts of data.
281 -v Verbose execution
283 These options are likely to change at the moment.
285 Note: Files on the command line will not be moved themselves, but shall
286 contain file names of the relevant files to be moved.
288 Examples:
290 ls | afv_move -v
291 find /var/tmp/afvroot | afv_move -vl -d newdir/%Y-%m-%d/%H
292 afv_move -vL /tmp/filenames.txt -d %Y/%Y-%m-%d
294 Made by Øyvind A. Holm <sunny\@sunbase.org>
295 License: GNU General Public License version 2 or later ♥
298 exit($Retval);
299 # }}}
300 } # usage()
302 __END__
304 =pod
306 =head1 LICENCE
308 This program is free software; you can redistribute it and/or modify it
309 under the terms of the GNU General Public License as published by the
310 Free Software Foundation; either version 2 of the License, or (at your
311 option) any later version.
313 This program is distributed in the hope that it will be useful, but
314 WITHOUT ANY WARRANTY; without even the implied warranty of
315 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
316 See the GNU General Public License for more details.
318 You should have received a copy of the GNU General Public License along
319 with this program; if not, write to the Free Software Foundation, Inc.,
320 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
322 =cut
324 # vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :
325 # End of file $Id$