-- ImageInfo.mesa; edited by Sandman, July 8, 1980 8:44 AM
-- Copyright Xerox Corporation 1979, 1980
DIRECTORY
AltoDefs USING [PageSize],
AltoFileDefs USING [FP],
BcdDefs USING [
FTIndex, FTNull, FTSelf, MTIndex, SGIndex, VersionStamp, NullVersion, Base],
BcdOps USING [
BcdBase, FTHandle, MTHandle, NameString, ProcessModules, ProcessSegs,
SGHandle],
ControlDefs USING [FrameCodeBase, GFT, GFTItem, GlobalFrame, NullGlobalFrame],
DirectoryDefs USING [EnumerateDirectory],
ImageFileInfoDefs: FROM "imagefileinfodefs",
ImageFormat USING [ImageHeader],
LoadStateFormat USING [
ModuleTable, ConfigIndex, NullConfig, LoadState, AltoVersionID, BcdObject],
LoadStateOps USING [EnumerationDirection, Map],
MiscDefs USING [Zero],
SDDefs USING [SD, sGFTLength],
SegmentDefs USING [
AddressFromPage, DeleteFileSegment, FileHandle, FileSegmentAddress,
FileSegmentHandle, FileSegmentObject, InsertFile, NewFile, NewFileSegment,
OldFileOnly, Read, SwapIn, SwapOut, Unlock, VMtoFileSegment],
String USING [
AppendSubString, EquivalentSubStrings, SubString, SubStringDescriptor],
Storage USING [Node, Pages, Free, FreePages];
ImageInfo: PROGRAM
IMPORTS
DirectoryDefs, ImageFileInfoDefs, BcdOps, MiscDefs, SegmentDefs, String,
Storage
EXPORTS ImageFileInfoDefs, LoadStateOps
SHARES ImageFormat =
BEGIN OPEN ImageFileInfoDefs, LoadStateFormat;
SetImage: PUBLIC PROCEDURE [name: STRING] =
BEGIN OPEN SegmentDefs;
ClearFileObjectTable[];
ClearFrameObjectTable[];
IF headerSeg # NIL THEN
BEGIN Unlock[headerSeg]; DeleteFileSegment[headerSeg] END;
headerSeg ← NewFileSegment[NewFile[name, Read, OldFileOnly], 1, 1, Read];
SwapIn[headerSeg];
InitImageLoadState[headerSeg];
InitializeImageCache[headerSeg];
Unlock[headerSeg];
RETURN
END;
headerSeg: FileSegmentHandle ← NIL;
Version: PUBLIC PROCEDURE RETURNS [v: BcdDefs.VersionStamp] =
BEGIN OPEN SegmentDefs;
image: POINTER TO ImageFormat.ImageHeader;
IF headerSeg = NIL THEN RETURN[BcdDefs.NullVersion];
SwapIn[headerSeg];
image ← FileSegmentAddress[headerSeg];
v ← image.prefix.version;
Unlock[headerSeg];
RETURN
END;
HeaderSegment: PUBLIC PROCEDURE RETURNS [FileSegmentHandle] =
BEGIN RETURN[headerSeg]; END;
-- Global Frame Table management
EnumerateGlobalFrames: PUBLIC PROCEDURE [
proc: PROCEDURE [GlobalFrameHandle] RETURNS [BOOLEAN]]
RETURNS [GlobalFrameHandle] =
BEGIN
i: CARDINAL;
frame: GlobalFrameHandle;
gft: POINTER TO ARRAY [0..0) OF ControlDefs.GFTItem ← ControlDefs.GFT;
FOR i IN [0..SDDefs.SD[SDDefs.sGFTLength]) DO
frame ← READ[@gft[i].frame];
IF frame # ControlDefs.NullGlobalFrame AND READ[@gft[i].epbase] = 0 AND
proc[frame] THEN RETURN[frame];
ENDLOOP;
RETURN[ControlDefs.NullGlobalFrame]
END;
GlobalFrame: TYPE = ControlDefs.GlobalFrame;
globalFrame: GlobalFrame;
VirtualGlobalFrame: PUBLIC PROCEDURE [frame: GlobalFrameHandle]
RETURNS [GlobalFrameHandle] =
BEGIN
CopyRead[to: @globalFrame, from: frame, nwords: SIZE[GlobalFrame]];
RETURN[@globalFrame]
END;
BcdBase: TYPE = BcdOps.BcdBase;
EnumerationDirection: TYPE = LoadStateOps.EnumerationDirection;
ConfigIndex: TYPE = LoadStateFormat.ConfigIndex;
loadstate: PUBLIC LoadStateFormat.LoadState;
gft: PUBLIC LoadStateFormat.ModuleTable;
nbcds: CARDINAL;
InputLoadState: PUBLIC PROCEDURE RETURNS [ConfigIndex] =
BEGIN OPEN LoadStateFormat, SegmentDefs, SDDefs;
IF state = NIL THEN ERROR;
SwapIn[state];
loadstate ← FileSegmentAddress[state];
BEGIN
ENABLE UNWIND => Unlock[state];
IF loadstate.versionident # AltoVersionID THEN ERROR;
END;
gft ← DESCRIPTOR[@loadstate.gft, READ[SD + sGFTLength]];
nbcds ← loadstate.nBcds;
RETURN[nbcds]
END;
ReleaseLoadState: PUBLIC PROCEDURE =
BEGIN OPEN SegmentDefs;
IF ~state.swappedin THEN RETURN;
Unlock[state];
IF state.lock = 0 THEN BEGIN SwapOut[state]; loadstate ← NIL; nbcds ← 0; END;
END;
MapConfigToReal: PUBLIC PROCEDURE [cgfi: GFTIndex, config: ConfigIndex]
RETURNS [rgfi: GFTIndex] =
BEGIN
IF cgfi = 0 THEN RETURN[0];
FOR rgfi IN [0..LENGTH[gft]) DO
IF gft[rgfi].config = config AND gft[rgfi].gfi = cgfi THEN RETURN[rgfi];
ENDLOOP;
RETURN[0];
END;
MapRealToConfig: PUBLIC PROCEDURE [rgfi: GFTIndex]
RETURNS [cgfi: GFTIndex, config: ConfigIndex] =
BEGIN RETURN[gft[rgfi].gfi, gft[rgfi].config]; END;
GetMap: PUBLIC PROCEDURE [config: ConfigIndex] RETURNS [map: LoadStateOps.Map] =
BEGIN
max: CARDINAL ← 0;
i: GFTIndex;
FOR i IN [0..LENGTH[gft]) DO
IF gft[i].config = config THEN max ← MAX[max, gft[i].gfi]; ENDLOOP;
max ← max + 1;
map ← DESCRIPTOR[Storage.Node[max], max];
MiscDefs.Zero[BASE[map], max];
FOR i IN [0..LENGTH[gft]) DO
IF gft[i].config = config THEN map[gft[i].gfi] ← i; ENDLOOP;
END;
ReleaseMap: PUBLIC PROCEDURE [map: LoadStateOps.Map] =
BEGIN Storage.Free[BASE[map]]; END;
BcdSegFromLoadState: PUBLIC PROCEDURE [bcd: ConfigIndex]
RETURNS [seg: FileSegmentHandle] =
BEGIN OPEN SegmentDefs, LoadStateFormat;
b: alto BcdObject ← LOOPHOLE[loadstate.bcds[bcd]];
seg ← NewFileSegment[state.file, b.base, b.pages, Read];
RETURN
END;
EnumerateLoadStateGFT: PUBLIC PROCEDURE [
proc: PROCEDURE [GFTIndex, GFTIndex, ConfigIndex] RETURNS [BOOLEAN]]
RETURNS [GFTIndex] =
BEGIN
i: GFTIndex;
FOR i IN [0..LENGTH[gft]) DO
IF proc[i, gft[i].gfi, gft[i].config] THEN RETURN[i]; ENDLOOP;
RETURN[0]
END;
EnumerateBcds: PUBLIC PROCEDURE [
dir: EnumerationDirection, proc: PROCEDURE [ConfigIndex] RETURNS [BOOLEAN]]
RETURNS [config: ConfigIndex] =
BEGIN
SELECT dir FROM
recentfirst =>
FOR config DECREASING IN [0..loadstate.nBcds) DO
IF proc[config] THEN RETURN[config]; ENDLOOP;
recentlast =>
FOR config IN [0..loadstate.nBcds) DO
IF proc[config] THEN RETURN[config]; ENDLOOP;
ENDCASE;
RETURN[NullConfig]
END;
InitImageLoadState: PUBLIC PROCEDURE [seg: FileSegmentHandle] =
BEGIN OPEN SegmentDefs;
image: POINTER TO ImageFormat.ImageHeader;
SwapIn[seg];
image ← FileSegmentAddress[seg];
state ← NewFileSegment[
seg.file, image.prefix.initialLoadStateBase, image.prefix.loadStatePages,
Read];
END;
state: PUBLIC FileSegmentHandle;
-- SymbolTable Lookup
FTIndex: TYPE = BcdDefs.FTIndex;
FTHandle: TYPE = BcdOps.FTHandle;
MTIndex: TYPE = BcdDefs.MTIndex;
MTHandle: TYPE = BcdOps.MTHandle;
SGIndex: TYPE = BcdDefs.SGIndex;
SGHandle: TYPE = BcdOps.SGHandle;
AddSymbolFileName: PUBLIC PROCEDURE [
bcd: BcdBase, sgi: SGIndex, sgh: SGHandle] =
BEGIN
ExistingFile: PROCEDURE [f: FileItem] RETURNS [BOOLEAN] =
BEGIN RETURN[f.fti = sgh.file]; END;
f: FileItem;
IF sgh.class = code THEN RETURN;
IF EnumFileObjects[ExistingFile] = NIL THEN
BEGIN f ← GetFileObject[]; f↑ ← [fileHandle: NIL, fti: sgh.file]; END;
IF sgh.file = BcdDefs.FTSelf THEN
f.fileHandle ← SegmentDefs.VMtoFileSegment[bcd].file;
RETURN;
END;
AddModuleSymbols: PUBLIC PROCEDURE [
bcd: BcdBase, config: ConfigIndex, mti: MTIndex, mth: MTHandle] =
BEGIN OPEN SegmentDefs;
sgh: SGHandle ← @LOOPHOLE[bcd + bcd.sgOffset, BcdDefs.Base][mth.sseg];
SymbolFile: PROCEDURE [f: FileItem] RETURNS [BOOLEAN] =
BEGIN RETURN[f.fti = sgh.file]; END;
fi: FileItem;
fr: FrameItem;
symfile: FileHandle;
frame: GlobalFrameHandle;
symfile ←
IF (fi ← EnumFileObjects[SymbolFile]) = NIL THEN NIL ELSE fi.fileHandle;
frame ← READ[@ControlDefs.GFT[MapConfigToReal[mth.gfi, config]].frame];
fr ← GetFrameItem[];
fr.symbols ←
IF symfile = NIL OR sgh.pages = 0 THEN NIL
ELSE NewFileSegment[symfile, sgh.base, sgh.pages + sgh.extraPages, Read];
fr.frame ← frame;
sgh ← @LOOPHOLE[bcd + bcd.sgOffset, BcdDefs.Base][mth.code.sgi];
fr.code ← NewFileSegment[headerSeg.file, sgh.base, sgh.pages, Read];
fr.offset ← mth.code.offset;
RETURN;
END;
bcd: BcdBase;
FindAllSymbols: PUBLIC PROCEDURE =
BEGIN
LookupEachBcd: PROCEDURE [config: ConfigIndex] RETURNS [BOOLEAN] =
BEGIN
EnterFile: PROCEDURE [sgh: SGHandle, sgi: SGIndex] RETURNS [BOOLEAN] =
BEGIN AddSymbolFileName[bcd, sgi, sgh]; RETURN[FALSE]; END;
DoModule: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] =
BEGIN AddModuleSymbols[bcd, config, mti, mth]; RETURN[FALSE]; END;
bcdseg: FileSegmentHandle ← BcdSegFromLoadState[config];
SegmentDefs.SwapIn[bcdseg];
bcd ← SegmentDefs.FileSegmentAddress[bcdseg];
[] ← BcdOps.ProcessSegs[bcd, EnterFile];
LookupFiles[];
[] ← BcdOps.ProcessModules[bcd, DoModule];
ClearFileObjectTable[];
RETURN[FALSE]
END;
[] ← InputLoadState[];
[] ← EnumerateBcds[recentfirst, LookupEachBcd];
ReleaseLoadState[];
RETURN
END;
SymbolSegForFrame: PUBLIC PROCEDURE [frame: GlobalFrameHandle]
RETURNS [seg: FileSegmentHandle] =
BEGIN
SymbolFile: PROCEDURE [f: FrameItem] RETURNS [BOOLEAN] =
BEGIN RETURN[f.frame = frame]; END;
FindModule: PROCEDURE [mth: MTHandle, mti: BcdDefs.MTIndex]
RETURNS [BOOLEAN] = BEGIN RETURN[mth.gfi = cgfi]; END;
f: FrameItem;
bcdseg: FileSegmentHandle;
config: ConfigIndex;
cgfi: GFTIndex;
mth: MTHandle;
mti: MTIndex;
sgb: BcdDefs.Base;
IF frame = ControlDefs.NullGlobalFrame THEN RETURN[NIL];
IF (f ← EnumFrameObjects[SymbolFile]) # NIL THEN RETURN[f.symbols];
IF VirtualGlobalFrame[frame].copied THEN
BEGIN
original: GlobalFrameHandle ← FindOriginal[frame];
seg ← SymbolSegForFrame[original];
AddSymbolsForCopy[frame, original];
RETURN
END;
[] ← InputLoadState[];
[config: config, cgfi: cgfi] ← MapRealToConfig[VirtualGlobalFrame[frame].gfi];
bcdseg ← BcdSegFromLoadState[config];
SegmentDefs.SwapIn[bcdseg];
bcd ← SegmentDefs.FileSegmentAddress[bcdseg];
[mth: mth, mti: mti] ← BcdOps.ProcessModules[bcd, FindModule];
sgb ← LOOPHOLE[bcd + bcd.sgOffset];
AddSymbolFileName[bcd, mth.sseg, @sgb[mth.sseg]];
LookupFiles[];
AddModuleSymbols[bcd, config, mti, mth];
ClearFileObjectTable[];
ReleaseLoadState[];
SegmentDefs.Unlock[bcdseg];
SegmentDefs.DeleteFileSegment[bcdseg];
RETURN[EnumFrameObjects[SymbolFile].symbols]
END;
CodeSegForFrame: PUBLIC PROCEDURE [frame: GlobalFrameHandle]
RETURNS [seg: FileSegmentHandle, offset: CARDINAL] =
BEGIN
CodeFile: PROCEDURE [f: FrameItem] RETURNS [BOOLEAN] =
BEGIN RETURN[f.frame = frame]; END;
f: FrameItem;
IF frame = ControlDefs.NullGlobalFrame THEN RETURN[NIL, 0];
IF (f ← EnumFrameObjects[CodeFile]) # NIL THEN RETURN[f.code, f.offset];
[] ← SymbolSegForFrame[frame];
f ← EnumFrameObjects[CodeFile];
RETURN[f.code, f.offset];
END;
FindOriginal: PUBLIC PROCEDURE [copy: GlobalFrameHandle]
RETURNS [GlobalFrameHandle] =
BEGIN
Original: PROCEDURE [f: GlobalFrameHandle] RETURNS [BOOLEAN] =
BEGIN
RETURN[
f
#
copy
AND
~VirtualGlobalFrame[
f].copied
AND
SameCode[
copy, f]
=
identical];
END;
RETURN[EnumerateGlobalFrames[Original]]
END;
CodeMatch: TYPE = {identical, same, different};
SameCode: PROCEDURE [f1, f2: GlobalFrameHandle] RETURNS [CodeMatch] =
BEGIN
Size: CARDINAL = SIZE[ControlDefs.FrameCodeBase];
fcb1, fcb2: ControlDefs.FrameCodeBase;
CopyRead[to: @fcb1, from: @f1.code, nwords: Size];
CopyRead[to: @fcb2, from: @f2.code, nwords: Size];
IF fcb1.highByte = 0 OR fcb2.highByte = 0 THEN
BEGIN
fcb1.out ← fcb2.out ← FALSE;
RETURN[IF fcb1 = fcb2 THEN identical ELSE different];
END;
IF fcb1.handle # fcb2.handle THEN RETURN[different];
RETURN[IF GetOffset[fcb1] = GetOffset[fcb2] THEN identical ELSE same];
END;
GetOffset: PROCEDURE [fcb: ControlDefs.FrameCodeBase]
RETURNS [offset: CARDINAL] =
BEGIN OPEN SegmentDefs;
seg: SegmentDefs.FileSegmentHandle ← VirtualFileSegment[fcb.handle];
IF ~seg.swappedin THEN BEGIN fcb.out ← FALSE; RETURN[fcb.offset]; END;
RETURN[fcb.shortbase - SegmentDefs.AddressFromPage[seg.VMpage]]
END;
FrameToModuleName: PUBLIC PROCEDURE [frame: GlobalFrameHandle, name: STRING] =
BEGIN
cgfi: GFTIndex;
config: ConfigIndex;
ns: BcdOps.NameString;
bcdseg: FileSegmentHandle;
bcd: BcdBase;
FindModule: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] =
BEGIN
ss: String.SubStringDescriptor;
IF cgfi IN [mth.gfi..mth.gfi + mth.ngfi) THEN
BEGIN
ss ← [base: @ns.string, offset: mth.name, length: ns.size[mth.name]];
String.AppendSubString[name, @ss];
RETURN[TRUE];
END;
RETURN[FALSE];
END;
IF frame = ControlDefs.NullGlobalFrame THEN RETURN;
IF VirtualGlobalFrame[frame].copied THEN
BEGIN
original: GlobalFrameHandle ← FindOriginal[frame];
FrameToModuleName[original, name];
RETURN
END;
[] ← InputLoadState[];
[config: config, cgfi: cgfi] ← MapRealToConfig[VirtualGlobalFrame[frame].gfi];
bcdseg ← BcdSegFromLoadState[config];
SegmentDefs.SwapIn[bcdseg];
bcd ← SegmentDefs.FileSegmentAddress[bcdseg];
ns ← LOOPHOLE[bcd + bcd.ssOffset];
[] ← BcdOps.ProcessModules[bcd, FindModule];
ReleaseLoadState[];
SegmentDefs.Unlock[bcdseg];
SegmentDefs.DeleteFileSegment[bcdseg];
RETURN
END;
segment: SegmentDefs.FileSegmentObject;
VirtualFileSegment: PUBLIC PROCEDURE [seg: FileSegmentHandle]
RETURNS [FileSegmentHandle] =
BEGIN OPEN SegmentDefs;
CopyRead[to: @segment, from: seg, nwords: SIZE[FileSegmentObject]];
RETURN[@segment]
END;
AddSymbolsForCopy: PROCEDURE [copy, original: GlobalFrameHandle] =
BEGIN
copyFI, originalFI: FrameItem;
FindMatch: PROCEDURE [f: FrameItem] RETURNS [BOOLEAN] =
BEGIN RETURN[f.frame = original]; END;
originalFI ← EnumFrameObjects[FindMatch];
copyFI ← GetFrameItem[];
copyFI↑ ← originalFI↑;
copyFI.frame ← copy;
RETURN
END;
-- File Lookup Object Management
FileObject: TYPE = RECORD [
fileHandle: SegmentDefs.FileHandle, fti: BcdDefs.FTIndex];
FileItem: TYPE = POINTER TO FileObject;
FileObjectTable: TYPE = RECORD [
link: POINTER TO FileObjectTable, subTable: ARRAY [0..0) OF FileObject];
fileTable: POINTER TO FileObjectTable ← NIL;
nFiles: CARDINAL ← MaxNFileObjects;
MaxNFileObjects: CARDINAL = (AltoDefs.PageSize - 1)/SIZE[FileObject];
GetFileObject: PROCEDURE RETURNS [f: FileItem] =
BEGIN
newTable: POINTER TO FileObjectTable;
IF nFiles < MaxNFileObjects THEN
BEGIN f ← @fileTable.subTable[nFiles]; nFiles ← nFiles + 1; END
ELSE
BEGIN
newTable ← Storage.Pages[1];
newTable.link ← fileTable;
fileTable ← newTable;
nFiles ← 1;
RETURN[@fileTable.subTable[0]];
END;
END;
EnumFileObjects: PROCEDURE [proc: PROCEDURE [f: FileItem] RETURNS [BOOLEAN]]
RETURNS [f: FileItem] =
BEGIN
i: CARDINAL;
table: POINTER TO FileObjectTable;
IF fileTable = NIL THEN RETURN[NIL];
IF nFiles < MaxNFileObjects THEN
FOR i IN [0..nFiles) DO
IF proc[f ← @fileTable.subTable[i]] THEN RETURN; ENDLOOP;
FOR table ← fileTable.link, table.link UNTIL table = NIL DO
FOR i IN [0..MaxNFileObjects) DO
IF proc[f ← @table.subTable[i]] THEN RETURN; ENDLOOP;
ENDLOOP;
RETURN[NIL];
END;
ClearFileObjectTable: PROCEDURE =
BEGIN
table: POINTER TO FileObjectTable;
UNTIL fileTable = NIL DO
table ← fileTable;
fileTable ← fileTable.link;
Storage.FreePages[table];
ENDLOOP;
nFiles ← MaxNFileObjects;
RETURN
END;
-- Frame Symbol Table Management
FrameObject: TYPE = RECORD [
frame: GlobalFrameHandle, symbols, code: FileSegmentHandle, offset: CARDINAL];
FrameItem: TYPE = POINTER TO FrameObject;
FrameObjectTable: TYPE = RECORD [
link: POINTER TO FrameObjectTable, subTable: ARRAY [0..0) OF FrameObject];
frameTable: POINTER TO FrameObjectTable ← NIL;
nFrames: CARDINAL ← MaxNFrameObjects;
MaxNFrameObjects: CARDINAL = (AltoDefs.PageSize - 1)/SIZE[FrameObject];
GetFrameItem: PROCEDURE RETURNS [f: FrameItem] =
BEGIN
newTable: POINTER TO FrameObjectTable;
IF nFrames < MaxNFrameObjects THEN
BEGIN f ← @frameTable.subTable[nFrames]; nFrames ← nFrames + 1; END
ELSE
BEGIN
newTable ← Storage.Pages[1];
newTable.link ← frameTable;
frameTable ← newTable;
nFrames ← 1;
RETURN[@frameTable.subTable[0]];
END;
END;
EnumFrameObjects: PROCEDURE [proc: PROCEDURE [f: FrameItem] RETURNS [BOOLEAN]]
RETURNS [f: FrameItem] =
BEGIN
i: CARDINAL;
table: POINTER TO FrameObjectTable;
IF frameTable = NIL THEN RETURN[NIL];
IF nFrames < MaxNFrameObjects THEN
FOR i IN [0..nFrames) DO
IF proc[f ← @frameTable.subTable[i]] THEN RETURN; ENDLOOP;
FOR table ← frameTable.link, table.link UNTIL table = NIL DO
FOR i IN [0..MaxNFrameObjects) DO
IF proc[f ← @table.subTable[i]] THEN RETURN; ENDLOOP;
ENDLOOP;
RETURN[NIL];
END;
ClearFrameObjectTable: PROCEDURE =
BEGIN
table: POINTER TO FrameObjectTable;
UNTIL frameTable = NIL DO
table ← frameTable;
frameTable ← frameTable.link;
Storage.FreePages[table];
ENDLOOP;
nFrames ← MaxNFrameObjects;
RETURN
END;
-- Directory Lookup of File Table
nFilesToFind: CARDINAL;
LookupFiles: PUBLIC PROCEDURE =
BEGIN
name: STRING ← [40];
CountFilesToLookup: PROCEDURE [f: FileItem] RETURNS [BOOLEAN] =
BEGIN nFilesToFind ← nFilesToFind + 1; RETURN[FALSE]; END;
nFilesToFind ← 0;
[] ← EnumFileObjects[CountFilesToLookup];
DirectoryDefs.EnumerateDirectory[CheckOne];
RETURN
END;
CheckOne: PROCEDURE [fp: POINTER TO AltoFileDefs.FP, name: STRING]
RETURNS [found: BOOLEAN] =
BEGIN OPEN String;
i: CARDINAL;
dirName: SubStringDescriptor;
bcd: SubStringDescriptor ← [base: "bcd"L, offset: 0, length: 3];
file: FileItem;
FOR i IN [0..name.length) DO
IF name[i] = '. THEN
BEGIN
IF name.length - i # 5 THEN GOTO UseWholeName;
dirName ← [base: name, offset: i + 1, length: 3];
IF ~EquivalentSubStrings[@dirName, @bcd] THEN GOTO UseWholeName;
dirName.offset ← 0;
dirName.length ← i;
GOTO HasBCDExtension;
END;
REPEAT
UseWholeName => NULL;
HasBCDExtension =>
BEGIN
[found, file] ← FindFileName[@dirName];
IF found THEN RETURN[ThisIsTheOne[fp, file]];
END;
ENDLOOP;
dirName ← [base: name, offset: 0, length: name.length - 1];
[found, file] ← FindFileName[@dirName];
RETURN[IF found THEN ThisIsTheOne[fp, file] ELSE FALSE];
END;
ThisIsTheOne: PROCEDURE [fp: POINTER TO AltoFileDefs.FP, file: FileItem]
RETURNS [BOOLEAN] =
BEGIN
file.fileHandle ← SegmentDefs.InsertFile[fp, SegmentDefs.Read];
RETURN[(nFilesToFind ← nFilesToFind - 1) = 0];
END;
FindFileName: PUBLIC PROCEDURE [name: String.SubString]
RETURNS [found: BOOLEAN, file: FileItem] =
BEGIN OPEN String;
ns: BcdOps.NameString ← LOOPHOLE[bcd + bcd.ssOffset];
ftb: BcdDefs.Base ← LOOPHOLE[bcd + bcd.ftOffset];
filename: SubStringDescriptor ← [base: @ns.string, offset:, length:];
MatchName: PROCEDURE [f: FileItem] RETURNS [BOOLEAN] =
BEGIN
IF f.fti = BcdDefs.FTNull THEN RETURN[FALSE];
IF f.fti = BcdDefs.FTSelf THEN RETURN[FALSE];
filename.offset ← ftb[f.fti].name;
filename.length ← ns.size[ftb[f.fti].name];
IF LastCharIsDot[@filename] THEN name.length ← name.length + 1;
RETURN[EquivalentSubStrings[@filename, name]];
END;
f: FileItem;
f ← EnumFileObjects[MatchName];
RETURN[f # NIL, f];
END;
LastCharIsDot: PUBLIC PROCEDURE [name: String.SubString] RETURNS [BOOLEAN] =
BEGIN RETURN[name.base[name.offset + name.length - 1] = '.]; END;
END...