#!/usr/bin/perl -w # $Id: linux-help,v 1.16 1999/11/08 21:49:54 root Exp root $ # Copyright (c) Mark Summerfield 1999. All Rights Reserved. # May be used/distributed under the GPL. # WARNING - linux-help is provided as an example of QuickForm use, (although # I now use it instead of dwww), and it may not be secure. # NOTE - linux-help has hard-coded paths for a Debian Linux system - your # paths may/will differ. See "CHANGE THESE FOR YOUR LOCAL SYSTEM" for the # paths that you will need to change. linux-help itself should be placed in # your cgi-bin directory and made executable. # linux-help has only ever been run as root on a local system - multi-user use # is not supported (since I do no record locking), in other words its only for # a single person to use. This program also uses the undocumented colour() # function from QuickForm. (This is one of CGI::QuickForm's example programs.) # TODO Configure option, e.g. to add/del paths to/from $Data{"$PREFIX PATH"} # so that hard-coding is not necessary # TODO Cache page searches, i.e. cache lists of links? # TODO Keyword searching use strict ; use CGI qw( :standard :html3 ) ; use CGI::QuickForm qw( show_form colour ) ; use DB_File ; use Fcntl ; # For DB_File constants. use File::Find ; use HTML::Entities ; use Storable qw( freeze thaw ) ; use URI::Escape ; use vars qw( $VERSION ) ; $VERSION = '1.01' ; use vars qw( $URL $PREDEFINED_PAGES ) ; ################### CHANGE THESE FOR YOUR LOCAL SYSTEM ############## # This is the db file where linux-help stores its configuration info - it must # be rw by linux-help running as a CGI script. my $DB_FILE = '/root/web/db/linux-help.db' ; # Change these to reflect your local situation; multiple paths are supported # separated by colons in the usual way. my $PATH_DOC = "/usr/doc" ; my $PATH_INFO = "/usr/info" ; my $PATH_MAN = "/usr/man:/usr/X11R6/man" ; my $PATH_POD = "/usr/doc/perl5:/usr/doc:/usr/lib/perl5:/root/lib" ; # This must be rw by this script. my $TMP = '/tmp' ; ################### END OF LOCAL CHANGES ############################ $| = 1 ; # Autoflush. my $TITLE = 'Linux Help' ; my $SEARCH = 'Search' ; my $NEW_SEARCH = 'New Search' ; my $NEW_TERM = 'New Term' ; my $PREV_TERM = 'Prev Term' ; my $PREV_PAGE = 'Prev Page' ; my $NEW_KEYWORD = 'New Keyword' ; my $SEARCH_PAGES = 'Search Pages' ; my $SEARCH_FOR = 'Search/Show' ; my $IGNORE_CASE = 'Ignore Case' ; my $DEL_PAGE = 'Del Page' ; my $DEL_PAGES = 'Del Pages' ; my $DEL_TERM = 'Del Term' ; my $DEL_TERMS = 'Del Terms' ; my $PREFIX = "\x01" ; my $SEARCH_TERMS = "$PREFIX SEARCH_TERMS" ; my $SHOWN = "$PREFIX SHOWN" ; my %Data ; my %Show ; my %SearchTerm ; # key is the term, value is the number of times used my @Term ; # Array of terms to be matched my $CaseSensitive = '(?i)' ; my %Found ; &initialise ; if( query_string() =~ /file=([^&]+)/o ) { &show_file( $1 ) ; } elsif( query_string() =~ /term=([^&]+)/o ) { my $term = $1 ; query_string() =~ /type=([^&]+)/o ; my $pagetype = $1 ; param( $SEARCH_PAGES, ( $pagetype ) ) ; param( $IGNORE_CASE, 'X' ) ; &show_matches( $term ) ; } else { my $footer = $PREDEFINED_PAGES ; #$footer .= hr . &show_config ; my @pages = sort @{ thaw( $Data{"$PREFIX PAGETYPE"} ) } ; show_form( -TITLE => $TITLE, -HEADER => header() . start_html( $TITLE ) . h3( $TITLE ), -FOOTER => $footer, -ACCEPT => \&on_valid_form, -FIELDS => [ { -LABEL => $PREV_PAGE, -TYPE => 'scrolling_list', '-values' => [ sort { lc $a cmp lc $b } keys %Show ], -size => 1, }, { -LABEL => $PREV_TERM, -TYPE => 'scrolling_list', '-values' => [ sort { $SearchTerm{$b} <=> $SearchTerm{$a} } keys %SearchTerm ], -size => 1, }, { -LABEL => $NEW_TERM, -size => 24, }, { -LABEL => $IGNORE_CASE, -TYPE => 'checkbox', -value => 'X', -checked => 'checked', -label => '', }, { -LABEL => $SEARCH_PAGES, -TYPE => 'checkbox_group', '-values' => \@pages, -default => \@pages, }, { -LABEL => $SEARCH_FOR, -TYPE => 'radio_group', '-values' => [ $PREV_PAGE, $PREV_TERM, $NEW_TERM, $NEW_KEYWORD ], }, ], -BUTTONS => [ { -name => $SEARCH_FOR }, { -name => $DEL_PAGE }, { -name => $DEL_PAGES }, { -name => $DEL_TERM }, { -name => $DEL_TERMS }, ], # If eventually we want to offer configuration we'll add the options # as fields and add a Configure button. ) ; } &clean_and_quit ; sub show_file { my $file = uri_unescape( shift ) ; my( $type, $compress ) = $file =~ /\.([^.]+)(?:\.(gz|z|zip|Z))?$/o ; ( $type, $compress ) = ( 'txt', $type ) if $type =~ /^(?:gz|z|zip|Z)$/o ; $type = 'man' if $type =~ /^\d[a-z]{0,2}$/o ; # Remember for next time. unless( $Show{ &file_to_name( $file ) } ) { $Show{ &file_to_name( $file ) } = $file ; $Data{$SHOWN} = freeze( \%Show ) ; } $compress ||= '' ; my $TIMEOUT = 30 ; my @lines ; local $_ ; if( $type !~ /man/o ) { if( $compress ) { @lines = `zcat $file` ; } else { @lines = `cat $file` ; } } print header ; CASE : { if( $type =~ /html?/o ) { # Should never get here! print @lines ; last CASE ; } if( $type =~ /te?xt/o ) { print start_html( $file ), h3( colour( 'BLUE', $file ) ), ; &new_search ; print "
" ; foreach( @lines ) { print encode_entities( $_ ) ; } print "" ; &new_search ; print end_html ; last CASE ; } # if( $type =~ /pod|pm/o ) { #/ # # Doesn't work except from the command line. # if( $compress ) { # print `zcat $file | pod2html --norecurse` ; # } # else { # print `pod2html --norecurse --infile $file` ; # } # last CASE ; # } if( $type =~ /man/o ) { print start_html( $file ), h3( colour( 'BLUE', $file ) ), ; &new_search ; print "
" ; my $temp = $file ; $temp =~ s,.+/,,o ; $temp = "$TMP/$temp.cache" ; &full_system( "man -l $file > $temp" ) ; my $i = 0 ; sleep 1 while not -e $temp and $i++ < $TIMEOUT ; if( -e $temp ) { @lines = `cat $temp` ; foreach( @lines ) { s/.\cH//g ; print encode_entities( $_ ) ; } unlink $temp if $Data{"$PREFIX DEL_CACHE"} ; } else { print "Timed out after $i seconds" ; } print "" ; &new_search ; print end_html ; last CASE ; } if( $type =~ /info/o ) { print start_html( $file ), h3( colour( 'BLUE', $file ) ), ; &new_search ; print "
" ; print `info --file $file` ; print "" ; &new_search ; print end_html ; last CASE ; } DEFAULT : { print start_html( $TITLE ), h3( colour( 'BLUE', $TITLE ) ), ; &new_search ; print p( colour( 'RED', "BUG: File
", ; foreach( @lines ) { print encode_entities( $_ ) ; } print "" ; &new_search ; print end_html ; } } } # Copied from Programming Perl 2nd Ed (Blue Camel). sub full_system { my $rc = 0xFFFF & system @_ ; my $result = '' ; if( $rc == 0 ) { $result = "ran with normal exit\n" ; } elsif( $rc == 0xFF00 ) { $result = "command failed: $!\n" ; } elsif( $rc > 0x80 ) { $rc >>= 8 ; $result = "ran with non-zero exit status $rc\n" ; } else { $result = "ran with " ; if( $rc & 0x80 ) { $rc &= ~0x80 ; $result .= "core dump from " ; } $result .= "signal $rc\n" ; } # print qq{$result} if $result ; ( $rc != 0 ) ; } sub on_valid_form { if( param( $DEL_PAGE ) and param( $PREV_PAGE ) ) { delete $Show{ param( $PREV_PAGE ) } ; $Data{$SHOWN} = freeze( \%Show ) ; &show_del( 'Page', param( $PREV_PAGE ) ) ; } elsif( param( $DEL_PAGES ) and param( $PREV_PAGE ) ) { %Show = () ; $Data{$SHOWN} = freeze( \%Show ) ; &show_del( 'All Pages', '' ) ; } elsif( param( $DEL_TERM ) and param( $PREV_TERM ) ) { delete $SearchTerm{ param( $PREV_TERM) } ; $Data{$SEARCH_TERMS} = freeze( \%SearchTerm ) ; &show_del( 'Term', param( $PREV_TERM ) ) ; } elsif( param( $DEL_TERMS ) and param( $PREV_TERM ) ) { %SearchTerm = () ; $Data{$SEARCH_TERMS} = freeze( \%SearchTerm ) ; &show_del( 'All Terms', '' ) ; } elsif( param( $SEARCH_FOR ) eq $NEW_KEYWORD ) { print header, start_html( $TITLE ), h2( $TITLE ), h3( colour( 'RED', "Find Keyword not implemented yet." ) ), ; &new_search ; print end_html ; } else { my $term ; my $find = param( $SEARCH_FOR ) ; if( $term = param( $NEW_TERM ) ) { # Look for a new term if given. &show_matches( $term ) ; } elsif( $term = param( $PREV_TERM ) and $find ne $PREV_PAGE ) { # Look for an existing term unless we're looking for a previous page. &show_matches( $term ) ; } elsif( param( $PREV_PAGE ) ) { &show_file( $Show{ param( $PREV_PAGE ) } ) ; } else { print header, start_html( $TITLE ), h2( $TITLE ), h3( colour( 'RED', "Invalid search/show" ) ), p( "Did you choose a $PREV_TERM without checking the $PREV_TERM " . "check box?" ), ; &new_search ; print end_html ; } } } sub show_del { my( $type, $value ) = @_ ; print header, start_html( $TITLE ), h2( $TITLE ), h3( qq{Deleted $type $value} ), ; &new_search ; print end_html ; } sub show_matches { my $term = shift ; @Term = split ' ', $term ; local $_ ; &reduce_terms if scalar keys %SearchTerm > $Data{"$PREFIX MAX_TERMS"} ; $SearchTerm{$term}++ if $term !~ /^\d[a-z]*\.?$/o ; # Always freeze as early as possible in case the user interrupts. $Data{$SEARCH_TERMS} = freeze( \%SearchTerm ) ; print header, start_html( $TITLE ), h2( $TITLE ), h3( "Files matching ", join " or ", map { qq{$_} } @Term ), ; &new_search ; my @path = () ; my %path = %{ thaw( $Data{"$PREFIX PATH"} ) } ; foreach my $pagetype ( param( $SEARCH_PAGES ) ) { push @path, split /:/, $path{$pagetype} ; } $CaseSensitive = '' unless param( $IGNORE_CASE ) eq 'X' ; %Found = () ; find( \&wanted, @path ) ; print "
Configuration | |
Paths: | |
$key | $path |
database | $DB_FILE |
Program: | $URL |
Del cache: | $Data{"$PREFIX DEL_CACHE"}} . qq{ |
Max Terms: } . qq{ | $Data{"$PREFIX MAX_TERMS"} |
Page types: | $pagetype |
man 1\ \ \ Executable programs or shell commands
man 1db\ DB
man 1p\ \ Perl Functions
man 1x\ \ X Executable programs or shell commands
man 2\ \ \ System calls (functions provided by the kernel)
man 3\ \ \ Library calls (functions within system libraries)
man 3paper\ Paper related
man 3pm\ Perl Modules
man 4\ \ \ Special files (usually found in /dev)
man 5\ \ \ File formats and conventions eg /etc/passwd
man 5vga\ VGA File formats and conventions
man 5x\ \ X File formats and conventions
man 6\ \ \ Games
man 7\ \ \ Macro packages and conventions eg man(7), groff(7)
man 7vga\ VGA Macro packages and conventions
man 8\ \ \ System administration commands (usually only for root)
man 9\ \ \ Kernel routines