-- Utilities.Mesa Edited by Sandman on July 10, 1980 7:52 AM -- Copyright Xerox Corporation 1979, 1980 DIRECTORY AltoFileDefs USING [FP], BcdDefs USING [ Base, CTIndex, CTNull, CTRecord, FTIndex, FTNull, FTSelf, MTIndex, MTNull, MTRecord, NTIndex, NTNull, NTRecord, SGIndex, SGNull, SGRecord], BcdOps USING [ BcdBase, CTHandle, MTHandle, NameString, NTHandle, ProcessModules, ProcessSegs, SGHandle], CacheOps USING [CopyRead, CopyWrite, READ, WRITE], BootmesaOps USING [BootData, BootmesaError, dataObject, VirtualGlobalFrame], ControlDefs USING [ ControlLink, GFTIndex, GFTNull, GlobalFrame, GlobalFrameHandle, NullGlobalFrame], DirectoryDefs USING [EnumerateDirectory], LoaderOps USING [], LoadStateOps USING [Map], SegmentDefs USING [ DefaultBase, DeleteFileSegment, FileHandle, FileSegmentAddress, FileSegmentHandle, HardUp, InsertFile, LockFile, MakeSwappedIn, NewFileSegment, Read, ReleaseFile, SwapIn, SwapUp, Unlock, UnlockFile, VMtoFileSegment], SegOps USING [EnumerateSegs, NewSeg, Seg], String USING [ AppendChar, AppendString, AppendSubString, EqualSubStrings, EquivalentSubStrings, SubString, SubStringDescriptor], Storage USING [Node, Free]; Utilities: PROGRAM IMPORTS DirectoryDefs, BcdOps, SegmentDefs, String, Storage, CacheOps, BootmesaOps, SegOps EXPORTS BootmesaOps, BcdOps, LoaderOps =PUBLIC BEGIN OPEN BcdOps, BcdDefs, ControlDefs; data: POINTER TO BootmesaOps.BootData _ @BootmesaOps.dataObject; BcdBase: PRIVATE TYPE = BcdOps.BcdBase; SubStringDescriptor: TYPE = String.SubStringDescriptor; currentCti: CTIndex _ CTNull; FileItem: TYPE = POINTER TO FileObject; FileObject: TYPE = RECORD [ fti: BcdDefs.FTIndex, ext: BOOLEAN, handle: SegmentDefs.FileHandle, link: FileItem]; files: FileItem _ NIL; loadee: BcdBase; ssb: NameString; ftb, mtb: Base; nfilestofind: CARDINAL _ 0; tableopen: BOOLEAN _ FALSE; FindFiles: PUBLIC PROCEDURE [bcd: BcdBase] = BEGIN EnterCodeFileNames[bcd]; LookupFileTable[]; END; EnterCodeFileNames: PROCEDURE [bcd: BcdBase] = BEGIN SegSearch: PROCEDURE [sgh: SGHandle, sgi: SGIndex] RETURNS [BOOLEAN] = BEGIN IF sgh.class = code THEN AddFileName[sgh.file]; RETURN[FALSE]; END; [] _ BcdOps.ProcessSegs[bcd, SegSearch]; RETURN; END; AddFileName: PROCEDURE [file: FTIndex] = BEGIN p: FileItem; i, offset, length: CARDINAL; FOR p _ files, p.link UNTIL p = NIL DO IF file = p.fti THEN RETURN; ENDLOOP; p _ Storage.Node[SIZE[FileObject]]; p^ _ [fti: file, handle: NIL, ext: FALSE, link: files]; files _ p; IF file = FTSelf THEN BEGIN p.handle _ SegmentDefs.VMtoFileSegment[loadee].file; RETURN END; IF file = FTNull THEN BEGIN p.handle _ NIL; RETURN END; offset _ ftb[file].name; length _ ssb.size[ftb[file].name]; FOR i IN [offset..offset + length) DO IF ssb.string.text[i] = '. THEN BEGIN p.ext _ TRUE; EXIT END; ENDLOOP; nfilestofind _ nfilestofind + 1; RETURN; END; FindFileName: PROCEDURE [name: String.SubString, ext: BOOLEAN] RETURNS [found: BOOLEAN, item: FileItem] = BEGIN file: SubStringDescriptor _ [base: @ssb.string, offset:, length:]; FOR item _ files, item.link UNTIL item = NIL DO file.offset _ ftb[item.fti].name; file.length _ ssb.size[ftb[item.fti].name]; IF LastCharIsDot[@file] THEN name.length _ name.length + 1; IF ext = item.ext AND String.EquivalentSubStrings[@file, name] THEN RETURN[TRUE, item]; ENDLOOP; RETURN[FALSE, NIL]; END; LastCharIsDot: PROCEDURE [name: String.SubString] RETURNS [BOOLEAN] = BEGIN RETURN[name.base[name.offset + name.length - 1] = '.]; END; FileNotFound: PUBLIC SIGNAL [name: STRING] = CODE; LookupFileTable: PROCEDURE = BEGIN p: FileItem; ssd: String.SubStringDescriptor; name: STRING _ [40]; IF nfilestofind # 0 THEN DirectoryDefs.EnumerateDirectory[CheckOne]; FOR p _ files, p.link UNTIL p = NIL DO IF p.handle = NIL AND p.fti # FTNull THEN BEGIN ssd _ [base: @ssb.string, offset: ftb[p.fti].name, length: ssb.size[ftb[p.fti].name]]; name.length _ 0; String.AppendSubString[name, @ssd]; IF p.ext THEN String.AppendString[name, ".bcd"L]; SIGNAL FileNotFound[name]; END; ENDLOOP; END; CheckOne: PROCEDURE [fp: POINTER TO AltoFileDefs.FP, name: STRING] RETURNS [found: BOOLEAN] = BEGIN i: CARDINAL; dirName: SubStringDescriptor; bcd: SubStringDescriptor _ [base: "bcd"L, offset: 0, length: 3]; item: 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 ~String.EquivalentSubStrings[@dirName, @bcd] THEN GOTO UseWholeName; dirName.offset _ 0; dirName.length _ i; GOTO HasBCDExtension; END; REPEAT UseWholeName => NULL; HasBCDExtension => BEGIN [found, item] _ FindFileName[@dirName, FALSE]; IF found THEN RETURN[ThisIsTheOne[fp, item]]; END; ENDLOOP; dirName _ [base: name, offset: 0, length: name.length - 1]; -- ignore dot on end [found, item] _ FindFileName[@dirName, TRUE]; RETURN[IF found THEN ThisIsTheOne[fp, item] ELSE FALSE]; END; ThisIsTheOne: PROCEDURE [fp: POINTER TO AltoFileDefs.FP, item: FileItem] RETURNS [BOOLEAN] = BEGIN item.handle _ SegmentDefs.InsertFile[fp, SegmentDefs.Read]; nfilestofind _ nfilestofind - 1; CheckFileVersion[item]; RETURN[nfilestofind = 0]; END; CheckFileVersion: PROCEDURE [item: FileItem] = BEGIN OPEN SegmentDefs; seg: FileSegmentHandle _ NewFileSegment[item.handle, 1, 1, Read]; bcd: BcdBase; msg: STRING _ [80]; SwapIn[seg]; bcd _ FileSegmentAddress[seg]; IF bcd.version # ftb[item.fti].version THEN BEGIN AppendFileName[msg, item]; String.AppendString[msg, " has incorrect version!"L]; BootmesaOps.BootmesaError[msg]; END; Unlock[seg]; LockFile[item.handle]; DeleteFileSegment[seg]; UnlockFile[item.handle]; END; AppendFileName: PROCEDURE [msg: STRING, item: FileItem] = BEGIN filename: SubStringDescriptor _ [base: @ssb.string, offset: ftb[item.fti].name, length: ssb.size[ftb[item.fti].name]]; String.AppendSubString[msg, @filename]; END; FileHandleFromTable: PROCEDURE [fti: FTIndex] RETURNS [file: SegmentDefs.FileHandle] = BEGIN p: FileItem; FOR p _ files, p.link UNTIL p = NIL DO IF p.fti = fti THEN RETURN[p.handle]; ENDLOOP; RETURN[NIL]; END; ModuleName: PROCEDURE [frame: GlobalFrameHandle, name: STRING] = BEGIN bname: SubStringDescriptor; mth: MTHandle; mti: MTIndex; cth: CTHandle; ctb: Base = LOOPHOLE[loadee + loadee.ctOffset]; gfi: GFTIndex = VirtualGlobalFrame[frame].gfi; FindInstance: PROCEDURE [nth: NTHandle, nti: NTIndex] RETURNS [BOOLEAN] = BEGIN WITH n: nth.item SELECT FROM module => IF n.mti # mti THEN RETURN[FALSE]; ENDCASE => RETURN[FALSE]; bname.offset _ nth.name; bname.length _ ssb.size[nth.name]; String.AppendSubString[name, @bname]; String.AppendChar[name, ':]; RETURN[TRUE]; END; FindModule: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] = BEGIN RETURN[mth.gfi = gfi]; END; bname.base _ @ssb.string; [mth, mti] _ ProcessModules[loadee, FindModule]; IF mth.namedInstance THEN [] _ ProcessNames[loadee, FindInstance]; IF mth.config # FIRST[CTIndex] THEN BEGIN cth _ @ctb[mth.config]; bname.offset _ cth.name; bname.length _ ssb.size[cth.name]; String.AppendSubString[name, @bname]; String.AppendChar[name, '>]; END; bname.offset _ mth.name; bname.length _ ssb.size[mth.name]; String.AppendSubString[name, @bname]; RETURN END; ModuleGFI: PROCEDURE [name: STRING] RETURNS [GFTIndex] = BEGIN ss: SubStringDescriptor; bname: SubStringDescriptor; nmti: MTIndex _ MTNull; CheckName: PROCEDURE [nth: NTHandle, nti: NTIndex] RETURNS [BOOLEAN] = BEGIN bname.offset _ nth.name; bname.length _ ssb.size[nth.name]; IF String.EqualSubStrings[@ss, @bname] THEN WITH n: nth.item SELECT FROM module => nmti _ n.mti; ENDCASE; RETURN[FALSE]; END; CheckModule: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] = BEGIN IF currentCti # CTNull AND mth.config # currentCti THEN RETURN[FALSE]; bname.offset _ mth.name; bname.length _ ssb.size[mth.name]; IF String.EqualSubStrings[@ss, @bname] THEN IF nmti = MTNull THEN nmti _ mti ELSE SIGNAL MultipleNames; RETURN[FALSE]; END; ss _ [base: name, offset: 0, length: name.length]; bname.base _ @ssb.string; [] _ ProcessNames[loadee, CheckName]; [] _ ProcessModules[loadee, CheckModule]; RETURN[IF nmti = MTNull THEN GFTNull ELSE mtb[nmti].gfi] END; MultipleNames: PUBLIC SIGNAL = CODE; FrameForGFI: PROCEDURE [gfi: GFTIndex] RETURNS [GlobalFrameHandle] = BEGIN RETURN[IF gfi = GFTNull THEN NullGlobalFrame ELSE data.moduleTable[gfi].frame] END; Frame: PROCEDURE [name: STRING] RETURNS [GlobalFrameHandle] = BEGIN RETURN[FrameForGFI[ModuleGFI[name]]] END; SetConfig: PROCEDURE [name: STRING] = BEGIN ss: SubStringDescriptor; bname: SubStringDescriptor; cti: CTIndex; CheckConfig: PROCEDURE [cth: CTHandle, cti: CTIndex] RETURNS [BOOLEAN] = BEGIN bname.offset _ cth.name; bname.length _ ssb.size[cth.name]; RETURN[String.EqualSubStrings[@ss, @bname]]; END; ss _ [base: name, offset: 0, length: name.length]; bname.base _ @ssb.string; cti _ ProcessConfigs[loadee, CheckConfig].cti; IF cti = CTNull THEN RETURN; currentCti _ cti; RETURN END; ResetConfig: PROCEDURE = BEGIN currentCti _ CTNull; RETURN END; FindCode: PUBLIC PROCEDURE [bcd: BcdBase, map: LoadStateOps.Map] = BEGIN ssb: NameString _ LOOPHOLE[loadee + loadee.ssOffset]; ModuleSearch: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] = BEGIN OPEN SegmentDefs; i: CARDINAL; frame: GlobalFrameHandle _ data.moduleTable[mth.gfi].frame; codeseg: SegOps.Seg; gf: GlobalFrame; FindShared: PROCEDURE [smth: MTHandle, smti: MTIndex] RETURNS [BOOLEAN] = BEGIN RETURN[smth # mth AND smth.code.sgi = mth.code.sgi] END; codeseg _ FindCodeSegment[mth, frame]; FOR i IN [mth.gfi..mth.gfi + mth.ngfi) DO data.moduleTable[i].code _ codeseg; ENDLOOP; codeseg.data _ FALSE; codeseg.link _ NIL; codeseg.resident _ FALSE; IF BcdOps.ProcessModules[loadee, FindShared].mth # NIL THEN BEGIN CacheOps.CopyRead[to: @gf, from: frame, size: SIZE[GlobalFrame]]; gf.shared _ TRUE; CacheOps.CopyWrite[from: @gf, to: frame, size: SIZE[GlobalFrame]]; END; RETURN[FALSE]; END; [] _ BcdOps.ProcessModules[loadee, ModuleSearch]; END; FindCodeSegment: PROCEDURE [mth: MTHandle, frame: GlobalFrameHandle] RETURNS [seg: SegOps.Seg] = BEGIN OPEN SegOps, SegmentDefs; file: FileHandle; sgh: SGHandle _ @LOOPHOLE[loadee + loadee.sgOffset, Base][mth.code.sgi]; i: CARDINAL; pages: CARDINAL; FindSegment: PROCEDURE [fs: Seg] RETURNS [BOOLEAN] = BEGIN RETURN[fs.realFile = file AND fs.base = sgh.base AND fs.pages = pages]; END; FOR i IN [1..LENGTH[data.moduleTable]) DO IF data.moduleTable[i].mth.code.sgi = mth.code.sgi THEN IF (seg _ data.moduleTable[i].code) # NIL THEN RETURN[seg]; ENDLOOP; file _ FileHandleFromTable[sgh.file]; pages _ sgh.pages + sgh.extraPages; seg _ EnumerateSegs[FindSegment]; IF seg = NIL THEN BEGIN seg _ NewSeg[file, sgh.base, pages]; seg.link2 _ NewFileSegment[file, sgh.base, pages, Read]; END; RETURN END; ProcessConfigs: PROCEDURE [ bcd: BcdBase, proc: PROCEDURE [CTHandle, CTIndex] RETURNS [BOOLEAN]] RETURNS [cth: CTHandle, cti: CTIndex] = BEGIN ctb: Base = LOOPHOLE[bcd + bcd.ctOffset]; FOR cti _ FIRST[CTIndex], cti + SIZE[CTRecord] + cth.nControls*SIZE[MTIndex] UNTIL cti = bcd.ctLimit DO cth _ @ctb[cti]; IF proc[cth, cti] THEN RETURN; ENDLOOP; RETURN[NIL, CTNull]; END; ProcessModules: PROCEDURE [ bcd: BcdBase, proc: PROCEDURE [MTHandle, MTIndex] RETURNS [BOOLEAN]] RETURNS [mth: MTHandle, mti: MTIndex] = BEGIN mtb: Base = LOOPHOLE[bcd + bcd.mtOffset]; FOR mti _ FIRST[MTIndex], mti + SIZE[MTRecord] + mth.frame.length UNTIL mti = bcd.mtLimit DO mth _ @mtb[mti]; IF proc[mth, mti] THEN RETURN; ENDLOOP; RETURN[NIL, MTNull]; END; ProcessNames: PROCEDURE [ bcd: BcdBase, proc: PROCEDURE [NTHandle, NTIndex] RETURNS [BOOLEAN]] RETURNS [nth: NTHandle, nti: NTIndex] = BEGIN ntb: Base = LOOPHOLE[bcd + bcd.ntOffset]; FOR nti _ FIRST[NTIndex], nti + SIZE[NTRecord] UNTIL nti = bcd.ntLimit DO nth _ @ntb[nti]; IF proc[nth, nti] THEN RETURN; ENDLOOP; RETURN[NIL, NTNull]; END; ProcessSegs: PROCEDURE [ bcd: BcdBase, proc: PROCEDURE [SGHandle, SGIndex] RETURNS [BOOLEAN]] RETURNS [sgh: SGHandle, sgi: SGIndex] = BEGIN sgb: Base = LOOPHOLE[bcd + bcd.sgOffset]; FOR sgi _ FIRST[SGIndex], sgi + SIZE[SGRecord] UNTIL sgi = bcd.sgLimit DO sgh _ @sgb[sgi]; IF proc[sgh, sgi] THEN RETURN; ENDLOOP; RETURN[NIL, SGNull]; END; FinalizeUtilities: PUBLIC PROCEDURE = BEGIN f: FileItem; FOR f _ files, files UNTIL f = NIL DO files _ f.link; IF f.handle.segcount = 0 THEN SegmentDefs.ReleaseFile[f.handle]; Storage.Free[f]; ENDLOOP; tableopen _ FALSE; END; InitializeMap: PUBLIC PROCEDURE [bcd: BcdBase] RETURNS [map: LoadStateOps.Map] = BEGIN OPEN Storage; i: CARDINAL; map _ DESCRIPTOR[Node[bcd.firstdummy], bcd.firstdummy]; FOR i IN [0..bcd.firstdummy) DO map[i] _ i; ENDLOOP; END; DestroyMap: PUBLIC PROCEDURE [map: LoadStateOps.Map] = BEGIN IF BASE[map] # NIL THEN Storage.Free[BASE[map]]; END; -- Link management ls: POINTER TO ControlDefs.ControlLink; dirty: BOOLEAN; seg: SegmentDefs.FileSegmentHandle; OpenLinkSpace: PROCEDURE [frame: GlobalFrameHandle, mth: MTHandle] = BEGIN IF BootmesaOps.VirtualGlobalFrame[frame].codelinks THEN BEGIN OPEN SegmentDefs; SwapIn[seg _ data.moduleTable[mth.gfi].code.link2]; ls _ FileSegmentAddress[seg] + mth.code.offset; END ELSE BEGIN seg _ NIL; ls _ LOOPHOLE[frame] END; ls _ ls - mth.frame.length; dirty _ FALSE; END; WriteLink: PROCEDURE [offset: CARDINAL, link: ControlDefs.ControlLink] = BEGIN dirty _ TRUE; IF seg = NIL THEN CacheOps.WRITE[ls + offset, link] ELSE (ls + offset)^ _ link; END; ReadLink: PROCEDURE [offset: CARDINAL] RETURNS [link: ControlDefs.ControlLink] = BEGIN RETURN[IF seg = NIL THEN CacheOps.READ[ls + offset] ELSE (ls + offset)^]; END; CloseLinkSpace: PROCEDURE [frame: GlobalFrameHandle] = BEGIN OPEN SegmentDefs; IF seg # NIL THEN BEGIN Unlock[seg]; IF dirty THEN BEGIN seg.write _ TRUE; SwapUp[seg]; seg.write _ FALSE; END; END; END; -- Utility Routines MakeResident: PUBLIC PROCEDURE [seg: SegmentDefs.FileSegmentHandle] = BEGIN OPEN SegmentDefs; MakeSwappedIn[seg, DefaultBase, HardUp]; RETURN END; globalFrame: GlobalFrame; currentFrame: GlobalFrameHandle _ NIL; VirtualGlobalFrame: PROCEDURE [f: GlobalFrameHandle] RETURNS [GlobalFrameHandle] = BEGIN IF f # currentFrame THEN BEGIN CacheOps.CopyRead[from: f, to: @globalFrame, size: SIZE[GlobalFrame]]; currentFrame _ f; END; RETURN[@globalFrame]; END; CodeSegment: PROCEDURE [f: GlobalFrameHandle] RETURNS [SegOps.Seg] = BEGIN RETURN[data.moduleTable[VirtualGlobalFrame[f].gfi].code]; END; InitializeUtilities: PUBLIC PROCEDURE [bcd: BcdBase] = BEGIN loadee _ bcd; mtb _ LOOPHOLE[loadee + loadee.mtOffset]; ssb _ LOOPHOLE[loadee + loadee.ssOffset]; ftb _ LOOPHOLE[loadee + loadee.ftOffset]; IF tableopen THEN FinalizeUtilities[]; tableopen _ TRUE; currentCti _ CTNull; RETURN END; END......