* docs/pmc.pod:
[parrot.git] / parrotbug
blob24dd3927c0a04cb38907ec763b03e42fb1695a77
1 #!/usr/bin/perl
3 # Copyright (C) 2004, The Perl Foundation.
4 # $Id$
7 eval 'exec perl -w -S $0 ${1+"$@"}'
8 if $running_under_some_shell;
10 $^W = 1; # Set warnings;
11 use strict;
13 use Config;
14 use File::Spec;
15 use Getopt::Long;
18 my $VERSION = "0.6.1";
20 # These are the standard addresses for reporting bugs.
21 my %std_to =
22 ( bug => 'parrotbug@parrotcode.org',
23 ok => 'parrotstatus-ok@parrotcode.org',
24 nok => 'parrotstatus-nok@parrotcode.org',
27 my $parrotdir = File::Spec->curdir();
28 my ( %opts, %parrot, %report );
29 my ( $editor, $user, $domain, $msgid, $tmpfile );
30 my ( $is_linux, $is_macos, $is_mswin32, $is_os2, $is_vms );
31 my @categories = qw[ core docs install library utilities languages ];
32 my @severities = qw[ critical high medium low wishlist none ];
35 #------------------------------------------------------------#
36 # Main program. #
38 init();
39 help() if $opts{help};
40 version() if $opts{version};
41 explain_parrotbug() unless $opts{quiet};
42 query_missing_info();
43 what_next();
44 unlink $tmpfile;
45 exit;
49 # Explain what C<parrotbug> is.
50 sub explain_parrotbug {
51 print <<EOT;
53 This program provides an easy way to create a message reporting a bug
54 in parrot, and e-mail it to the parrot developers.
56 It is *NOT* intended for:
57 - sending test messages,
58 - or reporting bugs in languages targetting parrot,
59 - or reporting bugs in some library bindings for parrot,
60 - or simply verifying that parrot works.
62 It is *ONLY* a mean of reporting verifiable problems with the core
63 parrot distribution, and any solutions to such problems, to parrot
64 developers.
66 If you're just looking for help with parrot, subscribe to the parrot
67 mailing list, parrot-porters<at>perl.org.
71 EOT
77 #------------------------------------------------------------#
78 # Utils subs. #
80 # Generate random filename to edit report.
81 sub generate_filename {
82 my $dir = File::Spec->tmpdir();
83 my $filename = "bugrep0$$";
84 $filename++ while -e File::Spec->catfile($dir, $filename);
85 $filename = File::Spec->catfile($dir, $filename);
86 return $filename;
90 # Check whether a subject is trivial. A subject is not considered trivial
91 # if it's an ok or a nok report.
92 # Return 1 if trivial, 0 otherwise (subject acceptable).
93 sub trivial_subject {
94 my $subject = shift;
96 return 0 if $opts{ok} || $opts{nok};
97 if ( $subject =~
98 /^(y(es)?|no?|help|parrot( (bug|problem))?|bug|problem)$/i ||
99 length($subject) < 4 ||
100 $subject !~ /\s/ ) {
101 return 1;
102 } else {
103 return 0;
110 #------------------------------------------------------------#
111 # Init subs. #
113 # Initialize the program.
115 # Get parrot information, process the options, create the message
116 # information (subject, to, body, etc.) depending on the type of report
117 # (ok, nok or bug report).
118 sub init {
119 $is_linux = lc($^O) eq 'linux';
120 $is_macos = $^O eq 'MacOS';
121 $is_mswin32 = $^O eq 'MSWin32';
122 $is_os2 = $^O eq 'os2';
123 $is_vms = $^O eq 'VMS';
126 ## Fetch Parrot information.
129 # Get parrot version.
130 # There will always be an up-to-date $parrot/VERSION
131 my $filename = File::Spec->catfile($parrotdir, "VERSION");
132 open(VERSION, "<$filename") or die "Cannot open '$filename': $!";
133 $parrot{version} = <VERSION>;
134 chomp $parrot{version};
135 close(VERSION) or die "Cannot close '$filename': $!";
137 # Get parrot configuration, stored in $parrot/myconfig
138 $filename = File::Spec->catfile($parrotdir, "myconfig");
139 open(MYCONFIG, "<$filename") or die "Cannot open '$filename': $!";
141 local $/;
142 $parrot{myconfig} = <MYCONFIG>;
144 close(MYCONFIG) or die "Cannot close '$filename': $!";
148 ## Process options.
150 @ARGV = split m/\s+/,
151 MacPerl::Ask("Provide command-line args here (-h for help):")
152 if $is_macos && $MacPerl::Version =~ /App/;
153 Getopt::Long::Configure("no_bundling", "no_ignore_case", "auto_abbrev");
154 help() unless GetOptions
155 ( \%opts,
156 "help|h", "version|V",
157 "send", "dump", "save",
158 "from|f=s", "to|test|t=s", "editor|e=s",
159 "subject|s=s", "category|C=s", "severity|S=s",
160 "input|input-file|i=s", "output|output-file|o=s",
161 "ok", "nok", "ack!", "quiet|q!" );
164 ## Report to be sent.
166 sw: {
167 ok_report: {
168 last ok_report unless defined $opts{ok};
170 # This is an ok report, woohoo!
171 $report{to} = $std_to{ok};
172 $report{subject} = "OK: parrot $parrot{version} "
173 . "on $Config{archname} $Config{osvers}";
174 $report{body} = "Parrot reported to build OK on this system.\n";
175 $report{category} = "install";
176 $report{severity} = "none";
177 $report{body} = "";
178 last sw;
181 # Ok reports do not need body, but nok and bug reports do need
182 # a body.
183 if ( $opts{input} ) {
184 # Report was pre-written, slurp it.
185 open BODY, "<$opts{input}" or die "Can't open '$opts{input}': $!";
186 local $/;
187 $report{body} = <BODY>;
188 close BODY or die "Can't close '$opts{input}': $!";
189 } else {
190 # No file provided...
191 $report{body} = "";
195 nok_report: {
196 last nok_report unless defined $opts{nok};
198 # This a nok report, how sad... :-(
199 $report{to} = $std_to{nok};
200 $report{subject} = "Not OK: parrot $parrot{version} "
201 . "on $Config{archname} $Config{osvers}";
202 $report{category} = "install";
203 $report{severity} = "none";
204 last sw;
207 # Neither an ok nor a nok.
208 $report{to} = $std_to{bug};
209 $report{subject} = $opts{subject} || "";
210 $report{category} = $opts{category} || "";
211 $report{severity} = $opts{severity} || "";
214 # Test message, shortcuting recipent.
215 $report{to} = $opts{to} if $opts{to};
218 ## User information.
221 # Username.
222 $user = $is_mswin32 ? $ENV{USERNAME}
223 : $is_os2 ? $ENV{USER} || $ENV{LOGNAME}
224 : $is_macos ? $ENV{USER}
225 : eval { getpwuid($<) }; # May be missing
227 # User address, used in message and in Reply-To header.
228 $report{from} = $opts{from} || "";
230 # Editor
231 $editor = $ENV{VISUAL} || $ENV{EDITOR} || $ENV{EDIT}
232 || ( $is_vms && "edit/tpu" )
233 || ( $is_mswin32 && "notepad" )
234 || ( $is_macos && "" )
235 || "vi";
239 ## Mail information.
242 # Message-Id.
243 eval "use Mail::Util;";
244 if ( $@ eq "" ) {
245 $domain = Mail::Util::maildomain();
246 } elsif ($is_mswin32) {
247 $domain = $ENV{USERDOMAIN};
248 } else {
249 require Sys::Hostname;
250 $domain = Sys::Hostname::hostname();
252 $msgid = "<parrotbug_${VERSION}_${$}_".time."\@$domain>";
257 #------------------------------------------------------------#
258 # Querying subs. #
260 # Query missing information in order to have a complete report.
261 sub query_missing_info {
262 $report{subject} = "" if trivial_subject( $report{subject} );
263 $report{subject} = ask_for_subject() unless $report{subject};
264 $report{category} = ask_for_alternative( "category", \@categories)
265 unless $report{category};
266 $report{severity} = ask_for_alternative( "severity", \@severities)
267 unless $report{severity};
268 $report{from} = ask_for_return_address() unless $report{from};
269 $report{body} = ask_for_body() unless $report{body};
273 # Prompt for alternatives from a set of choices.
275 # The arguments are: the name of alternative, the choices (as an array
276 # ref), and the default answer. (first element if undef)
278 # Return the lowercased alternative chosen.
280 # Die if more than 5 wrong answers.
281 sub ask_for_alternative {
282 my ( $what, $choices, $default ) = @_;
284 print <<EOT unless $opts{quiet};
285 Please pick a $what from the following:
286 @{$choices}
290 $default ||= $choices->[0];
291 my $alt;
292 my $err = 0;
293 do {
294 die "Invalid $alt: aborting.\n" if $err++ > 5;
295 print "Please enter a $what [$default]: ";
296 $alt = <STDIN>;
297 chomp $alt;
298 $alt = $default if $alt =~ /^\s*$/;
299 } until ( ($alt) = grep /^$alt/i, @$choices );
301 print "\n\n\n";
302 return lc $alt;
306 # Prompt for a body, through an external editor.
307 sub ask_for_body {
308 unless ( $opts{quiet} ) {
309 print <<EOT;
310 Now you need to supply the bug report. Try to make the report concise
311 but descriptive. Include any relevant detail. If you are reporting
312 something that does not work as you think it should, please try to
313 include example of both the actual result, and what you expected.
315 Some information about your local parrot configuration will
316 automatically be included at the end of the report. If you are using
317 any unusual version of parrot, please try and confirm exactly which
318 versions are relevant.
322 print "Press 'Enter' to continue...\n";
323 scalar <STDIN>;
326 # Prompt for editor to use if none supplied.
327 if ( $opts{editor} ) {
328 $editor = $opts{editor};
330 } else {
331 ask_for_editor($opts{quiet} ? "" : <<EOT);
332 You will probably want to use an editor to enter the report. If the
333 default editor proposed below is the editor you want to use, then just
334 press the 'Enter' key, otherwise type in the name of the editor you
335 would like to use.
339 # Launch editor.
340 $tmpfile = generate_filename();
341 my $body = "";
342 my $err = 0;
343 do {
344 edit_bug_report( $tmpfile );
345 # Slurp bug report.
346 open BODY, "<$tmpfile" or die "Can't open '$tmpfile': $!";
348 local $/;
349 $body = <BODY>;
351 close BODY or die "Can't close '$tmpfile': $!";
352 unless ( $body ) {
353 print "\nYou provided an empty bug report!\n";
354 print "Press 'Enter' to continue...\n";
355 scalar <STDIN>;
357 die "Aborting.\n" if $err++ == 5;
358 } until ( $body );
360 return $body;
364 # Prompt for editor to use.
365 sub ask_for_editor {
366 print shift() . "Editor [$editor]: ";
367 my $entry = <STDIN>;
368 chomp $entry;
369 $editor = $entry if $entry ne "";
370 $opts{editor} = $editor;
374 # Prompt for return address, return it.
375 sub ask_for_return_address {
376 print <<EOT unless $opts{quiet};
377 Your e-mail address will be useful if you need to be contacted. If the
378 default shown below is not your full internet e-mail address, please
379 correct it.
382 # Try and guess return address
383 my ($from, $guess);
384 if ( $is_macos ) {
385 require Mac::InternetConfig;
386 $guess = $Mac::InternetConfig::InternetConfig{
387 Mac::InternetConfig::kICEmail()
389 } else {
390 $guess = $ENV{'REPLY-TO'} || $ENV{REPLYTO} || "";
393 if ( ! $guess ) {
394 # Use $domain if we can.
395 if ( $domain ) {
396 $guess = $is_vms && !$Config{d_socket} ?
397 "$domain\:\:$user" : "$user\@$domain";
401 # Verify our guess.
402 print "Your address [$guess]: ";
403 $from = <STDIN>;
404 chomp $from;
405 $from = $guess if $from eq "";
406 print "\n\n\n";
407 return $from;
411 # Prompt for subject of message.
413 # Return the subject chosen.
415 # Die if more than 5 wrong subjects.
416 sub ask_for_subject {
417 print <<EOT unless $opts{quiet};
418 First of all, please provide a subject for the message. It should be a
419 concise description of the bug or problem. "parrot bug" or "parrot
420 problem" is not a concise description.
424 my $subject;
425 my $err = 0;
426 do {
427 $err and print "\nThat doesn't look like a good subject. "
428 . "Please be more verbose.\n";
429 print "Subject: ";
430 $subject = <STDIN>;
431 chomp $subject;
432 die "Aborting.\n" if $err++ == 5;
433 } while ( trivial_subject($subject) );
434 print "\n\n\n";
435 return $subject;
439 # Launch an editor in which to edit the bug report.
440 sub edit_bug_report {
441 my $filename = shift;
443 # Launch editor.
444 my $retval;
445 if ($is_macos) {
446 require ExtUtils::MakeMaker;
447 ExtUtils::MM_MacOS::launch_file($filename);
448 print "Press Enter when done.\n";
449 scalar <STDIN>;
450 } else {
451 $retval = system("$editor $filename");
454 # Check whether editor run was successful.
455 die <<EOT if $retval;
456 The editor you chose ('$editor') could apparently not be run! Did you
457 mistype the name of your editor?
465 #------------------------------------------------------------#
466 # Action subs. #
469 # Display everything collected.
470 sub dump_report {
471 print "==> Dumping message...\n";
472 print format_message();
476 # Last chance to edit report.
477 sub edit_report {
478 # Prompt for editor to use if none supplied.
479 unless ( $opts{editor} ) {
480 ask_for_editor(<<EOT);
481 You will probably want to use an editor to modify the report. If the
482 default editor proposed below is the editor you want to use, then just
483 press the 'Enter' key, otherwise type in the name of the editor you
484 would like to use.
488 $tmpfile ||= $opts{input};
489 my $err = 0;
490 my $body;
491 do {
492 edit_bug_report( $tmpfile );
493 # Slurp bug report.
494 open BODY, "<$tmpfile" or die "Can't open '$tmpfile': $!";
496 local $/;
497 $body = <BODY>;
499 close BODY or die "Can't close '$tmpfile': $!";
500 unless ( $body ) {
501 print "\nYou provided an empty bug report!\n";
502 print "Press 'Enter' to continue...\n";
503 scalar <STDIN>;
505 die "Aborting.\n" if $err++ == 5;
506 } until ( $body );
508 $report{body} = $body;
512 # Format the message with everything collected and return it.
513 sub format_message {
514 my $report = "";
516 # OS, arch, compiler...
517 $report .= <<EOT;
519 osname= $Config{osname}
520 osvers= $Config{osvers}
521 arch= $Config{archname}
524 my $cc = $Config{cc};
525 $report .= "cc= $cc $Config{${cc}.'version'}\n";
528 # ... flags...
529 $report .= <<EOT;
531 Flags:
532 category=$report{category}
533 severity=$report{severity}
535 $report .= " ack=no\n" if ! $opts{ack};
537 # ... bug report ...
538 $report .= "---\n$report{body}\n";
540 # ... myconfig ...
541 $report .= "---\n$parrot{myconfig}\n---\n";
543 # ... and environment.
544 $report .= "Environment:\n";
545 my @env = qw[ PATH LD_LIBRARY_PATH LANG SHELL HOME LOGDIR LANGUAGE ];
546 push @env, $Config{ldlibpthname} if $Config{ldlibpthname} ne '';
547 push @env, grep /^(?:PERL|LC_|LANG|CYGWIN)/, keys %ENV;
548 my %env; @env{@env} = @env;
549 for my $env (sort keys %env) {
550 $report .= " $env",
551 exists $ENV{$env} ? "=$ENV{$env}\n" : " (unset)\n";
554 return $report;
558 # Print synopsis + help message and exit.
559 sub help {
560 print <<EOT;
562 A program to help generate bug reports about parrot, and mail them.
563 It is designed to be used interactively. Normally no arguments will
564 be needed.
566 Simplest usage: run '$0', and follow the prompts.
567 Usage: $0 [OPTIONS] [ACTIONS]
569 Options:
570 --ok Report successful build on this system to parrot
571 developers. Only use --ok if *everything* was ok:
572 if there were *any* problems at all, use --nok.
573 --nok Report unsuccessful build on this system.
574 --subject <subject> Subject to include with the message.
575 --category <category> Category of the bug report.
576 --severity <severity> Severity of the bug report.
577 --from <address> Your email address.
578 --editor <editor> Editor to use for editing the bug report.
579 --ack, --noack Don't send a bug received acknowledgement.
580 --input-file File containing the body of the report. Use this
581 to quickly send a prepared message.
582 --output-file File where parrotbug will save its bug report.
583 --to <address> Email address to send report to. (testing only)
585 Note: you will be prompted if the program miss some information.
587 Actions:
588 --dump Dump message.
589 --save Save message.
590 --send Send message.
591 --help Print this help message and exit.
592 --version Print version information and exit.
595 exit;
599 # Save message to file.
600 sub save_report {
601 print "\n==> Saving message to file...\n";
602 if ( ! $opts{output} ) {
603 print "Enter filename to save bug report: ";
604 $opts{output} = <STDIN>;
607 open OUTPUT, ">$opts{output}" or die "Cannot open '$opts{output}': $!";
608 print OUTPUT format_message();
609 close OUTPUT or die "Cannot open '$opts{output}': $!";
611 print "Message saved.\n";
615 # Send message to final recipient.
616 sub send_report {
617 print "==> Sending message to recipient...\n";
619 # On linux certain mail implementations won't accept the subject
620 # as "~s subject" and thus the Subject header will be corrupted
621 # so don't use Mail::Send to be safe
622 eval "require Mail::Send";
623 if ( $@ eq "" && !$is_linux) {
624 my $msg = new Mail::Send Subject => $report{subject}, To => $report{to};
625 $msg->add( "Reply-To", $report{from} );
627 my $fh = $msg->open;
628 print $fh format_message();
629 $fh->close;
631 print "\nMessage sent.\n";
633 } else {
634 my $sendmail = "";
635 for ( qw[ /usr/lib/sendmail /usr/sbin/sendmail
636 /usr/ucblib/sendmail /var/qmail/bin/sendmail ] ) {
637 $sendmail = $_, last if -e $_;
640 die <<EOT if $sendmail eq "";
641 I am terribly sorry, but I cannot find sendmail, or a close
642 equivalent, and the perl package Mail::Send has not been installed, so
643 I can't send your bug report. We apologize for the inconvenience.
645 So you may attempt to find some way of sending your message, it has
646 been left in the file '$tmpfile'.
649 open(SENDMAIL, "|$sendmail -t -oi") || die "'|$sendmail -t -oi' failed: $!";
651 print SENDMAIL <<EOT;
652 To: $report{to}
653 Subject: $report{subject}
654 Reply-To: $report{from}
655 Message-Id: $msgid
659 print SENDMAIL format_message();
660 if (close(SENDMAIL)) {
661 printf "\nMessage sent.\n";
662 } else {
663 warn "\nSendmail returned status '", $? >> 8, "'\n";
669 # Print version information (of the parrotbug program) and exit.
670 sub version {
671 print <<"EOT";
673 This is $0, version $VERSION.
676 exit;
680 # Check whether actions have been provided on comand-line, otherwise
681 # prompt for what to do with bug report.
682 sub what_next {
683 dump_report() if $opts{dump};
684 save_report() if $opts{save};
685 send_report() if $opts{send};
687 return if $opts{dump} || $opts{save} || $opts{send};
689 # No actions provided on command-line, prompt for action.
691 my $action;
692 do {
693 print "Action (send,display,edit,save,quit): ";
694 $action = <STDIN>;
695 sw: for ($action) {
696 dump_report(), last sw if /^d/i;
697 edit_report(), last sw if /^e/i;
698 save_report(), last sw if /^sa/i;
699 send_report(), last sw if /^se/i;
700 print "Uh?\n" unless /^q/i;
702 } until ( $action =~ /^q/i );
706 __END__
708 =head1 NAME
710 parrotbug - Parrot Bug Reporter
712 =head1 SYNOPSIS
714 % ./parrotbug [options] [actions]
716 =head1 DESCRIPTION
718 A program to help generate bug reports about parrot, and mail them.
719 It is designed to be used interactively. Normally no arguments will
720 be needed.
723 =head1 COMMAND-LINE SWITCHES
726 =head2 Options
728 Note: you will be prompted if the program miss some information.
730 =over 4
732 =item B<--nok>
734 Report unsuccessful build on this system to parrot developers.
736 =item B<--ok>
738 Report successful build on this system to parrot developers Only use
739 C<--ok> if B<everything> was ok; if there were B<any> problems at all,
740 use C<--nok>.
742 =item B<--subject>
744 Subject of the report. You will be prompted if you don't supply one on
745 the command-line.
747 =item B<--category>
749 Category of the bug report. You will be prompted if you don't supply
750 one on the command-line.
752 =item B<--severity>
754 Severity of the bug report. You will be prompted if you don't supply
755 one on the command-line.
757 =item B<--address>
759 Your email address. The program will try to guess one if you don't
760 provide one, but you'll still need to validate it.
762 =item B<--editor>
764 Editor to use for editing the bug report.
766 =item B<--ack>, B<--noack>
768 Don't send a bug received acknowledgement.
770 =item B<--input-file>
772 File containing the body of the report. Use this to quickly send a
773 prepared message.
775 =item B<--output-file>
777 File where parrotbug will save its bug report, if you ask it to do so.
779 =item B<--to>
781 Email address to send report to. (for testing purposes only)
783 =back
786 =head2 Actions
788 You can provide more than one action on the command-line. If none is
789 supplied, then you will be prompted for what to do.
791 =over 4
793 =item B<--dump>
795 Dump formatted report on standard output.
797 =item B<--save>
799 Save message to a file, in order for you to send it later from your
800 own. See C<--output> flag.
802 =item B<--send>
804 Try to send a mail with the report.
806 =item B<--help>
808 Print a short synopsis and exit.
810 =item B<--version>
812 Print version information and exit.
814 =back
817 =head1 AUTHORS
819 Jerome Quelin (E<lt>jquelin@cpan.orgE<gt>), with lots of good stuff taken from perlbug.
822 =head1 SEE ALSO
824 perlbug(1), parrot(1), diff(1), patch(1)
826 =cut