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