#!/pgk/bin/perl --      # -*-Perl-*-
#
#	ngrep - "near" grep, gives surrounding "context"

#	Author:		Ron Maeder	(maederr@cs.ucdavis.edu)
#	History:	May 31, 1991	inital release

# Modified by N Matloff, July 3, 2000.

# Example:	ngrep -a1 -b1 '^hel' /usr/dict/words
#
# Note:		See code for interpretation of command line switches
#
$max_before = 4;	# set defaults
$max_after = 4;
$TRUE = 1;		# make the boolean flags obvious
$FALSE = 0;
$usage = "Usage: ngrep [-bxx] [-ayy] [-h] [-t] [-l] [-m] [-f] [-q] " .
    "[--] reg_expr [ filename... ]\n";
# while there are still switches to process ...
while ($_ = $ARGV[0], /^-/) {	# man page, pp. 14-15
    shift;			# get next arg
    last if /^--$/;		# stop processing switches if get to "--"
    # if match -bxx then $max_before = xx
    if    (/^-b(\d+)$/)	{ $max_before = $1; }
    elsif (/^-a(\d+)$/)	{ $max_after  = $1; }
    elsif (/^-h$/)	{ $no_head_line_nums = $TRUE; }
    elsif (/^-t$/)	{ $no_tail_line_nums = $TRUE; }
    elsif (/^-l$/) {			# no line numbers at all
	$no_head_line_nums = $TRUE;
	$no_tail_line_nums = $TRUE;
    }
    elsif (/^-m$/)	{ $no_match_info = $TRUE; }
    elsif (/^-f$/)	{ $no_filenames = $TRUE; }
    elsif (/^-q$/) {			# quiet
	$no_head_line_nums = $TRUE;
	$no_tail_line_nums = $TRUE;
	$no_match_info     = $TRUE;
	$no_filenames     = $TRUE;
    }
    elsif (/^-d$/)	{ $debug = $TRUE; }	# not documented in Usage
    else {
	print $usage;
	exit(1);
    }
}
unless ($reg_expr = shift) {		# get regular expression to match
    print $usage;
    exit(1);
}
if ($debug) {
    print "max_before = $max_before\tmax_after = $max_after\t";
    print "reg_expr = $reg_expr\n";
    print "no_head_line_nums = $no_head_line_nums\n";
    print "no_tail_line_nums = $no_tail_line_nums\n";
    print "no_match_info = $no_match_info\n";
    print "no_filenames = $no_filenames\n";
}
# interate over all the files specified
unshift(@ARGV, "-") if ($#ARGV < $[);	# use stdin if no filenames..., p. 14
# this line suppresses filenames if there is only one to print
$no_filenames = $no_filenames || ($#ARGV == $[);
while ($grep_filename = shift) {
    unless (open(GREP_FILE, $grep_filename)) {
	print STDERR "Can't open $grep_filename: $!\n";
	next;				# skip this file...
    }
    $gap_in_output = $TRUE;		# used to decide when to print line #
    $file_name_printed = $FALSE;
    while (<GREP_FILE>) {
	# if the pattern is matched...
	if (/$reg_expr/o) {		# man page, p. 47
	    if ($gap_in_output) {
		$gap_in_output = $FALSE;
		unless ($file_name_printed)  {
                   print "**** $grep_filename ****\n";
		   $file_name_printed = $TRUE;
		}
	    }
	    # spit out the before buffer
	    while ($bef = shift(@before_buf)) {
		print "b> " unless ($no_match_info);
		print $bef;
	    }
	    print "m> " unless ($no_match_info);
	    print;			# print the matched line
	    # while there is still input, and "after" lines to print...
	    for ($i = 0; ($i < $max_after) && ($_ = <GREP_FILE>); $i++) {
		if (/$reg_expr/o) {	# matched again before done printing
		    print "m> " unless ($no_match_info);
		    print;		# print the matched line
		    $i = -1;		# reset "after" counter, and continue
		} else {
		    print "a> " unless ($no_match_info);
		    print;		# print the matched line
		}
	    }
	} else {			# if the pattern is NOT matched...
	    # keep a buffer/queue of the last few lines seen
	    push(@before_buf, $_);	# man page, p. 55
	    # $#before_buf is index of last elt in @before_buf, length - 1
	    if ($#before_buf >= $max_before) {
		shift(@before_buf);	# man page, p. 63
		if (!$gap_in_output) {
		    $gap_in_output = $TRUE;	# if @before_buf overflowed...
		}
	    }
	}
    }
    # if printed last line of file...
    unless ($gap_in_output || $no_tail_line_nums) {
	print "^^^^ ", ($. - ($#before_buf + 1)), " ^^^^\n";
    }
}


