[comp.sources.unix] v15i082: Windowing search

rsalz@uunet.uu.net (Rich Salz) (07/07/88)

Submitted-by: "Mark E. Mallett" <mem@zinn.mv.com>
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 <stdio.h>
@#include <ctype.h>

@#ifdef	SYSINC
@#include <sys/types.h>
@#include <sys/dir.h>
@#else	/* !SYSINC */
@#include <types.h>
@#include <dir.h>
@#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@<value>         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.