LaurelToPeanut.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Last Edited by: Stone, June 13, 1983 5:11 pm
Last edited by Doug Wyatt, August 19, 1985 1:39:51 pm PDT
DIRECTORY
BasicTime USING [MonthOfYear, Unpacked],
Commander USING [CommandProc, Register],
Convert USING [Error, UnpackedTimeFromRope],
IO USING [EndOfStream, GetTokenRope, IDProc, int, Put, PutChar, PutFR, PutRope, RIS, rope, STREAM],
Rope USING [Cat, Concat, Equal, Fetch, Find, Index, ROPE, Size, SkipTo, Substr],
RopeIO USING [FromFile, ToFile],
TiogaFileOps USING [CreateRoot, InsertAsLastChild, Ref, SetContents, SetFormat, SetStyle, Store],
UserCredentials USING [Get];
LaurelToPeanut: CEDAR PROGRAM
IMPORTS Commander, Convert, IO, Rope, RopeIO, TiogaFileOps, UserCredentials
= BEGIN
ROPE: TYPE = Rope.ROPE;
Node: TYPE = TiogaFileOps.Ref;
File: TYPE = REF FileRep;
FileRep:
TYPE =
RECORD[
name: ROPE, -- category name ("Active")
filename: ROPE, -- file name ("Active.mail")
root: Node, -- root node
last: Node -- last top-level node
];
Message: TYPE = REF MessageRep;
MessageRep:
TYPE =
RECORD[
date: ROPE,
contents: ROPE
];
simpleUserName, userRName: ROPE ← NIL;
InitUserName:
PROC = {
uN: ROPE = UserCredentials.Get[].name;
pos: INT = Rope.Find[uN, "."];
IF pos<0 THEN { simpleUserName ← uN; userRName ← Rope.Concat[uN, ".pa"] }
ELSE { simpleUserName ← Rope.Substr[uN, 0, pos]; userRName ← uN };
};
CreateFile:
PROC[name:
ROPE]
RETURNS[File] = {
file: File = NEW[FileRep];
file.name ← name;
file.filename ← Rope.Concat[name, ".mail"];
file.root ← TiogaFileOps.CreateRoot[];
TiogaFileOps.SetStyle[file.root, "Mail"];
file.last ← TiogaFileOps.InsertAsLastChild[file.root];
TiogaFileOps.SetContents[file.last, file.filename];
RETURN[file];
};
A message in a laurel mail file has the following form:
*start*
02203 00112 US
Date: xxxx
... message header contents ...
/n/n
...message contents ...
ParseFailed: ERROR = CODE;
ParseMessage:
PROC[m:
ROPE]
RETURNS[Message] = {
message: Message = NEW[MessageRep];
index: INT ← 0;
Require:
PROC[x:
ROPE] = {
start: INT = index;
IF Rope.Find[m, x, start]=start THEN index ← start+Rope.Size[x]
ELSE ERROR ParseFailed;
};
SkipTo:
PROC[x:
ROPE] = {
start: INT = index;
index ← Rope.Find[m, x, start]+Rope.Size[x];
};
ReadTo:
PROC[x:
ROPE]
RETURNS[
ROPE] = {
start: INT = index;
end: INT = Rope.Find[m, x, start];
index ← end+Rope.Size[x];
RETURN[Rope.Substr[m, start, end-start]];
};
Require["*start*\n"];
SkipTo["\n"];
message.date ← Convert.ValueToRope[[time[DateAndTime.Parse[message.date !
DateAndTime.Unintelligible => {message.date ← "??-???-??"; CONTINUE};].dt]]];
message.date ← Rope.Substr[base: message.date,len: 9];
message.date ← DateFromRope[GetFieldContents[m,"\nDate: "]];
message.contents ← Rope.Substr[m, index];
RETURN[message];
};
RopeFromMonthArray: TYPE ~ ARRAY BasicTime.MonthOfYear OF ROPE;
ropeFromMonth:
REF RopeFromMonthArray ~
NEW[RopeFromMonthArray ← [
January: "Jan", February: "Feb", March: "Mar", April: "Apr", May: "May", June: "Jun",
July: "Jul", August: "Aug", September: "Sep", October: "Oct", November: "Nov", December: "Dec",
unspecified: "???"]];
DateFromRope:
PROC[rope:
ROPE]
RETURNS[
ROPE] ~ {
time: BasicTime.Unpacked ~ Convert.UnpackedTimeFromRope[rope
! Convert.Error => GOTO Fail];
day: INT ~ time.day;
month: ROPE ~ ropeFromMonth[time.month];
year: [0..100) ~ time.year MOD 100;
RETURN[IO.PutFR["%2g-%g-%02g", IO.int[day], IO.rope[month], IO.int[year]]];
EXITS Fail => RETURN["??-???-??"];
};
DeleteLeadingSPs:
PROC[m:
ROPE]
RETURNS[
ROPE] = {
start: INT ← 0;
len: INT ← Rope.Size[m];
WHILE start<len AND Rope.Fetch[m, start] = ' DO start ← start+1; ENDLOOP;
IF start>0 THEN RETURN[Rope.Substr[m, start, len-start]]
ELSE RETURN[m];
};
DeleteTrailingCRs:
PROC[m:
ROPE]
RETURNS[
ROPE] = {
len: INT ← Rope.Size[m];
WHILE len > 0 AND Rope.Fetch[m, len-1] = '\n DO len ← len-1; ENDLOOP;
RETURN[Rope.Substr[m, 0, len]];
};
Truncate:
PROC[rope:
ROPE, maxLen:
INT]
RETURNS[
ROPE] = {
s: ROPE ← DeleteLeadingSPs[rope];
IF Rope.Size[s] > maxLen THEN s ← Rope.Concat[Rope.Substr[s, 0, maxLen-3], "..."];
RETURN[s];
};
GetFieldContents:
PROC[m, name:
ROPE]
RETURNS[contents:
ROPE] = {
start: INT ← Rope.Find[m, name];
IF start<0 THEN contents ← "?"
ELSE {
end: INT;
start ← start + Rope.Size[name];
end ← Rope.Find[m, "\n", start];
IF end <= start THEN contents ← "?"
ELSE contents ← Rope.Substr[m, start, end-start];
};
};
MakeHeader:
PROC[message: Message]
RETURNS[header:
ROPE] = {
date, name, subject, sender: ROPE ← NIL;
date ← message.date;
sender ← GetFieldContents[message.contents, "From: "];
IF Rope.Equal[sender, userRName,
FALSE]
OR
Rope.Equal[sender, simpleUserName,
FALSE]
THEN
name ← Rope.Concat["To: ", GetFieldContents[message.contents, "\nTo: "]]
ELSE name ← sender;
subject ← GetFieldContents[message.contents, "\nSubject: "];
header ← IO.PutFR["\t%g\t%g\t%g",
IO.rope[date], IO.rope[Truncate[name, 16]], IO.rope[Truncate[subject, 44]]];
RETURN[header];
};
AppendMessage:
PROC[file: File, message: Message] = {
head: Node = TiogaFileOps.InsertAsLastChild[file.root, file.last];
body: Node = TiogaFileOps.InsertAsLastChild[head];
TiogaFileOps.SetContents[head, MakeHeader[message]];
TiogaFileOps.SetFormat[head, "header"];
TiogaFileOps.SetContents[body, DeleteTrailingCRs[message.contents]];
file.last ← head;
};
StoreFile:
PROC[file: File] = {
TiogaFileOps.Store[file.root, file.filename];
file.root ← NIL; file.last ← NIL;
};
ReadFiles:
PROC[list:
ROPE, s:
IO.
STREAM] = {
count: INT ← 0;
listStream: IO.STREAM ← IO.RIS[list];
GetNextFile:
PROCEDURE []
RETURNS [
ROPE] = {
token: ROPE ← IO.GetTokenRope[listStream,IO.IDProc].token;
dot: INT ← Rope.SkipTo[token,0,"."];
IF dot=Rope.Size[token] THEN RETURN[token]
ELSE RETURN[Rope.Substr[base: token, len: dot]];
};
DO
log: ROPE ← NIL;
start, length: INT ← 0;
count: INT ← 0;
file: File ← CreateFile[GetNextFile[! IO.EndOfStream => EXIT]];
s.PutRope["Opening "]; s.PutRope[file.filename]; s.PutRope["..."];
log ← RopeIO.FromFile[file.filename ! ANY => CONTINUE];
IF log=NIL THEN { s.PutRope["unable to open file!\n"]; LOOP };
length ← Rope.Size[log];
s.Put[IO.int[length], IO.rope[" bytes\n"]];
start ← Rope.Find[log, "*start*\n"];
IF start<0 THEN { s.PutRope["No messages found!\n"]; LOOP };
s.PutRope["Reading...\n"];
RopeIO.ToFile[Rope.Cat[file.name,".laurel"],log];
WHILE start<length
DO
end: INT = Rope.Index[log, start+8, "*start*\n"];
message: ROPE = Rope.Substr[log, start, end-start];
AppendMessage[file, ParseMessage[message ! ParseFailed => {
s.Put[IO.rope["message number"], IO.int[count],IO.rope[ "... failed!\n"]];
EXIT};
]];
count ← count+1;
IF count
MOD 10=0
THEN {
s.PutChar[IF count MOD 100=0 THEN '! ELSE '~];
};
start ← end;
ENDLOOP;
s.Put[IO.rope["... "], IO.int[count], IO.rope[" messages\n"]];
s.PutRope["Writing "]; s.PutRope[file.filename]; s.PutRope["..."];
StoreFile[file];
s.PutRope["ok\n"];
ENDLOOP;
};
LaurelToPeanutCommand: Commander.CommandProc = {
ReadFiles[cmd.commandLine, cmd.out];
};
InitUserName[];
Commander.Register["LaurelToPeanut", LaurelToPeanutCommand,
"Create Peanut mail files from a list of Laurel mail files.
If no extension, '.mail' is assumed.
Old files are renamed xxx.laurel"];
END.