-- 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.