<> <> <> <> <> <> <> DIRECTORY Feedback, FileNames, FS, GGBasicTypes, GGControlPanelTypes, GGFileIn, GGFileOps, GGInterfaceTypes, GGModelTypes, GGScene, GGState, GGUserInput, GGWindow, GList, IO, Match, MatchGrep, MatchViewer, Rope, ViewerOps; MatchGrepImpl: CEDAR PROGRAM IMPORTS Feedback, FileNames, FS, GGFileIn, GGFileOps, GGScene, GGState, GGWindow, GList, IO, Match, MatchViewer, Rope, ViewerOps EXPORTS GGInterfaceTypes, MatchGrep = BEGIN ControlsObj: PUBLIC TYPE = GGControlPanelTypes.ControlsObj; GGData: TYPE = GGInterfaceTypes.GGData; Point: TYPE = GGBasicTypes.Point; Scene: TYPE = GGModelTypes.Scene; <<>> fileList: LIST OF Rope.ROPE _ NIL; lastSpawned: GGData _ NIL; Grep: PUBLIC GGUserInput.UserInputProc = { <> GrepIt[ggData, event, FALSE]; }; GrepListOnly: PUBLIC GGUserInput.UserInputProc = { <> GrepIt[ggData, event, TRUE]; }; GrepIt: PROC [ggData: GGData, event: LIST OF REF ANY, listOnly: BOOL _ FALSE] = { FillFileList: FS.NameProc = { fileList _ CONS[fullFName, fileList]; RETURN[TRUE];}; matchData: MatchViewer.MatchData _ MatchViewer.GetMatchData[]; grepData: GGData _ MatchViewer.GetGrepData[]; patRope: Rope.ROPE _ NARROW[event.rest.first]; patStream: IO.STREAM _ IO.RIS[patRope]; IF IO.SkipWhitespace[patStream]=Rope.Length[patRope] THEN MatchViewer.ErrorFeedback["Must select a filename pattern"] ELSE { fileList _ NIL; -- clear out global file list UNTIL IO.EndOf[patStream] DO nextPat: Rope.ROPE _ IO.GetTokenRope[patStream, IO.IDProc].token; thisPat: Rope.ROPE _ FileNames.ResolveRelativePath[nextPat]; FS.EnumerateForNames[thisPat, FillFileList, grepData.currentWDir ! FS.Error => { msg: Rope.ROPE _ IO.PutFR["Invalid filename pattern: %g", IO.rope[thisPat]]; MatchViewer.ErrorFeedback[msg]; GOTO Abort; };]; ENDLOOP; IF fileList = NIL THEN MatchViewer.ErrorFeedback["No files match the pattern"] ELSE { aMatch: BOOL _ FALSE; fileList _ NARROW[GList.DReverse[fileList]]; -- back in order of enumeration FOR files: LIST OF Rope.ROPE _ fileList, files.rest UNTIL files = NIL DO fileList _ files.rest; IF GrepFile[files.first, TRUE] THEN { aMatch _ TRUE; IF listOnly THEN IO.PutF[matchData.commander.out, "\n%g matched.", IO.rope[files.first]] ELSE { SpawnViewer[grepData, files.first]; RETURN; -- Expects user to invoke NextFile for more files }; }; ENDLOOP; MatchViewer.ErrorFeedback[IF aMatch THEN "No more matches." ELSE "No matches."]; IF listOnly THEN IO.PutRope[matchData.commander.out, "\n"]; }; }; EXITS Abort => Feedback.Blink[MatchViewer.GetToData[].router, $Error]; }; GrepFile: PROC [fullFName: Rope.ROPE, verbose: BOOL _ TRUE] RETURNS [found: BOOL _ FALSE] = { <> grepData: GGData _ MatchViewer.GetGrepData[]; fullName: Rope.ROPE; success, versionSpecified: BOOL _ FALSE; [fullName, success, versionSpecified] _ GGFileOps.GetGargoyleFileName["GrepFile", fullFName, NIL, grepData.router, FALSE]; IF NOT success THEN { mesg: Rope.ROPE _ IO.PutFR["Couldn't load %g.\n", IO.rope[fullName]]; IF verbose THEN MatchViewer.ErrorFeedback[mesg]; RETURN[FALSE]; } ELSE { matchData: MatchViewer.MatchData _ MatchViewer.GetMatchData[]; f: FS.STREAM; IF verbose THEN Feedback.PutF[grepData.router, oneLiner, $Feedback, "Finding %g.", IO.rope[fullName]]; f _ FS.StreamOpen[fullName, $read ! FS.Error, IO.Error => { msgRope: Rope.ROPE _ IO.PutFR["Could not find: %g", [rope[fullName]]]; Feedback.Append[grepData.router, oneLiner, $Error, msgRope]; GOTO Abort; };]; grepData.scene _ GGScene.CreateScene[]; -- give GGData a new clean slate/scene [] _ GGFileIn.FileinSceneOnly[f, grepData.scene, FALSE, grepData.camera]; f.Close[]; IF verbose THEN Feedback.PutF[grepData.router, begin, $Feedback, "Searching %g ... ", IO.rope[fullName]]; <> matchData.theirData _ NIL; -- force a reset of search lists success _ Match.Search[grepData, LIST[$MatchSearch, $SearchFromTop], FALSE]; IF verbose THEN Feedback.PutF[grepData.router, end, $Feedback, IF success THEN "It matched!!!" ELSE "No match"]; RETURN[success]; }; EXITS Abort => { Feedback.Blink[MatchViewer.GetToData[].router, $Error]; RETURN[FALSE]; }; }; NextFile: PUBLIC GGUserInput.UserInputProc = { grepData: GGData _ MatchViewer.GetGrepData[]; IF fileList = NIL THEN MatchViewer.ErrorFeedback["No more file matches."] ELSE { FOR files: LIST OF Rope.ROPE _ fileList, files.rest UNTIL files = NIL DO fileList _ files.rest; IF GrepFile[files.first, TRUE] THEN { SpawnViewer[grepData, files.first]; RETURN; }; ENDLOOP; MatchViewer.ErrorFeedback["No more file matches."]; }; }; SpawnViewer: PROC [ggData: GGData, fileName: Rope.ROPE] = { <> f: FS.STREAM _ FS.StreamOpen[fileName, $read ! FS.Error, IO.Error => { msgRope: Rope.ROPE _ IO.PutFR["Could not find: %g", [rope[fileName]]]; Feedback.Append[ggData.router, oneLiner, $Error, msgRope]; GOTO Abort; };]; IF lastSpawned = NIL OR lastSpawned.controls.topper.destroyed OR lastSpawned.controls.topper.newVersion THEN { -- we create a gargoyle viewer lastSpawned _ GGWindow.CreateWindow[ggData.scene, TRUE, FALSE, ggData.currentWDir]; [] _ GGFileIn.FileinOptionsOnly[f, lastSpawned, FALSE]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, ggData: lastSpawned, remake: triggerBag, edited: FALSE, okToSkipCapture: FALSE]; ViewerOps.OpenIcon[lastSpawned.controls.topper]; MatchViewer.CenterSel[NIL, lastSpawned]; -- this may not happen, since OpenIcon is async. } ELSE { -- we use the old unedited viewer lastSpawned.scene _ ggData.scene; [] _ GGFileIn.FileinOptionsOnly[f, lastSpawned, FALSE]; MatchViewer.CenterSel[NIL, lastSpawned]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, ggData: lastSpawned, remake: triggerBag, edited: FALSE, okToSkipCapture: FALSE]; }; <> lastSpawned.controls.topper.file _ FileNames.StripVersionNumber[fileName]; lastSpawned.controls.topper.label _ FileNames.GetShortName[path: fileName]; lastSpawned.controls.topper.name _ fileName; GGState.ChangeIcon[lastSpawned, $clean]; -- will also repaint the label EXITS Abort => ERROR; -- we've already opened fileName prior to this, so something's screwy }; <<>> END.