-- File: CmFilesA.mesa - last edit: -- Smokey, Oct 17, 1979 1:02 PM -- Evans, Aug 19, 1980 10:44 AM -- Tom, Aug 28, 1979 6:01 PM -- Mark, September 5, 1980 1:15 AM -- Karlton, September 5, 1980 1:13 AM -- Levin, 8-Mar-82 17:12:37 DIRECTORY Ascii USING [ControlZ, CR, NUL, SP, TAB], CmFile USING [ErrorCode, Lop], Compatibility USING [SHandle], Segments, Streams, String USING [ AppendChar, EquivalentString, EquivalentSubString, StringBoundsFault, StringToDecimal, SubString, SubStringDescriptor], Storage USING [CopyString, FreeStringNil, String]; CmFilesA: PROGRAM IMPORTS CmFile, Segments, Streams, String, Storage EXPORTS CmFile = BEGIN -- Global Data userDotCmName: STRING = "User.cm"; lineSize: CARDINAL = 250; CmFileInfo: TYPE = RECORD [ keepOpen: BOOLEAN _ FALSE, fh: Segments.FHandle _ NIL, sh: Streams.Handle _ NIL, fileName: STRING _ NIL]; CmFileInfoHandle: TYPE = POINTER TO CmFileInfo; userDotCmInfo: CmFileInfo _ [fileName: userDotCmName]; otherCmInfo: CmFileInfo _ []; lastOpenedCFI: CmFileInfoHandle _ NIL; -- Current file open info quote: CHARACTER = '"; -- SIGNALs Error: PUBLIC SIGNAL [code: CmFile.ErrorCode] = CODE; -- Client collusion -- UserDotCmSetFHHint: PUBLIC PROCEDURE [file: Segments.FHandle] = { -- Segments.LockFile[file]}; -- Simple Interface Procedures Open: PUBLIC PROCEDURE [fileName: STRING] = BEGIN FileOpen[fileName]; lastOpenedCFI.keepOpen _ TRUE; END; Close: PUBLIC PROCEDURE [fileName: STRING] = BEGIN FileClose[fileName]; lastOpenedCFI.keepOpen _ FALSE; END; Line: PUBLIC PROCEDURE [fileName, title, name: STRING] RETURNS [s: STRING] = BEGIN localLine: STRING _ [lineSize]; card: CARDINAL; IF ~OpenAndMatch[localLine, fileName, title, name] THEN s _ NIL ELSE BEGIN s _ Storage.String[localLine.length-name.length]; card _ name.length+2; WHILE card EXIT]) FROM terminator, Ascii.ControlZ, Ascii.CR => EXIT; Ascii.SP, Ascii.TAB => NULL; ENDCASE => started _ TRUE; IF started THEN String.AppendChar[buffer, c ! String.StringBoundsFault => GOTO TooMany]; ENDLOOP; IF c=Ascii.ControlZ THEN WHILE c # Ascii.CR DO c _ Streams.GetByte[sh ! Streams.End[] => EXIT] ENDLOOP; EXITS TooMany => NULL; END; TitleMatch: PUBLIC PROCEDURE [buffer, title: STRING] RETURNS [BOOLEAN] = BEGIN llSubString, titleSubString: String.SubStringDescriptor; IF buffer.length < title.length+2 THEN RETURN[FALSE]; IF buffer[0] # '[ THEN RETURN[FALSE]; IF buffer[title.length+1] # '] THEN RETURN[FALSE]; llSubString _ [buffer, 1, title.length]; titleSubString _ [title, 0, title.length]; RETURN[String.EquivalentSubString[@llSubString, @titleSubString]] END; -- More efficient ones OpenSection: PUBLIC PROCEDURE [fileName, title: STRING] RETURNS [BOOLEAN] = BEGIN Open[fileName ! Segments.FileNameProblem[] => GOTO badFile]; IF FindSection[title] THEN RETURN[TRUE]; RETURN[FALSE]; EXITS badFile => RETURN[FALSE]; END; NextItem: PUBLIC PROCEDURE RETURNS[name, args: STRING] = BEGIN terminator: CHARACTER; localLine: STRING _ [lineSize]; name _ args _ NIL; terminator _ ReadLineOrToken[lastOpenedCFI.sh, localLine, ':]; IF terminator # ': THEN BEGIN [] _ ReadLineOrToken[lastOpenedCFI.sh, localLine, Ascii.CR]; RETURN[NIL,NIL]; END; name _ Storage.CopyString[localLine]; [] _ ReadLineOrToken[lastOpenedCFI.sh, localLine, Ascii.CR]; args _ IF localLine.length = 0 THEN NIL ELSE Storage.CopyString[localLine]; RETURN[name, args]; END; -- Use "User.cm" as fileName in above UserDotCmOpen: PUBLIC PROCEDURE = BEGIN Open[userDotCmName]; END; UserDotCmClose: PUBLIC PROCEDURE = BEGIN Close[userDotCmName]; END; UserDotCmLine: PUBLIC PROCEDURE [title, name: STRING] RETURNS [STRING] = BEGIN RETURN[Line[fileName: userDotCmName, title: title, name: name]]; END; UserDotCmOpenSection: PUBLIC PROCEDURE [title: STRING] RETURNS [BOOLEAN] = BEGIN RETURN[OpenSection[userDotCmName, title]]; END; UserDotCmNextItem: PUBLIC PROCEDURE RETURNS[name, args: STRING] = BEGIN -- Support pulling entries out of two files at the same time since it is -- not too hard and some clown will probably try it. saveCFI: CmFileInfoHandle _ lastOpenedCFI; lastOpenedCFI _ @userDotCmInfo; [name, args] _ NextItem[]; lastOpenedCFI _ saveCFI; RETURN[name, args]; END; -- Private Procedures FileClose: PRIVATE PROCEDURE [fileName: STRING] = BEGIN IF lastOpenedCFI = NIL THEN {SIGNAL Error[noneOpen]; RETURN}; IF ~String.EquivalentString[fileName, lastOpenedCFI.fileName] THEN SIGNAL Error[notOpen]; IF lastOpenedCFI.sh # NIL THEN Streams.Destroy[lastOpenedCFI.sh]; -- There are other pointers to lastOpenedCFI^, so NIL the freed pointers! lastOpenedCFI.fh _ NIL; lastOpenedCFI.sh _ NIL; END; FileOpen: PRIVATE PROCEDURE [fileName: STRING] = BEGIN isUserDotCm: BOOLEAN = IsUserDotCm[fileName]; IF isUserDotCm THEN lastOpenedCFI _ @userDotCmInfo ELSE BEGIN lastOpenedCFI _ @otherCmInfo; IF ~String.EquivalentString[fileName, lastOpenedCFI.fileName] THEN BEGIN IF lastOpenedCFI.keepOpen THEN BEGIN SIGNAL Error[multipleOpens]; FileClose[lastOpenedCFI.fileName]; lastOpenedCFI.keepOpen _ FALSE; END; lastOpenedCFI.fileName _ Storage.FreeStringNil[lastOpenedCFI.fileName]; lastOpenedCFI.fh _ NIL; -- Different file => bad FHandle END; END; IF lastOpenedCFI.keepOpen THEN {Streams.SetIndex[lastOpenedCFI.sh, 0]; RETURN}; IF lastOpenedCFI.fh = NIL THEN lastOpenedCFI.fh _ Segments.NewFile[fileName, Segments.Read]; lastOpenedCFI.sh _ Streams.CreateStream[lastOpenedCFI.fh, Segments.Read ! Segments.FileUnknown[] => BEGIN Segments.ReleaseFile[lastOpenedCFI.fh]; lastOpenedCFI.fh _ Segments.NewFile[fileName, Segments.Read]; RETRY; END]; IF lastOpenedCFI.fileName = NIL THEN lastOpenedCFI.fileName _ Storage.CopyString[fileName]; END; FindLineGivenSection: PRIVATE PROCEDURE [localLine, name: STRING] RETURNS [BOOLEAN] = BEGIN DO [] _ ReadLineOrToken[lastOpenedCFI.sh, localLine, Ascii.CR]; IF localLine.length = 0 THEN RETURN[FALSE]; IF localLine[0]='[ THEN RETURN[FALSE]; IF NameMatch[localLine, name] THEN RETURN[TRUE]; ENDLOOP; END; FindSection: PRIVATE PROCEDURE [title: STRING] RETURNS [BOOLEAN] = BEGIN temp: STRING _ [lineSize]; DO IF ReadLineOrToken[lastOpenedCFI.sh, temp, Ascii.CR] = Ascii.NUL THEN RETURN[FALSE]; IF temp.length # 0 AND TitleMatch[temp, title] THEN RETURN[TRUE]; ENDLOOP; END; IsUserDotCm: PRIVATE PROCEDURE [fileName: STRING] RETURNS [b: BOOLEAN] = BEGIN oldLength: CARDINAL = fileName.length; IF fileName[fileName.length-1] = '. THEN fileName.length _ fileName.length - 1; b _ String.EquivalentString[fileName, userDotCmName]; fileName.length _ oldLength; END; NameMatch: PRIVATE PROCEDURE [localLine, name: STRING] RETURNS [BOOLEAN] = BEGIN llSubString, nameSubString: String.SubStringDescriptor; IF localLine.length < name.length+2 THEN RETURN[FALSE]; IF localLine[name.length] # ': THEN RETURN[FALSE]; IF localLine[name.length+1] # Ascii.SP THEN RETURN[FALSE]; llSubString _ [localLine, 0, name.length]; nameSubString _ [name, 0, name.length]; RETURN[String.EquivalentSubString[@llSubString, @nameSubString]] END; OpenAndMatch: PRIVATE PROCEDURE [localLine, fileName, title, name: STRING] RETURNS [BOOLEAN] = INLINE BEGIN FileOpen[fileName ! Segments.FileNameError => GOTO badFile; Error => RESUME]; RETURN[(FindSection[title] AND FindLineGivenSection[localLine, name])]; EXITS badFile => RETURN[FALSE]; END; RealGetNextToken: PRIVATE PROCEDURE [ source: String.SubString, token: STRING, number: BOOLEAN] RETURNS [valid: BOOLEAN] = BEGIN started: BOOLEAN _ FALSE; c: CHARACTER; token.length _ 0; DO IF source.length = 0 THEN EXIT; SELECT c _ CmFile.Lop[source] FROM Ascii.SP, Ascii.TAB => IF started THEN EXIT; Ascii.CR, ': => EXIT; '-, IN ['0..'9], 'B, 'D => BEGIN IF token.length < token.maxlength THEN {token[token.length] _ c; token.length _ token.length + 1}; started _ TRUE; END; ENDCASE => BEGIN IF number THEN EXIT; IF token.length < token.maxlength THEN {token[token.length] _ c; token.length _ token.length + 1}; started _ TRUE; END; ENDLOOP; RETURN[token.length # 0]; END; END. -- of CmFilesA