* t/configure/25-options_test.t:
[parrot.git] / parrotbug
blobf4db2c35966aa00357e63873e2a6c009c9f91dd1
1 #!/usr/bin/perl
3 # Copyright (C) 2004-2007, The Perl Foundation.
4 # $Id$
7 eval 'exec perl -w -S $0 ${1+"$@"}'
8 if $running_under_some_shell;
10 use strict;
11 use warnings;
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_mswin32 = $^O eq 'MSWin32';
121 $is_os2 = $^O eq 'os2';
122 $is_vms = $^O eq 'VMS';
125 ## Fetch Parrot information.
128 # Get parrot version.
129 # There will always be an up-to-date $parrot/VERSION
130 my $filename = File::Spec->catfile($parrotdir, "VERSION");
131 open(VERSION, "<$filename") or die "Cannot open '$filename': $!";
132 $parrot{version} = <VERSION>;
133 chomp $parrot{version};
134 close(VERSION) or die "Cannot close '$filename': $!";
136 # Get parrot configuration, stored in $parrot/myconfig
137 $filename = File::Spec->catfile($parrotdir, "myconfig");
138 open(MYCONFIG, "<$filename") or die "Cannot open '$filename': $!";
140 local $/;
141 $parrot{myconfig} = <MYCONFIG>;
143 close(MYCONFIG) or die "Cannot close '$filename': $!";
147 ## Process options.
149 Getopt::Long::Configure("no_bundling", "no_ignore_case", "auto_abbrev");
150 help() unless GetOptions
151 ( \%opts,
152 "help|h", "version|V",
153 "send", "dump", "save",
154 "from|f=s", "to|test|t=s", "editor|e=s",
155 "subject|s=s", "category|C=s", "severity|S=s",
156 "input|input-file|i=s", "output|output-file|o=s",
157 "ok", "nok", "ack!", "quiet|q!" );
160 ## Report to be sent.
162 sw: {
163 ok_report: {
164 last ok_report unless defined $opts{ok};
166 # This is an ok report, woohoo!
167 $report{to} = $std_to{ok};
168 $report{subject} = "OK: parrot $parrot{version} "
169 . "on $Config{archname} $Config{osvers}";
170 $report{body} = "Parrot reported to build OK on this system.\n";
171 $report{category} = "install";
172 $report{severity} = "none";
173 $report{body} = "";
174 last sw;
177 # Ok reports do not need body, but nok and bug reports do need
178 # a body.
179 if ( $opts{input} ) {
180 # Report was pre-written, slurp it.
181 open BODY, "<$opts{input}" or die "Can't open '$opts{input}': $!";
182 local $/;
183 $report{body} = <BODY>;
184 close BODY or die "Can't close '$opts{input}': $!";
185 } else {
186 # No file provided...
187 $report{body} = "";
191 nok_report: {
192 last nok_report unless defined $opts{nok};
194 # This a nok report, how sad... :-(
195 $report{to} = $std_to{nok};
196 $report{subject} = "Not OK: parrot $parrot{version} "
197 . "on $Config{archname} $Config{osvers}";
198 $report{category} = "install";
199 $report{severity} = "none";
200 last sw;
203 # Neither an ok nor a nok.
204 $report{to} = $std_to{bug};
205 $report{subject} = $opts{subject} || "";
206 $report{category} = $opts{category} || "";
207 $report{severity} = $opts{severity} || "";
210 # Test message, shortcuting recipent.
211 $report{to} = $opts{to} if $opts{to};
214 ## User information.
217 # Username.
218 $user = $is_mswin32 ? $ENV{USERNAME}
219 : $is_os2 ? $ENV{USER} || $ENV{LOGNAME}
220 : $is_macos ? $ENV{USER}
221 : eval { getpwuid($<) }; # May be missing
223 # User address, used in message and in Reply-To header.
224 $report{from} = $opts{from} || "";
226 # Editor
227 $editor = $ENV{VISUAL} || $ENV{EDITOR} || $ENV{EDIT}
228 || ( $is_vms && "edit/tpu" )
229 || ( $is_mswin32 && "notepad" )
230 || ( $is_macos && "" )
231 || "vi";
235 ## Mail information.
238 # Message-Id.
239 eval "use Mail::Util;";
240 if ( $@ eq "" ) {
241 $domain = Mail::Util::maildomain();
242 } elsif ($is_mswin32) {
243 $domain = $ENV{USERDOMAIN};
244 } else {
245 require Sys::Hostname;
246 $domain = Sys::Hostname::hostname();
248 $msgid = "<parrotbug_${VERSION}_${$}_".time."\@$domain>";
253 #------------------------------------------------------------#
254 # Querying subs. #
256 # Query missing information in order to have a complete report.
257 sub query_missing_info {
258 $report{subject} = "" if trivial_subject( $report{subject} );
259 $report{subject} = ask_for_subject() unless $report{subject};
260 $report{category} = ask_for_alternative( "category", \@categories)
261 unless $report{category};
262 $report{severity} = ask_for_alternative( "severity", \@severities)
263 unless $report{severity};
264 $report{from} = ask_for_return_address() unless $report{from};
265 $report{body} = ask_for_body() unless $report{body};
269 # Prompt for alternatives from a set of choices.
271 # The arguments are: the name of alternative, the choices (as an array
272 # ref), and the default answer. (first element if undef)
274 # Return the lowercased alternative chosen.
276 # Die if more than 5 wrong answers.
277 sub ask_for_alternative {
278 my ( $what, $choices, $default ) = @_;
280 print <<EOT unless $opts{quiet};
281 Please pick a $what from the following:
282 @{$choices}
286 $default ||= $choices->[0];
287 my $alt;
288 my $err = 0;
289 do {
290 die "Invalid $alt: aborting.\n" if $err++ > 5;
291 print "Please enter a $what [$default]: ";
292 $alt = <STDIN>;
293 chomp $alt;
294 $alt = $default if $alt =~ /^\s*$/;
295 } until ( ($alt) = grep /^$alt/i, @$choices );
297 print "\n\n\n";
298 return lc $alt;
302 # Prompt for a body, through an external editor.
303 sub ask_for_body {
304 unless ( $opts{quiet} ) {
305 print <<EOT;
306 Now you need to supply the bug report. Try to make the report concise
307 but descriptive. Include any relevant detail. If you are reporting
308 something that does not work as you think it should, please try to
309 include example of both the actual result, and what you expected.
311 Some information about your local parrot configuration will
312 automatically be included at the end of the report. If you are using
313 any unusual version of parrot, please try and confirm exactly which
314 versions are relevant.
318 print "Press 'Enter' to continue...\n";
319 scalar <STDIN>;
322 # Prompt for editor to use if none supplied.
323 if ( $opts{editor} ) {
324 $editor = $opts{editor};
326 } else {
327 ask_for_editor($opts{quiet} ? "" : <<EOT);
328 You will probably want to use an editor to enter the report. If the
329 default editor proposed below is the editor you want to use, then just
330 press the 'Enter' key, otherwise type in the name of the editor you
331 would like to use.
335 # Launch editor.
336 $tmpfile = generate_filename();
337 my $body = "";
338 my $err = 0;
339 do {
340 edit_bug_report( $tmpfile );
341 # Slurp bug report.
342 open BODY, "<$tmpfile" or die "Can't open '$tmpfile': $!";
344 local $/;
345 $body = <BODY>;
347 close BODY or die "Can't close '$tmpfile': $!";
348 unless ( $body ) {
349 print "\nYou provided an empty bug report!\n";
350 print "Press 'Enter' to continue...\n";
351 scalar <STDIN>;
353 die "Aborting.\n" if $err++ == 5;
354 } until ( $body );
356 return $body;
360 # Prompt for editor to use.
361 sub ask_for_editor {
362 print shift() . "Editor [$editor]: ";
363 my $entry = <STDIN>;
364 chomp $entry;
365 $editor = $entry if $entry ne "";
366 $opts{editor} = $editor;
370 # Prompt for return address, return it.
371 sub ask_for_return_address {
372 print <<EOT unless $opts{quiet};
373 Your e-mail address will be useful if you need to be contacted. If the
374 default shown below is not your full internet e-mail address, please
375 correct it.
378 # Try and guess return address
379 my ($from, $guess);
380 $guess = $ENV{'REPLY-TO'} || $ENV{REPLYTO} || "";
382 if ( ! $guess ) {
383 # Use $domain if we can.
384 if ( $domain ) {
385 $guess = $is_vms && !$Config{d_socket} ?
386 "$domain\:\:$user" : "$user\@$domain";
390 # Verify our guess.
391 print "Your address [$guess]: ";
392 $from = <STDIN>;
393 chomp $from;
394 $from = $guess if $from eq "";
395 print "\n\n\n";
396 return $from;
400 # Prompt for subject of message.
402 # Return the subject chosen.
404 # Die if more than 5 wrong subjects.
405 sub ask_for_subject {
406 print <<EOT unless $opts{quiet};
407 First of all, please provide a subject for the message. It should be a
408 concise description of the bug or problem. "parrot bug" or "parrot
409 problem" is not a concise description.
413 my $subject;
414 my $err = 0;
415 do {
416 $err and print "\nThat doesn't look like a good subject. "
417 . "Please be more verbose.\n";
418 print "Subject: ";
419 $subject = <STDIN>;
420 $subject = q{} unless defined $subject;
421 chomp $subject;
422 die "Aborting.\n" if $err++ == 5;
423 } while ( trivial_subject($subject) );
424 print "\n\n\n";
425 return $subject;
429 # Launch an editor in which to edit the bug report.
430 sub edit_bug_report {
431 my $filename = shift;
433 # Launch editor.
434 my $retval;
435 $retval = system("$editor $filename");
437 # Check whether editor run was successful.
438 die <<EOT if $retval;
439 The editor you chose ('$editor') could apparently not be run! Did you
440 mistype the name of your editor?
448 #------------------------------------------------------------#
449 # Action subs. #
452 # Display everything collected.
453 sub dump_report {
454 print "==> Dumping message...\n";
455 print format_message();
459 # Last chance to edit report.
460 sub edit_report {
461 # Prompt for editor to use if none supplied.
462 unless ( $opts{editor} ) {
463 ask_for_editor(<<EOT);
464 You will probably want to use an editor to modify the report. If the
465 default editor proposed below is the editor you want to use, then just
466 press the 'Enter' key, otherwise type in the name of the editor you
467 would like to use.
471 $tmpfile ||= $opts{input};
472 my $err = 0;
473 my $body;
474 do {
475 edit_bug_report( $tmpfile );
476 # Slurp bug report.
477 open BODY, "<$tmpfile" or die "Can't open '$tmpfile': $!";
479 local $/;
480 $body = <BODY>;
482 close BODY or die "Can't close '$tmpfile': $!";
483 unless ( $body ) {
484 print "\nYou provided an empty bug report!\n";
485 print "Press 'Enter' to continue...\n";
486 scalar <STDIN>;
488 die "Aborting.\n" if $err++ == 5;
489 } until ( $body );
491 $report{body} = $body;
495 # Format the message with everything collected and return it.
496 sub format_message {
497 my $report = "";
499 # OS, arch, compiler...
500 $report .= <<EOT;
502 osname= $Config{osname}
503 osvers= $Config{osvers}
504 arch= $Config{archname}
507 my $cc = $Config{cc};
508 #$report .= "cc= $cc $Config{${cc}.'version'}\n";
509 $report .= "cc= $cc\n";
512 # ... flags...
513 $report .= <<EOT;
515 Flags:
516 category=$report{category}
517 severity=$report{severity}
519 $report .= " ack=no\n" if ! $opts{ack};
521 # ... bug report ...
522 $report .= "---\n$report{body}\n";
524 # ... myconfig ...
525 $report .= "---\n$parrot{myconfig}\n---\n";
527 # ... and environment.
528 $report .= "Environment:\n";
529 my @env = qw[ PATH LD_LIBRARY_PATH LANG SHELL HOME LOGDIR LANGUAGE ];
530 push @env, $Config{ldlibpthname} if $Config{ldlibpthname} ne '';
531 push @env, grep /^(?:PERL|LC_|LANG|CYGWIN)/, keys %ENV;
532 my %env;
533 @env{@env} = @env;
534 for my $env (sort keys %env) {
535 my $env_value = exists $ENV{$env} ? "=$ENV{$env}\n" : " (unset)\n";
536 $report .= " $env $env_value";
539 return $report;
543 # Print synopsis + help message and exit.
544 sub help {
545 print <<EOT;
547 A program to help generate bug reports about parrot, and mail them.
548 It is designed to be used interactively. Normally no arguments will
549 be needed.
551 Simplest usage: run '$0', and follow the prompts.
552 Usage: $0 [OPTIONS] [ACTIONS]
554 Options:
555 --ok Report successful build on this system to parrot
556 developers. Only use --ok if *everything* was ok:
557 if there were *any* problems at all, use --nok.
558 --nok Report unsuccessful build on this system.
559 --subject <subject> Subject to include with the message.
560 --category <category> Category of the bug report.
561 --severity <severity> Severity of the bug report.
562 --from <address> Your email address.
563 --editor <editor> Editor to use for editing the bug report.
564 --ack, --noack Don't send a bug received acknowledgement.
565 --input-file File containing the body of the report. Use this
566 to quickly send a prepared message.
567 --output-file File where parrotbug will save its bug report.
568 --to <address> Email address to send report to. (testing only)
570 Note: you will be prompted if the program miss some information.
572 Actions:
573 --dump Dump message.
574 --save Save message.
575 --send Send message.
576 --help Print this help message and exit.
577 --version Print version information and exit.
580 exit;
584 # Save message to file.
585 sub save_report {
586 print "\n==> Saving message to file...\n";
587 if ( ! $opts{output} ) {
588 print "Enter filename to save bug report: ";
589 $opts{output} = <STDIN>;
592 open OUTPUT, ">$opts{output}" or die "Cannot open '$opts{output}': $!";
593 print OUTPUT format_message();
594 close OUTPUT or die "Cannot open '$opts{output}': $!";
596 print "Message saved.\n";
600 # Send message to final recipient.
601 sub send_report {
602 print "==> Sending message to recipient...\n";
604 # On linux certain mail implementations won't accept the subject
605 # as "~s subject" and thus the Subject header will be corrupted
606 # so don't use Mail::Send to be safe
607 eval "require Mail::Send";
608 if ( $@ eq "" && !$is_linux) {
609 my $msg = new Mail::Send Subject => $report{subject}, To => $report{to};
610 $msg->add( "Reply-To", $report{from} );
612 my $fh = $msg->open;
613 print $fh format_message();
614 $fh->close;
616 print "\nMessage sent.\n";
618 } else {
619 my $sendmail = "";
620 for ( qw[ /usr/lib/sendmail /usr/sbin/sendmail
621 /usr/ucblib/sendmail /var/qmail/bin/sendmail ] ) {
622 $sendmail = $_, last if -e $_;
625 die <<EOT if $sendmail eq "";
626 I am terribly sorry, but I cannot find sendmail, or a close
627 equivalent, and the perl package Mail::Send has not been installed, so
628 I can't send your bug report. We apologize for the inconvenience.
630 So you may attempt to find some way of sending your message, it has
631 been left in the file '$tmpfile'.
634 open(SENDMAIL, "|$sendmail -t -oi") || die "'|$sendmail -t -oi' failed: $!";
636 print SENDMAIL <<EOT;
637 To: $report{to}
638 Subject: $report{subject}
639 Reply-To: $report{from}
640 Message-Id: $msgid
644 print SENDMAIL format_message();
645 if (close(SENDMAIL)) {
646 printf "\nMessage sent.\n";
647 } else {
648 warn "\nSendmail returned status '", $? >> 8, "'\n";
654 # Print version information (of the parrotbug program) and exit.
655 sub version {
656 print <<"EOT";
658 This is $0, version $VERSION.
661 exit;
665 # Check whether actions have been provided on comand-line, otherwise
666 # prompt for what to do with bug report.
667 sub what_next {
668 dump_report() if $opts{dump};
669 save_report() if $opts{save};
670 send_report() if $opts{send};
672 return if $opts{dump} || $opts{save} || $opts{send};
674 # No actions provided on command-line, prompt for action.
676 my $action;
677 do {
678 print "Action (send,display,edit,save,quit): ";
679 $action = <STDIN>;
680 sw: for ($action) {
681 dump_report(), last sw if /^d/i;
682 edit_report(), last sw if /^e/i;
683 save_report(), last sw if /^sa/i;
684 send_report(), last sw if /^se/i;
685 print "Uh?\n" unless /^q/i;
687 } until ( $action =~ /^q/i );
691 __END__
693 =head1 NAME
695 parrotbug - Parrot Bug Reporter
697 =head1 SYNOPSIS
699 % ./parrotbug [options] [actions]
701 =head1 DESCRIPTION
703 A program to help generate bug reports about parrot, and mail them.
704 It is designed to be used interactively. Normally no arguments will
705 be needed.
708 =head1 COMMAND-LINE SWITCHES
711 =head2 Options
713 Note: you will be prompted if the program miss some information.
715 =over 4
717 =item B<--nok>
719 Report unsuccessful build on this system to parrot developers.
721 =item B<--ok>
723 Report successful build on this system to parrot developers Only use
724 C<--ok> if B<everything> was ok; if there were B<any> problems at all,
725 use C<--nok>.
727 =item B<--subject>
729 Subject of the report. You will be prompted if you don't supply one on
730 the command-line.
732 =item B<--category>
734 Category of the bug report. You will be prompted if you don't supply
735 one on the command-line.
737 =item B<--severity>
739 Severity of the bug report. You will be prompted if you don't supply
740 one on the command-line.
742 =item B<--address>
744 Your email address. The program will try to guess one if you don't
745 provide one, but you'll still need to validate it.
747 =item B<--editor>
749 Editor to use for editing the bug report.
751 =item B<--ack>, B<--noack>
753 Don't send a bug received acknowledgement.
755 =item B<--input-file>
757 File containing the body of the report. Use this to quickly send a
758 prepared message.
760 =item B<--output-file>
762 File where parrotbug will save its bug report, if you ask it to do so.
764 =item B<--to>
766 Email address to send report to. (for testing purposes only)
768 =back
771 =head2 Actions
773 You can provide more than one action on the command-line. If none is
774 supplied, then you will be prompted for what to do.
776 =over 4
778 =item B<--dump>
780 Dump formatted report on standard output.
782 =item B<--save>
784 Save message to a file, in order for you to send it later from your
785 own. See C<--output> flag.
787 =item B<--send>
789 Try to send a mail with the report.
791 =item B<--help>
793 Print a short synopsis and exit.
795 =item B<--version>
797 Print version information and exit.
799 =back
802 =head1 AUTHORS
804 Jerome Quelin (E<lt>jquelin@cpan.orgE<gt>), with lots of good stuff taken from perlbug.
807 =head1 SEE ALSO
809 perlbug(1), parrot(1), diff(1), patch(1)
811 =cut
813 # Local Variables:
814 # mode: cperl
815 # cperl-indent-level: 4
816 # fill-column: 100
817 # End:
818 # vim: expandtab shiftwidth=4: