AddressBook

 view release on metacpan or  search on metacpan

lib/AddressBook/DB/BBDB.pm  view on Meta::CPAN

END

my $notes_pat = <<END;
(?:$nil_or_string_pat|           # Might be nil or just a string
\\(                              # An open (
   $alist_pat+                   # and at least one Alist
\\)                              # And a closed )
)
END

my $bbdb_entry_pat = <<END;
\\[                              # Opening vector [
$nil_or_string_pat   \\          # First name
$nil_or_string_pat   \\          # Last name
($aka_pat) \\                    # Also Known As
$nil_or_string_pat   \\          # Company name
($phone_pat) \\                  # Phone list
($address_pat) \\                # Address list
($net_pat)   \\                  # Net names list
($notes_pat) \\                  # Notes Alist
(?:nil\\ *)+                     # Always nil as far as I can tell
\\]                              # Closing vector ]
END

########################################################################
# I added some ()s inside the patterns above, to make it possible to 
# break out the sub fields of a bbdb record.  Once consequence of this 
# is to make it very difficult to figure out at what position the top
# level fields are, so the subroutine _figure_out_indices does exactly
# that.  It uses the sample data below, and searches the fields that are 
# matched by the $bbdb_entry_pat pattern above.  The results are stored
# in the %field_index hash so we can reference them by name, and perhaps
# change the ()s in the patterns above without breaking everything.
########################################################################

my @field_names = 
qw (first last aka company phone address net notes);
my %field_names;
@field_names{@field_names} = (0..$#field_names);

my $sample_data = <<END;
["first" "last" ("aka") "company" (["phone with integer" 123 456 789] ["phone with quotes" "123-456-789"]) (["address" "street1" "street2" "street3" "city" "state" ("zip")]) ("net") ((notes . "data")) nil]
END

my %field_index;
sub _figure_out_indices {
  my @fields = ($sample_data =~ m/^$bbdb_entry_pat$/ox);
  my @names = @field_names;
  my $i;
  for ($i=0; $i < @fields; $i++) {
    if ($fields[$i] =~ $names[0]) {
      $field_index{shift @names} = $i;
      last unless @names;
    }
  }
}
_figure_out_indices();

########################################################################

sub un_escape {
  my $s = shift;
  $s =~ s/\\(.)/$1/g;               # should just be " or \
  return $s;
}

########################################################################

sub decode {
  my ($self,$str) = @_;
  my @fields = ();
  unless (@fields = ($str =~ m/^$bbdb_entry_pat$/ox)) {
    if ($BBDB::debug) {
      my $pat = '';
      my @subpats = (
		     [ '\[', 'opening ['],
		     [ $nil_or_string_pat, 'First name'],
		     [ $nil_or_string_pat, 'Last name'],
		     [ $aka_pat, 'Also known as'],
		     [ $nil_or_string_pat, 'Company name'],
		     [ $phone_pat, 'Phone'],
		     [ $address_pat, 'Address'],
		     [ $net_pat, 'Net names' ],
		     [ $notes_pat, 'Notes'],
		     [ 'nil', 'Last nil'],
		     [ '\]', 'closing ]']
		    );
      my $i;
      foreach $i (@subpats) {
	$pat .= $i->[0];
	printf STDERR "No match at %s\n", $i->[1] and last unless
	  $str =~ m/^$pat/x;
	$pat .= '\ ' unless $i->[0] eq '\[' or $i->[0] eq 'nil' ;
      }
    }
    return undef;
  }


  my $i;
  local($_);

  foreach $i (@field_names) {
    $fields[$field_index{$i}] = ''
      if (!defined $fields[$field_index{$i}] or 
	  $fields[$field_index{$i}] eq 'nil');
  }

  my @aka = split(/$quoted_string_pat/ox,$fields[$field_index{aka}]);
  #    print "AKA=\n<",join(">\n<",@aka),">\nEND AKA\n";
  my $aka = [];
  for ($i=0; $i < @aka - 1; $i+=2) {
    push @$aka, un_escape($aka[$i+1]);
  }

  my @phone = split(/$single_phone_pat/ox,$fields[$field_index{phone}]);
  #    print "PHONE=\n<",join(">\n<",@phone),">\nEND PHONE\n";
  my $phone = [];
    for ($i=0; $i < @phone - 1; $i+=5) {
      push @$phone,[
		    un_escape($phone[$i+1]),
		     un_escape(defined $phone[$i+3] ? 
			       $phone[$i+3] : $phone[$i+4])
		   ];
    }

  my @address = split(/$single_address_pat/ox,$fields[$field_index{address}]);
  #    print "ADDRESS=\n<",join(">\n<",@address),">\nEND ADDRESS\n";
  my $address = [];
    for ($i=0; $i < @address - 1; $i+=9) {
      my $zip = $address[$i+7];
      $zip =~ s/^\((.*)\)$/$1/;   # remove ()
      if (defined $address[$i+8]) {  # we have quoted strings
	my @zip = split(/$quoted_string_pat/ox,$zip);
	#   print "ZIP = \n<",join(">\n<",@zip),">\nEND ZIP\n";
	$zip = join('',@zip);
      }
      push @$address,[
		      un_escape($address[$i+1]),
		      un_escape($address[$i+2]),
		      un_escape($address[$i+3]),
		      un_escape($address[$i+4]),
		      un_escape($address[$i+5]),
		      un_escape($address[$i+6]),
		      $zip
		     ];
    }

  my @net = split(/$quoted_string_pat/ox,$fields[$field_index{net}]);
  #    print "NET=\n<",join(">\n<",@net),">\nEND NET\n";
  my $net = [];
  for ($i=0; $i < @net - 1; $i+=2) {
    push @$net, un_escape($net[$i+1]);
  }


  my @notes = split(/$alist_pat/ox,$fields[$field_index{notes}]);
  #    print "NOTES=\n<",join(">\n<",@notes),">\nEND NOTES\n";
  my $notes = [];
  for ($i=0; $i < @notes - 1; $i+=4) {
    push @$notes, [
		   $notes[$i+2],
		   un_escape($notes[$i+3])
		  ]
  }

  $self->{'data'} =  [
	  un_escape($fields[$field_index{first}]),
	  un_escape($fields[$field_index{last}]),
	  $aka,
	  un_escape($fields[$field_index{company}]),
	  $phone,
	  $address,
	  $net,
	  $notes
	 ];
  return 1;
}

########################################################################

sub quoted_stringify {               # escape \ and " in a string
  my $s = shift;                     # and return it surrounded by
  $s =~ s/(\\|")/\\$1/g;             # quotes
  return "\"$s\"";
}

sub nil_or_string {                  # return nil if empty string
  return 'nil' if $_[0] eq '';       # otherwise quote it
  return quoted_stringify(@_);
}

sub nil_or_list {                    # return nil if empty string
  return 'nil' if $_[0] eq '';       # otherwise quote it and add ()s
  return '(' . quoted_stringify(@_) . ')' ;
}

########################################################################

sub encode {
  my $self = shift;
  my ($first, $last, $aka, $company, 
      $phone, $address, $net, $notes) = @{$self->{'data'}};
  my ($i,@result,$s);
  push @result,nil_or_string($first);
  push @result,nil_or_string($last);

  if (@$aka) {
    my @aka;
    foreach $i (@$aka) {
      push @aka, quoted_stringify($i);
    }
    push @result, "(@aka)";
  } else {
    push @result, 'nil';
  }

  push @result,nil_or_string($company);

  if (@$phone) {
    my @phone;
    foreach $i (@$phone) {
      my $number;
      if ( $i->[1] =~ m/^\D?(\d{3})\D(\d{3})\D+(\d{4})\D?(\d*)$/ ) {
	$number = "$1 $2 $3 ";
	$number .= $4 ? $4 : '0';
      } else {
	$number = quoted_stringify($i->[1]);
      }
      push @phone,"[" . quoted_stringify($i->[0]) . " $number]";
      ;
    }
    push @result, "(@phone)";
  } else {
    push @result, 'nil';
  }
  if (@$address) {
    my @address;
    foreach $i (@$address) {
      my $zip = $i->[6];
      if ($zip =~ m/^(\d{5})\D?(\d{4})?$/) {
	$zip = $1;



( run in 0.471 second using v1.01-cache-2.11-cpan-e1769b4cff6 )