+
+# does it look like a qra locator?
+sub is_qra
+{
+ return unless length $_[0] == 4 || length $_[0] == 6;
+ return $_[0] =~ /^[A-Ra-r][A-Ra-r]\d\d(?:[A-Xa-x][A-Xa-x])?$/;
+}
+
+# does it look like a valid lat/long
+sub is_latlong
+{
+ return $_[0] =~ /^\s*\d{1,2}\s+\d{1,2}\s*[NnSs]\s+1?\d{1,2}\s+\d{1,2}\s*[EeWw]\s*$/;
+}
+
+# is it an ip address?
+sub is_ipaddr
+{
+ return $_[0] =~ /^\d+\.\d+\.\d+\.\d+$/ || $_[0] =~ /^[0-9a-f:,]+$/;
+}
+
+# insert an item into a list if it isn't already there returns 1 if there 0 if not
+sub insertitem
+{
+ my $list = shift;
+ my $item = shift;
+
+ return 1 if grep {$_ eq $item } @$list;
+ push @$list, $item;
+ return 0;
+}
+
+# delete an item from a list if it is there returns no deleted
+sub deleteitem
+{
+ my $list = shift;
+ my $item = shift;
+ my $n = @$list;
+
+ @$list = grep {$_ ne $item } @$list;
+ return $n - @$list;
+}
+
+# find the correct local_data directory
+# basically, if there is a local_data directory with this filename and it is younger than the
+# equivalent one in the (system) data directory then return that name rather than the system one
+sub localdata
+{
+ my $ifn = shift;
+ my $ofn = "$main::data/$ifn";
+ my $tfn;
+
+ if (-e "$main::local_data") {
+ $tfn = "$main::local_data/$ifn";
+ if (-e $tfn && -e $ofn) {
+ $ofn = $tfn if -M $tfn < -M $ofn;
+ } elsif (-e $tfn) {
+ $ofn = $tfn;
+ }
+ }
+
+ return $ofn;
+}
+
+# move a file or a directory from data -> local_data if isn't there already
+sub localdata_mv
+{
+ my $ifn = shift;
+ if (-e "$main::data/$ifn" ) {
+ unless (-e "$main::local_data/$ifn") {
+ move("$main::data/$ifn", "$main::local_data/$ifn") or die "localdata_mv: cannot move $ifn from '$main::data' -> '$main::local_data' $!\n";
+ }
+ }
+}
+
+# measure the time taken for something to happen; use Time::HiRes qw(gettimeofday tv_interval);
+sub _diffms
+{
+ my $ta = shift;
+ my $tb = shift || [gettimeofday];
+ my $a = int($ta->[0] * 1000) + int($ta->[1] / 1000);
+ my $b = int($tb->[0] * 1000) + int($tb->[1] / 1000);
+ return $b - $a;
+}
+
+sub diffms
+{
+ my $call = shift;
+ my $line = shift;
+ my $ta = shift;
+ my $no = shift;
+ my $tb = shift;
+ my $msecs = _diffms($ta, $tb);
+
+ $line =~ s|\s+$||;
+ my $s = "subprocess stats cmd: '$line' $call ${msecs}mS";
+ $s .= " $no lines" if $no;
+ DXDebug::dbg($s);
+}