-- file: InstallLaurel.Mesa -- edited by Levin, January 16, 1981 11:14 AM. -- edited by Brotz, March 7, 1983 4:04 PM -- edited by Schroeder, March 14, 1981 12:36 PM. -- edited by Taft, May 10, 1983 6:31 PM DIRECTORY AltoDefs USING [BytesPerPage, PageCount, PageNumber, PageSize], AltoDisplay USING [MaxScanLines], AltoFileDefs USING [fillinDA, FP, LD, NullFP, vDA], Ascii USING [ControlZ, CR, TAB], BFSDefs USING [ActOnPages, GetNextDA], ccD: FROM "ChollaCmdDefs" USING [ChollaMailProcess], ControlDefs USING [FrameCodeBase, GlobalFrame, GlobalFrameHandle, MainBodyIndex, PrefixHandle], Core USING [FreeCacheEntry, InsertInFileCache, LookupInFileCache], csD: FROM "CoreStreamDefs" USING [Close, EndOfStream, GetLength, GetPosition, OpenFromName, Position, Read, ReadBlock, SetPosition, StreamHandle, Write, WriteBlock], DiskDefs USING [DiskRequest], displayCommon USING [bitMapReady, charPropertyTable], DMSTimeDefs USING [MapPackedTimeToTimeZoneString, timeStringLength], drD: FROM "LaurelDriverDefs" USING [InstallError], dsD: FROM "DisplayDefs" USING [backgtype, CursorBitMap, cursorBM, LegalCharacters, lineHeight], exD: FROM "ExceptionDefs" USING [SysBug], FrameDefs USING [GlobalFrame, IsBound], FrameOps USING [CodeHandle], inD: FROM "InteractorDefs" USING [BoundarySetArray, BuildScreenStructures, leftMargin, TextSelection], Inline USING [COPY, LowHalf], intCommon USING [actionPoint, audioEnabled, autoConfirm, bluePendingDelete, boundarySet, bugReportee, cForCopies, chollaArchiveFilePath, chollaDL, cmTextNbr, commandMode, commandType, composedMessageEdited, continuousScrollDelay, continuousScrollTimeOut, controlRedEnabled, currentCommand, currentSelection, dateLeftX, dateRightX, defaultHardCopies, defaultHardcopyFormName, deliverCommandVisible, deliverWithCR, disableWriting, displayAfterDelete, editorMenuState, editorType, exceptionType, fromLeftX, fromRightX, gvTestingMode, hardCopies, hardcopyHost, hardcopyInstallError, hardcopyUserName, imageFileName, isCholla, leafOk, mailcheckPollingInterval, multiClickTimeOut, newFormAfterDelivery, newMailTune, nextBracketDelay, nextBracketTimeout, numScanLines, passwordPrinting, passwordPrintingDefault, pendingDeleteSetByControl, profileRegistry, profileRetrieve, profileRetrieveMode, profileSend, profileSendMode, remoteFilePath, runCommandMode, runPath, secondarySelectionEnabled, source, subjectExtensionLeftX, subjectLeftX, tapTimeOut, target, timeMayBeBogus, twoSidedPrinting, twoSidedPrintingDefault, workstationName], LaurelHardcopyDefs USING [FontCode, FontError, InitHardcopyFonts, InstallHardcopy, ParseFont, ParseHardcopyForm], lsD: FROM "LaurelStateDefs" USING [AllocateStateNode, AllocateStateString, DefineStateSegment, GetWrittenTime, InstallSegments, PageCount, PageNumber, ReleaseStateSegment, StateHeader, SwapInStateSegment, WriteStateSegment], MailParseDefs USING [FinalizeParse, GetFieldBody, GetFieldName, InitializeParse, ParseError, ParseHandle, ParseNameList], MiscDefs USING [SetBlock], ovD: FROM "OverviewDefs" USING [LineBreakValue], SegmentDefs USING [CopyDataToFileSegment, DataSegmentAddress, DataSegmentHandle, DefaultAccess, DefaultBase, DeleteDataSegment, DeleteFileSegment, FileHandle, FileSegmentAddress, FileSegmentHandle, GetEndOfFile, GetFileSegmentDA, InsertFile, NewDataSegment, NewFile, NewFileSegment, OldFileOnly, PageCount, PageFromAddress, PageNumber, Read, ReadWrite, ReadWriteAppend, SetEndOfFile, SwapIn, Unlock, Write], Storage USING [PagesForWords, StringLength], StringDefs USING [AppendDecimal, AppendString, BcplSTRING, BcplToMesaString, EquivalentString, InvalidNumber, StringToDecimal, WordsForString], SwapperOps USING [FreePage, Update], TimeDefs USING [CurrentDayTime], VMDefs USING [CantOpen, CloseFile, OpenFile]; InstallLaurel: PROGRAM IMPORTS BFSDefs, ccD, Core, csD, disC: displayCommon, DMSTimeDefs, exD, Inline, intC: intCommon, inD, FrameDefs, FrameOps, LaurelHardcopyDefs, lsD, MailParseDefs, MiscDefs, SegmentDefs, Storage, StringDefs, SwapperOps, TimeDefs, VMDefs EXPORTS drD, lsD SHARES lsD = BEGIN OPEN drD; stateFH: SegmentDefs.FileHandle; stateHeaderSeg: SegmentDefs.FileSegmentHandle; stateHeader: POINTER TO lsD.StateHeader; stateSegPages: AltoDefs.PageCount; heapFirstFree: POINTER; heapLimit: POINTER; profileInUserCm: BOOLEAN; videoBackground: PUBLIC dsD.backgtype; log: csD.StreamHandle ← NIL; logStartPosition: csD.Position; InitializeState: PROCEDURE[imageFile: SegmentDefs.FileHandle, heapDS: SegmentDefs.DataSegmentHandle] = BEGIN OPEN SegmentDefs; GetGlobalFrameSize: PROCEDURE[link: UNSPECIFIED] RETURNS [length: CARDINAL] = BEGIN OPEN ControlDefs; frame: GlobalFrameHandle ← FrameDefs.GlobalFrame[link]; codeSeg: FileSegmentHandle ← FrameOps.CodeHandle[frame]; seg: FileSegmentHandle ← NewFileSegment[codeSeg.file, codeSeg.base, codeSeg.pages, Read]; fcb: FrameCodeBase ← frame.code; prefix: PrefixHandle; p: POINTER; SwapIn[seg]; IF fcb.out THEN fcb.out ← FALSE ELSE fcb.shortbase ← fcb.shortbase - LOOPHOLE[FileSegmentAddress[codeSeg], CARDINAL]; prefix ← p ← FileSegmentAddress[seg] + fcb.offset; length ← (p+prefix.entry[MainBodyIndex].initialpc-1)↑; Unlock[seg]; DeleteFileSegment[seg]; END; -- of GetGlobalFrameSize -- intCLength: CARDINAL = GetGlobalFrameSize[intC] - SIZE[ControlDefs.GlobalFrame]; disCLength: CARDINAL = GetGlobalFrameSize[disC] - SIZE[ControlDefs.GlobalFrame]; imageFileLength: CARDINAL = GetEndOfFile[imageFile].page; daTableLength: CARDINAL = imageFileLength + 3; diskrequest: DiskDefs.DiskRequest; scratchSeg: DataSegmentHandle = NewDataSegment[DefaultBase, 1]; DAs: DESCRIPTOR FOR ARRAY [-1 .. 0) OF AltoFileDefs.vDA; stateFP: AltoFileDefs.FP; stateFileName: STRING = "Laurel.state"L; heapLimit ← DataSegmentAddress[heapDS]; heapFirstFree ← heapLimit + AltoDefs.PageSize * heapDS.pages - 1; IF (stateFP ← Core.LookupInFileCache[stateFileName]) = AltoFileDefs.NullFP THEN BEGIN VMDefs.CloseFile[VMDefs.OpenFile[name: stateFileName, options: new]]; stateFH ← NewFile[stateFileName, ReadWriteAppend, OldFileOnly]; [] ← Core.InsertInFileCache[stateFileName, stateFH.fp, TRUE]; END ELSE stateFH ← InsertFile[@stateFP, ReadWriteAppend]; stateSegPages ← Storage.PagesForWords[SIZE[lsD.StateHeader] + intCLength + disCLength]; SetEndOfFile[stateFH, stateSegPages, AltoDefs.BytesPerPage]; stateHeaderSeg ← NewFileSegment[stateFH, 1, stateSegPages, ReadWrite]; SwapIn[stateHeaderSeg]; stateHeader ← FileSegmentAddress[stateHeaderSeg]; stateHeader.cachedHeapTop ← heapDS.VMpage + heapDS.pages; stateHeader.imageFP ← imageFile.fp; stateHeader.imageTime ← lsD.GetWrittenTime[imageFile]; stateHeader.intCOffset ← SIZE[lsD.StateHeader]; stateHeader.disCOffset ← stateHeader.intCOffset + intCLength; stateHeader.headerFF ← stateHeader.disCOffset + disCLength; stateHeader.imageDATableSeg ← lsD.DefineStateSegment[Storage.PagesForWords[daTableLength]]; DAs ← DESCRIPTOR [lsD.SwapInStateSegment[stateHeader.imageDATableSeg], daTableLength]; diskrequest ← DiskDefs.DiskRequest [ ca: DataSegmentAddress[scratchSeg], fixedCA: TRUE, da: @DAs[0], fp: @imageFile.fp, firstPage: 0, lastPage: imageFileLength, action: ReadD, lastAction: ReadD, signalCheckError: FALSE, option: update[cleanup: BFSDefs.GetNextDA]]; MiscDefs.SetBlock[@DAs[-1], AltoFileDefs.fillinDA, daTableLength]; DAs[0] ← imageFile.fp.leaderDA; [] ← BFSDefs.ActOnPages[LOOPHOLE[@diskrequest]]; lsD.WriteStateSegment[stateHeader.imageDATableSeg]; lsD.ReleaseStateSegment[stateHeader.imageDATableSeg]; DeleteDataSegment[scratchSeg]; END; -- of InitializeState -- InstallState: PROCEDURE [imageFile: SegmentDefs.FileHandle, heapDS: SegmentDefs.DataSegmentHandle] = BEGIN OPEN SegmentDefs; profileFP: AltoFileDefs.FP ← Core.LookupInFileCache[IF profileInUserCm THEN "User.cm"L ELSE "Laurel.profile"L]; profileFile: FileHandle ← IF profileFP = AltoFileDefs.NullFP THEN NIL ELSE InsertFile[@profileFP, DefaultAccess]; fontsWidthsFP: AltoFileDefs.FP ← Core.LookupInFileCache["Fonts.widths"L]; fontsWidthsFile: FileHandle ← IF fontsWidthsFP = AltoFileDefs.NullFP THEN NIL ELSE InsertFile[@fontsWidthsFP, DefaultAccess]; lowestHeapPage: PageNumber = PageFromAddress[heapFirstFree + 1]; heapPages: PageCount = heapDS.pages - (lowestHeapPage - heapDS.VMpage); heapFS: FileSegmentHandle; ShrinkHeap: PROCEDURE = BEGIN IF lowestHeapPage = heapDS.VMpage THEN RETURN; SwapperOps.Update [heapDS.VMpage, lowestHeapPage - heapDS.VMpage, SwapperOps.FreePage, FALSE]; heapDS.VMpage ← lowestHeapPage; heapDS.pages ← heapPages; END; -- of ShrinkHeap -- stateHeader.profileFP ← profileFP; IF profileFile # NIL THEN BEGIN stateHeader.profileTime ← lsD.GetWrittenTime[profileFile]; stateHeader.profileInUserCm ← profileInUserCm; END; stateHeader.fontsWidthsFP ← fontsWidthsFP; IF fontsWidthsFile # NIL THEN stateHeader.fontsWidthsTime ← lsD.GetWrittenTime[fontsWidthsFile]; Inline.COPY[to: stateHeader + stateHeader.intCOffset, from: @LOOPHOLE[intC, ControlDefs.GlobalFrameHandle].global[0], nwords: stateHeader.disCOffset - stateHeader.intCOffset]; Inline.COPY[to: stateHeader + stateHeader.disCOffset, from: @LOOPHOLE[disC, ControlDefs.GlobalFrameHandle].global[0], nwords: stateHeader.headerFF-stateHeader.disCOffset]; ShrinkHeap[]; lsD.InstallSegments[stateHeader]; stateHeader.heapSegFirstPage ← GetEndOfFile[stateFH].page+1; stateHeader.heapSegPages ← heapPages; SetEndOfFile[stateFH, stateHeader.heapSegFirstPage+heapPages-1, AltoDefs.BytesPerPage]; heapFS ← NewFileSegment [stateFH, stateHeader.heapSegFirstPage, stateHeader.heapSegPages, Read + Write]; CopyDataToFileSegment[heapDS, heapFS]; -- writes heap to state file stateHeader.heapSegDA ← GetFileSegmentDA[heapFS]; DeleteFileSegment[heapFS]; stateHeaderSeg.write ← TRUE; Unlock[stateHeaderSeg]; DeleteFileSegment[stateHeaderSeg]; END; -- of InstallState -- AllocateStateNode: PUBLIC PROCEDURE[size: CARDINAL] RETURNS [base: POINTER] = BEGIN base ← heapFirstFree-size+1; IF LOOPHOLE[base, CARDINAL] < LOOPHOLE[heapLimit, CARDINAL] THEN exD.SysBug[]; heapFirstFree ← base-1; END; -- of AllocateStateNode -- AllocateStateString: PUBLIC PROCEDURE [chars: CARDINAL] RETURNS [p: STRING] = BEGIN p ← AllocateStateNode[StringDefs.WordsForString[chars]]; p↑ ← StringBody[length: 0, maxlength: chars, text: ]; END; -- of AllocateStateString -- BuildInteractor: PROCEDURE = -- establishes static structures for interactor BEGIN InstallFont[]; inD.BuildScreenStructures[]; disC.bitMapReady ← FALSE; intC.composedMessageEdited ← FALSE; intC.timeMayBeBogus ← FALSE; intC.currentCommand ← NIL; intC.commandType ← noCommand; intC.editorMenuState ← singleLine; intC.target ← intC.source ← inD.TextSelection[mnp: intC.cmTextNbr, start: 0, end: 0, point: 0, key: 0, mode: char, pendingDelete: FALSE]; intC.actionPoint ← 0; intC.currentSelection ← target; intC.commandMode ← TRUE; intC.runCommandMode ← FALSE; intC.secondarySelectionEnabled ← FALSE; intC.deliverCommandVisible ← TRUE; END; -- of BuildInteractor -- InstallFont: PROCEDURE = -- Assumes 'font' points to the font. Sets the charPropTable to describe the font, assuming -- the smudges have already been installed. BEGIN ch: CHARACTER; i: CARDINAL; whiteString: STRING = " "L; punctuationString: STRING = "!@#$%~&*()-`=+[{]};:'"",.<>/?\|←↑"L; FOR ch IN dsD.LegalCharacters DO disC.charPropertyTable[ch] ← alphaNumeric; ENDLOOP; disC.charPropertyTable[140C] ← punctuation; FOR i IN [0 .. whiteString.length) DO disC.charPropertyTable[whiteString[i]] ← white; ENDLOOP; disC.charPropertyTable[Ascii.TAB + (ovD.LineBreakValue - 0C)] ← white; disC.charPropertyTable[Ascii.CR + (ovD.LineBreakValue - 0C)] ← white; FOR i IN [0 .. punctuationString.length) DO disC.charPropertyTable[punctuationString[i]] ← punctuation; ENDLOOP; END; -- of InstallFont -- ReadLaurelProfile: PROCEDURE RETURNS [installError: drD.InstallError] = -- Opens Laurel.Profile, parses it, and extracts information for intCommon. BEGIN OPEN StringDefs; keyWord: STRING ← [50]; value: STRING ← [100]; laurelProfileHandle: csD.StreamHandle; pH: MailParseDefs.ParseHandle; start: csD.Position ← 0; eof: csD.Position; offEnd: CARDINAL; crSeen: BOOLEAN; -- A LARGE number of local procedures for ReadLaurelProfile follow -- ProcessUserCm: PROCEDURE [section: STRING, proc: PROCEDURE] RETURNS[found: BOOLEAN] = BEGIN AdvancePastCR: PROCEDURE RETURNS [BOOLEAN] = BEGIN DO IF csD.Read[laurelProfileHandle ! csD.EndOfStream => EXIT] = Ascii.CR THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE] END; -- of AdvancePastCR -- found ← TRUE; laurelProfileHandle ← csD.OpenFromName["User.cm"L, byte, read ! VMDefs.CantOpen => {found ← FALSE; CONTINUE}]; IF ~found THEN RETURN; found ← FALSE; BEGIN ENABLE UNWIND => csD.Close[laurelProfileHandle]; DO SELECT csD.Read[laurelProfileHandle ! csD.EndOfStream => GO TO Done] FROM '[ => BEGIN sectionHeading: STRING ← [9]; sectionHeading.length ← csD.ReadBlock[laurelProfileHandle, @sectionHeading.text, 0, section.length]; IF sectionHeading.length ~= section.length THEN GO TO Done; -- eof IF EquivalentString[sectionHeading, section] THEN EXIT; csD.SetPosition[laurelProfileHandle, csD.GetPosition[laurelProfileHandle] - section.length]; END; Ascii.CR => LOOP; ENDCASE; IF ~AdvancePastCR[] THEN GO TO Done; -- section not found ENDLOOP; -- [section] found; scan past CR IF ~AdvancePastCR[] THEN GO TO Done; -- malformed user.cm; ignore it. found ← TRUE; start ← csD.GetPosition[laurelProfileHandle]; eof ← csD.GetLength[laurelProfileHandle]; DO SELECT csD.Read[laurelProfileHandle ! csD.EndOfStream => EXIT] FROM '[ => {eof ← csD.GetPosition[laurelProfileHandle] - 1; EXIT}; Ascii.CR => NULL; ENDCASE => IF ~AdvancePastCR[] THEN EXIT; ENDLOOP; csD.SetPosition[laurelProfileHandle, start]; FOR i: csD.Position IN [start..eof) DO IF csD.Read[laurelProfileHandle] ~= Ascii.CR THEN {csD.SetPosition[laurelProfileHandle, i]; EXIT}; ENDLOOP; proc[]; END; -- ENABLE -- GO TO Done; EXITS Done => csD.Close[laurelProfileHandle]; END; -- of ProcessUserCm -- ProcessLaurelProfile: PROCEDURE RETURNS [found: BOOLEAN] = BEGIN found ← TRUE; laurelProfileHandle ← csD.OpenFromName["Laurel.Profile"L, byte, read ! VMDefs.CantOpen => {found ← FALSE; CONTINUE}]; IF ~found THEN RETURN; BEGIN ENABLE UNWIND => csD.Close[laurelProfileHandle]; start ← 0; eof ← csD.GetLength[laurelProfileHandle]; FOR i: csD.Position IN [start..eof) DO IF csD.Read[laurelProfileHandle] ~= Ascii.CR THEN {csD.SetPosition[laurelProfileHandle, i]; EXIT}; ENDLOOP; DoLaurelPart[]; END; -- ENABLE -- csD.Close[laurelProfileHandle]; END; -- of ProcessLaurelProfile -- InitParse: PROCEDURE = BEGIN offEnd ← 0; crSeen ← FALSE; pH ← MailParseDefs.InitializeParse[GetProfileChar]; END; -- of InitParse -- GetProfileChar: PROCEDURE RETURNS [char: CHARACTER] = BEGIN OPEN Ascii; inBravoTrailer: BOOLEAN ← FALSE; DO IF offEnd > 0 OR csD.GetPosition[laurelProfileHandle] >= eof THEN GO TO noChar; IF (char ← csD.Read[laurelProfileHandle]) ~= CR THEN BEGIN IF ~inBravoTrailer THEN IF char = ControlZ THEN inBravoTrailer ← TRUE ELSE EXIT; END ELSE IF ~crSeen THEN EXIT; REPEAT noChar => {char ← CR; offEnd ← offEnd + 1}; ENDLOOP; crSeen ← char = CR; END; -- of GetProfileChar -- WriteStringToLog: PROCEDURE [s: STRING] = BEGIN IF log = NIL THEN InitLog[]; csD.WriteBlock[log, @s.text, 0, s.length]; END; -- of WriteStringToLog -- WriteCharToLog: PROCEDURE [char: CHARACTER] = BEGIN IF log = NIL THEN InitLog[]; csD.Write[log, char]; END; -- of WriteCharToLog -- WriteLogEntry: PROCEDURE [s: STRING] = BEGIN WriteStringToLog[s]; WriteStringToLog[" "L]; END; -- of WriteLogEntry -- FinishLogEntry: PROCEDURE = {WriteLogEntry[""L]}; WriteBadValueMsgInLog: PROCEDURE [fieldName: STRING] = BEGIN WriteStringToLog["The '"L]; WriteStringToLog[fieldName]; WriteLogEntry["' entry in the profile is specified incorrectly."L]; END; -- WriteBadValueMsgInLog -- InitLog: PROCEDURE = BEGIN preamble: STRING = "*start* 00000 00024 UU Date: "L; postamble: STRING = " Subject: Installation Difficulties From: Laurel Laurel discovered the following problem(s) during installation: "L; dateString: STRING ← [DMSTimeDefs.timeStringLength]; log ← csD.OpenFromName["InstallErrors.mail"L, byte, append]; logStartPosition ← csD.GetPosition[log]; csD.WriteBlock[log, @preamble.text, 0, preamble.length]; DMSTimeDefs.MapPackedTimeToTimeZoneString [LOOPHOLE[TimeDefs.CurrentDayTime[]], dateString, arpaMsg]; WriteStringToLog[dateString]; WriteStringToLog[postamble]; installError ← inLog; END; -- of InitLog -- FinishLog: PROCEDURE = BEGIN IF installError ~= none THEN BEGIN s: STRING ← [5]; csD.SetPosition[log, logStartPosition + 8]; AppendDecimal[s, Inline.LowHalf[csD.GetLength[log] - logStartPosition]]; THROUGH [0 .. 5 - s.length) DO WriteCharToLog['0] ENDLOOP; WriteStringToLog[s]; csD.Close[log]; END; END; -- of FinishLog -- DoLaurelPart: PROCEDURE = BEGIN DO IF ~MailParseDefs.GetFieldName[pH, keyWord] THEN EXIT; BEGIN -- for EXITS -- FOR i: CARDINAL IN [0 .. nStringOptions) DO IF EquivalentString[keyWord, stringOptions[i].key] THEN {FillFromProfile[stringOptions[i].stringP]; GO TO found}; ENDLOOP; FOR i: CARDINAL IN [0 .. nPreValueSpecialOptions) DO IF EquivalentString[keyWord, preValueSpecialOptions[i].key] THEN {preValueSpecialOptions[i].proc[]; GO TO found}; ENDLOOP; MailParseDefs.GetFieldBody[pH, value]; FOR i: CARDINAL IN [0 .. nDecommissionedOptions) DO IF EquivalentString[keyWord, decommissionedOptions[i]] THEN {WriteStringToLog["The profile field '"L]; WriteStringToLog[keyWord]; WriteLogEntry["' is no longer used."L]; GO TO found}; ENDLOOP; FOR i: CARDINAL IN [0 .. nBooleanOptions) DO IF EquivalentString[keyWord, booleanOptions[i].key] THEN {booleanOptions[i].boolP↑ ← EquivalentString[value, "TRUE"L] OR EquivalentString[value, "Yes"L]; GO TO found}; ENDLOOP; FOR i: CARDINAL IN [0 .. nTicksOptions) DO IF EquivalentString[keyWord, ticksOptions[i].key] THEN {ticksOptions[i].cardP↑ ← (StringToDecimal [value ! InvalidNumber => {ReportInvalidNumber[]; CONTINUE}] + 37) / 38; GO TO found}; ENDLOOP; FOR i: CARDINAL IN [0 .. nFieldOptions) DO IF EquivalentString[keyWord, fieldOptions[i].key] THEN {fieldOptions[i].cardP↑ ← inD.leftMargin + StringToDecimal [value ! InvalidNumber => {ReportInvalidNumber[]; CONTINUE}]; GO TO found}; ENDLOOP; FOR i: CARDINAL IN [0 .. nPostValueSpecialOptions) DO IF EquivalentString[keyWord, postValueSpecialOptions[i].key] THEN {postValueSpecialOptions[i].proc[]; GO TO found}; ENDLOOP; WriteCharToLog['']; WriteStringToLog[keyWord]; WriteLogEntry["' in the profile is unrecognizable."L]; EXITS found => NULL; END; ENDLOOP; END; -- of DoLaurelPart -- DoHardcopyPart: PROCEDURE = BEGIN discard: STRING ← [0]; DO IF ~MailParseDefs.GetFieldName[pH, keyWord] THEN EXIT; SELECT TRUE FROM EquivalentString[keyWord, "Press"L] => FillFromProfile[@intC.hardcopyHost]; EquivalentString[keyWord, "PrintedBy"L] => FillFromProfile[@intC.hardcopyUserName]; ENDCASE => MailParseDefs.GetFieldBody[pH, discard]; ENDLOOP; END; -- of DoHardcopyPart -- FillFromProfile: PROCEDURE [s: POINTER TO STRING] = BEGIN MailParseDefs.GetFieldBody[pH, value]; FillProfileString[s, value]; END; -- of FillFromProfile -- FillProfileString: PROCEDURE [s: POINTER TO STRING, value: STRING] = BEGIN IF s↑ # NIL THEN RETURN; s↑ ← AllocateStateString[value.length]; s↑.length ← 0; StringDefs.AppendString[s↑, value]; END; -- of FillProfileString -- ProcessFontError: PROCEDURE [code: LaurelHardcopyDefs.FontCode] = BEGIN SELECT code FROM profileBadFont => -- ParseFont only BEGIN WriteStringToLog["The profile Font entry '"L]; WriteStringToLog[value]; WriteLogEntry["' does not contain a valid font number."L]; END; badFontsWidths => WriteLogEntry["The file 'Fonts.Widths' is missing or unreadable."L]; fontNotInFontsWidths => BEGIN IF value.length = 0 THEN WriteStringToLog["A default font"L] ELSE {WriteCharToLog['']; WriteStringToLog[value]; WriteCharToLog['']}; WriteLogEntry[" is not in Fonts.Widths."L]; END; ENDCASE => exD.SysBug[]; intC.hardcopyInstallError ← TRUE; END; -- of ProcessFontError -- FontProc: PROCEDURE = BEGIN MailParseDefs.GetFieldBody[pH, value, TRUE]; LaurelHardcopyDefs.ParseFont[value ! LaurelHardcopyDefs.FontError => {ProcessFontError[code]; CONTINUE}]; END; -- of FontProc -- HardcopyFormProc: PROCEDURE = {LaurelHardcopyDefs.ParseHardcopyForm[pH]}; BoundaryProc: PROCEDURE = BEGIN bad: BOOLEAN ← FALSE; item: {command, toc, dm, cm, bad} ← command; keyNo: CARDINAL; ProcessOne: PROC [n, r: STRING, isFile, nested: BOOLEAN] RETURNS [BOOLEAN] = BEGIN val: CARDINAL; IF n.length = 0 OR r.length # 0 OR nested THEN GO TO bogus; val ← StringToDecimal[n ! InvalidNumber => GO TO bogus]; SELECT item FROM command => IF (keyNo ← val) ~IN [0 .. 9] THEN GO TO bogus; toc => intC.boundarySet[keyNo].toc ← val; dm => intC.boundarySet[keyNo].dm ← val; cm => intC.boundarySet[keyNo].cm ← val; ENDCASE => GO TO bogus; item ← SUCC[item]; RETURN[FALSE]; EXITS bogus => {bad ← TRUE; RETURN[FALSE]}; END; -- of ProcessOne -- MailParseDefs.ParseNameList[ph: pH, process: ProcessOne]; IF bad THEN WriteBadValueMsgInLog[keyWord]; END; -- of BoundaryProc -- ReportInvalidNumber: PROCEDURE = BEGIN WriteStringToLog[value]; WriteStringToLog[" in "L]; WriteStringToLog[keyWord]; WriteLogEntry[" profile entry is an invalid number."L]; END; -- ReportInvalidNumber -- Ignore: PROCEDURE = {}; SetPoll: PROCEDURE = BEGIN pollingInterval: CARDINAL ← 300; pollingInterval ← StringToDecimal [value ! InvalidNumber => {ReportInvalidNumber[]; CONTINUE}]; intC.mailcheckPollingInterval ← IF pollingInterval = 0 THEN 15 ELSE ((pollingInterval + 14) / 15) * 15; -- polling interval will be a multiple of 15 seconds and greater than 0. END; -- of SetPoll -- SetBackground: PROCEDURE = BEGIN SELECT TRUE FROM EquivalentString[value, "white"L] => videoBackground ← white; EquivalentString[value, "black"L] => videoBackground ← black; ENDCASE => WriteBadValueMsgInLog[keyWord]; END; -- of SetBackground -- SetSendMode: PROCEDURE = BEGIN SELECT TRUE FROM EquivalentString[value, "mtp"L] => intC.profileSendMode ← mtp; EquivalentString[value, "gv"L] => intC.profileSendMode ← gv; EquivalentString[value, "auto"L] => intC.profileSendMode ← auto; ENDCASE => WriteBadValueMsgInLog[keyWord]; END; -- of SetSendMode -- SetCForCopies: PROCEDURE = {intC.cForCopies ← EquivalentString[value, "c"L]}; SetEditorType: PROCEDURE = {IF EquivalentString[value, "modeless"L] THEN intC.editorType ← modeless}; SetCopies: PROCEDURE = BEGIN n: CARDINAL ← 0; n ← StringToDecimal[value ! InvalidNumber => {ReportInvalidNumber[]; CONTINUE}]; IF n IN [1 .. 99] THEN intC.defaultHardCopies ← n; END; -- of SetCopies -- SetControlRed: PROCEDURE = {intC.controlRedEnabled ← ~EquivalentString[value, "TRUE"L]}; SetExceptionType: PROCEDURE = {IF EquivalentString[value, "LaurelX"L] THEN intC.exceptionType ← LaurelX}; SetDisplayLines: PROCEDURE = BEGIN max: CARDINAL = AltoDisplay.MaxScanLines / dsD.lineHeight; min: CARDINAL = max/2; n: CARDINAL ← 0; n ← StringToDecimal[value ! InvalidNumber => {ReportInvalidNumber[]; CONTINUE}]; IF n IN [min .. max] THEN intC.numScanLines ← n * dsD.lineHeight; END; -- of SetCopies -- ProfileStringRecord: TYPE = RECORD [key: STRING, stringP: POINTER TO STRING]; ProfileBooleanRecord: TYPE = RECORD [key: STRING, boolP: POINTER TO BOOLEAN]; ProfileCardinalRecord: TYPE = RECORD [key: STRING, cardP: POINTER TO CARDINAL]; ProfileProcRecord: TYPE = RECORD [key: STRING, proc: PROCEDURE]; nStringOptions: CARDINAL = 14; nBooleanOptions: CARDINAL = 10; nTicksOptions: CARDINAL = 6; nPreValueSpecialOptions: CARDINAL = 3; nPostValueSpecialOptions: CARDINAL = 11; nDecommissionedOptions: CARDINAL = 8; nFieldOptions: CARDINAL = 3; stringOptions: ARRAY [0 .. nStringOptions) OF ProfileStringRecord ← [["Registry"L, @intC.profileRegistry], ["Send"L, @intC.profileSend], ["Retrieve"L, @intC.profileRetrieve], ["Printer"L, @intC.hardcopyHost], ["Hardcopy"L, @intC.hardcopyHost], ["PrintedBy"L, @intC.hardcopyUserName], ["LaurelSupport"L, @intC.bugReportee], ["RunPath"L, @intC.runPath], ["RemoteFilePath"L, @intC.remoteFilePath], ["ChollaArchive"L, @intC.chollaArchiveFilePath], ["DefaultHardcopyForm"L, @intC.defaultHardcopyFormName], ["NewMailTune"L, @intC.newMailTune], ["Workstation"L, @intC.workstationName], ["ChollaDL"L, @intC.chollaDL]]; preValueSpecialOptions: ARRAY [0 .. nPreValueSpecialOptions) OF ProfileProcRecord ← [["Font"L, FontProc], ["HardcopyForm"L, HardcopyFormProc], ["Boundary"L, BoundaryProc]]; decommissionedOptions: ARRAY [0 .. nDecommissionedOptions) OF STRING ← ["Authenticate"L, "ForceMTPSend"L, "Herald"L, "HeraldFont"L, "Logo"L, "LogoFont"L, "Poll"L, "DisplayErrorPups"L]; booleanOptions: ARRAY [0 .. nBooleanOptions) OF ProfileBooleanRecord ← [["Leaf"L, @intC.leafOk], ["Cholla"L, @intC.isCholla], ["GVTestingMode"L, @intC.gvTestingMode], ["NewFormAfterDelivery"L, @intC.newFormAfterDelivery], ["DisplayAfterDelete"L, @intC.displayAfterDelete], ["TwoSided"L, @intC.twoSidedPrintingDefault], ["Private"L, @intC.passwordPrintingDefault], ["WriteProtected"L, @intC.disableWriting], ["BluePendingDelete"L, @intC.bluePendingDelete], ["DeliverWithCRs"L, @intC.deliverWithCR]]; ticksOptions: ARRAY [0 .. nTicksOptions) OF ProfileCardinalRecord ← [["Click"L, @intC.multiClickTimeOut], ["Tap"L, @intC.tapTimeOut], ["ScrollTimeOut"L, @intC.continuousScrollTimeOut], ["ScrollDelay"L, @intC.continuousScrollDelay], ["BracketTimeOut"L, @intC.nextBracketTimeout], ["BracketDelay"L, @intC.nextBracketDelay]]; fieldOptions: ARRAY [0 .. nFieldOptions) OF ProfileCardinalRecord ← [["FromField"L, @intC.fromRightX], ["SubjectField"L, @intC.subjectLeftX], ["SubjectFieldExtension"L, @intC.subjectExtensionLeftX]]; postValueSpecialOptions: ARRAY [0 .. nPostValueSpecialOptions) OF ProfileProcRecord ← [["Comment"L, Ignore], ["C"L, Ignore], ["DendroicaStriata"L, SetPoll], ["Background"L, SetBackground], ["SendMode"L, SetSendMode], ["CopiesField"L, SetCForCopies], ["Editor"L, SetEditorType], ["Copies"L, SetCopies], ["HomoTollens"L, SetControlRed], ["ErrorKeys"L, SetExceptionType], ["DisplayLines"L, SetDisplayLines]]; -- Main body of ReadLaurelProfile follows -- installError ← none; -- initialize defaultable fields -- FOR i: CARDINAL IN [0 .. nStringOptions) DO stringOptions[i].stringP↑ ← NIL; ENDLOOP; FOR i: CARDINAL IN [0 .. nBooleanOptions) DO booleanOptions[i].boolP↑ ← FALSE; ENDLOOP; intC.autoConfirm ← FALSE; intC.profileSendMode ← auto; intC.profileRetrieveMode ← auto; intC.mailcheckPollingInterval ← 300; intC.multiClickTimeOut ← 10; intC.tapTimeOut ← 10; intC.continuousScrollTimeOut ← 26; intC.continuousScrollDelay ← 5; intC.nextBracketTimeout ← 19; intC.nextBracketDelay ← 19; intC.dateLeftX ← inD.leftMargin + 50; intC.dateRightX ← inD.leftMargin + 100; intC.fromLeftX ← inD.leftMargin + 110; intC.fromRightX ← inD.leftMargin + 250; intC.subjectLeftX ← inD.leftMargin + 260; intC.subjectExtensionLeftX ← inD.leftMargin + 275; videoBackground ← white; intC.cForCopies ← FALSE; intC.controlRedEnabled ← TRUE; intC.pendingDeleteSetByControl ← FALSE; intC.audioEnabled ← FALSE; intC.hardcopyInstallError ← FALSE; intC.editorType ← modal; intC.defaultHardCopies ← 1; intC.exceptionType ← Laurel; intC.boundarySet ← lsD.AllocateStateNode[SIZE[inD.BoundarySetArray]]; intC.boundarySet↑ ← ALL[[toc: 12, dm: 19, cm: 16]]; intC.boundarySet[1] ← [toc: 1, dm: 0, cm: 0]; intC.boundarySet[2] ← [toc: 0, dm: 1, cm: 0]; intC.boundarySet[3] ← [toc: 0, dm: 0, cm: 1]; intC.numScanLines ← 720; -- 60 text lines of 12 scan lines each -- acquire profile information LaurelHardcopyDefs.InitHardcopyFonts[]; InitParse[]; profileInUserCm ← FALSE; BEGIN ENABLE MailParseDefs.ParseError => BEGIN context: CARDINAL = 20; s: STRING ← [context]; position: csD.Position ← csD.GetPosition[laurelProfileHandle]; WriteStringToLog["Syntax error in profile near: '"L]; position ← IF position < start + context THEN start ELSE position - context; csD.SetPosition[laurelProfileHandle, position]; s.length ← csD.ReadBlock[laurelProfileHandle, @s.text, 0, context]; WriteStringToLog[s]; WriteCharToLog['']; FinishLogEntry[]; CONTINUE END; IF ~ProcessLaurelProfile[] THEN BEGIN profileInUserCm ← ProcessUserCm[section: "Laurel]"L, proc: DoLaurelPart]; IF profileInUserCm AND installError = none THEN BEGIN MailParseDefs.FinalizeParse[pH]; InitParse[]; [] ← ProcessUserCm[section: "Hardcopy]"L, proc: DoHardcopyPart]; END; END; END; -- of ENABLE -- MailParseDefs.FinalizeParse[pH]; IF Storage.StringLength[intC.profileRegistry] = 0 THEN WriteLogEntry["No 'Registry' field in profile."L] ELSE IF intC.profileRegistry.length > 40 THEN WriteLogEntry["Registry name is too long."L]; IF intC.isCholla ← (intC.isCholla AND FrameDefs.IsBound[ccD.ChollaMailProcess]) THEN BEGIN IF intC.chollaArchiveFilePath = NIL THEN WriteLogEntry["No ChollaArchive field in profile."L]; IF intC.workstationName = NIL THEN WriteLogEntry["No Workstation field in profile."L]; IF intC.chollaDL = NIL THEN WriteLogEntry["No ChollaDL field in profile."L]; END; -- provide defaults (overwrites only if string is still NIL) FillProfileString[@intC.profileRegistry, "NoRegistry"L]; FillProfileString[@intC.workstationName, "NoName"L]; FillProfileString[@intC.hardcopyHost, ""L]; FillProfileString[@intC.hardcopyUserName, "$"L]; FillProfileString[@intC.defaultHardcopyFormName, "Blank"L]; IF intC.hardcopyUserName[0] = '" AND intC.hardcopyUserName[intC.hardcopyUserName.length-1] = '" THEN BEGIN i: CARDINAL; FOR i IN [0 .. intC.hardcopyUserName.length - 2) DO intC.hardcopyUserName[i] ← intC.hardcopyUserName[i + 1]; ENDLOOP; intC.hardcopyUserName.length ← intC.hardcopyUserName.length - 2; END; intC.passwordPrinting ← intC.passwordPrintingDefault; intC.twoSidedPrinting ← intC.twoSidedPrintingDefault; intC.hardCopies ← intC.defaultHardCopies; FillProfileString[@intC.bugReportee, "LaurelSupport.PA"L]; value.length ← 0; LaurelHardcopyDefs.InstallHardcopy[ ! LaurelHardcopyDefs.FontError => {ProcessFontError[code]; CONTINUE}]; FinishLog[]; END; -- of ReadLaurelProfile -- GetImageFileName: PROCEDURE = BEGIN OPEN SegmentDefs; file: FileHandle = FrameOps.CodeHandle[FrameDefs.GlobalFrame[intC]].file; seg: FileSegmentHandle ← NewFileSegment[file, 0, 1, Read]; leader: POINTER TO AltoFileDefs.LD; SwapIn[seg]; leader ← FileSegmentAddress[seg]; intC.imageFileName ← lsD.AllocateStateString [LOOPHOLE[@leader.name, POINTER TO StringDefs.BcplSTRING].length]; StringDefs.BcplToMesaString[LOOPHOLE[@leader.name], intC.imageFileName]; intC.imageFileName.length ← intC.imageFileName.length - 1; -- get rid of dot. -- Unlock[seg]; DeleteFileSegment[seg]; END; -- of GetImageFileName -- CreateLaurelState: PUBLIC PROCEDURE[heapDS: SegmentDefs.DataSegmentHandle] RETURNS [installError: drD.InstallError] = BEGIN savedCursor: dsD.CursorBitMap; installCursor: dsD.CursorBitMap = [002000B, 001000B, 000400B, 001000B, 002000B, 007600B, 037740B, 177777B, 147631B, 140031B, 142031B, 142037B, 143430B, 160070B, 077760B, 017700B]; imageFile: SegmentDefs.FileHandle = FrameOps.CodeHandle[FrameDefs.GlobalFrame[intC]].file; savedCursor ← dsD.cursorBM↑; dsD.cursorBM↑ ← installCursor; InitializeState[imageFile, heapDS]; IF (installError ← ReadLaurelProfile[]) ~= none THEN BEGIN -- ensure that state will be recomputed next time. This guarantees that the user -- cannot use Laurel until the profile problem is fixed. If this code is removed, -- Laurel will install the defaults in the state and use them until the user edits -- user.cm or laurel.profile. Core.FreeCacheEntry["Laurel.profile"L]; profileInUserCm ← FALSE; END; BuildInteractor[]; GetImageFileName[]; InstallState[imageFile, heapDS]; dsD.cursorBM↑ ← savedCursor; END; -- of CreateLaurelState -- END. -- of InstallLaurel --