#!/usr/bin/perl my $VERSION = 1.0; # # Convert Palm Address datatbase to Yahoo CSV format. # # Install this file as padb2csv and csv2padb. # # For documentation use pod2text/pod2man on this file or # run it with '-h' arument. # # Copyright (C) 2001 by Alexander Kolbasov # # This program is free software; you can redistribute it and/or # modify it under the terms of the Artistic License, a copy of which # can be found with the Perl distribution. # # This code is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # Artistic License for more details. # # $Id: padb2csv,v 1.6 2001/12/13 18:48:55 akolb Exp $ use strict; use File::Basename; use Getopt::Std; use Palm::PDB; use Palm::Address; use Text::CSV_XS; my $padb2csv = 'padb2csv'; my $csv2padb = 'csv2padb'; my $p2c; my $default_db = 'AddressDB.pdb'; my $padb2csv_usage = < 2; my $verbose = 1 if $opts{v}; my $overwrite = 1 if $opts{o}; my $match = $opts{m}; my $except = $opts{e}; # Get input file my $input = ($argc ? $ARGV[0] : ($p2c ? $default_db : '-')); my $output = ($argc == 2 ? $ARGV[1] : ($p2c ? '-' : $default_db)); # Check output file for overwrites. die "File $output already exist, use -o flag if want to overwrite $output\n" if ($output ne '-' && ! $overwrite && stat $output); exit ($p2c ? palm2csv($input, $output): csv2palm($input, $output)); sub palm2csv { my ($input, $output) = @_; my $OUT; my ($work, $home, $fax, $other, $mail, $main, $pager, $mobile); my ($work1, $home1, $fax1, $other1, $mail1, $main1, $pager1, $mobile1); my ($work_p, $home_p, $fax_p, $other_p, $mail_p, $main_p, $pager_p, $mobile_p); my @cats; my $i = 0; my $pdb = new Palm::PDB; my $csv = new Text::CSV_XS; if ($output eq '-') { $OUT = \*STDOUT; } else { open (F, "> $output") || die "can't open $output for writing"; $OUT = \*F; } # Read Palm database $pdb->Load($input); # Find indices for various mail types. for (@Palm::Address::phoneLabels) { $work = $i if /Work/; $home = $i if /Home/; $fax = $i if /Fax/; $other = $i if /Other/; $mail = $i if /mail/; $main = $i if /Main/; $pager = $i if /Pager/; $mobile = $i if /Mobile/; $i++;; } my @categories = @{$pdb->{appinfo}{categories}}; # Build array of categories indexed by category ID. for my $c (@categories) { $cats[$c->{id}] = $c->{name}; } print $OUT <{records}}) { my @fl; my $status; my $category = $cats[$r->{category}]; # Find what phoneN means for ($i = 1; $i < 6; $i++) { $work1 = $i if ($r->{phoneLabel}{"phone$i"} == $work); $home1 = $i if ($r->{phoneLabel}{"phone$i"} == $home); $fax1 = $i if ($r->{phoneLabel}{"phone$i"} == $fax); $other1 = $i if ($r->{phoneLabel}{"phone$i"} == $other); $mail1 = $i if ($r->{phoneLabel}{"phone$i"} == $mail); $main1 = $i if ($r->{phoneLabel}{"phone$i"} == $main); $pager1 = $i if ($r->{phoneLabel}{"phone$i"} == $pager); $mobile1 = $i if ($r->{phoneLabel}{"phone$i"} == $mobile); } # Figure out various phones $work_p = $r->{fields}{"phone$work1"} if $work1; $home_p = $r->{fields}{"phone$home1"} if $home1; $fax_p = $r->{fields}{"phone$fax1"} if $fax1; $other_p = $r->{fields}{"phone$other1"} if $other1; $mail_p = $r->{fields}{"phone$mail1"} if $mail1; $main_p = $r->{fields}{"phone$work1"} if $main1; $pager_p = $r->{fields}{"phone$pager1"} if $pager1; $mobile_p = $r->{fields}{"phone$mobile1"} if $mobile1; # # Construct list of fields required by Yahoo CSV. # Push all field values in the right order to a single list and then # use csv->combine() method to convert the list into a CSV record. # push @fl, $r->{fields}{firstName}; # First push @fl, ''; # Middle push @fl, $r->{fields}{name}; # Last push @fl, ''; # Nickname push @fl, $mail_p; # Mail push @fl, $category; # Category push @fl, ''; # Distribution push @fl, ''; # Yahoo Id push @fl, $home_p; # Home push @fl, $work_p; # Work push @fl, $pager_p; # Pager push @fl, $fax_p; # Fax push @fl, $mobile_p; # Mobile push @fl, $other_p; # Other push @fl, ''; # Yahoo phone push @fl, $main_p; # Primary push @fl, ''; # Alt e-mail 1 push @fl, ''; # Alt e-mail 2 push @fl, ''; # Personal website push @fl, ''; # Business website push @fl, $r->{fields}{title}; # Title push @fl, $r->{fields}{company};# Company push @fl, ''; # Work Address push @fl, ''; # Work City push @fl, ''; # Work State push @fl, ''; # Work ZIP push @fl, ''; # Work COuntry push @fl, $r->{fields}{address};# Home addr push @fl, $r->{fields}{city}; # Home City push @fl, $r->{fields}{state}; # Home State push @fl, $r->{fields}{zipCode};# Home Zip push @fl, $r->{fields}{country};# Home country push @fl, ''; # Birthday push @fl, ''; # Anniversary push @fl, $r->{fields}{custom1};# Custom1 push @fl, $r->{fields}{custom2};# Custom2 push @fl, $r->{fields}{custom3};# Custom3 push @fl, $r->{fields}{custom4};# Custom4 push @fl, $r->{fields}{note}; # Comments $status = $csv->combine (@fl); if (!$status) { my $err = $csv->error_input; print STDERR "parse() failed on argument: ", $err, "\n"; } else { $_ = $csv->string; # Filter out record if required next if $match && ! /$match/; next if $except && /$except/; # Write CSV record print $OUT "$_\n"; print STDERR "$r->{fields}{firstName} $r->{fields}{name}\n" if $verbose; } } return 0; } sub csv2palm { my ($input, $output) = @_; my $CSV; my $pdb = new Palm::Address; my $csv = new Text::CSV_XS; my %categories = (Business => 1, Personal => 1, QuickList => 1); my %phones = (Work => 0, Home => 1, Fax => 2, Other => 3, Email => 4, 'Personal Website' => 5, Pager => 6, Mobile => 7); my %phones_prim = (work => 'Work', home => 'Home', fax => 'Fax', other => 'Other', email => 'Email', pager => 'Pager', mobile => 'Mobile'); # Map Pilot fields names to Yahoo field names. my %fields_map = (name => 'Last', firstName => 'First', company => 'Company', address => 'Home Address', city => 'Home City', state => 'Home State', zipCode => 'Home ZIP', country => 'Home Country', title => 'Title', custom1 => 'Custom 1', custom2 => 'Custom 2', custom3 => 'Custom 3', custom4 => 'Custom 4', note => 'Comments'); my $primary; my @csv_phones = qw(Home Work Mobile Email Pager Fax Other); push @csv_phones, 'Personal Website'; my @columns; my %fields; # Indices of each field hashed by field name my $i; my ($work_p, $home_p, $fax_p, $other_p, $mail_p, $main_p, $pager_p, $mobile_p); # # List of existing categories. Always create standard Palm categories. # New categories are added to this list. # my %cats = (Unfiled => 0, Busines => 1, Personal => 2, QuickList => 3); my $cat_id = 4; # Get input file descriptor. if ($input eq '-') { $CSV = \*STDOUT; } else { open (F, "< $input") || die "can't open $input for reading"; $CSV = \*F; } # Read and parse the header. my $header = <$CSV>; $csv->parse($header) or die "Bad header \"$header\", can't proceed!\n"; @columns = $csv->fields(); # Get field names and record index of each field into %fields. for my $f (@columns) { $fields{$f} = $i++; } # Add standard categories. $pdb->addCategory('Business'); $pdb->addCategory('Personal'); $pdb->addCategory('QuickList'); print STDERR "Saving database to $output....\n" if $verbose; # Process each record while (<$CSV>) { my @palm_phones = qw(); my @palm_types = qw(); # Filter records next if $match && ! /$match/; next if $except && /$except/; if (! $csv->parse($_)) { print STDERR "can't parse string [$_]\n"; } else { @columns = $csv->fields(); # Create new address record my $r = $pdb->new_Record; # Get category information. my $cat = $columns[$fields{'Category'}]; if (($cat ne 'Unfiled') && $categories{$cat} != 1) { # Add this category to the list of categories. $categories{$cat} = 1; $pdb->addCategory ($cat, $cat_id); $cats{$cat} = $cat_id++; } # Specify record category. $r->{category} = $cats{$cat}; # Deal with primary phone. $primary = $columns[$fields{'Primary'}]; $primary = $phones_prim{$primary}; if ($primary && $columns[$fields{$primary}]) { push @palm_phones, $columns[$fields{$primary}]; push @palm_types, $phones{$primary}; } for my $p (@csv_phones) { my $phone = $columns[$fields{$p}]; if ($phone && ($p ne $primary)) { push @palm_phones, $phone; push @palm_types, $phones{$p}; } } # Assign phones to 5 available slots. my $nphones = @palm_phones; $nphones = 5 if $nphones > 5; for ($i = 1; $i <= $nphones; $i++) { $r->{phoneLabel}{"phone$i"} = $palm_types[$i - 1]; $r->{fields}{"phone$i"} = $palm_phones[$i - 1]; } # # Specify other fields # for my $k (keys %fields_map) { $r->{fields}{$k} = $columns[$fields{$fields_map{$k}}]; } # Add record to the database $pdb->append_Record($r); print STDERR "$r->{fields}{firstName} $r->{fields}{name}\n" if $verbose; } # Save new database $pdb->Write ($output); } return 0; } # # POD section. # =head1 NAME padb2csv, csv2padb - Convert between Palm Address datatbase and Yahoo! CSV format. =head1 SYNOPSYS padb2csv [B<-v>] [B<-h>] [B<-o>] [B<-m> expr] [B<-e> expr] [input_file [output_file]] csv2padb [B<-v>] [B<-h>] [B<-o>] [B<-m> expr] [B<-e> expr] [input_file [output_file]] =head1 OPTIONS -h: print help -v: be verbose:print names as they are added. -o: override output file -e expr: Drop records matching expr -m expr: Only process records matching expr =head2 csv2padb: input_file: Yahoo CSV formatted address file. '-' means use I. output_file: Palm .pdb file. Use F if no output_file is gven. If no files are given, read from stdin and write to F. =head2 padb2csv: input_file: Palm .pdb Address database. Use F by default. output_file: Yahoo CSV formatted file. '-' means write to I. If no files are given, read from F and write to stdout. =head1 DESCRIPTION Convert to/from Palm Pilot Adress book database format file and Yahoo! Address Book CSV format.It creates a whole address book database that you can download directly into your Palm-compatible device, overwriting existing address database. You may use desktop tools like I to view the resulting database before you actually download or sync it. Always keep backups in case something goes wrong. If you have some addresses in your PDA and some addresses in your Yahoo address book, you may manually merge them by uploading your converted database to Yahoo and then putting all of your Yahoo database into PDA. You may select which records to convert using filtering options - they match all fields of the record. If you create new entries on your PDA, you may create a special category for them (e.g. C) and then extract only these records: padb2csv -v -e New AddressDB.pdb Yahoo.csv =head1 CAVEATS There is no 1-to-1 mapping between Yahoo fields and Palm fields. Yahoo address book includes much more information, so if you convert your data to .pdb format and back you may loose some of it. The best way is to keep your master database on Yahoo and periodycally sync it with PDA. As an alternative, you may only use Yahoo fields that have corresponding PDA fields. If you run padb2csv or csv2padb without arguments it will try to quietly create an output database. =head1 SEE ALSO L, L, L. =head1 README This is csv2padb and padb2csv - tools for conversion between Palm Address book database and Yahoo! CSV format. Install this file as padb2csv and csv2padb in your PATH. For documentation use pod2text/pod2man on padb2csv or run it with '-h' arument. Copyright (C) 2001 by Alexander Kolbasov This program is free software; you can redistribute it and/or modify it under the terms of the Artistic License, a copy of which can be found with the Perl distribution. This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Artistic License for more details. =head1 PREREQUISITES This script requires the following modules: C, C, C, C, C, C. =head1 AUTHOR Alexander Kolbasov =head1 BUGS This program may die if something goes wrong due to the way Palm::PDB works. It inherits all the bugs and limitations of Palm::PDB and Text::CSV_XS. In particular, it can not accept a multi-line CSV entry. =pod OSNAMES any (Only tested on Unix). =pod SCRIPT CATEGORIES Mail =cut