File Coverage

blib/lib/Win32API/File/Time.pm
Criterion Covered Total %
statement 22 24 91.6
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 30 32 93.7


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Win32API::File::Time - Set file times, even on open or readonly files.
4              
5             =head1 SYNOPSIS
6              
7             use Win32API::File::Time qw{:win};
8             ($atime, $mtime, $ctime) = GetFileTime ($filename);
9             SetFileTime ($filename, $atime, $mtime, $ctime);
10              
11             or
12              
13             use Win32API::File::Time qw{utime};
14             utime $atime, $mtime, $filename or die $^E;
15              
16             =head1 DESCRIPTION
17              
18             The purpose of Win32API::File::Time is to provide maximal access to
19             the file creation, modification, and access times under MSWin32.
20              
21             Under Windows, the Perl utime module will not modify the time of an
22             open file, nor a read-only file. The comments in win32.c indicate
23             that this is the intended functionality, at least for read-only
24             files.
25              
26             This module will read and modify the time on open files, read-only
27             files, and directories. I
28              
29             This module is based on the SetFileTime function in kernel32.dll.
30             Perl's utime built-in also makes explicit use of this function if
31             the "C" run-time version of utime fails. The difference is in how
32             the filehandle is created. The Perl built-in requests access
33             GENERIC_READ | GENERIC_WRITE when modifying file dates, whereas
34             this module requests access FILE_WRITE_ATTRIBUTES.
35              
36             Nothing is exported by default, but all documented subroutines
37             are exportable. In addition, the following export tags are
38             supported:
39              
40             :all => exports everything exportable
41             :win => exports GetFileTime and SetFileTime
42              
43             Wide system calls are implemented (based on the truth of
44             ${^WIDE_SYSTEM_CALLS}) but not currently supported. In other words: I
45             wrote the code, but haven't tested it and don't have any plans to.
46             Feedback will be accepted, and implemented when I get a sufficient
47             supply of tuits.
48              
49             =over 4
50              
51             =cut
52              
53             # Modifications:
54              
55             # 0.001 13-May-2004 T. R. Wyant
56             # Initial version.
57             # 0.002 02-Oct-2004 T. R. Wyant
58             # No code changes. Add the readme file to the manifest,
59             # and add the dependencies to Makefile.PL, since they
60             # really _should_ be there, and Active State is
61             # complaining about them missing.
62             # 0.003 17-Dec-2004 T. R. Wyant
63             # Reimplement time conversion. Also modified the self-
64             # test to use a scratch file for the date modification
65             # portion of the test.
66             # 0.003_01 28-Dec-2004 T. R. Wyant
67             # Close handles after use. In case of error, preserve
68             # the windows error around the close.
69             # 0.004 13-Jan-2005 T. R. Wyant
70             # Fix export tags.
71             # Tweak "Bugs" section to more accurately represent what
72             # I think is going on.
73             # Release to CPAN.
74             # 0.004_01 28-Apr-2005 T. R. Wyant
75             # Assert FILE_FLAG_BACKUP_SEMANTICS when reading times,
76             # so that it works for directories under XP and
77             # 2003 server.
78             # Centralize code to close handles on error.
79             # 0.004_02 04-May-2005 T. R. Wyant
80             # Document behaviour of FAT (and change test to
81             # accomodate.
82             # 0.005 04-May-2005 T. R. Wyant
83             # Release to CPAN.
84              
85 1     1   60372 use strict;
  1         3  
  1         38  
86 1     1   5 use warnings;
  1         2  
  1         50  
87              
88             package Win32API::File::Time;
89              
90 1     1   5 use base qw{Exporter};
  1         6  
  1         129  
91 1     1   6 use vars qw{@EXPORT_OK %EXPORT_TAGS $VERSION};
  1         1  
  1         75  
92 1         85 use vars qw{
93             $FileTimeToSystemTime
94             $FileTimeToLocalFileTime
95             $GetFileTime
96             $LocalFileTimeToFileTime
97             $SetFileTime
98             $SystemTimeToFileTime
99 1     1   6 };
  1         2  
100              
101 1     1   6 use Carp;
  1         2  
  1         74  
102 1     1   1076 use Time::Local;
  1         2591  
  1         149  
103 1     1   2056 use Win32::API;
  0            
  0            
104             use Win32API::File qw{:ALL};
105              
106             $VERSION = '0.006';
107              
108             @EXPORT_OK = qw{GetFileTime SetFileTime utime};
109             %EXPORT_TAGS = (
110             all => [@EXPORT_OK],
111             win => [qw{GetFileTime SetFileTime}],
112             );
113              
114             =item ($atime, $mtime, $ctime) = GetFileTime ($filename);
115              
116             This subroutine returns the access, modification, and creation times of
117             the given file. If it fails, nothing is returned, and the error code
118             can be found in $^E.
119              
120             No, there's no additional functionality here versus the stat
121             built-in. But it was useful for development and testing, and
122             has been exposed for orthogonality's sake.
123              
124             =cut
125              
126             sub GetFileTime {
127             my $fn = shift or croak "usage: GetFileTime (filename)";
128             my $fh = _get_handle ($fn) or return;
129             $GetFileTime ||= _map ('KERNEL32', 'GetFileTime', [qw{N P P P}], 'I');
130             my ($atime, $mtime, $ctime);
131             $atime = $mtime = $ctime = pack 'LL', 0, 0; # Preallocate 64 bits.
132             $GetFileTime->Call ($fh, $ctime, $atime, $mtime) or
133             return _close_handle ($fh);
134             CloseHandle ($fh);
135             return _filetime_to_perltime ($atime, $mtime, $ctime);
136             }
137              
138              
139             =item SetFileTime (filename, atime, mtime, ctime);
140              
141             This subroutine sets the access, modification, and creation times of
142             the given file. The return is true for success, and false for failure.
143             In the latter case, $^E will contain the error.
144              
145             If you don't want to set all of the times, pass 0 or undef for the
146             times you don't want to set. For example,
147              
148             $now = time ();
149             SetFileTime ($filename, $now, $now);
150              
151             is equivalent to the "touch" command for the given file.
152              
153             =cut
154              
155             sub SetFileTime {
156             my $fn = shift or croak "usage: SetFileTime (filename, atime, mtime, ctime)";
157             my $atime = _perltime_to_filetime (shift);
158             my $mtime = _perltime_to_filetime (shift);
159             my $ctime = _perltime_to_filetime (shift);
160             # We assume we can do something useful for an undef.
161             $SetFileTime ||= _map ('KERNEL32', 'SetFileTime', [qw{N P P P}], 'I');
162             my $fh = _get_handle ($fn, 1) or return;
163              
164             $SetFileTime->Call ($fh, $ctime, $atime, $mtime) or
165             return _close_handle ($fh);
166              
167             CloseHandle ($fh);
168             return 1;
169             }
170              
171             =item utime ($atime, $mtime, $filename, ...)
172              
173             This subroutine overrides the built-in of the same name. It does
174             exactly the same thing, but has a different idea than the built-in
175             about what files are legal to change.
176              
177             Like the core utime, it returns the number of files successfully
178             modified. If not all files can be modified, $^E contains the last
179             error encountered.
180              
181             =cut
182              
183             sub utime {
184             my $atime = shift;
185             my $mtime = shift;
186             my $num = 0;
187             foreach my $fn (@_) {
188             SetFileTime ($fn, $atime, $mtime) and $num++;
189             }
190             return $num;
191             }
192              
193              
194             #######################################################################
195             #
196             # Internal subroutines
197             #
198             # _close_handle
199             #
200             # This subroutine closes the given handle, preserving status
201             # around the call.
202              
203             sub _close_handle {
204             my $fh = shift;
205             my $err = Win32::GetLastError ();
206             CloseHandle ($fh);
207             $^E = $err;
208             return;
209             }
210              
211              
212             # _filetime_to_perltime
213             #
214             # This subroutine takes as input a number of Windows file times
215             # and converts them to Perl times.
216             #
217             # The algorithm is due to the unsung heros at Hip Communications
218             # Inc (currently known as ActiveState), who found a way around
219             # the fact that Perl and Windows have a fundamentally different
220             # idea of what local time corresponds to a given GMT when summer
221             # time was in effect at the given GMT, but not at the time the
222             # conversion is made. The given algorithm is consistent with the
223             # results of the stat () function.
224              
225             sub _filetime_to_perltime {
226             my @result;
227             $FileTimeToSystemTime ||= _map (
228             'KERNEL32', 'FileTimeToSystemTime', [qw{P P}], 'I');
229             $FileTimeToLocalFileTime ||= _map (
230             'KERNEL32', 'FileTimeToLocalFileTime', [qw{P P}], 'I');
231             my $st = pack 'ssssssss', 0, 0, 0, 0, 0, 0, 0, 0;
232             foreach my $ft (@_) {
233             my ($low, $high) = unpack 'LL', $ft;
234             $high or do {
235             push @result, undef;
236             next;
237             };
238             my $lf = $ft; # Just to get the space allocated.
239             $FileTimeToLocalFileTime->Call ($ft, $lf) &&
240             $FileTimeToSystemTime->Call ($lf, $st) or do {
241             push @result, undef;
242             next;
243             };
244             my @tm = unpack 'ssssssss', $st;
245             push @result, $tm[0] > 0 ?
246             timelocal (@tm[6, 5, 4, 3], $tm[1] - 1, $tm[0]) :
247             undef;
248             }
249             return wantarray ? @result : $result[0];
250             }
251              
252              
253             # _get_handle
254             #
255             # This subroutine takes a file name and returns a handle to the
256             # file. If the second argument is true, the handle is configured
257             # appropriately for writing attributes; otherwise it is
258             # configured appropriately for reading attributes.
259              
260             sub _get_handle {
261             my $fn = shift;
262             my $write = shift;
263              
264             ${^WIDE_SYSTEM_CALLS} ?
265             CreateFileW ($fn,
266             ($write ? FILE_WRITE_ATTRIBUTES : FILE_READ_ATTRIBUTES),
267             ($write ? FILE_SHARE_WRITE | FILE_SHARE_READ : FILE_SHARE_READ),
268             [],
269             OPEN_EXISTING,
270             ($write ? FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS : FILE_FLAG_BACKUP_SEMANTICS),
271             0,
272             ) :
273             CreateFile ($fn,
274             ($write ? FILE_WRITE_ATTRIBUTES : FILE_READ_ATTRIBUTES),
275             ($write ? FILE_SHARE_WRITE | FILE_SHARE_READ : FILE_SHARE_READ),
276             [],
277             OPEN_EXISTING,
278             ($write ? FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS : FILE_FLAG_BACKUP_SEMANTICS),
279             0,
280             )
281             or do {
282             $^E = Win32::GetLastError ();
283             return;
284             };
285             }
286              
287              
288             # _map
289             #
290             # This subroutine calls Win32API to map an entry point.
291              
292             sub _map {
293             return Win32::API->new (@_) ||
294             croak "Error - Failed to map $_[1] from $_[0]: $^E";
295             }
296              
297              
298             # _perltime_to_filetime
299             #
300             # This subroutine converts perl times to Windows file times.
301              
302             # The same considerations apply to the algorithm used here as to
303             # the one used in _filetime_to_perltime.
304              
305             sub _perltime_to_filetime {
306             my @result;
307             $SystemTimeToFileTime ||= _map (
308             'KERNEL32', 'SystemTimeToFileTime', [qw{P P}], 'I');
309             $LocalFileTimeToFileTime ||= _map (
310             'KERNEL32', 'LocalFileTimeToFileTime', [qw{P P}], 'I');
311             my $zero = pack 'LL', 0, 0; # To get a quadword zero.
312             my ($ft, $lf) = ($zero, $zero); # To get the space allocated.
313             foreach my $pt (@_) {
314             if (defined $pt) {
315             my @tm = localtime ($pt);
316             my $st = pack 'ssssssss', $tm[5] + 1900, $tm[4] + 1, 0,
317             @tm[3, 2, 1, 0], 0;
318             push @result, $SystemTimeToFileTime->Call ($st, $lf) &&
319             $LocalFileTimeToFileTime->Call ($lf, $ft) ? $ft : $zero;
320             }
321             else {
322             push @result, $zero;
323             }
324             }
325             return wantarray ? @result : $result[0];
326             }
327              
328             =back
329              
330             =head1 HISTORY
331              
332             0.001 Initial release
333             0.002 Correct MANIFEST and Makefile.PL dependencies.
334             Tweak documentation. No code changes.
335             0.003 Correct time conversion.
336             Modify test to not change own date.
337             0.004 Close handles after use. Thanks to Chris
338             Camejo for pointing out the leak.
339             Fix export tags to agree with docs.
340             Tweak "BUGS" documentation.
341             0.005 Assert FILE_FLAG_BACKUP_SEMANTICS when reading
342             times, so that it works for directories
343             under XP and 2003 server. Thanks to Leigh
344             Power for pointing out the problem and
345             suggesting the solution.
346             Document FAT, and accomodate it in self-test.
347             Thanks again, Leigh.
348             Centralize code to close handles on error.
349             0.006 T. R. Wyant
350             Re-layout kit, add POD tests.
351              
352             =head1 BUGS
353              
354             As implemented, GetFileTime () constitutes an access, and
355             therefore updates the access time.
356              
357             The stat () builtin, on the other hand, doesn't report
358             an access time change even after GetFileTime () has been
359             used. In fact, it looks to me very much like stat () reports
360             the modification time in element [8] of the list, but I
361             find this nowhere documented.
362              
363             FAT file time resolution is 2 seconds at best, as documented
364             at F.
365             Access time resolution seems to be to the nearest day.
366              
367             =head1 ACKNOWLEDGMENTS
368              
369             This module would not exist without the following people:
370              
371             Aldo Calpini, who gave us Win32::API.
372              
373             Tye McQueen, who gave us Win32API::File.
374              
375             Jenda Krynicky, whose "How2 create a PPM distribution"
376             (F) gave me a leg up on
377             both PPM and tar distributions.
378              
379             The folks of ActiveState (F,
380             formerly known as Hip Communications), who found a way to reconcile
381             Windows' and Perl's subtly different ideas of what time it is.
382              
383             The folks of Cygwin (F), especially those
384             who worked on times.cc in the Cygwin core. This is the B
385             implementation of utime I could find which did what B wanted
386             it to do.
387              
388              
389             =head1 AUTHOR
390              
391             Thomas R. Wyant, III (F)
392              
393             =head1 COPYRIGHT
394              
395             Copyright 2004, 2005 by
396             E. I. DuPont de Nemours and Company, Inc.
397             All rights reserved.
398              
399             Changes since version 0.005 copyright 2007 by Thomas R. Wyant, III. All
400             rights reserved.
401              
402             =head1 LICENSE
403              
404             This module is free software; you can use it, redistribute it and/or
405             modify it under the same terms as Perl itself. Please see
406             L for current licenses.
407              
408             =cut
409