DIRECTORY FS USING [Close, Error, ErrorDesc, GetInfo, nullOpenFile, Open, OpenFile, Read], Process USING [Detach, InitializeCondition, MsecToTicks, Ticks], RefText USING [ObtainScratch, ReleaseScratch, TrustTextAsRope], Rope USING [Fetch, FromRefText, Length, ROPE], SafeStorage USING [EnableFinalization, EstablishFinalization, FinalizationQueue, FQEmpty, FQNext, NewFQ, ReclaimCollectibleObjects], VM USING [AddressForPageNumber, Allocate, Free, Interval, nullInterval, WordsForPages], UFFileManager; UFFileManagerImpl: CEDAR MONITOR IMPORTS FS, Process, RefText, Rope, SafeStorage, VM EXPORTS UFFileManager = BEGIN OPEN UFFileManager; FontFile: PUBLIC TYPE = REF FontFilePermission; FontFilePermission: PUBLIC TYPE = RECORD [ link: REF FontFilePermission, key: Key ]; FontFileDescription: TYPE = RECORD [ permissions: REF FontFilePermission, permissionHint: REF FontFilePermission, clientData: REF _ NIL, openFile: FS.OpenFile, error: FS.ErrorDesc, vm: VM.Interval, origin: LONG POINTER, size: INT -- in words ]; keyTable: REF KeyTableRec _ NEW[KeyTableRec[6]]; KeyTableRec: TYPE = RECORD [ numberOfEntries: NAT _ 0, entries: SEQUENCE length: NAT OF NameFilePair ]; NameFilePair: TYPE = RECORD [fileName: ROPE, description: REF FontFileDescription]; permissionCount: INT _ 0; permissionsSinceCollectionRequest: NAT _ 0; permissionThreshold: NAT _ 96; RequestCollection: PROC = { permissionsSinceCollectionRequest _ 0; SafeStorage.ReclaimCollectibleObjects[]; }; finalizationQueue: SafeStorage.FinalizationQueue _ SafeStorage.NewFQ[permissionThreshold]; FinalizerProcess: PROCEDURE [finalizationQueue: SafeStorage.FinalizationQueue] = { DO permission: REF FontFilePermission _ NARROW[SafeStorage.FQNext[finalizationQueue]]; key: Key _ permission.key; closed: BOOLEAN _ Finalize[permission]; permission _ NIL; IF closed THEN fontFileWatcherProc[key, close]; IF SafeStorage.FQEmpty[finalizationQueue] THEN RequestCollection[]; ENDLOOP; }; Finalize: ENTRY PROCEDURE [permission: REF FontFilePermission] RETURNS [closed: BOOLEAN _ FALSE] = { ENABLE UNWIND => NULL; key: Key _ permission.key; description: REF FontFileDescription _ keyTable[key.index].description; IF permission = description.permissions THEN { description.permissions _ permission.link; } ELSE { prev: REF FontFilePermission _ description.permissions; FOR p: REF FontFilePermission _ description.permissions.link, p.link UNTIL p=permission DO prev _ p; ENDLOOP; prev.link _ permission.link; }; permission.link _ NIL; IF description.permissions = NIL THEN { IF description.openFile # FS.nullOpenFile THEN { description.origin _ NIL; description.size _ 0; TRUSTED { VM.Free[description.vm] }; description.vm _ VM.nullInterval; FS.Close[description.openFile]; description.openFile _ FS.nullOpenFile; closed _ TRUE; }; keyTable[key.index].description _ NIL; }; permissionCount _ permissionCount-1; }; Init: ENTRY PROCEDURE = { ENABLE UNWIND => NULL; SafeStorage.EstablishFinalization[CODE[FontFilePermission], 1, finalizationQueue]; TRUSTED {Process.Detach[FORK FinalizerProcess[finalizationQueue]]}; }; KeyOf: PUBLIC ENTRY PROCEDURE [fileName: ROPE] RETURNS [key: Key] = { ENABLE UNWIND => NULL; len: NAT _ fileName.Length[]; canonicalName: REF TEXT _ RefText.ObtainScratch[len]; i: NAT _ 0; Getc: PROC RETURNS [ch: CHAR] = {IF i {Put['/]; c _ Getc[]}; '] => {Put['/]; IF (c _ Getc[]) = '< THEN c _ Getc[]}; '> => {Put['/]; c _ Getc[]}; IN ['a .. 'z] => {Put[c + lowerToUpper]; c _ Getc[]}; ENDCASE => {Put[c]; c _ Getc[]}; ENDLOOP; canonicalName.length _ j; FOR k: NAT IN [0..keyTable.numberOfEntries) DO IF Match[keyTable[k].fileName] THEN {key _ [index: k]; EXIT}; ENDLOOP; IF key.index = LAST[NAT] THEN { PreventTableOverflow[]; keyTable[keyTable.numberOfEntries] _ [fileName: Rope.FromRefText[canonicalName], description: NIL]; key _ [index: keyTable.numberOfEntries]; keyTable.numberOfEntries _ keyTable.numberOfEntries+1; }; RefText.ReleaseScratch[canonicalName]; }; KeyOfRefText: PUBLIC PROCEDURE [fileName: REF TEXT] RETURNS [key: Key] = { key _ KeyOf[RefText.TrustTextAsRope[fileName]]; }; waitTime: Process.Ticks _ Process.MsecToTicks[5000]; fileHintTimeout: CONDITION; fileHintTimeoutProcess: PROCESS _ NIL; FileHintTimeoutProcess: ENTRY PROCEDURE = { ENABLE UNWIND => NULL; WAIT fileHintTimeout; fileHintTimeoutProcess _ NIL; FOR i: NAT IN [0..keyTable.numberOfEntries) DO description: REF FontFileDescription _ keyTable[i].description; IF description#NIL THEN description.permissionHint _ NIL; ENDLOOP; WAIT fileHintTimeout; IF fileHintTimeoutProcess = NIL THEN SafeStorage.ReclaimCollectibleObjects[suspendMe: FALSE]; }; StartFileHintTimeout: ENTRY PROCEDURE = { ENABLE UNWIND => NULL; IF fileHintTimeoutProcess=NIL THEN TRUSTED { Process.InitializeCondition[@fileHintTimeout, waitTime]; Process.Detach[fileHintTimeoutProcess _ FORK FileHintTimeoutProcess[]]; }; }; Open: PUBLIC PROCEDURE [key: Key, initProc: InitProc _ NIL] RETURNS [fontFile: FontFile] = { description: REF FontFileDescription _ keyTable[key.index].description; IF description#NIL THEN fontFile _ description.permissionHint; IF fontFile=NIL THEN { triedToOpen: BOOLEAN; [fontFile, triedToOpen] _ MonitoredOpen[key, initProc]; IF triedToOpen THEN fontFileWatcherProc[key, IF Status[fontFile].ok THEN open ELSE openFailed]; IF permissionsSinceCollectionRequest>permissionThreshold THEN RequestCollection[]; }; StartFileHintTimeout[]; }; MonitoredOpen: ENTRY PROCEDURE [key: Key, initProc: InitProc _ NIL] RETURNS [fontFile: FontFile, triedToOpen: BOOLEAN _ FALSE] = { ENABLE UNWIND => NULL; description: REF FontFileDescription _ keyTable[key.index].description; IF description = NIL THEN { openOk: BOOLEAN _ TRUE; description _ NEW[FontFileDescription]; description.vm _ VM.nullInterval; description.openFile _ FS.nullOpenFile; description.error _ [ok, NIL, NIL]; description.openFile _ FS.Open[ name: keyTable[key.index].fileName, lock: $read, remoteCheck: FALSE ! FS.Error => {openOk _ FALSE; description.error _ error; CONTINUE}]; triedToOpen _ TRUE; IF openOk THEN TRUSTED { pages: INT = FS.GetInfo[description.openFile].pages; description.vm _ VM.Allocate[pages]; description.origin _ VM.AddressForPageNumber[description.vm.page]; FS.Read[file: description.openFile, from: 0, nPages: pages, to: description.origin]; description.size _ VM.WordsForPages[pages]; }; }; keyTable[key.index].description _ description; fontFile _ NEW[FontFilePermission]; permissionCount _ permissionCount + 1; permissionsSinceCollectionRequest _ permissionsSinceCollectionRequest + 1; fontFile.link _ description.permissions; description.permissions _ fontFile; fontFile.key _ key; IF description.clientData = NIL AND description.openFile # NIL AND initProc # NIL THEN description.clientData _ initProc[key, fontFile]; description.permissionHint _ fontFile; SafeStorage.EnableFinalization[fontFile]; }; GetData: PUBLIC PROCEDURE [fontFile: FontFile] RETURNS [REF] = { RETURN[keyTable[fontFile.key.index].description.clientData]; }; Status: PUBLIC PROCEDURE [fontFile: FontFile] RETURNS [error: FS.ErrorDesc, ok: BOOLEAN] = { description: REF FontFileDescription _ keyTable[fontFile.key.index].description; error _ description.error; ok _ error.group=$ok; }; Pointer: PUBLIC UNSAFE PROCEDURE [fontFile: FontFile] RETURNS [origin: LONG POINTER] = { origin _ keyTable[fontFile.key.index].description.origin; }; NameOf: PUBLIC PROCEDURE [key: Key] RETURNS [fileName: ROPE] = { IF key.index= 0 AND addr >= org AND addr + sizeInWords <= end ]; }; fontFileWatcherProc: FontFileWatcher _ defaultFontFileWatcher; defaultFontFileWatcher: FontFileWatcher = {}; RegisterFontFileWatcher: PUBLIC PROCEDURE [fontFileWatcher: FontFileWatcher] = { fontFileWatcherProc _ fontFileWatcher; }; PermissionCount: PUBLIC PROCEDURE RETURNS [INT] = {RETURN[permissionCount]}; EnumerateOpenFontFiles: PUBLIC PROCEDURE [visit: PROC[Rope.ROPE]] = { i: NAT _ 0; DO table: REF KeyTableRec _ keyTable; rope: Rope.ROPE _ NIL; IF i>= table.numberOfEntries THEN EXIT; IF table[i].description # NIL THEN rope _ table[i].fileName; table _ NIL; IF rope # NIL THEN visit[rope]; i _ i+1; ENDLOOP; }; Init[]; END. UFFileManagerImpl.mesa Created January 7, 1983 Last edit by Michael Plass on December 28, 1983 2:58 pm Last edit by Doug Wyatt on November 22, 1983 2:31 pm This is written so it doesn't have to be an entry procedure, so it doesn't matter if the visit proc hangs. Κ ˜Jšœ™Jšœ™Jšœ7™7Jšœ4™4J˜šΟk ˜ JšœœH˜PJšœœ3˜@Jšœœ2˜?Jšœœœ˜.Jšœ œs˜„JšœœO˜WJšœ˜J˜—Iunitšœœ˜ Jšœœ'˜3Jšœ˜Jšœœ˜Kšœ œœœ˜/šœœœœ˜*Jšœœ˜Jšœ˜J˜—šœœœ˜$Jšœ œ˜$Jšœœ˜'Jšœ œœ˜Jšœ œ ˜Jšœœ ˜Jšœœ ˜Jšœœœ˜JšœœΟc ˜J˜—Kšœ œœ˜0šœ œœ˜Jšœœ˜Jšœ œ œœ ˜-J˜—Kš œœœ œœ˜SKšœœ˜Kšœ#œ˜+Kšœœ˜šΟnœœ˜Jšœ&˜&Jšœ(˜(Jšœ˜—KšœZ˜ZšŸœ œ7˜Rš˜Jšœ œœ(˜SJšœ˜Jšœœ˜'Jšœ œ˜Jšœœ!˜/Jšœ'œ˜CJšœ˜—Jšœ˜—šŸœœ œœœ œœ˜dJšœœœ˜Jšœ˜Jšœ œ7˜Gšœ%œ˜.Jšœ*˜*J˜—šœ˜Jšœœ.˜7šœœ;œ˜ZJšœ ˜ Jšœ˜—Jšœ˜J˜—Jšœ˜šœœœ˜'šœœœ˜0Jšœœ˜J˜Jšœœ˜$Jšœœ˜!Jšœ˜Jšœœ˜'Jšœ œ˜J˜—Jšœ"œ˜&J˜—Jšœ$˜$Jšœ˜—šŸœœ œ˜Jšœœœ˜Jšœ"œ,˜RJšœœ'˜CJšœ˜—š Ÿœœœ œ œœ˜EJšœœœ˜Jšœœ˜Jšœœœ˜5Jšœœ˜ JšŸœœœœœœœ˜YJšœœ ˜Jšœœ˜ JšŸœœœ'˜:šŸœœ˜Jšœ œ˜(šœœ˜#Jš œ œœ œœœ˜WJšœ&˜&šœœœ˜Jšœ˜Jšœ˜—Jšœ˜J˜—J˜—š Ÿœœ œœœ˜3Jšœœœœ˜&š œœ œœ˜"Jšœ"œœœ˜7Jšœ˜—Jšœœ˜ J˜—Jšœœ ˜ Jšœ œœ˜šœ˜ šœ˜ J˜J˜6J˜J˜5Jšœ˜ —Jšœ˜—Jšœ˜šœœœ˜.Jšœœœ˜=Jšœ˜—šœ œœœ˜Jšœ˜Jšœ^œ˜cJšœ(˜(Jšœ6˜6J˜—Jšœ&˜&Jšœ˜—š Ÿ œœ œ œœœ˜JJšœ/˜/Jšœ˜—K˜4Kšœ ˜Kšœœœ˜&šŸœœ œ˜+Jšœœœ˜Jšœ˜Jšœœ˜šœœœ˜.Jšœ œ/˜?Jšœ œœœ˜9Jš˜—Jšœ˜Jšœœœ2œ˜]Jšœ˜—šŸœœ œ˜)Jšœœœ˜šœœœœ˜,Jšœ8˜8Jšœ(œ˜GJšœ˜—Jšœ˜—š Ÿœœ œ!œœ˜\Jšœ œ7˜GJšœ œœ'˜>šœ œœ˜Jšœ œ˜Jšœ7˜7Jš œ œœœœ˜`Jšœ6œ˜RJ˜—Jšœ˜Jšœ˜—šŸ œœ œ!œœ#œœ˜‚Jšœœœ˜Jšœ œ7˜Gšœœœ˜Jšœœœ˜Jšœœ˜'Jšœœ˜!Jšœœ˜'Jšœœœ˜#šœœ˜Jšœ>œ˜DJšœœœœ˜E—Jšœœ˜šœœœ˜Jšœœœ%˜4Jšœœ˜$Jšœœ+˜BJšœR˜TJšœœ˜+J˜—Jšœ˜—Jšœ.˜.Jšœ œ˜#J˜&J˜JJšœ(˜(Jšœ#˜#Jšœ˜š œœœœœ œ˜VJšœ1˜1—Jšœ&˜&Jšœ)˜)Jšœ˜—š Ÿœœ œœœ˜@Jšœ6˜˜>Kšœ-˜-šŸœœ œ'˜PJšœ&˜&Jšœ˜—Kš Ÿœœ œœœœ˜Lš Ÿœœ œ œœ˜EJ™jJšœœ˜ š˜Jšœœ˜"Jšœ œœ˜Jšœœœ˜'Jšœœœ˜