summaryrefslogtreecommitdiff
path: root/srchx
blob: ccc9c87b665dbcc5a4c40c2eac43894bd6e24096 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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);
}