UnixCommandsImpl.mesa
Copyright Ó 1990, 1991 by Xerox Corporation. All rights reserved.
Michael Plass, October 16, 1992 4:50 pm PDT
DIRECTORY UnixSpawn, UnixErrno, Rope, UXStrings, UnixSysCalls, UnixTypes, PFS, PFSNames, PFSPrefixMap, Commander, CommanderOps, IO;
UnixCommandsImpl: CEDAR PROGRAM
IMPORTS UnixSpawn, UnixErrno, Rope, UXStrings, UnixSysCalls, PFS, PFSNames, PFSPrefixMap, Commander, CommanderOps, IO
~ BEGIN
PATH: TYPE ~ PFS.PATH;
ROPE: TYPE ~ Rope.ROPE;
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]];
END.