#!/usr/bin/perl #=========================================================================== # # LedgerSMB Command-line script host # # # LedgerSMB # Small Medium Business Accounting software # http://www.ledgersmb.org/ # # # Copyright (C) 2006 # This work contains copyrighted information from a number of sources all used # with permission. It is released under the GNU General Public License # Version 2 or, at your option, any later version. See COPYRIGHT file for # details. # # This is a simple wrapper that allows you to write simple scripts with LSMB # See sample for the file format. # # THIS IS EXPERIMENTAL AND THE INTERFACE IS SUBJECT TO CHANGE use Parse::RecDescent; use LedgerSMB::User; use LedgerSMB::Form; use LedgerSMB::Sysconfig; $form = new Form; $syntax = << '_END_SYNTAX_'; KEY : /\w[a-z0-9_]*/i MODSTR : /\w[a-z0-9_:]*/i FNKEY : /(?:\w|:|\-\>)+/ OP : m([-+*/%]) NUMBER : /[+-]?\d*\.?\d+/ ARGS : /(\w[a-z0-9_]*,?\s?)+/i expression : NUMBER OP expression { return main::expression(@item) } | KEY OP expression { return main::expression(@item) } | NUMBER | KEY assign_instruction : KEY "=" expression { main::assignval($item{KEY}, $item{expression}) } call_and_assign : /call/i FNKEY '(' ARGS ')' /into/i KEY { main::call_and_assign($item{FNKEY}, $item{ARGSTR}, $item{KEY}) } FUNCTIONCALL : /call/i FNKEY '(' ARGS ')' { main::call($item{FNKEY}, $item{ARGS});} for : /for/i KEY { main::push_loop($item{KEY}) } done : /^\s*done\s*$/ { main::pop_loop() } if : /^\s*if/i KEY { main::if_handler($item{KEY}) } # IF is terminated by END IF or FI on its own line login : /login/i { main::login() } module : /module/i MODSTR { main::load_mod($item{MODSTR}) } instruction : assign_instruction | call_and_assign | FUNCTIONCALL | for | done | if | login | module startrule : instruction _END_SYNTAX_ $::RD_HINT = 1; $::RD_ERRORS = 1; # Make sure the parser dies when it encounters an error $::RD_WARN = 1; # Enable warnings. This will warn on unused rules &c.` #$::RD_TRACE = 1; my @loopstack; my $loopindex; my $stackref; my @control_stack; push @loopstack, $form; sub assignval { my ( $key, $value ) = @_; if ( $key =~ /^ENV:/i ) { $ENV{$key} = $value; } else { %{ $loopstack[ $#loopstack - 1 ] }->{$key} = $value; } } sub expression { shift; my ( $lhs, $op, $rhs ) = @_; $lhs = $VARIABLE{$lhs} if $lhs =~ /[^-+0-9]/; return eval "$lhs $op $rhs"; } sub call { my ( $call, $argstr ) = @_; $argstr =~ s/form/\\\%\$form/; $argstr =~ s/user/\\\%myconfig/; my @args = split /,\s/, $argstr; eval "$call($argstr);\n" || print STDERR $@ . "\n"; } sub call_and_assign { my $key = pop; $stackref->{key} = call(@_); } sub push_loop { my $key = shift; my $is_hash = 0; if ( ref( $stackref->{$key} ) =~ /HASH/ ) { $is_hash = 1; } elsif ( ref( $stackref->{$key} ) !~ /ARRAY/ ) { print STDERR "Warning: Must loop through array or hash."; } push @loopstack, \$stackref->{$key}; push @controlstack, { "key" => $key, 'index' => 0, 'linenum' => $#linestack, is_hash => $is_hash }; } sub pop_loop { pop @loopstack; $stackref = \$loopstack[$#loopstack]; } sub if_handler { my $key = shift; if ( !$stackref->{$key} ) { $if_count = 1; } } sub login { $myconfig = new LedgerSMB::User "${LedgerSMB::Sysconfig::memberfile}", "$form->{login}"; $form->db_init($myconfig); } sub load_mod { my $mod = shift; $mod =~ s/::/\//; require "$mod.pm"; } my $scriptparse = new Parse::RecDescent($syntax); $loopindex = 0; my @linestack; while ( $line = <> ) { push @linestack, $line; if ($if_count) { if ( $line =~ /^\s*IF\s/ ) { ++$if_count; } if ( $line =~ /^(\s*FI\s*|\s*END\s+IF\s*)$/ ) { --$if_count; } } next if ($if_count); $line =~ s/#.*$//; # strip comments $scriptparse->startrule($line); } delete $form->{password}; for ( keys %$form ) { print "$_ = $form->{$_}\n"; }