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