| Current File : //usr/tmp/par-6c6566/cache-9ab9fe47781b3f807c8b3ee838b58c73318324f4/inc/lib/Config/Crontab.pm |
#line 1 "Config/Crontab.pm"
############################################################
############################################################
##
## Scott Wiersdorf
## Created: Fri May 9 14:03:01 MDT 2003
## Updated: $Id: Crontab.pm,v 1.8 2014/01/09 04:50:04 scott Exp $
##
## Config::Crontab - a crontab(5) parser
##
## This file contains the following classes:
##
## - Config::Crontab - the top level crontab object
## - Config::Crontab::Block - crontab block (paragraph) handling
## - Config::Crontab::Event - "5 0 * * * /bin/command"
## - Config::Crontab::Env - "VAR=value"
## - Config::Crontab::Comment - "## a comment"
## - Config::Crontab::Base - base class from which all other
## Config::Crontab classes inherit
## - Config::Crontab::Container - base class from which Crontab and
## Block classes inherit
##
############################################################
############################################################
## to do: if -file = /etc/crontab, set system => 1
## to do: if adding a non-block to a $ct file, make a block for us automatically
## a crontab object is a list of Block objects (see below) This class
## (Config::Crontab) is for working with crontab files as a whole.
package Config::Crontab;
use strict;
use warnings;
use Carp;
use 5.006_001;
our @ISA = qw(Config::Crontab::Base Config::Crontab::Container);
## these two are for the 'write' method
use Fcntl;
use File::Temp qw(:POSIX);
our $VERSION = '1.40';
sub init {
my $self = shift;
my %args = @_;
$self->file('');
$self->mode('block');
$self->squeeze(1); ## only in block mode
$self->strict(0);
$self->blocks([]);
$self->error('');
$self->system(0);
$self->owner('');
$self->owner_re( '[^a-zA-Z0-9\._-]' );
$self->file( $args{'-file'}) if exists $args{'-file'};
$self->mode( $args{'-mode'}) if exists $args{'-mode'};
$self->squeeze( $args{'-squeeze'}) if exists $args{'-squeeze'};
$self->strict( $args{'-strict'}) if exists $args{'-strict'};
$self->system( $args{'-system'}) if exists $args{'-system'};
$self->owner( $args{'-owner'}) if exists $args{'-owner'};
$self->owner_re( $args{'-owner_re'}) if exists $args{'-owner_re'};
## auto-parse if file is specified
$self->read if $self->file;
return 1;
}
sub read {
my $self = shift;
my %args = @_;
$self->file( $args{'-file'}) if exists $args{'-file'};
$self->mode( $args{'-mode'}) if exists $args{'-mode'};
$self->squeeze( $args{'-squeeze'}) if exists $args{'-squeeze'};
$self->strict( $args{'-strict'}) if exists $args{'-strict'};
$self->system( $args{'-system'}) if exists $args{'-system'};
$self->owner( $args{'-owner'}) if exists $args{'-owner'};
$self->owner_re( $args{'-owner_re'}) if exists $args{'-owner_re'};
## set default system crontab
if( $self->system && ! $self->file ) {
$self->file('/etc/crontab');
}
## parse the file accordingly
if( $self->file ) {
open FILE, $self->file
or do {
$self->error($!);
if( $self->strict ) {
croak "Could not open " . $self->file . ": " . $self->error . "\n";
}
return;
}
}
else {
my $crontab_cmd = "crontab -l 2>/dev/null|";
if( $self->owner ) {
if( $^O eq 'SunOS' ) {
$crontab_cmd = "crontab -l " . $self->owner . " 2>/dev/null|";
}
else {
$crontab_cmd = "crontab -u " . $self->owner . " -l 2>/dev/null|";
}
}
open FILE, $crontab_cmd
or do {
$self->error($!);
if( $self->strict ) {
croak "Could not open pipe from crontab: " . $self->error . "\n";
}
return;
}
}
## reset internal block list and errors
$self->blocks([]);
$self->error('');
PARSE: {
local $/;
## each line is a block
if( $self->mode eq 'line' ) {
$/ = "\n";
}
## whole file is a block
elsif( $self->mode eq 'file' ) {
$/ = undef;
}
## each paragraph (\n\n+) is a block
else {
$/ = ( $self->squeeze ? '' : "\n\n" );
}
local $_;
while( <FILE> ) {
chomp;
$self->last( new Config::Crontab::Block( -system => $self->system,
-data => $_ ) );
}
}
close FILE;
}
## this is needed for Config::Crontab::Container class methods
*elements = \&blocks;
sub blocks {
my $self = shift;
my $blocks = shift;
if( ref($blocks) eq 'ARRAY' ) {
$self->{'_blocks'} = $blocks;
}
## return only blocks (in case of accidental non-block pushing)
return grep { UNIVERSAL::isa($_, 'Config::Crontab::Block') }
grep { ref($_) } @{$self->{'_blocks'}};
}
sub select {
my $self = shift;
my @results = ();
push @results, $_->select(@_) for $self->blocks;
@results;
}
sub select_blocks {
my $self = shift;
my %crit = @_;
my @results = ();
unless( keys %crit ) {
@results = $self->blocks;
}
while( my($key, $value) = each %crit ) {
$key =~ s/^\-//; ## strip leading hyphen
if( $key eq 'index' ) {
unless( defined $value ) {
if( $self->strict ) {
carp "index value undefined\n";
}
next;
}
## a list ref of integers
if( ref($value) eq 'ARRAY' ) {
push @results, @{$self->{'_blocks'}}[@$value];
}
## an integer
elsif( $value =~ /^\d+$/ ) {
push @results, @{$self->{'_blocks'}}[$value];
}
else {
if( $self->strict ) {
carp "index value not recognized\n";
}
}
}
else {
if( $self->strict ) {
carp "Unknown block selection type '$key'\n";
}
}
}
@results;
}
sub block {
my $self = shift;
my $obj = shift
or return;
my $rblock;
BLOCK: for my $block ( $self->blocks ) {
for my $line ( $block->lines ) {
if( $line == $obj ) {
$rblock = $block;
last BLOCK;
}
}
}
return $rblock;
}
sub remove {
my $self = shift;
my @objs = @_;
if( @objs ) {
for my $obj ( @objs ) {
next unless defined $obj && ref($obj);
unless( UNIVERSAL::isa($obj, 'Config::Crontab::Block') ) {
if( $self->block($obj) ) {
$self->block($obj)->remove($obj);
}
## a non-block object in our crontab file!
else {
undef $obj;
}
next;
}
for my $block ( @{$self->{'_blocks'}} ) {
next unless defined $block && ref($block);
if( $block == $obj ) {
undef $block;
}
}
}
## strip out undefined objects
$self->blocks([ grep { defined } $self->elements ]);
}
return $self->elements;
}
## same as 'crontab -u user file'
sub write {
my $self = shift;
my $file = shift;
## see if a file is present, allow for ''
if( defined $file ) {
$self->file($file);
}
if( $self->file ) {
open CT, ">" . $self->file
or croak "Could not open " . $self->file . ": $!\n";
print CT $self->dump;
close CT;
}
## use a temporary filename
else {
my $tmpfile;
do { $tmpfile = tmpnam() }
until sysopen(CT, $tmpfile, O_RDWR|O_CREAT|O_EXCL);
print CT $self->dump;
close CT;
my $crontab;
if( my $owner = $self->owner ) {
$crontab = `crontab -u $owner $tmpfile 2>&1`;
}
else {
$crontab = `crontab $tmpfile 2>&1`;
}
chomp $crontab;
unlink $tmpfile;
if( $crontab || $? ) {
$self->error($crontab);
if( $self->strict ) {
carp "Error writing crontab (crontab exited with status " .
($? >> 8) . "): " . $self->error;
}
return;
}
}
return 1;
}
sub remove_tab {
my $self = shift;
my $file = shift;
## see if a file is present, allow for ''
if( defined $file ) {
$self->file($file);
}
if( $self->file ) {
unlink $self->file;
}
else {
my $output = '';
if( my $owner = $self->owner ) {
$output = `crontab -u $owner -r 2>&1`;
}
else {
$output = `yes | crontab -r 2>&1`;
}
chomp $output;
## FIXME: what if no $output, but only '$?' ?
if( $output || $? ) {
$self->error($output);
if( $self->strict ) {
carp "Error removing crontab (crontab exited with status " .
($? >> 8) ."): " . $self->error;
}
return;
}
}
return 1;
}
sub dump {
my $self = shift;
my $ret = '';
for my $block ( $self->blocks ) {
$ret .= "\n" if $ret && $block->dump; ## empty blocks should not invoke a newline
$ret .= $block->dump;
}
return $ret;
}
sub owner {
my $self = shift;
if( @_ ) {
my $owner = shift;
if( $owner ) {
unless( defined( getpwnam($owner) ) ) {
$self->error("Unknown user: $owner");
if( $self->strict ) {
croak $self->error;
}
return;
}
if( $owner =~ $self->owner_re ) {
$self->error("Illegal username: $owner");
if( $self->strict ) {
croak $self->error;
}
return;
}
}
$self->{_owner} = $owner;
}
return ( defined $self->{_owner} ? $self->{_owner} : '' );
}
sub owner_re {
my $self = shift;
if( @_ ) {
my $re = shift;
$self->{_owner_re} = qr($re);
}
return ( defined $self->{_owner_re} ? $self->{_owner_re} : qr() );
}
############################################################
############################################################
#line 991
## FIXME: need to say something about squeeze here, but squeeze(0)
## doesn't seem to work correctly (i.e., it still squeezes the file)
#line 1302
############################################################
############################################################
package Config::Crontab::Block;
use strict;
use warnings;
use Carp;
our @ISA = qw(Config::Crontab::Base Config::Crontab::Container);
sub init {
my $self = shift;
my %args = @_;
$self->lines([]); ## initialize
$self->strict(0);
$self->system(0);
$self->lines($args{'-lines'}) if defined $args{'-lines'};
$self->strict($args{'-strict'}) if defined $args{'-strict'};
$self->system($args{'-system'}) if defined $args{'-system'};
my $rv = 1;
if( defined $args{'-data'} ) {
$self->lines([]);
$rv = $self->data($args{'-data'});
}
return ( defined $rv ? 1 : undef );
}
sub data {
my $self = shift;
my $data = shift;
my @lines = ();
if( defined $data ) {
if( ref($data) eq 'ARRAY' ) {
@lines = @$data;
}
elsif( $data ) {
@lines = split(/\n/, $data);
}
elsif( $data eq '' ) {
@lines = ($data);
}
else {
@lines = ();
}
for my $line ( @lines ) {
my $obj;
if( $obj = new Config::Crontab::Event(-data => $line,
-system => $self->system) ) {
}
elsif( $obj = new Config::Crontab::Env(-data => $line) ) {
}
elsif( $obj = new Config::Crontab::Comment(-data => $line) ) {
}
else {
if( $self->strict ) {
carp "Skipping illegal line in block: $line\n";
}
next;
}
$self->last($obj);
}
}
my $ret = '';
for my $obj ( $self->lines ) {
$ret .= "\n" if $ret; ## empty objects are empty lines, so we do a newline always
$ret .= $obj->dump;
}
$ret .= "\n" if $ret;
return $ret;
}
## this is needed for Config::Crontab::Container class methods
*elements = \&lines;
sub lines {
my $self = shift;
my $objs = shift;
if( ref($objs) eq 'ARRAY' ) {
$self->{'_lines'} = $objs;
}
return @{$self->{'_lines'}};
}
sub select {
my $self = shift;
my %crit = @_;
## return all lines unless criteria specified
return $self->lines
unless scalar keys %crit;
my @results = ();
LINE: for my $line ( $self->lines ) {
my $j = scalar keys %crit; ## reset keys
while( my($key,$value) = each %crit ) {
$key =~ s/^\-//; ## strip leading hyphen
## FIXME: would be nice to have a negated 'type' option or a re
## special case for 'type'
if( $key eq 'type' ) {
if( $value eq 'event' ) {
next LINE unless UNIVERSAL::isa($line, 'Config::Crontab::Event');
}
elsif( $value eq 'env' ) {
next LINE unless UNIVERSAL::isa($line, 'Config::Crontab::Env');
}
elsif( $value eq 'comment' ) {
next LINE unless UNIVERSAL::isa($line, 'Config::Crontab::Comment');
}
else {
if( $self->strict ) {
carp "Unknown object type '$value'\n";
}
next LINE;
}
}
## not special 'type' case
else {
no strict 'refs';
if( $key =~ /^(.+)_re$/ ) {
next LINE unless $line->$1() =~ qr($value);
}
elsif( $key =~ /^(.+)_nre$/ ) {
next LINE unless $line->$1() !~ qr($value);
}
else {
next LINE unless $line->$key() eq $value;
}
}
}
push @results, $line;
}
return @results;
}
sub remove {
my $self = shift;
my @objs = @_;
if( @objs ) {
for my $obj ( @objs ) {
next unless defined $obj && ref($obj);
for my $line ( @{$self->{'_lines'}} ) {
next unless defined $line && ref($line);
if( $line == $obj ) {
undef $line;
}
}
}
## strip out undefined objects
$self->elements([ grep { defined } $self->elements ]);
}
return $self->elements;
}
sub active {
my $self = shift;
return 1 unless @_;
my $active = shift;
local $_;
$_->active($active) for $self->select(-type => 'env');
$_->active($active) for $self->select(-type => 'event');
return $active;
}
sub nolog {
my $self = shift;
return 1 unless @_;
my $nolog = shift;
local $_;
$_->nolog($nolog) for $self->select(-type => 'event');
return $nolog;
}
############################################################
############################################################
#line 1876
############################################################
############################################################
package Config::Crontab::Event;
use strict;
use warnings;
use Carp;
our @ISA = qw(Config::Crontab::Base);
use constant RE_DT => '(?:\d+|\*)(?:[-,\/]\d+)*';
use constant RE_DTLIST => RE_DT . '(?:,' . RE_DT . ')*';
use constant RE_DM => '\w{3}(?:,\w{3})*';
use constant RE_DTELEM => '(?:\*|' . RE_DTLIST . ')';
use constant RE_DTMOY => '(?:\*|' . RE_DTLIST . '|' . RE_DM . ')';
use constant RE_DTDOW => RE_DTMOY;
use constant RE_ACTIVE => '^\s*(\#*)\s*';
use constant RE_NOLOG => '(-?)'; ## SuSE-specific extension
use constant RE_SPECIAL => '(\@(?:reboot|midnight|(?:year|annual|month|week|dai|hour)ly))';
use constant RE_DATETIME => '(' . RE_DTELEM . ')' .
'\s+(' . RE_DTELEM . ')' .
'\s+(' . RE_DTELEM . ')' .
'\s+(' . RE_DTMOY . ')' .
'\s+(' . RE_DTDOW . ')';
use constant RE_USER => '\s+(\S+)';
use constant RE_COMMAND => '\s+(.+?)\s*$';
use constant SPECIAL => RE_ACTIVE . RE_NOLOG . RE_SPECIAL . RE_COMMAND;
use constant DATETIME => RE_ACTIVE . RE_NOLOG . RE_DATETIME . RE_COMMAND;
use constant SYS_SPECIAL => RE_ACTIVE . RE_NOLOG . RE_SPECIAL . RE_USER . RE_COMMAND;
use constant SYS_DATETIME => RE_ACTIVE . RE_NOLOG . RE_DATETIME . RE_USER . RE_COMMAND;
sub init {
my $self = shift;
my %args = @_;
my $rv = 1;
## set defaults
$self->active(1);
$self->nolog(0);
$self->system(0);
$self->special(undef);
$self->minute('*');
$self->hour('*');
$self->dom('*');
$self->month('*');
$self->dow('*');
$self->user('');
## get arguments and set new defaults
$self->system($args{'-system'}) if defined $args{'-system'}; ## -system arg overrides implicits
unless( $args{'-data'} ) {
$self->minute($args{'-minute'}) if defined $args{'-minute'};
$self->hour($args{'-hour'}) if defined $args{'-hour'};
$self->dom($args{'-dom'}) if defined $args{'-dom'};
$self->month($args{'-month'}) if defined $args{'-month'};
$self->dow($args{'-dow'}) if defined $args{'-dow'};
$self->user($args{'-user'}) if defined $args{'-user'};
$self->system(1) if defined $args{'-user'};
$self->special($args{'-special'}) if defined $args{'-special'};
$self->datetime($args{'-datetime'}) if defined $args{'-datetime'};
$self->command($args{'-command'}) if $args{'-command'};
$self->active($args{'-active'}) if defined $args{'-active'};
$self->nolog($args{'-nolog'}) if defined $args{'-nolog'};
}
$rv = $self->data($args{'-data'}) if defined $args{'-data'};
return ( defined $rv ? 1 : undef );
}
## returns the crontab line w/o '(in)?active' pound sign (#)
sub data {
my $self = shift;
my $data = '';
if( @_ ) {
$data = shift;
$data = '' unless $data; ## normalize false values
my @matches = ();
## system (user) syntax
if( $self->system ) {
if( @matches = $data =~ SYS_SPECIAL or
@matches = $data =~ SYS_DATETIME ) {
my $active = shift @matches;
my $nolog = shift @matches;
$self->active( ($active ? 0 : 1) );
$self->nolog( ($nolog ? 1 : 0) );
$self->command( pop @matches );
$self->user( pop @matches );
$self->datetime( \@matches );
}
## not a good -data value
else {
return;
}
}
## non-system (regular user crontab style) syntax
else {
## is a command
if( @matches = $data =~ SPECIAL or
@matches = $data =~ DATETIME ) {
my $active = shift @matches;
my $nolog = shift @matches;
$self->active( ($active ? 0 : 1) );
$self->nolog( ($nolog ? 1 : 0) );
$self->command( pop @matches );
$self->user('');
$self->datetime( \@matches );
}
## not a good -data value
else {
return;
}
}
}
my $fmt = "%s";
$fmt .= ( $self->command
? ( $self->system
? ($self->special ? "\t\t\t\t\t%s" : "\t%s") . ( $self->user ? "\t%s" : '' )
: " %s" )
: '' );
return sprintf($fmt, ( $self->command
? ( $self->datetime, ($self->system && $self->user ? $self->user : ()))
: () ), $self->command )
}
sub datetime {
my $self = shift;
my $data = shift;
my @matches = ();
if( $data ) {
## an array reference: when called from 'data' method
if( ref($data) eq 'ARRAY' ) {
@matches = @$data;
## likely special datetime format (e.g., @reboot, etc.)
if( scalar(@matches) == 1 ) {
$self->special( @matches );
$self->minute( '*' );
$self->hour( '*' );
$self->dom( '*' );
$self->month( '*' );
$self->dow( '*' );
}
## likely standard datetime format (e.g., '6 1 * * Fri', etc.)
elsif( scalar @matches ) {
$self->special( undef);
$self->minute( shift @matches );
$self->hour( shift @matches );
$self->dom( shift @matches );
$self->month( shift @matches );
$self->dow( shift @matches );
}
else {
## empty array ref
carp "No data in array constructor\n";
return;
}
}
## not a reference: when called as a method directly (e.g., 'init' method)
else {
## special datetime format (@reboot, @daily, etc.)
if( @matches = $data =~ RE_SPECIAL ) {
$self->special( @matches );
$self->minute( '*' );
$self->hour( '*' );
$self->dom( '*' );
$self->month( '*' );
$self->dow( '*' );
}
## standard datetime format ("0 5 * * Fri", etc.)
elsif( @matches = $data =~ RE_DATETIME ) {
$self->special( undef);
$self->minute( shift @matches );
$self->hour( shift @matches );
$self->dom( shift @matches );
$self->month( shift @matches );
$self->dow( shift @matches );
}
## not a valid datetime format
else {
## some bad data
carp "Bad datetime spec: $data\n";
return;
}
}
}
if( $self->special ) {
return $self->special;
}
my $fmt = ( $self->system
? "%s\t%s\t%s\t%s\t%s"
: "%s %s %s %s %s" );
return sprintf( $fmt, $self->minute, $self->hour, $self->dom, $self->month, $self->dow);
}
## this is duplicated in AUTOLOAD, but we need to set system also
sub user {
my $self = shift;
if( @_ ) { ## setting a value, set system too
$self->system($_[0] ? 1 : 0);
$self->{_user} = shift;
}
return ( defined $self->{_user} ? $self->{_user} : '' );
}
sub dump {
my $self = shift;
my $rv = '';
$rv .= ( $self->active
? ''
: '#' );
$rv .= ( $self->nolog
? '-'
: '' );
$rv .= $self->data;
return $rv;
}
############################################################
############################################################
#line 2580
############################################################
############################################################
## env objects are a few lines of comments followed by a variable assignment
package Config::Crontab::Env;
use strict;
use warnings;
our @ISA = qw(Config::Crontab::Base);
use constant RE_ACTIVE => '^\s*(\#*)\s*';
use constant RE_VAR => q!(["']?[^=]+?['"]?)\s*=\s*(.*)$!;
use constant RE_VARIABLE => RE_ACTIVE . RE_VAR;
sub init {
my $self = shift;
my %args = @_;
$self->active(1);
$self->active($args{'-active'}) if defined $args{'-active'};
$self->name($args{'-name'}) if $args{'-name'};
$self->value($args{'-value'}) if defined $args{'-value'};
my $rv = 1;
if( defined $args{'-data'} ) {
$rv = $self->data($args{'-data'});
}
return ( defined $rv ? 1 : undef );
}
sub data {
my $self = shift;
my $data = '';
if( @_ ) {
$data = shift;
$data = '' unless $data; ## normalize false values
my @matches = ();
if( @matches = $data =~ RE_VARIABLE ) {
my $active = shift @matches;
$self->active( ($active ? 0 : 1) );
$self->name( shift @matches );
$self->value( shift @matches );
}
## not a valid Env object
else {
return;
}
}
return ( $self->name
? $self->name . '=' . $self->value
: $self->name );
}
sub inactive {
my $self = shift;
return ( $self->active ? 0 : 1 );
}
sub dump {
my $self = shift;
my $ret = '';
if( $self->name ) {
$ret .= ( $self->active
? ''
: '#' );
}
$ret .= $self->data;
}
############################################################
############################################################
#line 2827
############################################################
############################################################
## comment objects are empty lines (lines containing only whitespace)
## or lines beginning with # and which do not match an event or
## environment pattern
package Config::Crontab::Comment;
use strict;
use warnings;
our @ISA = qw(Config::Crontab::Base);
sub init {
my $self = shift;
my %args = ( @_ == 1 ? ('-data' => @_) : @_ );
my $data = '';
if( exists $args{'-data'} ) {
$data = $args{'-data'};
}
## no '-data' tag, just the data
elsif( @_ ) {
$data = shift;
}
chomp $data if $data;
my $rv = $self->data($data);
return ( defined $rv ? 1 : undef );
}
sub data {
my $self = shift;
my $data = '';
if( @_ ) {
$data = shift;
$data = '' unless $data; ## normalize false values
unless( $data =~ /^\s*$/ || $data =~ /^\s*\#/ ) {
return;
}
$self->{'_data'} = $data;
}
return ( defined $self->{'_data'} ? $self->{'_data'} : $data );
}
############################################################
############################################################
#line 2961
############################################################
############################################################
## a virtual base class for top-level container classes
package Config::Crontab::Container;
use strict;
use warnings;
use Carp;
sub up {
my $self = shift;
my $targ = shift;
return unless ref($targ);
my @objs = $self->elements;
my $found;
for my $i ( 0..$#objs ) {
if( $objs[$i] == $targ ) {
($objs[$i], $objs[$i-1]) = ($objs[$i-1], $objs[$i]) ## swap...
unless $i == 0; ## unless already first
$found = 1;
last;
}
}
unshift @objs, $targ unless $found;
$self->elements( \@objs );
}
sub down {
my $self = shift;
my $targ = shift;
return unless ref($targ);
my @objs = $self->elements;
my $found;
for my $i ( 0..$#objs ) {
if( $objs[$i] == $targ ) {
($objs[$i], $objs[$i+1]) = ($objs[$i+1], $objs[$i]) ## swap...
unless $i == $#objs; ## unless already last
$found = 1;
last;
}
}
push @objs, $targ unless $found;
$self->elements( \@objs );
}
sub first {
my $self = shift;
my @targ = grep { ref($_) } @_;
$self->remove(@targ);
$self->elements( [@targ, $self->elements] );
}
sub last {
my $self = shift;
my @targ = grep { ref($_) } @_;
$self->remove(@targ);
$self->elements( [$self->elements, @targ] );
}
sub before {
my $self = shift;
my $ref = shift;
my @targ = @_;
$self->remove(@targ);
my @objs = ();
my $found = 0;
for my $obj ( $self->elements ) {
if( ! $found && $ref && ($obj == $ref) ) {
push @objs, @targ;
$found = 1;
}
push @objs, $obj;
}
unshift @objs, @targ unless $found;
$self->elements(\@objs);
}
sub after {
my $self = shift;
my $ref = shift;
my @targ = @_;
$self->remove(@targ);
my @objs = ();
my $found = 0;
for my $obj ( $self->elements ) {
push @objs, $obj;
if( ! $found && ($obj == $ref) ) {
push @objs, @targ;
$found = 1;
}
}
push @objs, @targ unless $found;
$self->elements(\@objs);
}
sub replace {
my $self = shift;
my $old = shift;
my $new = shift;
return unless ref($old) && ref($new);
my @objs = $self->elements;
my $found;
for my $i ( 0..$#objs ) {
if( $objs[$i] == $old ) {
$objs[$i] = $new;
$found = 1;
last;
}
}
$self->elements( \@objs );
return ( $found ? $old : undef );
}
############################################################
############################################################
## the virtual base class of all Config::Crontab classes
package Config::Crontab::Base;
use strict;
use warnings;
use Carp;
our $AUTOLOAD;
sub new {
my $self = { };
my $proto = shift;
my $class = ref($proto) || $proto;
bless $self, $class;
my $rv = $self->init(@_);
$self->flag('');
return ( $rv ? $self : undef );
}
## boolean: if returns false, 'new' will return undef, $self otherwise
sub init {
my $self = shift;
my %args = @_;
return 1;
}
sub dump {
my $self = shift;
return $self->data; ## this will AUTOLOAD if not present
}
sub flag {
my $self = shift;
$self->{'_flag'} = shift if @_;
return $self->{'_flag'};
}
sub AUTOLOAD {
my $self = shift or return;
my $sub = $AUTOLOAD;
$sub =~ s/^.*:://;
return if $sub eq 'DESTROY';
my $foni;
## new accessor
if( $sub =~ /^(\w+)$/ ) {
my $subname = $1;
$foni = sub {
my $self = shift;
$self->{"_$subname"} = shift if @_;
return ( defined $self->{"_$subname"} ? $self->{"_$subname"} : '' );
};
}
else {
croak "Undefined subroutine '$sub'";
}
## do magic
SYMBOLS: {
no strict 'refs';
*$AUTOLOAD = $foni;
}
unshift @_, $self; ## put me back on call stack
goto &$AUTOLOAD; ## jump to me
}
1;
__END__
############################################################
############################################################
#line 3314