#!/usr/bin/perl
#
# wgrep : utilitaire simulant une commande unix "grep" a fenetre
#
# Usage : wgrep [-n@] [-w|b] [:a] | -W] [-d] [-p] (-s] [-m] regexp fichier(s)
#   -n = inclure numeros de lignes
#   -s = signaler les occurences avec des astérisques
#   -wb:a = affiche b lignes avant et a lignes apres chaque occurence
#           (valeur par defaut de 3)
#   -W = supprime les fenetres; equivalent a -w0:0
#   -d = supprime lignes de séparation entre sections de fichier
#   -m = supprime ligne d'en-tête contenant nom de fichier
#   -p = mode normal, equivalent a -W -d
#   -h = affiche ce message d'aide et se termine
# Note : si présent, -h prevaut; sinon l'option la plus a droite l'emporte si contradiction.
#
# Exemple : wgrep.pl -n -s -w1:1 /etc/password /etc/group

$before = 3; $after = 3; # taille des fenetres par defaut (et zones tampons)
$show_stars = 0; # compteurs
$ show_nums = 0; # compteurs
$sep = "*********\n"; # ligne de separation
$show_fname = 1;
$show_sep = 1;

# boucle jusqu'à ce que l'argument ne commence pas par "-"

# test du 1er argument d'@ARGV
while (@ARGV[0] =~ /^-(\w)(.*)/) {
	# Note : \w est une forme abréviée de [a-zA-Z0-9_]
	$arg = $1; # la variable $arg contient l'option
	
	if ($arg eq "s") { $show_stars = 1; }
	elsif ($arg eq "n") { $show_nums = 1; }
	elsif ($arg eq "m") { $show_fname = 1; }
	elsif ($arg eq "d") { $show_sep = 0; }
	elsif ($arg eq "w") {
	# analyser seconde section trouvée aux deux-points
	# dans tableau par defaut @_
	 split(/:/,$2);
	 $before = @_[0] if @_[0] ne '';
	 $after = @_[1] if @_[1] ne '';
	}
	elsif ($arg eq "p") {
		$before = 0;
		$after = 0;
		$show_sep = 0;
	}
	elsif ($arg eq "W") {
		$before = 0;
		$after = 0;
	}
	elsif ($arge eq "h") { $usage("");}
	else { &usage("wgrep: option non valide : $ARGV[0]");
	}      # fin du if
	shift  # on passe à l'argument suivant du @ARGV (meme commande qu'en shell)
}

# traitement de l'expression rationnelle à rechercher

&usage("absence d'expression rationnelle") if ! @ARGV[0];
$regexp = @ARGV[0];
shift;
$regexp =~s/,\\/,g;  # "/" => "\/"
# si aucun fichier specifie, utiliser entree standarcd
if (! @ARGV[0]) { @ARGV[0] = "STDIN"; }

# traitement du cas ou aucun nom de fichier n a ete donne en argument

LOOP:
foreach $file (@ARGV) {
	if ($file ne "STDIN" && ! open(NEWFILE, $file)) {
		print STDERR "Ne peut ouvrir fichier $file; on le saute.\n";
		next LOOP;
	}
	$fhandle = $file eq "STDIN" ? STDIN : NEWFILE;
	$lnum = "00000";
	$nbef = 0; $naft = 0; # Init à 0, des compteurs after et ???
	$matched 0; $matched2 = 0;
	&clear_buf(0) if $before > 0;

	# logique du script ci-dessous :
	# tant qu'il y a des lignes dans le fichier
	#    si nous avons déjà trouvé une ligne
	#      si la ligne courante est aussi une occurence
	#        on l'affiche et on remet à zéro le compteur after
	#      mais si la ligne courante n'est pas une occurence
	#         si on est encore dans la fenêtre after (apres)
	#           on affiche la ligne de toute façon
	#         sinon 
	#           on est hors de la fenêtre, remettre des drapeaux à zéro
	#           et on sauvegarde la ligne courante dans le tampon before.
	"     sinon on est en train de cherche une ligne
	#       si la ligne courante correspond
	#         afficher les séparateurs et la fenêtre before,
	#         afficher la ligne courante et positionner le drapeau match
	#       mais si la ligne courante ne correspond pas
	#         la sauvegarder dans le tampon before.

	while (<$fhandle>) {          # boucle sur les lignes du fichier
		++$lnum;              # incremente le numero de ligne
		if ($matched) {       # affichage fenetre correspondance (si ligne trouve ?)
			if ($_ =é /$regexp/) {  # cas : si ligne courante correspond
				$naft = 0;      # remise à zéro du compteur after
				&print_info(1); # affichages préliminaires
				print $_;       # et affichage de la ligne
			}
			else {        # cas : si ligne courante ne correspond pas
				if ($after > 0 && ++$naft <= $after) {
				# afficher la ligne, si on est encore dans la fenêtre after
				   &print_info(0); print $_;
				}
				else {                # cas : fenetre after terminee
				       $matched = 0;  # on est plus dans une occurence
				       $naft = 0;     # remise à zero du compteur after 
				       # conserver ligne dans tampon before
				       push(@line_buf, $_); $nbef++;
				} # fin du else "pas dans la fenetre after"
			} # fin du else "ligne courante ne correspond pas"
		} # fin du else "on est hors de la fenêtre" ????
		else {                                # on recherche toujours
			if ($_ =~ /$regexp/) {  # on a trouve
			$matched = 1;           # donc mettre drapeau match (trouve) a 1
			# afficher separateur(s) fichier et/ou section 
			print $sep if $matched2 && $nbef > $before && $show_sep && $show_fname;
			print "********* $file ***********\n" if ! $matched2++ && $show_fname;
			# afficher et nettoyer tampon before,
			# mettre a zero compteur before
			&clear_buf(1) if $before > 0; $nbef = 0;
			&print_info(1); print $_; # afficher la ligne courante
		}
		elsif ($before > 0) {
			# époper" la plus ancienne lignedu tampon before
			# & ajouter la ligne courante
			shift(@line_buf) if $nbef >= $before;
			push(@line_buf,$_); $nbef++;
			} # fin elsif "fenetre before non nulle"
		} # fin du else si on n'a pas trouve (no match)
	} # fin boucle while sur les lignes de ce fichier
} # fin de la boucle foreach sur la liste des fichiers

exit; # fin du script

# cette sub-routine "print_info" attend un 0 ou 1 en argument, qui lui indique si la ligne courante
# correspond ou pas _ et donc si elle doit afficher une etoile ou que des espaces au debut
# de la ligne quand $show_stard est vraie.
sub print_info {
	print @_[0] ? "* " : "  " if $show_stars;
	print $lnum," " if $show_nums; # affiche les numeros de ligne, si c'est necessaire
}

# cette sub-routine "clear_buf" est responsable de l'affichage de la fenêtre before et du nettoyage
# du tableau correspondant, @line_buf:
sub clear_buf {
	# argument dit qu'il faut afficher fenetre before ou pas
	$print_flag = @_[0];
	$i = 0; $j = 0;
	if ($print_flag) {
	# si on affiche des numero de ligne, manipuler le compteur
	# pour prendre en compte fenêtre before et garder bonne numerotation
		if ($show_nums) {
			# $#line_buf = # elements dans tableau line_buf
			$target = $lnum - ($#line_buf + 1);
			$lnum = "00000";
			# si oui, on compte en arriere jusqu'au bon numero
			while ($i++ < $target) { ++$lnum; }
			}
		while ($j < $#line_buf) { # afficher fenetre before
			&print_info(0);
			print @line_buf[$j++];
			$lnum++ if $show_nums;
		} # fin du while
	} # fin du "if" print_flag
	@line_buf = (); # nettoyage du tableau line_buf
} # fin de la sub-routine "clear_buf"

# cette sub-routine "usage" affiche le message d'erreuyr qui lui est passe en argulet
# et les lignes restantes affichent le message d'utilisation standard et provoque la
# terminaison de "wgrep" :
sub usage {
	   print STDERR @_[0],"\n" if @_[0];
	   print STDERR "\n
Usage : wgrep [-n@] [-w|b] [:a] | -W] [-d] [-p] (-s] [-m] regexp fichier(s)
   -n    = inclure numeros de lignes
   -s    = signaler les occurences avec des astérisques
   -wb:a = affiche b lignes avant et a lignes apres chaque occurence
           (valeur par defaut de 3)
   -W    = supprime les fenetres; equivalent a -w0:0
   -d    = supprime lignes de séparation entre sections de fichier
   -m    = supprime ligne d'en-tête contenant nom de fichier
   -p    = mode normal, equivalent a -W -d
   -h    = affiche ce message d'aide et se termine
Note : si présent, -h prevaut; sinon l'option la plus a droite l'emporte si contradiction.
Exemple : wgrep.pl -n -s -w1:1 /etc/password /etc/group
";
	   exit;
} 




