summaryrefslogtreecommitdiff
path: root/srchx
diff options
context:
space:
mode:
Diffstat (limited to 'srchx')
-rwxr-xr-xsrchx124
1 files changed, 124 insertions, 0 deletions
diff --git a/srchx b/srchx
new file mode 100755
index 0000000..ccc9c87
--- /dev/null
+++ b/srchx
@@ -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);
+}