-- Mail file patcher
-- MailFilePatchImpl.mesa
-- Andrew Birrell 11-Mar-82 10:52:41
DIRECTORY
Ascii,
GlassDefs,
MailParse,
Segments USING[ FileNameProblem ],
Streams,
String;
MailFilePatchImpl: PROGRAM
IMPORTS GlassDefs, MailParse, Segments, Streams, String =
BEGIN
DoIt: PROC[str: GlassDefs.Handle] =
BEGIN
OPEN str;
file: STRING = [99];
DO IF ReadString["Mail file name: "L, file, word] = Ascii.DEL
THEN EXIT
ELSE BEGIN
FOR i: CARDINAL DECREASING IN [0..file.length)
DO IF file[i] = '. THEN EXIT;
REPEAT FINISHED =>
{ WriteString[".mail"L]; String.AppendString[file, ".mail"L] };
ENDLOOP;
WriteString[" ..."L];
WriteChar[Ascii.CR];
IF Patch[str, file] THEN EXIT;
END;
WriteChar[Ascii.CR];
ENDLOOP;
WriteString["Done"L]; WriteChar[Ascii.CR];
END;
Patch: PROC[str: GlassDefs.Handle, file: STRING] RETURNS[ok: BOOLEAN] =
BEGIN
OPEN str;
stream: Streams.Handle = Streams.NewStream[
name: file, access: Streams.ReadWrite ! Segments.FileNameProblem[] =>
GOTO badFile ];
ParseFile[str, stream !
BadMailFile =>
{ WriteString["Bad mail file - run MailFileScavenger.laurel then re-run this program"L]; CONTINUE };
UNWIND => Streams.Destroy[stream] ];
Streams.Destroy[stream];
ok ← TRUE;
EXITS badFile =>
{ WriteString[" ... """L];
WriteString[file];
WriteString[""" doesn't exist"L];
ok ← FALSE }
END;
BogusNumber: ERROR = CODE;
BadMailFile: ERROR = CODE;
ParseStamp: PROCEDURE [NextChar: PROC RETURNS [CHARACTER]] RETURNS [offsetToHeader, textLength: CARDINAL] =
BEGIN
startOfStamp: STRING = "*start*
"L;
i, j: CARDINAL;
ReadFive: PROCEDURE RETURNS [k: CARDINAL] =
BEGIN
char: CHARACTER;
k ← 0;
THROUGH [0 .. 5) DO
IF (char ← NextChar[]) ~IN ['0..'9] THEN ERROR BogusNumber;
k ← k * 10 + (char - '0);
ENDLOOP;
END; -- of ReadFive --
FOR i IN [0 .. 8) DO IF startOfStamp[i]#NextChar[] THEN GOTO notAStamp; ENDLOOP;
i ← ReadFive[ ! BogusNumber => GOTO notAStamp];
IF NextChar[] # Ascii.SP THEN GOTO notAStamp;
j ← ReadFive[ ! BogusNumber => GOTO notAStamp];
IF NextChar[] # Ascii.SP THEN GOTO notAStamp;
SELECT NextChar[] FROM
'D => NULL;
'U => NULL;
ENDCASE => GOTO notAStamp;
SELECT NextChar[] FROM
'S => NULL;
'U => NULL;
ENDCASE => GOTO notAStamp;
offsetToHeader ← j;
IF i < j THEN GOTO notAStamp;
textLength ← i - j;
[] ← NextChar[];
IF NextChar[] # Ascii.CR THEN GOTO notAStamp;
EXITS
notAStamp => ERROR BadMailFile[];
END; -- of ParseStamp --
repair: STRING = "Name-too-long@Probably-Berkeley"L;
ParseFile: PROC[str: GlassDefs.Handle, stream: Streams.Handle] =
BEGIN
OPEN str;
ch: CHARACTER;
Get: PROC RETURNS[CHARACTER] =
{ RETURN[ ch ← IF Streams.Ended[stream] THEN MailParse.endOfInput
ELSE Streams.GetChar[stream] ] };
Backup: PROC =
{ Streams.SetIndex[stream, Streams.GetIndex[stream]-1] };
ph: MailParse.ParseHandle = MailParse.InitializeParse[Get, Backup];
FOR msg: CARDINAL IN [1..LAST[CARDINAL]]
UNTIL Streams.Ended[stream]
DO -- for each message --
mStart: LONG CARDINAL = Streams.GetIndex[stream];
offsetToHeader: CARDINAL;
textLength: CARDINAL;
[offsetToHeader, textLength] ← ParseStamp[Get];
Streams.SetIndex[stream, mStart+offsetToHeader];
BEGIN
s: STRING = [99];
WHILE MailParse.GetFieldName[ph, s ! MailParse.ParseError => EXIT ]
DO IF String.EquivalentString[s, "From"L]
THEN BEGIN
fStart: LONG CARDINAL = Streams.GetIndex[stream];
ProcessFrom: PROC[name, reg, host: STRING,
ignored: MailParse.NameInfo]
RETURNS[BOOLEAN] =
BEGIN
total: CARDINAL ← name.length + reg.length + host.length;
IF reg.length > 0 THEN total ← total+1;
IF host.length > 0 THEN total ← total+1;
IF total > MailParse.maxRecipientLength
THEN -- Laurel bug! --
BEGIN
fEnd: LONG CARDINAL = Streams.GetIndex[stream];
Streams.SetIndex[stream, fStart];
IF fEnd < fStart + 2 + repair.length THEN ERROR;
Streams.PutChar[stream, Ascii.SP];
FOR i: CARDINAL IN [0..repair.length)
DO Streams.PutChar[stream, repair[i]] ENDLOOP;
WHILE Streams.GetIndex[stream]+1 < fEnd
DO Streams.PutChar[stream, Ascii.SP] ENDLOOP;
WriteString["Patched ""From"" field in message "L];
WriteDecimal[msg];
WriteString[":
"""L];
WriteString[name];
IF reg.length # 0 THEN WriteChar['.];
WriteString[reg];
IF host.length # 0 THEN WriteChar['@];
WriteString[host];
WriteChar['"];
WriteChar[Ascii.CR];
END;
RETURN[FALSE]
END;
MailParse.ParseNameList[ph, ProcessFrom !
MailParse.ParseError => CONTINUE];
END
ELSE MailParse.GetFieldBody[ph, s, TRUE !
MailParse.ParseError => CONTINUE];
ENDLOOP;
END;
Streams.SetIndex[stream, mStart+offsetToHeader+textLength];
ENDLOOP;
MailParse.FinalizeParse[ph];
END;
GlassDefs.Listen[DoIt];
END.