#!/usr/bin/perl -w if (not $ARGV[0] or $ARGV[0] =~ /^-(h|-help)$/ or not -f $ARGV[0]) { print <<'EOF'; # script to transform Vcards from PeopleFinder into a CSV file suitable for # import into JPilot. I don't know how far it can transform other Vcard # formats!? # Synopsis: # $0 [list of vcard files...] # e.g.: vcf2jp *.vcf # The resulting file will be 'jpVCF.csv' in the current directory. # (search for @@@ in the source code for sections you might need to adapt # to your needs and the configuration of your Palm) EOF exit } use strict; use IO::File; require Text::CSV_XS; sub readvcf($); my $tgtcsv = Text::CSV_XS->new({ 'binary' => 1, 'eol' => "\012", 'always_quote' => 1 }); my $tgtfile = new IO::File "jpVCF.csv", "w" || die; print $tgtfile "CSV address: Category, Private, Last, First, Title, Company, Phone1, Phone2, Phone3, Phone4, Phone5, Address, City, State, ZipCode, Country, Custom1, Custom2, Custom3, Custom4, Note, phoneLabel1, phoneLabel2, phoneLabel3, phoneLabel4, phoneLabel5, showPhone\n"; while ($ARGV[0] and -f $ARGV[0]) { my $vcfile = shift; print $vcfile,"\n"; my $vcard = readvcf($vcfile); $vcard = vchash2array($vcard); $tgtcsv->print($tgtfile, src2tgt($vcard) ) || die; } sub readvcf($) { my $vcfh = new IO::File shift, "r" || die; my %vchash; while (<$vcfh>) { chomp($_); s/ $//; # need to chop DOS EOL. if (/^([A-Z;,=-]+):(.+)$/) { my $key = $1; my $value = $2; if ($vchash{$key}) { push(@{$vchash{$key}}, split(/;/,$value)); } else { $vchash{$key} = [ split(/;/,$value) ]; } } } $vcfh->close(); foreach (keys %vchash) { print $_, "\t"; if (ref($vchash{$_}) eq 'ARRAY') { print @{$vchash{$_}}; } else { print $vchash{$_}; } print "\n"; } return \%vchash; } sub vchash2array { # @@@ this is very specific to the format of the vcard my $href = shift; my @src = qw/Last First Company Department WorkPhone OtherPhone Mobile Fax Address City State ZipCode Country Email WebPage Note/; $src[0] = ($href->{'N'}->[0] or ""); # Last $src[1] = ($href->{'N'}->[1] or ""); # First $src[2] = ($href->{'ORG'}->[0] or ""); # Company $src[3] = ($href->{'ORG'}->[1] or ""); # Department $src[4] = ($href->{'TEL;WORK;VOICE'}->[0] or ""); # WorkPhone $src[5] = ($href->{'TEL;WORK;VOICE'}->[1] or ""); # OtherPhone $src[6] = ($href->{'TEL;CELL;VOICE'}->[0] or ""); # Mobile $src[7] = ($href->{'TEL;WORK;FAX'}->[0] or ""); # Fax $src[8] = ($href->{'ADR;INTL;WORK;POSTAL'}->[0] or ""); # Address foreach ($href->{'ADR;INTL;WORK;POSTAL'}->[1] ,$href->{'ADR;INTL;WORK;POSTAL'}->[2]) { if ($src[8] and $_) { $_ =~ s/ *\$ */\n/g; $src[8] .= "\n" . $_; } } $src[9] = ($href->{'ADR;INTL;WORK;POSTAL'}->[3] or ""); # City $src[10] = ($href->{'ADR;INTL;WORK;POSTAL'}->[4] or ""); # State $src[11] = ($href->{'ADR;INTL;WORK;POSTAL'}->[5] or ""); # ZipCode $src[12] = ($href->{'ADR;INTL;WORK;POSTAL'}->[6] or ""); # Country $src[13] = ($href->{'EMAIL;PREF,INTERNET'}->[0] or ""); # Email $src[14] = ($href->{'URL;WORK'}->[0] or ""); # WebPage $src[15] = ($href->{'NOTE;ENCODING=QUOTED-PRINTABLE'}->[0] or ""); # WebPage $src[15] =~ s/=0D=0A/\n/g; print @src,"\n"; return \@src; } sub src2tgt { my $mysrc=shift; my @tgt = qw/Category Private Last First Title Company Phone1 Phone2 Phone3 Phone4 Phone5 Address City State ZipCode Country Custom1 Custom2 Custom3 Custom4 Note phoneLabel1 phoneLabel2 phoneLabel3 phoneLabel4 phoneLabel5 showPhone/; # @@@ That's specific to my Palm, change it for you... my %map = ( Custom1 => 'Birthday', Custom2 => 'Department', Custom3 => 'NickName', Custom4 => 'WebPage' ); @tgt = map { $map{$_} or $_ } @tgt; my %tgt; # difference in the array between a phone and it's label my $phone = 6; my $label = 21; my $maxphone = 5; my @tgtlabels = qw/WorkPhone HomePhone Fax OtherPhone Email CompanyPhone Pager Mobile/; my %tgtlabels; # The source array must have header names matching the target array # for the translation to work properly my @src = qw/Last First Company Department WorkPhone OtherPhone Mobile Fax Address City State ZipCode Country Email WebPage Note/; my (%src,$src); # we transform the source and target arrays in hashes, # makes things clearer. for(my $i=0;$i<@src;$i++) { $src{$src[$i]} = $mysrc->[$i]; } for(my $i=0;$i<@tgt;$i++) { $tgt{$tgt[$i]} = ""; } for(my $i=0;$i<@tgtlabels;$i++) { $tgtlabels{$tgtlabels[$i]} = $i; } ## Now let's do the translation... It's all automatic if headers are ## correctly set... my $currphone = 0; foreach $src (keys %src) { if (defined $tgt{$src}) { $tgt{$src} = $src{$src}; } elsif (defined $tgtlabels{$src}) { if ($currphone < $maxphone) { $tgt{$tgt[$phone+$currphone]} = $src{$src}; $tgt{$tgt[$label+$currphone]} = $tgtlabels{$src}; $currphone++; } } } # the following fields are required (@@@my own little default values): $tgt{"Category"} = "HP-VCF"; $tgt{"Private"} = "0"; $tgt{"showPhone"} = "0"; for(my $i=$currphone; $i < $maxphone; $i++) { $tgt{$tgt[$label+$i]} = "0"; } ## and we transform back the target array... for(my $i=0;$i<@tgt;$i++) { $tgt[$i] = $tgt{$tgt[$i]}; } return \@tgt; }