line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package File::Save::Home; |
2
|
|
|
|
|
|
|
require 5.006_001; |
3
|
5
|
|
|
5
|
|
4977
|
use strict; |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
197
|
|
4
|
5
|
|
|
5
|
|
30
|
use warnings; |
|
5
|
|
|
|
|
6
|
|
|
5
|
|
|
|
|
230
|
|
5
|
5
|
|
|
5
|
|
34
|
use Exporter (); |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
554
|
|
6
|
|
|
|
|
|
|
our $VERSION = '0.09'; |
7
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
8
|
|
|
|
|
|
|
our @EXPORT_OK = qw( |
9
|
|
|
|
|
|
|
get_home_directory |
10
|
|
|
|
|
|
|
get_subhome_directory_status |
11
|
|
|
|
|
|
|
make_subhome_directory |
12
|
|
|
|
|
|
|
restore_subhome_directory_status |
13
|
|
|
|
|
|
|
conceal_target_file |
14
|
|
|
|
|
|
|
reveal_target_file |
15
|
|
|
|
|
|
|
make_subhome_temp_directory |
16
|
|
|
|
|
|
|
); |
17
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( |
18
|
|
|
|
|
|
|
subhome_status => [ qw| |
19
|
|
|
|
|
|
|
get_subhome_directory_status |
20
|
|
|
|
|
|
|
restore_subhome_directory_status |
21
|
|
|
|
|
|
|
| ], |
22
|
|
|
|
|
|
|
target => [ qw| |
23
|
|
|
|
|
|
|
conceal_target_file |
24
|
|
|
|
|
|
|
reveal_target_file |
25
|
|
|
|
|
|
|
| ], |
26
|
|
|
|
|
|
|
); |
27
|
5
|
|
|
5
|
|
27
|
use Carp; |
|
5
|
|
|
|
|
13
|
|
|
5
|
|
|
|
|
641
|
|
28
|
5
|
|
|
5
|
|
31
|
use File::Path; |
|
5
|
|
|
|
|
7
|
|
|
5
|
|
|
|
|
325
|
|
29
|
5
|
|
|
|
|
503
|
use File::Spec::Functions qw| |
30
|
|
|
|
|
|
|
catdir |
31
|
|
|
|
|
|
|
catfile |
32
|
|
|
|
|
|
|
catpath |
33
|
|
|
|
|
|
|
splitdir |
34
|
|
|
|
|
|
|
splitpath |
35
|
5
|
|
|
5
|
|
4671
|
|; |
|
5
|
|
|
|
|
4835
|
|
36
|
5
|
|
|
5
|
|
6614
|
use File::Temp qw| tempdir |; |
|
5
|
|
|
|
|
129502
|
|
|
5
|
|
|
|
|
607
|
|
37
|
|
|
|
|
|
|
*ok = *Test::More::ok; |
38
|
5
|
|
|
5
|
|
46
|
use Cwd; |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
311
|
|
39
|
5
|
|
|
5
|
|
34
|
use File::Find; |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
7211
|
|
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
#################### DOCUMENTATION ################### |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=head1 NAME |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
File::Save::Home - Place file safely under user home directory |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
=head1 VERSION |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
This document refers to version 0.09, released December 14, 2012. |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
=head1 SYNOPSIS |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
use File::Save::Home qw( |
54
|
|
|
|
|
|
|
get_home_directory |
55
|
|
|
|
|
|
|
get_subhome_directory_status |
56
|
|
|
|
|
|
|
make_subhome_directory |
57
|
|
|
|
|
|
|
restore_subhome_directory_status |
58
|
|
|
|
|
|
|
conceal_target_file |
59
|
|
|
|
|
|
|
reveal_target_file |
60
|
|
|
|
|
|
|
make_subhome_temp_directory |
61
|
|
|
|
|
|
|
); |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
$home_dir = get_home_directory(); |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
$desired_dir_ref = get_subhome_directory_status("desired/directory"); |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
$desired_dir_ref = get_subhome_directory_status( |
68
|
|
|
|
|
|
|
"desired/directory", |
69
|
|
|
|
|
|
|
"pseudohome/directory", # two-argument version |
70
|
|
|
|
|
|
|
); |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
$desired_dir = make_subhome_directory($desired_dir_ref); |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
restore_subhome_directory_status($desired_dir_ref); |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
$target_ref = conceal_target_file( { |
77
|
|
|
|
|
|
|
dir => $desired_dir, |
78
|
|
|
|
|
|
|
file => 'file_to_be_checked', |
79
|
|
|
|
|
|
|
test => 0, |
80
|
|
|
|
|
|
|
} ); |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
reveal_target_file($target_ref); |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
$tmpdir = make_subhome_temp_directory(); |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
$tmpdir = make_subhome_temp_directory( |
87
|
|
|
|
|
|
|
"pseudohome/directory", # optional argument version |
88
|
|
|
|
|
|
|
); |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
=head1 DESCRIPTION |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
In the course of deploying an application on another user's system, you |
93
|
|
|
|
|
|
|
sometimes need to place a file in or underneath that user's home |
94
|
|
|
|
|
|
|
directory. Can you do so safely? |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
This Perl extension provides several functions which try to determine whether |
97
|
|
|
|
|
|
|
you can, indeed, safely create directories and files underneath a user's home |
98
|
|
|
|
|
|
|
directory. Among other things, if you are placing a file in such a location |
99
|
|
|
|
|
|
|
only temporarily -- say, for testing purposes -- you can temporarily hide |
100
|
|
|
|
|
|
|
any already existing file with the same name and restore it to its original |
101
|
|
|
|
|
|
|
name and timestamps when you are done. |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=head1 USAGE |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
=head2 C |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
Analyzes environmental information to determine whether there exists on the |
108
|
|
|
|
|
|
|
system a 'HOME' or 'home-equivalent' directory. Takes no arguments. Returns |
109
|
|
|
|
|
|
|
that directory if it exists; Cs otherwise. |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
On Win32, this directory is the one returned by the following function from the Fmodule: |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
Win32->import( qw(CSIDL_LOCAL_APPDATA) ); |
114
|
|
|
|
|
|
|
$realhome = Win32::GetFolderPath( CSIDL_LOCAL_APPDATA() ); |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
... which translates to something like F. |
117
|
|
|
|
|
|
|
(For a further discussion of Win32, see below L"SEE ALSO">.) |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
On Unix-like systems, things are much simpler. We simply check the value of |
120
|
|
|
|
|
|
|
C<$ENV{HOME}>. We cannot do that on Win32 because C<$ENV{HOME}> is not |
121
|
|
|
|
|
|
|
defined there. |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
=cut |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
sub get_home_directory { |
126
|
9
|
|
|
9
|
1
|
16710
|
my $realhome; |
127
|
9
|
50
|
|
|
|
67
|
if ($^O eq 'MSWin32') { |
128
|
0
|
|
|
|
|
0
|
require Win32; |
129
|
0
|
|
|
|
|
0
|
Win32->import( qw(CSIDL_LOCAL_APPDATA) ); # 0x001c |
130
|
0
|
|
|
|
|
0
|
$realhome = Win32::GetFolderPath( CSIDL_LOCAL_APPDATA() ); |
131
|
0
|
|
|
|
|
0
|
$realhome =~ s{ }{\ }g; |
132
|
0
|
0
|
|
|
|
0
|
return $realhome if (-d $realhome); |
133
|
0
|
|
|
|
|
0
|
$realhome =~ s|(.*?)\\Local Settings(.*)|$1$2|; |
134
|
0
|
0
|
|
|
|
0
|
return $realhome if (-d $realhome); |
135
|
0
|
|
|
|
|
0
|
croak "Unable to identify directory equivalent to 'HOME' on Win32: $!"; |
136
|
|
|
|
|
|
|
} else { # Unix-like systems |
137
|
9
|
|
|
|
|
44
|
$realhome = $ENV{HOME}; |
138
|
9
|
|
|
|
|
27
|
$realhome =~ s{ }{\ }g; |
139
|
9
|
50
|
|
|
|
265
|
return $realhome if (-d $realhome); |
140
|
0
|
|
|
|
|
0
|
croak "Unable to identify 'HOME' directory: $!"; |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
=head2 C |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
=head3 Single argument version |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
Takes as argument a string holding the name of a directory, either |
149
|
|
|
|
|
|
|
single-level (C) or multi-level (C). Determines |
150
|
|
|
|
|
|
|
whether that directory already exists underneath the user's |
151
|
|
|
|
|
|
|
home or home-equivalent directory. Calls C internally, |
152
|
|
|
|
|
|
|
then tacks on the path passed as argument. |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
=head3 Two-argument version |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
Suppose you want to determine the name of a user's home directory by some |
157
|
|
|
|
|
|
|
other route than C. Suppose, for example, that you're |
158
|
|
|
|
|
|
|
on Win32 and want to use the C method supplied by CPAN distribution |
159
|
|
|
|
|
|
|
File::HomeDir -- a method which returns a different result from that of our |
160
|
|
|
|
|
|
|
C -- but you still want to use those File::Save::Home |
161
|
|
|
|
|
|
|
functions which normally call C internally. Or, suppose |
162
|
|
|
|
|
|
|
you want to supply an arbitrary path. |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
You can now do so by supplying an I to |
165
|
|
|
|
|
|
|
C. This argument should be a valid path name |
166
|
|
|
|
|
|
|
for a directory to which you have write privileges. |
167
|
|
|
|
|
|
|
C will determine if the directory exists and, if |
168
|
|
|
|
|
|
|
so, determine whether the I argument is a subdirectory of the I |
169
|
|
|
|
|
|
|
argument. |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
=head3 Both versions |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
Whether you use the single argument version or the two-argument version, |
174
|
|
|
|
|
|
|
C returns a reference to a four-element hash |
175
|
|
|
|
|
|
|
whose keys are: |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
=over 4 |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=item home |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
The absolute path of the home directory. |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=item abs |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
The absolute path of the specified directory. |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=item flag |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
A Boolean value indicating whether that directory already exists (a true value) |
190
|
|
|
|
|
|
|
or not (C). |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
=item top |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
The uppermost subdirectory passed as the argument to this function. |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
=back |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=cut |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
sub get_subhome_directory_status { |
201
|
6
|
|
|
6
|
1
|
23639
|
my $subdir = shift; |
202
|
6
|
|
|
|
|
18
|
my ($pseudohome, $home); |
203
|
6
|
100
|
|
|
|
32
|
$pseudohome = $_[0] if $_[0]; |
204
|
6
|
100
|
|
|
|
43
|
if (defined $pseudohome) { |
205
|
2
|
100
|
|
|
|
378
|
-d $pseudohome or croak "$pseudohome is not a valid directory: $!"; |
206
|
|
|
|
|
|
|
} |
207
|
5
|
100
|
|
|
|
43
|
$home = defined $pseudohome |
208
|
|
|
|
|
|
|
? $pseudohome |
209
|
|
|
|
|
|
|
: get_home_directory(); |
210
|
5
|
|
|
|
|
23
|
my $dirname = "$home/$subdir"; |
211
|
5
|
|
|
|
|
42
|
my $subdir_top = (splitdir($subdir))[0]; |
212
|
|
|
|
|
|
|
|
213
|
5
|
100
|
|
|
|
760
|
if (-d $dirname) { |
214
|
|
|
|
|
|
|
return { |
215
|
1
|
|
|
|
|
21
|
home => $home, |
216
|
|
|
|
|
|
|
top => $subdir_top, |
217
|
|
|
|
|
|
|
abs => $dirname, |
218
|
|
|
|
|
|
|
flag => 1, |
219
|
|
|
|
|
|
|
}; |
220
|
|
|
|
|
|
|
} else { |
221
|
|
|
|
|
|
|
return { |
222
|
4
|
|
|
|
|
46
|
home => $home, |
223
|
|
|
|
|
|
|
top => $subdir_top, |
224
|
|
|
|
|
|
|
abs => $dirname, |
225
|
|
|
|
|
|
|
flag => undef, |
226
|
|
|
|
|
|
|
}; |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
=head2 C |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
Takes as argument the hash reference returned by |
233
|
|
|
|
|
|
|
C. Examines the first element in that array -- |
234
|
|
|
|
|
|
|
the directory name -- and creates the directory if it doesn't already exist. |
235
|
|
|
|
|
|
|
The function Cs if the directory cannot be created. |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
=cut |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
sub make_subhome_directory { |
240
|
3
|
|
|
3
|
1
|
1232
|
my $desired_dir_ref = shift; |
241
|
3
|
|
|
|
|
7
|
my $dirname = $desired_dir_ref->{abs}; |
242
|
3
|
50
|
|
|
|
59
|
if (! -d $dirname) { |
243
|
3
|
50
|
|
|
|
44555
|
mkpath $dirname |
244
|
|
|
|
|
|
|
or croak "Unable to create desired directory $dirname: $!"; |
245
|
|
|
|
|
|
|
} |
246
|
3
|
|
|
|
|
19
|
return $dirname; |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
=head2 C |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
Undoes C, I if there was no specified |
252
|
|
|
|
|
|
|
directory under the user's home directory on the user's system before |
253
|
|
|
|
|
|
|
testing, any such directory created during testing is removed. On the |
254
|
|
|
|
|
|
|
other hand, if there I such a directory present before testing, |
255
|
|
|
|
|
|
|
it is left unchanged. |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
=cut |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
sub restore_subhome_directory_status { |
260
|
3
|
|
|
3
|
1
|
8274
|
my $desired_dir_ref = shift; |
261
|
3
|
|
|
|
|
13
|
my $home = $desired_dir_ref->{home}; |
262
|
3
|
|
|
|
|
7
|
my $desired_dir = $desired_dir_ref->{abs}; |
263
|
3
|
|
|
|
|
8
|
my $subdir_top = $desired_dir_ref->{top}; |
264
|
3
|
50
|
|
|
|
16
|
if (! defined $desired_dir_ref->{flag}) { |
265
|
3
|
|
|
|
|
22760
|
my $cwd = cwd(); |
266
|
|
|
|
|
|
|
find { |
267
|
|
|
|
|
|
|
bydepth => 1, |
268
|
|
|
|
|
|
|
no_chdir => 1, |
269
|
|
|
|
|
|
|
wanted => sub { |
270
|
6
|
100
|
66
|
6
|
|
350
|
if (! -l && -d _) { |
271
|
5
|
50
|
|
|
|
1240
|
rmdir or warn "Couldn't rmdir $_: $!"; |
272
|
|
|
|
|
|
|
} else { |
273
|
1
|
50
|
|
|
|
173
|
unlink or warn "Couldn't unlink $_: $!"; |
274
|
|
|
|
|
|
|
} |
275
|
|
|
|
|
|
|
} |
276
|
3
|
|
|
|
|
1669
|
} => ("$home/$subdir_top"); |
277
|
3
|
50
|
|
|
|
296
|
(! -d $desired_dir) |
278
|
|
|
|
|
|
|
? return 1 |
279
|
|
|
|
|
|
|
: croak "Unable to restore directory created during test: $!"; |
280
|
|
|
|
|
|
|
} else { |
281
|
0
|
|
|
|
|
0
|
return 1; |
282
|
|
|
|
|
|
|
} |
283
|
|
|
|
|
|
|
} |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
=head2 C |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
=head3 Regular version: no arguments |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
Creates a randomly named temporary directory underneath the home or |
290
|
|
|
|
|
|
|
home-equivalent directory returned by C. |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
=head3 Optional argument version |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
Creates a randomly named temporary directory underneath the directory supplied |
295
|
|
|
|
|
|
|
as the single argument. This version is analogous to the two-argument verion |
296
|
|
|
|
|
|
|
of L"get_subhome_directory_status()"> above. You could use it if, for |
297
|
|
|
|
|
|
|
example, you wanted to use Cmy_home()> to supply a value for |
298
|
|
|
|
|
|
|
the user's home directory instead of our C. |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
=head3 Both versions |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
In both versions, the temporary subdirectory is created by calling |
303
|
|
|
|
|
|
|
C $home, CLEANUP => 1)>. The function |
304
|
|
|
|
|
|
|
returns the directory path if successful; Cs otherwise. |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
B Any temporary directory so created remains in existence for |
307
|
|
|
|
|
|
|
the duration of the program, but is deleted (along with all its contents) |
308
|
|
|
|
|
|
|
when the program exits. |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
=cut |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
sub make_subhome_temp_directory { |
313
|
3
|
|
|
3
|
1
|
3302
|
my ($pseudohome, $home); |
314
|
3
|
100
|
|
|
|
15
|
$pseudohome = $_[0] if $_[0]; |
315
|
3
|
100
|
|
|
|
13
|
if (defined $pseudohome) { |
316
|
2
|
100
|
|
|
|
219
|
-d $pseudohome or croak "$pseudohome is not a valid directory: $!"; |
317
|
|
|
|
|
|
|
} |
318
|
2
|
100
|
|
|
|
10
|
$home = defined $pseudohome |
319
|
|
|
|
|
|
|
? $pseudohome |
320
|
|
|
|
|
|
|
: get_home_directory(); |
321
|
|
|
|
|
|
|
# my $tdir = tempdir(DIR => get_home_directory(), CLEANUP => 1); |
322
|
2
|
|
|
|
|
26
|
my $tdir = tempdir(DIR => $home, CLEANUP => 1); |
323
|
2
|
50
|
|
|
|
1317
|
return $tdir ? $tdir : croak "Unable to create temp dir under home: $!"; |
324
|
|
|
|
|
|
|
} |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
=head2 C |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
Determines whether file with specified name already exists in specified |
329
|
|
|
|
|
|
|
directory and, if so, temporarily hides it by renaming it with a F<.hidden> |
330
|
|
|
|
|
|
|
suffix and storing away its last access and modification times. Takes as |
331
|
|
|
|
|
|
|
argument a reference to a hash with these keys: |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
=over 4 |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
=item dir |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
The directory in which the file is presumed to exist. |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
=item file |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
The targeted file, I the file to be temporarily hidden if it already |
342
|
|
|
|
|
|
|
exists. |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
=item test |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
Boolean value which, if turned on (C<1>), will cause the function, when |
347
|
|
|
|
|
|
|
called, to run two C tests. Defaults to off (C<0>). |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
=back |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
Returns a reference to a hash with these keys: |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
=over 4 |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
=item full |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
The absolute path to the target file. |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
=item hidden |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
The absolute path to the now-hidden file. |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
=item atime |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
The last access time to the target file (C<(stat($file{full}))[8]>). |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
=item modtime |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
The last modification time to the target file (C<(stat($file{full}))[9]>). |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
=item test |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
The value of the key C in the hash passed by reference as an argument to |
374
|
|
|
|
|
|
|
this function. |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
=back |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
=cut |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
sub conceal_target_file { |
381
|
2
|
|
|
2
|
1
|
1131
|
my $arg_ref = shift; |
382
|
2
|
|
|
|
|
6
|
my $desired_dir = $arg_ref->{dir}; |
383
|
2
|
|
|
|
|
4
|
my $target_file = $arg_ref->{file}; |
384
|
2
|
|
|
|
|
4
|
my $test_flag = $arg_ref->{test}; |
385
|
2
|
|
|
|
|
44
|
my $target_file_hidden = $target_file . '.hidden'; |
386
|
2
|
|
|
|
|
5
|
my %targ; |
387
|
2
|
|
|
|
|
14
|
$targ{full} = catfile( $desired_dir, $target_file ); |
388
|
2
|
|
|
|
|
11
|
$targ{hidden} = catfile( $desired_dir, $target_file_hidden ); |
389
|
2
|
100
|
|
|
|
45
|
if (-f $targ{full}) { |
390
|
1
|
|
|
|
|
23
|
$targ{atime} = (stat($targ{full}))[8]; |
391
|
1
|
|
|
|
|
17
|
$targ{modtime} = (stat($targ{full}))[9]; |
392
|
1
|
50
|
|
|
|
82
|
rename $targ{full}, $targ{hidden} |
393
|
|
|
|
|
|
|
or croak "Unable to rename $targ{full}: $!"; |
394
|
1
|
50
|
|
|
|
4
|
if ($test_flag) { |
395
|
1
|
|
|
|
|
20
|
ok(! -f $targ{full}, "target file temporarily suppressed"); |
396
|
1
|
|
|
|
|
663
|
ok(-f $targ{hidden}, "target file now hidden"); |
397
|
|
|
|
|
|
|
} |
398
|
|
|
|
|
|
|
} else { |
399
|
1
|
50
|
|
|
|
4
|
if ($test_flag) { |
400
|
1
|
|
|
|
|
11
|
ok(! -f $targ{full}, "target file not found"); |
401
|
1
|
|
|
|
|
356
|
ok(1, "target file not found"); |
402
|
|
|
|
|
|
|
} |
403
|
|
|
|
|
|
|
} |
404
|
2
|
|
|
|
|
592
|
$targ{test} = $test_flag; |
405
|
2
|
|
|
|
|
16
|
return { %targ }; |
406
|
|
|
|
|
|
|
} |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
=head2 C |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
Used in conjunction with C to restore the original |
411
|
|
|
|
|
|
|
status of the file targeted by C, I renames the |
412
|
|
|
|
|
|
|
hidden file to its original name by removing the F<.hidden> suffix, thereby |
413
|
|
|
|
|
|
|
deleting any other file with the original name created between the calls tothe |
414
|
|
|
|
|
|
|
two functions. Cs if the hidden file cannot be renamed. Takes as |
415
|
|
|
|
|
|
|
argument the hash reference returned by C. If the |
416
|
|
|
|
|
|
|
value for the C key in the hash passed as an argument to |
417
|
|
|
|
|
|
|
C was true, then a call to C |
418
|
|
|
|
|
|
|
will run three C tests. |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
=cut |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
sub reveal_target_file { |
423
|
2
|
|
|
2
|
1
|
10
|
my $target_ref = shift;; |
424
|
2
|
100
|
|
|
|
60
|
if(-f $target_ref->{hidden} ) { |
425
|
1
|
50
|
|
|
|
50
|
rename $target_ref->{hidden}, $target_ref->{full}, |
426
|
|
|
|
|
|
|
or croak "Unable to rename $target_ref->{hidden}: $!"; |
427
|
1
|
50
|
|
|
|
5
|
if ($target_ref->{test}) { |
428
|
1
|
|
|
|
|
17
|
ok(-f $target_ref->{full}, |
429
|
|
|
|
|
|
|
"target file re-established"); |
430
|
1
|
|
|
|
|
265
|
ok(! -f $target_ref->{hidden}, |
431
|
|
|
|
|
|
|
"hidden target now gone"); |
432
|
1
|
|
|
|
|
269
|
ok( (utime $target_ref->{atime}, |
433
|
|
|
|
|
|
|
$target_ref->{modtime}, |
434
|
|
|
|
|
|
|
($target_ref->{full}) |
435
|
|
|
|
|
|
|
), "atime and modtime of target file restored"); |
436
|
|
|
|
|
|
|
} |
437
|
|
|
|
|
|
|
} else { |
438
|
1
|
50
|
|
|
|
4
|
if ($target_ref->{test}) { |
439
|
1
|
|
|
|
|
4
|
ok(1, "test not relevant"); |
440
|
1
|
|
|
|
|
363
|
ok(1, "test not relevant"); |
441
|
1
|
|
|
|
|
355
|
ok(1, "test not relevant"); |
442
|
|
|
|
|
|
|
} |
443
|
|
|
|
|
|
|
} |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
=head1 BUGS AND TODO |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
So far tested only on Unix-like systems and Win32. |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
=head1 SEE ALSO |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
perl(1). ExtUtils::ModuleMaker::Auxiliary. ExtUtils::ModuleMaker::Utility. |
453
|
|
|
|
|
|
|
The latter two packages are part of the ExtUtils::ModuleMaker distribution |
454
|
|
|
|
|
|
|
available from the same author on CPAN. They and the ExtUtils::ModuleMaker |
455
|
|
|
|
|
|
|
test suite provide examples of the use of File::Save::Home. |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
Two other distributions located on CPAN, File::HomeDir and |
458
|
|
|
|
|
|
|
File::HomeDir::Win32, may also be used to locate a suitable value for a user's |
459
|
|
|
|
|
|
|
home directory. It should be noted, however, that those modules and |
460
|
|
|
|
|
|
|
File::Save::Home each take a different approach to defining a home directory |
461
|
|
|
|
|
|
|
on Win32 systems. Hence, each may deliver a different result on a given |
462
|
|
|
|
|
|
|
system. I cannot say that one distribution's approach is any more or less |
463
|
|
|
|
|
|
|
correct than the other two's approaches. The following comments should be |
464
|
|
|
|
|
|
|
viewed as my subjective impressions; YMMV. |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
File::HomeDir was originally written by Sean M Burke and is now maintained by |
467
|
|
|
|
|
|
|
Adam Kennedy. As of version 0.52 its interface provides three methods for the |
468
|
|
|
|
|
|
|
''current user'': |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
$home = File::HomeDir->my_home; |
471
|
|
|
|
|
|
|
$docs = File::HomeDir->my_documents; |
472
|
|
|
|
|
|
|
$data = File::HomeDir->my_data; |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
When I ran these three methods on a Win2K Pro system running ActivePerl 8, I |
475
|
|
|
|
|
|
|
got these results: |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
C:\WINNT\system32>perl -MFile::HomeDir -e "print File::HomeDir->my_home" |
478
|
|
|
|
|
|
|
C:\Documents and Settings\localuser |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
C:\WINNT\system32>perl -MFile::HomeDir -e "print File::HomeDir->my_documents" |
481
|
|
|
|
|
|
|
C:\Documents and Settings\localuser\My Documents |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
C:\WINNT\system32>perl -MFile::HomeDir -e "print File::HomeDir->my_data" |
484
|
|
|
|
|
|
|
C:\Documents and Settings\localuser\Local Settings\Application Data |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
In contrast, when I ran the closest equivalent method in File::Save::Home, |
487
|
|
|
|
|
|
|
C, I got this result: |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
C:\WINNT\system32>perl -MFile::Save::Home -e "print File::Save::Home->get_home_directory" |
490
|
|
|
|
|
|
|
C:\Documents and Settings\localuser\Local Settings\Application Data |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
In other words, Cget_home_directory> gave the same result |
493
|
|
|
|
|
|
|
as Cmy_data>, I, as I might have expected, the same |
494
|
|
|
|
|
|
|
result as Cmy_home>. |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
These results can be explained by peeking behind the curtains and looking at |
497
|
|
|
|
|
|
|
the source code for each module. |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
=head2 File::HomeDir |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
File::HomeDir's objective is to provide a value for a user's home directory on |
502
|
|
|
|
|
|
|
a wide variety of operating systems. When invoked, it detects the operating |
503
|
|
|
|
|
|
|
system you're on and calls a subclassed module. When used on a Win32 system, |
504
|
|
|
|
|
|
|
that subclass is called File::HomeDir::Windows (not to be confused with the |
505
|
|
|
|
|
|
|
separate CPAN distribution File::HomeDir::Win32). |
506
|
|
|
|
|
|
|
Cmy_home()> looks like this: |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
sub my_home { |
509
|
|
|
|
|
|
|
my $class = shift; |
510
|
|
|
|
|
|
|
if ( $ENV{USERPROFILE} ) { return $ENV{USERPROFILE}; } |
511
|
|
|
|
|
|
|
if ( $ENV{HOMEDRIVE} and $ENV{HOMEPATH} ) { |
512
|
|
|
|
|
|
|
return File::Spec->catpath( $ENV{HOMEDRIVE}, $ENV{HOMEPATH}, '',); |
513
|
|
|
|
|
|
|
} |
514
|
|
|
|
|
|
|
Carp::croak("Could not locate current user's home directory"); |
515
|
|
|
|
|
|
|
} |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
In other words, determine the current user's home directory simply by checking |
518
|
|
|
|
|
|
|
environmental variables analogous to the C<$ENV{HOME}> on Unix-like systems. |
519
|
|
|
|
|
|
|
A very straightforward approach! |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
As mentioned above, File::Save::Home takes a different approach. It uses the |
522
|
|
|
|
|
|
|
Win32 module to, in effect, check a particular key in the registry. |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
Win32->import( qw(CSIDL_LOCAL_APPDATA) ); |
525
|
|
|
|
|
|
|
$realhome = Win32::GetFolderPath( CSIDL_LOCAL_APPDATA() ); |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
This approach was suggested to me in August 2005 by several members of |
528
|
|
|
|
|
|
|
Perlmonks. (See threads I |
529
|
|
|
|
|
|
|
(L) and I |
530
|
|
|
|
|
|
|
(L).) I adopted this approach in part |
531
|
|
|
|
|
|
|
because the people recommending it knew more about Windows than I did, and in |
532
|
|
|
|
|
|
|
part because File::HomeDir was not quite as mature as it has since become. |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
But don't trust me; trust Microsoft! Here's their explanation for the use of |
535
|
|
|
|
|
|
|
CSIDL values in general and CSIDL_LOCAL_APPDATA() in particular: |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
=over 4 |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
=item * |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
I
|
542
|
|
|
|
|
|
|
to identify special folders used frequently by |
543
|
|
|
|
|
|
|
applications, but which may not have the same name or |
544
|
|
|
|
|
|
|
location on any given system. For example, the system |
545
|
|
|
|
|
|
|
folder may be ''C:\Windows'' on one system and |
546
|
|
|
|
|
|
|
''C:\Winnt'' on another. These constants are defined in |
547
|
|
|
|
|
|
|
Shlobj.h and Shfolder.h.> |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
=item * |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
I
|
552
|
|
|
|
|
|
|
Version 5.0. The file system directory that serves as |
553
|
|
|
|
|
|
|
a data repository for local (nonroaming) applications. |
554
|
|
|
|
|
|
|
A typical path is C:\Documents and |
555
|
|
|
|
|
|
|
Settings\username\Local Settings\Application Data.> |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
=back |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
(Source: |
560
|
|
|
|
|
|
|
L. |
561
|
|
|
|
|
|
|
Link valid as of Feb 18 2006. Thanks to Soren Andersen for reminding me of |
562
|
|
|
|
|
|
|
this citation.) |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
It is interesting that the I File::HomeDir methods listed above, |
565
|
|
|
|
|
|
|
C and C both rely on using a Win32 module to peer |
566
|
|
|
|
|
|
|
into the registry, albeit in a slightly different manner from |
567
|
|
|
|
|
|
|
Cget_home_directory>. TIMTOWTDI. |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
In an event, File::Save::Home has a number of useful methods I |
570
|
|
|
|
|
|
|
C which merit your consideration. And, as noted above, |
571
|
|
|
|
|
|
|
you can supply any valid directory as an optional additional argument to the |
572
|
|
|
|
|
|
|
two File::Save::Home functions which normally default to calling |
573
|
|
|
|
|
|
|
C internally. |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
=head2 File::HomeDir::Win32 |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
File::HomeDir::Win32 was originally written by Rob Rothenberg and is now |
578
|
|
|
|
|
|
|
maintained by Randy Kobes. According to Adam Kennedy |
579
|
|
|
|
|
|
|
(L), |
580
|
|
|
|
|
|
|
''The functionality in File::HomeDir::Win32 is gradually being merged into |
581
|
|
|
|
|
|
|
File::HomeDir over time and will eventually be deprecated (although left in |
582
|
|
|
|
|
|
|
place for compatibility purposes).'' Because I have not yet fully installed |
583
|
|
|
|
|
|
|
File::HomeDir::Win32, I will defer further comparison between it and |
584
|
|
|
|
|
|
|
File::Save::Home to a later date. |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
=head1 AUTHOR |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
James E Keenan |
589
|
|
|
|
|
|
|
CPAN ID: JKEENAN |
590
|
|
|
|
|
|
|
jkeenan@cpan.org |
591
|
|
|
|
|
|
|
http://search.cpan.org/~jkeenan |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
=head1 ACKNOWLEDGMENTS |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
File::Save::Home has its origins in the maintenance revisions I was doing on |
596
|
|
|
|
|
|
|
CPAN distribution ExtUtils::ModuleMaker in the summer of 2005. |
597
|
|
|
|
|
|
|
After I made a presentation about that distribution to the Toronto Perlmongers |
598
|
|
|
|
|
|
|
on October 27, 2005, Michael Graham suggested that certain utility functions |
599
|
|
|
|
|
|
|
could be extracted to a separate Perl extension for more general applicability. |
600
|
|
|
|
|
|
|
This module is the implementation of Michael's suggestion. |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
While I was developing those utility functions for ExtUtils::ModuleMaker, I |
603
|
|
|
|
|
|
|
turned to the Perlmonks for assistance with the problem of determining a |
604
|
|
|
|
|
|
|
suitable value for the user's home directory on Win32 systems. In the |
605
|
|
|
|
|
|
|
Perlmonks discussion threads referred to above I received helpful suggestions |
606
|
|
|
|
|
|
|
from monks CountZero, Tanktalus, xdg and holli, among others. |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
Thanks to Rob Rothenberg for prodding me to expand the SEE ALSO section and to |
609
|
|
|
|
|
|
|
Adam Kennedy for responding to questions about File::HomeDir. |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
=head1 COPYRIGHT |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
Copyright (c) 2005-06 James E. Keenan. United States. All rights reserved. |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
This program is free software; you can redistribute |
616
|
|
|
|
|
|
|
it and/or modify it under the same terms as Perl itself. |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
The full text of the license can be found in the |
619
|
|
|
|
|
|
|
LICENSE file included with this module. |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
=head1 DISCLAIMER OF WARRANTY |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY |
624
|
|
|
|
|
|
|
FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN |
625
|
|
|
|
|
|
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES |
626
|
|
|
|
|
|
|
PROVIDE THE SOFTWARE ''AS IS'' WITHOUT WARRANTY OF ANY KIND, EITHER |
627
|
|
|
|
|
|
|
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
628
|
|
|
|
|
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE |
629
|
|
|
|
|
|
|
ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH |
630
|
|
|
|
|
|
|
YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL |
631
|
|
|
|
|
|
|
NECESSARY SERVICING, REPAIR, OR CORRECTION. |
632
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |
634
|
|
|
|
|
|
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR |
635
|
|
|
|
|
|
|
REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE |
636
|
|
|
|
|
|
|
LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, |
637
|
|
|
|
|
|
|
OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE |
638
|
|
|
|
|
|
|
THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING |
639
|
|
|
|
|
|
|
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A |
640
|
|
|
|
|
|
|
FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF |
641
|
|
|
|
|
|
|
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF |
642
|
|
|
|
|
|
|
SUCH DAMAGES. |
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
=cut |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
1; |
647
|
|
|
|
|
|
|
|