<> <> <> <> <> <> <> <> <> <> <> <> 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 <> ForkableProc: TYPE ~ CedarProcess.ForkableProc; Writer: TYPE ~ TiogaAccess.Writer; Reader: TYPE ~ TiogaAccess.Reader; Column: TYPE ~ ViewerClasses.Column; Viewer: TYPE ~ ViewerClasses.Viewer; ROPE: TYPE ~ Rope.ROPE; 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") <> UnixCmd: PROC [cmd: ROPE, fork: BOOL ¬ FALSE] RETURNS [reply: ROPE ¬ NIL] ~ { <> 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] ~ { <> 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; }; <> ByeButton: Menus.ClickProc ~ { PeanutWindow.SaveAllMailFiles[mailFiles, mouseButton = $blue]; }; <> 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]; }; <> 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]; <> <> PutField["Subject", IF subject = NIL THEN "\001Topic\002" ELSE subject]; <> 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]}; <> 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]; }; }; <> 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; }; <> debug: IO.STREAM; 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. .. <> <> <> < _ "]>> <> <> <> <> < 0 THEN TiogaAccess.WriteFile[w, argv[1]];>> <<};>> <<};>> <<>> <> <> <> <> <> <> <> <> <> <<};>> <> <> <> <> <> <> <<};>> <> <> <> <> < 1 AND Rope.Fetch[to, Rope.Size[to]-1] = ' DO>> <> <> <> <> <<};>> <<>>