~
BEGIN
PATH: TYPE ~ PFS.PATH;
ux:
PATH ~
PFS.PathFromRope["-ux:/"];
vux:
PATH ~
PFS.PathFromRope["-vux:/"];
dirMode: UnixTypes.Mode ¬ [fmt: dir, owner: [true, true, true], group: [true, true, true], others: [true, false, true]];
ForceLower:
PROC [old:
CHAR]
RETURNS [
CHAR] ~ {
RETURN [IF old IN ['A..'Z] THEN 'a+(old-'A) ELSE old]
};
UnixName:
PROC [rope:
ROPE, checkexist:
BOOL ¬
FALSE, vuxOK:
BOOL ¬
FALSE]
RETURNS [
ROPE] ~ {
path: PATH ~ PFS.AbsoluteName[PFS.PathFromRope[rope]];
nodir: PATH ~ PFSNames.SubName[name: path, start: 0, absolute: TRUE, directory: FALSE];
translated: PATH ~ PFSPrefixMap.Translate[nodir];
s: PATH ~ PFSNames.SubName[name: translated, start: 0, absolute: TRUE, directory: FALSE];
uxP: BOOL ~ PFSNames.IsAPrefix[ux, s].isa;
vuxP: BOOL ~ NOT uxP AND PFSNames.IsAPrefix[vux, s].isa;
IF (uxP
OR (vuxOK
AND vuxP))
AND (
NOT checkexist
OR
PFS.FileLookup[translated,
NIL]#
NIL)
THEN {
name: ROPE ¬ PFS.RopeFromPath[translated, slashes];
colon: INT ~ Rope.Find[name, ":"];
IF colon >= 0 THEN name ¬ Rope.Substr[name, colon+1];
IF vuxP THEN name ¬ Rope.Translate[base: name, translator: ForceLower];
RETURN [name]
}
ELSE {
ERROR CommanderOps.Failed[Rope.Concat[
"Not a valid unix file system working directory: ",
PFS.RopeFromPath[translated]
]]
};
};
DirCommand: Commander.CommandProc ~ {
mk: BOOL ~ cmd.procData.clientData = $mk;
FOR token:
ROPE ¬ CommanderOps.NextArgument[cmd], CommanderOps.NextArgument[cmd]
UNTIL token =
NIL
DO
name: ROPE ¬ UnixName[token];
IF mk
THEN {
IF UnixSysCalls.MkDir[path: UXStrings.Create[name], mode: dirMode] = failure
THEN {
ERROR CommanderOps.Failed[IO.PutFR["MkDir[\"%g\"] failed (errno=%g)", [rope[name]], [cardinal[LOOPHOLE[UnixErrno.GetErrno[]]]]]]
};
}
ELSE {
IF UnixSysCalls.RmDir[path: UXStrings.Create[name]] = failure
THEN {
ERROR CommanderOps.Failed[IO.PutFR["RmDir[\"%g\"] failed (errno=%g)", [rope[name]], [cardinal[LOOPHOLE[UnixErrno.GetErrno[]]]]]]
};
};
ENDLOOP;
};
UnixCommandData: TYPE ~ REF UnixCommandDataRep;
UnixCommandDataRep:
TYPE ~
PACKED
RECORD [
commandPrefix: ROPE,
uxOnly: BOOL ¬ TRUE,
input: ROPE ¬ NIL,
output: ROPE ¬ NIL,
exec: BOOL ¬ FALSE,
tty: BOOL ¬ FALSE,
debug: BOOL ¬ FALSE,
vuxOK: BOOL ¬ FALSE,
wd: ROPE ¬ NIL
];
DoUnixCommand: Commander.CommandProc ~ {
data: UnixCommandData ~ NARROW[cmd.procData.clientData];
wd: ROPE ~ IF data.wd = NIL THEN UnixName[".", TRUE, data.vuxOK] ELSE data.wd;
ans: UnixSpawn.SpawnResult ¬ [0, 0, NIL];
IO.PutF1[cmd.out, "%l", [rope["f"]]];
ans ¬ UnixSpawn.Spawn[
command: Rope.Cat[IF data.exec THEN "" ELSE "stty -echo -onlcr; ", data.commandPrefix, cmd.commandLine],
wd: wd,
stdin: IF data.input # NIL THEN data.input ELSE cmd.in,
stdout: IF data.output # NIL THEN data.output ELSE cmd.out,
stderr: cmd.err,
exec: data.exec,
tty: data.tty,
debug: data.debug
];
IF ans.res # 0
THEN {
IF ans.errno = ORD[UnixErrno.Errno.EABORTED] THEN ERROR ABORTED;
result ¬ $Failure;
msg ¬ IO.PutFR["Failed: (res=%g, errno=%g)", [integer[ans.res]], [integer[ans.errno]]];
CommanderOps.PutProp[cmd, $FailureDetails, ans.details];
};
IO.PutF1[cmd.out, "%l", [rope["F"]]];
};
unixCommandUsage:
ROPE ~ "Usage: UnixCommand [ switch ... ] commanderName [ unixCommandPrefix ]
unixCommandPrefix defaults to be the same as commanderName
switches:
-e exec the command
-t pass -t tty and -p pty arguments to command
-in fname get stdin from named file
-n get stdin from /dev/null (good for commands that don't read)
-out fname redirect stdin to named file
-d print debugging information about spawning
-v allow a vux working directory
-w dirname execute the command in this directory instead of the current one
UnixCommand: Commander.CommandProc ~ {
commandName: ROPE ¬ NIL;
wd: ROPE ¬ NIL;
data: UnixCommandData ¬ NEW[UnixCommandDataRep ¬ []];
i: NAT ¬ 0;
FOR arg:
ROPE ¬ CommanderOps.NextArgument[cmd], CommanderOps.NextArgument[cmd]
UNTIL arg =
NIL
DO
SELECT
TRUE
FROM
Rope.Match["-e", arg, FALSE] => data.exec ¬ TRUE;
Rope.Match["-t", arg, FALSE] => data.tty ¬ TRUE;
Rope.Match["-in", arg, FALSE] => data.input ¬ CommanderOps.NextArgument[cmd];
Rope.Match["-n", arg, FALSE] => data.input ¬ "/dev/null";
Rope.Match["-out", arg, FALSE] => data.output ¬ CommanderOps.NextArgument[cmd];
Rope.Match["-d", arg, FALSE] => data.debug ¬ TRUE;
Rope.Match["-v", arg, FALSE] => data.vuxOK ¬ TRUE;
Rope.Match["-w", arg, FALSE] => wd ¬ CommanderOps.NextArgument[cmd];
Rope.Match["-*", arg, FALSE] => ERROR CommanderOps.Failed[unixCommandUsage];
ENDCASE => {
SELECT i
FROM
0 => commandName ¬ data.commandPrefix ¬ arg;
1 => data.commandPrefix ¬ arg;
ENDCASE => ERROR CommanderOps.Failed[unixCommandUsage];
i ¬ i + 1;
};
ENDLOOP;
IF i=0 THEN ERROR CommanderOps.Failed[unixCommandUsage];
IF wd # NIL THEN data.wd ¬ UnixName[wd, TRUE, data.vuxOK];
Commander.Register[commandName, DoUnixCommand, Rope.Concat["Unix ", data.commandPrefix], data];
};
Commander.Register["MkDir", DirCommand, "Make a Unix directory", $mk];
Commander.Register["RmDir", DirCommand, "Remove a Unix directory", $rm];
Commander.Register["UnixCommand", UnixCommand, Rope.Concat["Declare a Unix command to be accessible through the Commander\n", unixCommandUsage]];