The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!perl -w

# Copyright 2007, 2008, 2009, 2010, 2011, 2013 Kevin Ryde

# This file is part of Chart.
#
# Chart is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation; either version 3, or (at your option) any later version.
#
# Chart is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with Chart.  If not, see <http://www.gnu.org/licenses/>.

use 5.010;
use strict;
use warnings;

# BEGIN {
#   use Carp; # 0.22 needs this pre-loaded for the prototypes
#   $ENV{'LIST_MOREUTILS_PP'} = 1;
# }
use File::Spec;
use Getopt::Long;
use Glib 1.200; # for strerror()
use I18N::Langinfo ();
use I18N::Langinfo::Wide;
use List::Util qw(min max);
use Locale::Messages;
use Locale::TextDomain 'App-Chart';

use App::Chart;

if (@ARGV >= 1 && ($ARGV[0] eq '--subprocess' || $ARGV[0] eq '--emacs')) {
  binmode (STDOUT, ':utf8') or die;
  binmode (STDERR, ':utf8') or die;

} else {
  # locale encoding conversion on the tty, wide-chars everywhere internally
  require Encode;           # Encode::PERLQQ
  require PerlIO::encoding; # set fallback
  # version 0.06 for bug fix of a struct size for perl 5.10 (there's some
  # fragile duplication)
  require PerlIO::locale; PerlIO::locale->VERSION(0.06);
  { no warnings 'once';
    local $PerlIO::encoding::fallback = Encode::PERLQQ; # \x{1234} style
    (binmode (STDOUT, ':locale') && binmode (STDERR, ':locale'))
      or die "Cannot set :encoding on stdout/stderr: $!\n";
  }
}

my $option_output;
my $option_mode;
my @args;

{
  # callback option arg in perl 5.8 is a string, but in 5.10 it's a
  # Getopt::Long::Callback object, must stringize to get the plain name
  my $set_mode = sub {
    my ($opt) = @_;
    if ($option_mode) {
      print STDERR __x("chart: already got mode option --{mode}\n",
                       mode => $option_mode);
      exit 1;
    }
    $option_mode = "$opt";
  };
  my $set_output = sub {
    my ($opt) = @_;
    if ($option_output) {
      print STDERR __x("chart: already got output option --{output}\n",
                       output => $option_output);
      exit 1;
    }
    $option_output = "$opt";
  };
  my $add_symlist = sub {
    my ($opt) = @_;
    my $key = "$opt";
    if ($key eq 'favorites') { $key = 'favourites'; }
    require App::Chart::Gtk2::Symlist;
    push @args, App::Chart::Gtk2::Symlist->new_from_key ($key);
  };
  my $help = sub {
    print __("chart [--options] [SYMBOL...]\n");
    my @opts =
      (['-h, --help',    __('Print this help')],
       ['-v, --version', __('Print Chart version')],
       ['--verbose',     __('Print extra messages')],
       ['--download',    __('Download data for selected symbols, don\'t run the GUI')],
       ['--ticker',      __('Run just the stock ticker, on selected symbols')],
       ['--all',         __('Select all symbols')],
       ['--alerts',      __('Select alerts list symbols')],
       ['--favourites',  __('Select favourites list symbols')],
       ['--display=DPY',
        __("X display to use (default DISPLAY environment variable)")],
       ['--<gtk-options>', __('Standard Gtk options')]);
    my $width = 2 + max (map { length ($_->[0]) } @opts);
    foreach (@opts) {
      printf "%-*s%s\n", $width, $_->[0], $_->[1];
    }
    print "\n";
    print __"When running the GUI, the SYMBOL argument is an initial chart to show\n(can be a partial match, as in the open dialog).\n";
    print __"When running download or ticker, the SYMBOL... arguments select specific\nsymbols to show or download (must be full symbols, no partial matches).\n";
    exit 0;
  };

  GetOptions (require_order => 1,
              'help|?' => $help,
              version => sub {
                print "chart version $App::Chart::Gtk2::VERSION\n";
                exit 0;
              },
              all        => $add_symlist,
              favourites => $add_symlist,
              favorites  => $add_symlist,
              historical => $add_symlist,
              alerts     => $add_symlist,
              '<>'       => sub { my ($value) = @_;
                                  # stringize to avoid Getopt::Long object
                                  push @args, "$value";
                                },
              download   => $set_mode,
              ticker     => $set_mode,
              watchlist  => $set_mode,
              subprocess => $set_mode,  # undocumented
              emacs      => $set_mode,  # undocumented
              vacuum     => $set_mode,  # undocumented yet

              verbose    => \$App::Chart::option{'verbose'},
              'all-status' => $set_output,  # undocumented

              'lwp-debug' => sub {
                require LWP::Debug;
                LWP::Debug::level('+');
              },
             )
    or exit 1;
}

# $filename is in filesystem charset bytes.
#
# The file should end with some non-undef value, since otherwise it's
# indistinguishable from undef for the various do/require errors.
#
sub initfile {
  my ($filename) = @_;
  if (! -e $filename) { return; }
  $! = 0;
  my $ret = do $filename;
  if (! defined $ret) {
    my $display_filename = Glib::filename_display_name($filename);
    if ($@) {
      print __x("chart: error in {filename}\n{error}\n  (starting anyway)\n",
                filename => $display_filename,
                error => I18N::Langinfo::Wide::to_wide($@));
    } elsif ($! != 0) {
      my $err = Glib::strerror($!);
      print __x("chart: cannot read {filename}: {error}\n  (starting anyway)\n",
                filename => $display_filename,
                error => $err);
    } else {
      print __x("chart: {filename} didn't end with a true value\n",
                filename => $display_filename);
    }
  }
}

$option_mode   ||= 'gui';
$option_output ||= 'tty';

unshift @INC, File::Spec->catdir (App::Chart::chart_directory(), 'lib');
initfile (File::Spec->catfile (App::Chart::chart_directory(), 'init.pl'));

if ($option_mode eq 'gui') {
  # default is the gui
  require App::Chart::Gtk2::Main;
  App::Chart::Gtk2::Main->main (\@args);

} elsif ($option_mode eq 'download') {
  require App::Chart::Download;
  App::Chart::Download->command_line_download ($option_output, \@args);

} elsif ($option_mode eq 'subprocess') {
  require App::Chart::SubprocessMain;
  App::Chart::SubprocessMain->main ();

} elsif ($option_mode eq 'vacuum') {
  require App::Chart::Vacuum;
  App::Chart::Vacuum->command_line_vacuum ($option_output, \@args);

} elsif ($option_mode eq 'emacs') {
  require App::Chart::EmacsMain;
  App::Chart::EmacsMain->main ();

} elsif ($option_mode eq 'watchlist') {
  require App::Chart::Gtk2::WatchlistDialog;
  App::Chart::Gtk2::WatchlistDialog->main (\@args);

} elsif ($option_mode eq 'ticker') {
  require App::Chart::Gtk2::TickerMain;
  App::Chart::Gtk2::TickerMain->main (\@args);
}

exit 0;

__END__

=for stopwords Gtk BHP.AX bh wildcards Wildcards filenames tty ie watchlist Watchlist --watchlist SQLite Ryde

=head1 NAME

chart -- Stock and commodity price charting and downloading

=head1 SYNOPSIS

chart [--options] [symbol...]

=head1 DESCRIPTION

Chart is a stock and commodity charting program written in Perl and using
the Gtk toolkit.

For a summary of the command line options run C<chart --help>.  Full
documentation is provided in the manual:

=over 4

=item F<doc/chart.info>

Info format, can be viewed with Emacs or the stand-alone Info viewer.

=item F<lib/App/Chart/doc/chart.html>

HTML, can be viewed from within Chart (Help/Manual menu entry), or with any
browser.

=back

=head1 OPTIONS

The default is to start the Gtk graphical interface, with an optional
initial symbol to display.  Just a part of a symbol can be given, the same
as in the File/Open dialog, so for instance

    chart bh

is enough to start on BHP.AX, if that's the only "bh" in the database.

=over 4

=item --display=DPY

Specify the X display name for Gtk.  The default is the C<DISPLAY>
environment variable, as usual.  This is a standard Gtk option, see the
C<gtk-options> man page for others (none of which do much).

=item --download

Download data for the given symbols, lists, or wildcards instead of running
the GUI.  For example

    chart --download BHP.AX GM
    chart --download "*.NZ"
    chart --download --all

Wildcards usually have to be quoted to stop the shell expanding them as
filenames.  On a tty a status line is shown with progress.

=item --all

The "All" list of all current symbols (ie. non-historical) for download
etc.

=item --alerts

The "Alerts" list of symbols for download etc.

=item --favourites

The "Favourites" list of user symbols for download etc.

=item --watchlist

Start in the Watchlist dialog, with a selected symbols list, per the options
above, or the Favourites list by default.  For example

    chart --watchlist --all

=item --ticker

Run just the scrolling stock ticker display, not the whole GUI.  Symbols,
lists or wildcards select what to display (in order).  For example

    chart --ticker BHP.AX --favourites GM "*.NZ"

=item --verbose

Print extra messages (to standard output).  This is mainly for
C<--download>.

=item -v, --version

Print the program version number and exit.

=item -h, --help

Print a summary of the command line options and exit.

=back

=head1 FILES

=over 4

=item F<~/Chart/init.pl>

User extensions (Perl code).

=item F<~/Chart/gui.pl>

User GUI extensions (Perl code).

=item F<~/Chart/notes.sqdb>

User annotations and saved preferences (SQLite database).

=item F<~/Chart/database.sqdb>

Downloaded data (SQLite database).

=back

=head1 SEE ALSO

C<gtk-options(7)>, C<info(1)>

=head1 HOME PAGE

L<http://user42.tuxfamily.org/chart/index.html>

=head1 LICENCE

Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013 Kevin Ryde

Chart is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.

Chart is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
details.

You should have received a copy of the GNU General Public License along with
Chart; see the file F<@chartdatadir@/COPYING>.  Failing that, see
L<http://www.gnu.org/licenses/>.

=cut