<> <> <> DIRECTORY CIFS, Environment, File, Process, RefText, Rope, RTTypesBasic, SafeStorage, Space, UFFileManager; UFFileManagerImpl: CEDAR MONITOR IMPORTS CIFS, File, Process, RefText, Rope, RTTypesBasic, SafeStorage, Space EXPORTS UFFileManager = BEGIN OPEN UFFileManager; FontFile: PUBLIC TYPE = REF FontFilePermission; FontFilePermission: PUBLIC TYPE = RECORD [ link: REF FontFilePermission, key: Key ]; qZone: ZONE _ SafeStorage.NewZone[quantized]; <> FontFileDescription: TYPE = RECORD [ permissions: REF FontFilePermission, permissionHint: REF FontFilePermission, clientData: REF _ NIL, openFile: CIFS.OpenFile, errorCode: CIFS.ErrorCode, errorMsg: Rope.ROPE, space: Space.Handle, 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: RTTypesBasic.FinalizationQueue _ RTTypesBasic.NewFQ[permissionThreshold]; FinalizerProcess: PROCEDURE [finalizationQueue: RTTypesBasic.FinalizationQueue] = { DO permission: REF FontFilePermission _ NARROW[RTTypesBasic.FQNext[finalizationQueue]]; key: Key _ permission.key; closed: BOOLEAN _ Finalize[permission]; permission _ NIL; IF closed THEN fontFileWatcherProc[key, close]; IF RTTypesBasic.FQEmpty[finalizationQueue] THEN RequestCollection[]; ENDLOOP; }; Finalize: ENTRY PROCEDURE [permission: REF FontFilePermission] RETURNS [closed: BOOLEAN _ FALSE] = TRUSTED { 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 # NIL THEN { Space.Delete[description.space]; CIFS.Close[description.openFile]; closed _ TRUE; }; keyTable[key.index].description _ NIL; }; permissionCount _ permissionCount-1; }; Init: ENTRY PROCEDURE = TRUSTED { ENABLE UNWIND => NULL; RTTypesBasic.EstablishFinalization[CODE[FontFilePermission], 1, finalizationQueue]; 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 = TRUSTED { ENABLE UNWIND => NULL; IF fileHintTimeoutProcess=NIL THEN { 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[]; }; IF fileHintTimeoutProcess=NIL THEN 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; openOk: BOOLEAN _ TRUE; IF description = NIL THEN { description _ NEW[FontFileDescription]; description.space _ Space.nullHandle; description.openFile _ CIFS.Open[keyTable[key.index].fileName, CIFS.read + CIFS.dontCheck ! CIFS.Error => TRUSTED {openOk _ FALSE; description.errorCode _ code; description.errorMsg _ error; CONTINUE}]; triedToOpen _ TRUE; IF openOk THEN TRUSTED { capability: File.Capability _ CIFS.GetFC[description.openFile]; pages: File.PageCount _ File.GetSize[capability]; description.size _ INT[pages]*Environment.wordsPerPage; description.space _ Space.Create[size: pages, parent: Space.virtualMemory]; Space.Map[space: description.space, window: [file: capability, base: 1]]; Space.CreateUniformSwapUnits[16, description.space]; description.origin _ Space.LongPointer[description.space]; } ELSE description.openFile _ NIL; }; keyTable[key.index].description _ description; fontFile _ qZone.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; }; GetData: PUBLIC PROCEDURE [fontFile: FontFile] RETURNS [REF] = { RETURN[keyTable[fontFile.key.index].description.clientData]; }; Status: PUBLIC PROCEDURE [fontFile: FontFile] RETURNS [errorCode: CIFS.ErrorCode, errorMsg: Rope.ROPE, ok: BOOLEAN] = { description: REF FontFileDescription _ keyTable[fontFile.key.index].description; errorCode _ description.errorCode; errorMsg _ description.errorMsg; ok _ description.openFile # NIL }; 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.