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 (¶Info3, (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