<> <> <> <> <<>> DIRECTORY Commander USING [CommandProc, Handle, Register, CommandObject], CommandTool USING [ArgumentVector, Parse, Failed, GetProp], Convert USING [CardFromRope, Error], FileNames USING [CurrentWorkingDirectory, ConvertToSlashFormat, ResolveRelativePath, HomeDirectory], FS USING [ExpandName, Error, ErrorDesc], List USING [PutAssoc], ProcessProps USING [GetPropList], ReadEvalPrint USING [Handle], Rope USING [ROPE, Fetch, Length, Concat, Cat, Substr, Index], ViewerClasses USING [Viewer], ViewerOps USING [PaintViewer]; <<>> <> <<(1) The label of the CommandTool viewer is set to the ``TildeCompaction'' of the current working directory (so it prints in the space available) rather than the full name of the command tool viewer. Even heathens who think of /// as their home directory will be pleased to get whatever comes after the ``///'' instead of the leading ``CommandTool: WD = ''. TildeCompaction is defined by the procedure TildeCompact below, which substitutes an abbreviation for the user's home directory. Directories outside the user's home directory are untouched. The default printing of a directories is now the TildeCompation of the directory, for command like ``cd''. For sanity, the ``pwd'' command (and the long forms of the other commands, e.g. ``ChangeWorkingDirectory'') print the long form of the directory.>> <<(2) The stack of working directories can now be printed! The commands ``pds'' and ``PrintDirectoryStack'' print (respectively) the short and long forms of the directories in the stack, with the current working directory on the left.>> <<(3) The stack of working directories can now be rolled. The command ``RollDirectoryStack'' takes a cardinal argument (default 1) and rolls the directory stack that number of times. (No, I didn't implement the two argument form of roll, since I can't remember which argument comes first.)>> WorkingDirectory: CEDAR PROGRAM IMPORTS Commander, CommandTool, Convert, FileNames, FS, List, ProcessProps, Rope, ViewerOps = { HomeDirectoryAbbreviation: Rope.ROPE _ "~/"; GetHomeDirectoryAbbreviation: PROCEDURE [] RETURNS [Rope.ROPE] = { RETURN [HomeDirectoryAbbreviation]; }; SetHomeDirectoryAbbreviation: PROCEDURE [new: Rope.ROPE] RETURNS [old: Rope.ROPE] = { old _ GetHomeDirectoryAbbreviation[]; HomeDirectoryAbbreviation _ new; }; FSErrorMsg: PROCEDURE [error: FS.ErrorDesc] RETURNS [Rope.ROPE] = { SELECT error.group FROM lock => RETURN[" -- locked!\n"]; ENDCASE => IF error.code = $unknownFile THEN RETURN [" -- not found!\n"] ELSE RETURN[Rope.Cat[" -- FS.Error: ", error.explanation, "\n"]]; }; TildeCompact: PROCEDURE [dir: Rope.ROPE] RETURNS [Rope.ROPE] = { RopeIsPrefix: PROCEDURE [prefix: Rope.ROPE, subject: Rope.ROPE, case: BOOL _ TRUE] RETURNS [BOOL] = { RETURN [subject.Substr[len: prefix.Length[]].Index[s2: prefix, case: case] = 0]; }; FileNamesIsAbsolutePath: PROCEDURE [name: Rope.ROPE] RETURNS [BOOL] = { RETURN [ name.Fetch[0] = '/ OR name.Fetch[0] = '[ ]; }; home: Rope.ROPE = FileNames.HomeDirectory[]; dir _ FileNames.ResolveRelativePath[FileNames.ConvertToSlashFormat[dir]]; IF dir.Length[] # 0 AND NOT FileNamesIsAbsolutePath[dir] THEN dir _ Rope.Concat[FileNames.CurrentWorkingDirectory[], dir]; IF RopeIsPrefix[prefix: home, subject: dir] THEN dir _ Rope.Concat[GetHomeDirectoryAbbreviation[], Rope.Substr[dir, home.Length[]]]; RETURN [dir]; }; SetCommandToolHerald: PROCEDURE [cmd: REF Commander.CommandObject, wDir: Rope.ROPE] = { WITH CommandTool.GetProp[cmd, $ReadEvalPrintHandle] SELECT FROM rep: ReadEvalPrint.Handle => { root: ViewerClasses.Viewer _ rep.viewer; IF root # NIL THEN { root.name _ Rope.Concat["CommandTool: WD = ", wDir]; root.label _ TildeCompact[wDir]; ViewerOps.PaintViewer[viewer: root, hint: caption, clearClient: FALSE]; FOR v: ViewerClasses.Viewer _ root.link, v.link WHILE v # NIL AND v # root DO v.name _ root.name; v.name _ root.label; ViewerOps.PaintViewer[viewer: v, hint: caption, clearClient: FALSE]; ENDLOOP; }; }; ENDCASE; }; ChangeWorkingDirectory: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: Rope.ROPE _ NIL]>> <> <> <> wDir: Rope.ROPE; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd, TRUE ! CommandTool.Failed => {msg _ errorMsg; GO TO oops}]; list: LIST OF Rope.ROPE _ NARROW[CommandTool.GetProp[cmd, $WorkingDirectoryStack]]; SELECT cmd.procData.clientData FROM $PushWorkingDirectory, $Push, $PushR => list _ CONS[FileNames.CurrentWorkingDirectory[], list]; ENDCASE; SELECT cmd.procData.clientData FROM $ChangeWorkingDirectory, $CD, $CDR, $PushWorkingDirectory, $Push, $PushR => { root: BOOL _ (cmd.procData.clientData = $CDR OR cmd.procData.clientData = $PushR); SELECT argv.argc FROM 1 => wDir _ IF root THEN "///" ELSE FileNames.HomeDirectory[]; 2 => { wDir _ argv[1]; IF root THEN { wDir _ FileNames.ConvertToSlashFormat[FS.ExpandName[wDir, "///" ! FS.Error => { msg _ FSErrorMsg[error]; GO TO oops }; ].fullFName]; }; }; ENDCASE => { msg _ "Usage: ChangeWorkingDirectory directoryName"; GO TO oops }; }; $PopWorkingDirectory, $Pop => { IF list = NIL THEN wDir _ FileNames.CurrentWorkingDirectory[] ELSE { wDir _ list.first; list _ list.rest; }; }; $RollDirectoryStack, $RollDS => { count: CARDINAL; ListOfRopeAppend1: PROCEDURE [list: LIST OF Rope.ROPE, element: Rope.ROPE] RETURNS [LIST OF Rope.ROPE] = { <> IF list = NIL THEN RETURN [CONS[element, NIL]] ELSE RETURN [CONS[list.first, ListOfRopeAppend1[list.rest, element]]]; }; SELECT argv.argc FROM 1 => { <> count _ 1; }; 2 => { <> count _ Convert.CardFromRope[argv[1] ! Convert.Error => GO TO rollUsage ]; }; ENDCASE => GO TO rollUsage; <> list _ CONS[FileNames.CurrentWorkingDirectory[], list]; FOR rolls: CARDINAL IN [1..count] DO list _ ListOfRopeAppend1[list.rest, list.first]; ENDLOOP; wDir _ list.first; list _ list.rest; EXITS rollUsage => { msg _ "Usage: RollDirectoryStack [count: CARDINAL _ 1]"; GO TO oops; }; }; ENDCASE => GO TO huh; [result, msg] _ SetWD[wDir]; IF result # $Failure THEN { cmd.propertyList _ List.PutAssoc[$WorkingDirectoryStack, list, cmd.propertyList]; SetCommandToolHerald[cmd, msg]; }; SELECT cmd.procData.clientData FROM $CD, $Push, $Pop, $RollDS => msg _ TildeCompact[msg]; ENDCASE; EXITS oops => result _ $Failure; huh => { result _ $Failure; msg _ "bad clientData" }; }; SetWD: PROCEDURE [wDir: Rope.ROPE] RETURNS [result: REF ANY _ NIL, msg: Rope.ROPE _ NIL] = { wDir _ FileNames.ResolveRelativePath[FileNames.ConvertToSlashFormat[wDir]]; <> IF wDir.Length[] = 0 THEN RETURN[$Failure, "empty working directory"]; IF wDir.Fetch[0] # '/ THEN wDir _ Rope.Concat[FileNames.CurrentWorkingDirectory[], wDir]; IF wDir.Fetch[wDir.Length[] - 1] # '/ THEN wDir _ Rope.Concat[wDir, "/"]; IF wDir.Length[] < 3 THEN RETURN[NIL]; msg _ wDir; [] _ List.PutAssoc[key: $WorkingDirectory, val: wDir, aList: ProcessProps.GetPropList[]]; }; PrintWorkingDirectory: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: Rope.ROPE _ NIL]>> <> <> <> <> <> <> <> RETURN[NIL, FileNames.CurrentWorkingDirectory[]]; }; PrintDirectoryStack: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: Rope.ROPE _ NIL]>> <> <> <> IF cmd.procData.clientData = $PDS THEN msg _ TildeCompact[FileNames.CurrentWorkingDirectory[]] ELSE msg _ FileNames.CurrentWorkingDirectory[]; FOR list: LIST OF Rope.ROPE _ NARROW[CommandTool.GetProp[cmd, $WorkingDirectoryStack]], list.rest UNTIL list = NIL DO IF cmd.procData.clientData = $PDS THEN msg _ Rope.Cat[msg, " ", TildeCompact[list.first]] ELSE msg _ Rope.Cat[msg, " ", list.first]; ENDLOOP; RETURN[NIL, msg]; }; WorkingDirectory: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: Rope.ROPE _ NIL]>> <> <> <> SetCommandToolHerald[cmd: cmd, wDir: FileNames.CurrentWorkingDirectory[]]; }; Init: PROCEDURE = { Commander.Register[ key: "PrintWorkingDirectory", proc: PrintWorkingDirectory, doc: "Print working directory", clientData: $PrintWorkingDirectory]; Commander.Register[ key: "PWD", proc: PrintWorkingDirectory, doc: "Print working directory", clientData: $PWD]; Commander.Register[ key: "PrintDirectoryStack", proc: PrintDirectoryStack, doc: "Print directory stack", clientData: $PrintDirectoryStack]; Commander.Register[ key: "PDS", proc: PrintDirectoryStack, doc: "Print working directory", clientData: $PDS]; Commander.Register[ key: "ChangeWorkingDirectory", proc: ChangeWorkingDirectory, doc: "Change working directory", clientData: $ChangeWorkingDirectory]; Commander.Register[ key: "CD", proc: ChangeWorkingDirectory, doc: "Change working directory", clientData: $CD]; Commander.Register[ key: "CDR", proc: ChangeWorkingDirectory, doc: "Change working directory (root relative)", clientData: $CDR]; Commander.Register[ key: "PushWorkingDirectory", proc: ChangeWorkingDirectory, doc: "Push working directory", clientData: $PushWorkingDirectory]; Commander.Register[ key: "Push", proc: ChangeWorkingDirectory, doc: "Push working directory", clientData: $Push]; Commander.Register[ key: "PushR", proc: ChangeWorkingDirectory, doc: "Push working directory (root relative)", clientData: $PushR]; Commander.Register[ key: "PopWorkingDirectory", proc: ChangeWorkingDirectory, doc: "Pop working directory", clientData: $PopWorkingDirectory]; Commander.Register[ key: "Pop", proc: ChangeWorkingDirectory, doc: "Pop working directory", clientData: $Pop]; Commander.Register[ key: "RollDirectoryStack", proc: ChangeWorkingDirectory, doc: "Roll the directory stack n times", clientData: $RollDirectoryStack]; Commander.Register[ key: "RollDS", proc: ChangeWorkingDirectory, doc: "Roll the directory stack n times", clientData: $RollDS]; Commander.Register[ key: "WorkingDirectory", proc: WorkingDirectory, doc: "Initialize the CommandTool herald", clientData: NIL]; }; Init[]; }.