PeanutShellImpl.mesa
Copyright Ó 1983, 1984, 1989, 1990, 1991, 1992 Xerox Corporation. All rights reserved.
The Great Wheel of Peanut Incarnation:
Written by Alan Turing, February 1983
Last edited by Paxton on April 4, 1983 9:10 am
Last Edited by: Pausch, July 18, 1983 1:37 pm
Last Edited by: Gasbarro June 24, 1986 1:16:19 pm PDT
Willie-Sue, December 13, 1989 4:58:24 pm PST
Michael Plass, June 1, 1988 5:03:29 pm PDT
Last changed by Pavel on March 8, 1990 3:20 pm PST
Doug Wyatt, September 13, 1990 1:42 pm PDT
Jules Bloomenthal April 23, 1993 12:20 pm PDT
DIRECTORY BasicTime, CedarProcess, Commander, CommanderOps, Convert,
FS, Icons,
IO, Menus, PeanutProfile, PeanutWindow,
PFS, PFSNames, PFSPrefixMap, Process, RefText, Rope, TextNode, ThisMachine, TiogaAccess, TiogaAccessViewers, TiogaMenuOps, TiogaOps, UserProfile, ViewerClasses, ViewerOps, ViewerTools;
PeanutShellImpl:
CEDAR
MONITOR
IMPORTS CedarProcess, Commander, CommanderOps, Convert,
FS,
IO, Menus, PeanutProfile, PeanutWindow,
PFS, PFSNames, PFSPrefixMap, Process, RefText, Rope, TextNode, ThisMachine, TiogaAccess, TiogaAccessViewers, TiogaMenuOps, TiogaOps, UserProfile, ViewerOps, ViewerTools
~ BEGIN
Types and Globals
ForkableProc: TYPE ~ CedarProcess.ForkableProc;
Writer: TYPE ~ TiogaAccess.Writer;
Reader: TYPE ~ TiogaAccess.Reader;
Column: TYPE ~ ViewerClasses.Column;
Viewer: TYPE ~ ViewerClasses.Viewer;
dir: ROPE ¬ Rope.Concat[Prefix[PeanutProfile.workingDirectory], "/"];
cedarUser: ROPE ¬ NARROW[CommanderOps.GetProp[NIL, $USER]];
user: ROPE ¬ UserProfile.Token["Peanut.user", cedarUser];
host: ROPE ¬ UserProfile.Token["Peanut.mailMachine", ThisMachine.Name[]];
local: BOOL ¬ Rope.Equal[host, ThisMachine.Name[]];
spool:
ROPE ¬ Rope.Cat[Prefix[UserProfile.Token[
"Peanut.spoolDirectory", "/var/spool"]], "/", user];
mailFiles: LIST OF ROPE; -- short names ("Active" not "Active.mail")
Support
UnixCmd:
PROC [cmd:
ROPE, fork:
BOOL ¬
FALSE]
RETURNS [reply:
ROPE ¬
NIL] ~ {
IO.PutF1[debug, "command = %g\n", IO.rope[cmd]];
cmd ¬ Rope.Concat["sh1 ", cmd];
IF fork
THEN [] ¬ CedarProcess.Fork[UnixFork, cmd]
ELSE reply ¬ CommanderOps.DoCommandRope[cmd,, NIL].out;
};
UnixFork: ForkableProc ~ {[] ¬ CommanderOps.DoCommandRope[
NARROW[data],,
NIL]};
MakeRsh:
PROC [cmd:
ROPE]
RETURNS [rshCmd:
ROPE] ~ {
rshCmd ¬
IF local
THEN Rope.Cat["rsh -l ", user, " -n localhost ", Rope.Cat[" ", "\"", cmd, "\""]]
ELSE Rope.Cat["rsh -l ", user, " ", host, Rope.Cat[" ", "\"", cmd, "\""]];
};
Prefix:
PROC [in:
ROPE]
RETURNS [out:
ROPE] ~ {
IF Rope.Fetch[in, Rope.Length[in]-1] = '/ THEN in ¬ Rope.Substr[in, 0, Rope.Length[in]-1];
out ¬ PFS.RopeFromPath[PFSPrefixMap.Lookup[PFS.PathFromRope[in]]];
IF Rope.IsEmpty[out] THEN out ¬ in;
IF Rope.Find[out, "-vux"] = 0 THEN out ¬ Rope.Substr[out, 5];
IF Rope.Find[out, "-ux"] = 0 THEN out ¬ Rope.Substr[out, 4];
};
WriteViewer:
PROC [w: Writer, v: Viewer]
RETURNS [ok:
BOOL] ~ {
IF (ok ¬ v.link =
NIL)
THEN TiogaAccessViewers.WriteViewer[w, v]
ELSE PeanutWindow.OutputRope["\nCan't write to split viewer!"];
};
UnixMailFileToTiogaMail:
PROC
[mailFile:
ROPE,
w:
Writer]
RETURNS
[nMsgs:
INT]
~
{
nMsgs ¬ MailToWriter[FS.StreamOpen[mailFile ! FS.Error => CONTINUE], w];
};
MailToWriter:
PROC [mail:
IO.
STREAM, w: Writer]
RETURNS [nMsgs:
INT ¬ 0] ~ {
Start: PROC [line, start: ROPE] RETURNS [b: BOOL] ~ {b ¬ Rope.Find[line, start, 0, FALSE] = 0};
Write:
PROC [rope:
ROPE, header:
BOOL ¬
FALSE, format:
ATOM ¬
NIL] ~ {
Format is applied to the node that is terminated by tc.endOfNode.
tc: TiogaAccess.TiogaChar ¬ [0, '\000, ALL[FALSE], format, FALSE, FALSE, 0, NIL];
IF header THEN tc.looks['b] ¬ TRUE;
FOR n:
INT
IN [0..Rope.Length[rope])
DO
tc.char ¬ Rope.Fetch[rope, n];
TiogaAccess.Put[w, tc];
ENDLOOP;
IF header THEN {tc.endOfNode ¬ TRUE; TiogaAccess.Put[w, tc]};
};
BriefDate:
PROC [date:
ROPE]
RETURNS [brief:
ROPE ¬ "[no date]"] ~ {
months:
ARRAY BasicTime.MonthOfYear
OF
ROPE ¬
["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",NIL];
up: BasicTime.Unpacked ¬ Convert.UnpackedTimeFromRope[date];
IF up.month # unspecified
THEN brief ¬ Rope.Cat[Convert.RopeFromInt[up.day], "-",
months[up.month], "-", Rope.Substr[IO.PutFR1["%g", IO.int[up.year]], 2, 2]];
};
BriefFrom: PROC [f: ROPE] RETURNS [b: ROPE] ~ {b ¬ Rope.Substr[f,, Rope.SkipTo[f,, "(<"]]};
IF mail =
NIL
THEN RETURN[0]
ELSE
DO
ENABLE IO.EndOfStream => EXIT;
{
Type: TYPE ~ {date, from, to, cc, subject, replyTo, none};
keys: ARRAY Type OF ROPE ¬ ["Date", "From", "To", "Cc", "Subject", "ReplyTo", NIL];
fields: ARRAY Type OF ROPE ¬ ALL[NIL];
reading: Type ¬ none;
once: BOOL ¬ FALSE;
DO
Check:
PROC [type: Type]
RETURNS [check:
BOOL] ~ {
IF NOT (check ¬ Start[line, Rope.Concat[keys[type], ":"]]) THEN RETURN;
IF reading = type OR fields[type] # NIL THEN RETURN;
reading ¬ type;
fields[reading ¬ type] ¬ Rope.Substr[line, Rope.Length[keys[type]]+2]; -- new
};
NotKey:
PROC
RETURNS [b:
BOOL] ~ {
b ¬ Rope.Find[line, " "] < 1 OR Rope.Fetch[line, Rope.Find[line, " "]-1] # ':;
};
goodKey: BOOL ¬ FALSE;
line: ROPE ¬ IO.GetLineRope[mail];
IF Rope.Equal[line, ""] THEN EXIT; -- blank line presumed end of header
FOR type: Type IN Type DO IF (goodKey ¬ Check[type]) THEN EXIT; ENDLOOP;
IF
NOT goodKey
AND NotKey[]
AND reading # none
THEN
-- some fields multi-lined
fields[reading] ¬ Rope.Cat[fields[reading], "\n", line];
ENDLOOP;
Write[Rope.Cat[BriefDate[fields[date]], "\t", BriefFrom[fields[from]], "\t", fields[subject]], TRUE, $header];
Write[Rope.Cat["Date: ", fields[date], "\n"]];
Write[Rope.Cat["From: ", fields[from], "\n"]];
Write[Rope.Cat["To: ", fields[to], "\n"]];
IF NOT Rope.IsEmpty[fields[cc]] THEN Write[Rope.Cat["Cc: ", fields[cc], "\n"]];
Write[Rope.Cat["Subject: ", fields[subject], "\n"]];
IF
NOT Rope.IsEmpty[fields[replyTo]]
THEN
Write[Rope.Cat["Reply-To: ", fields[replyTo], "\n"]];
DO
ENABLE IO.EndOfStream => EXIT;
index: INT ¬ IO.GetIndex[mail];
line: ROPE ¬ IO.GetLineRope[mail];
IF Start[line, "From "]
THEN {
-- unexpected new message header?
index2: INT ¬ IO.GetIndex[mail];
line2: ROPE ¬ IO.GetLineRope[mail];
IF
Start[line2,
"Return-Path:
"]
OR
Start[line2, "Received"]
THEN {IO.SetIndex[mail, index]; EXIT} -- yes, new message
ELSE IO.SetIndex[mail, index2];
};
Write[
IF once
OR Rope.IsEmpty[line]
OR Rope.Fetch[line] # '\n
THEN Rope.Concat["\n", line] ELSE line];
once ¬ TRUE;
ENDLOOP;
TiogaAccess.Nest[w, 1];
Write[NIL, TRUE];
TiogaAccess.Nest[w, -1];
nMsgs ¬ nMsgs+1;
};
ENDLOOP;
};
Save Mail
ByeButton: Menus.ClickProc ~ {
PeanutWindow.SaveAllMailFiles[mailFiles, mouseButton = $blue];
};
Get Mail
MailCheck: ForkableProc ~ {
WHILE PeanutWindow.peanutParent #
NIL
AND
NOT PeanutWindow.peanutParent.destroyed
DO
cmd:
ROPE ¬
IF local
THEN Rope.Concat["ls ", spool]
ELSE MakeRsh[Rope.Concat["ls ", spool]];
reply: ROPE ¬ UnixCmd[cmd];
found: BOOL ¬ Rope.Find[reply, "not found"] = -1 AND Rope.Find[reply, user] # -1;
PeanutWindow.SetNewMail[found];
IF found AND PeanutProfile.automaticNewMail THEN GetMail[left];
Process.PauseMsec[15000];
ENDLOOP;
};
GetButton: Menus.ClickProc ~ {GetMail[
IF mouseButton = red
THEN left
ELSE right]};
GetMail:
PROC [column: Column] ~ {
nMsgs: INT;
w: Writer ¬ TiogaAccess.Create[];
ok:
BOOL ¬
IF local
THEN UnixCmd[Rope.Cat["mv ", spool, " ", dir, "mbox"]] = NIL
ELSE UnixCmd[MakeRsh[Rope.Cat["mv ", spool, " ", "mbox"]]] =
NIL
AND
UnixCmd[Rope.Cat["rcp ", user, "@", host, Rope.Cat[":mbox ", dir, "mbox"]]] = NIL;
IF
NOT ok
OR (nMsgs ¬ UnixMailFileToTiogaMail[Rope.Concat[dir, "mbox"], w]) = 0
THEN PeanutWindow.OutputRope["\nNo new messages"]
ELSE {
reply: ROPE;
v: Viewer ¬ PeanutWindow.GetMailViewer["Active"];
s: ViewerTools.SelPos ¬ NEW[ViewerTools.SelPosRec ¬ [LAST[INT], 0, FALSE, after]];
PeanutWindow.OutputRope[IO.PutFR1["\n%g new message(s)", IO.int[nMsgs]]];
IF v.column # column THEN ViewerOps.ChangeColumn[v, column];
ViewerOps.OpenIcon[v];
TiogaMenuOps.AllLevels[v];
[] ¬ v.class.scroll[v, thumb, 100];
ViewerTools.SetSelection[v, s];
TiogaOps.CaretBefore[];
TiogaOps.Break[];
THROUGH [1..TextNode.Level[TiogaOps.GetCaret[].node])
DO
-- make this top level
TiogaOps.UnNest[];
ENDLOOP;
TiogaOps.SetFormat["header"];
TiogaAccessViewers.WriteSelection[w];
s ¬ ViewerTools.GetSelection[v];
s.length ¬ 0;
ViewerTools.SetSelection[v, s];
reply ¬ UnixCmd[
IF local
THEN Rope.Cat["mv ", dir, "mbox ", dir, ".mbox"]
ELSE MakeRsh["mv mbox .mbox"],
TRUE];
IF reply # NIL THEN PeanutWindow.OutputRope[Rope.Concat["\nerror: ", reply]];
};
PeanutWindow.SetNewMail[FALSE];
};
Message Form
NewForm:
PROC [user:
ROPE, addressee, subject:
ROPE ¬
NIL, column: Column ¬ left] ~ {
EndNode:
PROC [format:
ATOM ¬
NIL] ~ {
TiogaAccess.Put[w, [0, '\000, ALL[FALSE], format, FALSE, TRUE, 0, NIL]];
};
PutField:
PROC [key, val:
ROPE, format:
ATOM ¬
NIL] ~ {
keyLooks: TiogaAccess.Looks ¬ ALL[FALSE];
keyLooks['b] ¬ keyLooks['s] ¬ TRUE;
PutRope[key, keyLooks];
PutRope[Rope.Cat[": ", val, "\n"]];
};
PutRope:
PROC
[rope:
ROPE,
looks:
TiogaAccess.Looks
¬
ALL[
FALSE]]
~
{
Action:
PROC [c:
CHAR]
RETURNS [quit:
BOOL ¬
FALSE]
~ {
tc.looks ¬ looks;
SELECT c FROM '\001, '\002 => tc.looks['r] ¬ tc.looks['t] ¬ TRUE; ENDCASE;
tc.char ¬ c;
TiogaAccess.Put[w, tc];
};
tc: TiogaAccess.TiogaChar ¬ [0, '\000, ALL[FALSE], NIL, FALSE, FALSE, 0, NIL];
[] ¬ Rope.Map[base: rope, action: Action];
};
icon: Icons.IconFlavor ¬ PeanutWindow.mailMessageIcon;
v: Viewer ¬ ViewerTools.MakeNewTextViewer[
[name: "Message", icon: icon, iconic: FALSE, column: column]];
w: Writer ~ TiogaAccess.Create[];
TiogaMenuOps.DefaultMenus[v];
Menus.InsertMenuEntry[v.menu, Menus.CreateEntry["SEND", SendButton,,,, TRUE]];
PutField["To", IF addressee = NIL THEN "\001Address\002" ELSE addressee];
EndNode[];
TiogaAccess.Nest[w, 1];
PutField["Subject", IF subject = NIL THEN "\001Topic\002" ELSE subject];
PutRope["\n"];
IF PeanutProfile.ccField THEN {PutField["Cc", "\001Copies To\002"]; PutRope["\n"]};
PutField["Reply-To", user];
PutRope["\n"];
PutRope["\001Message\002"];
EndNode[PeanutProfile.messageNodeFormat];
[] ¬ WriteViewer[w, v];
ViewerTools.SetSelection[v, NEW[ViewerTools.SelPosRec ¬ [0, 0]]];
v.class.notify[v, LIST[$NextPlaceholder]];
};
MessageButton: Menus.ClickProc ~ {NewForm[user,,, IF mouseButton=red THEN left ELSE right]};
Send Mail
ReplyButton: Menus.ClickProc ~ {
GetParent:
PROC
RETURNS [TiogaOps.Ref] = {
node: TiogaOps.Ref ¬ TiogaOps.GetSelection[].start.node;
root, parent: TiogaOps.Ref ¬ TiogaOps.Root[node];
IF node = NIL THEN RETURN[NIL];
DO
IF (parent ¬ TiogaOps.Parent[node]) = root THEN RETURN[node];
node ¬ parent;
ENDLOOP;
};
GetLine:
PROC [key:
ROPE]
RETURNS [
ROPE ¬
NIL] ~ {
FOR node: TiogaOps.Ref ¬ GetParent[], TiogaOps.StepForward[node]
WHILE node #
NIL
DO
s: IO.STREAM ¬ IO.RIS[TiogaOps.GetRope[node]];
DO
line: ROPE ¬ IO.GetLineRope[s ! IO.EndOfStream => EXIT];
IF Rope.Find[line, key] # -1 THEN RETURN[line];
ENDLOOP;
ENDLOOP;
};
GetSender:
PROC
RETURNS [
ROPE ¬
NIL] ~ {
IF (line ¬ GetLine["From:"]) #
NIL
THEN {
start, stop: INT ¬ Rope.Find[line, "From:"];
i0: INT ¬ Rope.Find[line, "<", start];
i1: INT ¬ Rope.Find[line, ">", start];
IF i0 > -1 AND i1 > i0 THEN RETURN[Rope.Substr[line, i0+1, i1-i0-1]];
stop ¬ Rope.SkipTo[line, start ¬ Rope.SkipOver[line, start+5, " \t"], " \t"];
RETURN[Rope.Substr[line, start, stop-start]];
};
};
GetSubject:
PROC
RETURNS [r:
ROPE ¬
NIL] ~ {
IF (line ¬ GetLine["Subject:"]) = NIL THEN RETURN;
r ¬ Rope.Substr[line, Rope.SkipOver[line, Rope.Find[line, "Subject:"]+8, " \t"]];
IF Rope.Find[r, "Re:"] # 0 THEN r ¬ Rope.Concat["Re: ", r];
};
line, sender: ROPE ¬ GetSender[];
IF sender =
NIL
THEN PeanutWindow.OutputRope["\nBad format"]
ELSE NewForm[user, sender, GetSubject[], IF mouseButton=red THEN left ELSE right];
};
Tiogaify:
PROC [reader: Reader]
RETURNS [w: Writer] ~ {
tc: TiogaAccess.TiogaChar ¬ [0, '\000, ALL[FALSE], NIL, FALSE, FALSE, 0, NIL];
w ¬ TiogaAccess.Create[];
WHILE
NOT TiogaAccess.EndOf[reader]
DO
IF (tc.char ¬ TiogaAccess.Get[reader].char) # '\n
THEN TiogaAccess.Put[w, tc]
ELSE
IF
NOT TiogaAccess.EndOf[reader]
THEN
SELECT TiogaAccess.Peek[reader].char
FROM
' , '\n, '\t => TiogaAccess.Put[w, tc];
ENDCASE => {tc.char ¬ ' ; TiogaAccess.Put[w, tc]};
ENDLOOP;
};
WritePlain:
PROC [reader: Reader, fixedPitch:
BOOL]
RETURNS [w: Writer] ~ {
Get:
PROC [op: {space, word}]
RETURNS [notEnd:
BOOL ¬
TRUE] ~ {
text.length ¬ 0;
DO
tc: TiogaAccess.TiogaChar ¬ TiogaAccess.Get[reader];
IF TiogaAccess.EndOf[reader] THEN RETURN[FALSE];
IF tc.endOfNode AND op = space AND foundText THEN PutChar['\n];
IF op = space
THEN
SELECT tc.char
FROM
' , '\t => text ¬ RefText.InlineAppendChar[text, tc.char];
'\n => {IF foundText THEN {text[0] ¬ '\n; text.length ¬ 1}; EXIT};
ENDCASE => {TiogaAccess.PutBack[reader, tc]; EXIT}
ELSE
SELECT tc.char
FROM
' , '\t, '\n => {TiogaAccess.PutBack[reader, tc]; EXIT};
ENDCASE => text ¬ RefText.InlineAppendChar[text, tc.char];
ENDLOOP;
};
NewLine: PROC ~ {PutChar['\n]; count ¬ 0};
PutChar: PROC [c: CHAR] ~ {tc.char ¬ c; TiogaAccess.Put[w, tc]; lastCharCR ¬ tc.char = '\n};
PutText:
PROC ~ {
FOR i: INT IN [0..text.length) DO PutChar[text[i]]; ENDLOOP;
count ¬ count+text.length;
};
tc: TiogaAccess.TiogaChar ¬ [0, '\000, ALL[FALSE], NIL, FALSE, FALSE, 0, NIL];
count: INT ¬ 0;
text: REF TEXT ¬ RefText.ObtainScratch[1000];
foundText, lastCharCR: BOOL ¬ FALSE;
IF fixedPitch THEN tc.looks['f] ¬ TRUE; -- causes Tioga formatting at end of file
w ¬ TiogaAccess.Create[];
WHILE
NOT TiogaAccess.EndOf[reader]
DO
IF NOT Get[space] THEN EXIT;
IF count+text.length > 79 THEN NewLine[] ELSE IF text.length > 0 THEN PutText[];
IF text.length > 0 AND text[0] = '\n THEN count ¬ 0;
[] ¬ Get[word];
foundText ¬ TRUE;
IF count+text.length > 79 THEN NewLine[];
IF text.length > 0 THEN PutText[];
ENDLOOP;
IF NOT lastCharCR THEN PutChar['\n]; -- most mail handlers want CR to end message
RefText.ReleaseScratch[text];
};
GetAddressees:
PROC [reader: Reader, ccToo:
BOOL ¬
TRUE]
RETURNS [to:
ROPE ¬
NIL] ~ {
GetLine:
PROC
RETURNS [rope:
ROPE ¬
NIL] ~ {
WHILE
NOT TiogaAccess.EndOf[reader]
DO
tc: TiogaAccess.TiogaChar ¬ TiogaAccess.Get[reader];
IF tc.char = '\n OR tc.endOfNode THEN EXIT;
rope ¬ Rope.Concat[rope, Rope.FromChar[tc.char]];
ENDLOOP;
};
Translate: Rope.TranslatorType ~ {RETURN[IF old = ', THEN ' ELSE old]};
cc: ROPE ¬ NIL;
foundText, doCc, doTo: BOOL ¬ FALSE;
WHILE
NOT TiogaAccess.EndOf[reader]
DO
line: ROPE ¬ GetLine[];
IF Rope.IsEmpty[line] AND foundText THEN EXIT;
IF
NOT Rope.IsEmpty[line]
THEN {
i: INT ¬ Rope.Find[line, ":"];
IF i > 0
THEN {
key: ROPE ¬ Rope.Substr[line, 0, i];
SELECT
TRUE
FROM
Rope.Equal[key, "To", FALSE] => {doTo ¬ TRUE; doCc ¬ FALSE};
Rope.Equal[key, "Cc", FALSE] => {doCc ¬ TRUE; doTo ¬ FALSE};
ENDCASE => EXIT;
};
IF doTo THEN to ¬ Rope.Concat[to, Rope.Substr[line, i+1]];
IF doCc THEN cc ¬ Rope.Concat[cc, Rope.Substr[line, i+1]];
foundText ¬ TRUE;
};
ENDLOOP;
to ¬
IF ccToo
THEN Rope.Translate[Rope.Cat[to, " ", cc],,, Translate]
ELSE Rope.Translate[to,,, Translate];
to ¬ Rope.Substr[to, Rope.SkipOver[to,, " \t"]];
WHILE Rope.Size[to] > 1
AND Rope.Fetch[to, Rope.Size[to]-1] = '
DO
to ¬ Rope.Substr[to,, Rope.Size[to]-1];
ENDLOOP;
TiogaAccess.SetIndex[reader, 0];
};
SendButton: Menus.ClickProc ~ {
v: Viewer ¬ ViewerTools.GetSelectedViewer[];
IF v =
NIL
THEN PeanutWindow.OutputRope["\nFirst select a message viewer"]
ELSE {
reader: Reader ¬ TiogaAccessViewers.FromViewer[v];
to: ROPE ¬ GetAddressees[reader];
IF Rope.IsEmpty[to]
THEN PeanutWindow.OutputRope["\nBad message format"]
ELSE {
msend: ROPE ¬ Rope.Concat[dir, "msend"];
dot: INT ¬ Rope.Find[to, "."];
[] ¬ UnixCmd[Rope.Cat["mv ", msend, " ", dir, ".msend "]];
TiogaAccess.WriteFile[WritePlain[reader, FALSE], msend];
v.name ¬
Rope.Concat["to ", Rope.Substr[to, 0, IF dot > 0 THEN dot ELSE LAST[INT]]];
IF
NOT WriteViewer[WritePlain[TiogaAccessViewers.FromViewer[v],
TRUE], v]
THEN RETURN;
ViewerOps.DestroyViewer[v];
IF local
THEN [] ¬ UnixCmd[MakeRsh[Rope.Cat["cat ", msend, " | /usr/lib/sendmail ", to]]]
ELSE {
[] ¬ UnixCmd[Rope.Cat["rcp ", msend, " ", user, Rope.Cat["@", host,":msend"]]];
[] ¬ UnixCmd[MakeRsh[Rope.Concat["cat msend | /usr/lib/sendmail ", to]]];
[] ¬ UnixCmd[MakeRsh["rm msend"], TRUE];
};
PeanutWindow.OutputRope[Rope.Concat["\nMail sent to ", to]];
};
};
};
SaveMessageButton: Menus.ClickProc ~ {
v: Viewer ¬ ViewerTools.GetSelectedViewer[];
IF v =
NIL
THEN PeanutWindow.OutputRope["\nFirst select a message viewer"]
ELSE {
to: ROPE ¬ GetAddressees[TiogaAccessViewers.FromViewer[v], FALSE];
IF Rope.IsEmpty[to]
THEN PeanutWindow.OutputRope["\nBad message format"]
ELSE {
Tr: Rope.TranslatorType ~ {RETURN[IF old = ' OR old = '@ THEN '← ELSE old]};
IF Rope.Equal[to, "Address"] THEN to ¬ "Message";
v.file ¬ Rope.Cat[dir, Rope.Translate[to,,, Tr], ".save"];
[] ¬ ViewerOps.SaveViewer[v];
PeanutWindow.OutputRope[Rope.Concat["\nSaved ", v.file]];
};
};
};
MakePlainButton: Menus.ClickProc ~ {Modify[makePlain]};
TiogaifyButton: Menus.ClickProc ~ {Modify[tiogaify]};
Modify:
PROC [mode: {makePlain, tiogaify}] ~ {
v: Viewer ¬ ViewerTools.GetSelectedViewer[];
IF v =
NIL
THEN PeanutWindow.OutputRope["\nFirst select a message viewer"]
ELSE {
sel: ViewerTools.SelPos ¬ ViewerTools.GetSelection[v];
r: Reader ¬ TiogaAccessViewers.FromViewer[v];
w: Writer ¬ IF mode = makePlain THEN WritePlain[r, TRUE] ELSE Tiogaify[r];
IF WriteViewer[w, v] THEN ViewerTools.SetSelection[v, sel];
};
};
Mail Files
MailFileButton: Menus.ClickProc ~ {
name: ROPE ~ NARROW[clientData];
IF mouseButton= yellow
THEN ViewerOps.OpenIcon[PeanutWindow.GetMailViewer[name]]
ELSE PeanutWindow.CopyMessages[name, mouseButton = blue];
};
GetMailFileList:
PROC
RETURNS [result:
LIST
OF
ROPE ←
NIL] ~ {
Names:
PFS.NameProc ~ {
base: ROPE ← PFSNames.ComponentRope[PFSNames.ShortName[name]];
IF base # NIL THEN tmp ← CONS[Rope.Substr[base, 0, Rope.FindBackward[base, "."]], tmp];
};
tmp: LIST OF ROPE;
pattern: PFS.PATH ← PFS.PathFromRope[Rope.Concat[dir, "*.mail!H"]];
PFS.EnumerateForNames[pattern, Names !
PFS.Error => {
PeanutWindow.OutputRope[
IO.PutFR["PFS.Error for %g: %g\n",
IO.rope[dir], IO.rope[error.explanation]]];
CONTINUE}];
FOR l: LIST OF ROPE ← tmp, l.rest WHILE l # NIL DO result ← CONS[l.first, result]; ENDLOOP;
};
Initialization
PeanutShellCmd:
ENTRY Commander.CommandProc ~ {
debug ¬ cmd.out;
PeanutWindow.Destroy[];
IF NOT PeanutWindow.Create[] THEN RETURN;
[] ¬ CedarProcess.Fork[MailCheck];
FOR l:
LIST
OF
ROPE ¬ mailFiles ¬ GetMailFileList[], l.rest
UNTIL l =
NIL
DO
PeanutWindow.AddButton[l.first, MailFileButton, l.first,,, l.rest = NIL];
ENDLOOP;
PeanutWindow.OutputRope["Peanut for PCedar\n"];
PeanutWindow.OutputRope["MIDDLE click mail file button to open the file\n"];
PeanutWindow.OutputRope["LEFT click to copy selected message(s)\n"];
PeanutWindow.OutputRope["RIGHT click to move selected message(s)\n"];
};
PeanutWindow.AddCommand["Get", GetButton,,,, 0];
PeanutWindow.AddCommand["Send", SendButton,,, TRUE, 0];
PeanutWindow.AddCommand["Message", MessageButton,,,, 0];
PeanutWindow.AddCommand["Reply", ReplyButton,,,, 0];
PeanutWindow.AddCommand["Bye", ByeButton,,,, 1];
PeanutWindow.AddCommand["SaveMessage", SaveMessageButton,,,, 1];
PeanutWindow.AddCommand["MakePlain", MakePlainButton,,,, 1];
PeanutWindow.AddCommand["Tiogaify", TiogaifyButton,,,, 1];
Commander.Register["PeanutShell", PeanutShellCmd, "organize mail"];
END.
..
PeanutShellUnixToTiogaCmd: Commander.CommandProc ~ {
argv: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd];
IF argv.argc # 4 OR NOT Rope.Equal[argv[2], "←"]
THEN RETURN[$Failure, "usage: PeanutShellUnixToTioga <tioga file> ← <unix file>"]
ELSE {
w: Writer ¬ TiogaAccess.Create[];
nMsgs: INT ¬ UnixMailFileToTiogaMail[argv[3], w];
IO.PutF1[cmd.out, "%g messages\n", IO.int[nMsgs]];
IF nMsgs > 0 THEN TiogaAccess.WriteFile[w, argv[1]];
};
};
Commander.Register["PeanutShellUnixToTioga", PeanutShellUnixToTiogaCmd, "xform Unix file"] ;
GetAddressees: PROC [reader: Reader] RETURNS [to: ROPE ¬ NIL] ~ {
GetAddressees: PROC [v: Viewer] RETURNS [to: ROPE ¬ NIL] ~ {
GetNode: PROC RETURNS [rope: ROPE ¬ NIL] ~ {
WHILE NOT TiogaAccess.EndOf[reader] DO
tc: TiogaAccess.TiogaChar ¬ TiogaAccess.Get[reader];
IF rope # NIL AND tc.endOfNode THEN EXIT;
rope ¬ Rope.Concat[rope, Rope.FromChar[tc.char]];
ENDLOOP;
};
Translate: Rope.TranslatorType ~ {RETURN[IF old = ', THEN ' ELSE old]};
GetField: PROC [key: ROPE] RETURNS [val: ROPE ¬ NIL] ~ {
start: INT ¬ Rope.Find[node, key];
IF start = -1 THEN RETURN;
start ¬ start+Rope.Length[key];
val ¬ Rope.Substr[node, start, Rope.SkipTo[node, start, "\n"]-start];
};
reader: Reader ¬ TiogaAccessViewers.FromViewer[v];
node: ROPE ¬ GetNode[];
to ¬ Rope.Translate[Rope.Cat[GetField["To: "], " ", GetField["Cc: "]],,, Translate];
to ¬ Rope.Substr[to, Rope.SkipOver[to,, " \t"]];
WHILE Rope.Size[to] > 1 AND Rope.Fetch[to, Rope.Size[to]-1] = ' DO
to ¬ Rope.Substr[to,, Rope.Size[to]-1];
ENDLOOP;
reader ← TiogaAccessViewers.FromViewer[v]; -- gross reset of reader, but SetPosition suspect
TiogaAccess.SetIndex[reader, 0]; -- was suspect (preferable to SetPosition) seems to work
};