File Coverage

blib/lib/Debug/Fork/Tmux.pm
Criterion Covered Total %
statement 41 57 71.9
branch 11 18 61.1
condition 2 6 33.3
subroutine 10 14 71.4
pod 0 1 0.0
total 64 96 66.6


line stmt bran cond sub pod time code
1             #
2             # This file is part of Debug-Fork-Tmux
3             #
4             # This software is Copyright (c) 2013 by Peter Vereshagin.
5             #
6             # This is free software, licensed under:
7             #
8             # The (three-clause) BSD License
9             #
10             # ABSTRACT: Makes fork() in debugger to open a new Tmux window
11             package Debug::Fork::Tmux;
12              
13             # Helps you to behave
14 2     2   125639 use strict;
  2         4  
  2         67  
15 2     2   10 use warnings;
  2         5  
  2         72  
16              
17             our $VERSION = '1.000012'; # VERSION
18             #
19             ### MODULES ###
20             #
21             # Glues up path components
22 2     2   8 use File::Spec;
  2         3  
  2         45  
23              
24             # Resolves up symlinks
25 2     2   10 use Cwd;
  2         3  
  2         233  
26              
27             # Dioes in a nicer way
28 2     2   20 use Carp;
  2         3  
  2         140  
29              
30             # Reads configuration
31 2     2   1414 use Debug::Fork::Tmux::Config;
  2         5  
  2         69  
32              
33             # Makes constants possible
34 2     2   11 use Const::Fast;
  2         3  
  2         13  
35              
36             ### CONSTANTS ###
37             #
38              
39             ### SUBS ###
40             #
41             # Function
42             # Gets the tty name, sets the $DB::fork_TTY to it and returns it.
43             # Takes : n/a
44             # Requires : DB, Debug::Fork::Tmux
45             # Overrides : DB::get_fork_TTY()
46             # Changes : $DB::fork_TTY
47             # Returns : Str tty name $DB::fork_TTY
48             sub DB::get_fork_TTY {
49              
50             # Create a TTY
51 0     0 0 0     my $tty_name = Debug::Fork::Tmux::_spawn_tty();
52              
53             # Output the name both to a variable and to the caller
54 2     2   227     no warnings qw/once/;
  2         5  
  2         1250  
55 0         0     $DB::fork_TTY = $tty_name;
56 0         0     return $tty_name;
57             }
58              
59             # Function
60             # Spawns a TTY and returns its name
61             # Takes : n/a
62             # Returns : Str tty name
63             sub _spawn_tty {
64              
65             # Create window and get its tty name
66 0     0   0     my $window_id = _tmux_new_window();
67 0         0     my $tty_name = _tmux_window_tty($window_id);
68              
69 0         0     return $tty_name;
70             }
71              
72             # Function
73             # Creates new 'tmux' window and returns its id/number
74             # Takes : n/a
75             # Depends : On 'tmux_fqfn', 'tmux_neww', 'tmux_neww_exec' configuration
76             # parameters
77             # Returns : Str id/number of the created 'tmux' window
78             sub _tmux_new_window {
79 0     0   0     my @cmd_to_read = (
80                     Debug::Fork::Tmux::Config->get_config('tmux_fqfn'),
81                     split(
82                         /\s+/, Debug::Fork::Tmux::Config->get_config('tmux_cmd_neww')
83                     ),
84                     Debug::Fork::Tmux::Config->get_config('tmux_cmd_neww_exec'),
85                 );
86              
87 0         0     my $window_id = _read_from_cmd(@cmd_to_read);
88              
89 0         0     return $window_id;
90             }
91              
92             # Function
93             # Gets a 'tty' name from 'tmux's window id/number
94             # Takes : Str 'tmux' window id/number
95             # Depends : On 'tmux_fqfn', 'tmux_cmd_tty' configuration parameters
96             # Returns : Str 'tty' device name of the 'tmux' window
97             sub _tmux_window_tty {
98 0     0   0     my $window_id = shift;
99              
100             # Concatenate the 'tmux' command and read its output
101 0         0     my @cmd_to_read = (
102                     Debug::Fork::Tmux::Config->get_config('tmux_fqfn'),
103                     split( /\s+/, Debug::Fork::Tmux::Config->get_config('tmux_cmd_tty') ),
104                     $window_id,
105                 );
106 0         0     my @tmux_cmd = (@cmd_to_read);
107 0         0     my $tty_name = _read_from_cmd(@tmux_cmd);
108              
109 0         0     return $tty_name;
110             }
111              
112             # Function
113             # Reads the output of a command supplied with parameters as the argument(s)
114             # and returns its output.
115             # Takes : Array[Str] command and its parameters
116             # Throws : If command failed or the output is not the non-empty Str
117             # single line
118             # Returns : Output of the command supplied with parameters as arguments
119             sub _read_from_cmd {
120 8     8   18229     my @cmd_and_args = @_;
121              
122             # Open the pipe to read
123 8 50       33038     _croak_on_cmd( @cmd_and_args, "failed opening command: $!" )
124                     unless open my $cmd_output_fh => '-|',
125                     @cmd_and_args;
126              
127             # Read a line from the command
128 8 100 33     35520     _croak_on_cmd( @cmd_and_args, "didn't write a line" )
      33        
129                     unless defined($cmd_output_fh)
130                     and ( 0 != $cmd_output_fh )
131                     and my $cmd_out = <$cmd_output_fh>;
132              
133             # If still a byte is readable then die as the file handle should be
134             # closed already
135 5         961     my $read_rv = read $cmd_output_fh => my $buf, 1;
136 5 50       51     _croak_on_cmd( @cmd_and_args, "failed reading command: $!/$buf" )
137                     unless defined $read_rv;
138 5 100       134     _croak_on_cmd( @cmd_and_args, "did not finish: $buf" )
139                     unless 0 == $read_rv;
140              
141             # Die on empty output
142 3         15     chomp $cmd_out;
143 3 100       47     _croak_on_cmd( @cmd_and_args, "provided empty string" )
144                     unless length $cmd_out;
145              
146 2         350     return $cmd_out;
147             }
148              
149             # Function
150             # Croaks nicely on the command with an explanation based on arguments and $?
151             # Takes : Array[Str] system command, its arguments, and an explanation
152             # on the situation when the command failed
153             # Requires : Carp
154             # Depends : On $? global variable set by system command failure
155             # Throws : Always
156             # Returns : n/a
157             sub _croak_on_cmd {
158 8     8   9965     my @cmd_args_msg = @_;
159              
160 8 50       96     if ( defined $? ) {
161 8         35         my $msg = '';
162              
163             # Depending on $?, add it to the death note
164             # Command may be a not-executable
165 8 50       77         if ( $? == -1 ) {
    50          
166 0         0             $msg = "failed to execute: $!";
167                     }
168              
169             # Command can be killed
170                     elsif ( $? & 127 ) {
171 0 0       0             $msg = sprintf "child died with signal %d, %s coredump",
172                             ( $? & 127 ), ( $? & 128 ) ? 'with' : 'without';
173                     }
174              
175             # Command may return the exit code for clearance
176                     else {
177 8         78             $msg = sprintf "child exited with value %d", $? >> 8;
178                     }
179              
180             # And the message can be returned as an appendix to the original
181             # arguments
182 8         27         push @cmd_args_msg, $msg;
183                 }
184              
185             # Report the datails via the Carp
186 8         59     my $croak_msg = "The command " . join ' ' => @cmd_args_msg;
187 8         603     croak($croak_msg);
188             }
189              
190             # Returns true to require()
191             1;
192              
193             __END__
194            
195             =pod
196            
197             =head1 NAME
198            
199             Debug::Fork::Tmux - Makes fork() in debugger to open a new Tmux window
200            
201             =head1 VERSION
202            
203             This documentation refers to the module contained in the distribution C<Debug-Fork-Tmux> version 1.000012.
204            
205             =head1 SYNOPSIS
206            
207             #!/usr/bin/perl -d
208             #
209             # ABSTRACT: Debug the fork()-contained code in this file
210             #
211             ## Works only under Tmux: http://tmux.sf.net
212             #
213             # Make fork()s debuggable with Tmux
214             use Debug::Fork::Tmux;
215            
216             # See what happens in your debugger then...
217             fork;
218            
219             =head1 DESCRIPTION
220            
221             Make sure you have the running C<Tmux> window manager:
222            
223             $ tmux
224            
225             =over
226            
227             =item * Only C<Tmux> version 1.6 and higher works with C<Debug::Fork::Tmux>.
228             See L</DEPENDENCIES>.
229            
230             =item * It is not necessary to run this under C<Tmux>, see L</Attaching to
231             the other C<Tmux> session>.
232            
233             =back
234            
235             Then the real usage example of this module is:
236            
237             $ perl -MDebug::Fork::Tmux -d your_script.pl
238            
239             As Perl's standard debugger requires additional code to be written and used
240             when the debugged Perl program use the L<fork()|perlfunc/fork> built-in.
241            
242             This module is about to solve the trouble which is used to be observed like
243             this:
244            
245             ######### Forked, but do not know how to create a new TTY. #########
246             Since two debuggers fight for the same TTY, input is severely entangled.
247            
248             I know how to switch the output to a different window in xterms, OS/2
249             consoles, and Mac OS X Terminal.app only. For a manual switch, put the
250             name of the created TTY in $DB::fork_TTY, or define a function
251             DB::get_fork_TTY() returning this.
252            
253             On UNIX-like systems one can get the name of a TTY for the given window
254             by typing tty, and disconnect the shell from TTY by sleep 1000000.
255            
256             All of that is about getting the pseudo-terminal device for another part of
257             user interface. This is probably why only the C<GUI>s are mentioned here:
258             C<OS/2> 'Command Prompt', C<Mac OS X>'s C<Terminal.app> and an C<xterm>. For
259             those of you who develop server-side stuff it should be known that keeping
260             C<GUI> on the server is far from always to be available as an option no
261             matter if it's a production or a development environment.
262            
263             The most ridiculous for every C<TUI> (the C<ssh> particularly) user is that
264             the pseudo-terminal device isn't that much about C<GUI>s by its nature so
265             the problem behind the bars of the L<perl5db.pl> report (see more detailed
266             problem description at the L<PerlMonks
267             thread|http://perlmonks.org/?node_id=128283>) is the consoles management.
268             It's a kind of a tricky, for example, to start the next C<ssh> session
269             initiated from the machine serving as an C<sshd> server for the existing
270             session.
271            
272             Thus we kind of have to give a chance to the consoles management with a
273             software capable to run on a server machine without as much dependencies as
274             an C<xterm>. This module is a try to pick the L<Tmux|http://tmux.sf.net>
275             windows manager for such a task.
276            
277             Because of highly-developed scripting capabilities of C<Tmux> any user can
278             supply the 'window' or a 'pane' to Perl's debugger making it suitable to
279             debug the separate process in a different C<UI> instance. Also this adds the
280             features like C<groupware>: imagine that your mate can debug the process
281             you've just C<fork()ed> by mean of attaching the same C<tmux> you are
282             running on a server. While you keep working on a process that called a
283             C<fork()>.
284            
285             =head1 SUBROUTINES/METHODS
286            
287             All of the following are functions:
288            
289             =head2 PUBLIC
290            
291             =head3 C<DB::get_fork_TTY()>
292            
293             Finds new C<TTY> for the C<fork()>ed process.
294            
295             Takes no arguments. Returns C<Str> name of the C<tty> device of the <tmux>'s
296             new window created for the debugger's new process.
297            
298             Sets the C<$DB::fork_TTY> to the same C<Str> value.
299            
300             =head2 PRIVATE
301            
302             =head3 C<_spawn_tty()>
303            
304             Creates a C<TTY> device and returns C<Str> its name.
305            
306             =head3 C<_tmux_new_window()>
307            
308             Creates a given C<tmux> window and returns C<Str> its id/number.
309            
310             =head3 C<_tmux_window_tty( $window_id )>
311            
312             Finds a given C<tmux> window's tty name and returns its C<Str> name based on
313             a given window id/number typically from L</_tmux_new_window()>.
314            
315             =head3 C<_read_from_cmd( $cmd =E<gt> @args )>
316            
317             Takes the list containing the C<Str> L<system()|perlfunc/system> command and
318             C<Array> its arguments and executes it. Reads C<Str> the output and returns it.
319             Throws if no output or if the command failed.
320            
321             =head3 C<_croak_on_cmd( $cmd =E<gt> @args, $happen )>
322            
323             Takes the C<Str> command, C<Array> its arguments and C<Str> the reason of
324             its failure, examines the C<$?> and dies with explanation on the
325             L<system()|perlfunc/system> command failure.
326            
327             =head1 CONFIGURATION AND ENVIRONMENT
328            
329             The module requires the L<Tmux|http://tmux.sf.net> window manager for the
330             console to be present in the system.
331            
332             This means that it requires the C<Unix>-like operating system not only to
333             have a L<fork> implemented and a C<TTY> device name supplement but the
334             system should have Tmux up and running.
335            
336             Therefore C<Cygwin> for example isn't in at this moment, see the
337             L<explanation|http://permalink.gmane.org/gmane.comp.terminal-emulators.tmux.user/1354>
338             why.
339            
340             Configuration is made via environment variables, the default is taken for
341             each of them with no such variable is set in the environment:
342            
343             =head2 C<DFTMUX_FQFN>
344            
345             The C<tmux> binary name with the full path.
346            
347             Default : The first of those for executable to exist:
348            
349             =over
350            
351             =item C<PATH> environment variable contents
352            
353             =item Path to the Perl binary interpreter
354            
355             =item Current directory
356            
357             =back
358            
359             and just the C<tmux> as a fallback if none of above is the location of the
360             C<tmux> executable file.
361            
362             =head2 C<DFTMUX_CMD_NEWW>
363            
364             The L<system()|perlfunc/system> arguments for a C<tmux>
365             command for opening a new window and with output of a window address from
366             C<tmux>. String is sliced by spaces to be a list of parameters.
367            
368             Default : C<neww -P>
369            
370             =head2 C<DFTMUX_CMD_NEWW_EXEC>
371            
372             The L<system()|perlfunc/system> or a shell command to be given to the
373             C<DFTMUX_CMD_NEWW> command to be executed in a brand new created
374             window. It should wait unexpectedly and do nothing till the debugger
375             catches the device and puts in into the proper use.
376            
377             Default : C<sleep 1000000>
378            
379             =head2 C<DFTMUX_CMD_TTY>
380            
381             Command- line parameter(s) for a C<tmux> command to find a C<tty> name in
382             the output. The string is sliced then by spaces. The C<tmux>'s window
383             address is added then as the very last argument.
384            
385             Default : C<lsp -F #{pane_tty} -t>
386            
387             =head2 Earlier versions' C<SPUNGE_*> environment variables
388            
389             Till v1.000009 the module was controlled by the environment variables like
390             C<SPUNGE_TMUX_FQDN>. Those are deprecated and should be replaced in your
391             configuration(s) onto the C<DFTMUX_>-prefixed ones.
392            
393             =head2 Attaching to the other C<Tmux> session
394            
395             For the case you can not or don't want to use the current C<tmux> session
396             you are running in, you may want to have the separate C<tmux> server up and
397             running and use its windows or panes to be created. This can be done by mean
398             of prepending the correct C<-L> or C<-S> switch to the start of the every of
399             the command-line parameters string to be used, for example:
400            
401             $ DFTMUX_CMD_NEWW="-L default neww -P" \
402             > DFTMUX_CMD_TTY="-L default lsp -F #{pane_tty} -t" \
403             > perl -MDebug::Fork::Tmux -d your_script.pl
404            
405             =head1 DIAGNOSTICS
406            
407             =over
408            
409             =item * C<The command ...>
410            
411             Typically the error message starts with the command the L<Debug::Fork::Tmux> tried
412             to execute, including the command's arguments.
413            
414             =item * C<failed opening command: ...>
415            
416             The command was not taken by the system as an executable binary file.
417            
418             =item * C<... didn't write a line>
419            
420             =item * C<failed reading command: ...>
421            
422             Command did not output exactly one line of the text.
423            
424             =item * C<... did not finish>
425            
426             Command outputs more than one line of the text.
427            
428             =item * C<provided empty string>
429            
430             Command outputs exactly one line of the text and the line is empty.
431            
432             =item * C<failed to execute: ...>
433            
434             There was failure executing the command
435            
436             =item * C<child died with(out) signal X, Y coredump>
437            
438             Command was killed by the signal X and the coredump is (not) located in Y.
439            
440             =item * C<child exited with value X>
441            
442             Command was not failed but there are reasons to throw an error like the
443             wrong command's output.
444            
445             =back
446            
447             =head1 DEPENDENCIES
448            
449             * C<Perl 5.8.9+>
450             is available from L<The Perl website|http://www.perl.org>
451            
452             * L<Config>, L<Cwd>, L<DB>, L<ExtUtils::MakeMaker>, L<File::Find>,
453             L<File::Spec>, L<File::Basename>, L<Scalar::Util>, L<Test::More> are
454             available in core C<Perl> distribution version 5.8.9 and later
455            
456             * L<Const::Fast>
457             is available from C<CPAN>
458            
459             * L<Module::Build>
460             is available in core C<Perl> distribution since version 5.9.4
461            
462             * L<Sort::Versions>
463             is available from C<CPAN>
464            
465             * L<Test::Exception>
466             is available from C<CPAN>
467            
468             * L<Test::Most>
469             is available from C<CPAN>
470            
471             * L<Test::Strict>
472             is available from C<CPAN>
473            
474             * L<Env::Path>
475             is available from C<CPAN>
476            
477             * L<autodie>
478             is available in core C<Perl> distribution since version 5.10.1
479            
480             * C<Tmux> v1.6+
481             is available from L<The Tmux website|http://tmux.sourceforge.net>
482            
483             Most of them can easily be found in your operating system
484             distribution/repository.
485            
486             =head1 BUGS AND LIMITATIONS
487            
488             You can make new bug reports, and view existing ones, through the
489             web interface at L<http://bugs.vereshagin.org/product/Debug-Fork-Tmux>.
490            
491             =head1 WEB SITE
492            
493             The web site of
494             L<Debug::Fork::Tmux|http://gitweb.vereshagin.org/Debug-Fork-Tmux/README.html> currently
495             consists of only one page cause it's a very small module.
496            
497             You may want to visit a L<GitHub
498             page|https://github.com/petr999/Debug-Fork-Tmux>, too.
499            
500             =for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan
501            
502             =head1 SUPPORT
503            
504             =head2 Perldoc
505            
506             You can find documentation for this module with the perldoc command.
507            
508             perldoc Debug::Fork::Tmux
509            
510             =head2 Websites
511            
512             The following websites have more information about this module, and may be of help to you. As always,
513             in addition to those websites please use your favorite search engine to discover more resources.
514            
515             =over 4
516            
517             =item *
518            
519             MetaCPAN
520            
521             A modern, open-source CPAN search engine, useful to view POD in HTML format.
522            
523             L<http://metacpan.org/release/Debug-Fork-Tmux>
524            
525             =item *
526            
527             Search CPAN
528            
529             The default CPAN search engine, useful to view POD in HTML format.
530            
531             L<http://search.cpan.org/dist/Debug-Fork-Tmux>
532            
533             =item *
534            
535             RT: CPAN's Bug Tracker
536            
537             The RT ( Request Tracker ) website is the default bug/issue tracking system for CPAN.
538            
539             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Debug-Fork-Tmux>
540            
541             =item *
542            
543             AnnoCPAN
544            
545             The AnnoCPAN is a website that allows community annotations of Perl module documentation.
546            
547             L<http://annocpan.org/dist/Debug-Fork-Tmux>
548            
549             =item *
550            
551             CPAN Ratings
552            
553             The CPAN Ratings is a website that allows community ratings and reviews of Perl modules.
554            
555             L<http://cpanratings.perl.org/d/Debug-Fork-Tmux>
556            
557             =item *
558            
559             CPAN Forum
560            
561             The CPAN Forum is a web forum for discussing Perl modules.
562            
563             L<http://cpanforum.com/dist/Debug-Fork-Tmux>
564            
565             =item *
566            
567             CPANTS
568            
569             The CPANTS is a website that analyzes the Kwalitee ( code metrics ) of a distribution.
570            
571             L<http://cpants.perl.org/dist/overview/Debug-Fork-Tmux>
572            
573             =item *
574            
575             CPAN Testers
576            
577             The CPAN Testers is a network of smokers who run automated tests on uploaded CPAN distributions.
578            
579             L<http://www.cpantesters.org/distro/D/Debug-Fork-Tmux>
580            
581             =item *
582            
583             CPAN Testers Matrix
584            
585             The CPAN Testers Matrix is a website that provides a visual overview of the test results for a distribution on various Perls/platforms.
586            
587             L<http://matrix.cpantesters.org/?dist=Debug-Fork-Tmux>
588            
589             =item *
590            
591             CPAN Testers Dependencies
592            
593             The CPAN Testers Dependencies is a website that shows a chart of the test results of all dependencies for a distribution.
594            
595             L<http://deps.cpantesters.org/?module=Debug::Fork::Tmux>
596            
597             =back
598            
599             =head2 Email
600            
601             You can email the author of this module at C<peter@vereshagin.org> asking for help with any problems you have.
602            
603             =head2 Bugs / Feature Requests
604            
605             Please report any bugs or feature requests by email to C<peter@vereshagin.org>, or through
606             the web interface at L<http://bugs.vereshagin.org/product/Debug-Fork-Tmux>. You will be automatically notified of any
607             progress on the request by the system.
608            
609             =head2 Source Code
610            
611             The code is open to the world, and available for you to hack on. Please feel free to browse it and play
612             with it, or whatever. If you want to contribute patches, please send me a diff or prod me to pull
613             from your repository :)
614            
615             L<http://gitweb.vereshagin.org/Debug-Fork-Tmux>
616            
617             git clone https://github.com/petr999/Debug-Fork-Tmux.git
618            
619             =head1 AUTHOR
620            
621             L<Peter Vereshagin|http://vereshagin.org> <peter@vereshagin.org>
622            
623             =head1 COPYRIGHT AND LICENSE
624            
625             This software is Copyright (c) 2013 by Peter Vereshagin.
626            
627             This is free software, licensed under:
628            
629             The (three-clause) BSD License
630            
631             =head1 SEE ALSO
632            
633             Please see those modules/websites for more information related to this module.
634            
635             =over 4
636            
637             =item *
638            
639             L<Debug::Fork::Tmux::Config|Debug::Fork::Tmux::Config>
640            
641             =item *
642            
643             L<http://perlmonks.org/?node_id=128283|http://perlmonks.org/?node_id=128283>
644            
645             =item *
646            
647             L<nntp://nntp.perl.org/perl.debugger|nntp://nntp.perl.org/perl.debugger>
648            
649             =item *
650            
651             L<http://debugger.perl.org/|http://debugger.perl.org/>
652            
653             =back
654            
655             =head1 DISCLAIMER OF WARRANTY
656            
657             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
658             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT
659             WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER
660             PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND,
661             EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
662             IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
663             PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
664             SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME
665             THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION.
666            
667             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
668             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
669             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE
670             TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR
671             CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
672             SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
673             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
674             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
675             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
676             DAMAGES.
677            
678             =cut
679