<> <> <> DIRECTORY Commander USING [CommandProc, Register], CommandTool USING [Failed, ParseToList], FileNames USING [ResolveRelativePath], FS USING [Error, ErrorDesc, StreamOpen], IO USING [Close, EndOf, EndOfStream, Error, GetBlock, PutBlock, STREAM], Process USING [CheckForAbort], Rope USING [Cat, Concat, Equal, ROPE], Cat, Tee; PipeFittingImpl: CEDAR PROGRAM IMPORTS Commander, CommandTool, FileNames, FS, IO, Process, Rope EXPORTS Cat, Tee = { FromNames: PUBLIC PROCEDURE [in: IO.STREAM, nameList: LIST OF Rope.ROPE, out: IO.STREAM] RETURNS [result: REF _ NIL, msg: Rope.ROPE _ NIL] = { FSErrorMsg: PROC [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"]]; }; streamList: LIST OF IO.STREAM _ NIL; IF nameList # NIL THEN { FOR left: LIST OF Rope.ROPE _ nameList, left.rest WHILE left # NIL DO IF Rope.Equal[left.first, "-"] THEN { streamList _ CONS[in, streamList]; } ELSE { fileStream: IO.STREAM _ FS.StreamOpen[FileNames.ResolveRelativePath[left.first] ! FS.Error => IF error.group # $bug THEN { msg _ Rope.Concat[left.first, FSErrorMsg[error]]; GO TO Die }]; streamList _ CONS[fileStream, streamList]; } ENDLOOP; [result, msg] _ FromStreams[streamList: streamList, out: out]; FOR left: LIST OF IO.STREAM _ streamList, left.rest WHILE left # NIL DO IF left.first # NIL AND left.first # in THEN IO.Close[left.first]; ENDLOOP; } EXITS Die => result _ $Failure; }; FromStreams: PUBLIC PROCEDURE [streamList: LIST OF IO.STREAM, out: IO.STREAM] RETURNS [result: REF _ NIL, msg: Rope.ROPE _ NIL] = { nBytesRead: NAT _ 0; block: REF TEXT _ NEW[TEXT[256]]; FOR left: LIST OF IO.STREAM _ streamList, left.rest WHILE left # NIL DO WHILE NOT IO.EndOf[left.first] DO block.length _ block.maxLength; nBytesRead _ IO.GetBlock[left.first, block, 0, block.maxLength ! IO.EndOfStream => EXIT; IO.Error => EXIT; ]; IO.PutBlock[out, block, 0, nBytesRead ! IO.EndOfStream => EXIT; IO.Error => EXIT;]; Process.CheckForAbort[]; ENDLOOP; ENDLOOP; }; CatCommand: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> <> <> arglist: LIST OF Rope.ROPE; argc: NAT; [arglist, argc] _ CommandTool.ParseToList[cmd, TRUE ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; IF arglist = NIL THEN { msg _ "Usage: Cat [filename | -] ..."; } ELSE { [result, msg] _ FromNames[cmd^.in, arglist, cmd^.out]; }; EXITS Die => result _ $Failure; }; ToNames: PUBLIC PROCEDURE [in: IO.STREAM, out: IO.STREAM, nameList: LIST OF Rope.ROPE] RETURNS [result: REF _ NIL, msg: Rope.ROPE _ NIL] = { FSErrorMsg: PROC [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"]]; }; nBytesRead: NAT _ 0; block: REF TEXT _ NEW[TEXT[256]]; streamList: LIST OF IO.STREAM _ NIL; fileStream: IO.STREAM; FOR left: LIST OF Rope.ROPE _ nameList, left.rest WHILE left # NIL DO fileStream _ FS.StreamOpen[fileName: FileNames.ResolveRelativePath[left.first], accessOptions: $create ! FS.Error => IF error.group # $bug THEN { msg _ Rope.Cat[left.first, FSErrorMsg[error]]; GO TO Die }]; streamList _ CONS[fileStream, streamList]; ENDLOOP; [result, msg] _ ToStreams[in: in, out: out, streamList: streamList]; FOR left: LIST OF IO.STREAM _ streamList, left.rest WHILE left # NIL DO IF left.first # NIL THEN IO.Close[left.first]; ENDLOOP; EXITS Die => result _ $Failure; }; ToStreams: PUBLIC PROCEDURE [in: IO.STREAM, out: IO.STREAM, streamList: LIST OF IO.STREAM] RETURNS [result: REF _ NIL, msg: Rope.ROPE _ NIL] = { nBytesRead: NAT _ 0; block: REF TEXT _ NEW[TEXT[256]]; WHILE NOT IO.EndOf[in] DO block.length _ block.maxLength; nBytesRead _ IO.GetBlock[in, block, 0, block.maxLength ! IO.EndOfStream => EXIT; IO.Error => EXIT; ]; IO.PutBlock[out, block, 0, nBytesRead ! IO.EndOfStream => EXIT; IO.Error => EXIT; ]; FOR left: LIST OF IO.STREAM _ streamList, left.rest WHILE left # NIL DO IO.PutBlock[left.first, block, 0, nBytesRead ! IO.EndOfStream => EXIT; IO.Error => EXIT; ]; ENDLOOP; Process.CheckForAbort[]; ENDLOOP; }; TeeCommand: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> <> <> arglist: LIST OF Rope.ROPE; argc: NAT; [arglist, argc] _ CommandTool.ParseToList[cmd, TRUE ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; IF arglist = NIL THEN { msg _ "Usage: Tee filename ..."; } ELSE { [result, msg] _ ToNames[cmd^.in, cmd^.out, arglist]; }; EXITS Die => result _ $Failure; }; Commander.Register["Cat", CatCommand, "Cat [fileName | -] ... (to cmd^.out)"]; Commander.Register["Tee", TeeCommand, "Tee fileName ... (from cmd^.in)"]; }.