TapeToolOpsImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Last edited by Tim Diebert: March 18, 1986 8:57:28 am PST
Last Edited by: McCreight, February 26, 1985 3:36:16 pm PST
Tim Diebert : June 7, 1985 9:19:26 am PDT
DIRECTORY
Atom USING [PropList, GetProp, GetPropFromList, MakeAtom],
BasicTime USING [Period, GMT, Now],
Containers USING [Container],
Convert USING [Error, CardFromRope, IntFromRope],
FS USING [ ComponentPositions, Error, EnumerateForNames, ExpandName,
NameProc, StreamOpen,
StreamOptions, defaultStreamOptions],
IO USING [ card, Close, GetBlock, PutBlock, GetTokenRope,
IDProc, EndOf, EndOfStream, Error,
int, PutF, PutFR, PutRope, RIS, rope,
RopeFromROS, ROS, STREAM],
Labels USING [Label, Set],
List USING [Reverse],
Process USING [Pause, SecondsToTicks],
Rope USING [Concat, Equal, Find, IsEmpty, Index, Replace, ROPE, Substr],
STP USING [Close, Create, CreateRemoteStream, Error, Handle, IsOpen, Login, Open],
TapeToolInternal USING [Action, TapeTool, TapeToolOp, WaitForResponse, GetToolParameters],
TapeOps USING [BackSpaceFile, CloseDrive, DriveNumber, ForwardSpaceFile,
GetStatus, OpenDrive, Rewind,
TapeHandle, TapeOpsError, TapeStatus, TapeStatusIndex, Unload],
TapeStreams USING [ConversionRecord, ConversionList, Error, StreamOpen, TapeRecordProc],
ViewerClasses USING [Viewer],
ViewerTools USING [SetContents],
UserCredentials USING [Get];
TapeToolOpsImpl: CEDAR PROGRAM
IMPORTS Atom, BasicTime, Convert, FS, IO, Labels, List, Process, Rope,
STP, TapeOps, TapeToolInternal, TapeStreams,
ViewerTools, UserCredentials
EXPORTS TapeToolInternal = BEGIN
OPEN Tool: TapeToolInternal;
ROPE: TYPE ~ Rope.ROPE;
STREAM: TYPE ~ IO.STREAM;
ExecuteOp: PUBLIC PROC [tool: Tool.TapeTool, op: Tool.Action] = BEGIN
ENABLE {ABORTED => tool.typeScript.PutRope["Aborted\n"]; -- ABORTED is terminated by the queue
UNWIND => {ViewerTools.SetContents[tool.question, " "];
IF tool.AbortNow THEN tool.AbortNow ← FALSE;}};
tool.params ← Tool.GetToolParameters[];
IF tool.AbortNow THEN BEGIN
tool.AbortNow ← FALSE;
MyError[tool, NIL];
END;
SELECT op.op FROM
Open => Open[tool, op];
Close => Close[tool];
Rewind => Rewind[tool];
Unload => Unload[tool];
Status => Status[tool];
Read => ReadFile[tool, op];
Write => WriteFile[tool, op];
FSF => FwdSpFile[tool];
BSF => BkSpFile[tool];
ENDCASE => ERROR;
ViewerTools.SetContents[tool.question, " "];
END;
myAborted: ERROR = CODE;
-- Internal Procs
MyError: PROC [tool: Tool.TapeTool, message: ROPE] = BEGIN
ENABLE UNWIND => NULL;
IF NOT message.IsEmpty[] THEN tool.typeScript.PutRope[message];
ERROR ABORTED;
END;
Open: PROC [tool: Tool.TapeTool, op: Tool.Action] = BEGIN
OPEN TapeOps;
ENABLE UNWIND => NULL;
tape: TapeHandle ← tool.tapeHandle;
dr: INT;
tool.serverName ←
IF NOT op.serverName.IsEmpty[] THEN op.serverName ELSE tool.params.defaultServer;
dr ← Convert.IntFromRope[op.driveNumberRope
! Convert.Error => MyError[tool, "Invalid drive number.\n"]];
tool.typeScript.PutF["Opening to %g ... ", IO.rope[tool.serverName]];
IF dr NOT IN TapeOps.DriveNumber THEN MyError[tool, "Invalid drive number.\n"];
tool.driveNumber ← dr;
IF tool.open THEN { tool.typeScript.PutRope["Connection already open.\n"]; RETURN; };
tape ← OpenDrive[tool.serverName, tool.driveNumber, tool.density
! TapeOps.TapeOpsError => {tool.open ← FALSE; MyError[tool, ec.Concat[" .. "]];}];
IF tape # NIL
THEN tool.typeScript.PutF["%g\n", IO.rope[tape.versionString]]
ELSE { tool.typeScript.PutRope["Did not open.\n"]; RETURN; };
tool.tapeHandle ← tape;
tool.open ← TRUE;
PrintStatus[tool];
tool.outer.guardDestroy ← TRUE;
END;
Close: PROC [tool: Tool.TapeTool] = BEGIN
OPEN TapeOps;
tape: TapeHandle;
IF tool = NIL THEN RETURN;
tape ← tool.tapeHandle;
IF NOT OpenCheck[tool] THEN RETURN;
tool.fileNumber ← 0;
tool.typeScript.PutF["Closing connection with %g\n", IO.rope[tool.serverName]];
tape.CloseDrive[]; -- Won't raise any errors.
tool.open ← FALSE;
PrintStatus[tool];
tool.tapeHandle ← NIL;
tool.outer.guardDestroy ← FALSE;
END;
ReadFile: PROC [tool: Tool.TapeTool, op: Tool.Action] = BEGIN
ENABLE UNWIND => NULL;
s, out: STREAMNIL;
wDir: ROPE ← op.opWDir;
list: LIST OF ROPENIL;
cp: FS.ComponentPositions;
stp: STP.Handle ← STP.Create[]; curServer: ROPENIL;
starInUse: BOOLFALSE;
fileRope, fullFName, starRope: ROPENIL;
starFileNumber: CARDINAL ← 0;
conversions: TapeStreams.ConversionList ← NIL;
CloseAll: PROC [message: ROPE] = BEGIN
IF s # NIL THEN {s.Close[ ! TapeStreams.Error => CONTINUE]; s ← NIL };
IF out # NIL THEN
{out.Close[ ! STP.Error => CONTINUE; FS.Error => CONTINUE]; out ← NIL };
IF stp # NIL AND stp.IsOpen[] THEN stp.Close[ ! STP.Error => CONTINUE];
PrintStatus[tool];
MyError[tool, (IF message.IsEmpty[] THEN NIL ELSE message.Concat[" .. "])];
END;
ReadFile: PROC [fileName: ROPE] RETURNS [BOOL] = BEGIN
ENABLE {UNWIND => NULL;
TapeStreams.Error => CloseAll[error.explanation];
FS.Error => CloseAll[error.explanation];
STP.Error => CloseAll[error];
IO.Error => SELECT ec FROM
NotImplementedForThisStream => CloseAll["Improper conversion chosen!"];
ENDCASE => CloseAll["Unexpected IO error!"];
myAborted => CloseAll[NIL];};
tool.inUseRecordCount ← 0;
tool.inUseByteCount ← 0;
s ← TapeStreams.StreamOpen[tape: tool.tapeHandle,
padding: tool.fillBlock, conversions: conversions,
tapeRecordProc: TapeRecordProc, clientData: tool];
tool.fileNumber ← tool.fileNumber + 1;
IF s.EndOf[] THEN BEGIN
tool.atEndOfTape ← TRUE;
tool.typeScript.PutRope["End Of Tape Reached\n"];
RETURN [FALSE];
END;
IF cp.server.length = 0
THEN out ← FS.StreamOpen[fileName: fullFName, accessOptions: $create]
ELSE { out ← CreateSTPStoreStream[tool, stp, fullFName, curServer];
curServer ← Rope.Substr[fullFName, cp.server.start, cp.server.length]};
TransferTheFile[tool: tool, from: s, to: out];
out.Close[]; s.Close[];
RETURN [TRUE];
END;
list ← GetRopeList[op.opFiles];
IF list = NIL THEN {tool.typeScript.PutRope["Please set file name field.\n"]; RETURN;};
conversions ← GetConversions[tool, op ! BadConversions => GOTO Punt ];
IF NOT OpenCheck[tool] THEN RETURN;
DO
yes: BOOL;
fileRope ← list.first;
IF EOTCheck[tool] THEN RETURN;
IF fileRope.Find["*"] >= 0 THEN {starInUse ← TRUE; starRope ← fileRope;};
IF starInUse THEN BEGIN
starFileNumber ← starFileNumber + 1;
fileRope ←
fileRope.Replace[fileRope.Index[0, "*"], 1, IO.PutFR["%g", IO.card[starFileNumber]]];
END;
[fullFName: fullFName, cp: cp] ← FS.ExpandName[fileRope, wDir
! FS.Error => {
IF stp # NIL AND stp.IsOpen THEN stp.Close[ ! STP.Error => CONTINUE];
MyError[tool, error.explanation.Concat[" .. "]]}];
IF (NOT tool.params.localDisk) AND (cp.server.length = 0) THEN
{ tool.typeScript.PutRope["Stores to local files illegal.\n"];
IF stp # NIL AND stp.IsOpen THEN stp.Close[ ! STP.Error => CONTINUE];
RETURN; };
ViewerTools.SetContents[tool.question, IO.PutFR["%g <= tape", IO.rope[fullFName]]];
yes ← Tool.WaitForResponse[tool];
IF yes
THEN BEGIN
start: BasicTime.GMT ← BasicTime.Now[];
sec: INT ← 0;
tool.typeScript.PutF["Starting read into %g ... ", IO.rope[fullFName]];
IF NOT ReadFile[fullFName] THEN EXIT;
ViewerTools.SetContents[tool.question, " "];
sec ← BasicTime.Period[start, BasicTime.Now[]];
tool.typeScript.PutF[" Done.\n%g bytes read, in %g records. (%g bits/sec)\n",
IO.int[tool.inUseByteCount], IO.int[tool.inUseRecordCount], IO.int[(tool.inUseByteCount*8)/(sec+1)]];
END
ELSE RETURN;
IF NOT starInUse THEN {list ← list.rest; IF list = NIL THEN EXIT; };
ENDLOOP;
IF stp # NIL AND stp.IsOpen THEN stp.Close[ ! STP.Error => CONTINUE];
EXITS
Punt => NULL;
END;
TapeRecordProc: TapeStreams.TapeRecordProc = BEGIN
--PROC [clientData: REF ANYNIL]
ENABLE UNWIND => NULL;
tool: Tool.TapeTool ← NARROW[clientData];
tool.inUseRecordCount ← tool.inUseRecordCount + 1;
PrintStatus[tool];
Labels.Set[tool.recordCountLabel, IO.PutFR["File number: %5g Records %g: %8g",
IO.card[tool.fileNumber], IO.rope[tool.inUseOp], IO.int[tool.inUseRecordCount]]];
IF tool.tapeHandle.status[errSE] THEN IO.PutRope[tool.typeScript, " SE "];
IF tool.AbortNow THEN { tool.AbortNow ← FALSE; ERROR myAborted};
END;
TransferTheFile: PROCEDURE [tool: Tool.TapeTool, from, to: IO.STREAM] = BEGIN
ENABLE UNWIND => NULL;
buffer: REF TEXT = NEW[TEXT[1024]];
buffer.length ← buffer.maxLength;
DO
nBytes: NAT = from.GetBlock[buffer, 0, buffer.maxLength];
to.PutBlock[buffer, 0, nBytes];
tool.inUseByteCount ← tool.inUseByteCount + nBytes;
IF from.EndOf[] THEN EXIT;
ENDLOOP;
END;
WriteFile: PROC [tool: Tool.TapeTool, op: Tool.Action] = BEGIN
ENABLE UNWIND => NULL;
so: FS.StreamOptions ← FS.defaultStreamOptions;
in, s: STREAM;
list: LIST OF ROPENIL;
fullFNameList, tail: LIST OF ROPENIL;
cp: FS.ComponentPositions;
blocking: INT;
openNew, fileFound: BOOLFALSE;
headFileList, tailFileList: REF FileList ← NIL;
FileList: TYPE ~ RECORD [ next: REF FileList ← NIL, file: ROPENIL ];
stp: STP.Handle ← STP.Create[];
fileRope, wDir, server, currentServer: ROPENIL;
conversions: TapeStreams.ConversionList ← NIL;
CloseAll: PROC [message: ROPE] = BEGIN
IF s # NIL THEN {s.Close[ ! TapeStreams.Error => CONTINUE]; s ← NIL };
IF in # NIL THEN
{in.Close[ ! STP.Error => CONTINUE; FS.Error => CONTINUE]; in ← NIL };
IF stp # NIL AND stp.IsOpen[] THEN stp.Close[ ! STP.Error => CONTINUE];
PrintStatus[tool];
MyError[tool, (IF message.IsEmpty[] THEN NIL ELSE message.Concat["\n"])];
END;
IF NOT OpenCheck[tool] THEN RETURN;
IF EOTCheck[tool] THEN RETURN;
list ← GetRopeList[op.opFiles];
IF list = NIL THEN {tool.typeScript.PutRope["Please set file name field.\n"]; RETURN;};
IF tool.params.localDisk THEN so[tiogaRead] ← NOT op.tiogaRead;
wDir ← op.opWDir;
blocking ← Convert.IntFromRope[op.opBlockingRope
! Convert.Error => MyError[tool, "Invalid blocking factor.\n"]];
IF blocking < 10 THEN BEGIN
tool.typeScript.PutF["Blocking factor less than 10: %g\n", IO.rope[op.opBlockingRope]];
RETURN;
END;
tool.typeScript.PutRope["Enumerating file list ... "];
DO
error: BOOLFALSE;
nameProc: FS.NameProc = BEGIN
NameProc: TYPE = PROC [fullFName: ROPE] RETURNS [continue: BOOLEAN];
IF tailFileList = NIL
THEN headFileList ← tailFileList ← NEW[FileList]
ELSE {tailFileList.next ← NEW[FileList]; tailFileList ← tailFileList.next;};
[cp: cp] ← FS.ExpandName[fullFName];
IF (NOT tool.params.localDisk) AND (cp.server.length = 0) THEN BEGIN
tool.typeScript.PutRope["Fetch from local files illegal.\n"];
error ← TRUE;
RETURN [FALSE];
END;
tailFileList.file ← fullFName;
fileFound ← TRUE;
RETURN [TRUE];
END;
fileRope ← list.first;
IF fileRope.Find["!"] < 0 THEN fileRope ← fileRope.Concat["!H"];
FS.EnumerateForNames[fileRope, nameProc, wDir
! FS.Error => MyError[tool, error.explanation.Concat["\n"]];];
IF error THEN {tool.typeScript.PutRope[" Done\n"]; RETURN;};
list ← list.rest;
IF list = NIL THEN {tool.typeScript.PutRope[" Done\n"]; EXIT;};
ENDLOOP;
IF NOT fileFound THEN {tool.typeScript.PutRope["Did not find file(s)!\n"]; RETURN;};
IF (tailFileList ← headFileList) = NIL THEN
{tool.typeScript.PutRope["Did not find file(s)!\n"]; RETURN;};
conversions ← GetConversions[tool, op ! BadConversions => GOTO Punt ];
DO
yes: BOOL;
start: BasicTime.GMT;
sec: INT ← 0;
fullFName: ROPE ← tailFileList.file;
tool.responseNeeded ← TRUE;
[cp: cp] ← FS.ExpandName[fullFName];
ViewerTools.SetContents[tool.question,
IO.PutFR["tape <-- %g", IO.rope[fullFName]]];
yes ← Tool.WaitForResponse[tool];
IF yes THEN BEGIN
ENABLE {UNWIND => NULL;
TapeStreams.Error => CloseAll[error.explanation];
FS.Error => CloseAll[error.explanation];
STP.Error => CloseAll[error];
IO.Error => SELECT ec FROM
NotImplementedForThisStream => CloseAll["Improper conversion chosen!"];
ENDCASE => CloseAll["Unexpected IO error!"];
myAborted => CloseAll[NIL];};
server ← fullFName.Substr[cp.server.start, cp.server.length];
start ← BasicTime.Now[];
IF NOT server.Equal[currentServer, FALSE]
THEN BEGIN
currentServer ← server;
IF stp # NIL THEN IF stp.IsOpen[] THEN stp.Close[];
openNew ← TRUE;
END
ELSE openNew ← FALSE;
tool.inUseRecordCount ← 0;
tool.inUseByteCount ← 0;
in ← IF cp.server.length = 0
THEN FS.StreamOpen[fileName: fullFName, streamOptions: so]
ELSE CreateSTPRetrieveStream[tool: tool, stp: stp, fullFName: fullFName, openNew: openNew];
s ← TapeStreams.StreamOpen[tape: tool.tapeHandle, access: write,
padding: tool.fillBlock,
blocking: blocking,
conversions: conversions,
tapeRecordProc: TapeRecordProc, clientData: tool];
tool.fileNumber ← tool.fileNumber + 1;
tool.typeScript.PutF["Starting write from %g ... ", IO.rope[fullFName]];
TransferTheFile[tool: tool, from: in, to: s];
s.Close[];
in.Close[];
sec ← BasicTime.Period[start, BasicTime.Now[]];
tool.typeScript.PutF[" Done.\n%g bytes written (not including padding), in %g records. (%g bits/sec)\n",
IO.int[tool.inUseByteCount], IO.int[tool.inUseRecordCount - 1], IO.int[(tool.inUseByteCount*8)/(sec+1)]];
ViewerTools.SetContents[tool.question, " "];
END;
IF tailFileList.next = NIL THEN EXIT;
tailFileList ← tailFileList.next;
ENDLOOP;
IF stp # NIL THEN IF stp.IsOpen[] THEN stp.Close[];
EXITS
Punt => NULL;
END;
Status: PROC [tool: Tool.TapeTool] = BEGIN
OPEN TapeOps;
ENABLE { UNWIND => NULL; TapeOpsError => MyError[tool, ec]; };
status: TapeStatus;
IF NOT OpenCheck[tool] THEN RETURN;
status ← tool.tapeHandle.GetStatus[];
PrintStatus[tool];
RETURN;
END;
Rewind: PROC [tool: Tool.TapeTool] = BEGIN
OPEN TapeOps;
ENABLE { UNWIND => NULL; TapeOpsError => MyError[tool, ec]; };
status: TapeStatus;
IF NOT OpenCheck[tool] THEN RETURN;
tool.atEndOfTape ← FALSE;
status ← tool.tapeHandle.Rewind[FALSE];
tool.fileNumber ← 0;
DO
Process.Pause[Process.SecondsToTicks[1]];
status ← tool.tapeHandle.GetStatus[];
PrintStatus[tool];
IF tool.tapeHandle.status[RDY] OR tool.tapeHandle.status[BOT] THEN EXIT;
IF tool.AbortNow THEN BEGIN
tool.AbortNow ← FALSE;
MyError[tool, NIL];
END;
ENDLOOP;
END;
Unload: PROC [tool: Tool.TapeTool] = BEGIN
OPEN TapeOps;
ENABLE { UNWIND => NULL; TapeOpsError => MyError[tool, ec]; };
tape: TapeHandle ← tool.tapeHandle;
IF NOT OpenCheck[tool] THEN RETURN;
tool.atEndOfTape ← FALSE;
[] ← tool.tapeHandle.Unload[];
PrintStatus[tool];
tool.typeScript.PutF["Closing connection with %g\n", IO.rope[tool.serverName]];
tape.CloseDrive[];
Labels.Set[tool.statusLabel, "Connection Closed"];
tool.fileNumber ← 0;
Labels.Set[tool.recordCountLabel, IO.PutFR["File number: %5g", IO.card[tool.fileNumber]]];
tool.tapeHandle ← NIL;
tool.open ← FALSE;
tool.outer.guardDestroy ← FALSE;
END;
FwdSpFile: PROC [tool: Tool.TapeTool] = BEGIN
OPEN TapeOps;
ENABLE { UNWIND => NULL; TapeOpsError => MyError[tool, ec]; };
tape: TapeHandle ← tool.tapeHandle;
IF NOT OpenCheck[tool] THEN RETURN;
IF EOTCheck[tool] THEN RETURN;
[] ← tool.tapeHandle.ForwardSpaceFile[];
tool.fileNumber ← tool.fileNumber + 1;
PrintStatus[tool];
END;
BkSpFile: PROC [tool: Tool.TapeTool] = BEGIN
OPEN TapeOps;
ENABLE { UNWIND => NULL; TapeOpsError => MyError[tool, ec]; };
tape: TapeHandle ← tool.tapeHandle;
IF NOT OpenCheck[tool] THEN RETURN;
tool.atEndOfTape ← FALSE;
[] ← tool.tapeHandle.BackSpaceFile[];
IF tool.fileNumber > 0 THEN tool.fileNumber ← tool.fileNumber - 1;
PrintStatus[tool];
END;
PrintStatus: PROC [tool: Tool.TapeTool] = BEGIN
OPEN TapeOps;
dont: BOOLFALSE;
status: TapeStatus ← tool.tapeHandle.status;
stat: WORDLOOPHOLE[status];
myS: STREAMIO.ROS[];
IF NOT tool.open THEN {Labels.Set[tool.statusLabel, "Connection Closed"]; RETURN;};
IF stat = 0 THEN myS.PutRope["Tape not ready"];
IF status[EOT] AND status[BOT] THEN
{ myS.PutRope["Tape not mounted"]; dont ← TRUE; };
IF NOT dont THEN FOR reason: TapeStatusIndex IN TapeStatusIndex DO
IF status[reason] THEN myS.PutRope[tapeExplanation[reason]] ELSE myS.PutRope[" "];
ENDLOOP;
Labels.Set[tool.statusLabel, myS.RopeFromROS[]];
Labels.Set[tool.recordCountLabel, IO.PutFR["File number: %5g", IO.card[tool.fileNumber]]];
RETURN;
END;
CreateSTPRetrieveStream: PROC
[tool: Tool.TapeTool, stp: STP.Handle, fullFName: ROPE, openNew: BOOL] RETURNS [outStream: STREAM] = BEGIN
ENABLE UNWIND => NULL;
user, password, server, file: ROPENIL;
cp: FS.ComponentPositions;
[fullFName: fullFName, cp: cp] ← FS.ExpandName[fullFName];
server ← fullFName.Substr[cp.server.start, cp.server.length];
file ← fullFName.Substr[cp.dir.start-1];
[user, password] ← UserCredentials.Get[];
IF stp = NIL THEN stp ← STP.Create[];
stp.Login[user, password];
IF openNew THEN [ ] ←
stp.Open[server
! STP.Error => IF code = connectionRejected OR tool.AbortNow THEN RETRY ELSE REJECT];
outStream ← stp.CreateRemoteStream[file: file, access: read];
RETURN [outStream];
END;
CreateSTPStoreStream: PROC
[tool: Tool.TapeTool, stp: STP.Handle, fullFName: ROPE, curServer: ROPENIL]
RETURNS [inStream: STREAM] = BEGIN
ENABLE UNWIND => NULL;
user, password, server, file: ROPENIL;
cp: FS.ComponentPositions;
[fullFName: fullFName, cp: cp] ← FS.ExpandName[fullFName];
server ← fullFName.Substr[cp.server.start, cp.server.length];
file ← fullFName.Substr[cp.dir.start-1];
[user, password] ← UserCredentials.Get[];
stp.Login[user, password];
IF NOT Rope.Equal[server, curServer, FALSE] THEN BEGIN
IF STP.IsOpen[stp] THEN STP.Close[stp];
[ ] ← stp.Open[server ! STP.Error => IF code = connectionRejected AND NOT tool.AbortNow
THEN {tool.typeScript.PutRope["\nConnection rejected; retrying .. "];
Process.Pause[Process.SecondsToTicks[1]]; RETRY}
ELSE REJECT];
END;
inStream ← stp.CreateRemoteStream[file: file, access: write,
fileType: binary, creation: BasicTime.Now[]];
END;
OpenCheck: PROC [tool: Tool.TapeTool] RETURNS [b: BOOLTRUE] = BEGIN
IF NOT tool.open
THEN { tool.typeScript.PutRope["Please open connection\n"];RETURN [FALSE]; };
END;
EOTCheck: PROC [tool: Tool.TapeTool] RETURNS [b: BOOLFALSE] = BEGIN
IF tool.atEndOfTape THEN {tool.typeScript.PutRope["At end of tape.\n"]; RETURN [TRUE];};
END;
GetRopeList: PROC [r: ROPE] RETURNS [list: LIST OF ROPENIL] = BEGIN
s: STREAMIO.RIS[r];
tail: LIST OF ROPENIL;
DO
r: ROPE ← s.GetTokenRope[IO.IDProc ! IO.EndOfStream => EXIT].token;
rL: LIST OF ROPE;
rL ← CONS[r, NIL];
IF list = NIL THEN list ← rL ELSE tail.rest ← rL;
tail ← rL;
ENDLOOP;
END;
GetConversions: PROC [ tool: Tool.TapeTool, op: Tool.Action ] RETURNS [ conversions: TapeStreams.ConversionList ← NIL ] =
BEGIN
cNames: ROPE = op.opConversions;
cns: IO.STREAM = IO.RIS[cNames];
DO
c: ATOM = Atom.MakeAtom[cns.GetTokenRope[breakProc: IO.IDProc ! IO.EndOfStream => EXIT].token];
pl: Atom.PropList = NARROW[Atom.GetProp[$TapeTool, $Conversions]];
cr: REF TapeStreams.ConversionRecord ← NARROW[Atom.GetPropFromList[pl, c]];
IF cr = NIL THEN GOTO Punt;
IF cr.name = $ToEbcdic THEN BEGIN
lc: LONG CARDINAL = Convert.CardFromRope[op.oplreclRope
! Convert.Error => GOTO Bad];
blocking: LONG CARDINAL = Convert.CardFromRope[op.opBlockingRope
! Convert.Error => GOTO Bad1];
IF lc # 0 AND blocking MOD lc # 0 THEN GOTO Bad;
cr.clientData ← NEW [CARDINAL ← 0];
NARROW[cr.clientData, REF CARDINAL]^ ← lc;
END;
conversions ← CONS[cr, conversions];
REPEAT
Bad => { tool.typeScript.PutRope["Bad lrecl value!\n"]; ERROR BadConversions};
Bad1 => { tool.typeScript.PutRope["Bad blocking value!\n"]; ERROR BadConversions};
Punt => { tool.typeScript.PutRope["Improper format conversions!\n"];
ERROR BadConversions};
ENDLOOP;
TRUSTED { conversions ← LOOPHOLE[List.Reverse[LOOPHOLE[conversions]]]};
END;
BadConversions: ERROR = CODE;
NilRope: TYPE = Rope.ROPENIL;
tapeExplanation: ARRAY TapeOps.TapeStatusIndex OF NilRope =
[
RDY: "Rdy ",
ONL: "Onl ",
RWD: "Rwd ",
FPT: "WP ",
BOT: "BOT ",
EOT: "EOT ",
FMK: "FMK ",
NRZI: "NRZI ",
errHE: "HE ",
errSE: "SE ",
errDL: "DL ",
errRDP: "RDP ",
errICL: "ICL ",
errHDW: "Hdw ",
errWFP: "FP ",
errCMD: "Cmd "
];
END...