File Coverage

blib/lib/File/Save/Home.pm
Criterion Covered Total %
statement 94 104 90.3
branch 40 60 66.6
condition 3 5 60.0
subroutine 16 16 100.0
pod 7 7 100.0
total 160 192 83.3


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.)
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 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