-- file: ChollaSpecs.mesa
-- edited by Brotz, March 7, 1983 1:27 PM

DIRECTORY
ccD: FROM "ChollaCmdDefs",
dsD: FROM "DisplayDefs",
Editor,
exD: FROM "ExceptionDefs",
inD: FROM "InteractorDefs",
intCommon,
lmD: FROM "LaurelMenuDefs",
MailParseDefs,
MessageParse,
opD: FROM "OperationsDefs",
Storage,
String,
tsD: FROM "TOCSelectionDefs",
vmD: FROM "VirtualMgrDefs";

ChollaSpecs: PROGRAM
IMPORTS ccD, dsD, Editor, exD, inD, intC: intCommon, lmD, MailParseDefs, MessageParse,
opD, Storage, String, tsD, vmD
EXPORTS ccD =

BEGIN


specOrData: ccD.ChollaFileType;
specOrDataFileName: STRING ← [ccD.tocCacheFileStringLength];


InstallSpecs: PUBLIC PROCEDURE =
BEGIN
tnp: inD.TOCTextNbrPtr = intC.tocTextNbr;
toc: vmD.TOCHandle;
key: CARDINAL;
toc ← tnp.toc;
key ← vmD.WaitForLock[toc];
tnp.toc ← NIL;
tnp.haveToc ← FALSE;
vmD.UnlockTOC[toc, key];
dsD.ClearRectangle[inD.leftMargin, inD.rightMargin, tnp.topY, tnp.bottomY];
intC.bboardCommandHouse.command ← SpecsBBoardCommand;
intC.userBracketsHouse.nextHouse ← specsTopHp;
inD.ChangeCommandMenu
[cnp: intC.mailCommandNbr, region: intC.mailCommandRegion, linesToKeep: 2];
intC.tocCommandNbr.houses ← specsMiddleHp;
inD.ChangeCommandMenu
[cnp: intC.tocCommandNbr, region: intC.TOCCommandRegion, linesToKeep: 0];
specPostHp.text.length ← 0;
specPostHp.typeface ← plainFace;
lmD.ChangeEditorMenu[singleLine];
intC.cmCommandNbr.houses ← specsLowerHp;
inD.ChangeCommandMenu
[cnp: intC.cmCommandNbr, region: intC.CMCommandRegion, linesToKeep: 0];
specOrData ← none;
END; -- of InstallSpecs --


SpecsSpecCommand: inD.CommandProcedure =
BEGIN
IF ~ccD.IsAuthorized[onlooker, hp] THEN RETURN;
SELECT TRUE FROM
~confirmed
AND ~inD.ConfirmBrackets[hp: specAndDataBracketsHp, fileExtension: "Spec.chml."L]
=> NULL;
specAndDataBracketsHp.text.length = 0 =>
exD.DisplayExceptionString["Must name a spec file."L];
specAndDataBracketsHp.text.length > ccD.maxSpecNameLength =>
exD.DisplayExceptionString["Spec name is too long!"L];
ENDCASE => InstallSpecOrData[spec, specAndDataBracketsHp.text];
inD.IndicateCommandFinished[hp];
IF specOrData = spec THEN
{inD.IndicateCommandFinished[dataCommandHp]; DrawLineUnder[hp]};
END; -- of SpecsSpecCommand --


SpecsDataCommand: inD.CommandProcedure =
BEGIN
IF ~ccD.IsAuthorized[onlooker, hp] THEN RETURN;
SELECT TRUE FROM
~confirmed
AND ~inD.ConfirmBrackets[hp: specAndDataBracketsHp, fileExtension: "Data.chml."L]
=> NULL;
specAndDataBracketsHp.text.length = 0 =>
exD.DisplayExceptionString["Must name a data file."L];
specAndDataBracketsHp.text.length > ccD.maxSpecNameLength =>
exD.DisplayExceptionString["Data name is too long!"L];
ENDCASE => InstallSpecOrData[data, specAndDataBracketsHp.text];
inD.IndicateCommandFinished[hp];
IF specOrData = data THEN
{inD.IndicateCommandFinished[specCommandHp]; DrawLineUnder[hp]};
END; -- of SpecsDataCommand --


SpecsLaurelCommand: inD.CommandProcedure =
BEGIN
ccD.RestoreLaurel[];
END; -- of SpecsLaurelCommand --


InstallSpecOrData: PROCEDURE [type: ccD.ChollaFileType, specOrDataName: STRING] =
BEGIN
postNewDataForm: STRING ← "Post new data form"L;
postNewSpec: STRING ← "Post new spec"L;
tnp: inD.TOCTextNbrPtr = intC.tocTextNbr;
toc: vmD.TOCHandle;
key: CARDINAL;
ccD.CleanupDisplayMessage[];
IF tnp.toc # NIL THEN
BEGIN
key ← vmD.WaitForLock[tnp.toc];
toc ← tnp.toc; tnp.toc ← NIL;
vmD.UnlockTOC[toc, key];
tnp.haveToc ← FALSE;
END;
specOrDataFileName.length ← 0;
String.AppendString[specOrDataFileName, specOrDataName];
String.AppendString
[specOrDataFileName, IF type = spec THEN "Spec"L ELSE "Data"L];
specOrData ← type;
[toc, key] ← ccD.GetTOCForFile[specOrDataFileName, IF type = spec THEN spec ELSE data];
IF toc = NIL THEN exD.SysBug[];
tsD.ResetTOCSelection[toc, key];
tnp.toc ← toc;
tnp.haveToc ← TRUE;
dsD.ClearRectangle[inD.leftMargin, inD.rightMargin, tnp.topY, tnp.bottomY];
inD.TOCTextPainter[tnp, key];
vmD.UnlockTOC[toc, key];
specPostHp.text.length ← 0;
specPostHp.typeface ← boldFace;
String.AppendString
[specPostHp.text, IF type = spec THEN postNewSpec ELSE postNewDataForm];
inD.TextHouseRefresher[specPostHp];
END; -- of InstallSpecOrData --


SpecsBBoardCommand: inD.CommandProcedure =
BEGIN
IF ~ccD.IsAuthorized[onlooker, hp] THEN RETURN;
ccD.CleanupDisplayMessage[];
ccD.InstallBB[];
END; -- of SpecsBBoardCommand --


SpecsDisplayCommand: inD.CommandProcedure =
BEGIN
IF ~ccD.IsAuthorized[onlooker, hp] THEN RETURN;
inD.DisplayMessageCommand[hp, TRUE];
END; -- of SpecsDisplayCommand --


SpecsVersionCommand: inD.CommandProcedure =
BEGIN
cm: vmD.ComposedMessagePtr;
key: CARDINAL;
toc: vmD.TOCHandle;
newVersionNumber: CARDINAL ← 0;
newVersionString: STRING = hp.nextHouse.text;
IF ~ccD.IsAuthorized[maven, hp] THEN RETURN;
IF specOrData = none THEN
{exD.DisplayExceptionString["Neither a spec nor a data file is specified."L]; RETURN};
IF ~confirmed AND ~inD.ConfirmBrackets[hp: hp.nextHouse] THEN RETURN;
cm ← vmD.AllocateComposedMessageObject[];
[toc, key] ← ccD.GetTOCForFile
[specOrDataFileName, IF specOrData = spec THEN spec ELSE data];
newVersionNumber ← String.StringToDecimal[newVersionString
! String.InvalidNumber => CONTINUE];
IF newVersionNumber + 1 ~IN [2 .. toc.indexFF) THEN
exD.DisplayExceptionString["Invalid version number."L]
ELSE BEGIN
subjectString: STRING ← [30];
fieldList: MessageParse.FieldList;
ccD.LoadComposedMessage[toc, key, 1, cm];
String.AppendString[subjectString, "Current version is "L];
String.AppendString[subjectString, hp.nextHouse.text];
fieldList ← MessageParse.MakeFieldList[cm];
MessageParse.ReplaceOrAppendField[cm, @fieldList, "Subject"L, subjectString];
MessageParse.ReplaceOrAppendField[cm, @fieldList, "CurrentVersion"L, hp.nextHouse.text];
MessageParse.ReplaceOrAppendField[cm, @fieldList, "FileName"L, specOrDataFileName];
IF ~ccD.SendChollaMail[replace, specOrData, toc, key, 1, cm, @fieldList] THEN
exD.DisplayExceptionString["Problem sending version update!"L];
MessageParse.FreeFieldList[fieldList];
END;
vmD.UnlockTOC[toc, key];
vmD.FreeVirtualMessageObject[cm];
END; -- of SpecsVersionCommand --


SpecsPostCommand: inD.CommandProcedure =
BEGIN
mnp: inD.MessageTextNbrPtr = intC.cmTextNbr;
cm: vmD.ComposedMessagePtr = vmD.ComposedMessage[mnp.message];
fieldList: MessageParse.FieldList;
key: CARDINAL;
toc: vmD.TOCHandle;
IF ~ccD.IsAuthorized[maven, hp] THEN RETURN;
IF ~ValidatePostedSpecOrData[cm] THEN
{exD.DisplayExceptionString["Improper syntax for spec or data form."L]; RETURN};
[toc, key] ← ccD.GetTOCForFile
[specOrDataFileName, IF specOrData = spec THEN spec ELSE data];
IF toc.indexFF = 1 THEN
BEGIN
cm: vmD.ComposedMessagePtr = vmD.AllocateComposedMessageObject[];
verFieldList: MessageParse.FieldList;
vmD.InitComposedMessage[cm, "Subject: Current version is 1
CurrentVersion: 1

"L];
verFieldList ← MessageParse.MakeFieldList[cm];
MessageParse.ReplaceOrAppendField[cm, @verFieldList, "FileName"L, specOrDataFileName];
IF ~ccD.SendChollaMail[insert, specOrData, toc, key, 1, cm, @verFieldList] THEN
exD.DisplayExceptionString["Problem sending version message!"L];
IF toc.indexFF # 2 THEN exD.SysBug[];
vmD.FreeVirtualMessageObject[cm];
MessageParse.FreeFieldList[verFieldList];
END;
fieldList ← MessageParse.MakeFieldList[cm];
MessageParse.ReplaceOrAppendField[cm, @fieldList, "FileName"L, specOrDataFileName];
IF ~ccD.SendChollaMail[insert, specOrData, toc, key, toc.indexFF, cm, @fieldList] THEN
exD.DisplayExceptionString["Problem posting new spec or data form!"L]
ELSE IF ~WriteMessageToArchive[toc, key, toc.indexFF - 1] THEN
exD.DisplayExceptionString["Problem archiving new spec or data form!"L];
vmD.UnlockTOC[toc, key];
MessageParse.DeleteField[cm, @fieldList, "FileName"L];
MessageParse.DeleteField[cm, @fieldList, "UniqueID"L];
MessageParse.DeleteField[cm, @fieldList, "ThisID"L];
MessageParse.DeleteField[cm, @fieldList, "Format"L];
MessageParse.FreeFieldList[fieldList];
Editor.RefreshSoThatFirstCharStartsLine[firstChar: 0, firstLine: mnp.lines, mnp: mnp];
END; -- of SpecsPostCommand --


SpecsArchiveCommand: inD.CommandProcedure =
BEGIN
toc: vmD.TOCHandle;
key, count: CARDINAL;
fieldList, subjectFL: MessageParse.FieldList;
cm: vmD.ComposedMessagePtr;
dm: vmD.DisplayMessagePtr;
fp: vmD.TOCFixedPart;
IF ~ccD.IsAuthorized[kahuna, hp] THEN RETURN;
IF specOrData = none THEN
{exD.DisplayExceptionString["Neither a spec nor a data file is specified."L]; RETURN};
dm ← vmD.AllocateDisplayMessageObject[];
cm ← vmD.AllocateComposedMessageObject[];
[toc, key] ← ccD.GetTOCForFile
[specOrDataFileName, IF specOrData = spec THEN spec ELSE data];
FOR index: vmD.TOCIndex ← tsD.FirstSelectedEntry[toc, key],
tsD.NextSelectedEntry[toc, key, index] UNTIL index = 0 DO
vmD.GetTOCFixedPart[toc, key, index, @fp];
IF fp.mark = ’a THEN exD.DisplayExceptionString["Message already archived!"L]
ELSE BEGIN
uniqueID: ccD.UniqueID;
[] ← WriteMessageToArchive[toc, key, index];
vmD.LoadDisplayMessage[toc, key, index, dm];
fieldList ← MessageParse.MakeFieldList[dm];
vmD.InitComposedMessage[cm, "Subject:"L];
[subjectFL, count] ← MessageParse.LocateField[fieldList, "Subject"L];
IF count > 0 THEN vmD.InsertRangeInMessage
[vmD.GetMessageSize[cm], cm, [subjectFL.valueStart, subjectFL.valueEnd, dm]];
uniqueID ← ccD.GetIDUsingVM[dm, @fieldList, "UniqueID"L];
vmD.FlushDisplayMessage[dm, key];
ccD.InsertString[cm, vmD.GetMessageSize[cm], ", Archived

Archived: TRUE
"L];
MessageParse.FreeFieldList[fieldList];
fieldList ← MessageParse.MakeFieldList[cm];
ccD.FillIDField[cm, @fieldList, "UniqueID"L, @uniqueID];
MessageParse.ReplaceOrAppendField[cm, @fieldList, "FileName"L, specOrDataFileName];
IF ~ccD.SendChollaMail[replace, specOrData, toc, key, index, cm, @fieldList] THEN
exD.DisplayExceptionString["Problem sending archive update!"L];
MessageParse.FreeFieldList[fieldList];
END;
ENDLOOP;
vmD.FreeVirtualMessageObject[dm];
vmD.FreeVirtualMessageObject[cm];
vmD.UnlockTOC[toc, key];
END; -- of SpecsArchiveCommand --


SpecsUnarchiveCommand: inD.CommandProcedure =
BEGIN
toc: vmD.TOCHandle;
key: CARDINAL;
fieldList: MessageParse.FieldList;
cm: vmD.ComposedMessagePtr;
fp: vmD.TOCFixedPart;
IF ~ccD.IsAuthorized[kahuna, hp] THEN RETURN;
IF specOrData = none THEN
{exD.DisplayExceptionString["Neither a spec nor a data file is specified."L]; RETURN};
cm ← vmD.AllocateComposedMessageObject[];
[toc, key] ← ccD.GetTOCForFile
[specOrDataFileName, IF specOrData = spec THEN spec ELSE data];
FOR index: vmD.TOCIndex ← tsD.FirstSelectedEntry[toc, key],
tsD.NextSelectedEntry[toc, key, index] UNTIL index = 0 DO
vmD.GetTOCFixedPart[toc, key, index, @fp];
IF fp.mark # ’a THEN exD.DisplayExceptionString["Message wasn’t archived!"L]
ELSE BEGIN
IF ~RetrieveFromArchive[specOrDataFileName, index - 1, cm] THEN
{exD.DisplayExceptionString["Problem retrieving from archive!"L]; LOOP};
fieldList ← MessageParse.MakeFieldList[cm];
MessageParse.ReplaceOrAppendField[cm, @fieldList, "FileName"L, specOrDataFileName];
IF ~ccD.SendChollaMail[replace, specOrData, toc, key, index, cm, @fieldList] THEN
exD.DisplayExceptionString["Problem sending unarchive update!"L];
MessageParse.FreeFieldList[fieldList];
END;
ENDLOOP;
vmD.FreeVirtualMessageObject[cm];
vmD.UnlockTOC[toc, key];
END; -- of SpecsUnarchiveCommand --


WriteMessageToArchive: PROCEDURE [toc: vmD.TOCHandle, key: CARDINAL,
index: vmD.TOCIndex] RETURNS [BOOLEAN] =
BEGIN
archiveToc: vmD.TOCHandle;
archiveKey: CARDINAL;
archiveFile: STRING ← [64];
dm: vmD.DisplayMessagePtr;
archiveFF: vmD.TOCIndex;
String.AppendString[archiveFile, intC.chollaArchiveFilePath];
String.AppendString[archiveFile, specOrDataFileName];
String.AppendString[archiveFile, ".archive"L];
[archiveToc, archiveKey] ← ccD.GetTOCForFile[archiveFile, archive];
IF archiveToc = NIL THEN RETURN[FALSE];
archiveFF ← vmD.FirstFreeTOCIndex[archiveToc, archiveKey];
dm ← vmD.AllocateDisplayMessageObject[];
FOR i: vmD.TOCIndex IN [archiveFF .. index) DO
vmD.LoadDisplayMessage[toc, key, i + 1, dm];
[] ← opD.ReplaceMailOperation
[delete: FALSE, index: i, msg: dm, toc: archiveToc, key: archiveKey];
vmD.FlushDisplayMessage[dm, key];
ENDLOOP;
vmD.FreeVirtualMessageObject[dm];
vmD.UnlockTOC[archiveToc, archiveKey];
[ , ] ← ccD.GetTOCForFile[NIL, archive];
RETURN[TRUE];
END; -- of WriteMessageToArchive --


RetrieveFromArchive: PUBLIC PROCEDURE
[name: STRING, version: CARDINAL, cm: vmD.ComposedMessagePtr]
RETURNS [haveIt: BOOLEAN] =
-- Fills in cm with a copy of the version’th message from the archived file whose root is
-- "name".
BEGIN
archiveToc: vmD.TOCHandle;
archiveKey: CARDINAL;
archiveFile: STRING ← [64];
String.AppendString[archiveFile, intC.chollaArchiveFilePath];
String.AppendString[archiveFile, name];
String.AppendString[archiveFile, ".archive"L];
[archiveToc, archiveKey] ← ccD.GetTOCForFile[archiveFile, archive];
IF archiveToc = NIL THEN RETURN[FALSE];
ccD.LoadComposedMessage[archiveToc, archiveKey, version, cm];
vmD.UnlockTOC[archiveToc, archiveKey];
[ , ] ← ccD.GetTOCForFile[NIL, archive];
RETURN[TRUE];
END; -- of RetrieveFromArchive --


ValidatePostedSpecOrData: PROC [vm: vmD.VirtualMessagePtr] RETURNS [valid: BOOLEAN] =
BEGIN
charIndex: vmD.CharIndex ← 0;
vmSize: vmD.CharIndex = vmD.GetMessageSize[vm];
pH: MailParseDefs.ParseHandle = MailParseDefs.InitializeParse[NextChar];

NextChar: PROCEDURE RETURNS [c: CHARACTER] =
BEGIN
c ← IF charIndex >= vmSize
THEN MailParseDefs.endOfInput ELSE vmD.GetMessageChar[vm, charIndex];
charIndex ← charIndex + 1;
END; -- of NextChar --

valid ← FALSE;
DO
string: STRING ← [8]; -- large enough for "Subject"
IF ~MailParseDefs.GetFieldName
[pH, string ! MailParseDefs.ParseError => GO TO NotParsable]
THEN EXIT;
IF String.EquivalentString[string, "Subject"L] THEN valid ← TRUE;
MailParseDefs.GetFieldBody[pH, string ! MailParseDefs.ParseError => GO TO NotParsable];
REPEAT
NotParsable => valid ← FALSE;
ENDLOOP;
MailParseDefs.FinalizeParse[pH];
END; -- of ValidatePostedSpecOrData --


DrawLineUnder: PROCEDURE [hp: inD.HousePtr] =
BEGIN
IF hp.topY = hp.bottomY OR hp.leftX = hp.rightX THEN RETURN;
dsD.BlackenRectangle[hp.leftX, hp.rightX, hp.bottomY - 2, hp.bottomY];
END; -- of DrawLineUnder --


-- Startup code for ChollaSpecs --


specsTopHp, specsMiddleHp, specsLowerHp, specAndDataBracketsHp, specPostHp, specCommandHp, dataCommandHp: inD.HousePtr;


Init: PROCEDURE =
BEGIN
x: inD.ScreenXCoord;
hp: inD.HousePtr;
postNewDataForm: STRING = "Post new data form"L;
specsTopMenu: ARRAY [0 .. 4) OF lmD.HouseDescriptor ←
[[text: "Spec"L, command: SpecsSpecCommand, indicDone: FALSE],
[text: ", "L, type: text],
[text: "Data"L, type: brackets, bracketsText: ""L, command: SpecsDataCommand,
indicDone: FALSE],
[text: "Laurel"L, command: SpecsLaurelCommand, indicDone: FALSE, endOfLine: TRUE,
rightFlush: TRUE]];
specsMiddleMenu: ARRAY [0 .. 3) OF lmD.HouseDescriptor ←
[[text: "Display"L, command: SpecsDisplayCommand],
[text: "Archive"L, command: SpecsArchiveCommand],
[text: "Unarchive"L, command: SpecsUnarchiveCommand]];
specsLowerMenu: ARRAY [0 .. 3) OF lmD.HouseDescriptor ←
[[text: "Get"L, command: inD.GetCommand],
[text: "Current version"L, command: SpecsVersionCommand, type: brackets,
bracketsText: ""L],
[text: postNewDataForm, rightFlush: TRUE, command: SpecsPostCommand]];

specsTopHp ← lmD.CreateCommandHouses
[DESCRIPTOR[specsTopMenu], Storage.Node, Storage.String, 2,
intC.newMailCommandHouse.leftX];
hp ← specsTopHp.nextHouse;
hp.leftX ← x ← specsTopHp.rightX;
hp.typeface ← boldFace;
hp.rightX ← x ← x + dsD.GetStringWidth[hp.text, boldFace];
hp ← hp.nextHouse;
hp.leftX ← x;
hp.rightX ← x ← x + dsD.GetStringWidth[hp.text, boldFace];
hp ← hp.nextHouse;
x ← x + 7;
hp.rightX ← hp.rightX - (hp.leftX - x);
hp.leftX ← x;
specCommandHp ← lmD.MapHouseTextToHousePtr[specsTopHp, "Spec"L];
dataCommandHp ← lmD.MapHouseTextToHousePtr[specCommandHp, "Data"L];
specAndDataBracketsHp ← dataCommandHp.nextHouse;
specsMiddleHp ← lmD.CreateCommandHouses
[DESCRIPTOR[specsMiddleMenu], Storage.Node, Storage.String, 0];
specsLowerHp ← lmD.CreateCommandHouses
[DESCRIPTOR[specsLowerMenu], Storage.Node, Storage.String, 0];
specPostHp ← lmD.MapHouseTextToHousePtr[specsLowerHp, postNewDataForm];
specPostHp.fixedEdge ← right;
END; -- of Init --


Init[];


END. -- of ChollaSpecs --