+ if ($fh) {
+ my $dd = new Data::Dumper([ $obj ]);
+ $dd->Indent(1);
+ $dd->Terse(1);
+ $dd->Quotekeys(0);
+ # $fh->print(@_) if @_ > 0; # any header comments, lines etc
+ $fh->print($dd->Dumpxs);
+ $fh->close;
+ }
+}
+
+sub filecopy
+{
+ copy(@_) or return $!;
+}
+
+# remove leading and trailing spaces from an input string
+sub unpad
+{
+ my $s = shift;
+ $s =~ s/\s+$//;
+ $s =~ s/^\s+//;
+ return $s;
+}
+
+# check that a field only has callsign characters in it
+sub is_callsign
+{
+ return $_[0] =~ m!^(?:[A-Z]{1,2}\d+ | \d[A-Z]{1,2}\d+) # basic prefix
+ (?:/(?:[A-Z]{1,2}\d+ | \d[A-Z]{1,2}\d+))? # / another one (possibly)
+ [A-Z]{1,3} # callsign letters
+ (?:/(?:[A-Z]{1,2}\d+ | \d[A-Z]{1,2}\d+))? # / another prefix possibly
+ (?:/[0-9A-Z]{1,2})? # /0-9A-Z+ possibly
+ (?:-\d{1,2})? # - nn possibly
+ $!x;
+}
+
+# check that a PC protocol field is valid text
+sub is_pctext
+{
+ return undef unless length $_[0];
+ return undef if $_[0] =~ /[\x00-\x08\x0a-\x1f\x80-\x9f]/;
+ return 1;
+}
+
+# check that a PC prot flag is fairly valid (doesn't check the difference between 1/0 and */-)
+sub is_pcflag
+{
+ return $_[0] =~ /^[01\*\-]+$/;
+}
+
+# check that a thing is a frequency
+sub is_freq
+{
+ return $_[0] =~ /^\d+(?:\.\d+)?$/;
+}
+
+# check that a thing is just digits
+sub is_digits
+{
+ return $_[0] =~ /^[\d]+$/;
+}
+
+# does it look like a qra locator?
+sub is_qra
+{
+ 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*$/;
+}
+
+# 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;