#!/usr/bin/perl # allhelp.pl for Linux # Display help for the Bourne Shell's internal commands and reserved words # Written by Rob van der Woude # http://www.robvanderwoude.com use strict; use warnings; use Config; use CGI qw(escapeHTML); my $scriptver = '1.02'; # Abort if not running on Linux if ( lc( $Config{osname} ) ne 'linux' ) { print "This script will run on Linux systems only."; exit 1; } my @cmds; my @builtins; my @coreutils; my @reserved; my %helplong; my %helpshort; my $outfile = 'allhelp_bash.html'; # Create a list of all internal commands for which help is available open( SHELL, "-|", "bash", "-c", "compgen -b" ); while( ) { chomp; push( @builtins, $_ ); } close( SHELL ); # Create a list of all reserved words for which help is available open( SHELL, "-|", "bash", "-c", "compgen -k" ); while( ) { chomp; push( @reserved, $_ ); } close( SHELL ); # Create a list of coreutils open( SHELL, '-|', 'bash', '-c', 'info | grep -F "(coreutils)"'); while ( ) { chomp; if ( $_ =~ m/^\*\s+([a-z][a-z0-9]+):\s+\(coreutils\)\1/ ) { $_ =~ /^\*\s+([a-z][a-z0-9]+):\s+\(coreutils\)\1\s[^.]+\.\s+(.+)$/; push( @coreutils, $1 ); my $anchor = &Anchor( $1 ); $helpshort{ $anchor } = $2; } } close( SHELL ); # Merge the lists of commands push( @cmds, @builtins ); push( @cmds, @reserved ); push( @cmds, @coreutils ); # Sort the list of commands, punction and numbers first @cmds = sort { if ( $a =~ m/^[a-z]/i and $b !~ m/^[a-z]/i ) { return 1; } elsif ( $a !~ m/^[a-z]/i and $b =~ m/^[a-z]/i ) { return -1; } else { return $a cmp $b; } } @cmds; # Calculate available column width for 3 columns display with 3 characters spacing my $cols = `tput cols`; my $rows = `tput lines`; my $colwidth = int( ( $cols - 6 ) / 3 ); # First display the Linux version print &LinuxVer( ) . "\n\n"; # Display the list of commands in 3 columns for ( my $i = 0; $i < @cmds; $i += 3 ) { printf '%-' . $colwidth . 's', $cmds[$i]; if ( $i + 1 < @cmds ) { printf ' %-' . $colwidth . 's', $cmds[$i+1]; } if ( $i + 2 < @cmds ) { printf ' %-' . $colwidth . 's', $cmds[$i+2]; } print "\n"; } # Display help for each command in the array foreach my $cmd ( @cmds ) { print "\nHelp for '$cmd'\n\n"; my $anchor = &Anchor( $cmd ); my $helptext = ''; if ( grep { $_ eq $cmd } @coreutils ) { # Use info command for coreutils open( SHELL, "-|", "bash", "-c", "info $cmd | grep -F \"\"" ); # Read resulting help text from STDOUT while ( ) { $helptext .= $_; } close( SHELL ); if ( $helptext ) { $helptext =~ s/\342\200\230/'/g; $helptext =~ s/\342\200\231/'/g; $helptext =~ s/^\/([^\n]*\n){3}\d+\.\d+\s+//; $helptext =~ s/\n=+\n/\n/g; $helplong{ $anchor } = $helptext; } } else { # Use help command for reserved words and builtin commands # Escape the commands, otherwise bash may break on parenthesis my $esccmd = quotemeta( $cmd ); # Run help command with escaped command name open( SHELL, "-|", "bash", "-c", "help $esccmd 2> /dev/null" ); # Read resulting help text from STDOUT while ( ) { $helptext .= $_; } close( SHELL ); if ( "$helptext" !~ m/^\s*$/g ) { $helplong{ $anchor } = $helptext; # Display help text print escapeHTML( $helptext ); # Get short help text summary my $firstline = $cmd . ': ' . $cmd . '[^\n]*\n\s+'; if ( $cmd ne $esccmd ) { $firstline = $esccmd . '[^a-z:\n]*: ' . $esccmd . '[^\n]*\n\s+'; } "$helptext" =~ /$firstline((.|\n)*?)\n\s*\n/; $helpshort{ $anchor } = $1; } } } print "\n\n"; # Remove commands from list if no help is available # Use reverse order to prevent skipping 1 each time an element is removed from the array for ( my $i = @cmds - 1; $i >= 0; $i-- ) { my $cmd = $cmds[$i]; my $anchor = &Anchor( $cmd ); unless ( exists( $helplong{ $anchor } ) ) { my $index = 0; $index++ until $cmds[$index] eq $cmd or $index == @cmds; splice( @cmds, $index, 1 ); } } # Write the help to a HTML file my $htmlhelp = &HTMLHead( ); $htmlhelp .= &HTMLList( ); foreach my $cmd ( @cmds ) { $htmlhelp .= &HTMLCommand( $cmd ); } $htmlhelp .= &HTMLFoot( ); open( HTML, ">", $outfile ) or die( "Error writing HTML to file \"$outfile\"." ); print HTML $htmlhelp; close( HTML ); # Open the newly created HTML file in the default browser (may generate warning messages with Firefox) if ( `which sensible-browser` ) { exec( 'sensible-browser', $outfile ); } elsif ( `which xdg-open > /dev/null` ) { exec( 'xdg-open', $outfile ); } elsif ( `which gnome-open > /dev/null` ) { exec( 'gnome-open', $outfile ); } elsif ( `which x-www-browser` ) { exec( 'x-www-browser', $outfile ); } else { my $browser = `gconftool -g /desktop/gnome/url-handlers/http/command 2> /dev/null`; if ( $browser ) { exec( $browser, $outfile ); } else { die "Unable to locate default browser; open \"$outfile\" manually."; } } sub Alphabet( ) { my $html = "\n\n"; my $found = 0; foreach my $c ( @cmds ) { my $A = uc( substr( $c, 0, 1 ) ); if ( $A !~ /[A-Z]/ ) { $found = 1; } } if ( $found ) { $html .= "\t\n"; } else { $html .= "\t\n"; } foreach my $a ( 'a' .. 'z' ) { my $A = uc( $a ); my $found = 0; foreach my $c ( @cmds ) { if ( lc( substr( $c, 0, 1 ) ) eq $a ) { $found = 1; } } if ( $found ) { $html .= "\t\n"; } else { $html .= "\t\n"; } } $html .= "\n
##$A$A
\n"; return $html; } sub Anchor( ) { my $cmd = $_[0]; my $firstchar = substr( $cmd, 0, 1 ); my $anchor = $cmd; if ( $firstchar eq "." ) { $anchor = "dot"; } if ( $firstchar eq ":" ) { $anchor = "colon"; } if ( $firstchar eq '!' ) { $anchor = "exclamation"; } if ( $firstchar eq "(" ) { $anchor = "parentheses"; } if ( $firstchar eq "[" ) { $anchor = "brackets"; } if ( $firstchar eq "{" ) { $anchor = "braces"; } return $anchor; } sub HTMLCommand( ) { my $cmd = $_[0]; my $anchor = &Anchor( $cmd ); my $type; my $class; if ( grep { $_ eq $cmd } @coreutils ) { $class = 'CoreUtils'; $type = "

coreutils

\n\n"; } elsif ( grep { $_ eq $cmd } @builtins ) { $class = 'BuiltIn'; $type = "

builtin command

\n\n"; } elsif ( grep { $_ eq $cmd } @reserved ) { $class = 'ReservedWord'; $type = "

reserved word

\n\n"; } else { $class = ''; $type = ''; } my $html = ''; if ( $class ne '' ) { $html .= "
\n\n"; } $html .= "

$cmd

\n\n$type
";
	$html .= escapeHTML( $helplong{ $anchor } );
	$html .= "
\n\n\n\n

 

\n\n"; if ( $class ne '' ) { $html .= "
\n\n"; } return $html; } sub HTMLFoot( ) { my @now = localtime( ); my $html = "\n\n\t\n\t\n"; $html .= "\t\n\n
Note: Not all reserved words are listed here, only the ones for which help is available.
\n\n"; $html .= "

 

\n\n
\n\n

This HTML help was generated on "; $html .= sprintf( "%4d-%02d-%02d", $now[5] + 1900, $now[4], $now[3] ); $html .= " by "; $html .= "allhelp.pl, Version $scriptver
\n"; $html .= "Written by Rob van der Woude
\n"; $html .= "http://www.robvanderwoude.com

\n\n"; $html .= "
\n\n\n\n\n\n

 

\n\n\n"; } sub HTMLHead( ) { my $title = &LinuxVer( ); my $html = <<"END_OF_HTML"; Help for $title

Help for

CoreUtils, internal Bash commands

and reserved words (*)

$title

 

END_OF_HTML $html .= Alphabet( ); return $html; } sub HTMLList( ) { my $lastfirstchar = ''; my $html = "\n"; $html .= "\n\t\n\n"; $html .= "\n\t\n\n"; foreach my $cmd ( @cmds ) { my $firstchar = ''; if ( "$cmd" =~ m/^[a-z]/ ) { "$cmd" =~ /^([a-z])/; $firstchar = uc( $1 ); if ( $firstchar ) { if ( $firstchar ne $lastfirstchar ) { $html .= "\n\t\n\n"; $html .= "\n\t\n\n"; $html .= "\n\t\n\n"; } } } $lastfirstchar = $firstchar; my $anchor = &Anchor( $cmd ); my $help = escapeHTML( $helpshort{ $anchor } ); my $class; if ( grep { $_ eq $cmd } @coreutils ) { $class = ' class="CoreUtils"'; } elsif ( grep { $_ eq $anchor } @builtins ) { $class = ' class="BuiltIn"'; } elsif ( grep { $_ eq $anchor } @reserved ) { $class = ' class="ReservedWord"'; } else { $class = ''; } $help =~ s/[\t ]+/ /g; $html .= "\n\t\n\t\n\n"; } $html .= "
Punctuation
 
 
$firstchar
 
$cmd$help
\n\n

 

\n\n"; return $html; } sub LinuxVer( ) { my $os = ''; open( SHELL, "-|", "bash", "-c", "uname -sirv" ); while( ) { chomp( $os = $_ ); } close( SHELL ); if ( $os eq '' ) { $os = "$Config{osname} $Config{osvers}"; } return $os; }