<> <> DIRECTORY Commander USING [CommandProc, Register], FileIO USING [CapabilityFromStream, Open, OpenFailed], IO USING [Close, GetBlock, GetLength, PutRope, SetIndex, STREAM], Process USING [Detach], PutGet USING [FromRope], RopeFile USING [], Rope USING [Concat, FetchType, Flatten, Index, MakeRope, MapType, Match, ROPE, Size, SkipOver, SkipTo, Text], RopeInline USING [NewText], RTFilesExtra USING [DecFileUse, IncFileUse], RTTypesBasic USING [CantEstablishFinalization, EstablishFinalization, FinalizationQueue, FQNext, NewFQ, ReEstablishFinalization], TEditImpl USING [InitTEditDocument], TiogaMenuOps USING [DefaultMenus], TiogaOps USING [FindDef], ViewerClasses USING [Viewer], ViewerOps USING [OpenIcon], ViewerTools USING [MakeNewTextViewer]; RopeFileImpl: CEDAR MONITOR LOCKS handle USING handle: Handle IMPORTS Commander, FileIO, IO, Process, PutGet, Rope, RopeInline, RTFilesExtra, RTTypesBasic, TEditImpl, TiogaMenuOps, TiogaOps, ViewerOps, ViewerTools EXPORTS RopeFile = BEGIN <> ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; Handle: TYPE = REF RopeFileRep; RopeFileRep: TYPE = MONITORED RECORD [ lockChange: CONDITION, next: Handle _ NIL, file: STREAM _ NIL, locked: BOOL _ FALSE, 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]; <> AcquireLock[dummyRopeFile]; handle.next _ dummyRopeFile.next; dummyRopeFile.next _ handle; ReleaseLock[dummyRopeFile]; }; 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]]; }; 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]; fullName: ROPE _ NIL; rope: ROPE _ NIL; ext: ROPE _ NIL; IF name.Size[] = 0 THEN RETURN; out.PutRope["Opening "]; out.PutRope[name]; out.PutRope["..."]; last _ next; IF Rope.Match["*.*", name] THEN { rope _ Create[ name: fullName _ name ! FileIO.OpenFailed => CONTINUE]; IF rope = NIL THEN { pos: INT = Rope.Index[name, 0, "."]; prefix: ROPE = Rope.Flatten[name, 0, pos]; ext _ Rope.Flatten[name, pos+1]; IF ext.Size[] # 0 THEN rope _ Create[ name: fullName _ prefix.Concat[".mesa"] ! FileIO.OpenFailed => CONTINUE]; }; } ELSE { FOR list: LIST OF ROPE _ extensionList, list.rest WHILE list # NIL DO rope _ Create[ name: fullName _ name.Concat[list.first] ! FileIO.OpenFailed => LOOP]; IF rope # NIL THEN EXIT; ENDLOOP; }; IF rope # NIL THEN { v: ViewerClasses.Viewer _ ViewerTools.MakeNewTextViewer[ info: [name: fullName, column: left, iconic: TRUE], paint: FALSE]; v.data _ PutGet.FromRope[rope]; v.file _ name; TEditImpl.InitTEditDocument[v]; TiogaMenuOps.DefaultMenus[v]; IF ext.Size[] # 0 THEN [] _ TiogaOps.FindDef[v, ext]; ViewerOps.OpenIcon[v]; out.PutRope["done.\n"]; } ELSE { out.PutRope["failed!\n"]; }; ENDLOOP; }; RopeFileFinalizer: PROC = { <> DO ref: REF _ RTTypesBasic.FQNext[fq]; WITH ref SELECT FROM rf: Handle => { <> lag: Handle _ dummyRopeFile; found: BOOL _ FALSE; IF rf = dummyRopeFile THEN LOOP; AcquireLock[dummyRopeFile]; AcquireLock[rf]; FOR each: Handle _ dummyRopeFile.next, each.next WHILE each # NIL DO IF each = rf THEN { <> lag.next _ each.next; found _ TRUE; EXIT; }; lag _ each; ENDLOOP; IF found AND rf.file # NIL THEN { <> ENABLE UNWIND => {ReleaseLock[rf]; ReleaseLock[dummyRopeFile]}; RTFilesExtra.DecFileUse[FileIO.CapabilityFromStream[rf.file]]; IO.Close[rf.file]; rf.file _ NIL; }; ReleaseLock[rf]; ReleaseLock[dummyRopeFile]; }; ENDCASE; ENDLOOP; }; dummyRopeFile: Handle _ NIL; -- for synchronization fq: RTTypesBasic.FinalizationQueue = RTTypesBasic.NewFQ[32]; -- for finalization RTTypesBasic.EstablishFinalization[ CODE[RopeFileRep], 1, fq ! RTTypesBasic.CantEstablishFinalization => RTTypesBasic.ReEstablishFinalization[CODE[RopeFileRep], 1, fq]]; dummyRopeFile _ NEW[RopeFileRep]; TRUSTED { Process.Detach[FORK RopeFileFinalizer[]]; }; Commander.Register["openHuge", OpenHuge, "Opens huge files in a somewhat unsafe fashion."]; END.