<<>> <> <> <> <<>> 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.