Path: utzoo!attcan!uunet!bbn.com!rsalz From: rsalz@uunet.uu.net (Rich Salz) Newsgroups: comp.sources.unix Subject: v15i082: Windowing search (not unlike context grep) Message-ID: <972@fig.bbn.com> Date: 6 Jul 88 20:39:20 GMT Lines: 611 Approved: rsalz@uunet.UU.NET Submitted-by: "Mark E. Mallett"Posting-number: Volume 15, Issue 82 Archive-name: window-srch I got a few requests for this, and I figured I'd mail it to you for your consideration for posting in comp.sources.unix (or wherever it is appropriate). It is a short little program that allows one to find an occurance of a pattern in a text file, and print a window of lines around the match point(s). -mm- -- Mark E. Mallett PO Box 4188/ Manchester NH/ 03103 Bus. Phone: 603 645 5069 Home: 603 424 8129 uucp: mem@zinn.MV.COM (...decvax!elrond!zinn!mem or ...sii!zinn!mem) BIX: mmallett #------ cut here ------- #! /bin/sh # # This is a shell archive; feed it to /bin/sh (not csh) to create: # Makefile # README # wns.1 # wns.c if test -f 'Makefile'; then echo shar: will not over-write existing file 'Makefile' else sed -e 's/^@//g' << 'SHAR_EOF' > Makefile @# Makefile for wns @# @# Define in CFLAGS: @# SYSINC if include file hierarchy includes the sys/ directory @# REGEX if using berkeley-style re_exec() and re_comp() @# REGCMP if using regcmp() and regex() @# OS_UNIX if running under unix @# OS_CPM if running under CP/M80 @# @# @CFLAGS=-DOS_UNIX -DREGCMP -DSYSINC @# @# Define LIBS to reflect the librar[y][ies] needed to fetch the r/e routines. @# @LIBS=-lPW @ @# @WNSOBJS=wns.o @ @wns: $(WNSOBJS) @ cc -o wns $(WNSOBJS) $(LIBS) SHAR_EOF fi if test -f 'README'; then echo shar: will not over-write existing file 'README' else sed -e 's/^@//g' << 'SHAR_EOF' > README @wns - Windowing Search Mark E. Mallett (mem@zinn.MV.COM) @ @This is a program to search for occurances of a pattern in a text file, and @print a window of lines around (before and after) each match point. The @size of the window is specified on the command line. @ @This is one of my earliest C programs, so don't be too critical of the @implementation. It was originally written on a CP/M system and later @moved to other environments (such as unix). @ @As for installation - there is not much to explain. The Makefile and the @manual source should be enough. @ @-mm- @April 19, 1988 SHAR_EOF fi if test -f 'wns.1'; then echo shar: will not over-write existing file 'wns.1' else sed -e 's/^@//g' << 'SHAR_EOF' > wns.1 @.TH WNS 1 @.SH NAME @wns \- windowing search @.SH SYNOPSIS @.B wns @[-a nnn] @[-b nnn] @[-l nnn] @[-w nnn] @pattern @[file ... ] @.SH DESCRIPTION @.I wns @searches through a file or list of files for occurances of a particular @pattern, and prints a window of lines around each match point. @.PP @Options which may be given are as follows: @.TP @.B \-a\ nnn @(After) specifies that nnn lines following the matching line will be @printed. default is 0. @.TP @.B \-b\ nnn @(Before) specifies that nnn lines preceding the matching line will be @printed. default is 0. @.TP @.B \-d @Enables debugging information. Not a very interesting option. @.TP @.B \-f @Suppress printing of the filename on each output line. @.TP @.B \-l\ nnn @Sets the maximum line length to nnn. Lines longer than this will be @truncated to this length before attempting to match them to the pattern as @well as when printing them. Default is 100. @.TP @.B \-n @Suppress printing of the line number on each output line. @.TP @.B \-w\ nnn @Sets the window size to nnn. This is the same as -a nnn -b nnn. @.PP @.I wns @outputs lines in the following format: @.PP @filename @nnnnn: text @.PP @where @.I filename @is the name of the file containing the matching text and may be suppressed @with the -f option, @.I lnnnn @is the line number of the displayed line and may be suppressed with the @-n option, and @.I text @is the line from the file. @Additionally, if the total window size is greater than 1 (that is, more than @zero lines before or after), then non-adjacent text areas are separated by @a short dashed line. @.SH FILES @/usr/local/bin/wns /usr/local/src/wns/* @.SH "CREDITS TO" @M. Mallett (mem@zinn.MV.COM) @.SH BUGS @You tell me.. SHAR_EOF fi if test -f 'wns.c'; then echo shar: will not over-write existing file 'wns.c' else awk << 'SHAR_EOF' > wns.c \ '{\ n=split($0,f,"@");\ n=f[1];\ while(n>=4){printf(" ");n=n-4}\ while(n>=1){printf(" ");--n}\ printf("%s\n",substr($0,length(f[1])+2,length))\ }' @/* wns.c - Search for string and print window of lines around it. 8@Nov 19 1984 Mark Mallett (mem@zinn.MV.COM) @mem 860224 Modified to do r/e (regular expression) parsing on unix @mem 860324 Added -f, -n; added code to number lines correctly on @ output. @mem 870325 Added support for regcmp()/regex() style regular expression @ library; redid some conditionals to provide better mix'n'match. @mem 870326 Don't try to print the filename if reading from stdin. @ Add -w option. Fix a small problem which occasionally allowed @ the separator to come out between adjacent lines of the file. @mem 871119 Fix semantics of call to regcmp(): the NULL terminating the @ argument list was missing. It worked, but probably only @ due to some bizarre coincidence. @*/ @/* The appropriate defines are done in the makefile */ @/* #define OS_UNIX */ /* Define this for unix systems */ @/* #define SYSINC */ /* Define this for sys/ include hierarchy */ @/* #define REGEX */ /* Define this for re_comp/re_exec library */ @/* #define REGCMP */ /* Define this to use regcmp/regex */ @/* #define OS_CPM */ /* Define this for CP/M-80 */ @/* Don't touch these */ @#define NOREGEXP /* Set this for no regular expression */ @#ifdef REGEX @#undef NOREGEXP @#endif REGEX @#ifdef REGCMP @#undef NOREGEXP @#endif REGCMP @#ifdef OS_CPM @#include "stdio.h" @#include "ctype.h" @#endif OS_CPM @#ifdef OS_UNIX @#include @#include @#ifdef SYSINC @#include @#include @#else /* !SYSINC */ @#include @#include @#endif SYSINC @#endif OS_UNIX @/* Local definitions */ @#ifndef NULL @#define NULL ((char *)0) @#endif NULL @#ifndef NUL @#define NUL '\000' @#endif @#ifndef TRUE @#define TRUE 1 @#define FALSE 0 @#endif @/* Internal data declared global */ @/* Internal routines */ @/* External data */ @/* External routines */ @#ifdef REGEX /* re_comp/ re_exec */ @extern char *re_comp(); /* r/e compile */ @extern int re_exec(); /* r/e exec */ @#endif REGEX @#ifdef REGCMP /* regcmp/regex */ @extern char *regcmp(); /* r/e compile */ @extern char *regex(); /* r/e exec */ @#endif REGCMP @/* Local data */ @static int Debug={FALSE}; /* Debug enabled flag */ @static int Lcur = {0}; /* Current line (in Lines array) */ @static char **Lines = {(char **)NULL}; /* Lines pointer array */ @static int Linlen = {100}; /* Line length */ @static int Lone = {0}; /* Line one (in Lines array) */ @static int Nmr = {0}; /* Number of matched regions */ @static char *Pat = {NULL}; /* Pattern */ @static char Shwfile = {TRUE}; /* Show file name... */ @static char Shwline = {TRUE}; /* Show line number */ @static int Waft = {0}; /* Window after */ @static int Wbef = {0}; /* Window before */ @static int Wsiz = {0}; /* Window size */ @#ifdef REGEX /* regex style r/e manipulations */ @char *Re; /* Result from re_comp() */ @#endif REGEX @#ifdef REGCMP /* regcmp style r/e */ @char *Re; /* Result from regcmp() */ @#endif REGCMP @main (argc, argv) @int argc; /* Argument count */ @char **argv; /* Argument values */ @{ @int i; /* Scratch */ @int n; /* Scratch again */ @int c; /* A character */ @char *aptr; /* Argument pointer */ @int nf; /* number of files on command line */ @nf = 0; /* No files on line */ @for (i = 1; i < argc; i++) /* Look at args */ 4@{ 4@if (argv[i][0] != '-') /* If option */ 8@{ 8@if (Pat == NULL) /* If no pattern yet given */ @ { 12@Pat = argv[i]; /* point here */ @#ifdef REGEX @ if ((Re = re_comp(Pat)) != NULL) @ { @ fprintf(stderr, "wns: %s\n", re); @ exit(1); @ } @#endif REGEX @#ifdef REGCMP @ if ((Re = regcmp(Pat, NULL)) == NULL) @ { @ fprintf(stderr, "wns: error in regular expression.\n"); @ exit(1); @ } @#endif REGCMP @ } 8@else /* This must be a file to search */ 12@{ 12@nf++; /* Count it */ 12@dosrch (argv[i]); /* Search */ 12@} 8@} 4@else /* Option char */ 8@{ 8@c = argv[i][1]; /* Get option char */ 8@if (isupper(c)) /* Trap idiot definition of tolower */ 12@c = tolower(c); /* Don't care about case */ 8@n = i; 8@aptr = NULL; /* Find arg, if any */ 8@if (argv[i][2] != NUL) 12@{ 12@aptr = &argv[i][2]; 12@n = i; /* Where to set i if we use this arg */ 12@} 8@else if (i < argc-1) /* use next.. */ 12@{ 12@n = i+1; 12@aptr = argv[n]; 12@} 8@switch (c) /* Process the option */ 12@{ 12@case 'a': /* Lines after */ 16@Waft = atoi (aptr); 16@Lines = NULL; 16@i = n; 16@break; 12@case 'b': /* Lines before */ 16@Wbef = atoi (aptr); 16@Lines = (char **)NULL; 16@i = n; 16@break; 12@case 'd': /* Enable debugging */ 16@Debug = TRUE; 16@break; @ case 'f': /* Suppress filename on output */ @ Shwfile = FALSE; @ break; 12@case 'l': /* Line length */ 16@Linlen = atoi (aptr); 16@Lines = NULL; 16@i = n; 16@break; @ case 'n': /* Suppress line number on output */ @ Shwline = FALSE; @ break; 12@case 'w': /* Window: lines before and after */ 16@Waft = Wbef = atoi (aptr); 16@Lines = NULL; 16@i = n; 16@break; 12@default: 16@fprintf (stderr, "Invalid option %s\n",argv[i]); 16@exit(); 12@} 8@} 4@} @if ( Pat == NULL ) /* If no pattern given */ 4@{ 4@fprintf(stderr, @"usage: wns [-a n] [-b n] [-d] [-f] [-l n] [-n] [-w n] pattern [filename... ]\n"); 4@exit(1); 4@} @if (nf == 0) /* No files processed ? */ 4@dosrch (NULL); /* Do standard input */ @} @/* @ @*//* dosrch (ifnm) 8@Perform the search @Accepts : 8@ifn Input file name @Returns : @*/ @dosrch (ifnm) @char *ifnm; /* Input filelname */ @{ @FILE *ifp; /* Input fp */ @char *lptr; /* Line pointer */ @int i; /* Scratch */ @int prtaft; /* Print-after count */ @int linnum; /* Line number */ @int nlb; /* Number of lines buffered */ @if (ifnm != NULL) /* If file name given */ 4@{ 4@ifp = fopen (ifnm, "r"); /* Open it for read access */ 4@if (ifp == NULL) 8@{ 8@fprintf (stderr, "Can not open file %s\n", ifnm); 8@return; 8@} 4@} @else 4@ifp = stdin; @if (Lines == NULL) /* If no line table allocated.. */ 4@{ 4@Wsiz = Wbef+2; /* Determine total window size */ 4@Lines = (char **) calloc (Wsiz, sizeof (char *)); 32@/* Allocate pointer table */ 4@for (i = 0; i < Wsiz; i++) /* Allocate line buffers */ 8@Lines[i] = (char *) calloc (Linlen, sizeof(char)); 4@} @Lcur = Lone = 0; /* Setup line pointers */ @nlb = 0; /* No lines buffered */ @linnum = 0; /* Line number is zero */ @prtaft = -(Wbef+1); /* Make sure separator given first time */ @for (;;) /* Loop through the file */ 4@{ 4@lptr = Lines[Lcur]; /* Get pointer to current line */ 4@if (++Lcur == Wsiz) /* Bump curr pointer and wrap */ 8@Lcur = 0; /* if hit end */ 4@if (Lone == Lcur) /* If wrapped to beginning of window */ 8@if (++Lone == Wsiz) /* Bump beginning */ 12@Lone = 0; /* and wrap if hit end */ 4@if (fgets (lptr, Linlen, ifp) != lptr) 8@break; /* if end of file */ 4@linnum++; /* Count line number */ 4@if (matlin (lptr)) /* If matching line */ 8@{ 8@if (prtaft < (-Wbef) ) /* Check for separator needed */ 12@if ( (Nmr++ > 0 ) && ((Wbef > 0) || (Waft > 0)) ) 16@printf ("-------------------\n"); 8@while (Lone != Lcur) /* Until we close the window */ 12@{ 12@shwlin (ifnm, linnum-nlb, Lines[Lone]); 32@/* Show the line */ 12@if (++Lone == Wsiz) 16@Lone = 0; @ nlb--; 12@} @ nlb = 0; /* No lines buffered */ 8@prtaft = Waft; /* Print n lines after */ 8@} 4@else /* Didn't match */ 8@{ 8@if (prtaft-- > 0) /* If must print lines after */ 12@{ 12@shwlin (ifnm, linnum, lptr); 32@/* Show the line */ 12@Lone = Lcur; /* Match pointers */ 12@} @ else if (nlb < Wbef) /* Count lines buffered */ @ nlb++; 8@} 4@} @if (ifnm != NULL) 4@fclose (ifp); @} @/* @ @*//* shwlin (fnm, linnum, line) 8@Show a matching line @Accepts : 8@fnm File name 8@linnum Line number 8@line Line to show @Returns : @*/ @shwlin (fnm, linnum, line) @char *fnm; /* File name */ @int linnum; /* Line number */ @char *line; /* Line (with newline at end) to print */ @{ @if (Shwfile && ( fnm != NULL) ) 4@printf("%s%s", fnm, Shwline?" ":":"); @if (Shwline) 4@printf("@%05d%:", linnum); @printf ("%s", line); @} @/* @ @*//* matlin (line) 8@Perform match against pattern and line @Accepts : 8@line Address of line to match @Returns : 8@ TRUE if match 24@FALSE if not @*/ @int matlin (line) @char *line; /* Line to match */ @{ @int rtncode; /* Return value from this routine */ @#ifdef NOREGEXP @char *pptr, *lptr, *tlptr; @int c1,c2; @#endif NOREGEXP @if (Debug) 4@printf ("Matching %s against %s", Pat, line); @#ifdef REGEX @rtncode = re_exec(line); /* Hand off to r/e evaluator */ @#endif REGEX @#ifdef REGCMP @rtncode = ( regex( Re, line ) != NULL ); @#endif REGCMP @#ifdef NOREGEX /* Have to do menial comparison.. */ @lptr = line; /* Init line pointer */ @for ( rtncode = -1; rtncode < 0; ) 4@{ 4@tlptr = lptr++; /* Get temp ptr to line */ 4@pptr = Pat; /* Get ptr to pattern */ 4@while (TRUE) 8@{ 8@if ((c1 = *pptr++) == NUL) 12@{ @ rtncode = 1; /* GOOD return value */ @ break; 12@} 8@if ((c2 = *tlptr++) == NUL) 12@{ @ rtncode = 0; /* BAD return value */ @ break; 12@} 8@if (isupper(c1)) 12@c1 = tolower(c1); 8@if (isupper(c2)) 12@c2 = tolower(c2); 8@if (c1 != c2) 12@break; 8@} 4@} @#endif NOREGEX @if (Debug) 4@printf("matlin returned %s\n", rtncode?"TRUE":"FALSE"); @return(rtncode); @} SHAR_EOF fi -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.