diff options
Diffstat (limited to 'srchx')
-rwxr-xr-x | srchx | 124 |
1 files changed, 124 insertions, 0 deletions
@@ -0,0 +1,124 @@ +#!/usr/bin/perl +# +# (c) 2019-2020 Vitaly Parnas <vp330@parnas.me> +# See LICENSE for licensing information. + +use strict; +use warnings; +use 5.010; +use File::Basename; +use Data::Dumper; +use Term::ANSIColor qw(:constants); + +my $DELIM = ';'; +my $CONF_FILE = basename($0) . '.conf'; +my $COLOR_FILE = YELLOW; +#my $COLOR_FILE = BRIGHT_MAGENTA; +my $COLOR_PATTERN = BRIGHT_CYAN; +my $DEBUG = 0; + +sub usage () +{ + say " +Usage: " . basename($0) . " [options] <search terms and/or tags> <source(s)> + Options: + [-h|--help] - print this message. + [-d|--debug] - debug mode, default: no. + [-n|--no-act] - display search command, do not execute. + [-c|--count] - display counts of search results of each file only. + [-r|--recursive] - recurse into directories. + [-s|--suppress-err] - ignore missing files. +"; + exit(1); +} + +sub read_ini +{ + my $ini = shift; + my $config; + my $section; + open(INI, '<', $ini) or die "Could not open file '$ini' $!\n"; + while (<INI>) { + chomp; + next if /^(#|;|\s*$)/; # comments, blanks + next if s/^\[(.*)\]/$section = $1/sie; # new config section + /^([^#;]+)\s*=\s*([^#]+)\s*/ and $config->{$section}->{$1} = $2; + } + close INI; + return $config; +} + +sub parse_free_args +{ + my ($conf, $free_args, $searches, $paths) = @_; + foreach (@$free_args) { + my $target; + my $val = $conf->{"content"}->{$_}; + if ($val) { + $target = $paths; + } else { + $val = ($conf->{"terms"}->{$_} or $_); + $target = $searches; + } + push (@$target, split(/$DELIM/, $val)); + } + say "Searches: @$searches" if $DEBUG; + say "Paths @$paths" if $DEBUG; +} + +sub exec_pretty_search +{ + my ($cmd, $search_str) = @_; + $_ = qx($cmd); + s/^[^\s:]+:/sprintf($COLOR_FILE . "%s" . RESET, $&)/egm; + s/$search_str/sprintf($COLOR_PATTERN . "%s" . RESET, $&)/eig; + s#$ENV{HOME}#~#g; # strip verbose $HOME path from results + say; +} + +# MAIN +{ + my $conf; + my (@free_args, @searches, @paths, @grep_options); + my ($no_act, $count_matches, $recurse, $min_count, $suppress_err) = (0,0,0,1,0); + my ($search_str, $cmd); + + while ($_ = shift) { + if (/^(-h|--help)/) { usage(); } + elsif (/^(-d|--debug)/) { $DEBUG = 1; } + elsif (/^(-n|--no-act)/) { $no_act = 1; } + elsif (/^(-c|--count)/) { $count_matches = 1; } + elsif (/^(-r|--recursive)/) { $recurse = 1;} + elsif (/^(-s|--suppress-err)/) { $suppress_err = 1;} + else { push (@free_args, $_); } + } + usage() unless (@free_args); + push (@grep_options, $recurse ? "-r" : "--exclude-dir=/*"); + push (@grep_options, "--no-messages") if $suppress_err; + foreach (".$CONF_FILE", "$ENV{HOME}/.$CONF_FILE", + "$ENV{HOME}/.config/$CONF_FILE") { + if (-e) { + say "Using conf $_"; + $conf = read_ini($_); + last; + } + } + $conf or die "Could not find or open $CONF_FILE\n"; + #print Dumper($conf) if $DEBUG; + parse_free_args($conf, \@free_args, \@searches, \@paths); + while (my ($filter_name, $val) = each %{$conf->{"filters"}}) { + ($filter_name eq "min-count") and $min_count = $val and next; + foreach (split(/$DELIM/, $val)) { + push (@grep_options, "--$filter_name '$_'"); + } + } + $count_matches and unshift (@grep_options, "-cH"); + $search_str = join('|', @searches); + $cmd = "grep -Ei @grep_options -e '$search_str' @paths"; + if ($count_matches) { + $cmd .= " | awk -F':' '{if (\$2 >= $min_count) " . + "printf(\"%4d : %s\\n\", \$2, \$1)}' | sort -n"; + } + $no_act ? say($cmd) : exec_pretty_search($cmd, $search_str); + exit(0); +} |