<<>> <> <> DIRECTORY Commander USING [CommandProc, Register], FileIO USING [CapabilityFromStream, Open, OpenFailed], IO USING [Close, GetBlock, GetLength, PutRope, SetIndex, STREAM], PutGet USING [FromRope], RopeFile USING [], Rope USING [Concat, FetchType, Flatten, MakeRope, MapType, ROPE, Size, SkipOver, SkipTo, Text], RopeInline USING [NewText], RTFilesExtra USING [IncFileUse], TEditImpl USING [InitTEditDocument], TiogaMenuOps USING [DefaultMenus], ViewerClasses USING [Viewer], ViewerOps USING [OpenIcon], ViewerTools USING [MakeNewTextViewer]; RopeFileImpl: CEDAR MONITOR LOCKS handle USING handle: Handle IMPORTS Commander, FileIO, IO, PutGet, Rope, RopeInline, RTFilesExtra, TEditImpl, TiogaMenuOps, ViewerOps, ViewerTools EXPORTS RopeFile = BEGIN <> ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; Handle: TYPE = REF RopeFileRep; RopeFileRep: TYPE = MONITORED RECORD [ file: STREAM _ NIL, locked: BOOL _ FALSE, lockChange: CONDITION, bufSize: NAT _ 0, offset: INT _ 0, data: SEQUENCE buffers: NAT OF Buf]; Buf: TYPE = REF BufRec; BufRec: TYPE = RECORD [start: INT, len: NAT, text: REF TEXT]; AcquireLock: ENTRY PROC [handle: Handle] = { WHILE handle.locked DO WAIT handle.lockChange; ENDLOOP; handle.locked _ TRUE; }; ReleaseLock: ENTRY PROC [handle: Handle] = { IF NOT handle.locked THEN ERROR; handle.locked _ FALSE; BROADCAST handle.lockChange; }; Create: PUBLIC PROC [name: ROPE, start: INT _ 0, len: INT _ LAST[INT], bufSize: INT _ 512, buffers: NAT _ 4, useMap: BOOL _ TRUE, raw: BOOL _ TRUE] RETURNS [rope: ROPE] = { handle: Handle; fileStream: STREAM _ FileIO.Open[ fileName: name, raw: raw, streamBufferParms: [bufferSize: 2, bufferSwapUnitSize: 1]]; filesize: INT _ IO.GetLength[fileStream]; bestBuffers: INT; IF start < 0 THEN start _ 0; IF len <= 0 THEN RETURN [NIL]; IF len = LAST[INT] OR start+len > filesize THEN len _ filesize - start; IO.SetIndex[fileStream, start]; IF buffers = 0 THEN buffers _ 1; IF bufSize < 64 THEN bufSize _ 64; IF bufSize > 16*1024 THEN bufSize _ 16*1024; bestBuffers _ (len + bufSize - 1) / bufSize; IF bestBuffers < buffers THEN buffers _ bestBuffers; IF len < 16*1024 AND len <= (buffers * bufSize) THEN TRUSTED { text: Rope.Text _ RopeInline.NewText[len]; [] _ IO.GetBlock[fileStream, LOOPHOLE[text], 0, len]; IO.Close[fileStream]; RETURN [text]; }; IF NOT RTFilesExtra.IncFileUse[FileIO.CapabilityFromStream[fileStream], FALSE] THEN ERROR; handle _ NEW[RopeFileRep[buffers]]; handle.bufSize _ bufSize; handle.file _ fileStream; handle.offset _ start; FOR i: NAT IN [0..buffers) DO handle[i] _ NEW[BufRec _ [start: 0, len: 0, text: NEW[TEXT[bufSize]]]]; ENDLOOP; rope _ Rope.MakeRope[handle, len, MyFetch, IF useMap THEN MyMap ELSE NIL]; }; MyFetch: Rope.FetchType = { <<[data: REF, index: INT] RETURNS [CHAR]>> handle: Handle = NARROW[data]; last: NAT = handle.buffers-1; c: CHAR; IF handle.file = NIL THEN ERROR; AcquireLock[handle]; index _ index + handle.offset; FOR i: NAT IN [0..last] DO buf: Buf _ handle[i]; delta: INT _ index - buf.start; SELECT TRUE FROM delta >= 0 AND delta < buf.len => { <> }; i < last => <> LOOP; ENDCASE => { <> length: NAT _ handle.bufSize; start: INT = index - (delta _ index MOD length); IO.SetIndex[handle.file, start]; length _ IO.GetBlock[handle.file, buf.text, 0, length]; IF length = 0 THEN {ReleaseLock[handle]; ERROR}; buf.start _ start; buf.len _ length; }; IF i # 0 THEN { <> handle[i] _ handle[i-1]; handle[i-1] _ buf; }; c _ buf.text[delta]; EXIT; ENDLOOP; ReleaseLock[handle]; RETURN [c]; }; MyMap: Rope.MapType = TRUSTED { <<[base: REF, start, len: INT, action: ActionType] RETURNS [quit: BOOL _ FALSE]>> handle: Handle _ NARROW[base]; last: NAT = handle.buffers-1; IF handle.file = NIL THEN ERROR; start _ start + handle.offset; WHILE len > 0 DO buf: Buf _ NIL; delta: INT _ 0; subLen: NAT = 32; string: STRING _ [subLen]; pos: NAT _ delta; spos: NAT _ 0; chars: NAT _ 0; AcquireLock[handle]; FOR i: NAT IN [0..last] DO buf _ handle[i]; delta _ start - buf.start; SELECT TRUE FROM delta >= 0 AND delta < buf.len => { <> }; i < last => <> LOOP; ENDCASE => { <> length: NAT _ handle.bufSize; bpos: INT = start - (delta _ start MOD length); IO.SetIndex[handle.file, bpos]; length _ IO.GetBlock[handle.file, buf.text, 0, length]; IF length = 0 THEN {ReleaseLock[handle]; ERROR}; buf.start _ bpos; buf.len _ length; }; IF i # 0 THEN { <> handle[i] _ handle[i-1]; handle[i-1] _ buf; }; EXIT; ENDLOOP; <> pos _ delta; chars _ MIN[buf.len-pos, subLen]; IF len < chars THEN chars _ len; FOR j: NAT IN [0..chars) DO string[j] _ buf.text[pos+j]; ENDLOOP; <> ReleaseLock[handle]; <> FOR j: NAT IN [0..chars) DO IF action[string[j]] THEN RETURN [TRUE]; ENDLOOP; len _ len - chars; start _ start + chars; ENDLOOP; RETURN [FALSE]; }; SubstrCreate: PUBLIC PROC [name: ROPE, start: INT _ 0, len: INT _ LAST[INT]] RETURNS [rope: ROPE] = { RETURN [Create[name, start, len]]; }; SimpleCreate: PUBLIC PROC [name: ROPE] RETURNS [rope: ROPE] = { RETURN [Create[name]]; }; useMap: BOOL _ TRUE; extensionList: LIST OF ROPE _ LIST["", ".mesa", ".tioga", ".df"]; OpenHuge: Commander.CommandProc = { line: ROPE _ cmd.commandLine; last: INT _ 0; out: STREAM _ cmd.out; DO start: INT _ line.SkipOver[last, " ,;\t\n"]; next: INT _ line.SkipTo[start, " ,;\t\n"]; name: ROPE _ line.Flatten[start, next-start]; rope: ROPE _ NIL; IF name.Size[] = 0 THEN RETURN; out.PutRope["Opening "]; out.PutRope[name]; out.PutRope["..."]; last _ next; FOR list: LIST OF ROPE _ extensionList, list.rest WHILE list # NIL DO rope _ Create[ name: name.Concat[list.first], useMap: useMap ! FileIO.OpenFailed => LOOP]; IF rope # NIL THEN EXIT; ENDLOOP; IF rope # NIL THEN { v: ViewerClasses.Viewer _ ViewerTools.MakeNewTextViewer[ info: [name: name, column: left, iconic: TRUE], paint: FALSE]; v.data _ PutGet.FromRope[rope]; v.file _ name; TEditImpl.InitTEditDocument[v]; TiogaMenuOps.DefaultMenus[v]; ViewerOps.OpenIcon[v]; out.PutRope["done.\n"]; } ELSE { out.PutRope["failed!\n"]; }; ENDLOOP; }; Commander.Register["openHuge", OpenHuge, "Opens huge files in a somewhat unsafe fashion."]; END.