#!/usr/bin/perl # # bintypes This script finds type-specific entries in a file. # This looks in a very specifically formatted file. # # usage: # bintypes [options] # # Revision History # 1.0 Initial revision. 150306 # 1.1 Added the -bin and -path options. 150323 # 1.2 Added the lang-php language type. 150401 # 1.3 Added license info. 180616 # # Written by Wayne Morrison, 150401. # # Copyright 2015 Wayne Morrison # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # use strict; use Getopt::Long qw(:config no_ignore_case_always); # # Version information. # my $NAME = "bintypes"; my $VERS = "$NAME version: 1.3"; ############################################################################ # # Options fields. # my %opts = (); # Options. # # Command line arguments. # my @opts = ( 'bin', # Give a default prefix for the files. 'count', # Give a count of files of given type. 'full', # Give complete matching lines. 'list', # Display recognized types. 'type', # Give the type along with the name. 'path:s', # Give a path prefix for the files. 'verbose', # Give verbose output. 'help', # Give a help message. 'Version', # Display the program version. ); my $count = 0; # File-count flag. my $full = 0; # Flag for complete lines. my $path = ''; # Path flag. my $typer = 0; # Flag for lines with type. my $verbose = 0; # Verbose flag. ############################################################################ # # Types of languages recognized in ~/bin/README. # my %validlangs = ( 'lang-c' => 'lang-c', 'lang-csh' => 'lang-csh', 'lang-objc' => 'lang-objc', 'lang-perl' => 'lang-perl', 'lang-php' => 'lang-php', 'lang-python' => 'lang-python', 'lang-ruby' => 'lang-ruby', 'lang-sh' => 'lang-sh', 'lang-tcl/tk' => 'lang-tcl/tk', 'lang-termcap' => 'lang-termcap', 'c' => 'lang-c', 'csh' => 'lang-csh', 'objc' => 'lang-objc', 'perl' => 'lang-perl', 'php' => 'lang-php', 'python' => 'lang-python', 'ruby' => 'lang-ruby', 'sh' => 'lang-sh', 'tcl/tk' => 'lang-tcl/tk', 'termcap' => 'lang-termcap', ); # # Other types of tags recognized in ~/bin/README. # my %validothers = ( 'freebsd' => 'freebsd', 'osx' => 'osx', 'dir' => 'dir', 'notmine' => 'notmine', 'obs' => 'obs', ); my $DEFPATH = glob("~/bin"); # Default path value. my $DEF_README = glob("~/bin/README"); # Default file to read. my $readme = $DEF_README; # File to read. my @readme = (); # Contents of ~/bin/README. my @hits = (); # Matching lines. my %hits = (); # Complete matching lines. ############################################################################ main(); exit(0); #----------------------------------------------------------------------------- # Routine: main() # # Purpose Do everything. # sub main { $| = 1; # # Munch on the options and arguments. # optsandargs(); # # Read the readme file. # reader(); foreach my $stype (@ARGV) { $stype = transtype($stype); searcher($stype); } scribe(); } #---------------------------------------------------------------------- # Routine: optsandargs() # # Purpose: Parse the command line for options and arguments. # sub optsandargs { my $searchtype; # File type to search for. # # Parse the options. # GetOptions(\%opts,@opts) || usage(); # # Check for some immediate-action options. # usage() if(defined($opts{'help'})); version() if(defined($opts{'Version'})); listtypes() if(defined($opts{'list'})); # # Get the rest of the option values. # $verbose = $opts{'verbose'}; $count = $opts{'count'}; $full = $opts{'full'}; $typer = $opts{'type'}; # # If the -path option was specified, we'll either set it to # the default path (if an option value of "-" was given) or # the user's path (if another option value was given.) # We'll also ensure the path has a single trailing slash. # if((defined($opts{'bin'})) && (defined($opts{'path'}))) { print STDERR "-bin may not be used with -path\n"; exit(1); } # # If the -bin option was specified, we'll set the -path # value to to the default path. # if(defined($opts{'bin'})) { $opts{'path'} = $DEFPATH; } # # If the -path option was specified, we'll either set it to # the default path (if an option value of "-" was given) or # the user's path (if another option value was given.) # We'll also ensure the path has a single trailing slash. # if(defined($opts{'path'})) { if($opts{'path'} eq '-') { $path = $DEFPATH; } else { $path = $opts{'path'}; } $path =~ s/(\/)*$/\//; } usage(1) if(@ARGV == 0); # # Check for mutually exclusive options. # if($full && $count) { print STDERR "-full may not be used with -count\n"; exit(1); } if($typer && $count) { print STDERR "-type may not be used with -count\n"; exit(1); } } #---------------------------------------------------------------------- # Routine: transtype() # # Purpose: Translate the given string into a recognized type. # If possible. # sub transtype { my $searchtype = shift; # String to translate. # # Get the type we're searching for and ensure it is valid. # if((! defined($validlangs{$searchtype})) && (! defined($validothers{$searchtype}))) { print STDERR "invalid type \"$searchtype\"\n"; exit(2); } # # Make sure we're using the versions used in the file, rather # than potential short-hand versions. # $searchtype = defined($validlangs{$searchtype}) ? $validlangs{$searchtype} : $validothers{$searchtype}; # # Return the translated type. # return($searchtype); } #---------------------------------------------------------------------- # Routine: reader() # # Purpose: Read the contents of the readme file. We'll exit with # an error if we can't open the file or if it's empty. # sub reader { if(open(README, "< $readme") == 0) { print STDERR "unable to open \"$readme\"\n"; exit(3); } @readme = ; close(README); if(@readme == 0) { print STDERR "\"$readme\" is empty\n"; exit(4); } } #---------------------------------------------------------------------- # Routine: searcher() # # Purpose: Search the file contents for entries with the given type. # Matching lines are saved to the @hits array and the # %hits hash. # sub searcher { my $searchtype = shift; # File type to search for. # # Search the type section of each line of the readme contents for a # matching type. When we find one, it'll be added to the @hits array. # foreach my $ln (@readme) { my $fn; # Name of file. my $ft; # Type of file. # # Divide the line into the entry and the type section. # $ln =~ /^(.+);;(.+)\n/; $fn = $1; $ft = $2; # # Go to the next line if this one doesn't have a matching type. # next if(! match($searchtype, $ft)); # # Save the match. # push @hits, $fn; $hits{$fn} = $ln; } } #---------------------------------------------------------------------- # Routine: scribe() # # Purpose: Output is given, based on matching entries and options. # sub scribe { # # Give some output. If -count was given, we'll only give a count # of the matching lines. Otherwise, we'll give each of the matches. # if($count) { my $out; # Output string. $out = $verbose ? (@hits . " matches") : @hits; print "$out\n"; } else { # # Print the matching lines. # If -verbose was given, then we'll print the whole # non-type portion of the line. # If -type was given, we'll print the entry's name and type. # If -full was given, we'll print the whole line. # If neither was given, we'll print the matching names only. # foreach my $ln (@hits) { if($full) { $ln = $hits{$ln}; chomp $ln; } elsif($typer) { $ln = $hits{$ln}; $ln =~ s/\s.*;;/\t\t/; chomp $ln; } elsif(! $verbose) { $ln =~ s/\s+.*$//; } print "$path$ln\n"; } } } #---------------------------------------------------------------------- # Routine: match() # # Purpose: Compare each element of a type list to the given search # string. Return a boolean indicating whether or not there's # a match in the list. # sub match { my $search = shift; # Search string. my $typelist = shift; # Type list from entry. my @types = (); # Broken-out types. # # Strip out whitespace from the type list and then # break it out into an array. # $typelist =~ s/\s+//g; @types = split /,/, $typelist; # # Compare each element of the type array to the given search # string. Return success on the first match. # foreach my $typ (@types) { return(1) if($search =~ /^$typ$/i); } # # Return failure if there were no matches. # return(0); } #---------------------------------------------------------------------- # Routine: listtypes() # # Purpose: Print the recognized types. Valid language types are given # in their long and short forms. Other type strings are # just given as is. # sub listtypes { # # Give the valid language types. # foreach my $vtype (sort(keys(%validlangs))) { my $short; # Short form of name. next if($vtype !~ /^lang-/); if($vtype =~ /^lang-(.*)/) { $short = $1; printf("%-15s\t($short)\n",$vtype); } else { print "$vtype\n"; } } print "\n"; # # Give the non-language types. # foreach my $vtype (sort(keys(%validothers))) { print "$vtype\n"; } exit(0); } #---------------------------------------------------------------------- # Routine: version() # # Purpose: Print the version number(s) and exit. # sub version { print STDERR "$VERS\n"; exit(0); } #---------------------------------------------------------------------- # Routine: usage() # # Purpose: Give usage message and exit. # sub usage { print STDERR "usage: bintypes [options] \n"; print STDERR "\n"; print STDERR "\twhere [options] are:\n"; print STDERR "\t\t-bin\n"; print STDERR "\t\t-count\n"; print STDERR "\t\t-full\n"; print STDERR "\t\t-list\n"; print STDERR "\t\t-path prefix\n"; print STDERR "\t\t-type\n"; print STDERR "\n"; print STDERR "\t\t-help\n"; print STDERR "\t\t-Version\n"; exit(0); } 1; ############################################################################## =pod =head1 NAME B - finds type-specific entries in an index file =head1 SYNOPSIS bintypes [options] =head1 DESCRIPTION B finds type-specific entries in an index file. The entries that match user-specified tag will be displayed. The information displayed depends on the options given. These are detailed below. The index file is expected to be in a very specific format, The entries give a command name, a brief description, and index tags. The file is expected to be in B<~/bin/README>. The following lines show the format of the file: ascstr print ASCII-only lines in a file ;;lang-c cfin find file in PATH ;;lang-python clbc command-line front-end to bc ;;lang-perl cronfetch fetches mail from a server ;;lang-csh lsdb list some NetPolicy db fields ;;lang-csh, obs oldsrc directory for old source files ;;dir playsound plays a soundfile ;;OSX,lang-objc The first column is the name of the entry. The two semicolons mark the start of the comma-separated tags list Everything between those is the description for the entry. The tags are described below. =head2 Output Format The output from B depends on the options that are given on the command line. Examples will best show the differences. These examples use this as a matching entry: cfin find file in PATH ;;lang-python Without any options, B gives this output: cfin With the I<-type> option, B gives this output: cfin lang-python With the I<-verbose> option, B gives this output: cfin find file in PATH With the I<-full> option, B gives this output: cfin find file in PATH ;;lang-python With the "B<-bin>" option, B gives this output: /Users/bob/bin/cfin With the "B<-path /usr/bin>" option, B gives this output: /usr/bin/cfin With the "B<-path ->" option, B gives this output: /Users/bob/bin/cfin This assumes B<~/bin> translates to B. With the "B<-path /foo///>" option, B gives this output: /foo/cfin With the I<-count> option, B will provide the count of entries that match the search tag. However, the matching entries will not be given. The I<-list> option will display all the valid search tags. =head2 Search Tag Valid search tags fall into two categories: language tags and other tags. Language tags can be specified in a long format or a short format. Other tags only have a single format. The following are the valid language tags. The meanings should be obvious. long format short format ----------- ------------ lang-c c lang-csh csh lang-objc objc lang-perl perl lang-php php lang-python python lang-ruby ruby lang-sh sh lang-tcl/tk tcl/tk lang-termcap termcap The following are the valid other tags. The meanings are provided. tag meaning --- ------- freebsd written for FreeBSD osx written for Mac OS X dir directory notmine written by someone else obs obsolete =head1 OPTIONS B takes the following options: =over 4 =item I<-bin> The default directory will be prepended to each filename printed. A single slash ('/') will separate the prefix path from the filename. This option is ignored if the I<-count> option is given as well. =item I<-count> This option displays the count of matching entries. It may not be used with I<-full> or I<-type>. The I<-count> option is ignored if it is given with I<-list>. =item I<-full> This option displays the full entry for matching lines. It may not be used with I<-count>. =item I<-list> This option displays the recognized types. The I<-count> option is ignored if it is given with I<-list>. =item I<-path prefix> The specified I will be prepended to each filename printed. If I is a dash ('-'), then the default directory will be used. A single slash ('/') will separate the prefix path from the filename. This option is ignored if the I<-count> option is given as well. =item I<-type> This option displays each matching entry's type information. It may not be used with I<-count>. =item I<-verbose> This option provides verbose output. =item I<-Version> Display the version information for B. =item I<-help> Display a help message. =back =head1 LICENSE Copyright 2015 Wayne Morrison Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =head1 AUTHOR Wayne Morrison, wayne@waynemorrison.com =cut