X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=utils%2Fperlbug.PL;h=78c3b428a1b9dac12146bfff922cf3d5be56d25f;hb=9ef5ed94af316c852dd085d2cbe8306867ac870b;hp=0e923159ed42d09b3bab6f1293bafbda5533f918;hpb=4d5692de8d33e3f52bd85bef7bcdbdffa4ac80e5;p=p5sagit%2Fp5-mst-13.2.git diff --git a/utils/perlbug.PL b/utils/perlbug.PL index 0e92315..78c3b42 100644 --- a/utils/perlbug.PL +++ b/utils/perlbug.PL @@ -40,6 +40,7 @@ if (! defined($_)) { my @patches; while () { last if /^\s*}/; + next if /^\s*#/; # preprocessor stuff chomp; s/^\s+,?\s*"?//; s/"?\s*,?$//; @@ -81,6 +82,8 @@ my \@patches = ( print OUT <<'!NO!SUBS!'; +use warnings; +no warnings 'once'; # Eventually, the $::opt_ stuff should get cleaned up use strict; use Config; use File::Spec; # keep perlbug Perl 5.005 compatible @@ -101,7 +104,7 @@ BEGIN { $::HaveCoreList = ($@ eq ""); }; -my $Version = "1.37"; +my $Version = "1.39"; # Changed in 1.06 to skip Mail::Send and Mail::Util if not available. # Changed in 1.07 to see more sendmail execs, and added pipe output. @@ -142,17 +145,31 @@ my $Version = "1.37"; # Changed in 1.34 Added Message-Id RFOLEY 18-06-2002 # Changed in 1.35 Use File::Temp (patch from Solar Designer) NWCLARK 28-02-2004 # Changed in 1.36 Initial Module::CoreList support Alexandr Ciornii 11-07-2007 -# Changed in 1.37 Killed some string evals, rewrote most prose JESSE 06-08-2008 -# -# TODO: - Allow the user to re-name the file on mail failure, and -# make sure failure (transmission-wise) of Mail::Send is -# accounted for. +# Changed in 1.37 Killed some string evals, rewrote most prose JESSE 2008-06-08 +# Changed in 1.38 Actually enforce the CoreList check, +# Record the module the user enters if they do so +# Refactor prompts to use common code JESSE 2008-06-08 +# Changed in 1.39 Trap mail sending failures (simple ones) so JESSE 2008-06-08 +# users might be able to recover their bug reports +# Refactor mail sending routines +# Unify message building code +# Unify message header building +# Fix "module" prompting to not squish "category" prompting +# use warnings; (except 'once' warnings) +# Unified report fingerprint/change detection code +# Removed some labeled 'gotos' +#TODO: +# make sure failure (transmission-wise) of Mail::Send is accounted for. +# (This may work now. Unsure of the original author's issue -JESSE 2008-06-08) # - Test -b option my( $file, $usefile, $cc, $address, $bugaddress, $testaddress, $thanksaddress, $filename, $messageid, $domain, $subject, $from, $verbose, $ed, $outfile, - $Is_MacOS, $category, $severity, $fh, $me, $Is_MSWin32, $Is_Linux, $Is_VMS, - $msg, $body, $andcc, %REP, $ok, $thanks, $Is_OpenBSD, $progname); + $fh, $me, $body, $andcc, %REP, $ok, $thanks, $progname, + $Is_MacOS, $Is_MSWin32, $Is_Linux, $Is_VMS, $Is_OpenBSD, + $report_about_module, $category, $severity, + +); my $perl_version = $^V ? sprintf("%vd", $^V) : $]; @@ -173,7 +190,16 @@ EOF Query(); Edit() unless $usefile || ($ok and not $::opt_n); NowWhat(); -Send(); +if ($outfile) { + save_message_to_disk($outfile); +} else { + Send(); + if ($thanks) { + print "\nThank you for taking the time to send a thank-you message!\n\n"; + } else { + print "\nThank you for taking the time to file a bug report!\n\n"; + } +} exit; @@ -194,30 +220,26 @@ sub ask_for_alternatives { # (category|severity) 'opts' => [qw(critical high medium low wishlist none)], # zero }, ); - die "Invalid alternative($name) requested\n" unless grep(/^$name$/, keys %alts); + die "Invalid alternative ($name) requested\n" unless grep(/^$name$/, keys %alts); my $alt = ""; my $what = $ok || $thanks; if ($what) { $alt = $alts{$name}{$what}; } else { my @alts = @{$alts{$name}{'opts'}}; + print "\n\n"; paraprint < 5) { die "Invalid $name: aborting.\n"; } - print "Please enter a \u$name [$alts{$name}{'default'}]: "; - $alt = <>; - chomp $alt; - if ($alt =~ /^\s*$/) { - $alt = $alts{$name}{'default'}; - } + $alt = _prompt('', "\u$name", $alts{$name}{'default'}); + $alt ||= $alts{$name}{'default'}; } while !((($alt) = grep(/^$alt/i, @alts))); } lc $alt; @@ -391,7 +413,7 @@ to the volunteers who maintain perl at $address. To send a thank-you note to $thanksaddress instead of a bug report, please run 'perlthanks'. Please do not use $0 to send test messages, test whether perl -works, or use it to report bugs in external perl modules. +works, or to report bugs in perl modules from CPAN. For help using perl, try posting to the Usenet newsgroup comp.lang.perl.misc. @@ -420,9 +442,7 @@ EOF my $err = 0; do { - print "Subject: "; - $subject = <>; - chomp $subject; + $subject = _prompt('','Subject'); if ($err++ == 5) { if ($thanks) { $subject = 'Thanks for Perl'; @@ -462,13 +482,13 @@ EOF paraprint <; - chomp $from; + $from = _prompt('','Your address',$guess); $from = $guess if $from eq ''; } } @@ -493,14 +511,12 @@ EOF # Prompt for administrator address, unless an override was given if( !$::opt_C and !$::opt_c ) { - paraprint <; - chomp $entry; + my $entry = _prompt($description, "Local perl administrator", $cc); if ($entry ne "") { $cc = $entry; @@ -509,11 +525,18 @@ EOF } $cc = '' if $cc =~ /^(none|yourself|me|myself|ourselves)$/i; - $andcc = " and $cc" if $cc; + if ($cc) { + $andcc = " and $cc" + } else { + $andcc = '' + } # Prompt for editor, if no override is given editor: unless ($::opt_e || $::opt_f || $::opt_b) { + + my $description; + chomp (my $common_end = <<"EOF"); You will probably want to use a text editor to enter the body of your report. If "$ed" is the editor you want to use, then just press @@ -521,12 +544,12 @@ Enter, otherwise type in the name of the editor you would like to use. If you have already composed the body of your report, you may enter -"file" "file", and Perlbug will prompt you for to enter the name -of the file containing your report. +"file", and $0 will prompt you to enter the name of the file +containing your report. EOF if ($thanks) { - paraprint <<"EOF"; + $description = <<"EOF"; It's now time to compose your thank-you message. Some information about your local perl configuration will automatically @@ -537,13 +560,13 @@ not share this information, you're welcome to delete it. $common_end EOF } else { - paraprint <<"EOF"; + $description = <<"EOF"; It's now time to compose your bug report. Try to make the report concise but descriptive. Please include any detail which you think might be relevant or might help the volunteers working to improve perl. If you are reporting something that does not work as you think -it should, please try to include example of both the actual result, -and what you expected. +it should, please try to include examples of the actual result and of +what you expected. Some information about your local perl configuration will automatically be included at the end of your report. If you are using an unusual @@ -554,10 +577,7 @@ $common_end EOF } - print "Editor [$ed]: "; - my $entry =scalar <>; - chomp $entry; - + my $entry = _prompt($description, "Editor", $ed); $usefile = 0; if ($entry eq "file") { $usefile = 1; @@ -565,28 +585,38 @@ EOF $ed = $entry; } } - my $report_about_module = ''; if ($::HaveCoreList && !$ok && !$thanks) { - paraprint <; - $entry =~ s/^\s+//s; - $entry =~ s/\s+$//s; - if ($entry ne q{}) { - $category ||= 'library'; - $report_about_module = $entry; + + my $entry = ''; + while ($entry eq '') { + $entry = _prompt($description, 'Module'); my $first_release = Module::CoreList->first_release($entry); - unless ($first_release) { + if ($entry and not $first_release) { paraprint <; - chomp $entry; + my $entry = _prompt($description, "Filename"); if ($entry eq "") { paraprint <) { - s/\s+//g; - $REP{$_}++; - } - close(REP) or die "Error closing report file '$filename': $!"; + # Set up an initial report fingerprint so we can compare it later + _fingerprint_lines_in_report(); + } # sub Query sub Dump { @@ -698,6 +720,12 @@ Flags: category=$category severity=$severity EFF + + if ($report_about_module ) { + print OUT <; - chomp $entry; + my $description = "Please make sure that the name of the editor you want to use is correct."; + my $entry = _prompt($description, 'Editor', $ed); $ed = $entry unless $entry eq ''; } -tryagain: - my $sts; - $sts = system("$ed $filename") unless $Is_MacOS; - if ($Is_MacOS) { - require ExtUtils::MakeMaker; - ExtUtils::MM_MacOS::launch_file($filename); - paraprint <; - } - if ($sts) { - paraprint <; - chomp $entry; +sub _edit_file { + my $editor = shift; - if ($entry ne "") { - $ed = $entry; - goto tryagain; - } else { - paraprint <) { - s/\s+//g; - $unseen++ if $_ ne '' and not exists $REP{$_}; - } + # Check that we have a report that has some, eh, report in it. - while ($unseen == 0) { - paraprint <); - if ($action =~ /^[re]/i) { # etry dit - goto tryagain; - } elsif ($action =~ /^[cq]/i) { # ancel, uit - Cancel(); - } + my $action = _prompt( $description, "Action (Retry/Cancel) " ); + if ( $action =~ /^[re]/i ) { # etry dit + next; + } elsif ( $action =~ /^[cq]/i ) { # ancel, uit + Cancel(); # cancel exits + } + } + # Ok. the user did what they needed to; + return; + } -} # sub Edit +} + sub Cancel { 1 while unlink($filename); # remove all versions under VMS - print "\nCancelling.\n"; + print "\nQuitting without sending your message.\n"; exit(0); } @@ -844,7 +863,9 @@ sub NowWhat { # Report is done, prompt for further action if( !$::opt_S ) { while(1) { - print <; - chomp $action; + print $menu; + my $action = _prompt('', "Action (Send/Display/Edit/Subject/Save to File)");; print "\n"; if ($action =~ /^(f|sa)/i) { # ile/ve - my $file_save = $outfile || "$progname.rep"; - print "\n\nName of file to save message in [$file_save]: "; - my $file = scalar <>; - chomp $file; - $file = $file_save if $file eq ""; - - unless (open(FILE, ">$file")) { - print "\nError opening $file: $!\n\n"; - goto retry; - } - open(REP, "<$filename") or die "Couldn't open file '$filename': $!\n"; - print FILE "To: $address\nSubject: $subject\n"; - print FILE "Cc: $cc\n" if $cc; - print FILE "Reply-To: $from\n" if $from; - print FILE "Message-Id: $messageid\n" if $messageid; - print FILE "\n"; - while () { print FILE } - close(REP) or die "Error closing report file '$filename': $!"; - close(FILE) or die "Error closing $file: $!"; - - paraprint <isplay, ist, ow # Display the message open(REP, "<$filename") or die "Couldn't open file '$filename': $!\n"; while () { print $_ } close(REP) or die "Error closing report file '$filename': $!"; } elsif ($action =~ /^su/i) { # bject - print "Subject: $subject\n\n"; - print "If the above subject is fine, press Enter. Otherwise, type a replacement now\n"; - print "Subject: "; - my $reply = scalar ; - chomp $reply; + my $reply = _prompt( "Subject: $subject", "If the above subject is fine, press Enter. Otherwise, type a replacement now\nSubject"); if ($reply ne '') { unless (TrivialSubject($reply)) { $subject = $reply; @@ -906,17 +898,12 @@ EOF } } elsif ($action =~ /^se/i) { # end # Send the message - print "Are you certain you want to send this message?\n" - . 'Please type "yes" if you are: '; - my $reply = scalar ; - chomp $reply; + my $reply = _prompt( "Are you certain you want to send this message?", 'Please type "yes" if you are','no'); if ($reply =~ /^yes$/) { last; } else { paraprint <dit, e-edit @@ -926,7 +913,7 @@ EOF Cancel(); } elsif ($action =~ /^s/i) { paraprint <$outfile" or die "Couldn't open '$outfile': $!\n"; - goto sendout; - } - # on linux certain mail implementations won't accept the subject + # on linux certain "mail" implementations won't accept the subject # as "~s subject" and thus the Subject header will be corrupted # so don't use Mail::Send to be safe - if ($::HaveSend && !$Is_Linux && !$Is_OpenBSD) { - $msg = new Mail::Send Subject => $subject, To => $address; - $msg->cc($cc) if $cc; - $msg->add("Reply-To",$from) if $from; - - $fh = $msg->open; - open(REP, "<$filename") or die "Couldn't open '$filename': $!\n"; - while () { print $fh $_ } - close(REP) or die "Error closing $filename: $!"; - $fh->close; - - print "\nMessage sent.\n"; - } elsif ($Is_VMS) { - if ( ($address =~ /@/ and $address !~ /^\w+%"/) or - ($cc =~ /@/ and $cc !~ /^\w+%"/) ) { - my $prefix; - foreach (qw[ IN MX SMTP UCX PONY WINS ], '') { - $prefix = "$_%", last if $ENV{"MAIL\$PROTOCOL_$_"}; - } - $address = qq[${prefix}"$address"] unless $address =~ /^\w+%"/; - $cc = qq[${prefix}"$cc"] unless !$cc || $cc =~ /^\w+%"/; - } - $subject =~ s/"/""/g; $address =~ s/"/""/g; $cc =~ s/"/""/g; - my $sts = system(qq[mail/Subject="$subject" $filename. "$address","$cc"]); - if ($sts) { - die <) { print SENDMAIL $_ } - close(REP) or die "Error closing $filename: $!"; - - if (close(SENDMAIL)) { - printf "\nMessage %s.\n", $outfile ? "saved" : "sent"; - } else { - warn "\nSendmail returned status '", $? >> 8, "'\n"; - } + SaveMessage(); + return; } - 1 while unlink($filename); # remove all versions under VMS -} # sub Send + + 1 while unlink($filename); # remove all versions under VMS +} # sub Send sub Help { print <); + chomp($result); + $result =~ s/^\s*(.*?)\s*$/$1/s; + if ($default && $result eq '') { + return $default; + } else { + return $result; + } +} + +sub _build_header { + my %attr = (@_); + + my $head = ''; + for my $header (keys %attr) { + $head .= "$header: ".$attr{$header}."\n"; + } + return $head; +} + +sub _message_headers { + my %headers = ( To => $address, Subject => $subject ); + $headers{'Cc'} = $cc if ($cc); + $headers{'Message-Id'} = $messageid if ($messageid); + $headers{'Reply-To'} = $from if ($from); + return \%headers; +} + +sub build_complete_message { + my $content = _build_header(%{_message_headers()}) . "\n\n"; + open( REP, "<$filename" ) or die "Couldn't open file '$filename': $!\n"; + while () { $content .= $_; } + close(REP) or die "Error closing report file '$filename': $!"; + return $content; +} + +sub save_message_to_disk { + my $file = shift; + + open OUTFILE, ">$file" or do { warn "Couldn't open '$file': $!\n"; return undef}; + print OUTFILE build_complete_message(); + close(OUTFILE) or do { warn "Error closing $file: $!"; return undef }; + print "\nMessage saved.\n"; + return 1; +} + +sub _send_message_vms { + if ( ( $address =~ /@/ and $address !~ /^\w+%"/ ) + or ( $cc =~ /@/ and $cc !~ /^\w+%"/ ) ) { + my $prefix; + foreach ( qw[ IN MX SMTP UCX PONY WINS ], '' ) { + $prefix = "$_%", last if $ENV{"MAIL\$PROTOCOL_$_"}; + } + $address = qq[${prefix}"$address"] unless $address =~ /^\w+%"/; + $cc = qq[${prefix}"$cc"] unless !$cc || $cc =~ /^\w+%"/; + } + $subject =~ s/"/""/g; + $address =~ s/"/""/g; + $cc =~ s/"/""/g; + my $sts = system(qq[mail/Subject="$subject" $filename. "$address","$cc"]); + if ($sts) { + die "Can't spawn off mail (leaving bug report in $filename): $sts"; + } +} + +sub _send_message_mailsend { + my $msg = Mail::Send->new(); + my %headers = %{_message_headers()}; + for my $key ( keys %headers) { + $msg->add($key => $headers{$key}); + } + + $fh = $msg->open; + open(REP, "<$filename") or die "Couldn't open '$filename': $!\n"; + while () { print $fh $_ } + close(REP) or die "Error closing $filename: $!"; + $fh->close; + + print "\nMessage sent.\n"; +} + +sub _probe_for_sendmail { + my $sendmail = ""; + for (qw(/usr/lib/sendmail /usr/sbin/sendmail /usr/ucblib/sendmail)) { + $sendmail = $_, last if -e $_; + } + if ( $^O eq 'os2' and $sendmail eq "" ) { + my $path = $ENV{PATH}; + $path =~ s:\\:/:; + my @path = split /$Config{'path_sep'}/, $path; + for (@path) { + $sendmail = "$_/sendmail", last if -e "$_/sendmail"; + $sendmail = "$_/sendmail.exe", last if -e "$_/sendmail.exe"; + } + } + return $sendmail; +} + +sub _send_message_sendmail { + my $sendmail = _probe_for_sendmail(); + unless ($sendmail) { + paraprint(<<"EOF"), die "\n"; +It appears that there is no program which looks like "sendmail" on +your system and that the Mail::Send library from CPAN isn't available. +Because of this, there's no easy way to automatically send your +message. + +A copy of your message has been saved in '$filename' for you to +send to '$address' with your normal mail client. +EOF + } + + open( SENDMAIL, "|$sendmail -t -oi" ) + || die "'|$sendmail -t -oi' failed: $!"; + print SENDMAIL build_complete_message(); + if ( close(SENDMAIL) ) { + print "\nMessage sent\n"; + } else { + warn "\nSendmail returned status '", $? >> 8, "'\n"; + } +} + + + +# a strange way to check whether any significant editing +# has been done: check whether any new non-empty lines +# have been added. + +sub _fingerprint_lines_in_report { + my $new_lines = 0; + # read in the report template once so that + # we can track whether the user does any editing. + # yes, *all* whitespace is ignored. + + open(REP, "<$filename") or die "Unable to open report file '$filename': $!\n"; + while (my $line = ) { + $line =~ s/\s+//g; + $new_lines++ if (!$REP{$line}); + + } + close(REP) or die "Error closing report file '$filename': $!"; + # returns the number of lines with content that wasn't there when last we looked + return $new_lines; +} + + + format STDOUT = ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ~~ $_ @@ -1128,11 +1222,13 @@ B S<[ B<-v> ]> S<[ B<-a> I
]> S<[ B<-s> I ]> S<[ B<-b> I | B<-f> I ]> S<[ B<-F> I ]> S<[ B<-r> I ]> S<[ B<-e> I ]> S<[ B<-c> I | B<-C> ]> -S<[ B<-S> ]> S<[ B<-t> ]> S<[ B<-d> ]> S<[ B<-A> ]> S<[ B<-h> ]> +S<[ B<-S> ]> S<[ B<-t> ]> S<[ B<-d> ]> S<[ B<-A> ]> S<[ B<-h> ]> S<[ B<-T> ]> B S<[ B<-v> ]> S<[ B<-r> I ]> S<[ B<-A> ]> S<[ B<-ok> | B<-okay> | B<-nok> | B<-nokay> ]> +B + =head1 DESCRIPTION @@ -1148,13 +1244,17 @@ non-core module (such as Tk, DBI, etc), then please see the documentation that came with that distribution to determine the correct place to report bugs. -If you are unable to send your report using B (most likely -because your system doesn't have a way to send mail that perlbug recognizes), you may be able to use this tool to compose your report and save it to a file -which you can then send to B using your regular mail client. +If you are unable to send your report using B (most likely +because your system doesn't have a way to send mail that perlbug +recognizes), you may be able to use this tool to compose your report +and save it to a file which you can then send to B +using your regular mail client. -In extreme cases, B may not work well enough on your system to -guide you through composing a bug report. In those cases, you may be able to -use B to get system configuration information to include in a manually composed bug report to B. +In extreme cases, B may not work well enough on your system +to guide you through composing a bug report. In those cases, you +may be able to use B to get system configuration +information to include in a manually composed bug report to +B. When reporting a bug, please run through this checklist: @@ -1171,12 +1271,13 @@ Look at http://www.perl.org/ to find out. If you are not using the latest released version, please try to replicate your bug on the latest stable release. -Note that bug reports about old versions of Perl, especially those -tested only on versions of Perl prior to the current stable release, -are likely to receive less attention from the volunteers who build -and maintain Perl than bugs in the current release. +Note that reports about bugs in old versions of Perl, especially +those which indicate you haven't also tested the current stable +release of Perl, are likely to receive less attention from the +volunteers who build and maintain Perl than reports about bugs in +the current release. -This tool isn't apropriate for reporting bugs in any version of +This tool isn't apropriate for reporting bugs in any version prior to Perl 5.0. =item Are you sure what you have is a bug? @@ -1193,10 +1294,10 @@ receive more attention. You may want to start with B L for pointers to common traps that new (and experienced) Perl programmers run into. -If you're unsure of them meaning of an error message you've run -across, B L for an explanation. If message isn't -in perldiag, it probably isn't generated by Perl. You may have -luck consulting your operating system documentation instead. +If you're unsure of the meaning of an error message you've run +across, B L for an explanation. If the message +isn't in perldiag, it probably isn't generated by Perl. You may +have luck consulting your operating system documentation instead. If you are on a non-UNIX platform B L, as some features may be unimplemented or work differently. @@ -1208,7 +1309,7 @@ L. =item Do you have a proper test case? The easier it is to reproduce your bug, the more likely it will be -fixed -- If nobody can duplicate your problem, it probably won't be +fixed -- if nobody can duplicate your problem, it probably won't be addressed. A good test case has most of these attributes: short, simple code; @@ -1274,23 +1375,33 @@ cannot run C at all on your system, be sure to include the entire output produced by running C (note the uppercase V). Whether you use C or send the email manually, please make -your Subject line informative. "a bug" not informative. Neither +your Subject line informative. "a bug" is not informative. Neither is "perl crashes" nor is "HELP!!!". These don't help. A compact description of what's wrong is fine. -=back +=item Can you use C to submit a thank-you note? -Having done your bit, please be prepared to wait, to be told the bug -is in your code, or possibly to get no reply at all. The volunteers who -maintain Perl are busy folks, so if your problem is an obvious bug in your own code, is difficult to understand or is a duplicate of an existing report, you -may not receive a personal reply. +Yes, you can do this by either using the C<-T> option, or by invoking +the program as C. Thank-you notes are good. It makes people +smile. -If it is important to you that your bug be fixed, do monitor the perl5-porters@perl.org mailing list, the commit logs to development versions of Perl -and encourage the maintainers with kind words or offers of frosty beverages. -(Please do be kind to the maintainers. Harassing or flaming them is likely to -have the opposite effect of the one you want.) +=back -Feel free update the ticket about your bug on http://rt.perl.org +Having done your bit, please be prepared to wait, to be told the +bug is in your code, or possibly to get no reply at all. The +volunteers who maintain Perl are busy folks, so if your problem is +an obvious bug in your own code, is difficult to understand or is +a duplicate of an existing report, you may not receive a personal +reply. + +If it is important to you that your bug be fixed, do monitor the +perl5-porters@perl.org mailing list and the commit logs to development +versions of Perl, and encourage the maintainers with kind words or +offers of frosty beverages. (Please do be kind to the maintainers. +Harassing or flaming them is likely to have the opposite effect of +the one you want.) + +Feel free to update the ticket about your bug on http://rt.perl.org if a new version of Perl is released and your bug is still present. =head1 OPTIONS @@ -1393,6 +1504,10 @@ supply one on the command line. Test mode. The target address defaults to B. +=item B<-T> + +Send a thank-you note instead of a bug report. + =item B<-v> Include verbose configuration data in the report. @@ -1409,7 +1524,7 @@ Mike Guy (Emjtg@cam.a.ukE), Dominic Dunlop (Edomo@computer.orgE), Hugo van der Sanden (Ehv@crypt.org), Jarkko Hietaniemi (Ejhi@iki.fiE), Chris Nandor (Epudge@pobox.comE), Jon Orwant (Eorwant@media.mit.eduE, -Richard Foley (Erichard@rfi.netE), and Jesse Vincent +Richard Foley (Erichard.foley@rfi.netE), and Jesse Vincent (Ejesse@bestpractical.com). =head1 SEE ALSO