+#
+# Do variable substitution prior to execution of a command.
+#
+sub cmdVarSubstitute
+{
+ my($bpc, $template, $vars) = @_;
+ my(@cmd);
+
+ #
+ # Return without any substitution if the first entry starts with "&",
+ # indicating this is perl code.
+ #
+ if ( (ref($template) eq "ARRAY" ? $template->[0] : $template) =~ /^\&/ ) {
+ return $template;
+ }
+ $template = [split(/\s+/, $template)] if ( ref($template) ne "ARRAY" );
+ #
+ # Merge variables into @tarClientCmd
+ #
+ foreach my $arg ( @$template ) {
+ #
+ # Replace scalar variables first
+ #
+ $arg =~ s{\$(\w+)(\+?)}{
+ defined($vars->{$1}) && ref($vars->{$1}) ne "ARRAY"
+ ? ($2 eq "+" ? $bpc->shellEscape($vars->{$1}) : $vars->{$1})
+ : "\$$1"
+ }eg;
+ #
+ # Now replicate any array arguments; this just works for just one
+ # array var in each argument.
+ #
+ if ( $arg =~ m{(.*)\$(\w+)(\+?)(.*)} && ref($vars->{$2}) eq "ARRAY" ) {
+ my $pre = $1;
+ my $var = $2;
+ my $esc = $3;
+ my $post = $4;
+ foreach my $v ( @{$vars->{$var}} ) {
+ $v = $bpc->shellEscape($v) if ( $esc eq "+" );
+ push(@cmd, "$pre$v$post");
+ }
+ } else {
+ push(@cmd, $arg);
+ }
+ }
+ return \@cmd;
+}
+
+#
+# Exec or eval a command. $cmd is either a string on an array ref.
+#
+# @args are optional arguments for the eval() case; they are not used
+# for exec().
+#
+sub cmdExecOrEval
+{
+ my($bpc, $cmd, @args) = @_;
+
+ if ( (ref($cmd) eq "ARRAY" ? $cmd->[0] : $cmd) =~ /^\&/ ) {
+ $cmd = join(" ", $cmd) if ( ref($cmd) eq "ARRAY" );
+ eval($cmd)
+ } else {
+ $cmd = [split(/\s+/, $cmd)] if ( ref($cmd) ne "ARRAY" );
+ exec(@$cmd);
+ }
+}
+
+#
+# System or eval a command. $cmd is either a string on an array ref.
+# $stdoutCB is a callback for output generated by the command. If it
+# is undef then output is returned. If it is a code ref then the function
+# is called with each piece of output as an argument. If it is a scalar
+# ref the output is appended to this variable.
+#
+# @args are optional arguments for the eval() case; they are not used
+# for system().
+#
+# Also, $? should be set when the CHILD pipe is closed.
+#
+sub cmdSystemOrEval
+{
+ my($bpc, $cmd, $stdoutCB, @args) = @_;
+ my($pid, $out);
+ local(*CHILD);
+
+ if ( (ref($cmd) eq "ARRAY" ? $cmd->[0] : $cmd) =~ /^\&/ ) {
+ $cmd = join(" ", $cmd) if ( ref($cmd) eq "ARRAY" );
+ my $out = eval($cmd);
+ $$stdoutCB .= $out if ( ref($stdoutCB) eq 'SCALAR' );
+ &$stdoutCB($out) if ( ref($stdoutCB) eq 'CODE' );
+ return $out if ( !defined($stdoutCB) );
+ return;
+ } else {
+ $cmd = [split(/\s+/, $cmd)] if ( ref($cmd) ne "ARRAY" );
+ if ( !defined($pid = open(CHILD, "-|")) ) {
+ my $err = "Can't fork to run @$cmd\n";
+ $? = 1;
+ $$stdoutCB .= $err if ( ref($stdoutCB) eq 'SCALAR' );
+ &$stdoutCB($err) if ( ref($stdoutCB) eq 'CODE' );
+ return $err if ( !defined($stdoutCB) );
+ return;
+ }
+ if ( !$pid ) {
+ #
+ # This is the child
+ #
+ close(STDERR);
+ open(STDERR, ">&STDOUT");
+ exec(@$cmd);
+ }
+ #
+ # The parent gathers the output from the child
+ #
+ while ( <CHILD> ) {
+ $$stdoutCB .= $_ if ( ref($stdoutCB) eq 'SCALAR' );
+ &$stdoutCB($_) if ( ref($stdoutCB) eq 'CODE' );
+ $out .= $_ if ( !defined($stdoutCB) );
+ }
+ $? = 0;
+ close(CHILD);
+ }
+ return $out;
+}
+