<> <> DIRECTORY AMBridge USING [ContextPC], AMModel USING [Context, ContextSection, RootContext, Section, SectionSource, SectionVersion, Source, SourceFileName, SourceObj, SourceSection, SourceVersion], AMModelBridge USING [LoadedSection, LoadedSectionForProc, LoadedSectionForProgPC], AMViewerOps USING [ReportProc, SelectionOption, Severity], AMTypes USING [GlobalParent, TVType, UnderClass], BBContext, BBSafety USING [Mother], BcdDefs USING [NullVersion, VersionStamp], Commander USING [CommandProc, Handle, Register], Convert USING [ValueToRope], Directory USING [Error, GetProps, Lookup], File USING [Capability], FileLookup USING [LookupFile, Result], LSD USING [Entry, Lookup], IO, List, Process USING [Pause, SecondsToTicks], ProcessProps, PutGet USING [FromRope], Rope, RTBasic USING [TV], System USING [GreenwichMeanTime], TEditDocument USING [LineTable, TEditDocumentData], TEditImpl USING [InitTEditDocument], TEditScrolling USING [AutoScroll, ScrollToPosition], TEditTouchup USING [LockAfterRefresh, UnlockAfterRefresh], TextNode USING [Forward, Location, Ref], TimeStamp USING [Null, Stamp], TiogaMenuOps USING [DefaultMenus, Open], TiogaOps USING [GetSelection, Location, LocOffset, LocRelative, Ref, Root, SetSelection, ViewerDoc], VersionMap USING [FetchName, FetchStamp, Length, Map, MapList], VersionMapDefaults USING [FileNameFromVersion, GetMapList], VersionMapOps USING [SourceFileEntry, SourceFileList], ViewerClasses USING [Viewer], ViewerOps USING [EnumerateViewers, EnumProc, OpenIcon], ViewerTools USING [MakeNewTextViewer, SelPos, SelPosRec, SetSelection], WorldVM USING [LocalWorld, World]; AMViewerOpsImpl: CEDAR MONITOR IMPORTS AMBridge, AMModel, AMModelBridge, AMTypes, BBContext, BBSafety, Commander, Convert, Directory, FileLookup, List, LSD, IO, Process, ProcessProps, PutGet, Rope, TEditImpl, TEditScrolling, TEditTouchup, TextNode, TiogaMenuOps, TiogaOps, VersionMap, VersionMapDefaults, ViewerOps, ViewerTools, WorldVM EXPORTS AMViewerOps, VersionMapOps SHARES VersionMap = BEGIN OPEN AMViewerOps, RTBasic; <> Section: TYPE = AMModel.Section; Source: TYPE = AMModel.Source; SourceFileEntry: TYPE = VersionMapOps.SourceFileEntry; SourceFileList: TYPE = VersionMapOps.SourceFileList; LoadedSection: TYPE = AMModelBridge.LoadedSection; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; TV: TYPE = RTBasic.TV; World: TYPE = WorldVM.World; <> ropeCreator: PROC [name: ROPE] RETURNS [rope: ROPE] _ NIL; <> oldSetSel: BOOL _ FALSE; AutoScrollOK: BOOL _ TRUE; skipComments: BOOL _ TRUE; lagLagMsg, lagMsg: ROPE _ NIL; lagErr: ROPE _ NIL; <<>> <> SourceError: PUBLIC ERROR [reason: ROPE] = CODE; ReportError: PUBLIC ERROR [msg: ROPE, severity: Severity] = CODE; SourceFromTV: PUBLIC PROC [tv: TV, report: ReportProc] RETURNS [name: ROPE _ NIL, index: INT _ -1] = { <> errmsg: ROPE _ NIL; inner: PROC = TRUSTED { section: Section _ NIL; source: Source _ NIL; SELECT AMTypes.UnderClass[AMTypes.TVType[tv]] FROM procedure => section _ AMModelBridge.LoadedSectionForProc[tv].section; globalFrame => section _ AMModel.ContextSection[tv]; localFrame => { section _ AMModelBridge.LoadedSectionForProgPC[ prog: AMTypes.GlobalParent[tv], pc: AMBridge.ContextPC[tv]].section; }; ENDCASE => {errmsg _ "invalid TV"; RETURN}; source _ AMModel.SectionSource[section]; name _ SourceToFullName[source, report]; IF name = NIL THEN { errmsg _ VersionExpectedMessage[AMModel.SourceVersion[source]]; RETURN}; WITH s: source^ SELECT FROM entire => index _ 0; field => index _ s.firstCharIndex; ENDCASE => ERROR; }; err: ROPE _ NIL; Report[report, comment, " Finding source... "]; err _ BBSafety.Mother[inner]; IF errmsg = NIL THEN errmsg _ err; IF errmsg # NIL THEN Report[report, fatal, " No source: ", errmsg] ELSE Report[report, success, NIL]; }; OpenSource: PUBLIC PROC [name: ROPE, index: INT, chars: INT _ 2, report: ReportProc] = { <> <= 0, then also sets the selection to the given index (for chars characters)>> viewer: ViewerClasses.Viewer _ NIL; inner: PROC = TRUSTED { viewer _ NameToOpenViewer[name, report]; IF viewer # NIL AND index > 0 THEN SetSel[viewer, index, chars, name]; }; err: ROPE _ NIL; err _ BBSafety.Mother[inner]; IF err # NIL THEN Report[report, fatal, " Can't open: ", err] ELSE Report[report, success, " Source opened."]; }; <> ViewerFromSection: PUBLIC PROC [section: AMModel.Section, report: ReportProc] RETURNS [viewer: ViewerClasses.Viewer _ NIL] = TRUSTED { errmsg: ROPE _ NIL; inner: PROC = TRUSTED { sourceVersion: BcdDefs.VersionStamp; source: Source _ AMModel.SectionSource[section]; name: ROPE _ AMModel.SourceFileName[source]; start: INT _ 0; IF name = NIL THEN { errmsg _ "can't get source name"; RETURN}; sourceVersion _ AMModel.SourceVersion[source]; IF sourceVersion = BcdDefs.NullVersion THEN { errmsg _ "can't get source version stamp"; RETURN}; name _ SourceToFullName[source, report]; IF name = NIL THEN { errmsg _ VersionExpectedMessage[sourceVersion]; RETURN}; errmsg _ "can't set source"; viewer _ NameToOpenViewer[name, report]; errmsg _ "can't set selection"; WITH s: source^ SELECT FROM field => start _ s.firstCharIndex; ENDCASE; SetSel[viewer, start, 2, name]; errmsg _ NIL; }; msg: ROPE _ BBSafety.Mother[inner]; IF errmsg = NIL THEN errmsg _ msg; IF errmsg # NIL THEN Report[report, fatal, errmsg]; }; SourceFromSelection: PUBLIC PROC [which: SelectionOption _ primary] RETURNS [name: ROPE _ NIL, index: INT _ -1] = TRUSTED { <<... returns the selected viewer's name and the source index into the viewer. Returns [NIL, -1] if the selection is not in a valid Tioga viewer.>> viewer: ViewerClasses.Viewer _ NIL; start: TiogaOps.Location; [viewer: viewer, start: start] _ TiogaOps.GetSelection[IF which = primary THEN primary ELSE feedback]; IF viewer # NIL AND NOT viewer.destroyed AND NOT viewer.newFile THEN { root: TiogaOps.Ref _ TiogaOps.Root[start.node]; offset: INT _ TiogaOps.LocOffset[loc1: [root, 0], loc2: start, skipCommentNodes: skipComments]; index _ offset; name _ viewer.name; }; }; SectionFromSelection: PUBLIC PROC [world: World _ NIL, which: SelectionOption _ primary] RETURNS [section: Section _ NIL, contexts: LIST OF AMModel.Context _ NIL] = TRUSTED { <<... returns a location for the given selection (primary or feedback); returns NIL if can't do it. If world = NIL then world _ LocalWorld. warn = TRUE => the version of the source file for the given location does not correspond to the viewer (although this is no guarantee that the wrong thing happened).>> name: ROPE _ NIL; index: INT _ -1; [name, index] _ SourceFromSelection[which]; [section, contexts] _ SectionFromSource[world, name, index]; }; SectionFromSource: PUBLIC PROC [world: World _ NIL, name: ROPE _ NIL, index: INT _ 0] RETURNS [section: Section _ NIL, contexts: LIST OF AMModel.Context _ NIL] = TRUSTED { msg: ROPE _ NIL; inner: PROC = TRUSTED { sourceVersion: BcdDefs.VersionStamp _ FileVersion[name]; source: Source _ NIL; context: AMModel.Context _ AMModel.RootContext[IF world = NIL THEN WorldVM.LocalWorld[] ELSE world]; IF sourceVersion = BcdDefs.NullVersion THEN { msg _ "version not accessible"; RETURN; }; IF index <= 0 THEN source _ NEW[AMModel.SourceObj _ [ fileName: shortName, class: prog, versionStamp: sourceVersion, sourceRange: entire[]]] ELSE source _ NEW[AMModel.SourceObj _ [ fileName: shortName, class: statement, versionStamp: sourceVersion, sourceRange: field[index, index]]]; [section, contexts] _ AMModel.SourceSection[source, context]; }; shortName: ROPE _ StripDir[name]; err: ROPE _ NIL; IF Rope.Match["*.mesa", shortName, FALSE] THEN err _ BBSafety.Mother[inner] ELSE err _ Rope.Concat[shortName , " is not a mesa file!"]; IF msg # NIL THEN err _ msg; IF err # NIL THEN ERROR SourceError[lagErr _ err]; }; NameToOpenViewer: PROC [name: ROPE, report: ReportProc] RETURNS [viewer: ViewerClasses.Viewer _ NIL] = { remoteName: BOOL = Rope.SkipTo[name, 0, "/>"] # Rope.Size[name]; visit: ViewerOps.EnumProc = { IF Rope.Match["*.mesa*", v.name, FALSE] AND EqualNames[v.name, name] THEN { viewer _ v; RETURN [FALSE]}; }; ViewerOps.EnumerateViewers[visit]; IF viewer = NIL OR viewer.destroyed THEN TRUSTED { Report[report, comment, " Opening ", name, "... "]; IF remoteName AND ropeCreator # NIL THEN { <> rope: ROPE _ ropeCreator[name]; viewer _ ViewerTools.MakeNewTextViewer[ info: [name: name, column: left, iconic: TRUE], paint: FALSE]; viewer.data _ PutGet.FromRope[rope]; viewer.file _ name; TEditImpl.InitTEditDocument[viewer]; TiogaMenuOps.DefaultMenus[viewer]; } ELSE viewer _ TiogaMenuOps.Open[name]; }; IF viewer.iconic THEN ViewerOps.OpenIcon[viewer]; }; GFToVersionStamps: PUBLIC PROC [gf: TV] RETURNS [source,object: BcdDefs.VersionStamp _ BcdDefs.NullVersion] = TRUSTED { <> ENABLE {ABORTED => GO TO abort; ANY => GO TO oops}; section: AMModel.Section _ AMModel.ContextSection[gf]; src: AMModel.Source _ AMModel.SectionSource[section]; source _ AMModel.SourceVersion[src]; object _ AMModel.SectionVersion[section]; EXITS abort => ERROR ABORTED; oops => {}; }; FileVersion: PUBLIC PROC [name: ROPE] RETURNS [version: BcdDefs.VersionStamp _ BcdDefs.NullVersion] = TRUSTED { <> local: BOOL = Rope.SkipTo[name, 0, "/>"] = name.Size[]; IF local THEN { ENABLE Directory.Error => GO TO none; flat: ROPE _ name.Flatten[]; fc: File.Capability _ Directory.Lookup[LOOPHOLE[flat]]; garbage: STRING _ [64]; version.time _ LOOPHOLE[Directory.GetProps[fc, garbage].createDate]; } ELSE { entry: LSD.Entry _ LSD.Lookup[name]; IF entry = NIL THEN { <> host: ROPE _ "Indigo"; nameSansHost: ROPE _ name; pos: INT _ Rope.SkipTo[name, 0, "[/"]; result: FileLookup.Result; create: System.GreenwichMeanTime; IF pos = 0 AND pos # name.Size[] THEN { pos _ Rope.SkipTo[name, 1, "]/"]; host _ name.Flatten[1, pos-1]; nameSansHost _ name.Flatten[pos+1]; }; [result: result, create: create] _ FileLookup.LookupFile[host, nameSansHost]; IF result = ok THEN version.time _ LOOPHOLE[create]; } ELSE version.time _ LOOPHOLE[entry.create] }; EXITS none => {}; }; SourceToFullName: PUBLIC PROC [source: Source, report: ReportProc] RETURNS [name: ROPE] = TRUSTED { <> fileVersion,sourceVersion: BcdDefs.VersionStamp _ BcdDefs.NullVersion; name _ AMModel.SourceFileName[source]; IF NOT Rope.Match["*.mesa", name, FALSE] THEN name _ name.Concat[".mesa"]; fileVersion _ FileVersion[name]; sourceVersion _ AMModel.SourceVersion[source]; IF fileVersion = sourceVersion THEN RETURN [name]; Report[report, comment, " (version map) "]; name _ VersionMapDefaults.FileNameFromVersion[$Source, sourceVersion]; }; EqualNames: PROC [name1,name2: ROPE] RETURNS [BOOL] = { <<... takes care of differences between CIFS and IFS file name formats. The file names are assumed in valid formats, although if they are not, the worst that can happen is a wrong answer.>> size1: INT _ name1.Size[]; size2: INT _ name2.Size[]; pos1,pos2: INT _ 0; WHILE pos1 < size1 AND pos2 < size2 DO c1: CHAR _ name1.Fetch[pos1]; c2: CHAR _ name2.Fetch[pos2]; pos1 _ pos1 + 1; pos2 _ pos2 + 1; IF c1 = c2 THEN LOOP; SELECT c1 FROM IN ['A..'Z] => c1 _ c1 + ('a-'A); '[, '>, '] => c1 _ '/; '< => { <> pos2 _ pos2 - 1; LOOP}; ENDCASE; SELECT c2 FROM IN ['A..'Z] => c2 _ c2 + ('a-'A); '[, '>, '] => c2 _ '/; '< => { <> pos1 _ pos1 - 1; LOOP}; ENDCASE; IF c1 # c2 THEN RETURN [FALSE]; ENDLOOP; RETURN [pos1 = size1 AND pos2 = size2]; }; StripDir: PROC [name: ROPE] RETURNS [Rope.Text] = { <<... turns a long path name into a short path name, stripping off the version information and the directory, but preserving the extension.>> size,pos: INT _ name.Size[]; start: INT _ 0; WHILE (pos _ pos - 1) >= 0 DO SELECT name.Fetch[pos] FROM '! => EXIT; '. => {pos _ size; EXIT}; ENDCASE; ENDLOOP; start _ pos; WHILE start > 0 DO SELECT name.Fetch[start _ start - 1] FROM '/, '\\, '[, '], '<, '> => { start _ start + 1; EXIT}; ENDCASE; ENDLOOP; RETURN [name.Flatten[start, pos-start]]; }; VersionExpectedMessage: PROC [version: BcdDefs.VersionStamp] RETURNS [msg: ROPE] = TRUSTED { msg _ Rope.Cat [ "source of ", Convert.ValueToRope [[time[time: LOOPHOLE[version.time], useZone: TRUE]]], " expected"]; }; SetSel: PROC [viewer: ViewerClasses.Viewer, pos,len: INT, openName: ROPE _ NIL] = TRUSTED { WHILE viewer.iconic AND NOT viewer.destroyed DO ViewerOps.OpenIcon[viewer]; Process.Pause[Process.SecondsToTicks[1]]; ENDLOOP; IF viewer.destroyed THEN RETURN; IF oldSetSel THEN ViewerTools.SetSelection[viewer, NEW[ViewerTools.SelPosRec _ [pos, len]]] ELSE { x: TiogaOps.Ref _ TiogaOps.ViewerDoc[viewer]; loc1: TiogaOps.Location _ TiogaOps.LocRelative[location: [x, 0], count: pos, skipCommentNodes: skipComments]; loc2: TiogaOps.Location _ TiogaOps.LocRelative[location: loc1, count: len, skipCommentNodes: skipComments]; r: REF _ loc1.node; opaqueLoc: TextNode.Location _ [NARROW[r], loc1.where]; TiogaOps.SetSelection [ viewer: viewer, start: loc1, end: loc2, level: char, caretBefore: TRUE, pendingDelete: FALSE, which: feedback]; IF AutoScrollOK OR OnScreen[viewer, opaqueLoc] THEN TEditScrolling.AutoScroll[viewer: viewer, tryToGlitch: TRUE, id: feedback] ELSE TEditScrolling.ScrollToPosition[viewer, opaqueLoc]; }; }; OnScreen: PROC [viewer: ViewerClasses.Viewer, point: TextNode.Location] RETURNS [BOOL] = { <> IF viewer = NIL OR point.node = NIL THEN RETURN [FALSE]; IF viewer.destroyed OR viewer.iconic THEN RETURN [FALSE]; WITH viewer.data SELECT FROM tdd: TEditDocument.TEditDocumentData => { <> IF TEditTouchup.LockAfterRefresh[tdd, "OnScreen"] THEN { <> ENABLE {UNWIND => TEditTouchup.UnlockAfterRefresh[tdd]}; lines: TEditDocument.LineTable _ tdd.lineTable; found: BOOL _ FALSE; IF lines # NIL AND lines.lastLine >= 4 THEN { first: TextNode.Location _ lines[1].pos; last: TextNode.Location _ lines[lines.lastLine-1].pos; each: TextNode.Ref _ first.node; IF point.node = first.node AND point.where < first.where THEN GO TO quickOut; IF point.node = last.node AND point.where > last.where THEN GO TO quickOut; WHILE each # NIL DO IF each = point.node THEN {found _ TRUE; EXIT}; IF each = last.node THEN EXIT; each _ TextNode.Forward[each].nx; ENDLOOP; EXITS quickOut => {}; }; TEditTouchup.UnlockAfterRefresh[tdd]; RETURN [found]; }; }; ENDCASE; RETURN [FALSE]; }; Report: PROC [report: ReportProc, severity: Severity, r1,r2,r3,r4: ROPE _ NIL] = { msg: ROPE _ Rope.Cat[r1,r2,r3,r4]; lagLagMsg _ lagMsg; lagMsg _ msg; IF report = NIL THEN { IF severity # fatal AND severity # warning THEN RETURN; ERROR ReportError[msg, severity]; }; report[msg, severity]; }; <> FindSource: PUBLIC PROC [short: ROPE, mapList: VersionMap.MapList _ NIL, firstOnly: BOOL _ FALSE] RETURNS [sfl: SourceFileList _ NIL] = TRUSTED { size: INT _ short.Size[]; match: INT _ -1; hasDot: BOOL _ short.Index[0, "."] # size; IF size = 0 THEN RETURN; IF short.Fetch[size-1] = '* THEN { match _ size _ size - 1; short _ short.Substr[0, match]; }; IF match <= 0 AND NOT hasDot THEN { short _ short.Concat[".mesa"]; size _ short.Size[]}; IF mapList = NIL THEN mapList _ VersionMapDefaults.GetMapList[$Source]; FOR list: VersionMap.MapList _ mapList, list.rest UNTIL list = NIL DO map: VersionMap.Map _ list.first; names: ROPE _ map.names; lag: INT _ names.Index[0, "\n"]; FOR i: INT IN [0..map.Length[]) DO anglePos: INT _ lag; versionPos: INT _ 0; dotPos: INT _ 0; next: INT _ names.Index[lag+1, "\n"]; FOR pos: INT DECREASING IN [lag..next) DO c: CHAR _ names.Fetch[pos]; SELECT c FROM '>, '/ => {anglePos _ pos; EXIT}; '. => IF dotPos = 0 THEN dotPos _ pos; '! => versionPos _ pos ENDCASE; ENDLOOP; IF anglePos < dotPos AND dotPos < versionPos THEN { start: INT _ anglePos + 1; target: INT _ IF match > 0 THEN match ELSE versionPos-start; IF target = size AND target = short.Run[0, names, start, FALSE] THEN { <> full: ROPE _ map.FetchName[i]; sfl _ CONS[[map: map, name: full, stamp: map.FetchStamp[i]], sfl]; IF firstOnly THEN RETURN; }; }; lag _ next; ENDLOOP; ENDLOOP; }; FindSourceInDir: PUBLIC PROC [dir, short: ROPE, mapList: VersionMap.MapList _ NIL, firstOnly: BOOL _ FALSE] RETURNS [sfl: SourceFileList _ NIL] = TRUSTED { size: INT _ short.Size[]; dirSize: INT = dir.Size[]; match: INT _ -1; hasDot: BOOL _ short.Index[0, "."] # size; IF size = 0 THEN RETURN; IF short.Fetch[size-1] = '* THEN { match _ size _ size - 1; short _ short.Substr[0, match]; }; IF match <= 0 AND NOT hasDot THEN { short _ short.Concat[".mesa"]; size _ short.Size[]}; IF mapList = NIL THEN mapList _ VersionMapDefaults.GetMapList[$Source]; FOR list: VersionMap.MapList _ mapList, list.rest UNTIL list = NIL DO map: VersionMap.Map _ list.first; names: ROPE _ map.names; lag: INT _ names.Index[0, "\n"]; FOR i: INT IN [0..map.Length[]) DO anglePos: INT _ lag; versionPos: INT _ 0; dotPos: INT _ 0; dirPos: INT _ -1; next: INT _ names.Index[lag+1, "\n"]; FOR pos: INT DECREASING IN [lag..next) DO c: CHAR _ names.Fetch[pos]; SELECT c FROM '>, '/ => {anglePos _ pos; EXIT}; '. => IF dotPos = 0 THEN dotPos _ pos; '! => versionPos _ pos ENDCASE; ENDLOOP; IF anglePos < dotPos AND dotPos < versionPos THEN { start: INT _ anglePos + 1; target: INT _ IF match > 0 THEN match ELSE versionPos-start; IF target = size AND target = short.Run[0, names, start, FALSE] THEN { <> full: ROPE _ map.FetchName[i]; -- get the full name new: SourceFileList _ NIL; IF dir # NIL THEN { <> dirPos: INT = Rope.Index[full, 0, dir, FALSE]; c: CHAR _ '/; IF dirPos = full.Size[] THEN GO TO notHere; IF dirPos > 0 THEN c _ full.Fetch[dirPos-1]; IF c # '/ AND c # '< THEN GO TO notHere; c _ full.Fetch[dirPos+dirSize]; IF c # '/ AND c # '> THEN GO TO notHere; }; sfl _ CONS[[map: map, name: full, stamp: map.FetchStamp[i]], sfl]; IF firstOnly THEN RETURN; EXITS notHere => {}; }; }; lag _ next; ENDLOOP; ENDLOOP; }; OpenViewerFromEntry: PROC [entry: SourceFileEntry] = TRUSTED { full: ROPE _ entry.name; short: ROPE _ ShortName[full]; stamp: TimeStamp.Stamp _ FileVersion[short]; readOnly: BOOL _ TRUE; IF stamp # TimeStamp.Null THEN { <> IF stamp = entry.stamp THEN { <> full _ short; readOnly _ FALSE}; }; OpenSource[full, 0, 0, ClarkKent]; }; ShortName: PROC [r: ROPE] RETURNS [ROPE] = { <> <> first: INT _ 0; last: INT _ r.Size[]; FOR i: INT DECREASING IN [0..last) DO c: CHAR _ r.Fetch[i]; SELECT c FROM '>, '/ => {first _ i+1; EXIT}; '! => last _ i ENDCASE; ENDLOOP; RETURN [r.Substr[first, last - first]] }; OpenCommand: Commander.CommandProc = TRUSTED { <<[cmd: Handle]>> <<=> [in, out, err: STREAM, commandLine,command: ROPE, propertyList: List.AList]>> st: IO.STREAM _ cmd.out; dir: ROPE _ NIL; each: PROC [r: ROPE] = TRUSTED { inStream: IO.STREAM _ IO.RIS[r]; DO sfl: SourceFileList _ NIL; r _ inStream.GetToken[! IO.EndOfStream => EXIT]; IF r.Size[] = 0 THEN RETURN; sfl _ RemoveDuplicates[FindSourceInDir[dir, r, NIL, FALSE]]; IF sfl = NIL THEN { st.PutRope["Sorry, '"]; st.PutRope[r]; st.PutRope["' is not in the current Cedar release.\n"]; RETURN}; IF sfl.rest # NIL THEN { st.PutRope["Sorry, multiple versions for '"]; st.PutRope[r]; st.PutRope["':"]; WHILE sfl # NIL DO st.PutRope["\n "]; st.PutRope[sfl.first.name]; st.PutRope[" "]; st.Put[[time[[sfl.first.stamp.time]]]]; sfl _ sfl.rest; ENDLOOP; st.PutRope["\n"]; RETURN}; OpenViewerFromEntry[sfl.first]; ENDLOOP; }; SELECT 5 FROM Rope.Run[cmd.command, 0, "openC", 0, FALSE] => dir _ "Cedar"; Rope.Run[cmd.command, 0, "openP", 0, FALSE] => dir _ "PreCedar"; ENDCASE; each[cmd.commandLine ! IO.UserAborted => {CONTINUE}]; }; OpenGlobalCommand: Commander.CommandProc = TRUSTED { <<[cmd: Handle]>> <<=> [in, out, err: STREAM, commandLine,command: ROPE, propertyList: List.AList]>> st: IO.STREAM _ cmd.out; dir: ROPE _ NIL; each: PROC [r: ROPE] = TRUSTED { inStream: IO.STREAM _ IO.RIS[r]; gf: TV _ NIL; DO fileName: ROPE _ NIL; r _ inStream.GetToken[! IO.EndOfStream => EXIT]; IF r.Size[] = 0 THEN RETURN; gf _ BBContext.GlobalFrameSearch[context: NIL, frameName: r, case: FALSE].gf; IF gf = NIL THEN { st.PutRope[r]; st.PutRope[" can not be found.\n"]; LOOP; }; fileName _ SourceFromTV[gf, ClarkKent].name; IF fileName # NIL THEN OpenSource[fileName, 0, 0, ClarkKent]; ENDLOOP; }; each[cmd.commandLine ! IO.UserAborted => {CONTINUE}]; }; ClarkKent: AMViewerOps.ReportProc = TRUSTED { <<[msg: ROPE, severity: Severity]>> <<=> [in, out, err: STREAM, commandLine,command: ROPE, propertyList: List.AList]>> WITH List.Assoc[$CommanderHandle, ProcessProps.GetPropList[]] SELECT FROM cmd: Commander.Handle => { st: IO.STREAM _ cmd.out; st.PutRope[msg]; }; ENDCASE; }; RemoveDuplicates: PROC [sfl: SourceFileList] RETURNS [new: SourceFileList _ NIL] = { IF sfl # NIL THEN { entry: SourceFileEntry _ sfl.first; thisStamp: TimeStamp.Stamp _ entry.stamp; each: SourceFileList _ sfl.rest _ RemoveDuplicates[sfl.rest]; lag: SourceFileList _ new _ sfl; WHILE each # NIL DO next: SourceFileList _ each.rest; IF each.first.stamp = thisStamp THEN lag.rest _ next ELSE lag _ each; each _ next; ENDLOOP; }; }; FindCommand: Commander.CommandProc = TRUSTED { <<[cmd: Handle]>> <<=> [in, out, err: STREAM, commandLine,command: ROPE, propertyList: List.AList]>> st: IO.STREAM _ cmd.out; each: PROC [r: ROPE] = TRUSTED { inStream: IO.STREAM _ IO.RIS[r]; DO sfl: SourceFileList _ NIL; r _ inStream.GetToken[! IO.EndOfStream => EXIT]; IF r.Size[] = 0 THEN RETURN; sfl _ RemoveDuplicates[FindSource[r, NIL, FALSE]]; IF sfl = NIL THEN { st.PutRope["Sorry, '"]; st.PutRope[r]; st.PutRope["' is not in the current Cedar release.\n"]; RETURN}; st.PutRope[r]; st.PutRope[" =>\n"]; sfl _ FindSource[r, NIL, FALSE]; IF sfl = NIL THEN { st.PutRope["Sorry, '"]; st.PutRope[r]; st.PutRope["' is not in the current Cedar release.\n"]; RETURN}; WHILE sfl # NIL DO st.PutRope[" "]; st.PutRope[sfl.first.name]; st.PutRope[" "]; st.Put[[time[[sfl.first.stamp.time]]]]; st.PutRope["\n"]; sfl _ sfl.rest; ENDLOOP; ENDLOOP; }; each[cmd.commandLine ! IO.UserAborted => {CONTINUE}]; }; Init: PROC = TRUSTED { Commander.Register [ "openr", OpenCommand, "Opens viewers on Cedar release source files given the short names (.mesa extension is the default). If a short name has multiple long names associated with it, the alternatives are listed, and no viewer is opened for that name. No spelling correction is performed."]; Commander.Register [ "openC", OpenCommand, "Like openr, but forces directory to be Cedar."]; Commander.Register [ "openG", OpenGlobalCommand, "Opens source for named global frame."]; Commander.Register [ "openP", OpenCommand, "Like openr, but forces directory to be PreCedar."]; Commander.Register [ "findr", FindCommand, "Finds Cedar release source file names given the short names (.mesa extension is the default). No spelling correction is performed."]; }; Init[]; END.