DIRECTORY DefaultRemoteNames USING [Get], GVAnswer USING [MakeHeader], GVMailParse, Menus, IO, Rope, RuntimeError USING [BoundsFault], TiogaOps USING [ Pattern, Ref, Location, ViewerDoc, CreateSimplePattern, GetSelection, InsertRope, SelectDocument, SelectPoint, SelectionRoot, SelectionSearch, SetSelection, ToPrimary], ViewerOps, ViewerClasses, ViewerTools, WalnutDocumentRope, WalnutParseMsg USING [SendParseStatus, Parse], WalnutSendInternal, WalnutSendOps; WalnutSendOpsImpl: CEDAR MONITOR IMPORTS DefaultRemoteNames, IO, Rope, RuntimeError, GVAnswer, GVMailParse, Menus, TiogaOps, ViewerOps, WalnutDocumentRope, WalnutParseMsg, WalnutSendInternal, WalnutSendOps EXPORTS WalnutSendInternal, WalnutSendOps SHARES Menus = BEGIN OPEN WalnutSendOps, WalnutSendInternal; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; TiogaContents: TYPE = ViewerTools.TiogaContents; Viewer: TYPE = ViewerClasses.Viewer; nullIndex: INT = LAST[INT]; defaultRegistry: PUBLIC ROPE _ DefaultRemoteNames.Get[].registry; Answer: PUBLIC PROC[msgHeaders: ROPE, who: Viewer_ NIL] RETURNS [v: Viewer] = { notOk: BOOL; errorIndex: INT; answer: ROPE; answerForm: WalnutSendOps.Form; AnswerGetChar: PROC[pos: INT] RETURNS[CHAR] = {RETURN[msgHeaders.Fetch[pos]]}; [notOk, answer, errorIndex]_ GVAnswer.MakeHeader[AnswerGetChar, msgHeaders.Length[], simpleUserName, defaultRegistry]; IF notOk THEN { start, end: INT_ errorIndex; BEGIN ENABLE RuntimeError.BoundsFault => {start_ 0; end_ 1; CONTINUE}; IF start = nullIndex THEN start_ 0 ELSE {UNTIL msgHeaders.Fetch[start] = '\n DO start_ start - 1; ENDLOOP; start_ start + 1}; IF end = nullIndex THEN end_ start + 1 ELSE {UNTIL msgHeaders.Fetch[end] = '\n DO end_ end + 1; ENDLOOP; end_ end - 1}; END; IF who # NIL THEN ShowErrorFeedback[who, start, end]; SenderReport[IO.PutFR[ "\nSyntax error in line starting at pos %g (in message being answered)", IO.int[start]]]; RETURN }; answerForm _ NEW[ WalnutSendOps.FormRec _ [formText: answerText, fields: ParseAnswerHeader[answer] ] ]; v_ BuildSendViewer[TRUE, FALSE, answerForm, who]; ClearFileAssoc[v]; GrabFocus[v]; }; ParseAnswerHeader: PROC[header: ROPE] RETURNS[fields: LIST OF ROPE] = { HeaderLines: ARRAY[0..3] OF Rope.ROPE; startPos: INT _ Rope.Find[header, ": ", 0]; endOfLine: INT _ Rope.Find[header, "\n", startPos]; FOR i: INT IN [0..3] DO HeaderLines[i] _ Rope.Substr[header, startPos+2, endOfLine-startPos-2]; startPos _ Rope.Find[header, ": ", endOfLine]; IF startPos = -1 THEN EXIT ELSE endOfLine _ Rope.Find[header, "\n", startPos] ENDLOOP; RETURN[ LIST[ HeaderLines[0], HeaderLines[1], HeaderLines[2], HeaderLines[3] ] ] }; Forward: PUBLIC PROC[msg: Viewer, who: Viewer_ NIL] RETURNS[v: Viewer] = TRUSTED { forwardForm: WalnutSendOps.Form = NEW[WalnutSendOps.FormRec _ [formText: forwardText, fields: NIL]]; messagePattern: TiogaOps.Pattern = TiogaOps.CreateSimplePattern["ForwardedMessage"]; headerPattern: TiogaOps.Pattern = TiogaOps.CreateSimplePattern["MessageHeader"]; pstart, pend: TiogaOps.Location; root: TiogaOps.Ref; found: BOOL; v_ BuildSendViewer[TRUE, FALSE, forwardForm, who]; ClearFileAssoc[v]; TiogaOps.SelectDocument[v]; root _ TiogaOps.SelectionRoot[]; pstart _ TiogaOps.Location[ root, 0 ]; TiogaOps.SelectPoint[ v, pstart ]; found _ TiogaOps.SelectionSearch[pattern: headerPattern]; IF found THEN { [ ,pstart, pend, , , ] _ TiogaOps.GetSelection[]; TiogaOps.SetSelection[viewer: v, start: pstart, end: pend, level: word, pendingDelete: TRUE]; TiogaOps.InsertRope[ msg.name ] }; found _ TiogaOps.SelectionSearch[pattern: messagePattern]; IF NOT found THEN RETURN; [ ,pstart, pend, , , ] _ TiogaOps.GetSelection[]; TiogaOps.SetSelection[ viewer: v, start: pstart, end: pend, level: node, pendingDelete: TRUE ]; TiogaOps.SelectDocument[ viewer: msg, which: secondary ]; TiogaOps.ToPrimary[]; UnsetNewVersion[v]; GrabFocus[v] }; ClearFileAssoc: PUBLIC PROC[v: Viewer] = { IF v.file # NIL THEN v.file_ NIL; v.name_ sendCaption; ViewerOps.PaintViewer[v, caption]; }; SenderNewVersion: PUBLIC PROC[viewer: Viewer] = { OPEN Menus; menu: Menus.Menu = viewer.menu; firstForm: Menus.MenuEntry = Menus.GetLine[formsMenu, 0]; getEntry: Menus.MenuEntry_ Menus.FindEntry[menu, "Get"]; IF getEntry # NIL THEN Menus.SetGuarded[getEntry, TRUE]; getEntry_ Menus.FindEntry[menu, "Default"]; IF getEntry # NIL THEN Menus.SetGuarded[getEntry, TRUE]; getEntry_ Menus.FindEntry[menu, "PrevMsg"]; IF getEntry # NIL THEN Menus.SetGuarded[getEntry, TRUE]; FOR i: Menus.MenuLine IN [1..5) DO thisLine: Menus.MenuEntry = Menus.GetLine[menu, i]; IF thisLine = NIL THEN EXIT; IF Rope.Equal[thisLine.name, firstForm.name] THEN FOR entry: MenuEntry _ thisLine, entry.link UNTIL entry = NIL DO SetGuarded[entry, TRUE] ENDLOOP; ENDLOOP; ViewerOps.PaintViewer[viewer, menu]; -- show as guarded viewer.newVersion_ TRUE; }; UnsetNewVersion: PUBLIC PROC[viewer: Viewer] = { OPEN Menus; menu: Menu = viewer.menu; firstForm: Menus.MenuEntry = Menus.GetLine[formsMenu, 0]; getEntry: Menus.MenuEntry_ Menus.FindEntry[menu, "Get"]; IF getEntry # NIL THEN Menus.SetGuarded[getEntry, FALSE]; getEntry_ Menus.FindEntry[menu, "Default"]; IF getEntry # NIL THEN Menus.SetGuarded[getEntry, FALSE]; getEntry_ Menus.FindEntry[menu, "PrevMsg"]; IF getEntry # NIL THEN Menus.SetGuarded[getEntry, FALSE]; FOR i: Menus.MenuLine IN [1..5) DO thisLine: Menus.MenuEntry = Menus.GetLine[menu, i]; IF thisLine = NIL THEN EXIT; IF Rope.Equal[thisLine.name, firstForm.name] THEN FOR entry: MenuEntry _ thisLine, entry.link UNTIL entry = NIL DO SetGuarded[entry, FALSE] ENDLOOP; ENDLOOP; ViewerOps.PaintViewer[viewer, menu]; -- show as unguarded viewer.newVersion_ FALSE; ViewerOps.PaintViewer[viewer, caption]; }; TiogaTextFromStrm: PUBLIC PROC [strm: IO.STREAM, startPos: INT_ 0, len: INT_ LAST[INT]] RETURNS [contents: TiogaContents] = { ENABLE IO.EndOfStream => GOTO TooShort; fulltext: ROPE _ RopeFromStream[strm, startPos, len]; formatPos: INT _ Rope.Index[fulltext, 0, "\000\000"]; contents_ NEW[ViewerTools.TiogaContentsRec]; contents.contents_ Rope.Substr[fulltext, 0, formatPos]; IF formatPos < Rope.Length[fulltext] THEN contents.formatting_ Rope.Substr[fulltext, formatPos]; EXITS TooShort => RETURN[NIL]; }; RopeFromStream: PUBLIC PROC [strm: STREAM, startPos, len: INT] RETURNS [contents: ROPE] = { rem: INT _ IO.GetLength[strm] - startPos; IF rem < len THEN len _ rem; IF len < 2048 THEN TRUSTED { nat: NAT _ len; text: Rope.Text _ Rope.NewText[nat]; IO.SetIndex[strm, startPos]; [] _ IO.GetBlock[strm, LOOPHOLE[text], 0, nat]; RETURN [text]; }; rem _ len/2; len _ len - rem; contents _ RopeFromStream[strm, startPos, len]; contents _ Rope.Concat[contents, RopeFromStream[strm, startPos+len, rem] ]; }; GetFieldBody: PUBLIC PROC[text, fieldName: ROPE] RETURNS[fieldBody: ROPE] = { OPEN GVMailParse; mPos: INT_ 0; lastCharPos: INT_ text.Length[]; pH: GVMailParse.ParseHandle_ GVMailParse.InitializeParse[]; NextChar: PROC[] RETURNS [ch: CHAR] = { IF mPos > lastCharPos THEN ch_ endOfInput ELSE ch_ text.Fetch[mPos]; mPos_ mPos + 1; }; BEGIN field: ROPE_ NIL; DO found, fieldNotRecognized: BOOL_ TRUE; [field, found]_ GVMailParse.GetFieldName[pH, NextChar ! ParseError => GOTO parseErrorExit]; IF ~found THEN EXIT; IF Rope.Equal[fieldName, field, FALSE] THEN -- ignore case { fieldBody_ GVMailParse.GetFieldBody[pH, NextChar]; EXIT} ELSE []_ GVMailParse.GetFieldBody[pH, NextChar, TRUE]; ENDLOOP; GVMailParse.FinalizeParse[pH]; EXITS parseErrorExit => { GVMailParse.FinalizeParse[pH]; RETURN[NIL]}; END; }; GetRecipients: PUBLIC PROC[sender: Viewer] RETURNS[rList: LIST OF ROPE, parseError: BOOL] = { text: ROPE; status: WalnutParseMsg.SendParseStatus; sPos, mPos: INT; TRUSTED {text_ WalnutDocumentRope.Create[LOOPHOLE [TiogaOps.ViewerDoc[sender]]]}; [status, sPos, mPos, rList]_ WalnutParseMsg.Parse[text]; IF (status # ok) AND (status # includesPublicDL) THEN BEGIN SELECT status FROM fieldNotAllowed => IF sPos # mPos THEN { ShowErrorFeedback[sender, sPos, mPos]; SenderReport[Rope.Substr[text, MAX[0, sPos-1], mPos-sPos]]; SenderReport[" field is not allowed\n"]} ELSE SenderReport[IO.PutFR[" field at pos %g is not allowed\n", IO.int[sPos]]]; syntaxError => IF sPos # mPos THEN { ShowErrorFeedback[sender, sPos, mPos]; SenderReport["\nSyntax error on line beginning with "]; SenderReport[Rope.Substr[text, MAX[0, sPos-1], mPos-sPos]]} ELSE SenderReport[IO.PutFR["..... Syntax error at position %g ", IO.int[sPos]]]; includesPrivateDL => SenderReport[" Private dl's are not yet implemented\n"]; ENDCASE => ERROR; ViewerOps.BlinkIcon[sender, IF sender.iconic THEN 0 ELSE 1]; RETURN[NIL, TRUE] END; RETURN[rList, FALSE] }; END. ΌWalnutSendOpsImpl.mesa - More procs for WalnutSend Copyright c 1984, 1985 by Xerox Corporation. All rights reserved. Willie-Sue, March 20, 1985 8:30:48 am PST Russ Atkinson (RRA) March 21, 1985 0:56:04 am PST returns NIL IF endOfStream encountered during read Reads arbitrary length ROPE from a stream. If some caller catches the error, then the caller can diagnose it. Otherwise we do something more or less reasonable here. Short enough to make in one piece The rope is too large to make in one piece, so we divide the length in two and recursviely call ourselves. This gets at least 1024 characters in each piece in a balanced binary tree, so the recursion is quite acceptable even for large messages. Do this first to ensure that the evaluation order is OK. expects text to be the text of a message; parses the header fields looking for a field named fieldName and returns the value of the fieldBody, or NIL if no such fieldName is found expects sender to contain a message to be sent; will parse the headers and return a list of all the recipients of the message; if an error occurs while parsing the viewer, parseError=TRUE is returned, and the viewer will have the offending field underlined. Κ /˜codešœ2™2Kšœ Οmœ7™BK™)K™1—K˜šΟk ˜ Kšœžœ˜ Kšœ žœ˜K˜ K˜Kšžœ˜K˜Kšœ žœ˜!Kšœ žœ«˜ΉK˜ K˜K˜ K˜Kšœžœ˜.K˜Kšœ˜K˜—šœžœž˜ KšžœžœŽ˜¬Kšžœ"˜)Kšžœ žœžœ#˜