1package GPS::Babel;
2
3use warnings;
4use strict;
5use Carp;
6use Geo::Gpx 0.15;
7use File::Which qw(which);
8use IO::Handle;
9use Scalar::Util qw(blessed);
10
11our $VERSION = '0.11';
12
13my $EXENAME = 'gpsbabel';
14
15sub new {
16  my $class = shift;
17  my $args  = shift || {};
18  my $self  = bless {}, $class;
19
20  if ( exists $args->{exename} ) {
21    my $exename = delete $args->{exename};
22    $exename = [$exename] unless ref $exename eq 'ARRAY';
23    $self->set_exename( @$exename );
24  }
25  else {
26    $self->set_exename( which( $EXENAME ) || () );
27  }
28
29  return $self;
30}
31
32sub get_exename {
33  my $self = shift;
34  return @{ $self->{exepath} };
35}
36
37sub set_exename {
38  my $self = shift;
39  $self->{exepath} = [@_];
40}
41
42sub check_exe {
43  my $self = shift;
44
45  my @exe = $self->get_exename;
46  croak "$EXENAME not found" unless @exe;
47  return @exe;
48}
49
50sub _with_babel {
51  my $self = shift;
52  my ( $mode, $opts, $cb ) = @_;
53
54  my @exe = $self->check_exe;
55  my $exe_desc = "'" . join( "' '", @exe ) . "'";
56
57  my @args = ( @exe, @{$opts} );
58
59  if ( $^O =~ /MSWin32/ ) {
60    # Windows: shell escape and collapse to a single string
61    @args = ( '"' . join( '" "', map { s/"/""/g } @args ) . '"' );
62  }
63
64  open( my $fh, $mode, @args )
65   or die "Can't execute $exe_desc ($!)\n";
66  $cb->( $fh );
67  $fh->close or die "$exe_desc failed ($?)\n";
68}
69
70sub _with_babel_reader {
71  my $self = shift;
72  my ( $opts, $cb ) = @_;
73
74  $self->_with_babel( '-|', $opts, $cb );
75}
76
77sub _with_babel_lines {
78  my $self = shift;
79  my ( $opts, $cb ) = @_;
80  my @buf   = ();
81  my $flush = sub {
82    my $line = join '', @buf;
83    $cb->( $line ) unless $line =~ /^\s*$/;
84    @buf = ();
85  };
86  $self->_with_babel_reader(
87    $opts,
88    sub {
89      my $fh = shift;
90      while ( defined( my $line = <$fh> ) ) {
91        chomp $line;
92        $flush->() unless $line =~ /^\s+/;
93        push @buf, $line;
94      }
95    }
96  );
97  $flush->();
98}
99
100sub _with_babel_writer {
101  my $self = shift;
102  my ( $opts, $cb ) = @_;
103
104  $self->_with_babel( '|-', $opts, $cb );
105}
106
107sub _tidy {
108  my $str = shift;
109  $str = '' unless defined $str;
110  $str =~ s/^\s+//;
111  $str =~ s/\s+$//;
112  $str =~ s/\s+/ /g;
113  return $str;
114}
115
116sub _find_info {
117  my $self = shift;
118
119  my $info = {
120    formats => {},
121    filters => {},
122    for_ext => {}
123  };
124
125  # Read the version
126  $self->_with_babel_reader(
127    ['-V'],
128    sub {
129      my $fh = shift;
130      local $/;
131      $info->{banner} = _tidy( <$fh> );
132    }
133  );
134
135  if ( $info->{banner} =~ /([\d.]+)/ ) {
136    $info->{version} = $1;
137  }
138  else {
139    $info->{version} = '0.0.0';
140  }
141
142  my $handle_extra = sub {
143    my @extra = @_;
144    return unless @extra;
145    my $doclink = shift @extra;
146    return (
147      doclink => $doclink,
148      @extra ? ( extra => \@extra ) : ()
149    );
150  };
151
152  # -^3 and -%1 are 1.2.8 and later
153  if ( _cmp_ver( $info->{version}, '1.2.8' ) >= 0 ) {
154
155    # File formats
156    $self->_with_babel_lines(
157      ['-^3'],
158      sub {
159        my $ln = shift;
160        my ( $type, @f ) = split( /\t/, $ln );
161        if ( $type eq 'file' ) {
162          my ( $modes, $name, $ext, $desc, $parent, @extra, ) = @f;
163          ( my $nmodes = $modes ) =~ tr/rw-/110/;
164          $nmodes = oct( '0b' . $nmodes );
165          $info->{formats}->{$name} = {
166            modes  => $modes,
167            nmodes => $nmodes,
168            desc   => $desc,
169            parent => $parent,
170            $handle_extra->( @extra ),
171          };
172          if ( $ext ) {
173            $ext =~ s/^[.]//;    # At least one format has a stray '.'
174            $ext = lc( $ext );
175            $info->{formats}->{$name}->{ext} = $ext;
176            push @{ $info->{for_ext}->{$ext} }, $name;
177          }
178        }
179        elsif ( $type eq 'option' ) {
180          my ( $fname, $name, $desc, $type, $default, $min, $max,
181            @extra, )
182           = @f;
183          $info->{formats}->{$fname}->{options}->{$name} = {
184            desc    => $desc,
185            type    => $type,
186            default => $default || '',
187            min     => $min || '',
188            max     => $max || '',
189            $handle_extra->( @extra ),
190          };
191        }
192        else {
193
194          # Something we don't know about - so ignore it
195        }
196      }
197    );
198
199    # Filters
200    $self->_with_babel_lines(
201      ['-%1'],
202      sub {
203        my $ln = shift;
204        my ( $name, @f ) = split( /\t/, $ln );
205        if ( $name eq 'option' ) {
206          my ( $fname, $oname, $desc, $type, @extra ) = @f;
207          my @valid = splice @extra, 0, 3;
208          $info->{filters}->{$fname}->{options}->{$oname} = {
209            desc  => $desc,
210            type  => $type,
211            valid => \@valid,
212            $handle_extra->( @extra ),
213          };
214        }
215        else {
216          $info->{filters}->{$name} = { desc => $f[0] };
217        }
218      }
219    );
220  }
221
222  return $info;
223}
224
225sub get_info {
226  my $self = shift;
227
228  return $self->{info} ||= $self->_find_info;
229}
230
231sub banner {
232  my $self = shift;
233  return $self->get_info->{banner};
234}
235
236sub version {
237  my $self = shift;
238  return $self->get_info->{version};
239}
240
241sub _cmp_ver {
242  my ( $v1, $v2 ) = @_;
243  my @v1 = split( /[.]/, $v1 );
244  my @v2 = split( /[.]/, $v2 );
245
246  while ( @v1 && @v2 ) {
247    my $cmp = ( shift @v1 <=> shift @v2 );
248    return $cmp if $cmp;
249  }
250
251  return @v1 <=> @v2;
252}
253
254sub got_ver {
255  my $self = shift;
256  my $need = shift;
257  my $got  = $self->version;
258  return _cmp_ver( $got, $need ) >= 0;
259}
260
261sub guess_format {
262  my $self = shift;
263  my $name = shift;
264  my $dfmt = shift;
265
266  croak( "Missing filename" )
267   unless defined( $name );
268
269  my $info = $self->get_info;
270
271  # Format specified
272  if ( defined( $dfmt ) ) {
273    croak( "Unknown format \"$dfmt\"" )
274     if %{ $info->{formats} }
275       && !exists( $info->{formats}->{$dfmt} );
276    return $dfmt;
277  }
278
279  croak( "Filename \"$name\" has no extension" )
280   unless $name =~ /[.]([^.]+)$/;
281
282  my $ext = lc( $1 );
283  my $fmt = $info->{for_ext}->{$ext};
284
285  croak( "No format handles extension .$ext" )
286   unless defined( $fmt );
287
288  my @fmt = sort @{$fmt};
289
290  return $fmt[0] if @fmt == 1;
291
292  my $last = pop @fmt;
293  my $list = join( ' and ', join( ', ', @fmt ), $last );
294
295  croak( "Multiple formats ($list) handle extension .$ext" );
296}
297
298sub _convert_opts {
299  my $self = shift;
300  my $inf  = shift;
301  my $outf = shift;
302  my $opts = shift || {};
303
304  croak "Must provide input and output filenames"
305   unless defined( $outf );
306
307  my $infmt  = $self->guess_format( $inf,  $opts->{in_format} );
308  my $outfmt = $self->guess_format( $outf, $opts->{out_format} );
309
310  my $info = $self->get_info;
311
312  my $inmd  = $info->{formats}->{$infmt}->{nmodes}  || 0b111111;
313  my $outmd = $info->{formats}->{$outfmt}->{nmodes} || 0b111111;
314
315 # Work out which modes can be read by the input format /and/ written by
316 # the output format.
317  my $canmd = ( $inmd >> 1 ) & $outmd;
318
319  my @proc = ();
320  push @proc, '-r' if ( $canmd & 0x01 );
321  push @proc, '-t' if ( $canmd & 0x04 );
322  push @proc, '-w' if ( $canmd & 0x10 );
323
324  croak
325   "Formats $infmt and $outfmt have no read/write capabilities in common"
326   unless @proc;
327
328  my @opts = (
329    '-p', '',   @proc,   '-i', $infmt, '-f',
330    $inf, '-o', $outfmt, '-F', $outf
331  );
332
333  return @opts;
334}
335
336sub convert {
337  my $self = shift;
338
339  my @opts = $self->_convert_opts( @_ );
340
341  $self->direct( @opts );
342}
343
344sub direct {
345  my $self = shift;
346
347  if ( system( $self->check_exe, @_ ) ) {
348    croak( "$EXENAME failed with error " . ( ( $? == -1 ) ? $! : $? ) );
349  }
350}
351
352sub read {
353  my $self = shift;
354  my $inf  = shift;
355  my $opts = shift || {};
356
357  require Geo::Gpx;
358
359  croak "Must provide an input filename"
360   unless defined( $inf );
361
362  $opts->{out_format} = 'gpx';
363
364  my @opts = $self->_convert_opts( $inf, '-', $opts );
365  my $gpx = undef;
366
367  $self->_with_babel_reader(
368    \@opts,
369    sub {
370      my $fh = shift;
371      $gpx = Geo::Gpx->new( input => $fh );
372    }
373  );
374
375  return $gpx;
376}
377
378sub write {
379  my $self = shift;
380  my $outf = shift;
381  my $gpx  = shift;
382  my $opts = shift || {};
383
384  croak "Must provide some data to output"
385   unless blessed( $gpx ) && $gpx->can( 'xml' );
386
387  $opts->{in_format} = 'gpx';
388
389  my $xml = $gpx->xml;
390
391  my @opts = $self->_convert_opts( '-', $outf, $opts );
392  $self->_with_babel_writer(
393    \@opts,
394    sub {
395      my $fh = shift;
396      $fh->print( $xml );
397    }
398  );
399}
400
4011;
402__END__
403
404=head1 NAME
405
406GPS::Babel - Perl interface to gpsbabel
407
408=head1 VERSION
409
410This document describes GPS::Babel version 0.11
411
412=head1 SYNOPSIS
413
414    use GPS::Babel;
415
416    my $babel = GPS::Babel->new();
417
418    # Read an OZIExplorer file into a data structure
419    my $data  = $babel->read('route.ozi', 'ozi');
420
421    # Convert a file automatically choosing input and output
422    # format based on extension
423    $babel->convert('points.wpt', 'points.gpx');
424
425    # Call gpsbabel directly
426    $babel->direct(qw(gpsbabel -i saroute,split
427        -f in.anr -f in2.anr -o an1,type=road -F out.an1));
428
429=head1 DESCRIPTION
430
431From L<http://gpsbabel.org/>:
432
433    GPSBabel converts waypoints, tracks, and routes from one format to
434    another, whether that format is a common mapping format like
435    Delorme, Streets and Trips, or even a serial upload or download to a
436    GPS unit such as those from Garmin and Magellan. By flattening the
437    Tower of Babel that the authors of various programs for manipulating
438    GPS data have imposed upon us, it returns to us the ability to
439    freely move our own waypoint data between the programs and hardware
440    we choose to use.
441
442As I write this C<gpsbabel> supports 96 various GPS related data
443formats. In addition to file conversion it supports upload and
444download to a number of serial and USB devices. This module provides a
445(thin) wrapper around the gpsbabel binary making it easier to use in a
446perlish way.
447
448GPSBabel supports many options including arbitrary chains of filters,
449merging data from multiple files and many format specific parameters.
450This module doesn't attempt to provide an API wrapper around all these
451options. It does however provide for simple access to the most common
452operations. For more complex cases a passthrough method (C<direct>)
453passes its arguments directly to gpsbabel with minimal preprocessing.
454
455GPSBabel is able to describe its built in filters and formats and
456enumerate the options they accept. This information is available as a
457perl data structure which may be used to construct a dynamic user
458interface that reflects the options available from the gpsbabel binary.
459
460=head2 Format Guessing
461
462C<GPS::Babel> queries the capabilities of C<gpsbabel> and can use this
463information to automatically choose input and output formats based on
464the extensions of filenames. This makes it possible to, for example,
465create tools that bulk convert a batch of files choosing the correct
466format for each one.
467
468While this can be convenient there is an important caveat: if more than
469one format is associated with a particular extension GPS::Babel will
470fail rather than risking making the wrong guess. Because new formats are
471being added to gpsbabel all the time it's possible that a format that
472can be guessed today will become ambiguous tomorrow. That raises the
473spectre of a program that works now breaking in the future.
474
475Also some formats support a particular extension without explicitly
476saying so - for example the compegps format supports .wpt files but
477gpsbabel (currently) reports that the only format explicitly associated
478with the .wpt extension is xmap. This means that C<GPS::Babel> will
479confidently guess that the format for a file called something.wpt is
480xmap even if the file contains compegps data.
481
482In general then you should only use format guessing in applications
483where the user will have the opportunity to select a format explicitly
484if an unambiguous guess can't be made. For applications that must run
485unattended or where the user doesn't have this kind of control you
486should make the choice of filter explicit by passing C<in_format> and/or
487C<out_format> options to C<read>, C<write> and C<convert> as
488appropriate.
489
490=head1 INTERFACE
491
492=over
493
494=item C<new( { options } )>
495
496Create a new C<GPS::Babel> object. Optionally the exename option may
497be used to specify the full name of the gpsbabel executable
498
499    my $babel = GPS::Babel->new({
500        exename => 'C:\GPSBabel\gpsbabel.exe'
501    });
502
503=item C<check_exe()>
504
505Verify that the name of the gpsbabel executable is known throwing an
506error if it isn't. This is generally called by other methods but you may
507call it yourself to cause an error to be thrown early in your program if
508gpsbabel is not available.
509
510=item C<get_info()>
511
512Returns a reference to a hash that describes the capabilities of your
513gpsbabel binary. The format of this hash is probably best explored by
514running the following script and perusing its output:
515
516    #!/usr/bin/perl -w
517
518    use strict;
519    use GPS::Babel;
520    use Data::Dumper;
521
522    $| = 1;
523
524    my $babel = GPS::Babel->new();
525    print Dumper($babel->get_info());
526
527This script is provided in the distribution as C<scripts/babel_info.pl>.
528
529In general the returned hash has the following structure:
530
531    $info = {
532        version     => $gpsbabel_version,
533        banner      => $gpsbabel_banner,
534        filters     => {
535            # big hash of filters
536        },
537        formats     => {
538            # big hash of formats
539        },
540        for_ext     => {
541            # hash mapping lower case extension name to a list
542            # of formats that use that extension
543        }
544    };
545
546The C<filters>, C<formats> and C<for_ext> hashes are only present if you have
547gpsbabel 1.2.8 or later installed.
548
549=item C<banner()>
550
551Get the GPSBabel banner string - the same string that is output by the command
552
553    $ gpsbabel -V
554
555=item C<version()>
556
557Get the GPSBabel version number. The version is extracted from the banner string.
558
559    print $babel->version(), "\n";
560
561=item C<got_ver( $ver )>
562
563Return true if the available version of gpsbabel is equal to or greater
564than the supplied version string. For example:
565
566    die "I need gpsbabel 1.3.0 or later\n"
567        unless $babel->got_ver('1.3.0');
568
569=item C<guess_format( $filename )>
570
571Given a filename return the name of the gpsbabel format that handles
572files of that type. Croaks with a suitable message if the format can't
573be identified from the extension. If more than one format matches an
574error listing all of the matching formats will be thrown.
575
576Optionally a format name may be supplied as the second argument in which
577case an error will be thrown if the installed gpsbabel doesn't support
578that format.
579
580Format guessing only works with gpsbabel 1.2.8 or later. As mentioned
581above, the requirement that an extension maps unambiguously to a format
582means that installing a later version of gpsbabel which adds support for
583another format that uses the same extension can cause code that used to
584work to stop working. For this reason format guessing should only be
585used in interactive programs that give the user the opportunity to
586specify a format explicitly if such an ambiguity exists.
587
588=item C<get_exename()>
589
590Get the name of the gpsbabel executable that will be used. This defaults
591to whatever File::Which::which('gpsbabel') returns. To use a particular
592gpsbabel binary either pass the path to the constructor using the
593'exename' option or call C<set_exename( $path )>.
594
595=item C<set_exename( $path )>
596
597Set the path and name of the gpsbabel executable to use. The executable
598doesn't have to be called 'gpsbabel' - although naming any other program
599is unlikely to have pleasing results...
600
601    $babel->set_exename('/sw/bin/gpsbabel');
602
603=item C<read( $filename [, { $options } ] )>
604
605Read a file in a format supported by gpsbabel into a C<Geo::Gpx> object.
606The input format is guessed from the filename unless supplied explicitly
607in the options like this
608
609    $data = $babel->read('hotels.wpt', { in_format => 'xmap' });
610
611See C<Geo::Gpx> for documentation on the returned object.
612
613=item C<write( $filename, $gpx_data [, { $options }] )>
614
615Write GPX data (typically in the form of an instance of C<Geo::Gpx>) to
616a file in one of the formats gpsbabel supports. C<$gpx_data> must be a
617reference to an object that exposes a method called C<xml> that returns
618a GPX document. C<Geo::Gpx> satisfies this requirement.
619
620The format will be guessed from the filename (see caveats above) or may
621be explicitly specified by passing a hash containing C<out_format> as
622the third argument:
623
624    $babsel->write('points.kml', $my_points, { out_format => 'kml' });
625
626For consistency the data is filtered through gpsbabel even if the desired
627output format is 'gpx'. If you will only be dealing with GPX files use
628C<Geo::Gpx> directly.
629
630=item C<convert( $infile, $outfile, [, { $options } ] )>
631
632Convert a file from one format to another. Both formats must be
633supported by gpsbabel.
634
635With no options C<convert> attempts to guess the input and output formats
636using C<guess_format> - see the caveats about that above. To specify the
637formats explicitly supply as a third argument a hash containing the keys
638C<in_format> and C<out_format> like this:
639
640    $babel->convert('infile.wpt', 'outfile.kml',
641        { in_format => 'compegps', out_format => 'kml' });
642
643gpsbabel treats waypoints, tracks and routes as separate channels of
644information and not all formats support reading and writing all three.
645C<convert> attempts to convert anything that can be both read by the
646input format and written by the output format. If the formats have
647nothing in common an error will be thrown.
648
649=item C<direct( @options )>
650
651Invoke gpsbabel with the supplied options. The supplied options are passed
652unmodified to system(), for example:
653
654    $babel->direct(qw(-i gpx -f somefile.gpx -o kml -F somefile.kml));
655
656Throws appropriate errors if gpsbabel fails.
657
658=back
659
660=head1 DIAGNOSTICS
661
662=for author to fill in:
663    List every single error and warning message that the module can
664    generate (even the ones that will "never happen"), with a full
665    explanation of each problem, one or more likely causes, and any
666    suggested remedies.
667
668=over
669
670=item C<< %s not found >>
671
672Can't find the gpsbabel executable.
673
674=item C<< Missing filename >>
675
676C<guess_format> (or a method that calls it) needs a filename from
677which to guess the format.
678
679=item C<< Unknown format "%s" >>
680
681An explicit format was passed to C<guess_format> that doesn't appear
682to be supported by the installed gpsbabel.
683
684=item C<< Filename "%s" has no extension >>
685
686Can't guess the format of a filename with no extension.
687
688=item C<< No format handles extension .%s >>
689
690The installed gpsbabel doesn't contain a format that explicitly supports
691the named extension. That doesn't necessarily mean that gpsbabel can't
692handle the file: many file formats use a number of different extensions
693and many gpsbabel input/output modules don't specify the extensions they
694support. If in doubt check the gpsbabel documentation and supply the
695format explicitly.
696
697=item C<< Multiple formats (%s) handle extension .%s >>
698
699C<guess_format> couldn't unambiguously guess the appropriate format
700from the extension. Check the gpsbabel documentation and supply an
701explicit format.
702
703=item C<< Must provide input and output filenames >>
704
705C<convert> needs input and output filenames.
706
707=item C<< Formats %s and %s have no read/write capabilities in common >>
708
709Some gpsbabel formats are read only, some are write only, some support only
710waypoints or only tracks. C<convert> couldn't find enough common ground
711between input and output formats to be able to convert any data.
712
713=item C<< %s failed with error %s >>
714
715A call to gpsbabel failed.
716
717=item C<< Must provide an input filename >>
718
719C<read> needs to know the name of the file to read.
720
721=item C<< Must provide some data to output >>
722
723C<write> needs data to output. The supplied object must expose a
724method called C<xml> that returns GPX data. Typically this is achieved
725by passing a C<Geo::Gpx>.
726
727=back
728
729=head1 CONFIGURATION AND ENVIRONMENT
730
731GPS::Babel requires no configuration files or environment variables.
732With the exception of C<direct()> all calls pass the argument -p '' to
733gpsbabel to inhibit reading of any inifile. See L<http://www.gpsbabel.org/htmldoc-
7341.3.2/inifile.html> for more details.
735
736=head1 DEPENDENCIES
737
738GPS::Babel needs gpsbabel, ideally installed on your PATH and ideally
739version 1.2.8 or later.
740
741In addition GPS::Babel requires the following Perl modules:
742
743    Geo::Gpx (for read, write)
744    File::Which
745
746=head1 INCOMPATIBILITIES
747
748GPS::Babel has only been tested with versions 1.3.0 and later of
749gpsbabel. It should work with earlier versions but it's advisable to
750upgrade to the latest version if possible. The gpsbabel developer
751community is extremely active so it's worth having the latest version
752installed.
753
754=head1 BUGS AND LIMITATIONS
755
756No bugs have been reported.
757
758Please report any bugs or feature requests to
759C<bug-gps-babel@rt.cpan.org>, or through the web interface at
760L<http://rt.cpan.org>.
761
762=head1 AUTHOR
763
764Andy Armstrong  C<< <andy@hexten.net> >>
765
766Robert Lipe and numerous contributors did all the work by providing
767gpsbabel in the first place. This is just a wafer-thin layer on top of
768all their goodness.
769
770=head1 LICENCE AND COPYRIGHT
771
772Copyright (c) 2006, Andy Armstrong C<< <andy@hexten.net> >>. All rights reserved.
773
774This module is free software; you can redistribute it and/or
775modify it under the same terms as Perl itself. See L<perlartistic>.
776
777=head1 DISCLAIMER OF WARRANTY
778
779BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
780FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
781OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
782PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
783EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
784WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
785ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
786YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
787NECESSARY SERVICING, REPAIR, OR CORRECTION.
788
789IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
790WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
791REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
792LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
793OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
794THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
795RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
796FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
797SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
798SUCH DAMAGES.
799