+#
+# The shorter version that sets $ignoreStderr = 0, ie: merges stdout
+# and stderr together.
+#
+sub cmdSystemOrEval
+{
+ my($bpc, $cmd, $stdoutCB, @args) = @_;
+
+ return $bpc->cmdSystemOrEvalLong($cmd, $stdoutCB, 0, undef, @args);
+}
+
+#
+# Promotes $conf->{BackupFilesOnly}, $conf->{BackupFilesExclude}
+# to hashes and $conf->{$shareName} to an array.
+#
+sub backupFileConfFix
+{
+ my($bpc, $conf, $shareName) = @_;
+
+ $conf->{$shareName} = [ $conf->{$shareName} ]
+ if ( ref($conf->{$shareName}) ne "ARRAY" );
+ foreach my $param qw(BackupFilesOnly BackupFilesExclude) {
+ next if ( !defined($conf->{$param}) );
+ if ( ref($conf->{$param}) eq "HASH" ) {
+ #
+ # A "*" entry means wildcard - it is the default for
+ # all shares. Replicate the "*" entry for all shares,
+ # but still allow override of specific entries.
+ #
+ next if ( !defined($conf->{$param}{"*"}) );
+ $conf->{$param} = {
+ map({ $_ => $conf->{$param}{"*"} }
+ @{$conf->{$shareName}}),
+ %{$conf->{$param}}
+ };
+ } else {
+ $conf->{$param} = [ $conf->{$param} ]
+ if ( ref($conf->{$param}) ne "ARRAY" );
+ $conf->{$param} = { map { $_ => $conf->{$param} }
+ @{$conf->{$shareName}} };
+ }
+ }
+}
+
+#
+# This is sort() compare function, used below.
+#
+# New client LOG names are LOG.MMYYYY. Old style names are
+# LOG, LOG.0, LOG.1 etc. Sort them so new names are
+# first, and newest to oldest.
+#
+sub compareLOGName
+{
+ my $na = $1 if ( $a =~ /LOG\.(\d+)(\.z)?$/ );
+ my $nb = $1 if ( $b =~ /LOG\.(\d+)(\.z)?$/ );
+
+ $na = -1 if ( !defined($na) );
+ $nb = -1 if ( !defined($nb) );
+
+ if ( length($na) >= 5 && length($nb) >= 5 ) {
+ #
+ # Both new style: format is MMYYYY. Bigger dates are
+ # more recent.
+ #
+ my $ma = $2 * 12 + $1 if ( $na =~ /(\d+)(\d{4})/ );
+ my $mb = $2 * 12 + $1 if ( $nb =~ /(\d+)(\d{4})/ );
+ return $mb - $ma;
+ } elsif ( length($na) >= 5 && length($nb) < 5 ) {
+ return -1;
+ } elsif ( length($na) < 5 && length($nb) >= 5 ) {
+ return 1;
+ } else {
+ #
+ # Both old style. Smaller numbers are more recent.
+ #
+ return $na - $nb;
+ }
+}
+
+#
+# Returns list of paths to a clients's (or main) LOG files,
+# most recent first.
+#
+sub sortedPCLogFiles
+{
+ my($bpc, $host) = @_;
+
+ my(@files, $dir);
+
+ if ( $host ne "" ) {
+ $dir = "$bpc->{TopDir}/pc/$host";
+ } else {
+ $dir = "$bpc->{LogDir}";
+ }
+ if ( opendir(DIR, $dir) ) {
+ foreach my $file ( readdir(DIR) ) {
+ next if ( !-f "$dir/$file" );
+ next if ( $file ne "LOG" && $file !~ /^LOG\.\d/ );
+ push(@files, "$dir/$file");
+ }
+ closedir(DIR);
+ }
+ return sort compareLOGName @files;
+}
+
+#
+# converts a glob-style pattern into a perl regular expression.
+#
+sub glob2re
+{
+ my ( $bpc, $glob ) = @_;
+ my ( $char, $subst );
+
+ # $escapeChars escapes characters with no special glob meaning but
+ # have meaning in regexps.
+ my $escapeChars = [ '.', '/', ];
+
+ # $charMap is where we implement the special meaning of glob
+ # patterns and translate them to regexps.
+ my $charMap = {
+ '?' => '[^/]',
+ '*' => '[^/]*', };
+
+ # multiple forward slashes are equivalent to one slash. We should
+ # never have to use this.
+ $glob =~ s/\/+/\//;
+
+ foreach $char (@$escapeChars) {
+ $glob =~ s/\Q$char\E/\\$char/g;
+ }
+
+ while ( ( $char, $subst ) = each(%$charMap) ) {
+ $glob =~ s/(?<!\\)\Q$char\E/$subst/g;
+ }
+
+ return $glob;
+}
+