#!/usr/bin/perl # # Copyright (C) 2004-2011, Parrot Foundation. # eval 'exec perl -w -S $0 ${1+"$@"}' if $running_under_some_shell; use strict; use warnings; use Config; use File::Spec; use Getopt::Long; my $VERSION = "1.0"; my $parrotdir = File::Spec->curdir(); my ( %opts, %parrot, %report ); my ( $editor, $user, $domain, $msgid, $tmpfile ); my ( $is_linux, $is_macos, $is_mswin32, $is_os2, $is_vms ); my @categories = qw[ core docs install library utilities languages ]; my @severities = qw[ critical high medium low wishlist none ]; #------------------------------------------------------------# # Main program. # init(); help() if $opts{help}; version() if $opts{version}; explain_parrotbug() unless $opts{quiet}; query_missing_info(); what_next(); unlink $tmpfile; exit; # Explain what C is. sub explain_parrotbug { print <lists.parrot.org. EOT } #------------------------------------------------------------# # Utils subs. # # Generate random filename to edit report. sub generate_filename { my $dir = File::Spec->tmpdir(); my $filename = "bugrep0$$"; $filename++ while -e File::Spec->catfile($dir, $filename); $filename = File::Spec->catfile($dir, $filename); return $filename; } # Check whether a summary is trivial. A summary is not considered trivial # if it's an ok or a nok report. # Return 1 if trivial, 0 otherwise (summary acceptable). sub trivial_summary { my $summary = shift; return 0 if $opts{ok} || $opts{nok}; if ( $summary =~ /^(y(es)?|no?|help|parrot( (bug|problem))?|bug|problem)$/i || length($summary) < 4 || $summary !~ /\s/ ) { return 1; } else { return 0; } } #------------------------------------------------------------# # Init subs. # # Initialize the program. # # Get parrot information, process the options, create the message # information (summary, to, body, etc.) depending on the type of report # (ok, nok or bug report). sub init { $is_linux = lc($^O) eq 'linux'; $is_mswin32 = $^O eq 'MSWin32'; $is_os2 = $^O eq 'os2'; $is_vms = $^O eq 'VMS'; ## ## Fetch Parrot information. ## # Get parrot version. # There will always be an up-to-date $parrot/VERSION my $filename = File::Spec->catfile($parrotdir, "VERSION"); open my $VERSION, '<', $filename or die "Cannot open '$filename': $!"; $parrot{version} = <$VERSION>; chomp $parrot{version}; close $VERSION or die "Cannot close '$filename': $!"; # Get parrot configuration, stored in $parrot/myconfig $filename = File::Spec->catfile($parrotdir, "myconfig"); open my $MYCONFIG, '<', $filename or die "Cannot open '$filename': $!"; { local $/; $parrot{myconfig} = <$MYCONFIG>; } close $MYCONFIG or die "Cannot close '$filename': $!"; ## ## Process options. ## Getopt::Long::Configure("no_bundling", "no_ignore_case", "auto_abbrev"); help() unless GetOptions ( \%opts, "help|h", "version|V", "dump", "save", "from|f=s", "to|test|t=s", "editor|e=s", "summary|s=s", "category|C=s", "severity|S=s", "input|input-file|i=s", "output|output-file|o=s", "ok", "nok", "ack!", "quiet|q!" ); ## ## Report to be sent. ## sw: { ok_report: { last ok_report unless defined $opts{ok}; # This is an ok report, woohoo! $report{summary} = "OK: parrot $parrot{version} " . "on $Config{archname} $Config{osvers}"; $report{body} = "Parrot reported to build OK on this system.\n"; $report{category} = "install"; $report{severity} = "none"; $report{body} = ""; last sw; }; # Ok reports do not need body, but nok and bug reports do need # a body. if ( $opts{input} ) { # Report was pre-written, slurp it. open my $BODY, '<', $opts{input} or die "Can't open '$opts{input}': $!"; local $/; $report{body} = <$BODY>; close $BODY or die "Can't close '$opts{input}': $!"; } else { # No file provided... $report{body} = ""; } nok_report: { last nok_report unless defined $opts{nok}; # This a nok report, how sad... :-( $report{summary} = "Not OK: parrot $parrot{version} " . "on $Config{archname} $Config{osvers}"; $report{category} = "install"; $report{severity} = "none"; last sw; }; # Neither an ok nor a nok. $report{summary} = $opts{summary} || ""; $report{category} = $opts{category} || ""; $report{severity} = $opts{severity} || ""; }; # Test message, shortcutting recipient. $report{to} = $opts{to} if $opts{to}; ## ## User information. ## # Username. $user = $is_mswin32 ? $ENV{USERNAME} : $is_os2 ? $ENV{USER} || $ENV{LOGNAME} : $is_macos ? $ENV{USER} : eval { getpwuid($<) }; # May be missing # User address, used in message $report{from} = $opts{from} || ""; # Editor $editor = $ENV{VISUAL} || $ENV{EDITOR} || $ENV{EDIT} || ( $is_vms && "edit/tpu" ) || ( $is_mswin32 && "notepad" ) || ( $is_macos && "" ) || "vi"; } #------------------------------------------------------------# # Querying subs. # # Query missing information in order to have a complete report. sub query_missing_info { $report{summary} = "" if trivial_summary( $report{summary} ); $report{summary} = ask_for_summary() unless $report{summary}; $report{category} = ask_for_alternative( "category", \@categories) unless $report{category}; $report{severity} = ask_for_alternative( "severity", \@severities) unless $report{severity}; $report{from} = ask_for_return_address() unless $report{from}; $report{body} = ask_for_body() unless $report{body}; } # Prompt for alternatives from a set of choices. # # The arguments are: the name of alternative, the choices (as an array # ref), and the default answer. (first element if undef) # # Return the lowercased alternative chosen. # # Die if more than 5 wrong answers. sub ask_for_alternative { my ( $what, $choices, $default ) = @_; print <[0]; my $alt; my $err = 0; do { die "Invalid $alt: aborting.\n" if $err++ > 5; print "Please enter a $what [$default]: "; $alt = ; chomp $alt; $alt = $default if $alt =~ /^\s*$/; } until ( ($alt) = grep /^$alt/i, @$choices ); print "\n\n\n"; return lc $alt; } # Prompt for a body, through an external editor. sub ask_for_body { unless ( $opts{quiet} ) { print <; } # Prompt for editor to use if none supplied. if ( $opts{editor} ) { $editor = $opts{editor}; } else { ask_for_editor($opts{quiet} ? "" : <; } close BODY or die "Can't close '$tmpfile': $!"; unless ( $body ) { print "\nYou provided an empty bug report!\n"; print "Press 'Enter' to continue...\n"; scalar ; } die "Aborting.\n" if $err++ == 5; } until ( $body ); return $body; } # Prompt for editor to use. sub ask_for_editor { print shift() . "Editor [$editor]: "; my $entry = ; chomp $entry; $editor = $entry if $entry ne ""; $opts{editor} = $editor; } # Prompt for return address, return it. sub ask_for_return_address { print <; chomp $from; $from = $guess if $from eq ""; print "\n\n\n"; return $from; } # Prompt for summary of message. # # Return the summary chosen. # # Die if more than 5 wrong summaries. sub ask_for_summary { print <; $summary = q{} unless defined $summary; chomp $summary; die "Aborting.\n" if $err++ == 5; } while ( trivial_summary($summary) ); print "\n\n\n"; return $summary; } # Launch an editor in which to edit the bug report. sub edit_bug_report { my $filename = shift; # Launch editor. my $retval; $retval = system($editor, $filename); # Check whether editor run was successful. die < Dumping message...\n"; my $report = format_message(); if ( defined($ENV{PAGER}) ) { open(my $ofh, '|-', $ENV{PAGER}); print {$ofh} $report; close $ofh; } else { print $report; } } # Last chance to edit report. sub edit_report { # Prompt for editor to use if none supplied. unless ( $opts{editor} ) { ask_for_editor(<; } close $BODY or die "Can't close '$tmpfile': $!"; unless ( $body ) { print "\nYou provided an empty bug report!\n"; print "Press 'Enter' to continue...\n"; scalar ; } die "Aborting.\n" if $err++ == 5; } until ( $body ); $report{body} = $body; } # Format the message with everything collected and return it. sub format_message { my $report = ""; # ... summary ... $report .= "Summary: $report{summary}\n"; # ... sender ... $report .= "Reported by: $report{from}\n"; # ... bug report ... $report .= "---\n$report{body}\n"; # OS, arch, compiler... $report .= < Summary to include with the message. --category Category of the bug report. --severity Severity of the bug report. --from
Your email address. --editor Editor to use for editing the bug report. --ack, --noack Don't send a bug received acknowledgement. --input-file File containing the body of the report. Use this to quickly send a prepared message. --output-file File where parrotbug will save its bug report. Note: you will be prompted if the program miss some information. Actions: --dump Dump message. --save Save message. --help Print this help message and exit. --version Print version information and exit. EOT exit; } # Save message to file. sub save_report { print "\n==> Saving message to file...\n"; if ( ! $opts{output} ) { print "Enter filename to save bug report: "; chomp($opts{output} = ); } open my $OUTPUT, ">", $opts{output} or die "Cannot open '$opts{output}': $!"; print $OUTPUT format_message(); close $OUTPUT or die "Cannot close '$opts{output}': $!"; print <; sw: for ($action) { dump_report(), last sw if /^d/i; edit_report(), last sw if /^e/i; save_report(), last sw if /^sa/i; print "Uh?\n" unless /^q/i; }; } until ( $action =~ /^q/i ); } sub describe_actions { my $str = < Report unsuccessful build on this system to parrot developers. =item B<--ok> Report successful build on this system to parrot developers Only use C<--ok> if B was ok; if there were B problems at all, use C<--nok>. =item B<--summary> Summary of the report. You will be prompted if you don't supply one on the command-line. =item B<--category> Category of the bug report. You will be prompted if you don't supply one on the command-line. =item B<--severity> Severity of the bug report. You will be prompted if you don't supply one on the command-line. =item B<--address> Your email address. The program will try to guess one if you don't provide one, but you'll still need to validate it. =item B<--editor> Editor to use for editing the bug report. =item B<--output-file> File where parrotbug will save its bug report, if you ask it to do so. =back =head2 Actions You can provide more than one action on the command-line. If none is supplied, then you will be prompted for what to do. =over 4 =item B<--dump> Dump formatted report on standard output. =item B<--save> Save message to a file, in order for you to send it later from your own. See C<--output> flag. =item B<--help> Print a short synopsis and exit. =item B<--version> Print version information and exit. =back =head1 SEE ALSO perlbug(1), parrot(1), diff(1), patch(1) =cut # Local Variables: # mode: cperl # cperl-indent-level: 4 # fill-column: 100 # End: # vim: expandtab shiftwidth=4: