-- Files.Mesa Edited by Sandman on July 1, 1980 7:58 AM -- Copyright Xerox Corporation 1979, 1980 DIRECTORY AltoDefs USING [BytesPerPage, MaxFilePage], AltoFileDefs USING [CFA, eofDA, FA, FP, LD, NullFP, TIME, vDA], BFSDefs USING [CreatePages, DeletePages], DirectoryDefs USING [DirectoryLookup, DirectoryPurgeFP], DiskKDDefs USING [UpdateDiskKD], InlineDefs USING [BITAND, BITOR], MiscDefs USING [], Mopcodes USING [zEXCH], NucleusOps USING [], SegmentDefs USING [ AccessOptions, Append, DataSegmentAddress, DataSegmentHandle, DefaultAccess, DefaultBase, DeleteDataSegment, DeleteFileSegment, FileHandle, FileObject, FileSegmentAddress, FileSegmentHandle, JumpToPage, LengthHandle, MakeDataSegment, MakeSwappedIn, MaxFileLocks, NewDataSegment, NewFileOnly, NewFileSegment, Object, OldFileOnly, PageNumber, Read, SwapIn, Unlock, VersionOptions, Write], StreamDefs USING [], SwapperOps USING [ AllocateObject, EnumerateObjects, LiberateObject, ValidateObject], TimeDefs USING [DefaultTime, PackedTime]; Files: PROGRAM IMPORTS BFSDefs, SwapperOps, DirectoryDefs, DiskKDDefs, InlineDefs, SegmentDefs EXPORTS SwapperOps, MiscDefs, NucleusOps, SegmentDefs, StreamDefs SHARES SegmentDefs = BEGIN OPEN AltoFileDefs, SegmentDefs, SwapperOps; FileError: PUBLIC SIGNAL [file: FileHandle] = CODE; FileNameError: PUBLIC SIGNAL [name: STRING] = CODE; FileAccessError: PUBLIC SIGNAL [file: FileHandle] = CODE; NullFileObject: FileObject = [busy: FALSE, body: file[ open: FALSE, length: FALSE, lengthvalid: FALSE, read: FALSE, write: FALSE, append: FALSE, lengthchanged: FALSE, lock: 0, swapcount: 0, segcount: 0, fp: NullFP]]; NewFile: PUBLIC PROCEDURE [ name: STRING, access: AccessOptions, version: VersionOptions] RETURNS [file: FileHandle] = BEGIN OPEN InlineDefs; fp: FP; old, create: BOOLEAN; [access, version] _ ValidateOptions[access, version]; create _ BITAND[version, OldFileOnly] = 0; old _ DirectoryDefs.DirectoryLookup[@fp, name, create]; IF (old AND BITAND[version, NewFileOnly] # 0) OR (~old AND ~create) THEN ERROR FileNameError[name]; IF (file _ FindFile[@fp]) = NIL THEN BEGIN file _ LOOPHOLE[SwapperOps.AllocateObject[SIZE[file Object]]]; file^ _ NullFileObject; file.fp _ fp; END; SetFileAccess[file, access]; RETURN END; InsertFile: PUBLIC PROCEDURE [fp: POINTER TO FP, access: AccessOptions] RETURNS [file: FileHandle] = BEGIN [access, ] _ ValidateOptions[access, 0]; IF (file _ FindFile[fp]) = NIL THEN BEGIN file _ LOOPHOLE[SwapperOps.AllocateObject[SIZE[file Object]]]; file^ _ NullFileObject; file.fp _ fp^; END; SetFileAccess[file, access]; RETURN END; BootFile: PUBLIC PROCEDURE [access: AccessOptions] RETURNS [file: FileHandle] = BEGIN [access, ] _ ValidateOptions[access, 0]; file _ LOOPHOLE[SwapperOps.AllocateObject[SIZE[file Object]]]; file^ _ NullFileObject; SetFileAccess[file, access]; RETURN END; InsertFileLength: PUBLIC PROCEDURE [file: FileHandle, fa: POINTER TO FA] = BEGIN lh: LengthHandle _ GetLength[file]; IF fa.da # eofDA THEN [da: lh.da, page: lh.page, byte: lh.byte] _ fa^; RETURN END; ValidateOptions: PROCEDURE [access: AccessOptions, version: VersionOptions] RETURNS [AccessOptions, VersionOptions] = BEGIN OPEN InlineDefs; IF access = DefaultAccess THEN access _ Read; -- IF version = DefaultVersion THEN version _ 0; IF BITAND[version, NewFileOnly + OldFileOnly] = NewFileOnly + OldFileOnly OR (BITAND[version, NewFileOnly] # 0 AND BITAND[access, Append] = 0) THEN ERROR FileAccessError[NIL]; IF BITAND[access, Append] = 0 THEN version _ BITOR[version, OldFileOnly]; RETURN[access, version] END; GetFileAccess: PUBLIC PROCEDURE [file: FileHandle] RETURNS [AccessOptions] = BEGIN access: AccessOptions _ 0; ValidateObject[file]; IF file.read THEN access _ access + Read; IF file.write THEN access _ access + Write; IF file.append THEN access _ access + Append; RETURN[access] END; SetFileAccess: PUBLIC PROCEDURE [file: FileHandle, access: AccessOptions] = BEGIN OPEN InlineDefs; ValidateObject[file]; IF access = DefaultAccess THEN access _ Read; file.read _ file.read OR BITAND[access, Read] # 0; file.write _ file.write OR BITAND[access, Write] # 0; file.append _ file.append OR BITAND[access, Append] # 0; RETURN END; LockFile: PUBLIC PROCEDURE [file: FileHandle] = BEGIN OPEN file; ValidateObject[file]; IF lock = MaxFileLocks THEN ERROR FileError[file]; lock _ lock + 1; RETURN END; UnlockFile: PUBLIC PROCEDURE [file: FileHandle] = BEGIN OPEN file; ValidateObject[file]; IF lock = 0 THEN ERROR FileError[file]; lock _ lock - 1; RETURN END; ReleaseFile: PUBLIC PROCEDURE [file: FileHandle] = BEGIN [] _ PurgeFile[file]; DiskKDDefs.UpdateDiskKD[]; RETURN END; DestroyFile: PUBLIC PROCEDURE [file: FileHandle] = BEGIN seg: DataSegmentHandle; fp: FP _ file.fp; IF ~PurgeFile[file] OR ~DirectoryDefs.DirectoryPurgeFP[@fp] THEN ERROR FileError[file]; seg _ NewDataSegment[DefaultBase, 1]; BFSDefs.DeletePages[ DataSegmentAddress[seg], @fp, fp.leaderDA, 0 ! UNWIND => DeleteDataSegment[seg]]; DeleteDataSegment[seg]; DiskKDDefs.UpdateDiskKD[]; RETURN END; PurgeFile: PROCEDURE [file: FileHandle] RETURNS [BOOLEAN] = BEGIN OPEN file, SwapperOps; ValidateObject[file]; IF segcount # 0 THEN ERROR FileError[file]; IF lock # 0 THEN RETURN[FALSE]; CloseFile[file]; IF file.length THEN LiberateObject[FindLength[file]]; LiberateObject[file]; RETURN[TRUE] END; -- File length stuff NormalizeFileIndex: PUBLIC PROCEDURE [page: PageNumber, byte: CARDINAL] RETURNS [PageNumber, CARDINAL] = BEGIN page _ page + byte/AltoDefs.BytesPerPage; byte _ byte MOD AltoDefs.BytesPerPage; RETURN[page, byte] END; RoundFileIndex: PUBLIC PROCEDURE [page: PageNumber, byte: CARDINAL] RETURNS [PageNumber, CARDINAL] = BEGIN [page, byte] _ NormalizeFileIndex[page, byte]; IF byte = AltoDefs.BytesPerPage THEN BEGIN byte _ 0; page _ page + 1; END; RETURN[page, byte] END; TruncateFileIndex: PUBLIC PROCEDURE [page: PageNumber, byte: CARDINAL] RETURNS [PageNumber, CARDINAL] = BEGIN [page, byte] _ NormalizeFileIndex[page, byte]; IF page > 0 AND byte = 0 THEN BEGIN page _ page - 1; byte _ AltoDefs.BytesPerPage END; RETURN[page, byte] END; GetEndOfFile: PUBLIC PROCEDURE [file: FileHandle] RETURNS [page: PageNumber, byte: CARDINAL] = BEGIN OPEN file; lh: LengthHandle; cfa: CFA; seg: DataSegmentHandle; ValidateObject[file]; lh _ GetLength[file]; IF ~lengthvalid THEN BEGIN next: vDA; IF ~open THEN OpenFile[file]; IF lh.da = eofDA THEN GetLengthHint[lh]; seg _ NewDataSegment[DefaultBase, 1]; cfa _ CFA[fp, FA[lh.da, lh.page, lh.byte]]; [prev:, next: next] _ JumpToPage[ @cfa, lh.page, DataSegmentAddress[seg] ! UNWIND => DeleteDataSegment[seg]]; IF next # eofDA THEN [] _ JumpToPage[ @cfa, AltoDefs.MaxFilePage, DataSegmentAddress[seg] ! UNWIND => DeleteDataSegment[seg]]; DeleteDataSegment[seg]; ChangeFileLength[@cfa.fa, lh]; END; [page, byte] _ TruncateFileIndex[lh.page, lh.byte]; RETURN END; SetEndOfFile: PUBLIC PROCEDURE [ file: FileHandle, page: PageNumber, byte: CARDINAL] = BEGIN da: vDA; lh: LengthHandle; cfa: CFA; seg: DataSegmentHandle = NewDataSegment[DefaultBase, 1]; buf: POINTER = DataSegmentAddress[seg]; BEGIN ENABLE UNWIND => DeleteDataSegment[seg]; ValidateObject[file]; lh _ GetLength[file]; IF ~file.open THEN OpenFile[file]; IF lh.da = eofDA THEN GetLengthHint[lh]; cfa _ CFA[fp: file.fp, fa: [da: lh.da, page: lh.page, byte: lh.byte]]; [page, byte] _ RoundFileIndex[page, byte]; IF page = 0 THEN ERROR FileError[file]; [, da] _ JumpToPage[@cfa, page, buf]; SELECT cfa.fa.page FROM = page => SELECT cfa.fa.byte FROM > byte => IF ~file.write THEN ERROR FileAccessError[file]; < byte => IF ~file.append THEN ERROR FileAccessError[file]; ENDCASE => IF da = eofDA THEN GO TO done ELSE ERROR FileError[file]; < page => BEGIN da _ eofDA; IF ~file.append THEN ERROR FileAccessError[file]; END; ENDCASE => ERROR FileError[file]; BFSDefs.CreatePages[buf, @cfa, page, byte]; IF da # eofDA THEN BFSDefs.DeletePages[buf, @cfa.fp, da, page + 1]; EXITS done => NULL; END; DeleteDataSegment[seg]; ChangeFileLength[@cfa.fa, lh]; RETURN END; ChangeFileLength: PROCEDURE [fa: POINTER TO FA, lh: LengthHandle] = BEGIN currentlength: FA _ [da: lh.da, page: lh.page, byte: lh.byte]; IF currentlength # fa^ THEN BEGIN lh.file.lengthchanged _ TRUE; [da: lh.da, page: lh.page, byte: lh.byte] _ fa^; END; lh.file.lengthvalid _ TRUE; RETURN END; SetFileLength, UpdateFileLength: PUBLIC PROCEDURE [ file: FileHandle, fa: POINTER TO FA] = BEGIN OPEN file; lh: LengthHandle; ValidateObject[file]; lh _ GetLength[file]; ChangeFileLength[fa, lh]; RETURN END; GetFileLength: PUBLIC PROCEDURE [file: FileHandle, fa: POINTER TO FA] = BEGIN lh: LengthHandle; ValidateObject[file]; IF ~file.length THEN BEGIN fa^ _ [da: eofDA, page: 0, byte: 0]; RETURN END; lh _ FindLength[file]; fa^ _ [da: lh.da, page: lh.page, byte: lh.byte]; RETURN END; -- Open and Close (leader page stuff) MakePageZeroSeg: PROCEDURE [file: FileHandle, access: AccessOptions] RETURNS [seg: FileSegmentHandle] = BEGIN temp: FileHandle = BootFile[access]; temp.fp _ file.fp; temp.open _ TRUE; seg _ NewFileSegment[temp, 0, 1, access ! UNWIND => ReleaseFile[temp]]; SwapIn[seg ! UNWIND => DeletePageZeroSeg[seg]]; RETURN END; DeletePageZeroSeg: PROCEDURE [seg: FileSegmentHandle] = BEGIN IF seg.swappedin THEN Unlock[seg]; DeleteFileSegment[seg]; RETURN END; SecondsClock: POINTER TO TIME = LOOPHOLE[572B]; DAYTIME: PUBLIC PROCEDURE RETURNS [TIME] = BEGIN RETURN[SecondsClock^] END; OpenFile: PUBLIC PROCEDURE [file: FileHandle] = BEGIN OPEN file; lh: LengthHandle; ld: POINTER TO LD; seg: FileSegmentHandle; ValidateObject[file]; IF ~open THEN BEGIN seg _ MakePageZeroSeg[file, Read + Write]; ld _ FileSegmentAddress[seg]; IF length THEN BEGIN lh _ FindLength[file]; [page: lh.page, byte: lh.byte, da: lh.da] _ ld.eofFA; -- PATCH for OS versions 5 & up IF lh.da = 0 THEN lh.da _ eofDA; END; IF read THEN ld.read _ DAYTIME[]; IF write OR append THEN ld.created _ ld.written _ DAYTIME[]; DeletePageZeroSeg[seg]; open _ TRUE; END; RETURN END; CloseFile: PUBLIC PROCEDURE [file: FileHandle] = BEGIN OPEN file; ld: POINTER TO LD; lh: LengthHandle; seg: FileSegmentHandle; ValidateObject[file]; IF swapcount # 0 THEN SIGNAL FileError[file]; IF open AND length AND lengthchanged THEN BEGIN seg _ MakePageZeroSeg[file, Read + Write]; ld _ FileSegmentAddress[seg]; lh _ FindLength[file]; ld.eofFA _ FA[byte: lh.byte, page: lh.page, da: lh.da]; DeletePageZeroSeg[seg]; lengthchanged _ FALSE; END; open _ FALSE; RETURN END; GetLengthHint: PROCEDURE [lh: LengthHandle] = BEGIN ld: POINTER TO LD; seg: FileSegmentHandle; seg _ MakePageZeroSeg[lh.file, Read]; ld _ FileSegmentAddress[seg]; [page: lh.page, byte: lh.byte, da: lh.da] _ ld.eofFA; -- PATCH for OS versions 5 & up IF lh.da = 0 THEN lh.da _ eofDA; DeletePageZeroSeg[seg]; RETURN END; MesaToBCPLTime: PROCEDURE [LONG CARDINAL] RETURNS [TIME] = MACHINE CODE BEGIN Mopcodes.zEXCH END; BCPLToMesaTime: PROCEDURE [TIME] RETURNS [LONG CARDINAL] = MACHINE CODE BEGIN Mopcodes.zEXCH END; GetFileTimes: PUBLIC PROCEDURE [file: FileHandle] RETURNS [read, write, create: TimeDefs.PackedTime] = BEGIN ld: POINTER TO LD; seg: FileSegmentHandle; ValidateObject[file]; seg _ MakePageZeroSeg[file, Read]; ld _ FileSegmentAddress[seg]; read _ BCPLToMesaTime[ld.read]; write _ BCPLToMesaTime[ld.written]; create _ BCPLToMesaTime[ld.created]; DeletePageZeroSeg[seg]; RETURN END; SetFileTimes: PUBLIC PROCEDURE [ file: FileHandle, read, write, create: TimeDefs.PackedTime _ TimeDefs.DefaultTime] = BEGIN now: TIME = DAYTIME[]; ld: POINTER TO LD; seg: FileSegmentHandle; ValidateObject[file]; IF ~file.open THEN OpenFile[file]; seg _ MakePageZeroSeg[file, Read + Write]; ld _ FileSegmentAddress[seg]; ld.read _ IF read = TimeDefs.DefaultTime THEN now ELSE MesaToBCPLTime[read]; ld.written _ IF write = TimeDefs.DefaultTime THEN now ELSE MesaToBCPLTime[write]; ld.created _ IF create = TimeDefs.DefaultTime THEN now ELSE MesaToBCPLTime[create]; DeletePageZeroSeg[seg]; RETURN END; -- Managing File Objects EnumerateFiles: PUBLIC PROCEDURE [ proc: PROCEDURE [FileHandle] RETURNS [BOOLEAN]] RETURNS [FileHandle] = BEGIN RETURN[LOOPHOLE[SwapperOps.EnumerateObjects[file, LOOPHOLE[proc]]]] END; FindFile: PUBLIC PROCEDURE [fp: POINTER TO FP] RETURNS [FileHandle] = BEGIN MatchFP: PROCEDURE [file: FileHandle] RETURNS [BOOLEAN] = BEGIN RETURN[file.fp.leaderDA = fp.leaderDA AND file.fp.serial = fp.serial] END; RETURN[EnumerateFiles[MatchFP]] END; GetFileFP: PUBLIC PROCEDURE [file: FileHandle, fp: POINTER TO FP] = BEGIN ValidateObject[file]; fp^ _ file.fp; RETURN END; -- Managing Length Objects FindLength: PROCEDURE [file: FileHandle] RETURNS [LengthHandle] = BEGIN FindLength: PROCEDURE [lh: LengthHandle] RETURNS [BOOLEAN] = BEGIN RETURN[lh.file = file] END; RETURN[LOOPHOLE[SwapperOps.EnumerateObjects[length, LOOPHOLE[FindLength]]]] END; GetLength: PROCEDURE [file: FileHandle] RETURNS [lh: LengthHandle] = BEGIN IF file.length THEN RETURN[FindLength[file]]; lh _ LOOPHOLE[SwapperOps.AllocateObject[SIZE[length Object]]]; lh^ _ [FALSE, length[0, 0, 0, file, eofDA]]; file.length _ TRUE; file.lengthvalid _ FALSE; RETURN END; END.