#!/bin/sh
# read rc file if exists
if [ -e ~/.thruk  ]; then
    . ~/.thruk
fi

if [ -e $(dirname $0)/../lib ]; then
  export PERL5LIB=$PERL5LIB:$(dirname $0)/../lib;
  if [ -z $CATALYST_CONFIG ]; then export CATALYST_CONFIG="$(dirname $0)/../"; fi
else
  export PERL5LIB=$PERL5LIB:/usr/lib/thruk/perl5:/usr/share/thruk/lib;
  if [ -z $CATALYST_CONFIG ]; then export CATALYST_CONFIG='/etc/thruk'; fi
fi

eval 'exec /usr/bin/perl -x $0 ${1+"$@"} ;'
    if 0;

#! -*- perl -*-
# vim: expandtab:ts=4:sw=4:syntax=perl
#line 21

##############################################
use strict;
use warnings;

my $naglint = Thruk::naglint->new();
exit $naglint->run();
##############################################

=head1 NAME

naglint - beautify nagios config files

=head1 SYNOPSIS

Usage: naglint [options] <file> [<files...>]

Options:
  -h, --help                    Show this help message and exit
  -v, --verbose                 Print verbose output
  -V, --version                 Print version
  -i                            replace content inline instead of printing to stdout
  -r                            read directories recursivly
  -c, --core                    core type can be nagios, icinga or shinken. Will be
                                autodetected unless specified.

=head1 DESCRIPTION

This script beautifies nagios config files.

  - clean whitespace
  - clean indention
  - remove deprecated attributes
  - validate duplicate or invalid attributes
  - break long command lines into shorter pieces
  - sort attributes naturally

=head1 OPTIONS

naglint has the following arguments:

=over 4

=item B<-h> , B<--help>

    print help and exit

=item B<-v> , B<--verbose>

    print verbose output too

=item B<-V> , B<--version>

    print version and exit

=item B<-c> , B<--core>

    use specific config type. can be 'nagios', 'icinga' or 'shinken'.
    enables core type specific attributes. Will be autodetected unless
    specified.

=item B<-i>

    edit given files inline and overwrite them with their beautified objects

=item B<-r>

    read directories recursivly

=back

=head1 RETURN VALUE

returns 0 on success or number of errors otherwise

=head1 EXAMPLES

Beautify single config file

  %> naglint objects.cfg > beauty.cfg

Beautify and replace single config file

  %> naglint -i objects.cfg

Process objects from STDIN

  %> cat objects.cfg | naglint > beauty.cfg

=head1 AUTHOR

2012, Sven Nierlein, <sven@nierlein.de>

=cut

##############################################

package Thruk::naglint;

use strict;
use warnings;
use Getopt::Long;
use Pod::Usage;
use lib '/usr/share/thruk/lib/plugins/plugins-available/conf/lib/';
use lib 'plugins/plugins-available/conf/lib/';
use Thruk::Utils;
use Thruk::Utils::Scripts;
use Monitoring::Config;
use Monitoring::Config::File;

our $VERSION = '1.56';
my $branch   = '';
$branch      = Thruk::Utils::get_git_name() unless $branch ne '';

##############################################
sub new {
    my($class) = @_;
    my $self = {};
    bless $self, $class;
    return $self;
}

##############################################
sub run {
    my($self) = @_;

    $self->{'opt'} = {
        'verbose'  => 0,
        'files'    => [],
        'inline'   => 0,
        'coretype' => 'any',
    };
    Getopt::Long::Configure('no_ignore_case');
    Getopt::Long::Configure('bundling');
    GetOptions (
       "h|help"             => \$self->{'opt'}->{'help'},
       "v|verbose"          => \$self->{'opt'}->{'verbose'},
       "V|version"          => \$self->{'opt'}->{'version'},
       "c|core=s"           => \$self->{'opt'}->{'coretype'},
       "i"                  => \$self->{'opt'}->{'inline'},
       "r"                  => \$self->{'opt'}->{'recursive'},
       "<>"                 => sub { push @{$self->{'opt'}->{'files'}}, $_[0] },
    ) or pod2usage( { -verbose => 2, -message => 'error in options' } );

    if($self->{'opt'}->{'version'}) { print "Thruk Version $VERSION".($branch ne '' ? '~'.$branch : '')."\n"; exit 0; }
    pod2usage( { -verbose => 2, -exit => 1 } ) if $self->{'opt'}->{'help'};

    ##############################################
    # make sure we don't touch owner and permissions
    local $ENV{'THRUK_NO_TOUCH_PERM'} = 1;

    my $errors = 0;

    # attached to a terminal?
    if (!-t STDIN and scalar @{$self->{'opt'}->{'files'}} == 0) {
        my $file = Monitoring::Config::File->new('virt.cfg', [], $self->{'opt'}->{'coretype'});
        my $text = "";
        while(<STDIN>) {
            $text .= $_;
            if($_ =~ m/^\s*}\s*$/mx) {
                $file->update_objects_from_text($text);
                $errors += Thruk::Utils::Scripts::print_errors($file);
                print $file->_get_new_file_content();
                $text = '';
            }
        }
        $file->update_objects_from_text($text);
        $errors += Thruk::Utils::Scripts::print_errors($file);
        print $file->_get_new_file_content();
        exit $errors;
    }

    pod2usage( { -verbose => 2, -exit => 1 } ) if scalar @{$self->{'opt'}->{'files'}} == 0;

    # iterate all given files
    for my $filename (@{$self->{'opt'}->{'files'}}) {
        $errors += $self->_process_file($filename);
    }

    return $errors;
}


##############################################
# SUBS
##############################################
sub _process_file {
    my($self, $filename) = @_;
    if(!-e $filename) {
        print STDERR "ERROR: ", $filename, " ", $!, "\n";
        return 1;
    }
    if(-d $filename) {
        if(!$self->{'opt'}->{'recursive'}) {
            print STDERR "ERROR: won't process directory unless -r specified\n";
            return 1;
        } else {
            my $errors = 0;
            my $files = Monitoring::Config->_get_files_for_folder($filename);
            for my $f (@{$files}) {
                $errors += $self->_process_file($f);
            }
            return $errors;
        }
    }

    print STDERR "parsing ", $filename, "\n" if $self->{'opt'}->{'verbose'};
    my $file = Monitoring::Config::File->new($filename, [], $self->{'opt'}->{'coretype'});
    die("could not create file object") unless defined $file;
    $file->update_objects();
    $file->{'changed'} = 1; # new content will only be returned for changed files
    return 1 if Thruk::Utils::Scripts::print_errors($file);
    if($self->{'opt'}->{'inline'}) {
        $file->save();
        print STDERR "wrote ", $filename, "\n";
    } else {
        print $file->_get_new_file_content();
    }
    return 0;
}

##############################################

1;
