package Perinci::Sub::GetArgs::Argv;

use 5.010001;
use strict;
use warnings;
#use Log::Any '$log';

use Data::Sah::Normalize qw(normalize_schema);
use Perinci::Sub::GetArgs::Array qw(get_args_from_array);
use Perinci::Sub::Util qw(err);

use Exporter;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(get_args_from_argv);

our $DATE = '2014-07-08'; # DATE
our $VERSION = '0.36'; # VERSION

our %SPEC;

my $re_simple_scalar = qr/^(str|num|int|float|bool)$/;

# retun ($success?, $errmsg, $res)
sub _parse_json {
    require Data::Clean::FromJSON;
    require JSON;

    my $str = shift;

    state $json = JSON->new->allow_nonref;

    # to rid of those JSON::XS::Boolean objects which currently choke
    # Data::Sah-generated validator code. in the future Data::Sah can be
    # modified to handle those, or we use a fork of JSON::XS which doesn't
    # produce those in the first place (probably only when performance is
    # critical).
    state $cleanser = Data::Clean::FromJSON->get_cleanser;

    my $res;
    eval { $res = $json->decode($str); $cleanser->clean_in_place($res) };
    my $e = $@;
    return (!$e, $e, $res);
}

if(0) {$SPEC{gen_go_specs_from_meta} = {
    v           => 1.1,
    summary     => 'Generate Getopt::Long spec from function metadata',
    description => <<'_',

Function arguments will be mapped to command-line options with the same name,
with non-alphanumeric characters changed to `-` (`-` is preferred over `_`
because it lets user avoid pressing Shift on popular keyboards). For example:
`file_size` becomes `file-size`, `file_size.max` becomes `file-size-max`.

Command-line aliases (`cmdline_aliases` property) in the argument specification
will also be added as command-line option. For more information about
`cmdline_aliases`, see `Rinci::function`.

If function argument option name clashes with command-line option or another
existing option, it will be renamed to `NAME-arg` (or `NAME-arg2` and so on).
For example: `help` will become `help-arg` (if `common_opts` contains `help`,
that is). If a command-line alias conflicts, a warning will be displayed and the
alias will not be added as option.

For arguments with type of `bool`, Getopt::Long will by default also add
`--noNAME` in addition to `--name`.

If `per_arg_json` setting is active, and argument's schema is not a "required
simple scalar" (e.g. an array, or a nullable string), then `--NAME-json` will
also be added to let users input undef (through `--NAME-json null`) or a
non-scalar value (e.g. `--NAME-json '[1,2,3]'`). If this name conflicts with
another existing option, a warning will be displayed and the option will not be
added.

If `per_arg_yaml` setting is active, and argument's schema is not a "required
simple scalar" (e.g. an array, or a nullable string), then `--NAME-yaml` will
also be added to let users input undef (through `--NAME-yaml '~'`) or a
non-scalar value (e.g. `--NAME-yaml '[foo, bar]'`). If this name conflicts with
another existing option, a warning will be displayed and the option will not be
added. YAML can express a larger set of values, e.g. binary data, circular
references, etc.

_
    args => {
        meta => {
            summary => 'Rinci function metadata',
            schema  => 'hash*',
            req     => 1,
        },
        common_opts => {
            summary => 'A hash of Getopt::Long option specifications'.
                'and handlers',
            schema  => 'hash*',
            description => <<'_',

This argument is used to specify common options.

_
        },
        per_arg_json => {
            summary => 'Whether to add --NAME-json for non-simple arguments',
            schema  => 'bool',
            default => 0,
            description => <<'_',

Will also interpret command-line arguments as JSON if assigned to function
arguments, if arguments' schema is not simple scalar.

_
        },
        per_arg_yaml => {
            summary => 'Whether to add --NAME-yaml for non-simple arguments',
            schema  => 'bool',
            default => 0,
            description => <<'_',

Will also interpret command-line arguments as YAML if assigned to function
arguments, if arguments' schema is not simple scalar.

_
        },
    },
};
sub _gen_go_specs_from_meta {
    my %args = @_;
}
}

sub _parse_yaml {
    require YAML::Syck;

    my $str = shift;

    local $YAML::Syck::ImplicitTyping = 1;
    my $res;
    eval { $res = YAML::Syck::Load($str) };
    my $e = $@;
    return (!$e, $e, $res);
}

$SPEC{get_args_from_argv} = {
    v => 1.1,
    summary => 'Get subroutine arguments (%args) from command-line arguments '.
        '(@ARGV)',
    description => <<'_',

Using information in function metadata's 'args' property, parse command line
arguments '@argv' into hash '%args', suitable for passing into subs.

Currently uses Getopt::Long's GetOptions to do the parsing.

As with GetOptions, this function modifies its 'argv' argument.

Why would one use this function instead of using Getopt::Long directly? Among
other reasons, we want to be able to parse complex types.

This function exists mostly to support command-line options parsing for
Perinci::CmdLine. See its documentation, on the section of command-line
options/argument parsing.

_
    args => {
        argv => {
            schema => ['array*' => {
                of => 'str*',
            }],
            description => 'If not specified, defaults to @ARGV',
        },
        meta => {
            schema => ['hash*' => {}],
            req => 1,
        },
        meta_is_normalized => {
            summary => 'Can be set to 1 if your metadata is normalized, '.
                'to avoid duplicate effort',
            schema => 'bool',
            default => 0,
        },
        check_required_args => {
            schema => ['bool'=>{default=>1}],
            summary => 'Whether to check required arguments',
            description => <<'_',

If set to true, will check that required arguments (those with req=>1) have been
specified. Normally you want this, but Perinci::CmdLine turns this off so users
can run --help even when arguments are incomplete.

_
        },
        strict => {
            schema => ['bool' => {default=>1}],
            summary => 'Strict mode',
            description => <<'_',

If set to 0, will still return parsed argv even if there are parsing errors. If
set to 1 (the default), will die upon error.

Normally you would want to use strict mode, for more error checking. Setting off
strict is used by, for example, Perinci::Sub::Complete.

_
        },
        per_arg_yaml => {
            schema => ['bool' => {default=>0}],
            summary => 'Whether to recognize --ARGNAME-yaml',
            description => <<'_',

This is useful for example if you want to specify a value which is not
expressible from the command-line, like 'undef'.

    % script.pl --name-yaml '~'

See also: per_arg_json. You should enable just one instead of turning on both.

_
        },
        per_arg_json => {
            schema => ['bool' => {default=>0}],
            summary => 'Whether to recognize --ARGNAME-json',
            description => <<'_',

This is useful for example if you want to specify a value which is not
expressible from the command-line, like 'undef'.

    % script.pl --name-json 'null'

But every other string will need to be quoted:

    % script.pl --name-json '"foo"'

See also: per_arg_yaml. You should enable just one instead of turning on both.

_
        },
        extra_getopts_before => {
            schema => ['array' => {}],
            summary => 'Specify extra Getopt::Long specification',
            description => <<'_',

If specified, insert extra Getopt::Long specification. This is used, for
example, by Perinci::CmdLine::run() to add general options --help, --version,
--list, etc so it can mixed with spec arg options, for convenience.

Since the extra specification is put at the front (before function arguments
specification), the extra options will not be able to override function
arguments (this is how Getopt::Long works). For example, if extra specification
contains --help, and one of function arguments happens to be 'help', the extra
specification won't have any effect.

_
        },
        extra_getopts_after => {
            schema => ['array' => {}],
            summary => 'Specify extra Getopt::Long specification',
            description => <<'_',

Just like *extra_getopts_before*, but the extra specification is put _after_
function arguments specification so extra options can override function
arguments.

_
        },
        allow_extra_elems => {
            schema => ['bool' => {default=>0}],
            summary => 'Allow extra/unassigned elements in argv',
            description => <<'_',

If set to 1, then if there are array elements unassigned to one of the
arguments, instead of generating an error, the function will just ignore them.

This option will be passed to Perinci::Sub::GetArgs::Array's allow_extra_elems.

_
        },
        on_missing_required_args => {
            schema => 'code',
            summary => 'Execute code when there is missing required args',
            description => <<'_',

This can be used to give a chance to supply argument value from other sources if
not specified by command-line options. Perinci::CmdLine, for example, uses this
hook to supply value from STDIN or file contents (if argument has `cmdline_src`
specification key set).

This hook will be called for each missing argument. It will be supplied hash
arguments: (arg => $the_missing_argument_name, args =>
$the_resulting_args_so_far, spec => $the_arg_spec).

The hook can return true if it succeeds in making the missing situation
resolved. In this case, the function won't complain about missing argument for
the corresponding argument.

_
        },
    },
    result => {
        description => <<'_',

Error codes:

* 500 - failure in GetOptions, meaning argv is not valid according to metadata
  specification.

* 502 - coderef in cmdline_aliases got converted into a string, probably because
  the metadata was transported (e.g. through Riap::HTTP/Riap::Simple).

_
    },
};
sub get_args_from_argv {
    require Getopt::Long;

    my %fargs = @_;
    my $argv       = $fargs{argv} // \@ARGV;
    my $meta       = $fargs{meta} or return [400, "Please specify meta"];
    unless ($fargs{meta_is_normalized}) {
        require Perinci::Sub::Normalize;
        $meta = Perinci::Sub::Normalize::normalize_function_metadata($meta);
    }
    my $strict     = $fargs{strict} // 1;
    my $extra_go_b = $fargs{extra_getopts_before} // [];
    my $extra_go_a = $fargs{extra_getopts_after} // [];
    my $per_arg_yaml = $fargs{per_arg_yaml} // 0;
    my $per_arg_json = $fargs{per_arg_json} // 0;
    my $allow_extra_elems = $fargs{allow_extra_elems} // 0;
    my $on_missing = $fargs{on_missing_required_args};
    #$log->tracef("-> get_args_from_argv(), argv=%s", $argv);

    # the resulting args
    my $rargs = {};

    my @go_spec;

    # 1. first we form Getopt::Long spec

    my $args_p = $meta->{args} // {};
    for my $a (keys %$args_p) {
        my $as = $args_p->{$a};
        # XXX normalization of 'of' clause should've been handled by sah itself
        if ($as->{schema}[0] eq 'array' && $as->{schema}[1]{of}) {
            $as->{schema}[1]{of} = normalize_schema($as->{schema}[1]{of});
        }
        my $go_opt;
        $a =~ s/_/-/g; # arg_with_underscore becomes --arg-with-underscore
        my @name = ($a);
        my $name2go_opt = sub {
            my ($name, $schema) = @_;
            if ($schema->[0] eq 'bool') {
                if (length($name) == 1 || $schema->[1]{is}) {
                    # single-letter option like -b doesn't get --nob.
                    # [bool=>{is=>1}] also means it's a flag and should not get
                    # --nofoo.
                    return $name;
                } else {
                    return "$name!";
                }
            } else {
                return "$name=s";
            }
        };
        my $arg_key;
        for my $name (@name) {
            unless (defined $arg_key) { $arg_key = $name; $arg_key =~ s/-/_/g }
            $name =~ s/\./-/g;
            $go_opt = $name2go_opt->($name, $as->{schema});
            my $type = $as->{schema}[0];
            my $cs   = $as->{schema}[1];
            my $is_simple_scalar = $type =~ $re_simple_scalar;
            my $is_array_of_simple_scalar = $type eq 'array' &&
                $cs->{of} && $cs->{of}[0] =~ $re_simple_scalar;
            #$log->errorf("TMP:$name ss=%s ass=%s",
            #             $is_simple_scalar, $is_array_of_simple_scalar);

            # why we use coderefs here? due to getopt::long's behavior. when
            # @ARGV=qw() and go_spec is ('foo=s' => \$opts{foo}) then %opts will
            # become (foo=>undef). but if go_spec is ('foo=s' => sub {
            # $opts{foo} = $_[1] }) then %opts will become (), which is what we
            # prefer, so we can later differentiate "unspecified"
            # (exists($opts{foo}) == false) and "specified as undef"
            # (exists($opts{foo}) == true but defined($opts{foo}) == false).

            my $go_handler = sub {
                my ($val, $val_set);
                if ($is_array_of_simple_scalar) {
                    $rargs->{$arg_key} //= [];
                    $val_set = 1; $val = $_[1];
                    push @{ $rargs->{$arg_key} }, $val;
                } elsif ($is_simple_scalar) {
                    $val_set = 1; $val = $_[1];
                    $rargs->{$arg_key} = $val;
                } else {
                    {
                        my ($success, $e, $decoded);
                        ($success, $e, $decoded) = _parse_json($_[1]);
                        if ($success) {
                            $val_set = 1; $val = $decoded;
                            $rargs->{$arg_key} = $val;
                            last;
                        }
                        ($success, $e, $decoded) = _parse_yaml($_[1]);
                        if ($success) {
                            $val_set = 1; $val = $decoded;
                            $rargs->{$arg_key} = $val;
                            last;
                        }
                        die "Invalid YAML/JSON in arg '$arg_key'";
                    }
                }
                # XXX special parsing of type = date

                if ($val_set && $as->{cmdline_on_getopt}) {
                    $as->{cmdline_on_getopt}->(
                        arg=>$name, value=>$val, args=>$rargs,
                        opt=>$_[0]{ctl}[1], # option name
                    );
                }
            };
            push @go_spec, $go_opt => $go_handler;

            if ($per_arg_json && $as->{schema}[0] ne 'bool') {
                push @go_spec, "$name-json=s" => sub {
                    my ($success, $e, $decoded);
                    ($success, $e, $decoded) = _parse_json($_[1]);
                    if ($success) {
                        $rargs->{$arg_key} = $decoded;
                    } else {
                        die "Invalid JSON in option --$name-json: $_[1]: $e";
                    }
                };
            }
            if ($per_arg_yaml && $as->{schema}[0] ne 'bool') {
                push @go_spec, "$name-yaml=s" => sub {
                    my ($success, $e, $decoded);
                    ($success, $e, $decoded) = _parse_yaml($_[1]);
                    if ($success) {
                        $rargs->{$arg_key} = $decoded;
                    } else {
                        die "Invalid YAML in option --$name-yaml: $_[1]: $e";
                    }
                };
            }

            # parse argv_aliases
            if ($as->{cmdline_aliases}) {
                for my $al (keys %{$as->{cmdline_aliases}}) {
                    my $alspec = $as->{cmdline_aliases}{$al};
                    my $type =
                        $alspec->{schema} ? $alspec->{schema}[0] :
                            $as->{schema} ? $as->{schema}[0] : '';
                    if ($alspec->{code} && $type eq 'bool') {
                        # bool --alias doesn't get --noalias if has code
                        $go_opt = $al; # instead of "$al!"
                    } else {
                        $go_opt = $name2go_opt->(
                            $al, $alspec->{schema} // $as->{schema});
                    }

                    if ($alspec->{code}) {
                        if ($alspec->{code} eq 'CODE') {
                            if (grep {/\A--\Q$al\E(-yaml|-json)?(=|\z)/}
                                    @$argv) {
                                return [
                                    502,
                                    join("",
                                         "Code in cmdline_aliases for arg $a ",
                                         "got converted into string, probably ",
                                         "because of JSON transport"),
                                ];
                            }
                        }
                        push @go_spec,
                            $go_opt=>sub {$alspec->{code}->($rargs, $_[1])};
                    } else {
                        push @go_spec, $go_opt=>$go_handler;
                    }
                }
            }
        }
    }

    # 2. then we run GetOptions to fill $rargs from command-line opts

    @go_spec = (@$extra_go_b, @go_spec, @$extra_go_a);
    #$log->tracef("GetOptions spec: %s", \@go_spec);
    my $old_go_opts = Getopt::Long::Configure(
        $strict ? "no_pass_through" : "pass_through",
        "no_ignore_case", "permute", "bundling", "no_getopt_compat");
    my $result = Getopt::Long::GetOptionsFromArray($argv, @go_spec);
    Getopt::Long::Configure($old_go_opts);
    unless ($result) {
        return [500, "GetOptions failed"] if $strict;
    }

    # 3. then we try to fill $rargs from remaining command-line arguments (for
    # args which have 'pos' spec specified)

    if (@$argv) {
        my $res = get_args_from_array(
            array=>$argv, meta => $meta,
            meta_is_normalized => 1,
            allow_extra_elems => $allow_extra_elems,
        );
        if ($res->[0] != 200 && $strict) {
            return err(500, "Get args from array failed", $res);
        } elsif ($res->[0] == 200) {
            my $pos_args = $res->[2];
            for my $name (keys %$pos_args) {
                my $as  = $args_p->{$name};
                my $val = $pos_args->{$name};
                if (exists $rargs->{$name}) {
                    return [400, "You specified option --$name but also ".
                                "argument #".$as->{pos}] if $strict;
                }
                my $type = $as->{schema}[0];
                my $cs   = $as->{schema}[1];
                my $is_simple_scalar = $type =~ $re_simple_scalar;
                my $is_array_of_simple_scalar = $type eq 'array' &&
                    $cs->{of} && $cs->{of}[0] =~ $re_simple_scalar;

                if ($as->{greedy} && ref($val) eq 'ARRAY') {
                    # try parsing each element as JSON/YAML
                    my $i = 0;
                    for (@$val) {
                        {
                            my ($success, $e, $decoded);
                            ($success, $e, $decoded) = _parse_json($_);
                            if ($success) {
                                $_ = $decoded;
                                last;
                            }
                            ($success, $e, $decoded) = _parse_yaml($_);
                            if ($success) {
                                $_ = $decoded;
                                last;
                            }
                            die "Invalid JSON/YAML in #$as->{pos}\[$i]";
                        }
                        $i++;
                    }
                }
                if (!$as->{greedy} && !$is_simple_scalar) {
                    # try parsing as JSON/YAML
                    my ($success, $e, $decoded);
                    ($success, $e, $decoded) = _parse_json($val);
                    {
                        if ($success) {
                            $val = $decoded;
                            last;
                        }
                        ($success, $e, $decoded) = _parse_yaml($val);
                        if ($success) {
                            $val = $decoded;
                            last;
                        }
                        die "Invalid JSON/YAML in #$as->{pos}";
                    }
                }
                $rargs->{$name} = $val;
                # we still call cmdline_on_getopt for this
                if ($as->{cmdline_on_getopt}) {
                    if ($as->{greedy}) {
                        $as->{cmdline_on_getopt}->(
                            arg=>$name, value=>$_, args=>$rargs,
                            opt=>undef, # this marks that value is retrieved from cmdline arg
                        ) for @$val;
                    } else {
                        $as->{cmdline_on_getopt}->(
                            arg=>$name, value=>$val, args=>$rargs,
                            opt=>undef, # this marks that value is retrieved from cmdline arg
                        );
                    }
                }
            }
        }
    }

    # 4. check required args

    my $missing_arg;
    for my $a (keys %$args_p) {
        my $as = $args_p->{$a};
        if (!exists($rargs->{$a})) {
            next unless $as->{req};
            # give a chance to hook to set missing arg
            if ($on_missing) {
                next if $on_missing->(arg=>$a, args=>$rargs, spec=>$as);
            }
            next if exists $rargs->{$a};
            $missing_arg = $a;
            if (($fargs{check_required_args} // 1) && $strict) {
                return [400, "Missing required argument: $a"];
            }
        }
    }

    #$log->tracef("<- get_args_from_argv(), args=%s, remaining argv=%s",
    #             $rargs, $argv);
    [200, "OK", $rargs, {"func.missing_arg"=>$missing_arg}];
}

1;
#ABSTRACT: Get subroutine arguments from command line arguments (@ARGV)

__END__

=pod

=encoding UTF-8

=head1 NAME

Perinci::Sub::GetArgs::Argv - Get subroutine arguments from command line arguments (@ARGV)

=head1 VERSION

This document describes version 0.36 of Perinci::Sub::GetArgs::Argv (from Perl distribution Perinci-Sub-GetArgs-Argv), released on 2014-07-08.

=head1 SYNOPSIS

 use Perinci::Sub::GetArgs::Argv;

 my $res = get_args_from_argv(argv=>\@ARGV, meta=>$meta, ...);

=head1 DESCRIPTION

This module provides C<get_args_from_argv()>, which parses command line
arguments (C<@ARGV>) into subroutine arguments (C<%args>). This module is used
by L<Perinci::CmdLine>. For explanation on how command-line options are
processed, see Perinci::CmdLine's documentation.

This module uses L<Log::Any> for logging framework.

This module has L<Rinci> metadata.

=head1 FUNCTIONS


=head2 get_args_from_argv(%args) -> [status, msg, result, meta]

Get subroutine arguments (%args) from command-line arguments (@ARGV).

Using information in function metadata's 'args' property, parse command line
arguments '@argv' into hash '%args', suitable for passing into subs.

Currently uses Getopt::Long's GetOptions to do the parsing.

As with GetOptions, this function modifies its 'argv' argument.

Why would one use this function instead of using Getopt::Long directly? Among
other reasons, we want to be able to parse complex types.

This function exists mostly to support command-line options parsing for
Perinci::CmdLine. See its documentation, on the section of command-line
options/argument parsing.

Arguments ('*' denotes required arguments):

=over 4

=item * B<allow_extra_elems> => I<bool> (default: 0)

Allow extra/unassigned elements in argv.

If set to 1, then if there are array elements unassigned to one of the
arguments, instead of generating an error, the function will just ignore them.

This option will be passed to Perinci::Sub::GetArgs::Array's allowI<extra>elems.

=item * B<argv> => I<array>

If not specified, defaults to @ARGV

=item * B<check_required_args> => I<bool> (default: 1)

Whether to check required arguments.

If set to true, will check that required arguments (those with req=>1) have been
specified. Normally you want this, but Perinci::CmdLine turns this off so users
can run --help even when arguments are incomplete.

=item * B<extra_getopts_after> => I<array>

Specify extra Getopt::Long specification.

Just like I<extra_getopts_before>, but the extra specification is put I<after>
function arguments specification so extra options can override function
arguments.

=item * B<extra_getopts_before> => I<array>

Specify extra Getopt::Long specification.

If specified, insert extra Getopt::Long specification. This is used, for
example, by Perinci::CmdLine::run() to add general options --help, --version,
--list, etc so it can mixed with spec arg options, for convenience.

Since the extra specification is put at the front (before function arguments
specification), the extra options will not be able to override function
arguments (this is how Getopt::Long works). For example, if extra specification
contains --help, and one of function arguments happens to be 'help', the extra
specification won't have any effect.

=item * B<meta>* => I<hash>

=item * B<meta_is_normalized> => I<bool> (default: 0)

Can be set to 1 if your metadata is normalized, to avoid duplicate effort.

=item * B<on_missing_required_args> => I<code>

Execute code when there is missing required args.

This can be used to give a chance to supply argument value from other sources if
not specified by command-line options. Perinci::CmdLine, for example, uses this
hook to supply value from STDIN or file contents (if argument has C<cmdline_src>
specification key set).

This hook will be called for each missing argument. It will be supplied hash
arguments: (arg => $theI<missing>argumentI<name, args =>
$the>resultingI<args>soI<far, spec => $the>arg_spec).

The hook can return true if it succeeds in making the missing situation
resolved. In this case, the function won't complain about missing argument for
the corresponding argument.

=item * B<per_arg_json> => I<bool> (default: 0)

Whether to recognize --ARGNAME-json.

This is useful for example if you want to specify a value which is not
expressible from the command-line, like 'undef'.

    % script.pl --name-json 'null'

But every other string will need to be quoted:

    % script.pl --name-json '"foo"'

See also: perI<arg>yaml. You should enable just one instead of turning on both.

=item * B<per_arg_yaml> => I<bool> (default: 0)

Whether to recognize --ARGNAME-yaml.

This is useful for example if you want to specify a value which is not
expressible from the command-line, like 'undef'.

    % script.pl --name-yaml '~'

See also: perI<arg>json. You should enable just one instead of turning on both.

=item * B<strict> => I<bool> (default: 1)

Strict mode.

If set to 0, will still return parsed argv even if there are parsing errors. If
set to 1 (the default), will die upon error.

Normally you would want to use strict mode, for more error checking. Setting off
strict is used by, for example, Perinci::Sub::Complete.

=back

Return value:

Returns an enveloped result (an array).

First element (status) is an integer containing HTTP status code
(200 means OK, 4xx caller error, 5xx function error). Second element
(msg) is a string containing error message, or 'OK' if status is
200. Third element (result) is optional, the actual result. Fourth
element (meta) is called result metadata and is optional, a hash
that contains extra information.

=head1 FAQ

=head1 SEE ALSO

L<Perinci>

=head1 HOMEPAGE

Please visit the project's homepage at L<https://metacpan.org/release/Perinci-Sub-GetArgs-Argv>.

=head1 SOURCE

Source repository is at L<https://github.com/sharyanto/perl-Perinci-Sub-GetArgs-Argv>.

=head1 BUGS

Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Perinci-Sub-GetArgs-Argv>

When submitting a bug or request, please include a test-file or a
patch to an existing test-file that illustrates the bug or desired
feature.

=head1 AUTHOR

Steven Haryanto <stevenharyanto@gmail.com>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2014 by Steven Haryanto.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut
