NAME

Getopt::Yath::Tutorial - A step-by-step guide to using Getopt::Yath

DESCRIPTION

This tutorial walks you through using Getopt::Yath to handle command-line options in your Perl scripts and modules. It starts with simple examples and builds up to advanced features.

YOUR FIRST SCRIPT

Here is the simplest useful script with Getopt::Yath:

#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Yath;

option_group {group => 'my', category => 'My Options'} => sub {

    option name => (
        type        => 'Scalar',
        description => 'Your name',
    );

};

my $state = parse_options(\@ARGV);
my $settings = $state->settings;

print "Hello, " . ($settings->my->name // 'World') . "!\n";

Run it:

$ perl hello.pl --name Bob
Hello, Bob!

$ perl hello.pl
Hello, World!

What just happened?

use Getopt::Yath exports several functions into your package:

option

Define a single command-line option with a name and specification.

option_group

Set shared attributes (group, category, prefix, etc.) for a block of options.

parse_options

Process an arrayref of command-line arguments and return a state hashref with settings, remaining args, skipped items, and more.

include_options

Import option definitions from another module that also uses Getopt::Yath.

option_post_process

Register a callback to run after all options have been parsed, for cross-option validation or fixups.

options

Return the Getopt::Yath::Instance object that holds all defined options. Used for generating help and documentation output.

category_sort_map

Control the display order of option categories in help output.

Every option needs a group (the key under which it appears in settings) and a type (how it parses arguments). The category is a human-readable label used for help output.

OPTION GROUPS

option_group lets you set shared attributes for a block of options so you don't have to repeat yourself:

option_group {group => 'server', category => 'Server Options'} => sub {

    option host => (
        type        => 'Scalar',
        default     => 'localhost',
        description => 'Hostname to bind to',
    );

    option port => (
        type        => 'Scalar',
        short       => 'p',
        default     => 8080,
        description => 'Port to listen on',
    );

    option verbose => (
        type        => 'Bool',
        short       => 'v',
        description => 'Enable verbose output',
    );

};

my $state = parse_options(\@ARGV);
my $settings = $state->settings;

printf "Starting server on %s:%s\n",
    $settings->server->host,
    $settings->server->port;

Every option declared inside this block inherits group => 'server' and category => 'Server Options'. The parsed values are accessed via $settings->server->host, $settings->server->port, etc.

OPTION TYPES

Getopt::Yath provides a rich set of option types. Here they are from simplest to most specialized.

Bool

A simple on/off flag. No argument needed.

option verbose => (
    type        => 'Bool',
    short       => 'v',
    default     => 0,
    description => 'Enable verbose output',
);

Usage:

--verbose       # turns it on (1)
-v              # same thing
--no-verbose    # turns it off (0)

Scalar

Takes exactly one value.

option output => (
    type        => 'Scalar',
    short       => 'o',
    description => 'Output file path',
);

Usage:

--output results.txt
--output=results.txt
-o results.txt
-o=results.txt
-oresults.txt       # short form allows value directly after flag
--no-output         # clears the value (sets to undef)

Count

An incrementing counter. Each use bumps the value by one.

option verbosity => (
    type        => 'Count',
    short       => 'v',
    initialize  => 0,
    description => 'Increase verbosity level',
);

Usage:

-v          # 1
-vvv        # 3 (short flags can be stacked)
--verbosity # 1
-v -v -v    # 3
-v=5        # explicitly set to 5
--no-verbosity  # reset to 0

List

Collects multiple values into an arrayref. Can be specified multiple times.

option include => (
    type        => 'List',
    short       => 'I',
    description => 'Add a directory to the include path',
);

Usage:

-I lib -I blib/lib
--include lib --include blib/lib
--no-include    # empties the list

Lists also accept JSON arrays:

--include '["lib","blib/lib"]'

Splitting values

Use split_on to allow comma-separated (or other delimiter) values:

option tags => (
    type        => 'List',
    split_on    => ',',
    description => 'Comma-separated list of tags',
);

# --tags foo,bar,baz   =>   ['foo', 'bar', 'baz']

Map

Collects key=value pairs into a hashref. Can be specified multiple times.

option env_var => (
    type        => 'Map',
    short       => 'E',
    alt         => ['env-var'],
    description => 'Set an environment variable',
);

Usage:

-E HOME=/tmp -E USER=test
--env-var HOME=/tmp
--env-var '{"HOME":"/tmp","USER":"test"}'   # JSON also works
--no-env-var    # empties the hash

Like List, Map supports split_on for multiple pairs in one argument, and key_on to change the key/value delimiter (default is =).

Auto

A scalar option with an autofill value. --opt uses the autofill value, --opt=val uses the provided value. Does not support --opt val (the space-separated form), because without a required argument the next token is ambiguous.

option coverage => (
    type        => 'Auto',
    autofill    => '-silent,1',
    description => 'Enable coverage; optionally provide Devel::Cover args',
    long_examples  => ['', '=-silent,1'],
);

Usage:

--coverage          # uses autofill: "-silent,1"
--coverage=-db,cov  # uses the provided value
--no-coverage       # clears

AutoList

Combines Auto and List. --opt adds the autofill values to the list. --opt=val adds a specific value.

option plugins => (
    type        => 'AutoList',
    autofill    => sub { qw/Foo Bar/ },
    description => 'Plugins to load (defaults to Foo and Bar)',
);

Usage:

--plugins           # adds 'Foo' and 'Bar'
--plugins=Baz       # adds 'Baz'
--no-plugins        # empties the list

AutoMap

Combines Auto and Map. --opt adds the autofill key/value pairs. --opt=key=val adds a specific pair.

option defaults => (
    type        => 'AutoMap',
    autofill    => sub { timeout => 30, retries => 3 },
    description => 'Default settings',
);

Usage:

--defaults              # adds {timeout => 30, retries => 3}
--defaults=color=red    # adds {color => 'red'}
--no-defaults           # empties the hash

PathList

Like List, but values containing wildcards are expanded using glob().

option test_files => (
    type        => 'PathList',
    split_on    => ',',
    description => 'Test files to run (globs allowed)',
);

Usage:

--test-files 't/*.t'
--test-files t/foo.t,t/bar.t
--test-files 'lib/**/*.pm'

AutoPathList

Combines AutoList and PathList. --opt adds autofill paths, --opt=glob expands the glob.

option dev_libs => (
    type        => 'AutoPathList',
    short       => 'D',
    name        => 'dev-lib',
    autofill    => sub { 'lib', 'blib/lib', 'blib/arch' },
    description => 'Add dev library paths (default: lib, blib/lib, blib/arch)',
);

Usage:

-D              # adds lib, blib/lib, blib/arch
-D=lib          # adds just lib
-D='lib/*'      # adds all matches

BoolMap

Matches a pattern of options dynamically, building a hash of boolean values. Requires a pattern attribute with a capture group.

option features => (
    type        => 'BoolMap',
    pattern     => qr/feature-(.+)/,
    description => 'Toggle features on or off',
);

Usage:

--feature-color         # {color => 1}
--feature-unicode       # {unicode => 1}
--no-feature-color      # {color => 0}

The pattern is embedded into qr/^--(no-)?$pattern$/ automatically. Each captured key gets a true or false value depending on the --no- prefix.

COMMON OPTION ATTRIBUTES

These attributes work across all option types.

short

A single-character alias for the option:

option jobs => (
    type  => 'Scalar',
    short => 'j',
    ...
);

# -j4   --jobs 4   --jobs=4   -j 4

alt and alt_no

Alternate long names for the option:

option use_stream => (
    type   => 'Bool',
    alt    => ['stream'],       # --stream also works
    alt_no => ['TAP'],          # --TAP is equivalent to --no-use-stream
    description => 'Use streaming format instead of TAP',
);

prefix

Adds a prefix to the option name. Especially useful in option groups:

option_group {group => 'db', category => 'Database', prefix => 'db'} => sub {

    option host => (
        type => 'Scalar',
        description => 'Database host',
    );

    option port => (
        type    => 'Scalar',
        default => 5432,
        description => 'Database port',
    );

};

# --db-host myhost --db-port 3306

The values are still accessed as $settings->db->host and $settings->db->port -- the prefix only affects the command-line form.

field and name

By default, the option title determines both the field name (underscores) and the CLI name (dashes). You can override either:

option test_args => (
    type  => 'List',
    field => 'args',        # $settings->tests->args (not test_args)
    alt   => ['test-arg'],
    ...
);

default

A value used when the option is not provided on the command line and not set via an environment variable:

option timeout => (
    type    => 'Scalar',
    default => 60,
    description => 'Timeout in seconds',
);

# Can also be a coderef:
option search => (
    type    => 'PathList',
    default => sub { './t', './t2' },
    ...
);

initialize

Set an initial value before parsing begins. This differs from default in that default is applied after parsing if the option was never set, while initialize sets a starting value that can then be modified by command-line arguments.

option verbosity => (
    type       => 'Count',
    initialize => 0,
    ...
);

ENVIRONMENT VARIABLES

from_env_vars

Read an option's initial value from environment variables. The first one that is defined wins:

option verbose => (
    type          => 'Bool',
    from_env_vars => ['MYAPP_VERBOSE', 'VERBOSE'],
    description   => 'Enable verbose output',
);

Prefix a variable with ! to negate its value:

option quiet => (
    type          => 'Bool',
    from_env_vars => ['!VERBOSE'],     # quiet = true when VERBOSE is false
    description   => 'Suppress output',
);

set_env_vars

Set environment variables after parsing is complete (Bool, Scalar, Count, and Auto types only):

option cover => (
    type          => 'Auto',
    autofill      => '-silent,1',
    from_env_vars => ['T2_DEVEL_COVER'],
    set_env_vars  => ['T2_DEVEL_COVER'],
    description   => 'Enable Devel::Cover',
);

The ! prefix also works here for inverted env vars.

clear_env_vars

Clear specified environment variables after parsing:

option token => (
    type           => 'Scalar',
    from_env_vars  => ['AUTH_TOKEN'],
    clear_env_vars => ['AUTH_TOKEN'],    # don't leak to child processes
    description    => 'Auth token (cleared from env after reading)',
);

NORMALIZE AND TRIGGER

normalize

Transform a value as it is parsed:

option module => (
    type      => 'Scalar',
    normalize => sub {
        my ($val) = @_;
        $val =~ s/-/::/g;      # Allow Foo-Bar instead of Foo::Bar
        return $val;
    },
    description => 'Module name',
);

# --module Foo-Bar  =>  "Foo::Bar"

For List and Map types, normalize is called on each value individually.

trigger

A callback invoked whenever the option is set or cleared from the command line. Triggers do not fire for defaults, autofill, or initialization.

option input_file => (
    type    => 'Scalar',
    trigger => sub {
        my $opt    = shift;
        my %params = @_;

        return unless $params{action} eq 'set';
        my ($file) = @{$params{val}};
        die "File not found: $file\n" unless -f $file;
    },
    description => 'Input file',
);

The %params hash contains:

action   => 'set' or 'clear'
val      => \@values (arrayref, even for scalars)
ref      => \$field_ref
state    => $parse_state
options  => $instance
settings => $settings
group    => $group

ALLOWED VALUES

Restrict what values an option will accept:

option format => (
    type           => 'Scalar',
    allowed_values => [qw/json csv xml/],
    description    => 'Output format',
);

# --format json   # ok
# --format yaml   # dies: Invalid value

allowed_values can be an arrayref, a regex, or a coderef:

allowed_values => qr/^\d+$/,            # must be numeric
allowed_values => sub { $_[1] > 0 },    # must be positive

Use allowed_values_text to customize the help message:

allowed_values_text => 'json, csv, or xml',

PARSING OPTIONS

The parse_options function is how you process command-line arguments. It returns a state hashref with all the parsed data.

Basic parsing

my $state = parse_options(\@ARGV);

my $settings = $state->settings;     # Getopt::Yath::Settings object
my $remains  = $state->remains;      # args after a stop token
my $skipped  = $state->skipped;      # skipped non-options
my $stop     = $state->stop;         # what token stopped parsing
my $env      = $state->env;          # env vars that were/would be set
my $cleared  = $state->cleared;      # options cleared via --no-opt
my $modules  = $state->modules;      # modules whose options were used

Stops

Use stops to stop parsing at certain tokens. This is most commonly used for --:

my $state = parse_options(\@ARGV,
    stops => ['--'],
);

# perl script.pl --verbose -- --not-an-option foo bar
#   $state->stop    = '--'
#   $state->remains = ['--not-an-option', 'foo', 'bar']

You can define multiple stop tokens. :: is commonly used as a separator for passing arguments through to tests:

my $state = parse_options(\@ARGV,
    stops => ['--', '::'],
);

# perl script.pl --verbose :: --some-test-arg
#   $state->stop    = '::'
#   $state->remains = ['--some-test-arg']

Handling non-options

By default, encountering a non-option (something not starting with -) throws an error. You can change this:

# Skip non-options (collect them)
my $state = parse_options(\@ARGV,
    skip_non_opts => 1,
);
my @files = @{$state->skipped};

# Stop at first non-option
my $state = parse_options(\@ARGV,
    stop_at_non_opts => 1,
);
# $state->stop = the non-option that caused the stop
# $state->remains = everything after it

Handling invalid options

Similarly, you can control what happens with unrecognized options:

# Skip invalid options
my $state = parse_options(\@ARGV,
    skip_invalid_opts => 1,
);

# Stop at invalid options
my $state = parse_options(\@ARGV,
    stop_at_invalid_opts => 1,
);

Suppressing env var side effects

Prevent parse_options from modifying %ENV:

my $state = parse_options(\@ARGV,
    no_set_env => 1,
);

# $state->env still shows what would have been set

Argument groups

Argument groups let you collect arguments between delimiter tokens into an arrayref. This is useful for passing structured groups of values:

my $state = parse_options(\@ARGV,
    groups => { ':{' => '}:' },
);

# perl script.pl --opt :{ arg1 arg2 arg3 }:
# The :{ ... }: group is collected as ['arg1', 'arg2', 'arg3']

Groups can be used as option values or as standalone arguments (which end up in skipped when skip_non_opts is enabled).

GENERATING HELP OUTPUT

CLI help

sub show_help {
    print options()->docs('cli');
}

This produces formatted terminal output with ANSI colors (when available), organized by category.

POD documentation

sub show_pod {
    print options()->docs('pod', head => 2);
}

This produces POD markup suitable for embedding in your module's documentation. The head parameter controls the heading level (e.g., =head2).

Controlling category order

By default, categories are sorted alphabetically. Use category_sort_map to control the order:

category_sort_map(
    'General Options' => 1,
    'Server Options'  => 2,
    'Debug Options'   => 3,
);

Lower values appear first.

REUSABLE OPTION LIBRARIES

You can define options in a module and include them elsewhere. This lets you build composable option sets.

Defining an option library

package My::Options::Database;
use strict;
use warnings;
use Getopt::Yath;

option_group {group => 'db', category => 'Database Options'} => sub {

    option host => (
        type    => 'Scalar',
        default => 'localhost',
        description => 'Database hostname',
    );

    option port => (
        type    => 'Scalar',
        default => 5432,
        description => 'Database port',
    );

    option name => (
        type        => 'Scalar',
        description => 'Database name',
    );

};

1;

Including options from another module

package My::App;
use strict;
use warnings;
use Getopt::Yath;

include_options('My::Options::Database');

option_group {group => 'app', category => 'App Options'} => sub {

    option debug => (
        type        => 'Bool',
        description => 'Debug mode',
    );

};

my $state = parse_options(\@ARGV);
# $state->settings->db->host, ->db->port, etc.
# $state->settings->app->debug

Selective inclusion

You can include only specific options from a library:

include_options(
    'My::Options::Database' => [qw/host port/],
);

Only the host and port options will be included; name will be excluded.

POST-PROCESSING

option_post_process registers a callback that runs after all options have been parsed but before environment variables are set. This is useful for validation that depends on multiple options:

option_post_process sub {
    my ($instance, $state) = @_;

    my $settings = $state->settings;
    my $server   = $settings->server;

    if ($server->ssl && !$server->cert_file) {
        die "--ssl requires --cert-file\n";
    }
};

Weighted post-processors

The first argument is a weight that controls execution order (lower runs first, default is 0):

option_post_process 10 => sub {
    my ($instance, $state) = @_;
    # Runs after weight-0 post-processors
};

Conditional post-processors

An optional applicable coderef controls whether the post-processor runs:

option_post_process 0 => sub { ... if server group exists ... } => sub {
    my ($instance, $state) = @_;
    ...
};

If used inside an option_group block, the group's applicable is inherited automatically.

DYNAMIC OPTIONS WITH mod_adds_options

Some options specify a module name whose options should be dynamically loaded when the option is used:

option runner_class => (
    type             => 'Scalar',
    name             => 'runner',
    field            => 'class',
    default          => 'My::Runner',
    mod_adds_options => 1,
    normalize        => sub { fqmod($_[0], 'My::Runner') },
    description      => 'Runner class to use',
);

When --runner=My::Custom::Runner is specified, the module is loaded, and if it has an options() method, those options are included into the current instance. This allows plugins and extensions to contribute their own command-line options.

APPLICABILITY

Options can be conditionally shown/hidden based on runtime state:

option reloader => (
    type       => 'Auto',
    autofill   => 'My::Reloader',
    applicable => sub {
        my ($opt, $options, $settings) = @_;
        return $settings->runner->preloads && @{$settings->runner->preloads};
    },
    description => 'Module reloader (only available with preloads)',
);

When applicable returns false, the option is excluded from parsing and documentation.

THE SETTINGS OBJECT

The $state->settings value is a Getopt::Yath::Settings object. Groups are accessed as methods:

$settings->server->host;
$settings->db->port;

Useful methods:

# Check if a group exists
if ($settings->check_group('server')) { ... }

# Safe access with a default
my $val = $settings->maybe('server', 'host', 'localhost');

COMPLETE EXAMPLE

Here is a complete script demonstrating many features together:

#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Yath;

option_group {group => 'app', category => 'Application Options'} => sub {

    option verbose => (
        type          => 'Count',
        short         => 'v',
        initialize    => 0,
        from_env_vars => ['MYAPP_VERBOSE'],
        set_env_vars  => ['MYAPP_VERBOSE'],
        description   => 'Increase verbosity (-v, -vv, -vvv)',
    );

    option config => (
        type        => 'Scalar',
        short       => 'c',
        description => 'Path to config file',
        trigger     => sub {
            my $opt    = shift;
            my %params = @_;
            return unless $params{action} eq 'set';
            my ($file) = @{$params{val}};
            die "Config file not found: $file\n" unless -f $file;
        },
    );

    option output_format => (
        type           => 'Scalar',
        short          => 'f',
        default        => 'json',
        allowed_values => [qw/json csv xml/],
        description    => 'Output format',
    );

    option tags => (
        type        => 'List',
        short       => 't',
        split_on    => ',',
        description => 'Tags to apply (comma-separated)',
    );

    option env_vars => (
        type        => 'Map',
        short       => 'E',
        description => 'Extra environment variables (KEY=VAL)',
    );

    option dry_run => (
        type        => 'Bool',
        short       => 'n',
        default     => 0,
        description => 'Dry run, do not make changes',
    );

};

option_group {group => 'files', category => 'File Options'} => sub {

    option include => (
        type        => 'PathList',
        short       => 'I',
        description => 'Files to include (globs allowed)',
    );

};

category_sort_map(
    'Application Options' => 1,
    'File Options'        => 2,
);

option_post_process sub {
    my ($instance, $state) = @_;
    my $settings = $state->settings;

    if ($settings->app->dry_run && $settings->app->verbose < 1) {
        warn "Note: --dry-run without --verbose; enabling verbose=1\n";
        $settings->app->option(verbose => 1);
    }
};

# Show help if requested
if (grep { $_ eq '--help' || $_ eq '-h' } @ARGV) {
    print options()->docs('cli');
    exit 0;
}

my $state = parse_options(\@ARGV,
    stops         => ['--', '::'],
    skip_non_opts => 1,
);

my $settings = $state->settings;

printf "Verbosity: %d\n", $settings->app->verbose;
printf "Format: %s\n",    $settings->app->output_format;
printf "Dry run: %s\n",   $settings->app->dry_run ? 'yes' : 'no';

if (my $tags = $settings->app->tags) {
    printf "Tags: %s\n", join(', ', @$tags) if @$tags;
}

if (my @files = @{$state->skipped}) {
    printf "Files: %s\n", join(', ', @files);
}

if ($state->stop) {
    printf "Stopped at: %s\n", $state->stop;
    printf "Remaining: %s\n", join(' ', @{$state->remains});
}

ADVANCED: MULTI-STAGE PARSING WITH SUBCOMMANDS

Many tools follow a script [GLOBAL OPTIONS] subcommand [COMMAND OPTIONS] pattern, where the script has its own set of options, a subcommand name, and then the subcommand has its own options. Getopt::Yath supports this through multi-stage parsing: parse the global options first (stopping at the subcommand), then load the subcommand and parse its options from the remaining arguments.

Step 1: Define global options

Create a package for your script-level options.

package My::CLI;
use strict;
use warnings;
use Getopt::Yath;

option_group {group => 'global', category => 'Global Options'} => sub {

    option verbose => (
        type        => 'Count',
        short       => 'v',
        initialize  => 0,
        description => 'Increase verbosity',
    );

    option config => (
        type        => 'Scalar',
        short       => 'c',
        description => 'Path to config file',
    );

};

Step 2: Define subcommand option packages

Each subcommand is a separate package with its own options:

package My::CLI::Command::deploy;
use strict;
use warnings;
use Getopt::Yath;

option_group {group => 'deploy', category => 'Deploy Options'} => sub {

    option target => (
        type        => 'Scalar',
        short       => 't',
        description => 'Deployment target (staging, production)',
    );

    option dry_run => (
        type        => 'Bool',
        short       => 'n',
        default     => 0,
        description => 'Perform a dry run',
    );

};

sub run {
    my ($class, $settings) = @_;
    printf "Deploying to %s (dry_run=%s, verbose=%d)\n",
        $settings->deploy->target // 'default',
        $settings->deploy->dry_run ? 'yes' : 'no',
        $settings->global->verbose;
}

package My::CLI::Command::test;
use strict;
use warnings;
use Getopt::Yath;

option_group {group => 'test', category => 'Test Options'} => sub {

    option jobs => (
        type        => 'Scalar',
        short       => 'j',
        default     => 1,
        description => 'Number of parallel test jobs',
    );

    option tags => (
        type        => 'List',
        split_on    => ',',
        description => 'Filter tests by tag',
    );

};

sub run {
    my ($class, $settings) = @_;
    printf "Running tests with %d jobs (verbose=%d)\n",
        $settings->test->jobs,
        $settings->global->verbose;
}

Step 3: Two-stage dispatch

Parse global options first, stopping at the subcommand name. Then load the subcommand module, include its options, and parse the remaining arguments using the combined option set. Pass the settings object through so both stages share state.

package main;

my %commands = (
    deploy => 'My::CLI::Command::deploy',
    test   => 'My::CLI::Command::test',
);

# Stage 1: Parse global options, stop at the subcommand name
my $stage1 = My::CLI::parse_options(
    \@ARGV,
    stop_at_non_opts => 1,
);

my $command_name = $stage1->stop
    or die "Usage: my-cli [global options] <command> [command options]\n";

my $command_class = $commands{$command_name}
    or die "Unknown command: $command_name\n";

# Stage 2: Include command options, parse remaining args
#          Carry forward settings so global values are preserved
My::CLI::include_options($command_class);

my $stage2 = My::CLI::parse_options(
    $stage1->remains,
    settings => $stage1->settings,
);

# Dispatch — settings object has both global and command groups
$command_class->run($stage2->settings);

Example invocations

$ my-cli -vv deploy --target production --dry-run
Deploying to production (dry_run=yes, verbose=2)

$ my-cli --config app.yml test -j8 --tags smoke,unit
Running tests with 8 jobs (verbose=0)

The key techniques are:

  • stop_at_non_opts in Stage 1 stops parsing at the subcommand name, placing it in $state->stop and the rest in $state->remains.

  • include_options merges the subcommand's options into the script's option instance before Stage 2.

  • settings is passed from Stage 1 to Stage 2 so the global values are preserved and both stages write into the same settings object.

SEE ALSO

Getopt::Yath - Main module documentation and API reference
Getopt::Yath::State - The parse result object returned by parse_options
Getopt::Yath::Option - Base option class and all available attributes
Getopt::Yath::Settings - The parsed settings object
Getopt::Yath::Instance - Internal option instance (advanced usage)

SOURCE

The source code repository for Getopt-Yath can be found at http://github.com/Test-More/Getopt-Yath/.

MAINTAINERS

Chad Granum <exodist@cpan.org>

AUTHORS

Chad Granum <exodist@cpan.org>

COPYRIGHT

Copyright Chad Granum <exodist7@gmail.com>.

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

See http://dev.perl.org/licenses/