home *** CD-ROM | disk | FTP | other *** search
/ linuxmafia.com 2016 / linuxmafia.com.tar / linuxmafia.com / pub / palmos / other-os / pad.pl < prev    next >
Perl Script  |  2000-12-06  |  15KB  |  484 lines

  1. #! /bin/sh  --  # This comment tells perl not to loop.
  2.  
  3. eval 'exec perl -S $0 ${1+"$@"}'
  4.         if 0;
  5.  
  6. # Pilot Address Dumper (or Pretty Awesome D...)
  7. # (C) Martin von Weissenberg (mvw@hut.fi), 1997
  8. # Still quite beta-stage software.  Insert standard disclaimer here.
  9. #
  10. # This script, pad, is a utility for quickly finding and displaying
  11. # addresses from an AddressDB.pdb file.  It's a read-only process, so
  12. # this utility can be rated as safe.  The AddressDB.pdb file is what
  13. # you get if you sync your USR Pilot or PalmPilot to e.g. a Unix
  14. # workstation using pilot-link, PilotManager or similar software.  If
  15. # you have neither a Pilot or a workstation, the usefulness of this
  16. # utility may be somewhat limited.
  17. #
  18. # Pad keeps a translated database in a work file in ~/.pilotmgr to
  19. # save some time.  If AddressDB.pdb is newer than the work file, we
  20. # update the work file by translating the address file again.  If
  21. # there are arguments left when all options have been parsed, we grep
  22. # through the work file and then pretty-print (?) the lines we found.
  23. #
  24. # The search terms are grouped together using either Boolean AND or
  25. # Boolean OR, depending on the command line options.  There is
  26. # currently no way of specifying "term1 AND term2 OR term3".
  27. #
  28. # The information needed to parse pdb files was taken from:
  29. #     Darrin Massena's page at http://www.massena.com/darrin/pilot/
  30. #     pilot-link.0.7.6/libsock/address.c
  31.  
  32. # LICENCE:  Martin von Weissenberg's Web page at 
  33. # http://www.hut.fi/~mweissen/pad.html states, in part, "The Pilot Address 
  34. # Dumper is a utility which will find certain keywords in an AddressDB.pdb 
  35. # file and output the matching records in several different formats. PAD was
  36. # originally written in the summer of 1997 and is now (4/2000) provided as 
  37. # it is to everyone who is interested."
  38.  
  39.  
  40. # Contributors:
  41. # Brent:    Brent Browning <Brent.Browning@Eng.Sun.COM>
  42. # Donnell:  Mark Donnell, donnell@arlut.utexas.edu
  43. # Martin:   Martin von Weissenberg, mvw@hut.fi
  44. # Mick:     Dan Mick, dan.mick@West.Sun.COM
  45.  
  46. # Changelog:
  47. # Martin  ??0797: First alpha version released.
  48. # Donnell 080897: Tests for Backup/LatestArchive/AddressDB.pdb
  49. #          before Backup/AddressDB.pdb.
  50. # Donnell 080897: To handle multi-line notes, moved Note to end.
  51. # Donnell 080897: Replaced \n with \\n & \t with \\t.
  52. # Donnell 080897: Added textual indexes (eg: $data[$labels{"Company"}]).
  53. # Donnell 080897: Added -lp & -la modes.
  54. # Donnell 080897: Added -L and PrintRecordCols.
  55. # Martin  130897: Added the $pilmgrbase variable.
  56. # Martin  200897: Changed $caseSensitivity variable from string to
  57. #          boolean.
  58. # Martin  200897: Added -a and -o options.
  59. # Martin  200897: Fixed an index bug in UpdateWorkbase which left out
  60. #          the last record.
  61. # Martin  200897: Fixed up -lz mode a lot, inserting field labels.
  62. # Martin  210897: Fixed a bug with phone labels in -lz and -ld modes
  63. # Martin  210897: Decided to go beta.
  64. # Brent   230897: Fixed a bug with @ads.
  65. # Mick    161097: Fixed the unpacking of @fileHeader
  66. # rct@ny.ubs.com 201197: Added LDIF output mode
  67. #
  68. # To do:
  69. # - Fix up the grepping stuff, it could be a lot faster using Perl
  70. #   internal regexps.  On the other hand the whole search takes less
  71. #   than one second anyway (on a SparcStation 5).
  72. # - Include the notes field.
  73. #
  74.  
  75. $pilmgrbase = $ENV{HOME} . "/.pilotmgr";
  76. $workdbpath = $pilmgrbase . "/addresses";
  77. if (-f $pilmgrbase . "/Backup/LatestArchive/AddressDB.pdb") {
  78.     $addressdbpath = $pilmgrbase .
  79.     "/Backup/LatestArchive/AddressDB.pdb";
  80. } else {
  81.     $addressdbpath = $pilmgrbase . "/Backup/AddressDB.pdb";
  82. }
  83.  
  84. $recordSep = "\n";
  85. $caseSensitivity=0;
  86. $fieldNum=22;    # = 23-1, why not 20-1 as in address.c??
  87. @phoneLabels = ("Work", "Home", "Fax", "Other", "E-mail", "Main",
  88.         "Pager", "Mobile");
  89. $mode='d';
  90. $forceUpdate=0;
  91. $printHeader=0;
  92. $booleanAnd=1;
  93.  
  94. while ($_=$ARGV[0], /^-/) {
  95.     shift @ARGV;
  96.     if (/^-d(.*)$/ && length($1)) {
  97.     $addressdbpath=$1;
  98.     next;
  99.     } elsif (/^-w(.*)$/ && length($1)) {
  100.     $workdbpath=$1;
  101.     next;
  102.     } elsif (/^-l(.*)$/ && length($1)) {
  103.         $mode=substr($1,0,1);
  104.     next;
  105.     } elsif (/^-L(.*)$/ && length($1)) {
  106.         $labelmode=$1;
  107.         next;
  108.     } elsif (/^-a$/) {
  109.         $booleanAnd = 1;
  110.     next;
  111.     } elsif (/^-o$/) {
  112.         $booleanAnd = 0;
  113.     next;
  114.     } elsif (/^-f$/) {
  115.     $forceUpdate=1;
  116.     next;
  117.     } elsif (/^-H$/) {
  118.     $printHeader=1;
  119.     next;
  120.     } elsif (/^-c$/) {
  121.     $caseSensitivity=1;
  122.     next;    
  123.     } else {
  124.     goto USAGE; # don't but me no buts about the use of goto!
  125.     }
  126. }
  127.  
  128. if ($forceUpdate) {
  129.     &UpdateWorkbase($addressdbpath, $workdbpath);
  130.     if (! $ARGV[0]) {
  131.     print (STDERR "$workdbpath successfully updated "
  132.            . "from $addressdbpath\n");
  133.     exit 0;
  134.     }
  135. }
  136.  
  137. if ($ARGV[0]) {
  138.     (@ads = stat($addressdbpath)) || die "$0: No such address "
  139.     . "database as $addressdbpath";
  140.     @wds = stat($workdbpath);
  141.     
  142.     if ($ads[9]>$wds[9]) {
  143.     &UpdateWorkbase($addressdbpath, $workdbpath);
  144.     }
  145.     open (INF, "< $workdbpath") ||
  146.     die "$0: Cannot find work database file $workdbpath\n";
  147.     $labels=<INF>;
  148.     close(INF);
  149.  
  150.     @labels = split(/\t/, $labels);
  151.     for ($i=0; $i<$#labels; $i++) {
  152.     $labels{"$labels[$i]"} = $i;
  153.     }
  154. # $labels = "Last name    First name    ..." 
  155. # @labels: $labels[0] = "Last name"; $labels[1] = "First name"; ...
  156. # %labels: $labels{"Last name"} = 0; $labels{"First name"} = 1; ...
  157. # $labelcolumn will contain indexes of columns to print based on
  158. #         -L"cols ..."
  159.     if ($labelmode) {
  160.     @labelmode = split(/,/, $labelmode . ",XXXX");
  161. # doesn't work w/o extra entry (?)
  162.     # need to pick which columns these are & get their col nums
  163.     for ($i=0; $i<=$#labelmode; $i++) {
  164.         $labelcolumn[$i] = -1;
  165.         for ($j=0; $j<=$#labels; $j++) {
  166.         if ($labels[$j] =~ /^$labelmode[$i]/) {
  167.             $labelcolumn[$i] = $j;
  168.             last;
  169.         }
  170.         }
  171.         #$labelcolumn[$i] = $labels{$labelmode[$i]};
  172.         #$labelcolumn[$i] = -1 if (($labelcolumn[$i]==0)
  173.         #&& ($labelmode[$i] ne $labels[0]));
  174.     }
  175.     }
  176.     if ($labelmode) {    &PrintRecordCols($labels) if ($printHeader); }
  177.     else {        &PrintRecord($labels, $mode) if ($printHeader); }
  178.  
  179.     $cs = $caseSensitivity ? '' : '-i';
  180. # Now pick the lines to be printed.
  181. #
  182. # If $booleanAnd is true, all terms must match for each line.  Adding
  183. # one grep after the other is the easy way to do it.  I'd like to
  184. # merge the terms into one regexp, but I'm not able to do it.
  185. #
  186.     if ($booleanAnd) {
  187.     $query = "grep $cs $ARGV[0] $workdbpath |";
  188.     shift;
  189.     while ($keyword=$ARGV[0]) {
  190.         $query .= "grep $cs $keyword |";
  191.         shift;
  192.     }
  193.     open (INF, $query);
  194.     while ($line=<INF>) {
  195.         if ($labelmode) {    &PrintRecordCols($line); }
  196.         else         {    &PrintRecord($line, $mode); }
  197.     }
  198.     close (INF);
  199.     } else {
  200. #
  201. # Boolean OR: any matching term will do.
  202. #
  203. # This is also done the easy and inefficient way.  The terms should of
  204. # course be merged into one regexp, but how?
  205. #
  206.     while ($keyword=$ARGV[0]) {
  207.         shift;
  208.         open (INF, "grep $cs $keyword $workdbpath |");
  209.         while ($line=<INF>) {
  210.         if ($labelmode) {    &PrintRecordCols($line); }
  211.         else         {    &PrintRecord($line, $mode); }
  212.         }
  213.         close (INF);
  214.     }
  215.     }
  216. } else {
  217. #
  218. # Print a short manual.  I skipped the \t format in favour of
  219. # pre-formatted ascii.
  220. #
  221. USAGE:
  222.     print <<EOF;
  223. Usage: $0 term1 [term2 ...]
  224. Options:
  225.   -l[paz]  List options: default is to print only current phone
  226.            p = phone#, a = address, z = all, l = LDIF (netscape)
  227.   -L"label,label,label,..." = print these labeled columns (partial names OK)
  228.            eg: =L"Last,First,Note"
  229.   -a       AND: All terms must match for the record to be printed (default)
  230.   -o       OR: The record is printed if there is at least one matching term
  231.   -c       Case sensitivity on (default: off)
  232.   -f       Force work file update (default: off)
  233.   -H       Print header line (default: off)
  234.   -dPATH   The address pdb file to use.
  235.   -wPATH   The work file to use.
  236. EOF
  237. exit 1;
  238. }
  239. exit 0;
  240.  
  241. #
  242. # PrintRecordCols prints the specified columns of the specified record.
  243. # Unfortunately the formatting is not too pretty right now
  244. #
  245. sub PrintRecordCols {
  246.     my ($line) = @_;
  247.     my ($i);
  248.  
  249.     $line =~ s/\\n/\n/go;
  250.     @data = split(/\t/, $line);
  251.     for ($i=0; $i<$#data; $i++) {$data[$i] =~ s/\\t/\t/go;}
  252.  
  253.     $whph = (ord($data[$labels{"Labels"}]) - ord('0')); # first digit
  254.     $whph = 0 if ($whph > 4 || $whph < 0);
  255.     $phndx = $whph + $labels{"Work"}; # should be first phone field
  256.  
  257.     for ($i=0; $i<$#labelcolumn; $i++) {
  258.     printf "%s\t", $data[$labelcolumn[$i]] if
  259.         ($labelcolumn[$i] > -1);
  260.     }
  261.     print "\n";
  262. }
  263.  
  264. #
  265. # PrintRecord prints the specified record using the labels and mode.
  266. # Data field $labels{"Labels"} = 21 contains phone field labels in a
  267. # packed format.
  268. #
  269. sub PrintRecord {
  270.     local ($line, $mode) = @_;
  271.     my ($i);
  272.  
  273.     $line =~ s/\\n/\n/go;
  274.     @data = split(/\t/, $line);
  275.     for ($i=0; $i<$#data; $i++) {$data[$i] =~ s/\\t/\t/go;}
  276.  
  277.     #$whph = (ord($data[$labels{"Labels"}]) - ord('0')); # first digit
  278.     #$whph = 0 if ($whph > 4 || $whph < 0);
  279.     #$whlbl = ord(substr($data[$labels{"Labels"}],$whph+1,1)) - ord('0');
  280.     #$phndx = $whph + $labels{"Work"}; # should be first phone field
  281.     ##    $phoneLabels[$whlbl] . ": " . $data[$whph + 3]
  282.  
  283.     if ($mode eq 'd' || $mode eq 'p') {
  284.     $whph = (ord($data[$labels{"Labels"}]) - ord('0')); # first digit
  285.     $whph = 0 if ($whph > 4 || $whph < 0);
  286. #    $phndx = $whph + $labels{"Work"}; # should be first phone field
  287.     $phndx = 3;
  288.     printf (STDOUT  "%-24s\t ",
  289.         ($data[$labels{"Last name"}]
  290.          ? $data[$labels{"Last name"}] . ", " .
  291.          $data[$labels{"First name"}] 
  292.          : $data[$labels{"Company"}]));
  293.     for ($i=0; $i<5; $i++) {
  294.         if ($data[$phndx+$i] ne "") {
  295.         $whlbl = ord(substr($data[$labels{"Labels"}],$i+1,1))
  296.             - ord('0');
  297.         printf (STDOUT "%s\t", substr($phoneLabels[$whlbl],0,1)
  298.             . ":" . $data[$phndx+$i]); 
  299.         }
  300.     }
  301.     print (STDOUT "\n");
  302.  
  303.     }
  304.     elsif ($mode eq 'a') {
  305.     printf (STDOUT  "%-30s\t %s%s%s%s%s\n",
  306.         ($data[$labels{"Last name"}] . ", "
  307.          . $data[$labels{"First name"}]) .
  308.         ($data[$labels{"Company"}] ? ", "
  309.          . $data[$labels{"Company"}] : "") .
  310.         ($data[$labels{"Title"}] ? ", "
  311.          . $data[$labels{"Title"}] : ""),
  312.         ($data[$labels{"Address"}] ? ", "
  313.          . $data[$labels{"Address"}] : ""),
  314.         ($data[$labels{"City"}] ? ", "
  315.          . $data[$labels{"City"}] : ""),
  316.         ($data[$labels{"State"}] ? ", "
  317.          . $data[$labels{"State"}] : ""),
  318.         ($data[$labels{"Zip Code"}] ? ", "
  319.          . $data[$labels{"Zip Code"}] : ""),
  320.         ($data[$labels{"Country"}] ? ", "
  321.          . $data[$labels{"Country"}] : "")
  322.         );
  323.     } 
  324.     elsif ($mode eq 'z') {
  325.     print "----------------------\n";
  326.     for ($i=0; $i<$fieldNum-1; $i++) {
  327.         if ($data[$i]) {
  328.         $lbl = $labels[$i];
  329.         if ($i>=3 && $i<=7) { # fix the phone labels
  330.             $lbl = $phoneLabels[(ord(substr($data[$labels{"Labels"}],
  331.                             $i-2,1)) - ord('0'))];
  332.         }
  333.         printf(STDOUT "%-15s:  %s\n", $lbl, $data[$i]);
  334.         }
  335.     }
  336. #print "$line----------------\n";
  337.     }
  338.     elsif ($mode eq 'l') {
  339.     %tmp = ();
  340.     for ($i=0; $i<$fieldNum-1; $i++) {
  341.         if ($data[$i]) {
  342.         $lbl = $labels[$i];
  343.         # print $lbl,"\n";
  344.         if ($i>=3 && $i<=7) { # fix the phone labels
  345.             $lbl = $phoneLabels[(ord(substr($data[$labels{"Labels"}],
  346.                             $i-2,1)) - ord('0'))];
  347.         }
  348.         $tmp{$lbl}=$data[$i];
  349.         # print $lbl,"-\n";
  350.         }
  351.     };
  352.     if ($tmp{"E-mail"} && $tmp{"Last name"} && $tmp{"First name"}) {
  353.         print "dn: cn=$tmp{'First name'} $tmp{'Last name'}, $tmp{'E-mail'}\n";
  354.         print "cn: $tmp{'Last name'}, $tmp{'First name'}\n";
  355.         print "sn: $tmp{'Last name'}\n";
  356.         print "givenname: $tmp{'First name'}\n";
  357.         print "objectclass: top\n";
  358.         print "objectclass: person\n";
  359.         print "locality: $tmp{'City'}\n" if ($tmp{'City'});
  360.         print "st: $tmp{'State'}\n" if ($tmp{'State'});
  361.         print "mail: $tmp{'E-mail'}\n" if ($tmp{'E-mail'});
  362.         print "o: $tmp{'Company'}\n" if ($tmp{'Company'});
  363.         print "title: $tmp{'Title'}\n" if ($tmp{'Title'});
  364.         print "telephonenumber: $tmp{'Work'}\n" if ($tmp{'Work'});
  365.         print "facsimiletelephonenumber: $tmp{'Fax'}\n" if ($tmp{'Fax'});
  366.         print "streetaddress: $tmp{'Address'}\n"
  367.         if ($tmp{'Address'});
  368.         print "postalcode: $tmp{'Zip Code'}\n" if ($tmp{'Zip Code'});
  369.         print "countryname: $tmp{'Country'}\n" if ($tmp{'Country'});
  370.         
  371.         print "\n";
  372.     }
  373.     } else {
  374.     die "$0: Invalid mode specification.";
  375.     }
  376. }
  377.  
  378. #
  379. # UpdateWorkbase parses the specified AddressDB file to a
  380. # tab-delimited file specified by $wdb.  The last field in each record
  381. # contains the phone number field labels in a packed format.
  382. #
  383. sub UpdateWorkbase {
  384.     local ($adb,$wdb)=@_;
  385.     
  386.     @foo = stat($adb);
  387.  
  388.     open (ADB, "<" . $adb) ||
  389.     die "$0: Cannot find the AddressDB.pdb file\n";
  390.     if (-f $wdb) {
  391.     system("mv -f $wdb $wdb.bak");
  392.     }
  393.     unless (open (WDB, ">" . $wdb)) {
  394.     system("mv -f $wdb.bak $wdb");
  395.     die "$0: Cannot open the work data file $wdb\n";
  396.     }
  397.  
  398.     read (ADB, $packedHeader, 78);
  399.     @fileHeader = unpack("A32 a28 a8 a8 n", $packedHeader);
  400.     $fileHeader[0] =~ s/\0.*//;
  401.     $name = $fileHeader[0];
  402.     $fileHeader[2] =~ s/\0.*//;
  403.     $typecrea = $fileHeader[2];
  404.     $numRecords = $fileHeader[4];
  405.  
  406.     if (($typecrea ne 'DATAaddr') || # The type and creator...
  407.     ($name ne 'AddressDB')) { # ...and the name must match.
  408.     system("mv -f $wdb.bak $wdb");
  409.     die "$0: File $adb is not an address database\n";
  410.     }
  411.     for ($i=0; $i<$numRecords; $i++) { # read in record offsets
  412.     read ADB, $d, 8;
  413.     $offset[$i] = unpack ("N x4", $d);
  414. #    print (STDERR $offset[$i]);
  415.     }
  416.     $offset[$numRecords] = $foo[7]; # EOF location
  417.  
  418. # skip all category stuff, it can be implemented later if necessary
  419.     read ADB, $d, 284;
  420.  
  421.     $noteField=-1;
  422.     for ($i=0; $i<$fieldNum; $i++) {    # read in field labels
  423.     read ADB, $labels[$i], 16;
  424.     $idx = index($labels[$i], "\0");
  425.     $labels[$i] = substr($labels[$i], 0, $idx);
  426.     if ($labels[$i] eq "Note") {
  427.       $noteField = $i;
  428.     }
  429.     else {
  430.       print (WDB $labels[$i] . "\t");
  431.     }
  432.     
  433.     $labels{$labels[$i]} = $i - ($noteField < 0 ? 0 : 1);
  434.     }
  435.     print (WDB "Labels\t$labels[$noteField]\n");
  436.     $labels{"Labels"} = $fieldNum;
  437.     $labels{"$labels[$noteField]"} = $noteField;
  438.  
  439.     read ADB, $d, 4;        # skip stuff
  440.  
  441.     for ($i=0; $i<$numRecords; $i++) {
  442. # The following seek command could be commented out.
  443.     seek ADB, $offset[$i], 0;
  444.     read ADB, $d, 9;
  445.     @rawRec = unpack("C C C C C C C C C", $d);
  446.     $whichPh = ($rawRec[1] & 0xF0)>>4;# which phone nr is the default
  447.     $phLbl[4] = ($rawRec[1] & 0x0F);
  448.     $phLbl[3] = ($rawRec[2] & 0xF0)>>4;
  449.     $phLbl[2] = ($rawRec[2] & 0x0F);
  450.     $phLbl[1] = ($rawRec[3] & 0xF0)>>4;
  451.     $phLbl[0] = ($rawRec[3] & 0x0F);
  452.     $contents =  ($rawRec[5] * (1 << 16))
  453.         + ($rawRec[6] * (1 << 8)) + ($rawRec[7]);
  454.  
  455.     read ADB, $d, ($offset[$i+1] - $offset[$i]) - 9;
  456.  
  457.     $note = "";
  458.     for ($j=0; $j<$fieldNum; $j++) {
  459.         if ($contents & (1 << $j)) {
  460.         $idx = index($d, "\0");
  461.         $data[$j] = substr($d, 0, $idx);
  462.         $d = substr($d, length($data[$j])+1);
  463.         } else {
  464.         $data[$j] = "";
  465.         }            # 
  466.         $data[$j] =~ s/\r/, /go;
  467.         $data[$j] =~ s/\n/\\n/go;
  468.         $data[$j] =~ s/\t/\\t/go;
  469.         $data[$j] =~ s/\s$//og;
  470.         if ($j == $noteField) {
  471.           $note = $data[$j];
  472.         }
  473.         else {
  474.           print (WDB $data[$j] . "\t");
  475.         }
  476.     }
  477.     print (WDB join("",$whichPh,@phLbl));
  478.     $note = "\\n" . $note if ($note ne "");
  479.     print (WDB "\t$note$recordSep");
  480.     }
  481.     close (WDB);
  482.     close (ADB);
  483. }
  484.