#/usr/local/bin/perl
#
#    Copyright (C) 1991 by Lutz Prechelt, Karlsruhe
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 1, or (at your option)
#    any later version.
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#    If you don't have a copy of the GNU General Public License write to
#    Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

# Author: Lutz Prechelt ([email protected]),
#         23.03.91
# Change: Lutz Prechelt, 02.07.91
#
# Usage: see message at "die" below.

$infinity = 10000;

$precontext  = 2;
$postcontext = 2;
$paragraphmode    = 0;
$withlinenumber   = 0;
$withfilename     = 0;
$wrong_option     = 0;
$reversemode      = 0;
$delimiterstring  = "-----------\n";
# $endpara = '\S.*\n';

sub showline {
 if ($withfilename != 0) {
   printf ("\"%s\"", $ARGV);
 }
 if ($withfilename != 0 && $withlinenumber != 0) {
   print ",";
 }
 if ($withlinenumber != 0) {
   printf ("%4d", $.);
 }
 if ($withfilename != 0 || $withlinenumber != 0) {
   print ": ";
 }
 print ($_[0]);
}


# Process the Options:
do {
 $something_done = 1;
 if ($ARGV[0] =~ /^-(\d+)$/) {
   $precontext = $postcontext = $1;
   shift;
 }
 elsif ($ARGV[0] =~ /^-(\d+)[\,\+\/\;](\d+)$/) {
   $precontext  = $1;
   $postcontext = $2;
   shift;
 }
 elsif ($ARGV[0] =~ /^-d$/ && $#ARGV > 0) {
   $delimiterstring = $ARGV[1];
   $delimiterstring =~ s/\\n/\n/o;
   shift; shift;
 }
 elsif ($ARGV[0] =~ /^-d(.*)$/) {
   $delimiterstring = $1;
   $delimiterstring =~ s/\\n/\n/o;
   shift;
 }
 elsif ($ARGV[0] =~ /^-p$/) {
   $paragraphmode = 1;
   shift;
 }
 elsif ($ARGV[0] =~ /^-n$/) {
   $withlinenumber = 1;
   shift;
 }
 elsif ($ARGV[0] =~ /^-h$/) {
   $withfilename = 1;
   shift;
 }
 elsif ($ARGV[0] =~ /^-v$/) {
   $reversemode = 1;
   shift;
 }
 elsif ($ARGV[0] =~ /^-e$/) { # end options (for expressions starting with - )
   $something_done = 0;
   shift;
 }
 elsif ($ARGV[0] =~ /^-/) {
   printf ("don't know option '%s'\n", $ARGV[0]);
   $wrong_option = 1;
   $something_done = 0;
   shift;
 }
 else {
   $something_done = 0;
 }
} while ($something_done);


# Usage message:
if ($#ARGV == -1 || $wrong_option) {
 die "
  Usage: cgrep [-pre[,post]] [-p] [-v] [-h] [-n] [-d string] pattern
[file...]

  cgrep is a context grep. It displays more than the one matching line for
  every match (2 before and 2 after as default).

  -3  means display 3 lines before and 3 lines after the match
  -5,12  means display 5 lines before the match and 12 lines after
  -p  means display only as much of the context as belongs to the
      current paragraph. (paragraphs bounded by empty lines)
  -v  means invert search (display nomatches)
  -h  means toggle display filename before every line
  -n  means display line number before every line
  -d string  means use string as the output delimiter string
  pattern  is a Perl regular expression (you better quote it !)
Exiting";
}


if (length (@ARGV) > 1) {
 $withfilename = !$withfilename;
}


# Get the pattern and protect the delimiter.
$pat = shift;
$pat =~ s#/#\\/#g;


# current line will always be at end of array, i.e. $ary[$currentpre]
$_ = <>;
push(@ary,$_);
$currentpre = 0;


# now use @ary as a silo, shifting and pushing.
# the length of the @ary at any time is $currentpre + 1
# the current line is @ary[$currentpre], the postcontext is not held in @ary.
$seq = 0;
$lastoutput = $infinity;  #last output is infinitely many lines ago
$cur = @ary[0];       #current line
while ($cur) {  #as long as there is something to look at
 if ($reversemode == 1 ? $cur !~ /$pat/o : $cur =~ /$pat/o) {   #match found
   if ($lastoutput <= $postcontext) {
     &showline ($cur);
   }
   else {
     print $delimiterstring if ($seq++ && $precontext + $postcontext > 0);
     foreach $line (@ary) {
       &showline ($line);
     }
   }
   $lastoutput = 0;
 }
 elsif (($cur !~ /\S.*\n/o && $paragraphmode == 1) || eof) {
#paragraph/file end
   for (; $currentpre >= 0; $currentpre--) {
     shift (@ary);
   }
   $lastoutput = $infinity;
   close (ARGV) if (eof);
 }
 elsif ($lastoutput <= $postcontext) {     #another line of postcontext
   &showline ($cur);
 }
 #goto next line of input:
 $lastoutput++;
 $_ = <> if $_;
 push(@ary,$_);
 if ($currentpre < $precontext) {
   $currentpre++;
 }
 else {
   shift(@ary);
 }
 $cur = $ary[$currentpre];
}