package DBIx::dbMan::Extension::MacroWorks;
use strict;
use base 'DBIx::dbMan::Extension';
use Text::FormatTable;
use File::Spec;
our $VERSION = '0.05';
1;
sub IDENTIFICATION { return "000001-000086-000005"; }
sub preference { return 0; }
sub known_actions { return [ qw/MACRO/ ]; }
sub menu {
my $obj = shift;
return ( { label => 'Input', submenu => [
{ label => 'Macros', submenu => [
{ label => 'Show',
action => { action => 'MACRO', operation => 'show' } },
{ label => 'Clear',
action => { action => 'MACRO', operation => 'clear' } },
{ label => 'Reload',
action => { action => 'KEYS', operation => 'reload' } }
] } ] } );
}
sub macrofile {
my $obj = shift;
return $ENV{DBMAN_MACROFILE} if $ENV{DBMAN_MACROFILE};
return $obj->{-config}->macro_file if $obj->{-config}->macro_file;
return $ENV{HOME}.'/.dbman/macros';
}
sub load_macros {
my $obj = shift;
my @macros = ();
my @files = ();
if ( -d $obj->macrofile ) {
if (opendir D, $obj->macrofile) {
while (my $file = readdir D) {
next if $file=~ /^\./;
push @files, File::Spec->catfile( $obj->macrofile, $file );
}
closedir D;
}
} else {
push @files, $obj->macrofile;
}
for my $file (@files) {
if (open F, $file) {
if ( $obj->{ -interface }->is_utf8 ) {
binmode F, ':utf8';
}
while (<F>) {
chomp;
push @macros,$_ if m#^s/(.+)/(.*)/[gei]?#;
}
close F;
}
}
$obj->{-mempool}->set(macros => \@macros);
}
sub clear_macros {
my $obj = shift;
$obj->{-mempool}->set(macros => []);
}
sub init {
my $obj = shift;
$obj->load_macros;
}
sub done {
my $obj = shift;
$obj->clear_macros;
}
sub handle_action {
my ($obj,%action) = @_;
if ($action{action} eq 'MACRO') {
if ($action{operation} eq 'clear') {
$obj->clear_macros;
my $add = '';
$add = 'permanently ' if ($action{permanent} and unlink $obj->macrofile);
$action{action} = 'OUTPUT';
$action{output} = "Macro definitions $add"."cleared.\n";
} elsif ($action{operation} eq 'reload') {
$obj->load_macros;
my $count = @{$obj->{-mempool}->get('macros')};
$action{action} = 'OUTPUT';
$action{output} = "Macro definitions reloaded ($count substitution".($count == 1?'':'s').").\n";
} elsif ($action{operation} eq 'show') {
my @list = @{$obj->{-mempool}->get('macros')};
$action{action} = 'OUTPUT';
unless (@list) {
$action{output} = "No macro definition yet.\n";
} else {
my $table = new Text::FormatTable '| l | l | l |';
$table->rule;
$table->head('MACRO','SUBSTITUTION','FLAGS');
$table->rule;
for my $macro (sort @list) {
$macro =~ s/^s\///;
my $flags = '';
$flags = $1 if $macro =~ s#/([ige])?$##;
my $name = '';
$name = $1 if $macro =~ s#^(.+?)\$?(?!\\)/##;
$name =~ s/^\^//;
$table->row($name,$macro,$flags);
}
$table->rule;
$action{output} = $table->render($obj->{-interface}->render_size);
}
} elsif ($action{operation} eq 'define') {
my $def = $action{macro};
$action{action} = 'OUTPUT';
if ($def !~ m#^s/(.+)/(.*)/[gei]?#) {
$action{output} = "Invalid substitution definition.\n";
} else {
my @macros = @{$obj->{-mempool}->get('macros')};
push @macros,$def;
$obj->{-mempool}->set(macros => \@macros);
my $addenum = '';
if (open F,">>".$obj->macrofile) {
if ( $obj->{ -interface }->is_utf8 ) {
binmode F, ':utf8';
}
print F "$def\n";
close F;
} else {
$addenum = ", but not permanently";
}
$action{output} = "New macro substitution defined$addenum.\n";
}
} elsif ($action{operation} eq 'undefine') {
my $def = $action{macro};
my @macros = @{$obj->{-mempool}->get('macros')};
my $i = 0;
my @clearlist = ();
for (@macros) {
my $name = '';
s#/([ige])?$##;
$name = $1 if m#^s/\^?(.+?)\$?(?!\\)/#;
push @clearlist,$i if ($name and $name eq $def);
++$i;
}
$action{action} = 'OUTPUT';
if (@clearlist) {
delete $macros[$_] for reverse sort @clearlist;
$obj->{-mempool}->set(macros => \@macros);
if (open F,$obj->macrofile) {
if ( $obj->{ -interface }->is_utf8 ) {
binmode F, ':utf8';
}
my @all = <F>;
close F;
if (open F,">".$obj->macrofile) {
if ( $obj->{ -interface }->is_utf8 ) {
binmode F, ':utf8';
}
for my $line (@all) {
chomp $line;
my $test = $line;
my $name = '';
$test =~ s#/([ige])?$##;
$name = $1 if $test =~ m#^s/(.+)(?!\\)/#;
print F "$line\n" if ($name and $name ne $def);
}
close F;
}
}
$action{output} = "Macro named $def undefined.\n";
} else {
$action{output} = "No macro named $def found.\n";
}
}
}
$action{processed} = 1;
return %action;
}
sub cmdcomplete {
my ($obj,$text,$line,$start) = @_;
my @macros = @{$obj->{-mempool}->get('macros')};
return () unless @macros;
my @names = ();
for (@macros) {
s#^s/##;
s#/([ige])?$##;
s#^(.+)(?!\\)/.*$#$1#;
push @names,$_ if $_;
}
my @result = ();
for my $name (@names) {
$name =~ s/\\s[+*]?/ /g;
$name =~ s/^\^//;
$name =~ s/\$$//;
my @words = ();
for (split /\s+/,$name) {
if (/^[-a-z0-9_\\]+$/i) {
push @words,$_;
} else {
last;
}
}
if (@words) {
if ($line =~ /^\s*[A-Z]*$/i) {
push @result,$words[0];
} else {
my $saved = pop @words;
while (@words) {
$name = '$line =~ /^\s*'.join('\\s+',@words).'\s+\S*$/i';
push @result,$saved if eval $name;
$saved = pop @words;
}
}
}
}
return @result;
}