WorkingDirectory.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Last edited by PeterKessler, July 9, 1985 12:00:00 pm PDT
Peter Kessler February 12, 1986 3:12:37 pm PST
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];
This program manipulates the same data structures as the CommandTool that define the current working directory and stack of working directories of a command tool. There are several major changes from the standard CommandTool:
(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: BOOLTRUE] 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: REFNIL, msg: Rope.ROPE ← NIL]
CommandObject = [
in, out, err: STREAM, commandLine, command: Rope.ROPE,
propertyList: List.AList, procData: CommandProcHandle]
wDir: Rope.ROPE;
argv: CommandTool.ArgumentVector ← CommandTool.Parse[cmd, TRUE
! CommandTool.Failed => {msg ← errorMsg; GO TO oops}];
list: LIST OF Rope.ROPENARROW[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] = {
Note that this procedure only knows it is dealing with lists of Rope.ROPE in the declaration of the arguments and return value. Given a type T, this could take lists of T and a T and return a list of T.
IF list = NIL
THEN
RETURN [CONS[element, NIL]]
ELSE
RETURN [CONS[list.first, ListOfRopeAppend1[list.rest, element]]];
};
SELECT argv.argc FROM
1 => {
no arguments means roll the directory stack once.
count ← 1;
};
2 => {
one argument means roll the directory stack argv[1] times
count ← Convert.CardFromRope[argv[1]
! Convert.Error => GO TO rollUsage
];
};
ENDCASE => GO TO rollUsage;
cons the current directory onto the list, roll the list, and extract the first element.
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 ANYNIL, msg: Rope.ROPENIL] = {
wDir ← FileNames.ResolveRelativePath[FileNames.ConvertToSlashFormat[wDir]];
Do a bit of checking!
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: REFNIL, msg: Rope.ROPENIL]
CommandObject = [
in, out, err: STREAM, commandLine, command: ROPE,
propertyList: List.AList, procData: CommandProcHandle]
IF cmd.procData.clientData = $PWD
THEN
RETURN[NIL, TildeCompact[FileNames.CurrentWorkingDirectory[]]]
ELSE
RETURN[NIL, FileNames.CurrentWorkingDirectory[]];
};
PrintDirectoryStack: Commander.CommandProc = {
[cmd: REF CommandObject] RETURNS [result: REFNIL, msg: Rope.ROPENIL]
CommandObject = [
in, out, err: STREAM, commandLine, command: ROPE,
propertyList: List.AList, procData: CommandProcHandle]
IF cmd.procData.clientData = $PDS
THEN msg ← TildeCompact[FileNames.CurrentWorkingDirectory[]]
ELSE msg ← FileNames.CurrentWorkingDirectory[];
FOR list: LIST OF Rope.ROPENARROW[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: REFNIL, msg: Rope.ROPENIL]
CommandObject = [
in, out, err: STREAM, commandLine, command: ROPE,
propertyList: List.AList, procData: CommandProcHandle]
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[];
}.