[mod.mac.sources] Grep-Wc 1.1 sources

dubois@uwmacc.UUCP (Paul DuBois) (12/08/86)

#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	Grep.src/Grep.h
#	Grep.src/GrepGrep.c
#	Grep.src/GrepIO.c
#	Grep.src/GrepMain.c
#	Grep.src/GrepPattern.c
sed 's/^X//' << 'SHAR_EOF' > Grep.src/Grep.h
X/*
X	Grep.h - Grep-Wc header file
X*/
X
X# include	<ControlMgr.h>
X# include	<MenuMgr.h>
X# include	<TextEdit.h>
X# include	<pascal.h>
X
X# define	nil			(0L)
X# define	bufSiz		512
X# define	monaco		4
X
X
Xtypedef enum	/*  menu item numbers  */
X{
X	about = 1,
X	/* --- */
X	count = 3,
X	search,
X	setPattern,
X	saveOutput
X};
X
X
Xtypedef enum		/* resource offsets from resource base number */
X{
X	aboutBox = 0,
X	alarmBox,
X	patBox
X};
X
X
Xtypedef enum	/* input file types */
X{
X	text,		/* text file */
X	mwrt3,		/* MacWrite 2.2 (version 3) */
X	mwrt6		/* MacWrite 4.5 (version 6) */
X};
X
X
Xextern WindowPtr	theWind;
Xextern MenuHandle	theMenu;
Xextern TEHandle		teHand;
Xextern int			streamType;
Xextern Boolean		fileOpen;	/* whether output file is open */
Xextern int			outFile;	/* output file reference number */
X
Xextern int			resBase;	/* base DA resource number */
Xextern Boolean		prtMatches;	/* true: print lines w/pattern */
Xextern Boolean		prtLineNum;	/* print line numbers if true */
Xextern Boolean		ignoreCase;	/* ignore letter case if true */
Xextern Boolean		havePat;	/* whether have good pattern or not */
X
Xextern Boolean		grepping;		/* true if currently searching */
Xextern Boolean		paused;			/* true if pause button was hit */
X
Xextern ControlHandle	pauseCtl;
Xextern ControlHandle	cancelCtl;
X
Xextern char			rawPattern[bufSiz];	/* pattern user types in */
X
X
X/*
X	Stream input defines.
X*/
X
X/* functions returning non-integer values */
X
XOSErr		OpenStream ();
XStringPtr	StreamGetS ();
XBoolean		GetStream ();
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > Grep.src/GrepGrep.c
X/*
X	GrepGrep.c - Grep and Word Count functions
X*/
X
X
X# include	"Grep.h"
X
Xstatic long	lineNum;
X
X
X/*
X	Set the grep state.  Val is either 0 (false) or 255 (true).
X*/
X
XGrepState (val)
Xint		val;
X{
X	HiliteControl (pauseCtl, val);
X	HiliteControl (cancelCtl, val);
X	grepping = ! (Boolean) val;
X}
X
X
X/*
X	Display lines matching (or not matching) pattern.  This is
X	called to get a line at a time.  Mouse clicks control the
X	state of the pause variable.
X*/
X
XGrepLine ()
X{
Xchar	buf[bufSiz];
X
X	if (!paused)
X	{
X		if (StreamGetS (buf) == nil)
X		{
X			StopGrep ();
X		}
X		else
X		{
X			++lineNum;
X			PtoCstr (buf);
X			if (match (buf) == prtMatches)
X			{
X				if (prtLineNum)
X				{
X					DisplayLong (lineNum);
X					DisplayString ("\p: ");
X				}
X				CtoPstr (buf);
X				DisplayString (buf);
X				DisplayLn ();
X			}
X		}
X	}
X}
X
X
X/*
X	Cancel any current grep operation.
X*/
X
XStopGrep ()
X{
X	if (grepping)
X	{
X		CloseStream ();
X		GrepState (255);
X	}
X}
X
X
X/*
X	Start grepping a file
X*/
X
XStartGrep ()
X{
X
X	StopGrep ();		/* terminate any ongoing grep operation */
X	if (!havePat)
X		GetGrepPat ();
X	if (/* now */ havePat)
X	{
X		if (GetStream ())	/* do grep setup */
X		{
X			lineNum = 0;
X			paused = false;
X			SetCTitle (pauseCtl, "\pPause");
X			GrepState (0); /* turn controls on, grepping true */
X		}
X	}
X}
X
X
X/*
X	Word Count
X*/
X
X
XWc ()
X{
Xregister long	lines = 0;
Xlong	nonEmptyLines = 0;
Xlong	words = 0;
Xregister long	chars = 0;
Xregister Boolean	inToken = false;
Xregister int	c, lastc;
X
X	for (lastc = '\r'; /* empty */ ; lastc = c)
X	{
X		c = StreamGetC ();
X		if (c == -1)
X			break; /* eof */
X		++chars;
X		switch (c)
X		{
X			case '\r':
X			{
X				++lines;
X				if (lastc != '\r')
X					++nonEmptyLines;
X				inToken = false;
X				break;
X			}
X			case ' ':
X			case '\t':
X			{
X				inToken = false;
X				break;
X			}
X			default:
X			{
X				if (inToken == false)
X				{
X					++words;
X					inToken = true;
X				}
X			}
X		}
X	}
X
X	if (lastc != '\r')	/* in case of missing cr on last line */
X	{
X		++lines;
X		++nonEmptyLines;
X	}
X	DisplayLong (chars);
X	DisplayString ("\p Chars, ");
X	DisplayLong (words);
X	DisplayString ("\p Words, ");
X	DisplayLong (lines);
X	if (streamType == text)
X		DisplayString ("\p Lines\r");
X	else
X	{
X		DisplayString ("\p Paragraphs (");
X		DisplayLong (nonEmptyLines);
X		DisplayString ("\p non-empty)\r");
X	}
X}
X
X
X/*
X	Add string to display area.  First insert it at the end.  Test if
X	must scroll lines off top to get the new stuff to show up.  If yes,
X	then do the scroll.  To keep from filling up the TERec, delete
X	whatever got scrolled out of view every once in a while.  (The number
X	of lines scrolled off the top to check for is arbitrary - I clobber
X	stuff after every 25 lines.)  To avoid unnecessary redrawing, set to
X	no clip before doing the delete (which would redraw) and the scroll
X	back down (which would also redraw).
X
X	Also write string to output file, if one is open.
X*/
X
XDisplayString (theStr)
XStringPtr	theStr;
X{
Xregister int	dispLines;	 /* number of lines displayable in window */
Xregister int	topLines;	 /* number of lines currently scrolled off top */
Xregister int	scrollLines; /* number of lines to scroll up */
Xregister int	height;
Xlong			len;
XRect			r;
X
X	len = theStr[0];
X	height = (**teHand).lineHeight;
X	TESetSelect (32760L, 32760L, teHand); /* set to insert at end */
X	TEInsert (theStr+1, len, teHand);
X	r = (**teHand).viewRect;
X	dispLines = (r.bottom - r.top) / height;
X	topLines = (r.top - (**teHand).destRect.top) / height;
X	scrollLines = (**teHand).nLines - topLines - dispLines;
X	if (scrollLines > 0) /* must scroll up */
X	{
X		TEScroll (0, -height * scrollLines, teHand); /* scroll up */
X		topLines += scrollLines;
X		if (topLines > 25)	/* keep TERec from filling up */
X		{
X/*
X	clobber first line(s), and scroll back down to resync what will then
X	be the first line.  Set clipping empty, so that the redraw from the
X	delete and the scroll down are not shown.
X*/
X			SetRect (&r, 0, 0, 0, 0);
X			ClipRect (&r);
X			TESetSelect (0L, (**teHand).lineStarts[topLines], teHand);
X			TEDelete (teHand);
X			TEScroll (0, height * topLines, teHand);
X			ClipRect (&theWind->portRect);
X		}
X	}
X
X	if (fileOpen)
X	{
X		if (FSWrite (outFile, &len, theStr+1) != noErr)
X		{
X			Alarm ("\pWrite Error (Closing File)");
X			FileOutput ();
X		}
X	}
X}
X
X
XDisplayLn ()
X{
X	DisplayString ("\p\r");
X}
X
X
XDisplayLong (l)
Xlong		l;
X{
XStr255		s;
X
X	NumToString (l, s);
X	DisplayString (s);
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > Grep.src/GrepIO.c
X/*
X	GrepIO - routines for treating TEXT or WORD (MacWrite)
X	files as a stream of characters or lines, and for output file
X	setup and termination.
X*/
X
X# include	<FileMgr.h>
X# include	<StdFilePkg.h>
X# include	"Grep.h"
X
X
X# define	bufSize	1024	/* input buffer size */
X# define	lineLen	65		/* output line len before break */
X
X
Xtypedef Byte ByteBuf[];
X
X
Xtypedef struct
X{
X	int		version;
X	int		paraOffset;
X	int		paraCount;
X} DocInfo3;
X
Xtypedef struct
X{
X	int	paraType;
X	int	paraLen;
X} ParaInfo3;
X
X
Xtypedef struct
X{
X	long	iArrayPos;
X	int		iArrayLen;
X} DocInfo6;
X
X
Xtypedef struct		/* information array element */
X{
X	int		height;
X	int		pagePos;
X	long	**paraHand;
X	union
X	{
X		Byte	st;		/* first byte is status */
X		long	pos;
X	} stPos;
X	int		dataLength;
X	int		formats;
X} ParaInfo6, InfoArray[];
X
X
X
XSFReply	outReply;				/* output file */
XSFReply	theStream;				/* SFGetFile reply record */
XBoolean	streamOpen = false;		/* whether stream currently open */
Xint		streamType;				/* file type of stream */
Xint		f;						/* input file reference number */
X
X
X/*  vars needed for TEXT stream only */
X
Xchar	filBuf[bufSize];	/* file buffer */
Xlong	fLen;
Xint		fChars;
X
X/*  vars needed for WORD stream only */
X
XByteBuf		**paraBuf = nil;
XInfoArray	**infoHand = nil;
XBoolean		compressed;
XBoolean		inPara;
Xint			nParas;		/* number of paragraphs */
Xint			paraNum;	/* current paragraph number */
Xint			pIndex;		/* index into paragraph */
Xint			pChars;		/* number of chars extracted from paragraph */
Xint			pLen;		/* number of chars in paragraph */
X
X/* decompression variables */
X
XBoolean	firstHalf;	/* which half of current index char */
XBoolean	needNib;	/* true = 2nd nibble needed for ascii */
XBoolean	nextAsc;	/* true = two nibbles needed */
XByte	lastNib;	/* holds last nibble */
X
X/* miscellaneous */
X
XPoint	dlogWhere = { 70 /* = y */, 100 /* = x */ };
XOSType	type[2] = { 'WORD', 'TEXT' };
X
X
XMyFRead (p, amount)
XPtr		p;
Xlong	amount;
X{ 
X	(void) FSRead (f, &amount, p);
X}
X
XMyFSeek (pos)
Xlong	pos;
X{
X	(void) SetFPos (f, fsFromStart, pos);
X}
X
X
X/*
X	_FSOpen - like FSOpen, but has open mode parameter.
X
X	CloseStream - close current stream, if one is open.  Releases any
X		heap storage currently used, if stream is WORD type.
X
X	OpenStream - open 'TEXT' or 'WORD' stream.
X		Return noErr if file opened OK, fnOpnErr or mFulErr if not.
X		if OK, set fileOpen true.
X*/
X
X
XOSErr _FSOpen (fName, vRefNum, refNum, mode)
XStringPtr	fName;
Xint			vRefNum;
Xint			*refNum;
Xint			mode;
X{
XParamBlockRec	p;
Xregister OSErr	result;
X
X	p.ioParam.ioNamePtr = fName;
X	p.ioParam.ioVRefNum = vRefNum;
X	p.ioParam.ioPermssn = mode;
X	p.ioParam.ioVersNum = 0;
X	p.ioParam.ioMisc = 0;
X	result = PBOpen (&p, false);
X	*refNum = p.ioParam.ioRefNum;
X	return (result);
X}
X
X
XCloseStream ()
X{
X	if (streamOpen)
X	{
X		(void) FSClose (f);
X		/*(void) FlushVol (nil, theStream.vRefNum);*/
X		streamOpen = false;
X		if (paraBuf != nil)
X		{
X			DisposHandle (paraBuf);
X			paraBuf = nil;
X		}
X		if (infoHand != nil)
X		{
X			DisposHandle (infoHand);
X			infoHand = nil;
X		}
X	}
X}
X
X
XOSErr OpenStream ()
X{
XDocInfo3		docInfo3;
XDocInfo6		docInfo6;
Xregister OSErr	result = fnOpnErr;
Xregister int	curRes;
X
X	CloseStream ();			/* close any currently open stream */
X
X	curRes = CurResFile ();
X	UseResFile (0);
X	SFGetFile (dlogWhere, "\p", nil, 2, &type, nil, &theStream);
X	UseResFile (curRes);
X	Update ();				/* refresh screen immediately */
X	if (theStream.good)
X	{
X		if (_FSOpen (theStream.fName, theStream.vRefNum,
X									&f, fsRdPerm) == noErr)
X		{
X			streamOpen = true;
X			result = noErr;
X		}
X	}
X
X	if (result == noErr)		/* so far, so good */
X	{
X		if (theStream.fType == 'TEXT')
X		{
X			streamType = text;
X			fChars = 0;	/* set these to trigger a read on the first */
X			fLen = 0;	/* call to StreamGetC() */
X		}
X		else	/* must be WORD file - determine version */
X		{
X			paraBuf = (ByteBuf **) NewHandle (0L);
X			MyFRead (&docInfo3, (long) sizeof (DocInfo3));
X
X			if (docInfo3.version == 3)		/* MacWrite 2.2 file */
X			{
X				streamType = mwrt3;
X				nParas = docInfo3.paraCount;
X				MyFSeek ((long) docInfo3.paraOffset);
X			}
X			else if (docInfo3.version == 6)	/* MacWrite 4.5 file */
X			{
X				streamType = mwrt6;
X				MyFSeek (264L);		/* 252 + 12 */
X				MyFRead (&docInfo6, (long) sizeof (DocInfo6));
X				MyFSeek (docInfo6.iArrayPos);
X				infoHand = (InfoArray **) NewHandle (docInfo6.iArrayLen);
X				if (infoHand == nil)
X				{
X					CloseStream ();
X					result = mFulErr;
X				}
X				else
X				{
X					HLock (infoHand);
X					MyFRead (*infoHand, (long) docInfo6.iArrayLen);
X					HUnlock (infoHand);
X					nParas = docInfo6.iArrayLen / sizeof (ParaInfo6);
X				}
X			}
X			else
X			{
X				CloseStream ();
X				Alarm ("\pFile created by unsupported MacWrite version");
X				result = fnOpnErr;
X			}
X			paraNum = -1;
X			inPara = false;			/* not in any paragraph yet */
X		}
X	}
X	return (result);
X}
X
X
XBoolean GetStream ()
X{
X
X	if (OpenStream () != noErr)
X		return (false);
X	DisplayString (theStream.fName);
X	DisplayString ("\p (");
X	DisplayString (streamType == text ? "\pText" : "\pMacWrite");
X	DisplayString ("\p file)\r");
X	return (true);
X}
X
X
X/*
X	_Decompress takes a nibble at a time of compressed text.  If more
X	nibbles are needed to complete the next character, return -1, else
X	returns the character.
X*/
X	   
X_Decompress (b)
XByte	b;
X{
Xregister int	result = -1;
X
X	if (needNib)				/* Low half of ascii nibble is needed. */
X	{
X		needNib = false;
X		result = lastNib | b;	/* Put the two halves together */
X	}
X	else if (nextAsc)			/* Two nibbles are needed */
X	{
X		nextAsc = false;
X		lastNib = b << 4;		/* Save this one as the high nibble */
X		needNib = true;			/* Need one more nibble */
X	}
X	else if (b == 15)			/* Nibble of 15 means the next char is ascii */
X		nextAsc = true;
X	else			/* Add the nibble value to the English decompression */
X					/* key (saved as Resource Type 'STR ' 700 in file)   */
X					/* to get the proper character */
X	{
X		result = " etnroaisdlhcfp" [b];	/* note: C string */
X	}
X	return (result);
X}
X
X
XBoolean ReadParaBuf ()
X{
X	SetHandleSize (paraBuf, pLen);		/* make big enough */
X	if (MemError () != noErr)
X	{
X		Alarm ("\pCan't read paragraph buffer");
X		paraNum = nParas;	/* force stream close and exit of GetC loop */
X		return (false);
X	}
X	HLock (paraBuf);
X	MyFRead (*paraBuf, (long) pLen);
X	HUnlock (paraBuf);
X	return (true);
X}
X
X
XWordStreamGetC ()		/* return -1 on EOF */
X{
Xregister int	c;
Xregister int	result;
XParaInfo3		paraInfo3;
X
X	if (!inPara)		/* must read in next paragraph */
X	{
X		for (;;)
X		{
X			if (++paraNum >= nParas)
X			{
X				CloseStream ();
X				return (-1);
X			}
X			if (streamType == mwrt3)
X			{
X				MyFRead (&paraInfo3, (long) sizeof (ParaInfo3));
X				pLen = paraInfo3.paraLen;
X				if (ReadParaBuf () == false)
X					continue;
X				if (paraInfo3.paraType != 1)
X				{
X					continue;	/* not text */
X				}
X				/* adding two skips length word */
X				if ((pLen = ((int *) **paraBuf)[0] + 2) == 2)
X					continue;	/* empty paragraph */
X				pChars = 2;
X				compressed = false;
X				inPara = true;
X				break;
X			}
X			else	/* mwrt6 */
X			{
X				if ((**infoHand)[paraNum].height <= 0)
X				{
X					continue;	/* not text */
X				}
X
X/*
X	Seek to this paragraph's data (must mask the high byte to get the
X	correct value).  Move to the paragraph, get its length, make
X	the pointer big enough, and read it in.  (skip to next para if this
X	one is empty, though.)
X	compressed will be set true if the paragraph is compressed.
X*/
X
X				MyFSeek ((**infoHand)[paraNum].stPos.pos & 0x00FFFFFF);
X				MyFRead (&pLen, (long) sizeof (int));	/* get length */
X				if (pLen == 0)
X				{
X					continue;						/* empty para - skip */
X				}
X				if (ReadParaBuf () == false)
X					continue;
X				compressed = ((**infoHand)[paraNum].stPos.st >> 3) & 1;
X				inPara = true;
X				nextAsc = false;
X				needNib = false;
X				pIndex = 0;		/* index into current paragraph */
X				pChars = 0;		/* chars extracted from current paragraph */
X				firstHalf = true;	/* use first half of current index char */
X				break;
X			}
X		}
X	}
X
X/*
X	At this point, know either that we have a new non-empty paragraph, or
X	are still in the previous one.
X*/
X
X	if (!compressed)	/* uncompressed */
X	{
X		c = (**paraBuf)[pChars];
X	}
X	else
X	{
X		do
X		{
X			c = (**paraBuf)[pIndex];
X			if (firstHalf)
X			{
X				c = _Decompress (((Byte) c) >> 4);
X			}
X			else
X			{
X				c = _Decompress ((Byte) (c & 0x0f));
X				++pIndex;	/* go to next char at next index */
X			}
X			firstHalf = !firstHalf;
X		} while (c == -1);
X	}
X	if (++pChars >= pLen)	/* see if need new paragraph next time */
X		inPara = false;
X	return (c);
X}
X
X
X/*
X	StreamGetC - get character from stream.
X*/
X
XStreamGetC ()
X{
X
X	if (!streamOpen)
X		return (-1);
X
X	if (streamType != text)
X		return (WordStreamGetC ());
X
X	if (fChars >= fLen)	/* need to read in a new block */
X	{
X		fLen = bufSize;
X		(void) FSRead (f, &fLen, filBuf);
X		if (fLen == 0)
X		{
X			CloseStream ();
X			return (-1);
X		}
X		fChars = 0;
X	}
X	return (filBuf[fChars++]);
X}
X
X
X/*
X	StreamGetS - get string from stream.
X
X	Returns nil if no string obtained, otherwise a pointer to the argument.
X*/
X
XStringPtr StreamGetS (s)
Xregister StringPtr	s;
X{
Xregister int		c;
Xregister StringPtr	result = nil;
X
X	s[0] = 0;	/* clear string */
X	while ((c = StreamGetC ()) != -1)
X	{
X		result = s;	/* got something, so StreamGetS succeeds */
X		if (c == '\r' || (s[0] > lineLen && c == ' '))
X			break;
X		s[++s[0]] = c;		/* add char to end */
X	}
X	return (result);
X}
X
X
X
XFileOutput ()
X{
XFInfo				f;
Xlong				pos;
Xregister int		curRes;
X
X	if (fileOpen)	/* close it */
X	{
X		fileOpen = false;
X		(void) GetFPos (outFile, &pos);
X		(void) SetEOF (outFile, pos);
X		(void) FSClose (outFile);
X		(void) FlushVol (nil, outReply.vRefNum);
X		SetItem (theMenu, saveOutput, "\pSave Output...");
X	}
X	else
X	{
X		curRes = CurResFile ();
X		UseResFile (0);
X		SFPutFile (dlogWhere, "\pWrite To...", "\p", nil, &outReply);
X		UseResFile (curRes);
X		Update ();			/* refresh screen immediately */
X		if (outReply.good)
X		{
X			if (GetFInfo (outReply.fName, outReply.vRefNum, &f) == noErr) /* exists */
X			{
X				if (f.fdType != 'TEXT')
X				{
X					Alarm ("\pNot a TEXT File");
X					return;
X				}
X			}
X			else	/* doesn't exist.  create it. */
X			{
X				if (Create (outReply.fName, outReply.vRefNum,
X							'Grep', 'TEXT') != noErr)
X				{
X					Alarm ("\pCan't Create");
X					return;
X				}
X			}
X
X			if (_FSOpen (outReply.fName, outReply.vRefNum,
X								&outFile, fsWrPerm) != noErr)
X				Alarm ("\pCan't Open");
X			else
X			{
X				fileOpen = true;
X				SetItem (theMenu, saveOutput, "\pStop Saving Output");
X			}
X		}
X	}
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > Grep.src/GrepMain.c
X/*
X	Grep-Wc
X
X	Grep - Globally search for Regular Expressions and Print, i.e.,
X		g/r.e./p
X
X	Wc - char, word, line/paragraph count
X
X	Works on text or MacWrite files
X
X	Special characters for patterns
X
X	^		Match beginning of line (if at beginning of pattern)
X	$		Match end of line (if at end of pattern)
X	.		Match any character
X	[..]	Match class of characters.  If first character following
X			the [ is ^, match all BUT the range of characters.  A range
X			of characters may be specified by separating them with a
X			dash, e.g., [a-z].  The dash itself may be included as a class
X			member by giving it as the first class char (e.g., [-a-z]).
X	*		Match any number of preceding things (if does not follow
X			*, ^ or $)
X
X	Characters which have special meanings only in certain places in
X	the pattern do not have that meaning elsewhere.  Special meaning
X	may be turned off otherwise by escaping it with '\'.  The backslash
X	may be entered into a pattern by doubling it.
X	
X
X	Paul DuBois
X	Wisconsin Regional Primate Research Center
X	1220 Capitol Court
X	University of Wisconsin-Madison
X	Madison, WI  53706  USA
X	
X	UUCP: {allegra, ihnp4, seismo}!uwvax!uwmacc!dubois
X	ARPA: dubois@rhesus.primate.wisc.edu
X		  dubois@unix.macc.wisc.edu
X
X	Version 1.0	 	12 March 1986  (written in Rascal)
X	Version 1.1		22 August 1986 (written in LightspeedC)
X		Fixed:
X		Menu goes away when window not active.
X		Can be selected from Apple menu while open w/o crashing.
X		Doesn't pass handle-ized rects to ToolBox routines.
X		Upper limits on window sizing are machine sensitive.
X		Always uses System file Get/PutFile dialogs (e.g., in MacWrite).
X*/
X
X
X
X# include	"Grep.h"
X# include	<DeviceMgr.h>
X# include	<EventMgr.h>
X# include	<MenuMgr.h>
X# include	<FileMgr.h>
X
X
X
X/*  global variables  */
X
XBoolean		isOpen = false;		/* true if DA already open */
XWindowPtr	theWind;
XDCtlPtr		dce;
Xint			dCtlRefNum;
XTEHandle	teHand;
XMenuHandle	theMenu;
XRect		growRect;
Xint			resBase;			/* base DA resource number */
XBoolean		fileOpen = false;	/* whether output file is open */
Xint			outFile;			/* output file reference number */
X
X/*
X	search options
X*/
X
XBoolean		prtMatches = true;	/* true: print lines w/pattern */
XBoolean		prtLineNum = false;	/* print line numbers if true */
XBoolean		ignoreCase = false;	/* ignore letter case if true */
X
X/*
X	other information about search
X*/
X
XBoolean		grepping;		/* true if currently searching */
XBoolean		paused;			/* true if pause button was hit */
XBoolean		havePat = true;	/* whether have good pattern or not */
X
XControlHandle	pauseCtl;
XControlHandle	cancelCtl;
X
X
X
XAlarm (mesg)
XStringPtr	mesg;
X{
X	ParamText (mesg, "\p", "\p", "\p");
X	(void) Alert (resBase + alarmBox, nil);
X}
X
X
X
X/*
X	Redraw the window.  It is assumed that the port is set correctly.
X	The ValidRect is done because sometimes this is called before any
X	update event is obtained.  The ValidRect cancels any that might be
X	pending, preventing two redraws.
X*/
X
XUpdate ()
X{
XRect	r;
X
X	DrawControls (theWind);
X	MoveTo (0, 24);
X	LineTo (30000, 24);
X	r = (**teHand).viewRect;
X	TEUpdate (&r, teHand);
X	ValidRect (&theWind->portRect);
X}
X
X
XFiddleMenu (activate)
XBoolean	activate;
X
X{
X	if (activate)
X		InsertMenu (theMenu, 0);
X	else
X		DeleteMenu (dCtlRefNum);
X
X	DrawMenuBar ();
X}
X
X
Xmain(p, d, n)
Xregister cntrlParam	*p;	/*  ==> parameter block  */
Xregister DCtlPtr	d;	/*  ==> device control entry  */
Xint					n;	/*  entry point selector  */
X
X{
XPoint			thePt;
XRect			r;
XEventRecord		*theEvent;
XControlHandle	ctl;
X
X	dCtlRefNum = (dce = d)->dCtlRefNum;
X
X	/*  check to make sure data area was allocated  */
X		
X	if (d->dCtlStorage == 0)
X	{
X		if (n == 0)						/*  open  */
X			CloseDriver(dCtlRefNum);
X	}
X	else switch (n)	/*  dispatch  */
X	{
X
X	case 0:		/*  open  */
X
X		/*
X			Need to set these values each time, because the OS resets
X			them before every call...
X		*/
X
X		d->dCtlFlags |= dNeedLock | dNeedTime | dNeedGoodBye;
X		d->dCtlDelay = 0;
X		d->dCtlEMask = updateEvt | activateEvt | mouseDown ;
X		d->dCtlMenu = dCtlRefNum;
X
X/*
X	Check to see whether already open or not.  If not, set up window,
X	window growing limits, etc.  The SelectWindow is for the case where
X	the DA is already open but hidden behind some other window.
X	Selecting Grep from the DA menu when already open simply has the
X	effect of bringing it to the front.
X*/
X	
X		if (!isOpen)
X		{
X			isOpen = true;
X
X			theMenu = NewMenu (dCtlRefNum, "\pGrep-Wc");
X			AppendMenu (theMenu, "\pAbout Grep-Wc;(-;Count...");
X			AppendMenu (theMenu, "\pSearch...;Set Pattern...;Save Output...");
X
X			GetWMgrPort (&theWind);
X			growRect = theWind->portRect;
X			growRect.left = 180;
X			growRect.top = 60;
X			SetRect (&r, 25, 80, 490, 320);
X			theWind = NewWindow (nil, &r, "\pGrep-Wc", true,
X									documentProc, -1L, true, 0L);
X			((WindowPeek) theWind)->windowKind = dCtlRefNum;
X			SetPort (theWind);
X			TextFont (monaco);
X			TextSize (9);
X
X			/*  Create TERec and build controls  */
X
X			OffsetRect (&r, -r.left, -r.top);	/* move to (0, 0) */
X			r.top += 25;			/* leave room for buttons */
X			r.left += 6;
X			teHand = TENew (&r, &r);
X			(**teHand).crOnly = -1; /* no word wrap */
X
X			SetRect (&r, 5, 2, 85, 22);
X			pauseCtl =
X				NewControl (theWind, &r, "\pPause", true,
X								0, 0, 0, pushButProc, nil);
X			OffsetRect (&r, 90, 0);
X			cancelCtl =
X				NewControl (theWind, &r, "\pCancel", true,
X								0, 0, 0, pushButProc, nil);
X		
X			GrepState (255);	/* set grepping false, inactivate buttons */
X
X			/* determine base resource number */
X
X			resBase =  0xc000 + (~dCtlRefNum << 5);
X
X		}
X
X		SelectWindow (theWind);
X
X		break;
X
X	case 2:		/*  control  */
X
X		SetPort (theWind);
X		switch (p->csCode)
X		{
X
X		case accEvent:	/* need glasses for next statement */
X
X			theEvent = (EventRecord *) * (long *) &p->csParam;
X			switch (theEvent->what)
X			{
X
X			case activateEvt:
X
X				/*
X					Set up or destroy menu, depending
X					on activate type.
X				*/
X				FiddleMenu (theEvent->modifiers & activeFlag);
X				break;
X			
X			case updateEvt:
X			
X				BeginUpdate (theWind);
X				Update ();
X				EndUpdate (theWind);
X				break;
X
X			case mouseDown:
X
X/*
X	Check mouse click and whether it's a window grow event.
X	Must do the test explicitly, since DA's don't get told about
X	clicks in the inGrow region.
X*/
X				thePt = theEvent->where;
X				GlobalToLocal (&thePt);
X				r = theWind->portRect;
X				r.left = r.right - 15;
X				r.top = r.bottom - 15;
X				if (PtInRect (thePt, &r))
X				{
X					LocalToGlobal (&thePt);
X					* (long *) &thePt = GrowWindow (theWind, thePt, &growRect);
X					SizeWindow (theWind, thePt.h, thePt.v, true);
X					r = theWind->portRect;
X					ClipRect (&r);
X					/*InvalRect (&r);		/* force update event */
X				/*
X					Reset the text viewRect.  It's
X					not necessary to reset the destRect,
X					since only the top and left are
X					used, and they haven't changed.
X				*/
X					r.top += 25;
X					r.left += 6;
X					(**teHand).viewRect = r;
X				}
X				else if (FindControl (thePt, theWind, &ctl))
X				{
X					if (TrackControl (ctl, thePt, nil))
X					{
X						if (ctl == cancelCtl)
X						{
X							StopGrep ();
X						}
X						else if (ctl == pauseCtl)
X						{
X							if (paused)
X								SetCTitle (pauseCtl, "\pPause");
X							else
X								SetCTitle (pauseCtl, "\pResume");
X							paused = !paused;
X						}
X					}
X				}
X				break;
X			}
X			break;
X
X		case accMenu:
X
X			switch (* (int *) ((Ptr) &p->csParam + 2))
X			{
X				case about:
X					(void) Alert (resBase + aboutBox, nil);
X					break;
X
X				case count:
X					StopGrep ();	/* terminate any ongoing grep operation */
X					if (GetStream ())
X						Wc ();
X					break;
X
X				case search:
X					StartGrep ();
X					break;
X
X				case setPattern:
X					GetGrepPat ();
X					break;
X
X				case saveOutput:
X					FileOutput ();
X					break;
X			}
X
X			HiliteMenu (0);
X			break;
X
X		case accRun:
X			if ((theWind == FrontWindow ()) && grepping)
X				GrepLine ();
X			break;
X
X		case goodBye:
X			CloseStream ();		 		/* close any open input file */
X			if (fileOpen)				/* close output file if open */
X				FileOutput ();
X			break;
X
X		}
X		break;
X
X	case 4:		/*  close  */
X		CloseStream ();		 		/* close any open input file */
X		if (fileOpen)				/* close output file if open */
X			FileOutput ();
X		FiddleMenu (false);
X		DisposeMenu (theMenu);		/* toss menu */
X		TEDispose (teHand);	 		/* toss text record */
X		DisposeWindow (theWind);	/* get rid of window and controls */
X		break;
X	}
X	
X	/*  done  */
X	
X	return (0);
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > Grep.src/GrepPattern.c
X/*
X	GrepCompile - routines for compiling patterns into internal form,
X	and for matching strings against the compiled pattern.
X*/
X
X
X# include	<DialogMgr.h>
X# include	"Grep.h"
X
X/*  special internal chars for compiled pattern  */
X
X# define	CCL		1		/* match characters in class */
X# define	NCCL	2		/* all but characters in class */
X# define	CRANGE	3		/* range of chars */
X# define	ENDCCL	4		/* end char class */
X# define	ANY		5		/* match any char */
X# define	CLOSURE	6		/* closure */
X# define	EOL		7		/* end of line */
X
X
X/*  pattern compilation and matching vars  */
X
Xstatic Boolean	matchBol;			/* match beginning of line? */
Xstatic int		pix;				/* index into pattern */
Xstatic int		pMark;
Xstatic Boolean	canClose;
X
X/*
X	Pattern buffers.  rawPattern is the text typed by user into
X	the dialog box.  cmpPattern is the compiled pattern.  Initially
X	they are both empty.  (Format of the nil compiled pattern is
X	dependent on algorithm used to compile rawPattern.)  This is
X	legal - it matches every line.  If a file is grepped without
X	specifying a pattern, therefore, the whole file will be
X	displayed.  A side effect of this is to turn grep on WORD files
X	into a WORD-to-TEXT file converter when the save output option
X	is turned on.
X*/
X
Xchar		rawPattern[bufSiz] = "";
Xstatic char	cmpPattern[bufSiz] = "";
X
X
X/* ----------------------------------------------------------------------- */
X/*						pattern-compilation routines					 */
X/* ----------------------------------------------------------------------- */
X
X
X
X/*
X	Add char to pattern (may be a metachar, not necessarily
X	a literal character to match)
X*/
X
XAddPatChar (c)
Xchar	c;
X{
X	if (ignoreCase && c >= 'A' && c <= 'Z')
X		c += 'a' - 'A';
X	cmpPattern[pix] = c;
X	++pix;
X	cmpPattern[pix] = 0;
X
X}	/* AddPatChar */
X
X
X/*
X	Put a closure indicator into the pattern, in front of the
X	stuff that's to be closed.
X*/
X
XAddClosure ()
X{
Xregister int	i;
X
X	++pix;
X	for (i = pix; i > pMark; --i)
X	/*loop (, i = pix,	, --i <= pMark)*/
X		cmpPattern [i] = cmpPattern [i-1];
X	cmpPattern [pMark] = CLOSURE;
X	canClose = false;
X
X}	/* AddClosure */
X
X
X/*
X	have found something that may be followed by a closure.  set
X	canClose to indicate that fact, and set a mark to remember where
X	the closable thing is.
X*/
X
XMarkIt ()
X{
X	pMark = pix;		/* set mark in case closure comes up next */
X	canClose = true;
X}
X
X
X/*
X	Get escaped char (char following '\').  The only special one
X	right now is '\t', which is turned into a tab.  Caller must check
X	that return value isn't zero.
X*/
X
Xchar EscapeChar (c)
Xchar	c;
X{
X	return (c == 't' ? '\t' : c);
X}
X
X/*
X	compile character class.  pass pointer to char after '[' that begins
X	the class pattern.  Return nil if (error, else pointer to char
X	after closing ']' bracket.
X*/
X
XStringPtr Class (p)
Xregister StringPtr	p;
X{
Xregister char	c, type, low, high;
X
X	type = CCL;			/* 'character class' metachar */
X	if (*p == '^')
X	{
X		type = NCCL;		/* 'match all but this class' metachar */
X		++p;
X	}
X	AddPatChar (type);
X	for (;;)
X	{
X		c = *p;
X		++p;
X		if (c == '\\')
X		{
X			c = EscapeChar (*p);
X			++p;
X		}
X		else if (c == ']')
X			break;				/* end of class pattern */
X		if (c == 0)
X			return (nil);		/* missing ']' - pattern error */
X		if (*p != '-')
X			AddPatChar (c);
X		else		/* range */
X		{
X			low = c;					/* low end */
X			++p;
X			high = *p;					/* high end */
X			++p;
X			if (high == 0)
X				return (nil);			/* pattern error */
X			AddPatChar (CRANGE);
X			AddPatChar (low);
X			AddPatChar (high);
X		}
X	}
X	AddPatChar (ENDCCL);
X	return (p);							/* all ok */
X
X}	/* class */
X
X
X/*
X	COMPILE - compile string into internal form suitable for efficient
X		pattern matching.  String should be in C format.
X*/
X
XBoolean Compile (p)
Xregister StringPtr	p;
X{
Xregister char	c;
X
X	pix = 0;
X	cmpPattern[0] = 0;
X	canClose = false;
X	matchBol = false;
X/*
X	check for ^ - it's only special at beginning of line
X*/
X	if (*p == '^')
X	{
X		matchBol = true;
X		++p;
X	}
X	for (;;)
X	{
X		c = *p;
X		++p;
X
X		if (c == '*')
X		{
X/*
X	if (canClose is true, there was a preceding pattern which can be
X	closed (not closure, ^ or $), so close it.  otherwise, take *
X	literally.
X*/
X			if (canClose)	/* something to close */
X			{
X				AddClosure ();
X				continue;
X			}
X		}
X
X/*
X	$ only special at end of line
X*/
X		if (c == '$' && *p == 0)
X		{
X			AddPatChar ((char) EOL);
X			continue;
X		}
X/*
X	at this point we know we have a character that can be followed by a
X	closure, so mark the pattern position.
X*/				
X		MarkIt ();
X
X/*
X	use most escaped chars literally, except null, which is an error,
X	and \t, which is a tab.
X*/
X		if (c == '\\')
X		{
X			c = EscapeChar (*p++);
X			if (c == 0)
X				return (false);					/* pattern error */
X			AddPatChar (c);
X			continue;
X		}
X		if (c == 0) break;						/* done compiling */
X		switch (c)
X		{
X			case '.': AddPatChar (ANY); break;	/* match any char */
X			case '[':							/* match character class */
X			{
X				if ((p = Class (p)) == nil)
X					return (false);				/* class pattern error */
X				break;
X			}
X			default: AddPatChar (c);			/* match char literally */
X		}
X
X	}	/* loop */
X
X	return (true);								/* all ok */
X
X}	/* compile */
X
X
X/* ----------------------------------------------------------------------- */
X/*						 pattern-matching routines					   */
X/* ----------------------------------------------------------------------- */
X
X/*
X	NEXTPOS - find position in pattern of next component to match
X*/
X
XStringPtr NextPos (p)
Xregister StringPtr	p;
X{
Xregister char	c;
X
X	c = *p;
X	++p;
X	if (c == CCL || c == NCCL)
X	{
X		do		/* look for end of class stuff */
X		{
X			c = *p;
X			++p;
X		} while (c != ENDCCL);
X	}
X	return (p);
X
X}	/* NextPos */
X
X
XBoolean InClass (c, p)
Xregister char		c;
Xregister StringPtr	p;
X{
Xregister char	high, low, pc;
X
X	for (;;)
X	{
X		pc = *p;
X		++p;
X		if (pc == ENDCCL)
X			return (false);
X		if (pc == CRANGE)		/* range */
X		{
X			low = *p;
X			++p;
X			high = *p;
X			++p;
X			if (low <= c && c <= high)
X				break;			/* it's within the range */
X		}
X		else if (c == pc)
X			break;				/* it matched this char of class */
X	}
X	return (true);
X
X}	/* InClass */
X
X/*
X	OMATCH - match character c against the current pattern position.
X*/
X
XBoolean omatch (c, p)
Xregister char		c;
Xregister StringPtr	p;
X{
Xregister char	pc;
X
X	if (ignoreCase && c >= 'A' && c <= 'Z')
X		c += 'a' - 'A';
X	pc = *p;
X	++p;
X	switch (pc)
X	{
X		case CCL: return (InClass (c, p));
X		case NCCL: return (!InClass (c, p));
X		case ANY: return (c != 0);	/* don't match end of line */
X		default: return (c == pc);
X	}
X
X}	/* omatch */
X
X
X/*
X	try to match pattern p at the given position in string s
X*/
X
XBoolean amatch (s, p)
Xregister StringPtr	s, p;
X{
Xregister StringPtr	cursp;
X
X	if (*p == 0)
X		return (true);		/* end of pattern, have matched it */
X	if (*p == EOL)
X		return (*s == 0);	/* must be end of string to match EOL */
X
X	if (*p == CLOSURE)
X	{
X
X/*
X	advance as far as possible, matching the current pattern position.
X	when omatch fails, s will point 1 past the character that failed.
X	then back up one and try to match rest of pattern.  if that fails,
X	keep retreating until back at point of original closure start
X*/
X
X		++p;		/* skip closure marker */
X		cursp = s;	/* save current string position */
X
X		while (omatch (*s++, p))
X			/* march! */ ;
X
X		do						/* keep backing up */
X		{
X			--s;
X			if (amatch (s, NextPos (p)))
X				return (true);
X		} while (s > cursp);
X		return (false);
X	}
X
X	if (omatch (*s++, p))
X		return (amatch (s, NextPos (p)));
X	return (false);
X
X}	/* amatch */
X
X
X/*
X	MATCH - match string s against the compiled pattern
X
X	if matchBol is true, then anchor the match to the beginning of the
X	string, else try the pattern against successive string positions until
X	the match succeeds or the end of the string is reached.
X
X	s should be in C format.
X*/
X
XBoolean match (s)
Xregister StringPtr	s;
X{
X	if (matchBol)	/* anchored match */
X	{
X		return (amatch (s, cmpPattern));
X	}
X	for (;;)			/* floating match */
X	{
X		if (amatch (s, cmpPattern))
X			return (true);
X		if (*s == 0)
X			return (false);	/* end of string but no match */
X		++s;
X	}
X
X}	/* match */
X
X
X/*
X	Routines for presenting the pattern entry dialog.
X*/
X
X
X/*
X	pattern dialog items
X
X	item	type
X	1		ok button
X	2		cancel button
X	3		prompt
X	4		edittext item for typing in pattern
X	5		"lines not containing pattern" radio button
X	6		"print line numbers" check box
X	7		"ignore case" check box
X
X	Puts the string entered into theString, which on return is
X	empty if either the user clicked Cancel or typed no string
X	and clicked ok.
X*/
X
Xenum
X{
X	okButton = 1,
X	cancelButton,
X	promptStatText,
X	patText,
X	lineOption,
X	numberOption,
X	caseOption
X};
X
X
Xstatic DialogPtr	theDialog;
X
X
X
X/*
X	Set or get the value of a checkbox (i.e., boolean) dialog item
X*/
X
XSetDBoolean (itemNo, itemValue)
Xint		itemNo;
XBoolean	itemValue;
X{
XHandle	itemHandle;
Xint		itemType;
XRect	r;
X
X	GetDItem (theDialog, itemNo, &itemType, &itemHandle, &r);
X/*
X	Note type conversion here.  True turns the control on.
X*/
X	SetCtlValue (itemHandle, (int) itemValue);
X}
X
X
XBoolean GetDBoolean (itemNo)
Xint		itemNo;
X{
XHandle	itemHandle;
Xint		itemType;
XRect	r;
X
X	GetDItem (theDialog, itemNo, &itemType, &itemHandle, &r);
X	return ((Boolean) GetCtlValue (itemHandle));
X}
X
X
XBoolean GetPatDlog ()
X{
Xint		itemNo, itemType;
XHandle	itemHandle;
XRect	r;
Xregister Boolean	result;
X
X	theDialog = GetNewDialog (resBase + patBox, nil, -1L);
X	SetDBoolean (lineOption, !prtMatches);
X	SetDBoolean (numberOption, prtLineNum);
X	SetDBoolean (caseOption, ignoreCase);
X	GetDItem (theDialog, patText, &itemType, &itemHandle, &r);
X	SetIText (itemHandle, rawPattern);
X	SelIText (theDialog, patText, 0, 32760);
X	ShowWindow (theDialog);
X	for (;;)
X	{
X		ModalDialog (nil, &itemNo);
X		if (itemNo == cancelButton)
X		{
X			result = false;
X			break;
X		}
X		else if (itemNo == okButton)
X		{
X			GetDItem (theDialog, patText, &itemType, &itemHandle, &r);
X			GetIText (itemHandle, rawPattern);
X			prtMatches = !GetDBoolean (lineOption);
X			prtLineNum = GetDBoolean (numberOption);
X			ignoreCase = GetDBoolean (caseOption);
X			result = true;
X			break;
X		}
X		else		/* must be option check box - flip value */
X		{
X			SetDBoolean (itemNo, !GetDBoolean (itemNo));
X		}
X	}
X
X	DisposDialog (theDialog);
X	return (result);
X
X}	/* GetPatDlog */
X
X
XGetGrepPat ()
X{
X	if (GetPatDlog ())
X	{
X		PtoCstr (rawPattern);
X		havePat = Compile (rawPattern);
X		CtoPstr (rawPattern);
X		if (!havePat)
X			Alarm ("\pBad Pattern");
X	}
X}
SHAR_EOF
exit