DIRECTORY FileWriter, IO, FileOps, FS, MonitoredQueue, Rope, RopeReader, TextNode; FileWriterImpl: CEDAR MONITOR IMPORTS IO, FileWriter, FS, MonitoredQueue, Rope, TextNode EXPORTS FileWriter SHARES FileWriter = BEGIN OPEN FileWriter; ROPE: TYPE = Rope.ROPE; OpenC: PUBLIC PROC [capability: FS.OpenFile, start: Offset _ 0, makeControl: BOOL _ TRUE] RETURNS [control,comment,data: Ref] = { stream: IO.STREAM = FS.StreamFromOpenFile[capability, $write]; IO.SetLength[stream,start]; IO.SetIndex[stream,start]; [control,comment,data] _ FinishOpen[stream,FALSE,makeControl,TRUE]; }; ToRope: PUBLIC PROC [makeControl: BOOL _ TRUE] RETURNS [control,comment,data: Ref] = { [control,comment,data] _ FinishOpen[NIL,TRUE,makeControl,FALSE]; }; ToStream: PUBLIC PROC [stream: IO.STREAM, makeControl: BOOL _ TRUE] RETURNS [control,comment,data: Ref] = { [control,comment,data] _ FinishOpen[stream,FALSE,makeControl,FALSE]; }; FinishOpen: PROC [stream: IO.STREAM, toRope: BOOL _ FALSE, makeControl, closeStream: BOOL _ TRUE] RETURNS [control,comment,data: Ref] = { MakeOne: PROC [kind: OfWriter] RETURNS [writer: Ref] = { writer _ GetFileWriter[]; writer.kind _ kind; writer.block _ GetBLOCK[]; writer.stream _ stream; writer.toRope _ toRope; writer.closeStream _ closeStream }; IF makeControl THEN { -- make comment and control writers control _ MakeOne[control]; FOR i:NAT IN [0..FileOps.fileIdSize) DO WriteChar[FileOps.controlHeaderId[i],control]; ENDLOOP; THROUGH [0..3] DO WriteChar[0C,control]; ENDLOOP; -- for length comment _ MakeOne[comment]; FOR i:NAT IN [0..FileOps.fileIdSize) DO WriteChar[FileOps.commentHeaderId[i],comment]; ENDLOOP; THROUGH [0..3] DO WriteChar[0C,comment]; ENDLOOP; -- for length }; data _ MakeOne[data]; IF ~toRope THEN { -- fork process to write data as it is produced data.blockQueue _ GetMQ[]; data.consumer _ FORK Consumer[data] }}; q1, q2: MonitoredQueue.MQ; GetMQ: ENTRY PROC RETURNS [q: MonitoredQueue.MQ] = { ENABLE UNWIND => NULL; IF q1 # NIL THEN { q _ q1; q1 _ NIL } ELSE IF q2 # NIL THEN { q _ q2; q2 _ NIL } ELSE q _ MonitoredQueue.Create[TextNode.pZone] }; FreeMQ: ENTRY PROC [q: MonitoredQueue.MQ] = { ENABLE UNWIND => NULL; IF q=NIL THEN RETURN; MonitoredQueue.Reset[q]; IF q1 = NIL THEN q1 _ q ELSE IF q2 = NIL THEN q2 _ q }; fw1, fw2: Ref; GetFileWriter: ENTRY PROC RETURNS [fw: Ref] = { ENABLE UNWIND => NULL; IF fw1 # NIL THEN { fw _ fw1; fw1 _ NIL } ELSE IF fw2 # NIL THEN { fw _ fw2; fw2 _ NIL } ELSE fw _ TextNode.pZone.NEW[FileWriterBody]; IF fw.kind # unused THEN ERROR }; FreeFileWriter: ENTRY PROC [fw: Ref] = { ENABLE UNWIND => NULL; fw.block _ NIL; fw.blockList _ NIL; fw.blockQueue _ NIL; fw.blockCount _ 0; fw.stream _ NIL; fw.kind _ unused; IF fw1 = NIL THEN fw1 _ fw ELSE IF fw2 = NIL THEN fw2 _ fw }; blks: NAT = 8; BlockArray: TYPE = ARRAY [0..blks) OF REF TEXT; blocks: REF BlockArray _ NEW[BlockArray]; GetBLOCK: ENTRY PROC RETURNS [b: REF TEXT] = { ENABLE UNWIND => NULL; FOR i:NAT IN [0..blks) DO IF blocks[i] # NIL THEN { b _ blocks[i]; blocks[i] _ NIL; EXIT }; ENDLOOP; IF b=NIL THEN b _ TextNode.qZone.NEW[TEXT[blockSize]]; b.length _ 0 }; FreeBLOCK: ENTRY PROC [b: REF TEXT] = { ENABLE UNWIND => NULL; FOR i:NAT IN [0..blks) DO IF blocks[i] = NIL THEN { blocks[i] _ b; RETURN }; ENDLOOP }; BumpWriter: PUBLIC PROC [writer: Ref] RETURNS [block: REF TEXT] = { writer.blockCount _ writer.blockCount+1; -- number added to queue/list IF writer.kind=data AND ~writer.toRope THEN MonitoredQueue.Add[writer.block, writer.blockQueue] ELSE writer.blockList _ TextNode.pZone.NEW[BlockListBody _ [writer.block, writer.blockList]]; writer.block _ block _ GetBLOCK[] }; Consumer: PROC [data: Ref] = { OPEN MonitoredQueue; queue: MQ _ data.blockQueue; stream: IO.STREAM _ data.stream; DO x: REF ANY _ Remove[queue ! EndOfQueue => EXIT]; WITH x SELECT FROM xx: REF TEXT => { IO.PutBlock[stream,xx]; FreeBLOCK[xx] }; ENDCASE => ERROR; ENDLOOP }; Close: PUBLIC PROC [control,comment,data: Ref, textOnly: BOOL] RETURNS [dataLen, count: Offset, output: ROPE] = { deltaBytes: PACKED ARRAY [0..3] OF CHARACTER; commentStart, commentLen, controlLen: Offset _ 0; stream: IO.STREAM _ data.stream; len, controlBlocks, commentBlocks, dataBlocks: NAT _ 0; toRope: BOOL _ data.toRope; controlList,commentList,dataList: BlockList; controlBlock,commentBlock,dataBlock: REF TEXT; Reverse: PROC [lst: BlockList] RETURNS [prev: BlockList] = { next: BlockList; UNTIL lst=NIL DO next _ lst.next; lst.next _ prev; prev _ lst; lst _ next; ENDLOOP }; LastBlock: PROC [lst: BlockList] RETURNS [block: REF TEXT] = { FOR l: BlockList _ lst, l.next DO IF l.next=NIL THEN RETURN [l.block]; ENDLOOP }; MakeRope: PROC [first, num: NAT] RETURNS [ROPE] = { half: NAT; SELECT num FROM 0 => RETURN [NIL]; 1 => RETURN [Rope.FromRefText[GetChars[first]]]; ENDCASE; half _ num/2; RETURN [Rope.Concat[MakeRope[first,half],MakeRope[first+half,num-half]]] }; FinishWriter: PROC [writer: Ref] RETURNS [list: BlockList, block: REF TEXT] = { list _ Reverse[writer.blockList]; block _ writer.block; FreeFileWriter[writer] }; GetChars: PROC [num: NAT] RETURNS [chars: REF TEXT] = { GetIt: PROC [blocks: NAT, list: BlockList, block: REF TEXT] RETURNS [ok: BOOL] = { IF num NOT IN [0..blocks) THEN RETURN [FALSE]; FOR lst: BlockList _ list, lst.next UNTIL lst=NIL DO IF num = 0 THEN { chars _ lst.block; RETURN [TRUE] }; num _ num-1; ENDLOOP; IF num = 0 THEN { chars _ block; RETURN [TRUE] }; ERROR }; IF GetIt[dataBlocks,dataList,dataBlock] THEN RETURN [chars]; num _ num-dataBlocks; IF GetIt[commentBlocks,commentList,commentBlock] THEN RETURN [chars]; num _ num-commentBlocks; IF GetIt[controlBlocks,controlList,controlBlock] THEN RETURN [chars]; ERROR }; dataBlocks _ data.blockCount; commentStart _ dataBlocks*LONG[blockSize]; IF (len_data.block.length) MOD 2 = 1 THEN { WriteChar[0C,data]; len _ len+1 }; commentStart _ commentStart+len; dataBlocks _ dataBlocks+1; IF control # NIL THEN { IF ~textOnly THEN { chars: REF TEXT; WriteControlInt: PROC [int: INT] = { deltaBytes _ LOOPHOLE[int]; FOR i:NAT IN [0..3] DO WriteChar[deltaBytes[i],control]; ENDLOOP }; Check: PROC [writer: Ref, kind: OfWriter] = { IF writer.kind # kind THEN ERROR; IF writer.toRope # toRope THEN ERROR; IF writer.closeStream # data.closeStream THEN ERROR; IF writer.stream # data.stream THEN ERROR }; Count: PROC [list: BlockList, writer: Ref, extra: Offset _ 0] RETURNS [blocks: NAT, len: Offset] = { blocks _ writer.blockCount; len _ LONG[blocks]*blockSize+writer.block.length+extra; blocks _ blocks+1; deltaBytes _ LOOPHOLE[len]; chars _ IF list=NIL THEN writer.block ELSE LastBlock[list]; FOR i:NAT IN [0..3] DO chars[FileOps.fileIdSize+i] _ deltaBytes[i]; ENDLOOP }; Check[comment,comment]; Check[control,control]; FOR i:NAT IN [0..FileOps.fileIdSize) DO -- write end trailer WriteChar[FileOps.controlTrailerId[i],control]; ENDLOOP; WriteControlInt[0]; -- WriteControlInt[commentStart]; IF comment.block.length MOD 2 = 1 THEN WriteChar[0C,comment]; -- fill word [commentBlocks,commentLen] _ Count[comment.blockList,comment]; [controlBlocks,controlLen] _ Count[control.blockList,control,4]; -- add 4 for final file length WriteControlInt[commentStart+commentLen+controlLen]; }; [controlList,controlBlock] _ FinishWriter[control]; [commentList,commentBlock] _ FinishWriter[comment] }; IF data.kind # data THEN ERROR; dataBlock _ data.block; IF toRope THEN { dataList _ Reverse[data.blockList]; output _ MakeRope[0,dataBlocks+commentBlocks+controlBlocks] } -- return a balanced rope ELSE { MonitoredQueue.Close[data.blockQueue]; TRUSTED {JOIN data.consumer}; FreeMQ[data.blockQueue]; IO.PutBlock[stream,dataBlock]; FreeBLOCK[dataBlock]; IF control # NIL THEN { DoBlocks: PROC [list: BlockList, block: REF TEXT] = { FOR lst:BlockList _ list, lst.next UNTIL lst=NIL DO IF ~textOnly THEN IO.PutBlock[stream,lst.block]; FreeBLOCK[lst.block]; ENDLOOP; IF ~textOnly THEN IO.PutBlock[stream,block]; FreeBLOCK[block] }; DoBlocks[commentList,commentBlock]; DoBlocks[controlList,controlBlock] }}; count _ commentStart+commentLen+controlLen; dataLen _ commentStart; IF data.closeStream THEN IO.Close[stream]; FreeFileWriter[data] }; Start: PUBLIC PROC = { }; END. äFileWriterImpl.Mesa written by Paxton. March 1981 McGregor. September 10, 1982 1:15 pm Maxwell, January 5, 1983 12:50 pm Russ Atkinson, September 26, 1983 1:17 pm -- this is the place to write out the file props, if any Ê ™˜šÏc™Jš™Jš$™$J™!J™)J˜—šÏk ˜ J˜ Jšžœ˜J˜Jšžœ˜J˜J˜J˜ J˜ —J˜šœž ˜Jšžœžœ0˜:Jšžœ ˜Jšžœ ˜—Jšžœžœ ˜J˜Jšžœžœžœ˜J˜šÏnœžœž˜Jšœ žœ+žœžœ˜FJšžœ ˜'Jšœž œ-˜>Jšžœ˜Jšžœ˜Jšœ+žœ žœ˜CJšœ˜J˜—š Ÿœžœžœžœžœ˜.Jšžœ ˜'Jšœ$žœžœ žœ˜@Jšœ˜J˜—š Ÿœžœžœ ž œžœžœ˜Cšžœ ˜'Jšœ+žœ žœ˜D—šœ˜J˜——šŸ œž˜Jš œ ž œ žœžœžœžœ˜PJšžœ ˜'šŸœžœžœ˜8J˜J˜J˜J˜J˜Jšœ#˜#—šžœ žœ#˜9J˜šžœžœžœž˜'Jšœ/žœ˜7—Jšžœžœžœ ˜?J˜šžœžœžœž˜'Jšœ/žœ˜7—šžœžœžœ ˜?J˜——J˜šžœ žœ/˜AJ˜Jšœžœ˜'J˜——Jšœžœ˜J˜š Ÿœžœžœžœžœ˜4Jšžœžœžœ˜Jšžœžœžœžœ˜%Jš žœžœžœžœžœ˜*Jšžœ-˜1J˜—šŸœžœžœžœ˜-Jšžœžœžœ˜Jšžœžœžœžœ˜J˜Jšžœžœžœ˜Jšžœžœžœžœ ˜J˜—J˜J˜šŸ œžœžœžœ˜/Jšžœžœžœ˜Jšžœžœžœžœ˜)Jš žœžœžœžœžœ˜.Jšžœžœ˜-Jšžœžœžœ˜!J˜—šŸœžœžœ˜(Jšžœžœžœ˜Jšœ žœ˜Jšœžœ˜Jšœžœ˜J˜Jšœ žœ˜J˜Jšžœžœžœ ˜Jšžœžœžœžœ ˜"J˜—Jšœžœ˜Jš œ žœžœ žœžœžœ˜/Jšœžœžœ ˜)J˜š Ÿœžœžœžœžœžœ˜.Jšžœžœžœ˜šžœžœžœ ž˜Jš žœ žœžœžœžœ˜AJšžœ˜—Jš žœžœžœžœžœ ˜6J˜J˜—š Ÿ œžœžœžœžœ˜'Jšžœžœžœ˜šžœžœžœ ž˜Jšžœ žœžœžœ˜2Jšžœ˜ J˜——š Ÿ œžœžœžœ žœžœ˜CJšœ)˜FJšžœžœžœ4˜_Jšžœ#žœ3˜]J˜$J˜—šŸœžœžœ˜3Jšœžœ˜Jšœž œ˜ šžœžœžœ žœ˜3Jšžœžœž˜Jšœžœžœžœ&˜:Jšžœžœ˜Jšžœ˜ J˜——šŸœžœžœ'žœ˜>Jšžœ"žœ˜2Jš œ žœžœžœž œ˜.J˜1Jšœž œ˜ Jšœ/žœ˜7Jšœžœ˜J˜,Jšœ%žœžœ˜.J˜šŸœžœžœ˜šžœž˜!Jšžœžœžœžœ ˜$Jšžœ˜ J˜——š Ÿœžœžœžœžœ˜3Jšœžœ˜ šžœž˜Jšœžœžœ˜Jšœžœ%˜0Jšžœ˜—J˜ JšžœE˜KJ˜—š Ÿ œžœžœžœžœ˜OJ˜QJ˜—š Ÿœžœžœžœ žœžœ˜7š Ÿœžœ žœžœžœ˜;Jšžœžœ˜Jš žœžœžœ žœžœžœ˜.šžœ!žœžœž˜4Jšžœ žœžœžœ˜5Jšœ žœ˜—Jšžœ žœžœžœ˜1Jšžœ˜—Jšžœ&žœžœ ˜JšœA˜_J˜J˜4J˜J˜—J˜3J˜5J˜—Jšžœžœžœ˜J˜šžœžœ˜J˜#Jšœ>˜W—šžœ˜J˜&Jšžœžœ˜J˜Jšžœ˜J˜šžœ žœžœ˜šŸœžœžœžœ˜5šžœ žœžœž˜3Jšžœ žœžœ˜0J˜Jšžœ˜—Jšžœ žœžœ˜,J˜—J˜#J˜&——J˜+J˜Jšžœžœžœ˜*J˜J˜—šŸœžœžœ˜J˜J˜—Jšžœ˜J˜—…— ,…