<> <> <> DIRECTORY Atom USING [GetPName], BasicTime USING [GMT, nullGMT, Period], BcdDefs USING [NullVersion, VersionStamp], Commander USING [CommandProc, Handle, Register], CommandTool USING [ArgumentVector, DoCommand, Failed, GetProp, Parse], Convert USING [RopeFromInt], DFUtilities USING [DateToStream], EditedStream USING [GetEcho, Rubout, SetEcho, SetMode, UnAppendBufferChars], File USING [GetVolumeName, SystemVolume], FileNames USING [GetShortName, ResolveRelativePath], FS USING [Close, EnumerateForNames, Error, ExpandName, FileInfo, GetName, NameProc, Open, OpenFile], IO USING [BreakProc, Close, EndOf, EndOfStream, Error, GetRefAny, GetToken, GetTokenRope, IDProc, PeekChar, PutChar, PutF, PutRope, RIS, SkipWhitespace, STREAM, TokenProc], MBQueue USING [CreateMenuEntry], Menus USING [AppendMenuEntry, ClickProc, CopyEntry, CreateMenu, FindEntry, Menu, MenuEntry, ReplaceMenuEntry], PriorityQueue USING [Create, Insert, Ref, Remove, Size, SortPred], Process USING [CheckForAbort], ReadEvalPrint USING [Handle], Rope USING [Cat, Compare, Concat, Equal, Fetch, Find, Flatten, FromChar, FromRefText, Index, Length, Match, Replace, ROPE, Run, SkipTo, Substr], RopeList USING [Reverse], ThisMachine USING [Name], TiogaMenuOps USING [Open], TiogaOps USING [FindDef, GetSelection, Location, LocOffset, Ref, Root], UserCredentials USING [Get, Login], UserProfile USING [CallWhenProfileChanges, Line, ProfileChangedProc, Token], VersionMap USING [Length, Map, MapList, Range, RangeList, RangeToEntry, ShortNameToRanges], VersionMapDefaults USING [GetMapList], ViewerClasses USING [Viewer], ViewerIO USING [GetBuffer, TypeChars], ViewerOps USING [EnumerateViewers, EnumProc, FetchProp, PaintViewer], ViewerTools USING [GetSelectionContents, SetSelection]; OtherCommandsImpl: CEDAR MONITOR IMPORTS Atom, BasicTime, Commander, CommandTool, Convert, DFUtilities, EditedStream, File, FileNames, FS, IO, MBQueue, Menus, PriorityQueue, Process, Rope, RopeList, ThisMachine, TiogaMenuOps, TiogaOps, UserCredentials, UserProfile, VersionMap, VersionMapDefaults, ViewerIO, ViewerOps, ViewerTools SHARES VersionMap = BEGIN GMT: TYPE = BasicTime.GMT; LORA: TYPE = LIST OF REF ANY; Map: TYPE = VersionMap.Map; MapList: TYPE = VersionMap.MapList; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; VersionStamp: TYPE = BcdDefs.VersionStamp; NullVersion: VersionStamp = BcdDefs.NullVersion; SourceFileList: TYPE = LIST OF SourceFileEntry; SourceFileEntry: TYPE = RECORD [ map: VersionMap.Map, name: ROPE, created: BasicTime.GMT, stamp: VersionStamp]; AliasCellObject: TYPE = RECORD [ args: LIST OF ROPE _ NIL, def: ROPE ]; AliasCell: TYPE = REF AliasCellObject; lastUser: ROPE _ NIL; <> asterisky: BOOLEAN _ FALSE; <> Alias: Commander.CommandProc = TRUSTED { commandLineStream: IO.STREAM = IO.RIS[cmd.commandLine]; name, def: ROPE; token: REF TEXT _ NEW[TEXT[30]]; args: LIST OF ROPE _ NIL; aliasCell: AliasCell; { ENABLE { IO.EndOfStream => GOTO Die; IO.Error => GOTO Die; }; token.length _ 0; token _ commandLineStream.GetToken[IO.TokenProc, token].token; name _ Rope.FromRefText[token]; [] _ commandLineStream.SkipWhitespace[FALSE]; IF NOT commandLineStream.EndOf[] AND commandLineStream.PeekChar[] = '( THEN { FOR l: LIST OF REF ANY _ NARROW[commandLineStream.GetRefAny[]], l.rest UNTIL l = NIL DO WITH l.first SELECT FROM r: ROPE => args _ CONS[r, args]; a: ATOM => args _ CONS[Atom.GetPName[a], args]; ENDCASE => ERROR; ENDLOOP; TRUSTED {args _ RopeList.Reverse[args]; }; }; token.length _ 0; token _ commandLineStream.GetToken[CRBreak, token].token; def _ Rope.FromRefText[token]; aliasCell _ NEW[AliasCellObject _ [args, def]]; Commander.Register[ key: name, proc: AliasImplProc, doc: Rope.Concat["Alias ", cmd.commandLine], clientData: aliasCell]; IO.Close[commandLineStream]; EXITS Die => NULL; }; }; AliasImplProc: Commander.CommandProc = TRUSTED { aliasCell: AliasCell _ NARROW[cmd.procData.clientData]; newCommandLine: ROPE _ aliasCell.def; token: REF TEXT _ NEW[TEXT[40]]; new: ROPE; restOfStream: ROPE; commandLineStream: IO.STREAM = IO.RIS[cmd.commandLine]; synonyms: LIST OF REF SynonymRecord; SynonymRecord: TYPE = RECORD[key, val: ROPE]; FOR l: LIST OF ROPE _ aliasCell.args, l.rest UNTIL l = NIL DO token.length _ 0; token _ commandLineStream.GetToken[IO.IDProc, token ! IO.EndOfStream => CONTINUE; IO.Error => GO TO Nasty ].token; new _ Rope.FromRefText[token]; IF RopeMemb[new, l.rest] THEN { <> dummy: ROPE = UniqueRope[]; synonyms _ CONS[NEW[SynonymRecord _ [new, dummy]], synonyms]; new _ dummy; }; newCommandLine _ RopeSubst[old: l.first, new: new, base: newCommandLine]; ENDLOOP; FOR l: LIST OF REF SynonymRecord _ synonyms, l.rest UNTIL l = NIL DO newCommandLine _ RopeSubst[old: l.first.val, new: l.first.key, base: newCommandLine]; ENDLOOP; token.length _ 0; token _ commandLineStream.GetToken[CRBreak, token ! IO.EndOfStream => CONTINUE; IO.Error => GO TO Nasty ].token; restOfStream _ Rope.FromRefText[token]; newCommandLine _ Rope.Cat[newCommandLine, restOfStream, "\n"]; IO.Close[commandLineStream]; result _ CommandTool.DoCommand[commandLine: newCommandLine, parent: cmd]; EXITS Nasty => {msg _ "IO.Error while parsing arguments"; result _ $Failed}; }; CreateButton: Commander.CommandProc = TRUSTED { <> commandLineStream: IO.STREAM = IO.RIS[cmd.commandLine]; name: ROPE; def: ROPE; token: REF TEXT _ NEW[TEXT[30]]; br: ButtonImplRef _ NEW[ButtonImplObject]; cth: Commander.Handle _ cmd; reph: ReadEvalPrint.Handle _ br.rep _ GetReadEvalPrint[cmd]; { ENABLE { IO.EndOfStream => GOTO Die; IO.Error => GOTO Die; }; token.length _ 0; token _ commandLineStream.GetToken[IO.TokenProc, token].token; name _ Rope.FromRefText[token]; [] _ commandLineStream.SkipWhitespace[FALSE]; <> token.length _ 0; token _ commandLineStream.GetToken[CRBreak, token ! IO.EndOfStream => CONTINUE].token; EXITS Die => NULL; }; IO.Close[commandLineStream]; def _ Rope.FromRefText[token]; SELECT TRUE FROM Rope.Length[def] = 0 => def _ NIL; Rope.Match["*\n", def] => {}; ENDCASE => def _ Rope.Concat[def, "\n"]; br.def _ def; IF reph = NIL THEN RETURN [$Failure, "Can't find $ReadEvalPrintHandle"] ELSE { <> <<(0) no old entry & no new definition => ignore>> <<(1) no old entry & a new definition => create a new entry>> <<(2) old entry & no new definition => remove the old one>> <<(3) old entry & a new definition => replace the old entry>> viewer: ViewerClasses.Viewer _ reph.viewer; IF viewer # NIL THEN { menu: Menus.Menu _ reph.viewer.menu; IF menu # NIL THEN { new: Menus.MenuEntry _ IF def = NIL THEN NIL ELSE MBQueue.CreateMenuEntry[q: reph.menuHitQueue, name: name, proc: ButtonImplButtonProc, clientData: br]; old: Menus.MenuEntry _ Menus.FindEntry[menu, name]; SELECT TRUE FROM old # NIL => Menus.ReplaceMenuEntry[menu, old, new]; new # NIL => Menus.AppendMenuEntry[menu, new, 0]; ENDCASE; ViewerOps.PaintViewer[viewer: viewer, hint: menu, clearClient: FALSE]; }; }; }; }; ClearMenu: Commander.CommandProc = TRUSTED { <> reph: ReadEvalPrint.Handle _ GetReadEvalPrint[cmd]; IF reph # NIL THEN { viewer: ViewerClasses.Viewer _ reph.viewer; IF viewer # NIL THEN { oldMenu: Menus.Menu _ viewer.menu; IF oldMenu # NIL THEN { <> newMenu: Menus.Menu _ Menus.CreateMenu[1]; CopyNamedEntry["STOP!", oldMenu, newMenu]; CopyNamedEntry["Find", oldMenu, newMenu]; CopyNamedEntry["Split", oldMenu, newMenu]; reph.viewer.menu _ newMenu; ViewerOps.PaintViewer[viewer: viewer, hint: menu, clearClient: FALSE]; }; RETURN; }; }; }; ButtonImplButtonProc: Menus.ClickProc = { <<[parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]>> <> <<$CurrentSelection$ => replaced by the current selection up to but not including the first carriage return>> <<$FileNameSelection$ => replaced by the current selection if it appears to be a file name, otherwise replaced by the name of the selected viewer>> <<$ShortFileNameSelection$ => same as $FileNameSelection$ except that version number and directory are omitted>> <<$SelectedViewerName$ => replaced by the name of the selected viewer>> <<$ViewerPosition$ => replaced by the position of the current selection in a viewer>> <<$MouseButton$ => "left", "middle", or "right">> <<$ShiftKey$ => "shift", "noShift">> <<$ControlKey$ "control", "noControl">> br: ButtonImplRef _ NARROW[clientData]; def: ROPE; curSel: ROPE; viewer: ViewerClasses.Viewer _ NIL; start: TiogaOps.Location; viewerName: ROPE _ NIL; fileName: ROPE _ NIL; shortFileName: ROPE _ NIL; index: INT _ -1; pos: INT; controlRope: ROPE _ IF control THEN "control" ELSE "noControl"; shiftRope: ROPE _ IF shift THEN "shift" ELSE "noShift"; buttonRope: ROPE; SELECT mouseButton FROM red => buttonRope _ "left"; yellow => buttonRope _ "middle"; blue => buttonRope _ "right"; ENDCASE => ERROR; IF br = NIL THEN RETURN; def _ br.def; [viewer: viewer, start: start] _ TiogaOps.GetSelection[primary]; curSel _ ViewerTools.GetSelectionContents[]; 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: TRUE]; index _ offset; viewerName _ viewer.file; IF viewerName = NIL THEN viewerName _ viewer.name; fileName _ viewer.file; fileName _ Rope.Substr[fileName, 0, Rope.SkipTo[fileName, 0, "!"]]; }; <> pos _ curSel.Find["\n"]; IF pos # -1 THEN curSel _ Rope.Substr[base: curSel, start: 0, len: pos]; <> fileName _ IF (Rope.SkipTo[s: curSel, pos: 0, skip: " \t"] = curSel.Length[]) AND (curSel.Length[] > 1) THEN curSel ELSE fileName; shortFileName _ FileNames.GetShortName[path: fileName, stripOffVersionNumber: TRUE]; IF Rope.SkipTo[def, 0, "$"] < Rope.Length[def] THEN { <> def _ RopeSubst[old: "$CurrentSelection$", new: curSel, base: def, case: TRUE]; def _ RopeSubst[old: "$FileNameSelection$", new: fileName, base: def, case: TRUE]; def _ RopeSubst[old: "$ShortFileNameSelection$", new: shortFileName, base: def, case: TRUE]; def _ RopeSubst[old: "$SelectedViewerName$", new: viewerName, base: def, case: TRUE]; def _ RopeSubst[old: "$ViewerPosition$", new: Convert.RopeFromInt[index, 10, FALSE], base: def, case: TRUE]; def _ RopeSubst[old: "$MouseButton$", new: buttonRope, base: def, case: TRUE]; def _ RopeSubst[old: "$ControlKey$", new: controlRope, base: def, case: TRUE]; def _ RopeSubst[old: "$ShiftKey$", new: shiftRope, base: def, case: TRUE]; }; { bufferContents: REF TEXT _ ViewerIO.GetBuffer[br.rep.in]; IF bufferContents # NIL AND bufferContents.length > 0 AND bufferContents[bufferContents.length - 1] # '\n THEN { FOR n: NAT DECREASING IN [0..bufferContents.length) DO IF bufferContents[n] = '\n THEN { EditedStream.UnAppendBufferChars[ stream: br.rep.in, nChars: bufferContents.length - n - 1]; EXIT; } REPEAT FINISHED => EditedStream.UnAppendBufferChars[stream: br.rep.in, nChars: LAST[NAT]]; ENDLOOP; }; }; <> IF viewer = br.rep.viewer THEN ViewerTools.SetSelection[viewer: viewer, selection: NIL]; ViewerIO.TypeChars[editedViewerStream: br.rep.in, chars: def]; }; ButtonImplRef: TYPE = REF ButtonImplObject; ButtonImplObject: TYPE = RECORD [ rep: ReadEvalPrint.Handle _ NIL, def: ROPE _ NIL ]; WhenProfileChanges: UserProfile.ProfileChangedProc = { IF reason = rollBack THEN { <> user: ROPE _ UserCredentials.Get[].name; IF NOT Rope.Equal[user, lastUser, FALSE] THEN { <> firstTime: BOOL _ TRUE; eachTool: ViewerOps.EnumProc = { <<[v: ViewerClasses.Viewer] RETURNS [BOOL _ TRUE]>> IF Rope.Match["CommandTool: *", v.name] AND v.class.flavor = $Typescript AND NOT v.destroyed THEN { IF firstTime THEN { firstTime _ FALSE; StuffCommand[v, "///Commands/NoteNewUser\n"]; }; StuffCommand[v, "///Commands/NotePerLogin\n"]; }; }; ViewerOps.EnumerateViewers[eachTool]; }; }; }; NotePerLogin: Commander.CommandProc = { line: ROPE _ UserProfile.Line["CommandTool.PerLogin", NIL]; IF line # NIL THEN [] _ CommandTool.DoCommand[line, cmd]; }; NoteNewUser: Commander.CommandProc = { line: ROPE _ UserProfile.Line["CommandTool.NewUser", NIL]; lastUser _ UserCredentials.Get[].name; IF line # NIL THEN [] _ CommandTool.DoCommand[line, cmd]; }; NotePerCommandTool: Commander.CommandProc = { line: ROPE _ UserProfile.Line["CommandTool.PerCommandTool", NIL]; IF line = NIL THEN line _ UserProfile.Line["CommandTool.EachCommandToolCommands", NIL]; <> IF line # NIL THEN [] _ CommandTool.DoCommand[line, cmd]; }; NoteBootCommands: Commander.CommandProc = { line: ROPE _ UserProfile.Line["CommandTool.BootCommands", NIL]; IF line # NIL THEN [] _ CommandTool.DoCommand[line, cmd]; }; Login: Commander.CommandProc = { oldEcho: IO.STREAM; StartInteraction: PROC RETURNS [in, out: IO.STREAM] = { IF asterisky THEN EditedStream.SetMode[stream: cmd.in, echoAsterisks: TRUE] ELSE { oldEcho _ EditedStream.GetEcho[cmd.in]; EditedStream.SetEcho[cmd.in, NIL]; }; in _ cmd.in; out _ cmd.out; }; EndInteraction: PROC [in, out: IO.STREAM] = { IF asterisky THEN EditedStream.SetMode[stream: cmd.in, echoAsterisks: FALSE] ELSE EditedStream.SetEcho[cmd.in, oldEcho]; }; UserCredentials.Login[ startInteraction: StartInteraction, endInteraction: EndInteraction, options: [alwaysInteract: TRUE] ! EditedStream.Rubout => {result _ $Failed; msg _ " -- Aborted.\n"; CONTINUE} ]; }; <> <<>> ListCommandProc: Commander.CommandProc = { <<[cmd: Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> EachFile: FS.NameProc = { <<[fullFName: ROPE] RETURNS [continue: BOOL]>> attachedTo: ROPE _ NIL; created: GMT _ BasicTime.nullGMT; bytes: INT _ 0; keep: CARDINAL _ 0; item: FileItem _ NIL; needInfo: BOOL _ unattachedOnly; continue _ TRUE; Process.CheckForAbort[]; IF exactLevelMatch AND anglesRequired # CountAngles[fullFName] THEN RETURN; SELECT TRUE FROM directoriesOnly => {}; briefPrint AND NOT complexSorting AND NOT attachedPrint => {}; ENDCASE => needInfo _ TRUE; IF needInfo THEN { pos: INT _ Rope.SkipTo[fullFName, 1, "]"]+1; len: INT _ Rope.Length[fullFName]; [fullFName, attachedTo, keep, bytes, created] _ FS.FileInfo[name: fullFName, remoteCheck: remoteCheck]; }; IF unattachedOnly AND attachedTo # NIL THEN RETURN; IF bytes < 0 AND needInfo AND NOT remoteCheck THEN <> [fullFName, attachedTo, keep, bytes, created] _ FS.FileInfo[name: fullFName, remoteCheck: TRUE]; IF gNamePrint THEN fullFName _ FNameToGName[name: fullFName]; item _ NEW[FileItemRep _ [fullFName, attachedTo, created, bytes, keep]]; filesSeen _ filesSeen + 1; IF bytes > 0 THEN bytesTotal _ bytesTotal + bytes; SELECT TRUE FROM directoriesOnly => { oldLag: ROPE _ lagPrefix; SetLagPrefix[fullFName]; IF oldLag # lagPrefix THEN { item.fullFName _ lagPrefix; PriorityQueue.Insert[pq, item]; }; }; complexSorting => PriorityQueue.Insert[pq, item]; ENDCASE => PrintOneFile[item]; }; PrintOneFile: PROC [item: FileItem] = { <> oldLag: ROPE _ lagPrefix; printName: ROPE _ item.fullFName; Process.CheckForAbort[]; IF NOT fullPrint AND NOT directoriesOnly THEN { <> SetLagPrefix[printName]; IF oldLag # lagPrefix THEN { IO.PutRope[out, lagPrefix]; IO.PutChar[out, IF oneLine THEN ' ELSE '\n]; }; printName _ Rope.Substr[printName, lagPrefixLen]; IF NOT oneLine THEN IO.PutRope[out, " "]; }; SELECT TRUE FROM directoriesOnly => IO.PutRope[out, printName]; briefPrint => { IO.PutRope[out, printName]; IF attachedPrint AND item.attachedTo # NIL THEN { IF NOT oneLine THEN IO.PutRope[out, "\n "]; IO.PutF[out, " => %g", [rope[item.attachedTo]] ]; }; }; ENDCASE => { form: ROPE = IF narrowPrint THEN "%g\n%12g " ELSE "%-24g %6g "; IO.PutF[out, form, [rope[printName]], [integer[item.bytes]] ]; IF item.created = BasicTime.nullGMT THEN IO.PutRope[out, "??"] ELSE DFUtilities.DateToStream[out, [explicit, item.created] ]; IF attachedPrint AND item.attachedTo # NIL THEN { IF NOT oneLine THEN IO.PutRope[out, "\n "]; IO.PutF[out, " => %g", [rope[item.attachedTo]] ]; }; IF printKeep THEN IO.PutF[out, ", keep: %g", [cardinal[item.keep]] ]; }; IO.PutChar[out, IF oneLine THEN ' ELSE '\n]; }; TryPattern: PROC [pattern: ROPE] = { ENABLE FS.Error => IF error.group # $bug THEN { IO.PutRope[out, " -- "]; IO.PutRope[out, error.explanation]; GO TO err}; patternsTried _ patternsTried + 1; pattern _ FileNames.ResolveRelativePath[pattern]; pattern _ FS.ExpandName[pattern].fullFName; IF highestPrint AND NOT Rope.Match["*!*", pattern] THEN pattern _ Rope.Concat[pattern, "!h"]; IF exactLevelMatch THEN anglesRequired _ CountAngles[pattern]; complexSorting _ sortData # NIL; SELECT TRUE FROM directoriesOnly => pq _ PriorityQueue.Create[SortPred, NIL]; complexSorting => pq _ PriorityQueue.Create[SortPred, sortData]; ENDCASE => pq _ NIL; SetLagPrefix[NIL]; FS.EnumerateForNames[pattern, EachFile]; SetLagPrefix[NIL]; IF pq # NIL THEN { lagName: ROPE _ NIL; THROUGH [0..PriorityQueue.Size[pq]) DO item: FileItem = NARROW[PriorityQueue.Remove[pq]]; IF directoriesOnly THEN { IF Rope.Equal[item.fullFName, lagName] THEN LOOP; lagName _ item.fullFName; }; PrintOneFile[item]; ENDLOOP; }; EXITS err => {IO.PutRope[out, "\n"]; RETURN}; }; SetLagPrefix: PROC [fileName: ROPE] = { <<... sets the lagging prefix from the given file name, which is presumed to be syntactically correct, although it need not be complete. A file name without a prefix will set the lagPrefix to NIL. We also enforce lagPrefixLen = Rope.Length[lagPrefix] at exit, assuming that no other routine sets lagPrefix.>> IF lagPrefix # NIL THEN { <> IF Rope.Run[lagPrefix, 0, fileName, 0, FALSE] = lagPrefixLen THEN { <> pos: INT = Rope.SkipTo[fileName, lagPrefixLen, ">/]"]; IF pos = Rope.Length[fileName] THEN RETURN; }; }; <> FOR i: INT DECREASING IN [0..Rope.Length[fileName]) DO SELECT Rope.Fetch[fileName, i] FROM '>, '/, '] => {lagPrefix _ Rope.Flatten[fileName, 0, lagPrefixLen _ i+1]; RETURN}; ENDCASE; ENDLOOP; <> lagPrefix _ NIL; lagPrefixLen _ 0; }; AddSortOption: PROC [option: ATOM] = { new: LORA _ LIST[option]; IF sortDataTail = NIL THEN sortData _ new ELSE sortDataTail.rest _ new; sortDataTail _ new; }; RemSortOption: PROC [option: ATOM] = { lag: LORA _ sortData; IF lag = NIL THEN RETURN; IF lag.first = option THEN { sortData _ sortData.rest; RETURN}; FOR each: LORA _ lag.rest, each.rest WHILE each # NIL DO IF each.first = option THEN {lag.rest _ each.rest; EXIT}; lag _ each; ENDLOOP; }; FNameToGName: PROC [name: ROPE] RETURNS [ROPE] = { IF Rope.Match["[]*", name] THEN { IF gHost = NIL THEN gHost _ Rope.Cat["[", ThisMachine.Name[], "]"]; IF gDir = NIL THEN gDir _ Rope.Cat[gHost, "<", File.GetVolumeName[File.SystemVolume[]], ">"]; IF Rope.Match["[]<>*", name] THEN RETURN [Rope.Replace[base: name, start: 0, len: 4, with: gDir]] ELSE RETURN [Rope.Replace[base: name, start: 0, len: 2, with: gHost]]; }; RETURN [name]; }; gHost: ROPE _ NIL; gDir: ROPE _ NIL; out: STREAM = cmd.out; lagPrefix: ROPE _ NIL; lagPrefixLen: INT _ 0; patternsTried: INT _ 0; filesSeen: INT _ 0; bytesTotal: INT _ 0; directoriesOnly: BOOL _ FALSE; complexSorting: BOOL _ FALSE; printKeep: BOOL _ FALSE; narrowPrint: BOOL _ FALSE; attachedPrint: BOOL _ FALSE; briefPrint: BOOL _ FALSE; remoteCheck: BOOL _ FALSE; fullPrint: BOOL _ FALSE; highestPrint: BOOL _ cmd.procData.clientData = $Highest; oneLine: BOOL _ FALSE; unattachedOnly: BOOL _ FALSE; exactLevelMatch: BOOL _ FALSE; gNamePrint: BOOL _ FALSE; anglesRequired: INT _ 0; sortData: LORA _ NIL; sortDataTail: LORA _ NIL; pq: PriorityQueue.Ref _ NIL; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO failed}]; ProcessSwitches: PROC [arg: ROPE] = { sense: BOOL _ TRUE; direction: {up, down} _ down; FOR index: INT IN [0..Rope.Length[arg]) DO SELECT Rope.Fetch[arg, index] FROM '~ => {sense _ NOT sense; LOOP}; '> => direction _ down; '< => direction _ up; 'a, 'A => attachedPrint _ sense; 'b, 'B => briefPrint _ sense; 'd, 'D => { RemSortOption[$MoreRecent]; RemSortOption[$LessRecent]; IF sense THEN AddSortOption[IF direction = up THEN $LessRecent ELSE $MoreRecent]; }; 'f, 'F => fullPrint _ sense; 'g, 'G => gNamePrint _ sense; 'h, 'H => highestPrint _ sense; 'k, 'K => printKeep _ sense; 'n, 'N => narrowPrint _ sense; 'o, 'O => oneLine _ sense; 'p, 'P => directoriesOnly _ sense; 'r, 'R => remoteCheck _ sense; 's, 'S => { RemSortOption[$Larger]; RemSortOption[$Smaller]; IF sense THEN AddSortOption[IF direction = up THEN $Smaller ELSE $Larger]; }; 'u, 'U => unattachedOnly _ sense; 'x, 'X => exactLevelMatch _ sense; ENDCASE; sense _ TRUE; ENDLOOP; }; ProcessSwitches[UserProfile.Token["ListCommand.DefaultSwitches"]]; FOR i: NAT IN [1..argv.argc) DO arg: ROPE = argv[i]; IF Rope.Length[arg] = 0 THEN LOOP; IF Rope.Fetch[arg, 0] = '- THEN { <> ProcessSwitches[arg]; LOOP; }; <> TryPattern[arg]; ENDLOOP; IF patternsTried = 0 THEN TryPattern["*"]; IF oneLine THEN IO.PutChar[out, '\n]; IF filesSeen > 0 THEN { IO.PutF[out, "-- %g files", [integer[filesSeen]] ]; IF bytesTotal > 0 THEN IO.PutF[out, ", %g total bytes", [integer[bytesTotal]] ]; IO.PutChar[out, '\n]; }; EXITS failed => {result _ $Failure}; }; CountAngles: PROC [pattern: ROPE] RETURNS [count: INT _ 0] = { len: INT = Rope.Length[pattern]; pos: INT _ Rope.SkipTo[pattern, 0, ">"]; WHILE pos < len DO pos _ Rope.SkipTo[pattern, pos+1, ">"]; count _ count + 1; ENDLOOP; }; FileItem: TYPE = REF FileItemRep; FileItemRep: TYPE = RECORD [ fullFName, attachedTo: ROPE, created: GMT, bytes: INT, keep: CARDINAL]; SortPred: PriorityQueue.SortPred = { <<[x: Item, y: Item, data: REF] RETURNS [BOOL]>> xx: FileItem = NARROW[x]; yy: FileItem = NARROW[y]; options: LORA = NARROW[data]; FOR each: LORA _ options, each.rest WHILE each # NIL DO SELECT each.first FROM $MoreRecent => { IF xx.created = yy.created THEN LOOP; RETURN [BasicTime.Period[xx.created, yy.created] < 0]; }; $LessRecent => { IF xx.created = yy.created THEN LOOP; RETURN [BasicTime.Period[xx.created, yy.created] > 0]; }; $Larger => { IF xx.bytes = yy.bytes THEN LOOP; RETURN [xx.bytes > yy.bytes]; }; $Smaller => { IF xx.bytes = yy.bytes THEN LOOP; RETURN [xx.bytes < yy.bytes]; }; ENDCASE; ENDLOOP; RETURN [Rope.Compare[xx.fullFName, yy.fullFName, FALSE] = less]; }; <> OpenCommand: Commander.CommandProc = { <<[cmd: Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> st: IO.STREAM _ cmd.out; inStream: IO.STREAM _ IO.RIS[cmd.commandLine]; eachFile: PROC = { MyOpen: PROC [def: ROPE _ NIL] = { file: FS.OpenFile _ FS.Open[name: name, wantedCreatedTime: date ! FS.Error => { IF error.code = $unknownFile THEN { IO.PutF[st, "Not found: %g\n", [rope[name]] ]; } ELSE { IO.PutF[st, "%g\n", [rope[error.explanation]] ]; }; result _ $Failure; GO TO exit; }; ]; viewer: ViewerClasses.Viewer _ NIL; name _ FS.GetName[file].fullFName; FS.Close[file]; viewer _ TiogaMenuOps.Open[name]; IF viewer = NIL THEN { IO.PutF[st, "Not found: %g\n", [rope[r]] ]; result _ $Failure; } ELSE { IO.PutF[st, "Opened: %g\n", [rope[r]] ]; result _ NIL; IF Rope.Length[def] # 0 THEN [] _ TiogaOps.FindDef[viewer, def, forwards, feedback]; }; EXITS exit => {}; }; sfl: SourceFileList _ NIL; name: ROPE _ NIL; date: GMT; r: ROPE _ IO.GetTokenRope[inStream, IO.IDProc].token; IF Rope.Length[r] = 0 THEN RETURN; sfl _ FindSource[r]; SELECT TRUE FROM sfl = NIL => { <> rBase, rExt: ROPE; [rBase, rExt] _ SplitName[r]; IF rExt = NIL THEN { sfl _ FindSource[Rope.Concat[rBase, ".*"]]; [name, date] _ TryExtensions[sfl]; } ELSE { sfl _ FindSource[Rope.Concat[rBase, ".mesa"]]; [name, date] _ FindMostRecent[sfl]; }; IF name # NIL THEN MyOpen[rExt] ELSE { IO.PutF[st, "Sorry, '%g' is not in the current Cedar release.\n", [rope[r]]]; result _ $Failure; RETURN; }; }; ENDCASE => { <> [name, date] _ FindMostRecent[sfl]; MyOpen[]; }; }; DO eachFile[ ! FS.Error => {msg _ error.explanation; result _ $Failed; EXIT}; IO.EndOfStream => EXIT; ]; ENDLOOP; }; FindCommand: Commander.CommandProc = { <<[cmd: Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> st: IO.STREAM _ cmd.out; inStream: IO.STREAM _ IO.RIS[cmd.commandLine]; useBin: BOOL _ cmd.procData.clientData = $Bin; eachFile: PROC = { sfl: SourceFileList _ NIL; r: ROPE _ IO.GetTokenRope[inStream, IO.IDProc].token; IF Rope.Length[r] = 0 THEN RETURN; IF NOT Rope.Match["*.*", r] THEN r _ Rope.Concat[r, ".*"]; sfl _ FindSource[r, FALSE, IF useBin THEN $Symbols ELSE $Source]; IF sfl = NIL THEN { IO.PutF[st, "Sorry, '%g' is not in the current Cedar release.\n", [rope[r]] ]; RETURN; }; IO.PutF[st, "%g =>\n", [rope[r]] ]; WHILE sfl # NIL DO IO.PutF[st, " %g\n %g\n", [rope[sfl.first.name]], [time[sfl.first.created]] ]; sfl _ sfl.rest; ENDLOOP; }; DO eachFile[ ! FS.Error => {msg _ error.explanation; result _ $Failed; EXIT}; IO.EndOfStream => EXIT; ]; ENDLOOP; }; <> FindSource: PROC [short: ROPE, removeDuplDates: BOOL _ TRUE, which: ATOM _ NIL] RETURNS [SourceFileList _ NIL] = TRUSTED { size: INT _ Rope.Length[short]; starPos: INT _ short.Index[0, "*"]; match: BOOL _ starPos # size; hasDot: BOOL _ short.Index[0, "."] # size; rangeList: VersionMap.RangeList _ NIL; head: SourceFileList _ NIL; tail: SourceFileList _ NIL; shortShort: ROPE _ Rope.Flatten[short, 0, starPos]; shortShortLen: INT _ Rope.Length[shortShort]; mapList: MapList _ NIL; IF size = 0 THEN RETURN; IF which = NIL THEN which _ $Source; IF mapList = NIL THEN mapList _ VersionMapDefaults.GetMapList[which]; rangeList _ VersionMap.ShortNameToRanges[mapList, short]; WHILE rangeList # NIL DO range: VersionMap.Range _ rangeList.first; map: Map = range.map; rangeList _ rangeList.rest; Process.CheckForAbort[]; IF match THEN { entries: CARDINAL = VersionMap.Length[map]; IF range.first >= entries THEN LOOP; range.len _ entries - range.first; WHILE range.len # 0 DO fullName: ROPE; stamp: VersionStamp; thisShort: ROPE; created: BasicTime.GMT; [fullName, stamp, created, range] _ VersionMap.RangeToEntry[range]; thisShort _ ShortName[fullName]; IF Rope.Run[shortShort, 0, thisShort, 0, FALSE] # shortShortLen THEN EXIT; IF Rope.Match[short, thisShort, FALSE] THEN { new: SourceFileList _ LIST[[map: range.map, name: fullName, created: created, stamp: stamp]]; IF tail = NIL THEN head _ new ELSE tail.rest _ new; tail _ new; }; ENDLOOP; } ELSE { WHILE range.len # 0 DO new: SourceFileList; fullName: ROPE; stamp: VersionStamp; created: BasicTime.GMT; [fullName, stamp, created, range] _ VersionMap.RangeToEntry[range]; new _ LIST[[map: range.map, name: fullName, created: created, stamp: stamp]]; IF tail = NIL THEN head _ new ELSE tail.rest _ new; tail _ new; ENDLOOP; }; ENDLOOP; RemoveDuplicates[head, removeDuplDates]; RETURN [head]; }; ShortName: PROC [r: ROPE] RETURNS [ROPE] = { <> <> first: INT _ 0; last: INT _ Rope.Length[r]; 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]] }; SplitName: PROC [name: ROPE] RETURNS [prefix: ROPE _ NIL, ext: ROPE _ NIL] = { dot: INT _ 0; len: INT _ Rope.Length[name]; pos: INT _ len; WHILE (pos _ pos - 1) > 0 DO SELECT Rope.Fetch[name, pos] FROM '! => {name _ Rope.Flatten[name, 0, pos]; len _ pos}; '. => {prefix _ Rope.Flatten[name, 0, pos]; ext _ Rope.Flatten[name, pos+1]; RETURN}; '], '/, '> => EXIT; ENDCASE; ENDLOOP; RETURN [name, NIL]; }; FindMostRecent: PROC [sfl: SourceFileList] RETURNS [name: ROPE _ NIL, date: GMT] = { IF sfl # NIL THEN { date _ sfl.first.created; name _ sfl.first.name; FOR each: SourceFileList _ sfl.rest, each.rest WHILE each # NIL DO eachDate: GMT _ each.first.created; period: INT _ BasicTime.Period[from: date, to: eachDate]; IF period >= 0 THEN { <> eachName: ROPE _ each.first.name; IF period < 0 OR Rope.Compare[eachName, name, FALSE] = greater THEN { <> date _ eachDate; name _ eachName; }; }; ENDLOOP; }; }; RemoveDuplicates: PROC [sfl: SourceFileList, removeDuplDates: BOOL] = { <> WHILE sfl # NIL DO entry: SourceFileEntry _ sfl.first; thisStamp: VersionStamp _ entry.stamp; each: SourceFileList _ sfl.rest; lag: SourceFileList _ sfl; WHILE each # NIL DO next: SourceFileList _ each.rest; SELECT TRUE FROM Rope.Equal[each.first.name, entry.name, FALSE] => lag.rest _ next; removeDuplDates AND each.first.stamp = thisStamp => lag.rest _ next; ENDCASE => lag _ each; each _ next; ENDLOOP; sfl _ sfl.rest; ENDLOOP; }; TryExtensions: PROC [sfl: SourceFileList] RETURNS [ROPE _ NIL, GMT _ BasicTime.nullGMT] = { line: ROPE _ UserProfile.Line["SourceFileExtensions", "mesa tioga df cm config"]; in: STREAM _ IO.RIS[line, NIL]; DO token: ROPE _ IO.GetTokenRope[in, IO.IDProc ! IO.EndOfStream => EXIT].token; currentList: SourceFileList _ NIL; IF Rope.Length[token] = 0 THEN EXIT; FOR each: SourceFileList _ sfl, each.rest WHILE each # NIL DO name: ROPE _ each.first.name; base, ext: ROPE _ NIL; [base, ext] _ SplitName[name]; IF Rope.Equal[ext, token, FALSE] THEN { name _ FS.FileInfo[ name: Rope.Cat[base, ".", token], wantedCreatedTime: each.first.created, remoteCheck: FALSE ! FS.Error => SELECT error.code FROM $unknownFile, $unknownCreatedTime => LOOP; ENDCASE ].fullFName; currentList _ CONS[each.first, currentList]; }; ENDLOOP; IF currentList # NIL THEN RETURN FindMostRecent[currentList]; ENDLOOP; }; <> <<>> RopeSubst: PROC [old, new, base: ROPE, case: BOOL _ FALSE, allOccurrences: BOOL _ TRUE] RETURNS [ROPE] = { <> <> lenOld: INT = old.Length[]; lenNew: INT = new.Length[]; i: INT _ 0; WHILE (i _ Rope.Find[s1: base, s2: old, case: case, pos1: i]) # -1 DO base _ Rope.Replace[base: base, start: i, len: lenOld, with: new]; IF ~allOccurrences THEN EXIT; i _ i + lenNew; ENDLOOP; RETURN[base]; }; RopeMemb: PROC [rope: ROPE, lst: LIST OF ROPE] RETURNS[BOOL] = { FOR l: LIST OF ROPE _ lst, l.rest UNTIL l = NIL DO IF Rope.Equal[rope, l.first, FALSE] THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE] }; gennum: LONG CARDINAL _ 10000; UniqueRope: ENTRY PROC [c: CHAR _ 'A] RETURNS [ROPE] = { ENABLE UNWIND => NULL; gennum _ gennum + 1; RETURN[Rope.Concat[Rope.FromChar[c], Convert.RopeFromInt[gennum, 10, FALSE]]]; }; <<>> CRBreak: IO.BreakProc = { IF char = '\n THEN RETURN[break]; RETURN[other]; }; CopyNamedEntry: PROC [name: ROPE, oldMenu,newMenu: Menus.Menu] = { old: Menus.MenuEntry _ Menus.FindEntry[oldMenu, name]; IF old # NIL THEN Menus.AppendMenuEntry[newMenu, Menus.CopyEntry[old], 0]; }; GetReadEvalPrint: PROC [cmd: Commander.Handle] RETURNS [ReadEvalPrint.Handle] = { <> DO WITH CommandTool.GetProp[cmd, $ReadEvalPrintHandle] SELECT FROM reph: ReadEvalPrint.Handle => IF reph # NIL AND reph.viewer # NIL THEN RETURN [reph]; ENDCASE; WITH CommandTool.GetProp[cmd, $ParentCommander] SELECT FROM next: Commander.Handle => {cmd _ next; LOOP;}; ENDCASE; RETURN [NIL]; ENDLOOP; }; StuffCommand: PROC [viewer: ViewerClasses.Viewer, commands: ROPE] = { WITH ViewerOps.FetchProp[viewer, $ReadEvalPrint] SELECT FROM reph: ReadEvalPrint.Handle => { data: ButtonImplRef _ NEW[ButtonImplObject _ [rep: reph, def: commands]]; ButtonImplButtonProc[viewer, data]; }; ENDCASE; }; <> Init: PROC = { listDoc: ROPE = "(List | LS) {switch | pattern}*\nswitch = -a: attached print, -b: brief format, -d: date sort, -f: full name print, -g: GName print, -k: keep print, -n: narrow print, -o: one line, -p: prefixes only, -r: remote check, -s: size sort, -u: un backed up, -x: exact level match"; Commander.Register[key: "///Commands/Alias", proc: Alias, doc: "Create an alias for a command", interpreted: FALSE]; Commander.Register[key: "///Commands/CreateButton", proc: CreateButton, doc: "Create a CommandTool herald button", interpreted: FALSE]; Commander.Register[key: "///Commands/ClearMenu", proc: ClearMenu, doc: "Reset the CommandTool menu"]; Commander.Register[key: "///Commands/Login", proc: Login, doc: "Login a new user"]; Commander.Register[key: "///Commands/NoteBootCommands", proc: NoteBootCommands, doc: "Perform commands for the first commander after a full boot"]; Commander.Register[key: "///Commands/NoteNewUser", proc: NoteNewUser, doc: "Perform commands for a new user"]; Commander.Register[key: "///Commands/NotePerCommandTool", proc: NotePerCommandTool, doc: "Perform commands for a login (or rollback)"]; Commander.Register[key: "///Commands/NotePerLogin", proc: NotePerLogin, doc: "Perform commands for a login (or rollback)"]; Commander.Register[key: "///Commands/RemoveButton", proc: CreateButton, doc: "Remove a CommandTool herald button"]; <> Commander.Register["///Commands/LS", ListCommandProc, listDoc]; Commander.Register["///Commands/LSH", ListCommandProc, listDoc, $Highest]; Commander.Register["///Commands/List", ListCommandProc, listDoc]; Commander.Register["///Commands/ListH", ListCommandProc, listDoc, $Highest]; <> <<>> Commander.Register [ "///Commands/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."]; Commander.Register [ "///Commands/FindR", FindCommand, "Finds Cedar release source file names given the short names (.mesa extension is the default)"]; Commander.Register [ "///Commands/FindRBin", FindCommand, "Finds Cedar release binary file names given the short names.", $Bin]; UserProfile.CallWhenProfileChanges[WhenProfileChanges]; }; Init[]; END.