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 = { 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 { 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. RopeFileImpl.mesa Russ Atkinson, August 26, 1983 4:02 pm T Y P E S splice this ropeFile onto the global list [data: REF, index: INT] RETURNS [CHAR] this buffer contains the character range we have not come to the last buffer need to swap in new buffer swap to speed searches [base: REF, start, len: INT, action: ActionType] RETURNS [quit: BOOL _ FALSE] this buffer contains the character range we have not come to the last buffer need to swap in new buffer swap to speed searches At this point we have found a good buffer, so we snatch a sub-buffer from it we will not touch the buffer until the next time around (if any) Now yield all of the chars in the sub-buffer This procedure closes the files associated with rope files as part of finalizing the ropes in question. We have to be quite careful to lock things properly, which is why there is a dummyRopeFile. The dummyRopeFile is used to hold on to the ropes created by Create, and to synchronize insertinon of new rope files and deletion of old rope files. Remember: paranoia pays! note: always use the following order in acquiring/releasing locks splice out this file Only close the file if it is on the list AND has been opened. This makes us insensitive to multiple occurences of rope files on the finalization queue. Ê ž˜šœ™Jšœ&™&—J˜šÏk ˜ Jšœ œ˜(Jšœœ*˜6Jšœœ1œ˜AJšœœ ˜Jšœœ ˜Jšœ œ˜šœ˜ Jšœ<œ ˜b—Jšœ œ ˜Jšœ œ˜,šœ ˜Jšœn˜n—Jšœ œ˜$Jšœ œ˜"Jšœ œ ˜Jšœœ ˜Jšœ œ ˜Jšœ œ˜&J˜—J˜šÐbl œ ˜Jšœœ˜!š˜Jšœœz˜—Jšœ ˜Jšœ˜J˜—šœ ™ Jšœœœ˜Jšœœœœ˜Jšœœœ ˜J˜šœ œ œœ˜&Jšœ œ˜Jšœœ˜Jšœœœ˜Jšœœœ˜Jšœ œ˜Jšœœ˜Jšœœ œœ˜$J˜—Jšœœœ˜Jš œœœ œœœœ˜=J˜—šÏn œœœ˜,Jšœœœœ˜7Jšœœ˜J˜J˜—šŸ œœœ˜,Jšœœœœ˜ Jšœœ˜Jš œ˜J˜J˜—šŸœœ˜š œœ œ œœœ˜2Jšœ œœ˜%Jš œœœœœ˜&—Jšœœ˜Jšœ˜šœ œ˜!Jšœ˜J˜ Jšœ;˜;—Jšœ œœ˜)Jšœ œ˜Jšœ œ ˜Jšœ œœœ˜Jš œœœœœ˜GJšœ˜Jšœ œ ˜ Jšœœ˜"Jšœœ˜,Jšœ,˜,Jšœœ˜4šœœœœ˜>Jšœ*˜*Jšœœœ˜5Jšœ˜Jšœ˜J˜—šœœBœ˜NJšœœ˜ —Jšœ œ˜#J˜Jšœ˜J˜šœœœ˜Jšœ œ#œœ ˜GJšœ˜—Jš œ+œœœœ˜JJšœ)™)Jšœ˜Jšœ!˜!Jšœ˜Jšœ˜J˜J˜—šœ˜Jšœ&™&Jšœœ˜Jšœœ˜Jšœœ˜Jšœœœœ˜ Jšœ˜J˜šœœœ ˜J˜Jšœœ˜šœœ˜šœ œ˜#Jšœ(™(Jšœ˜—šœ ˜ Jšœ#™#Jšœ˜—šœ˜ Jšœ™Jšœœ˜Jšœœœ ˜0Jšœ˜ Jšœ œ,˜7Jšœ œœ˜0J˜J˜J˜——šœœ˜Jšœ™J˜J˜J˜—Jšœ˜Jšœ˜Jšœ˜—J˜Jšœ˜ J˜J˜—šœœ˜JšœM™MJšœœ˜Jšœœ˜Jšœœœœ˜ Jšœ˜šœ ˜Jšœ œ˜Jšœœ˜Jšœœ˜Jšœœ ˜Jšœœ ˜Jšœœ˜Jšœœ˜Jšœ˜šœœœ ˜J˜Jšœ˜šœœ˜šœ œ˜#Jšœ(™(Jšœ˜—šœ ˜ Jšœ#™#Jšœ˜—šœ˜ Jšœ™Jšœœ˜Jšœœœ ˜/Jšœ˜Jšœ œ,˜7Jšœ œœ˜0Jšœ˜J˜J˜——šœœ˜Jšœ™J˜J˜J˜—Jšœ˜Jšœ˜—JšœL™LJšœ ˜ Jšœœ˜!Jšœ œ ˜ šœœœ ˜Jšœ˜Jšœ˜—Jšœ@™@J˜Jšœ,™,šœœœ ˜Jšœœœœ˜(Jšœ˜—Jšœ˜J˜Jšœ˜—Jšœœ˜J˜J˜—šŸ œœ˜Jšœœ œ œœœœœ˜KJšœ˜"J˜J˜—š Ÿ œœœœœœ˜?Jšœ˜J˜J˜—Jš œœœœœ˜Ašœ#˜#Jšœœ˜Jšœœ˜Jšœœ ˜š˜Jšœœ"˜,Jšœœ!˜*Jšœœ#˜-Jšœ œœ˜Jšœœœ˜Jšœœœ˜Jšœœœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ ˜ šœ˜šœ˜šœ˜Jšœ˜Jšœœ˜!—šœœœ˜Jšœœ˜$Jšœœ˜*Jšœ ˜ šœ˜šœ˜Jšœ'˜'Jšœœ˜!——J˜—J˜—šœ˜š œœœœœœ˜Ešœ˜Jšœ(˜(Jšœœ˜—Jšœœœœ˜Jšœ˜—J˜——šœ˜ šœ˜šœ8˜8Jšœ-œ˜3Jšœœ˜—J˜Jšœ˜J˜J˜Jšœœ˜5Jšœ˜Jšœ˜J˜—šœ˜Jšœ˜J˜——Jšœ˜—J˜J˜—šŸœœ˜Jšœô™ôš˜Jšœœ˜#šœœ˜˜JšœA™AJšœ˜Jšœœœ˜Jšœœœ˜ Jšœ˜Jšœ˜šœ.œœ˜Dšœ œ˜Jšœ™J˜Jšœœ˜ Jšœ˜J˜—J˜ Jšœ˜—šœœ œœ˜!Jšœ˜™˜Jšœœ2˜?Jšœ>˜>Jšœ˜Jšœ œ˜J˜—Jšœ˜Jšœ˜J˜—Jšœ˜—Jšœ˜ —Jšœ˜—J˜JšœœÏc˜4Jšœ> ˜QJ˜šœ#˜#Jšœ˜šœ+˜+Jšœ%œ˜@——Jšœœ˜!šœ˜ Jšœœ˜)J˜—Jšœ[˜[J˜šœ˜J˜——…—^0