-- DirectoryFilesImpl.mesa (last edited by: Keith on: January 14, 1981 10:38 PM) -- -- Schmidt March 16, 1983 2:15 pm DIRECTORY Ascii USING [BEL], CommonSoftwareFileTypes USING [tDirectory], Directory, DirectoryContexts, DirectoryFiles, DirectoryInternal, DirectoryProps, DirectoryTrees, Environment USING [Byte, wordsPerPage], File USING [ Capability, Create, Delete, GetAttributes, GetSize, LimitPermissions, MakePermanent, nullCapability, PageCount, Permissions, Type, Unknown], Inline USING [LowHalf], LongString USING[EquivalentString], Space USING [ Create, CreateUniformSwapUnits, Delete, ForceOut, Handle, LongPointer, Map, PageCount, Unmap, virtualMemory], Volume USING [ ID, GetAttributes, GetLabelString, GetNext, GetStatus, nullID, Unknown]; DirectoryFilesImpl: MONITOR LOCKS DirectoryLock IMPORTS Directory, DirectoryContexts, DirectoryProps, DirectoryTrees, File, Inline, LongString, Space, Volume EXPORTS Directory, DirectoryInternal, DirectoryFiles SHARES Directory, File = BEGIN DirectoryLock: PUBLIC MONITORLOCK; -- Volume Table -- This table is a cache of the information needed for volumes VolumeTable: TYPE = ARRAY [0..DirectoryFiles.maxDVolumes) OF DirectoryFiles.VTEntry _ ALL[[volName: [length: 0, maxlength: 40, text:], filler: ALL[' ], volID: Volume.nullID, pDT: NIL, dtSpace:]]; volumeTable: PUBLIC LONG POINTER TO VolumeTable; -- Directory Cache -- This cache contains the information on all of the directories currently in use DirectoryCache: TYPE = ARRAY [1..DirectoryFiles.maxDCache] OF DirectoryFiles.DCEntry _ ALL[ [File.nullCapability, 0, 0, DirectoryInternal.pDTnil, [base: NIL, top: DirectoryInternal.nilPagePointer, size: 0, space:]]]; directoryCache: PUBLIC LONG POINTER TO DirectoryCache; -- Contexts Context: PUBLIC TYPE = DirectoryInternal.Context; Handle: TYPE = LONG POINTER TO Context; -- miscellanous type and variable definitions Error: PUBLIC ERROR [type: Directory.ErrorType] = CODE; OutOfBounds: PUBLIC SIGNAL = CODE; VolumeError: PUBLIC ERROR [type: Directory.VolumeErrorType, volume: Volume.ID] = CODE; FieldType: TYPE = {File, Volume, Directory, Empty, Funny}; -- result from Break SearchType: TYPE = {UseWD, UseSP, MustBeQualified}; -- parameter to XlateName -- Valid Character Table ValidChars: TYPE = PACKED ARRAY [0.. 256) OF BOOLEAN; pValidChars: LONG POINTER TO ValidChars; -- GetNext cached directory descriptor gnLastPath: LONG STRING _ NIL; gnLastNameFromPath: LONG STRING _ NIL; gnLastNextName: LONG STRING _ NIL; gnLastNameOfNext: LONG STRING _ NIL; gnLastDD: LONG POINTER TO DirectoryInternal.DirectoryDescriptor _ NIL; -- These variables are used by the entry routines. If the monitor is changed to multiple monitors, beware! nudeName: LONG STRING _ NIL; -- used by entry procs to store unqual name eType: Directory.ErrorType; -- used by entry procedures to save the type of error for return -- entry procedures CreateFile: PUBLIC ENTRY PROC [ fileName: LONG STRING, fileType: File.Type, size: File.PageCount, context: Handle] RETURNS [file: File.Capability] = -- Create and enter a file into the directory BEGIN ENABLE UNWIND => NULL; pDD: LONG POINTER TO DirectoryInternal.DirectoryDescriptor _ NIL; wc, ok, noRoom: BOOLEAN; cardSize: LONG CARDINAL _ size; actualSize: File.PageCount _ cardSize+DirectoryInternal.leaderPageSize; pE: DirectoryInternal.DirectoryEntryHandle; BEGIN ENABLE Directory.Error => {eType _ type; GOTO AnError}; context _ DirectoryContexts.ValidateContext[context]; [pDD: pDD, wc: wc] _ XlateName[fileName, context, nudeName, UseWD]; IF wc OR (nudeName.length = 0) THEN {eType _ invalidFileName; GOTO AnError}; [ok: ok, noRoom: noRoom, ent: pE] _ DirectoryTrees.BTreeInsert[pDD, nudeName, File.nullCapability]; IF noRoom THEN {eType _ notImplemented; GOTO AnError}; IF ~ ok THEN {eType _ fileAlreadyExists; GOTO AnError}; pE.cap _ file _ File.Create[ volume: volumeTable[directoryCache[PDCFromDD[pDD]].pVT].volID, initialSize: actualSize, type: fileType]; Space.ForceOut[pDD.space]; File.MakePermanent[file]; DirectoryProps.SetCreateDate[file, nudeName, directoryCache[PDCFromDD[pDD]].cap]; DoneWith[pDD]; RETURN EXITS AnError => {DoneWith[pDD]; RETURN WITH ERROR Directory.Error[eType]}; END; END; CreateSubdirectory: PUBLIC ENTRY PROC [ parentPathName, subDirectory: LONG STRING] = -- Create a directory in the parentPathName BEGIN ENABLE UNWIND => NULL; pDD: LONG POINTER TO DirectoryInternal.DirectoryDescriptor _ NIL; pPage: DirectoryInternal.DirectoryPageHandle; dirCap: File.Capability _File.nullCapability; pE: DirectoryInternal.DirectoryEntryHandle; dirName: STRING _ [Directory.maxDirectoryNameLength + 1]; -- name with trailing BEL to indicate dir space: Space.Handle; pDC: CARDINAL; ok, noRoom: BOOLEAN; BEGIN ENABLE Directory.Error => {eType _ type; GOTO AnError}; [] _ DirectoryContexts.ValidateContext[Directory.defaultContext]; IF subDirectory = NIL THEN RETURN WITH ERROR Error[invalidFileName]; IF subDirectory.length = 0 THEN RETURN WITH ERROR Error[invalidFileName]; DirectoryTrees.MoveLongString[to: dirName, from: subDirectory]; dirName[dirName.length] _ Ascii.BEL; dirName.length _ dirName.length + 1; FOR i: CARDINAL IN [0..subDirectory.length) DO IF ~CheckChar[subDirectory[i]] THEN RETURN WITH ERROR Error[invalidFileName]; ENDLOOP; pDD _ XlateName[parentPathName, NIL, nudeName, MustBeQualified].pDD; IF nudeName.length # 0 THEN {eType _ invalidPathName; GOTO AnError}; [ok: ok, noRoom: noRoom, ent: pE] _ DirectoryTrees.BTreeInsert[pDD, dirName, dirCap]; IF noRoom THEN {eType _ notImplemented; GOTO AnError}; IF ~ ok THEN {eType _ fileAlreadyExists; GOTO AnError}; pDC _ PDCFromDD[pDD]; pE.cap _ dirCap _ File.Create[ volumeTable[directoryCache[pDC].pVT].volID, DirectoryInternal.initDirSize, CommonSoftwareFileTypes.tDirectory]; File.MakePermanent[dirCap]; Space.ForceOut[pDD.space]; space _ Space.Create[DirectoryInternal.initDirSize, Space.virtualMemory]; Space.Map[space, [dirCap, DirectoryInternal.leaderPageSize]]; pPage _ Space.LongPointer[space]; -- Initialize the first page pPage.parent _ pPage.lastPointer _ DirectoryInternal.nilPagePointer; pPage.size _ DirectoryInternal.emptySize; pPage.free _ FALSE; pPage.top _ FIRST[DirectoryInternal.PagePointer]; pPage _ pPage + Environment.wordsPerPage; -- mark the rest of the pages as free FOR x: File.PageCount _ DirectoryInternal.leaderPageSize+1, x + 1 UNTIL x = DirectoryInternal.initDirSize DO pPage.free _ TRUE; pPage _ pPage + Environment.wordsPerPage; ENDLOOP; Space.Unmap[space]; Space.Delete[space]; DirectoryProps.SetCreateDate[dirCap, subDirectory, directoryCache[pDC].cap]; IF ~DirectoryTrees.DTInsert[ volumeTable[directoryCache[pDC].pVT].pDT, directoryCache[pDC].pDTNode, subDirectory, dirCap].ok THEN RETURN WITH ERROR Directory.VolumeError[directoryNeedsScavenging, volumeTable[directoryCache[pDC].pVT].volID]; Space.ForceOut[volumeTable[directoryCache[pDC].pVT].dtSpace]; DoneWith[pDD]; RETURN EXITS AnError => {DoneWith[pDD]; RETURN WITH ERROR Directory.Error[eType]}; END END; DeleteFile: PUBLIC ENTRY PROC [fileName: LONG STRING, context: Handle] = -- Delete and remove a file from the directory BEGIN ENABLE UNWIND => NULL; pDD: LONG POINTER TO DirectoryInternal.DirectoryDescriptor _ NIL; file: File.Capability; exists, sd, wc: BOOLEAN; BEGIN ENABLE Directory.Error => {eType _ type; GOTO AnError}; context _ DirectoryContexts.ValidateContext[context]; [pDD: pDD, wc: wc] _ XlateName[fileName, context, nudeName, UseWD]; IF wc OR (nudeName.length = 0) THEN {eType _ invalidFileName; GOTO AnError}; [ok: exists, cap: file, isSD: sd] _ DirectoryTrees.BTreeFind[pDD, nudeName]; IF ~exists THEN {eType _ fileNotFound; GOTO AnError}; IF sd THEN {eType _ fileIsSD; GOTO AnError}; File.Delete[file]; [] _ DirectoryTrees.BTreeDelete[pDD, nudeName, FALSE]; Space.ForceOut[pDD.space]; DoneWith[pDD]; RETURN EXITS AnError => {DoneWith[pDD]; RETURN WITH ERROR Directory.Error[eType]}; END; END; DeleteSubdirectory: PUBLIC ENTRY PROC [ parentPathName, subDirectory: LONG STRING] = -- Delete an empty directory and remove from D-Tree. Flush cache entry if one exists BEGIN ENABLE UNWIND => NULL; parentDD: LONG POINTER TO DirectoryInternal.DirectoryDescriptor _ NIL; sdDD: LONG POINTER TO DirectoryInternal.DirectoryDescriptor _ NIL; sdWithBEL: STRING _ [Directory.maxDirectoryNameLength+1]; sdDC, parentDC: CARDINAL; sdCap: File.Capability; ok, isSD: BOOLEAN; BEGIN ENABLE Directory.Error => {eType _ type; GOTO AnError}; IF subDirectory = NIL THEN {eType _ invalidFileName; GOTO AnError}; parentDD _ XlateName[parentPathName, NIL, nudeName, MustBeQualified].pDD; IF nudeName.length # 0 THEN {eType _ invalidPathName; GOTO AnError}; parentDC _ PDCFromDD[parentDD]; [ok: ok, cap: sdCap, isSD: isSD] _ DirectoryTrees.BTreeFind[parentDD, subDirectory]; IF ~ ok THEN {eType _ fileNotFound; GOTO AnError}; IF ~ isSD THEN {eType _ fileNotSD; GOTO AnError}; sdDC _ GetDirectoryCache[sdCap]; sdDD _ @directoryCache[sdDC].dir; IF ~ DirectoryTrees.BTreeEmpty[sdDD] THEN {eType _ directoryNotEmpty; GOTO AnError}; DirectoryTrees.MoveLongString[to: sdWithBEL, from: subDirectory]; sdWithBEL[sdWithBEL.length] _ Ascii.BEL; sdWithBEL.length _ sdWithBEL.length+1; [] _ DirectoryTrees.BTreeDelete[parentDD, sdWithBEL, TRUE]; Space.ForceOut[parentDD.space]; ok _ DirectoryTrees.DTDelete[volumeTable[directoryCache[parentDC].pVT].pDT, directoryCache[parentDC].pDTNode, subDirectory]; IF ~ ok THEN { DoneWith[parentDD]; DoneWith[sdDD]; RETURN WITH ERROR Directory.VolumeError[ directoryNeedsScavenging, volumeTable[directoryCache[parentDC].pVT].volID]}; Space.ForceOut[volumeTable[directoryCache[parentDC].pVT].dtSpace]; IF directoryCache[sdDC].refCount > 1 THEN { -- this directory is referenced somewhere in contexts... set bad DirectoryContexts.InvalidateContext[sdDC]; directoryCache[sdDC].refCount _ 1}; DoneWith[sdDD]; File.Delete[sdCap]; DoneWith[parentDD]; RETURN EXITS AnError => {DoneWith[parentDD]; DoneWith[sdDD]; RETURN WITH ERROR Directory.Error[eType]}; END; END; GetNext: PUBLIC ENTRY PROC [pathName, currentName, nextName: LONG STRING] RETURNS [file: File.Capability] = -- Stateless Directory Enumerator BEGIN ENABLE UNWIND => NULL; nameFromPath: STRING _ [Directory.maxDirectoryNameLength]; nameFromCurrent: STRING _ [Directory.maxDirectoryNameLength]; nameOfNext: STRING _ [Directory.maxDirectoryNameLength]; dirOfCurrent: LONG POINTER TO DirectoryInternal.DirectoryDescriptor _ NIL; dirOfPath: LONG POINTER TO DirectoryInternal.DirectoryDescriptor _ NIL; i, j, k: CARDINAL; wcCurrent: BOOLEAN; context: Handle; BEGIN ENABLE Directory.Error => {eType _ type; GOTO AnError}; IF (currentName = NIL) OR (nextName = NIL) THEN RETURN WITH ERROR Directory.Error[invalidFileName]; IF pathName = NIL THEN RETURN WITH ERROR Directory.Error[invalidPathName]; IF (gnLastDD # NIL) AND (LongString.EquivalentString[pathName, gnLastPath] ) AND (LongString.EquivalentString[currentName, gnLastNextName]) THEN { -- use cached info from last time dirOfCurrent _ NIL; dirOfPath _ gnLastDD; i _ PDCFromDD[dirOfPath]; directoryCache[i].refCount _ directoryCache[i].refCount + 1; DirectoryTrees.MoveLongString[to: nameFromPath, from: gnLastNameFromPath]; DirectoryTrees.MoveLongString[to: nameFromCurrent, from: gnLastNameOfNext]; IF nameFromCurrent[nameFromCurrent.length-1] = '> THEN nameFromCurrent[nameFromCurrent.length-1] _ Ascii.BEL} ELSE { -- must translate the names and do some checking context _ DirectoryContexts.ValidateContext[Directory.defaultContext]; IF pathName.length = 0 THEN dirOfPath _ XlateName["*", context, nameFromPath, UseWD].pDD ELSE dirOfPath _ XlateName[pathName, context, nameFromPath, UseWD].pDD; IF currentName.length # 0 THEN { IF currentName[currentName.length - 1] = '> THEN currentName[currentName.length - 1] _ Ascii.BEL; [pDD: dirOfCurrent, wc: wcCurrent] _ XlateName[ currentName, context, nameFromCurrent, UseWD]} ELSE { wcCurrent _ FALSE; dirOfCurrent _ dirOfPath; i _ PDCFromDD[dirOfPath]; directoryCache[i].refCount _ directoryCache[i].refCount + 1}; IF wcCurrent THEN {eType _ invalidFileName; GOTO AnError}; IF dirOfCurrent.base # dirOfPath.base THEN GOTO LastExit}; file _ DirectoryTrees.BTreeGetNext[dirOfPath, nameFromCurrent, nameOfNext, nameFromPath]; IF nameOfNext.length = 0 THEN GOTO LastExit; IF nameOfNext[nameOfNext.length - 1] = Ascii.BEL THEN nameOfNext[nameOfNext.length - 1] _ '>; IF (pathName[0] = '<) AND (pathName.length # 0) THEN { -- name can be qualified by supplied path name FOR j DECREASING IN [0..pathName.length) DO IF pathName[j] = '> THEN EXIT ENDLOOP; FOR i IN [0..j] DO IF i = nextName.maxlength THEN {eType _ stringTooShort; GOTO AnError}; nextName[i] _ pathName[i]; ENDLOOP; j _ j + 1; FOR i IN [0..nameOfNext.length) DO IF i + j = nextName.maxlength THEN {eType _ stringTooShort; GOTO AnError}; nextName[i + j] _ nameOfNext[i]; ENDLOOP; nextName.length _ j + nameOfNext.length} ELSE { -- name must be qualified by default working directory name DirectoryTrees.MoveLongString[ to: nextName, from: @DirectoryContexts.contextTable[0].wdName]; FOR i IN [0..pathName.length) DO IF pathName[i] = '> THEN EXIT ENDLOOP; i _ i + 1; FOR k DECREASING IN [0..pathName.length) DO IF pathName[k] = '> THEN EXIT ENDLOOP; FOR j IN [nextName.length..nextName.maxlength) DO IF i > k THEN EXIT; nextName[j] _ pathName[i]; i _ i + 1 REPEAT FINISHED => {eType _ stringTooShort; GOTO AnError} ENDLOOP; FOR i IN [j..nextName.maxlength) DO IF i - j = nameOfNext.length THEN EXIT; nextName[i] _ nameOfNext[i - j]; REPEAT FINISHED => {eType _ stringTooShort; GOTO AnError} ENDLOOP; nextName.length _ i; }; -- cache some state information for next time around DirectoryTrees.MoveLongString[to: gnLastNameOfNext, from: nameOfNext]; DirectoryTrees.MoveLongString[to: gnLastPath, from: pathName]; DirectoryTrees.MoveLongString[to: gnLastNameFromPath, from: nameFromPath]; DirectoryTrees.MoveLongString[to: gnLastNextName, from: nextName]; IF gnLastDD # dirOfPath THEN { DoneWith[gnLastDD]; i _ PDCFromDD[dirOfPath]; directoryCache[i].refCount _ directoryCache[i].refCount+1; gnLastDD _ dirOfPath}; DoneWith[dirOfPath]; DoneWith[dirOfCurrent]; RETURN EXITS AnError => { DoneWith[gnLastDD]; gnLastDD _ NIL; DoneWith[dirOfPath]; DoneWith[dirOfCurrent]; RETURN WITH ERROR Directory.Error[eType]}; LastExit => { DoneWith[gnLastDD]; gnLastDD _ NIL; DoneWith[dirOfPath]; DoneWith[dirOfCurrent]; nextName.length _ 0; RETURN[File.nullCapability]}; END; END; InsertFile: PUBLIC ENTRY PROC [ fileName: LONG STRING, file: File.Capability, context: Handle] = -- Insert capability/file name pair into directory BEGIN ENABLE UNWIND => NULL; pDD: LONG POINTER TO DirectoryInternal.DirectoryDescriptor _ NIL; wc, noRoom ,ok: BOOLEAN; BEGIN ENABLE Directory.Error => {eType _ type; GOTO AnError}; context _ DirectoryContexts.ValidateContext[context]; [pDD: pDD, wc: wc] _ XlateName[fileName, context, nudeName, UseWD]; IF wc OR (nudeName.length = 0) THEN {eType _ invalidFileName; GOTO AnError}; [ok: ok, noRoom: noRoom] _ DirectoryTrees.BTreeInsert[pDD, nudeName, file]; IF noRoom THEN {eType _ notImplemented; GOTO AnError}; IF ~ ok THEN {eType _ fileAlreadyExists; GOTO AnError}; Space.ForceOut[pDD.space]; DoneWith[pDD]; RETURN EXITS AnError => {DoneWith[pDD]; RETURN WITH ERROR Directory.Error[eType]}; END; END; LimitPermissions: PUBLIC ENTRY PROC [ fileName: LONG STRING, permissions: File.Permissions, context: Handle] = -- Limit the capability in the directory with the passed permissions BEGIN ENABLE UNWIND => NULL; ok, sd, wc: BOOLEAN; pDD: LONG POINTER TO DirectoryInternal.DirectoryDescriptor _ NIL; pDE: DirectoryInternal.DirectoryEntryHandle; file: File.Capability; BEGIN ENABLE Directory.Error => {eType _ type; GOTO AnError}; context _ DirectoryContexts.ValidateContext[context]; [pDD: pDD, wc: wc] _ XlateName[fileName, context, nudeName, UseWD]; IF wc THEN {eType _ invalidFileName; GOTO AnError}; [ok: ok, cap: file, isSD: sd, ent: pDE] _ DirectoryTrees.BTreeFind[ pDD, nudeName]; IF sd THEN {eType _ fileIsSD; GOTO AnError}; IF ~ok THEN {eType _ fileNotFound; GOTO AnError}; file _ File.LimitPermissions[file, permissions]; pDE.cap _ file; Space.ForceOut[pDD.space]; DoneWith[pDD]; RETURN EXITS AnError => {DoneWith[pDD]; RETURN WITH ERROR Directory.Error[eType]}; END; END; Lookup: PUBLIC ENTRY PROC [ fileName: LONG STRING, context: Handle, permissions: File.Permissions] RETURNS [file: File.Capability] = -- Return a file's capability given its name. Update the dates as necessary after limiting the permissions. BEGIN ENABLE UNWIND => NULL; ok, sd, wc: BOOLEAN; pDD: LONG POINTER TO DirectoryInternal.DirectoryDescriptor _ NIL; pSP: LONG POINTER TO DirectoryInternal.SPRecord _ NIL; badVol: Volume.ID; BEGIN ENABLE Directory.Error => {eType _ type; GOTO AnError}; context _ DirectoryContexts.ValidateContext[context]; DO [pDD: pDD, next: pSP, wc: wc] _ XlateName[ fileName, context, nudeName, UseSP, pSP]; IF wc OR (nudeName.length = 0) THEN {eType _ invalidFileName; GOTO AnError}; [ok: ok, cap: file, isSD: sd] _ DirectoryTrees.BTreeFind[pDD, nudeName]; IF sd THEN {eType _ fileIsSD; GOTO AnError}; IF ok OR (pSP = NIL) THEN EXIT; DoneWith[pDD]; ENDLOOP; IF ~ok THEN {eType _ fileNotFound; GOTO AnError}; IF permissions # Directory.ignore THEN { file _ File.LimitPermissions[file, permissions]; DirectoryProps.SetTimes[file ! Directory.Error => IF type = fileNotFound THEN { badVol _ volumeTable[directoryCache[PDCFromDD[pDD]].pVT].volID; DoneWith[pDD]; GOTO AVolumeError} ]}; DoneWith[pDD]; RETURN EXITS AnError => {DoneWith[pDD]; RETURN WITH ERROR Directory.Error[eType]}; AVolumeError => RETURN WITH ERROR Directory.VolumeError[directoryNeedsScavenging, badVol]; END; END; LookupUnlimited: PUBLIC ENTRY PROC [fileName: LONG STRING, context: Handle] RETURNS [file: File.Capability] = -- Return a file's capability given its name. Update the dates as necessary. BEGIN ENABLE UNWIND => NULL; ok, sd, wc: BOOLEAN; pDD: LONG POINTER TO DirectoryInternal.DirectoryDescriptor _ NIL; pSP: LONG POINTER TO DirectoryInternal.SPRecord _ NIL; badVol: Volume.ID; BEGIN ENABLE Directory.Error => {eType _ type; GOTO AnError}; context _ DirectoryContexts.ValidateContext[context]; DO [pDD: pDD, next: pSP, wc: wc] _ XlateName[ fileName, context, nudeName, UseSP, pSP]; IF wc OR (nudeName.length = 0) THEN {eType _ invalidFileName; GOTO AnError}; [ok: ok, cap: file, isSD: sd] _ DirectoryTrees.BTreeFind[pDD, nudeName]; IF sd THEN {eType _ fileIsSD; GOTO AnError}; IF ok OR (pSP = NIL) THEN EXIT; DoneWith[pDD]; ENDLOOP; IF ~ok THEN {eType _ fileNotFound; GOTO AnError}; DirectoryProps.SetTimes[file ! Directory.Error => IF type = fileNotFound THEN { badVol _ volumeTable[directoryCache[PDCFromDD[pDD]].pVT].volID; DoneWith[pDD]; GOTO AVolumeError} ]; DoneWith[pDD]; RETURN EXITS AnError => {DoneWith[pDD]; RETURN WITH ERROR Directory.Error[eType]}; AVolumeError => RETURN WITH ERROR Directory.VolumeError[directoryNeedsScavenging, badVol]; END; END; ModifyContext: PUBLIC ENTRY PROC [ context: Handle, pathName: LONG STRING, sP: Directory.SearchPath] = -- Changes the values of a context BEGIN ENABLE { UNWIND => NULL; Directory.Error => {eType _ type; GOTO AnError}}; pDD: LONG POINTER TO DirectoryInternal.DirectoryDescriptor; wc: BOOLEAN; emptyName: STRING _ [Directory.maxDirectoryNameLength]; pSP, nextSP: LONG POINTER TO DirectoryInternal.SPRecord; IF context = Directory.defaultContext THEN context _ DirectoryContexts.contextTable[0]; [] _ DirectoryContexts.ValidateContext[context ! Directory.Error => IF type = fileNotFound THEN CONTINUE]; IF pathName # NIL THEN { -- change working directory [pDD: pDD, wc: wc] _ XlateName[pathName, NIL, emptyName, MustBeQualified]; IF wc OR (emptyName.length # 0) THEN { DoneWith[pDD]; eType _ invalidFileName; GOTO AnError}; DirectoryTrees.MoveLongString[from: pathName, to: @context.wdName]; DoneWith[gnLastDD]; -- invalidate cached information for GetNext gnLastDD _ NIL; IF context.valid THEN FlushCache[context.pDC]; context.valid _ TRUE; context.pDC _ PDCFromDD[pDD]}; IF sP # NIL THEN { -- change search path IF LENGTH[sP] = 0 THEN RETURN WITH ERROR Directory.Error[invalidSearchPath]; IF sP[0].length = 0 THEN RETURN WITH ERROR Directory.Error[invalidSearchPath]; pSP _ context.pSP; WHILE pSP # NIL DO nextSP _ pSP.pSP; IF pSP.defined THEN FlushCache[pSP.pDC]; DirectoryContexts.ContextSpace.FREE[@pSP]; pSP _ nextSP; ENDLOOP; FOR i: CARDINAL IN [0..LENGTH[sP]) DO IF sP[i].length = 0 THEN EXIT; SELECT Break[sP[i], emptyName, 0].field FROM = Directory => nextSP _ DirectoryContexts.ContextSpace.NEW[ withWD DirectoryInternal.SPRecord]; ENDCASE => nextSP _ DirectoryContexts.ContextSpace.NEW[ noWD DirectoryInternal.SPRecord]; WITH sp: nextSP^ SELECT FROM noWD => NULL; withWD => {sp.pWD _ context; sp.cap _ File.nullCapability}; ENDCASE; nextSP.spName _ [length: 0, maxlength: Directory.maxPathNameLength, text:]; DirectoryTrees.MoveLongString[to: @nextSP.spName, from: sP[i]]; nextSP.pSP _ NIL; nextSP.pDC _ 0; nextSP.defined _ FALSE; IF i = 0 THEN context.pSP _ nextSP ELSE pSP.pSP _ nextSP; pSP _ nextSP ENDLOOP}; EXITS AnError => RETURN WITH ERROR Directory.Error[eType]; END; RemoveFile: PUBLIC ENTRY PROC [ fileName: LONG STRING, file: File.Capability, context: Directory.Handle] = -- Removes a file/capability pair from the directory BEGIN ENABLE UNWIND => NULL; pDD: LONG POINTER TO DirectoryInternal.DirectoryDescriptor _ NIL; actualFile: File.Capability; exists, sd, wc: BOOLEAN; BEGIN ENABLE Directory.Error => {eType _ type; GOTO AnError}; context _ DirectoryContexts.ValidateContext[context]; [pDD: pDD, wc: wc] _ XlateName[fileName, context, nudeName, UseWD]; IF wc OR (nudeName.length = 0) THEN {eType _ invalidFileName; GOTO AnError}; [ok: exists, cap: actualFile, isSD: sd] _ DirectoryTrees.BTreeFind[pDD, nudeName]; IF ~exists OR (file # actualFile) THEN {eType _ fileNotFound; GOTO AnError}; IF sd THEN {eType _ fileIsSD; GOTO AnError}; [] _ DirectoryTrees.BTreeDelete[pDD, nudeName, FALSE]; Space.ForceOut[pDD.space]; DoneWith[pDD]; RETURN EXITS AnError => {DoneWith[pDD]; RETURN WITH ERROR Directory.Error[eType]}; END; END; Rename: PUBLIC ENTRY PROC [ oldName, newName: LONG STRING, context: Directory.Handle] = -- Renames a file leaving the capability unchanged BEGIN ENABLE UNWIND => NULL; oldDD: LONG POINTER TO DirectoryInternal.DirectoryDescriptor _ NIL; newDD: LONG POINTER TO DirectoryInternal.DirectoryDescriptor _ NIL; newNudeName: STRING _ [Directory.maxDirectoryNameLength+1]; cap: File.Capability; wc, ok: BOOLEAN; pDCOld, pDCNew: CARDINAL; pDT: DirectoryInternal.PDT; pDTNode: DirectoryInternal.PDTNode; BEGIN ENABLE Directory.Error => {eType _ type; GOTO AnError}; context _ DirectoryContexts.ValidateContext[context]; [pDD: oldDD, wc: wc] _ XlateName[oldName, context, nudeName, UseWD]; IF wc OR (nudeName.length = 0) THEN {eType _ invalidFileName; GOTO AnError}; [pDD: newDD, wc: wc] _ XlateName[newName, context, newNudeName, UseWD]; IF wc OR (newNudeName.length = 0) THEN {eType _ invalidFileName; GOTO AnError}; IF DirectoryTrees.BTreeFind[newDD, newNudeName].ok THEN {eType _ fileAlreadyExists; GOTO AnError}; [ok: ok, cap: cap] _ DirectoryTrees.BTreeDelete[oldDD, nudeName, FALSE]; IF ok THEN -- renaming a normal file [] _ DirectoryTrees.BTreeInsert[newDD, newNudeName, cap] ELSE { -- renaming a subdirectory IF ~ DirectoryTrees.BTreeFind[oldDD, nudeName].ok THEN {eType _ fileNotFound; GOTO AnError}; pDCOld _ PDCFromDD[oldDD]; pDCNew _ PDCFromDD[newDD]; pDT _ volumeTable[directoryCache[pDCOld].pVT].pDT; IF pDT # volumeTable[directoryCache[pDCNew].pVT].pDT THEN {eType _ invalidPathName; GOTO AnError}; pDTNode _ DirectoryTrees.DTFind[pDT, directoryCache[pDCOld].pDTNode, nudeName]; DirectoryTrees.DTMoveNode[ pDT: pDT, pDTNode1: pDTNode, pDTNode2: directoryCache[pDCNew].pDTNode]; DirectoryTrees.MoveLongString[from: newNudeName, to: @pDT[pDTNode].name]; nudeName[nudeName.length] _ Ascii.BEL; nudeName.length _ nudeName.length+1; newNudeName[newNudeName.length] _ Ascii.BEL; newNudeName.length _ newNudeName.length+1; [cap: cap] _ DirectoryTrees.BTreeDelete[oldDD, nudeName, TRUE]; [] _ DirectoryTrees.BTreeInsert[newDD, newNudeName, cap]; Space.ForceOut[volumeTable[directoryCache[pDCOld].pVT].dtSpace]; newNudeName.length _ newNudeName.length-1}; DirectoryProps.SetNameAndCap[cap, newNudeName, directoryCache[PDCFromDD[newDD]].cap]; Space.ForceOut[oldDD.space]; IF oldDD.base # newDD.base THEN Space.ForceOut[newDD.space]; DoneWith[oldDD]; DoneWith[newDD]; RETURN EXITS AnError => {DoneWith[oldDD]; DoneWith[newDD]; RETURN WITH ERROR Directory.Error[eType]}; END; END; SetPermissions: PUBLIC ENTRY PROC [ fileName: LONG STRING, file: File.Capability, context: Directory.Handle] = -- Sets the permissions in the capability in the directory BEGIN ENABLE UNWIND => NULL; ok, sd, wc: BOOLEAN; pDD: LONG POINTER TO DirectoryInternal.DirectoryDescriptor _ NIL; pDE: DirectoryInternal.DirectoryEntryHandle; BEGIN ENABLE Directory.Error => {eType _ type; GOTO AnError}; context _ DirectoryContexts.ValidateContext[context]; [pDD: pDD, wc: wc] _ XlateName[fileName, context, nudeName, UseWD]; IF wc THEN {eType _ invalidFileName; GOTO AnError}; [ok: ok, isSD: sd, ent: pDE] _ DirectoryTrees.BTreeFind[pDD, nudeName]; IF sd THEN {eType _ fileIsSD; GOTO AnError}; IF ~ok OR (file.fID # pDE.cap.fID) THEN {eType _ fileNotFound; GOTO AnError}; pDE.cap.permissions _ file.permissions; Space.ForceOut[pDD.space]; DoneWith[pDD]; RETURN EXITS AnError => {DoneWith[pDD]; RETURN WITH ERROR Directory.Error[eType]}; END; END; -- internal procedures Break: INTERNAL PROC [ls, s: LONG STRING, position: CARDINAL] RETURNS [new: CARDINAL, field: FieldType, wildCard: BOOLEAN] = -- Breaks a file name into left component starting at position BEGIN wildCard _ FALSE; s.length _ 0; IF ls.length <= position THEN RETURN[position, Empty, wildCard]; IF ls[position] = '< THEN { FOR i: CARDINAL IN [0..ls.length - position - 1) DO IF ls[i + position + 1] = '> THEN { position _ position + i + 2; field _ Volume; EXIT} ELSE { IF WildCard[ls[i + position + 1]] THEN wildCard _ TRUE ELSE IF ~CheckChar[ls[i + position + 1]] THEN GOTO BadChar; s[i] _ ls[i + position + 1]; s.length _ s.length + 1}; REPEAT BadChar => {field _ Funny; position _ ls.length}; FINISHED => {field _ Funny; position _ ls.length}; ENDLOOP; RETURN[position, field, wildCard]} ELSE { FOR i: CARDINAL IN [0..ls.length - position) DO IF ls[i + position] = '> THEN { position _ position + i + 1; field _ Directory; EXIT} ELSE { IF WildCard[ls[i + position]] THEN wildCard _ TRUE ELSE IF ~CheckChar[ls[i + position]] THEN GOTO BadChar; s[i] _ ls[i + position]; s.length _ s.length + 1}; REPEAT BadChar => {field _ Funny; position _ ls.length}; FINISHED => {field _ File; position _ ls.length}; ENDLOOP; RETURN[position, field, wildCard]}; END; CheckChar: INTERNAL PROC [c: CHARACTER] RETURNS [BOOLEAN] = -- test if a character is valid for inclusion in a file name INLINE {RETURN[pValidChars[LOOPHOLE[c]]]}; DoneWith: PUBLIC INTERNAL PROC [ pDD: LONG POINTER TO DirectoryInternal.DirectoryDescriptor] = -- flush the directory cache entry of a directory descriptor BEGIN IF pDD # NIL THEN FlushCache[PDCFromDD[pDD]]; RETURN END; FlushCache: INTERNAL PROC [pDC: CARDINAL] = -- Decrement the reference count on the cache entry. If zero, remove entry BEGIN space: Space.Handle _ directoryCache[pDC].dir.space; directoryCache[pDC].refCount _ directoryCache[pDC].refCount - 1; IF directoryCache[pDC].refCount > 0 THEN RETURN; Space.Unmap[space]; Space.Delete[space]; END; GetDirectoryCache: PUBLIC INTERNAL PROC [cap: File.Capability] RETURNS [pDC: CARDINAL] = -- returns a "pointer" to the cache entry of a capability, doing the load if necessary BEGIN dirSize: Space.PageCount; dirSpace: Space.Handle; dirPage: DirectoryInternal.DirectoryPageHandle; pFreeDC: CARDINAL _ DirectoryFiles.maxDCache + 1; FOR pDC IN (0..DirectoryFiles.maxDCache] DO IF directoryCache[pDC].refCount = 0 THEN pFreeDC _ pDC ELSE IF directoryCache[pDC].cap = cap THEN { directoryCache[pDC].refCount _ directoryCache[pDC].refCount + 1; RETURN[pDC]}; ENDLOOP; IF pFreeDC = DirectoryFiles.maxDCache + 1 THEN ERROR; IF ~ DirectoryProps.ValidLP[cap] THEN { vol: Volume.ID; {ENABLE File.Unknown => {vol _ Volume.nullID; CONTINUE}; vol _ File.GetAttributes[cap].volume; ERROR Directory.VolumeError[directoryNeedsScavenging, vol]}}; pDC _ pFreeDC; directoryCache[pDC].cap _ cap; directoryCache[pDC].refCount _ 1; directoryCache[pDC].pVT _ GetVTEntryFromID[File.GetAttributes[cap].volume]; directoryCache[pDC].pDTNode _ GetPDTNode[ volumeTable[directoryCache[pDC].pVT].pDT, cap]; dirSize _ Inline.LowHalf[File.GetSize[cap]]; dirSpace _ Space.Create[DirectoryInternal.maxDirSize, Space.virtualMemory]; Space.CreateUniformSwapUnits[DirectoryInternal.swapUnitSize, dirSpace]; Space.Map[dirSpace, [cap, DirectoryInternal.leaderPageSize]]; dirPage _ Space.LongPointer[dirSpace]; directoryCache[pDC].dir _ [base: dirPage, top: dirPage.top, size: dirSize - DirectoryInternal.leaderPageSize, space: dirSpace]; RETURN[pDC]; END; GetPDTNode: INTERNAL PROC [pDT: DirectoryInternal.PDT, cap: File.Capability] RETURNS [pDTNode: DirectoryInternal.PDTNode] = -- returns a pointer to the Directory Tree entry of a directory capability BEGIN pDTNode _ pDT.son; IF cap = pDT[pDTNode].cap THEN RETURN[pDTNode]; UNTIL pDTNode = DirectoryInternal.pDTnil DO pDTNode _ DirectoryTrees.PDTNext[pDT, pDTNode]; IF pDT[pDTNode].cap = cap THEN RETURN[pDTNode]; IF pDT[pDTNode].level = 0 THEN ERROR Directory.VolumeError[directoryNeedsScavenging, File.GetAttributes[cap].volume]; ENDLOOP; END; GetVTEntry: INTERNAL PROC [name: LONG STRING] RETURNS [pVT: CARDINAL] = -- returns a "pointer" to the volume table entry of a volume label string, doing the load if necessary BEGIN vol: Volume.ID _ Volume.nullID; volLabel: STRING _ [40]; pFreeVT: CARDINAL _ DirectoryFiles.maxDVolumes; treeCap: File.Capability; isTree: BOOLEAN; dirSize, treeSize: Space.PageCount; dirSpace, treeSpace: Space.Handle; dirPage: DirectoryInternal.DirectoryPageHandle; pDC: CARDINAL _ DirectoryFiles.maxDCache; -- get cached entry FOR pVT IN [0..DirectoryFiles.maxDVolumes) DO IF LongString.EquivalentString[name, @volumeTable[pVT].volName] THEN RETURN[pVT]; IF volumeTable[pVT].pDT = NIL THEN pFreeVT _ pVT; ENDLOOP; -- not in table: do load DO vol _ Volume.GetNext[vol, [TRUE, TRUE, TRUE, FALSE]]; IF vol = Volume.nullID THEN ERROR Directory.Error[volumeNotFound]; Volume.GetLabelString[vol, volLabel]; IF LongString.EquivalentString[name, volLabel] THEN EXIT; ENDLOOP; IF pFreeVT = DirectoryFiles.maxDVolumes THEN ERROR; pVT _ pFreeVT; volumeTable[pVT].volID _ vol; DirectoryTrees.MoveLongString[to: @volumeTable[pVT].volName, from: volLabel]; -- load the directory cache with the root directory of this volume -- cannot use GetDirectoryCache here since it assumes a valid volume table entry FOR pDC IN (0..DirectoryFiles.maxDCache] DO IF directoryCache[pDC].refCount = 0 THEN EXIT; REPEAT FINISHED => ERROR; ENDLOOP; directoryCache[pDC].cap _ Volume.GetAttributes[vol].rootFile; IF directoryCache[pDC].cap = File.nullCapability THEN {volumeTable[pVT].volName.length _ 0; ERROR Directory.VolumeError[rootNotFound, vol]}; IF Volume.GetStatus[vol] # open THEN {volumeTable[pVT].volName.length _ 0; ERROR Directory.VolumeError[volumeNotOpen, vol]}; IF ~ DirectoryProps.ValidLP[directoryCache[pDC].cap] THEN {volumeTable[pVT].volName.length _ 0; ERROR Directory.VolumeError[directoryNeedsScavenging, vol]}; directoryCache[pDC].pVT _ pVT; dirSize _ Inline.LowHalf[File.GetSize[directoryCache[pDC].cap]]; dirSpace _ Space.Create[DirectoryInternal.maxDirSize, Space.virtualMemory]; Space.CreateUniformSwapUnits[DirectoryInternal.swapUnitSize, dirSpace]; Space.Map[dirSpace, [directoryCache[pDC].cap, DirectoryInternal.leaderPageSize]]; directoryCache[pDC].refCount _ 1; dirPage _ Space.LongPointer[dirSpace]; directoryCache[pDC].dir _ [base: dirPage, top: dirPage.top, size: dirSize - DirectoryInternal.leaderPageSize, space: dirSpace]; [ok: isTree, cap: treeCap] _ DirectoryTrees.BTreeFind[ @directoryCache[pDC].dir, "Volume.DirectoryTree"L]; IF ~isTree THEN { Space.Unmap[dirSpace]; Space.Delete[dirSpace]; volumeTable[pVT].volName.length _ 0; ERROR Directory.VolumeError[directoryNeedsScavenging, vol]}; treeSize _ Inline.LowHalf[File.GetSize[treeCap]]; volumeTable[pVT].dtSpace _ treeSpace _ Space.Create[DirectoryInternal.maxDirSize, Space.virtualMemory]; treeCap.permissions _ Directory.fileMaxPermissions; Space.Map[treeSpace, [treeCap, DirectoryInternal.leaderPageSize]]; volumeTable[pVT].pDT _ Space.LongPointer[treeSpace]; directoryCache[pDC].pDTNode _ DirectoryTrees.DTFirst[volumeTable[pVT].pDT]; RETURN[pVT]; END; GetVTEntryFromID: PUBLIC INTERNAL PROC [vol: Volume.ID] RETURNS [pVT: CARDINAL] = -- returns a "pointer" to the volume table entry of a volume ID, doing the load if necessary -- this is a code-saving slow way to do this, but it is only used rarely BEGIN name: STRING _ [40]; Volume.GetLabelString[ vol, name ! Volume.Unknown => ERROR Error[volumeNotFound]]; RETURN[GetVTEntry[name]]; END; PDCFromDD: PUBLIC INTERNAL PROC [pDD: LONG POINTER TO DirectoryInternal.DirectoryDescriptor] RETURNS [pDC: CARDINAL] = -- returns the directory cache entry of a directory descriptor -- note the reference count is unchanged since to get the descriptor we went to the cache! BEGIN FOR pDC DECREASING IN (0..DirectoryFiles.maxDCache] DO IF (directoryCache[pDC].refCount > 0) AND (directoryCache[pDC].dir.base = pDD.base) THEN RETURN[pDC] ENDLOOP; ERROR; END; ValidateSP: INTERNAL PROC [pSP: LONG POINTER TO DirectoryInternal.SPRecord] RETURNS [LONG POINTER TO DirectoryInternal.SPRecord] = -- ensures that the search path entry is current with the working directory BEGIN emptyName: STRING _ [Directory.maxDirectoryNameLength]; WITH sp: pSP^ SELECT FROM noWD => IF sp.defined THEN RETURN[pSP] ELSE { sp.pDC _ PDCFromDD[ XlateName[@sp.spName, NIL, emptyName, MustBeQualified].pDD]; IF emptyName.length # 0 THEN ERROR Directory.Error[invalidSearchPath]; sp.defined _ TRUE; RETURN[pSP]}; withWD => IF sp.defined AND (sp.cap = directoryCache[sp.pWD^.pDC].cap) THEN RETURN[pSP] ELSE { IF sp.defined THEN FlushCache[sp.pDC]; sp.pDC _ PDCFromDD[XlateName[@sp.spName, sp.pWD, emptyName, UseWD].pDD]; IF emptyName.length # 0 THEN ERROR Directory.Error[invalidSearchPath]; sp.cap _ directoryCache[sp.pWD^.pDC].cap; sp.defined _ TRUE; RETURN[pSP]}; ENDCASE; ERROR; -- this is an impossible place to get to! END; WildCard: INTERNAL PROC [c: CHARACTER] RETURNS [BOOLEAN] = -- Returns boolean indicating whether the passed character is a wildcard or not INLINE {RETURN[c = '* OR c = '#]}; XlateName: INTERNAL PROC [ fullName: LONG STRING, context: Handle, name: LONG STRING, search: SearchType, at: LONG POINTER TO DirectoryInternal.SPRecord _ NIL] RETURNS [ pDD: LONG POINTER TO DirectoryInternal.DirectoryDescriptor, next: LONG POINTER TO DirectoryInternal.SPRecord, wc: BOOLEAN] = -- This is the guts of the name translation. The file name fullName is broken down to the unqualified -- name and a pointer to a directory descriptor. IF search useWD, use the working directory; if it is -- UseWD, use the search path at element at (NIL to start); otherwise, the name must be qualified. -- The pointer next is non-NIL if there is another search path element to consult (for this fullName must -- not be qualified). The boolean wc indicates whether the returned file name includes any wildcards. BEGIN posn: CARDINAL _ 0; ft: FieldType; qualified: BOOLEAN; pDT: DirectoryInternal.PDT; -- the directory tree we are using pDTNode: DirectoryInternal.PDTNode; -- the current node in the directory tree pDC: CARDINAL; pDCEntry: LONG POINTER TO DirectoryFiles.DCEntry; IF fullName = NIL THEN ERROR Directory.Error[invalidFileName]; [posn, ft, wc] _ Break[fullName, name, posn]; SELECT ft FROM -- set up starting values in tree from first identifier = File => IF search = UseWD THEN { pDCEntry _ @directoryCache[context.pDC]; pDD _ @pDCEntry.dir; pDCEntry.refCount _ pDCEntry.refCount + 1; next _ NIL; RETURN} ELSE IF search = UseSP THEN { IF at = NIL THEN at _ context.pSP; pDCEntry _ @directoryCache[ValidateSP[at].pDC]; pDD _ @pDCEntry.dir; pDCEntry.refCount _ pDCEntry.refCount + 1; next _ at.pSP; RETURN} ELSE ERROR Error[invalidPathName]; = Volume => { IF wc THEN ERROR Error[notImplemented]; qualified _ TRUE; pDT _ volumeTable[GetVTEntry[name]].pDT; pDTNode _ DirectoryTrees.DTFirst[pDT]}; = Directory => { IF wc THEN ERROR Error[notImplemented]; IF LongString.EquivalentString["workdir"L, name] THEN { -- really is a name qualified with the context's working directory (or default) qualified _ TRUE; IF search = UseWD THEN { IF ~context.valid THEN ERROR Error[fileNotFound] ELSE pDC _ context.pDC} ELSE { IF ~DirectoryContexts.contextTable[0].valid THEN ERROR Error[fileNotFound] ELSE pDC _ DirectoryContexts.contextTable[0].pDC}; pDT _ volumeTable[directoryCache[pDC].pVT].pDT; pDTNode _ directoryCache[pDC].pDTNode} ELSE { -- this is really a directory which needs to be qualified with the working directory IF search = MustBeQualified THEN ERROR Error[invalidPathName]; qualified _ FALSE; IF search = UseWD THEN { pDT _ volumeTable[directoryCache[context.pDC].pVT].pDT; pDTNode _ directoryCache[context.pDC].pDTNode} ELSE { IF at = NIL THEN at _ context.pSP; pDT _ volumeTable[directoryCache[ValidateSP[at].pDC].pVT].pDT; pDTNode _ directoryCache[at.pDC].pDTNode}; pDTNode _ DirectoryTrees.DTFind[pDT, pDTNode, name]; IF pDTNode = DirectoryInternal.pDTnil THEN ERROR Error[fileNotFound]}}; ENDCASE => ERROR Directory.Error[invalidFileName]; DO -- trace down the tree according to the subdirectories in the name [posn, ft, wc] _ Break[fullName, name, posn]; SELECT ft FROM = File, Empty => EXIT; = Directory => { IF wc THEN ERROR Error[notImplemented]; pDTNode _ DirectoryTrees.DTFind[pDT, pDTNode, name]; IF pDTNode = DirectoryInternal.pDTnil THEN ERROR Error[fileNotFound]}; ENDCASE => ERROR Directory.Error[invalidFileName]; ENDLOOP; pDD _ @directoryCache[GetDirectoryCache[pDT[pDTNode].cap]].dir; next _ IF qualified OR (search # UseSP) THEN NIL ELSE at.pSP; RETURN END; -- Allocate the needed tables and strings volumeTable _ DirectoryContexts.ContextSpace.NEW[VolumeTable _ ALL[]]; FOR i: CARDINAL IN [0.. DirectoryFiles.maxDVolumes) DO volumeTable[i] _ [volName: [length: 0, maxlength: 40, text:], filler: ALL[' ], volID: Volume.nullID, pDT: NIL, dtSpace:] ENDLOOP; directoryCache _ DirectoryContexts.ContextSpace.NEW[DirectoryCache _ ALL[]]; FOR i: CARDINAL IN [1.. DirectoryFiles.maxDCache] DO directoryCache[i] _ [File.nullCapability, 0, 0, DirectoryInternal.pDTnil, [base: NIL, top: DirectoryInternal.nilPagePointer, size: 0, space:]] ENDLOOP; gnLastPath _ DirectoryContexts.ContextSpace.NEW[StringBody[Directory.maxPathNameLength]]; gnLastNameFromPath _ DirectoryContexts.ContextSpace.NEW[StringBody[Directory.maxDirectoryNameLength]]; gnLastNextName _ DirectoryContexts.ContextSpace.NEW[StringBody[Directory.maxPathNameLength]]; gnLastNameOfNext _ DirectoryContexts.ContextSpace.NEW[StringBody[Directory.maxDirectoryNameLength]]; nudeName _ DirectoryContexts.ContextSpace.NEW[StringBody[Directory.maxDirectoryNameLength+1]]; pValidChars _ DirectoryContexts.ContextSpace.NEW[ValidChars _ ALL[FALSE]]; FOR i: Environment.Byte IN [0.. 256) DO { c: CHARACTER _ LOOPHOLE[i]; SELECT c FROM IN ['+..';] => pValidChars[i] _ TRUE; -- includes the numbers IN ['@..'_] => pValidChars[i] _ TRUE; -- includes the upper case letters IN ['a..'~] => pValidChars[i] _ TRUE; -- includes the lower case letters = ',, = '?, = '$, = '%, = '', = '", = '!, = '&, = '(, = ') => pValidChars[i] _ TRUE; -- misc stuff which is ok = Ascii.BEL => pValidChars[i] _ TRUE; -- for directory names ENDCASE}; ENDLOOP; END. LOG Time: August 28, 1980 11:42 AM By: Keith Action: Created File Time: September 19, 1980 2:06 PM By: Keith Action: Corrected errors returned from CreateFile and CreateSubdirectory Time: October 8, 1980 3:28 PM By: Keith Action: Fixed easy bugs (ARs 5373, 6075, 6115, 6123, 6180, 6184); fixed Remove so DoneWith is performed before exit; fixed Insert and Remove to return correct Booleans. Time: October 12, 1980 4:39 PM By: Fay Action: Changes for merge of DirectoryExtras into Directory. Time: January 13, 1981 11:35 AM By: Keith Action: Fixed ARs, added masking to GetNext, changed CheckChar to use a table and reduced global frame size. (1792)\i3b18B66I676b18B239b13B28bi12BI6i59I206b11B39i5b15B85I232b14B47bi8BI4b7B91i44I2b5B53b11B26b11B140i17I52i29I58i22I1i30I99i43I225bi110BI31i45I33i63I5bi16BI4b10B147i51I1228b18B71i49I317i46I1719i43I778b10B63i52I876b18B71i91I1719i62I292b7B104i36I862i41I444i56I1593i67I694i53I880b10B91i56I778b16B99i74I897b6B135i113I1313b15B99i82I1223b13B94i40I523i35I281i47I164i29I1174b10B101i58I889b6B86i56I1044i26I81i27I1435b14B101i64I849bi25BI132i67I1227i66I149i66I115i81I343i91I1615i79I465i107I355i24I209i29I457i153I2081i174I249i47I464i164I350i80I879i85I361i537I113i31I46i38I214i52I738i84I374i89I590i73I512i43I1292i22I47i32I48i32I102i22I50i19I43bi3B164b10B5b18B79bI10B2b4B2b4B2b4B2b4Bi9b6B4b8B33b6B5b6B106b15B6b9B89b7B66I