DIRECTORY BcdDefs: TYPE USING [ Base, Link, MTIndex, MTRecord, SGIndex, VersionStamp, FTSelf, SGNull, VersionID], BcdOps: TYPE USING [BcdBase, NameString], CommandUtil: TYPE USING [PairList, FreePairList, KeyValue], ConvertUnsafe: TYPE USING [AppendRope, EqualSubStrings, SubString, SubStringToRope, ToRope], FS: TYPE USING [Create, Close, Error, Read, nullOpenFile, Open, OpenFile, read, SameFile, write], FileParms: TYPE USING [ ActualId, BindingProc, Name, Ops, SymbolSpace, nullActual, nullSymbolSpace], FileParmOps: TYPE USING [], FileSegment: TYPE USING [Span, nullSpan], Rope: TYPE USING [ROPE], TimeStamp: TYPE USING [Stamp], VM: TYPE USING [Allocate, Free, Interval, Map, nullInterval, PageNumberToAddress]; FSFileParmPack: PROGRAM IMPORTS CommandUtil, ConvertUnsafe, FS, VM EXPORTS FileParmOps = { Name: TYPE = FileParms.Name; ActualId: TYPE = FileParms.ActualId; nullActual: ActualId = FileParms.nullActual; FileIndex: TYPE = NAT; nullFileIndex: FileIndex = FileIndex.LAST; Binding: PROC [ formalId, formalType: Name, defaultLocator: LONG STRING, binder: FileParms.BindingProc] = { i: FileIndex; name: LONG STRING _ FileName[@formalId, defaultLocator]; type: LONG STRING _ CopyName[@formalType]; IF name = NIL THEN i _ nullFileIndex ELSE { file: FS.OpenFile _ FS.nullOpenFile; FOR i IN [0 .. nextFile) DO IF EquivalentStrings[name, fileTable[i].name] THEN { IF EquivalentStrings[type, fileTable[i].type] THEN GO TO found; file _ fileTable[i].file}; REPEAT found => {zone.FREE[@name]; zone.FREE[@type]}; FINISHED => { version: TimeStamp.Stamp; span: FileSegment.Span; IF file = FS.nullOpenFile THEN file _ CreateFile[name]; IF file = FS.nullOpenFile THEN i _ nullFileIndex ELSE { [version, span] _ ReadHeader[file, @formalType]; i _ SearchCache[version]; IF i = nullFileIndex AND version # nullActual.version THEN { i _ NewCacheEntry[]; fileTable[i] _ [ version: version, file: file, span: span, name: name, type: type]} ELSE { FS.Close[file]; file _ FS.nullOpenFile; zone.FREE[@name]; zone.FREE[@type]}}}; ENDLOOP; IF i = nullFileIndex THEN binder[nullActual] ELSE binder[[fileTable[i].version, [fileTable[i].name, 0, fileTable[i].name.length]]]}}; Acquire: PROC [id: Name, actual: ActualId] RETURNS [FileParms.SymbolSpace] = { i: FileIndex _ SearchCache[actual.version]; IF i = nullFileIndex THEN { i _ NewCacheEntry[]; fileTable[i] _ [ version: actual.version, name: CopyName[@actual.locator], type: CopyName[@id]]}; OpenFile[i]; RETURN [IF fileTable[i].file = FS.nullOpenFile OR fileTable[i].span = nullSpan THEN FileParms.nullSymbolSpace ELSE [file: fileTable[i].file, span: fileTable[i].span]]}; Release: PROC [s: FileParms.SymbolSpace] = {NULL}; -- add ref counts? Forget: PROC [actual: ActualId] = { i: NAT _ 0; WHILE i < nextFile DO { IF fileTable[i].version = actual.version THEN GO TO delete; IF fileTable[i].name # NIL THEN { d: ConvertUnsafe.SubString _ [fileTable[i].name, 0, fileTable[i].name.length]; IF ConvertUnsafe.EqualSubStrings[d, actual.locator, FALSE] THEN GO TO delete}; i _ i + 1; EXITS delete => { ClearCacheEntry[i]; nextFile _ nextFile - 1; IF i # nextFile THEN { fileTable[i] _ fileTable[nextFile]; fileTable[nextFile] _ [nullActual.version]}}}; ENDLOOP}; outputFile: FS.OpenFile; AcquireOutput: PUBLIC PROC [name: LONG STRING] RETURNS [FS.OpenFile] = { fileName: Rope.ROPE = ConvertUnsafe.ToRope[name]; outputFile _ FS.Open[fileName, FS.write ! FS.Error => IF error.code = notFound THEN {outputFile _ FS.Create[fileName]; CONTINUE}]; RETURN [outputFile]}; ReleaseOutput: PUBLIC PROC [file: FS.OpenFile] = { IF outputFile = FS.nullOpenFile OR ~FS.SameFile[file, outputFile] THEN ERROR; FS.Close[outputFile]; outputFile _ FS.nullOpenFile}; aList: CommandUtil.PairList; SetAList: PUBLIC PROC [map: CommandUtil.PairList] = {aList _ map}; ClearAList: PUBLIC PROC = {aList _ CommandUtil.FreePairList[aList]}; Initialize: PUBLIC PROC [scratchZone: UNCOUNTED ZONE] RETURNS [FileParms.Ops] = { zone _ scratchZone; fileTable _ NIL; AdjustFileTable[16]; nextFile _ 0; outputFile _ FS.nullOpenFile; RETURN [[Binding, Acquire, Release, Forget]]}; Finalize: PUBLIC PROC = { FOR i: NAT IN [0..nextFile) DO ClearCacheEntry[i] ENDLOOP; IF outputFile # FS.nullOpenFile THEN {FS.Close[outputFile]; outputFile _ FS.nullOpenFile}; --zone.--FREE[@fileTable]; zone _ NIL}; FileName: PROC [key: ConvertUnsafe.SubString, default: LONG STRING] RETURNS [LONG STRING] = { t: LONG STRING = CommandUtil.KeyValue[key, aList]; d: ConvertUnsafe.SubString _ SELECT TRUE FROM (t # NIL) => [base: t, offset: 0, length: t.length], (default # NIL) => [base: default, offset: 0, length: default.length], ENDCASE => key; RETURN [NormalizeFileName[d]]}; CopyName: PROC [master: ConvertUnsafe.SubString] RETURNS [s: LONG STRING] = { s _ zone.NEW[StringBody[master.length]]; ConvertUnsafe.AppendRope[s, ConvertUnsafe.SubStringToRope[master]]; RETURN}; NormalizeFileName: PROC [formal: ConvertUnsafe.SubString] RETURNS [s: LONG STRING] = { IF formal.length = 1 AND formal.base[formal.offset] = '$ THEN s _ NIL ELSE { dot: BOOL _ FALSE; s _ zone.NEW[StringBody[formal.length+(".bcd"L).length]]; FOR i: CARDINAL IN [formal.offset .. formal.offset+formal.length) DO IF formal.base[i] = '. THEN dot _ TRUE; ENDLOOP; ConvertUnsafe.AppendRope[s, ConvertUnsafe.SubStringToRope[formal]]; IF ~dot THEN ConvertUnsafe.AppendRope[s, ".bcd"]}; RETURN}; EquivalentStrings: PROC [s1, s2: LONG STRING] RETURNS [BOOL] = { IF s1 # NIL AND s2 # NIL THEN { d1: ConvertUnsafe.SubString _ [base: s1, offset: 0, length: s1.length]; d2: ConvertUnsafe.SubString _ [base: s2, offset: 0, length: s2.length]; RETURN [ConvertUnsafe.EqualSubStrings[d1, d2, FALSE]]} ELSE RETURN [FALSE]}; OpenFile: PROC [i: FileIndex] = { IF fileTable[i].file = FS.nullOpenFile AND fileTable[i].name # NIL THEN fileTable[i].file _ CreateFile[fileTable[i].name]; IF fileTable[i].file # FS.nullOpenFile AND fileTable[i].span = nullSpan THEN { version: TimeStamp.Stamp; d: ConvertUnsafe.SubString _ [fileTable[i].type, 0, fileTable[i].type.length]; [version, fileTable[i].span] _ ReadHeader[fileTable[i].file, @d]; IF version # fileTable[i].version THEN { ClearCacheEntry[i]; fileTable[i].file _ FS.nullOpenFile; fileTable[i].span _ nullSpan}}}; zone: UNCOUNTED ZONE _ NIL; nullSpan: FileSegment.Span = FileSegment.nullSpan; FileRecord: TYPE = RECORD[ version: TimeStamp.Stamp _ , file: FS.OpenFile _ FS.nullOpenFile, span: FileSegment.Span _ nullSpan, name: LONG STRING _ NIL, type: LONG STRING _ NIL]; FileTable: TYPE = RECORD [SEQUENCE length: FileIndex OF FileRecord]; fileTable: REF FileTable; nextFile: NAT; SearchCache: PROC [version: TimeStamp.Stamp] RETURNS [i: FileIndex] = { FOR i IN [0 .. nextFile) DO IF fileTable[i].version = version THEN EXIT; REPEAT FINISHED => i _ nullFileIndex; ENDLOOP; RETURN}; NewCacheEntry: PROC RETURNS [i: FileIndex] = { WHILE nextFile >= fileTable.length DO AdjustFileTable[fileTable.length + 16] ENDLOOP; i _ nextFile; nextFile _ nextFile + 1}; AdjustFileTable: PROC [newSize: NAT] = { newTable: REF FileTable; oldSize: NAT = IF fileTable = NIL THEN 0 ELSE fileTable.length; IF newSize = 0 THEN newTable _ NIL ELSE { i: FileIndex; newTable _ --zone.--NEW[FileTable[newSize]]; FOR i IN [0..MIN[oldSize, newSize]) DO newTable[i] _ fileTable[i] ENDLOOP; FOR i IN [oldSize..newSize) DO newTable[i] _ [version: nullActual.version] ENDLOOP}; IF fileTable # NIL THEN --zone.--FREE[@fileTable]; fileTable _ newTable}; ClearCacheEntry: PROC [i: FileIndex] = { IF fileTable[i].file # FS.nullOpenFile THEN { FS.Close[fileTable[i].file]; fileTable[i].file _ FS.nullOpenFile}; IF fileTable[i].name # NIL THEN zone.FREE[@fileTable[i].name]; IF fileTable[i].type # NIL THEN zone.FREE[@fileTable[i].type]}; NameToRope: PROC [s: LONG STRING] RETURNS [r: Rope.ROPE] = { oldLength: CARDINAL = s.length; IF oldLength > 1 AND s[s.length-1] = '. THEN s.length _ s.length - 1; -- undo Alto convention for Pilot r _ ConvertUnsafe.ToRope[s]; s.length _ oldLength; RETURN}; CreateFile: PROC [s: LONG STRING] RETURNS [file: FS.OpenFile _ FS.nullOpenFile] = { IF s # NIL THEN file _ FS.Open[NameToRope[s], FS.read ! FS.Error => TRUSTED {CONTINUE}]; RETURN}; ReadHeader: PROC [file: FS.OpenFile, typeId: ConvertUnsafe.SubString] RETURNS [ version: TimeStamp.Stamp _ nullActual.version, span: FileSegment.Span _ nullSpan] = { headerInterval: VM.Interval _ VM.nullInterval; DeleteHeader: PROC = { IF headerInterval # VM.nullInterval THEN { VM.Free[headerInterval]; headerInterval _ VM.nullInterval}}; IF file # FS.nullOpenFile THEN { ENABLE { UNWIND => {NULL}; ANY => {GO TO badFile}}; BcdBase: PROC [p: LONG POINTER] RETURNS [BcdDefs.Base] = INLINE { RETURN [LOOPHOLE[p, BcdDefs.Base]]}; bcd: BcdOps.BcdBase; bcdPages: CARDINAL _ 8; mtb, ftb, sgb: BcdDefs.Base; mti: BcdDefs.MTIndex; sSeg: BcdDefs.SGIndex; nString: BcdOps.NameString; d: ConvertUnsafe.SubString; DO headerInterval _ VM.Allocate[count: bcdPages]; bcd _ VM.PageNumberToAddress[headerInterval.page]; FS.Read[file: file, fromPage: 1, pageCount: bcdPages, to: bcd]; IF bcd.versionIdent # BcdDefs.VersionID THEN GO TO badFile; IF bcdPages >= bcd.nPages THEN EXIT; bcdPages _ bcd.nPages; VM.Free[headerInterval]; headerInterval _ VM.nullInterval ENDLOOP; IF bcd.nConfigs # 0 THEN GO TO badFile; -- no packaged bcd's (for now) nString _ LOOPHOLE[bcd + bcd.ssOffset]; d.base _ @nString.string; ftb _ BcdBase[bcd + bcd.ftOffset]; mtb _ BcdBase[bcd + bcd.mtOffset]; mti _ BcdDefs.MTIndex.FIRST; UNTIL mti = bcd.mtLimit DO d.offset _ mtb[mti].name; d.length _ nString.size[mtb[mti].name]; IF ConvertUnsafe.EqualSubStrings[typeId, d] THEN EXIT; mti _ mti + (WITH m: mtb[mti] SELECT FROM direct => BcdDefs.MTRecord.direct.SIZE + m.length*BcdDefs.Link.SIZE, indirect => BcdDefs.MTRecord.indirect.SIZE, multiple => BcdDefs.MTRecord.multiple.SIZE, ENDCASE => ERROR); REPEAT FINISHED => IF bcd.nModules = 1 THEN mti _ BcdDefs.MTIndex.FIRST ELSE GOTO badFile; ENDLOOP; ftb _ BcdBase[bcd + bcd.ftOffset]; version _ IF mtb[mti].file = BcdDefs.FTSelf THEN bcd.version ELSE ftb[mtb[mti].file].version; sgb _ BcdBase[bcd + bcd.sgOffset]; sSeg _ mtb[mti].sseg; IF sSeg = BcdDefs.SGNull OR sgb[sSeg].pages = 0 OR sgb[sSeg].file # BcdDefs.FTSelf THEN GO TO badFile; span _ [base: sgb[sSeg].base, pages: sgb[sSeg].pages]; DeleteHeader[]; EXITS badFile => {DeleteHeader[]; span _ nullSpan}}; RETURN}; }. Êfile FSFileParmPack.mesa last modified by Satterthwaite, September 15, 1982 9:15 am Last Edited by: Maxwell, July 28, 1983 1:44 pm primary operations for read-only access operations for update access command line arguments initialization/finalization interpretation of file names (Pilot PreCascade conventions) -- ConvertUnsafe.AppendChar[s, formal.base[i]]; file setup low-level file manipulation and cache management file table management file setup Ê ˜Jšœ™Jšœ:™:J™.J˜šÏk ˜ šœ œœ˜J˜Q—Jšœœœ˜)Jšœ œœ$˜;JšœœœC˜\JšœœœS˜ašœ œœ˜J˜L—Jšœ œœ˜Jšœ œœ˜)Jšœœœœ˜Jšœ œœ ˜JšœœœD˜RJ˜—šœ˜Jšœœ˜*Jšœ˜J˜Jšœœ˜Jšœ œ˜$J˜,J˜Jšœ œœ˜Jšœ%œ˜*J˜J˜Jšœ'™'˜šÏnœœ˜J˜Jšœœœ˜J˜"J˜ Jšœœœ'˜8Jšœœœ˜*Jšœœœ˜$šœ˜Jšœœ œ˜$šœœ˜šœ,œ˜4Jšœ,œœœ˜?J˜—š˜Jšœœœ ˜.šœ˜ J˜J˜Jšœœœ˜7Jšœœœ˜0šœ˜Jšœ0˜0J˜šœœœ˜Jšœœœœ˜?J˜J˜—Jšœ ™ J˜š ž œœœœœ œ˜