Operations on Messages
The message invariants are as follows:
All messages belong to at least one message set. A message which is a member of the Deleted message set cannot belong to any other message sets.
A message is automatically added to the Active message set when it is created.
A message cannot be destroyed unless it has been deleted.
MsgExists:
PUBLIC
PROC[opsH: WalnutOpsHandle, msg:
ROPE]
RETURNS [exists:
BOOL] =
Does a message with this name exist in the database.
{ RETURN[GetMsgEntity[opsH, msg].exists] };
DestroyMsg is not allowed; this is ONLY done by the Expunge operation
GetIsInReplyTo:
PUBLIC
PROC[opsH: WalnutOpsHandle, msg:
ROPE]
RETURNS[isInReplyTo: BOOL] = {
returns TRUE if the subject field of this message started with "Re: "
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
m: IsEntity = GetMsgEntity[opsH, msg];
IF ~m.exists THEN RETURN[FALSE];
isInReplyTo ¬ LoganBerryEntry.V2B[LoganBerryEntry.GetAttr[m.entity, sH.mMIIsInReplyTo]];
};
GetHasBeenRead:
PUBLIC
PROC[opsH: WalnutOpsHandle, msg:
ROPE]
RETURNS[has: BOOL] = {
returns the mHasBeenReadIs attribute for msg
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
m: IsEntity = GetMsgEntity[opsH, msg];
IF ~m.exists THEN { has ¬ FALSE; RETURN };
has ¬ LoganBerryEntry.V2B[LoganBerryEntry.GetAttr[LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.mDisplayInfo, msg]].entry, sH.mDIHasBeenRead]];
};
SetHasBeenRead:
PUBLIC
PROC[opsH: WalnutOpsHandle, msg:
ROPE] = {
sets the mHasBeenReadIs attribute for msg to TRUE (no check on old value)
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
m: IsEntity = GetMsgEntity[opsH, msg];
md: LoganBerry.Entry;
IF ~m.exists THEN RETURN;
md ¬ LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.mDisplayInfo, msg]].entry;
LoganBerryEntry.SetAttr[md, sH.mDIHasBeenRead, LoganBerryEntry.B2V[TRUE]];
LoganBerry.WriteEntry[db: opsH.db, entry: md, replace: TRUE];
};
AddNewMsg:
PUBLIC
PROC[opsH: WalnutOpsHandle, msg: MsgLogEntry]
RETURNS[mExisted: BOOL] = {
takes a parsed message from the log & puts it in the database -- if the message already exists in the database, then return TRUE. Note: this guy takes the LogEntry given and has the responsibility for constructing the TOCEntry and herald for the message
msgEName: ROPE = msg.msg;
sH: SchemaHandle = opsH.schemaHandle;
m: IsEntity = GetMsgEntity[opsH, msgEName];
IF mExisted ¬ m.exists THEN RETURN; -- id's assumed unique
BEGIN
me: ROPE = msgEName;
herald, tocHead: ROPE;
shortNameLen: INT;
date: BasicTime.GMT = msg.date;
sender: Address;
[herald, tocHead, shortNameLen] ¬ ComputeHeraldAndTOC[msg];
sender ¬ SetAddresses[opsH, me, msg];
SetAllMsgInfo[opsH, me, msg, herald, shortNameLen, date, sender];
SetMsgDisplayInfo[opsH, me, FALSE, tocHead];
SetMsgInfo[opsH, me, msg.date, msg.show];
WalnutDB.ChangeCountOfMsgs[opsH, 1];
BEGIN
-- put it in active - no checking necessary
init: LoganBerry.Entry =
LIST[
[$Key, Rope.Concat[sH.cdRelation, me]],
[sH.cdMsg, me],
[sH.cdMsgSet, opsH.schemaHandle.activeEntity],
[sH.cdDate, LoganBerryEntry.T2V[msg.date]]
];
LoganBerry.WriteEntry[db: opsH.db, entry: init];
IF msg.show
THEN
WalnutDB.ChangeCountInMsgSet[opsH, opsH.schemaHandle.activeEntity, 1];
END;
END;
};
GetMsgEntryPosition:
PUBLIC
PROC[opsH: WalnutOpsHandle, msg:
ROPE]
RETURNS[pos: INT] = {
returns the log position for the entry for this message (-1 if the message doesn't exist); used by the lazy evaluator
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
m: IsEntity = GetMsgEntity[opsH, msg];
IF ~m.exists THEN {pos ¬ -1; RETURN};
pos ¬ LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.mMsgInfo, msg]].entry, sH.mMIEntryStart] ];
};
SetMsgEntryPosition:
PUBLIC
PROC[opsH: WalnutOpsHandle, to:
INT] = {
Sets the message entry position to refer to the new value. We depend on the fact that SetMsgEntryPosition is ONLY called by the expunge code and that it has just done a MsgExists call, which sets the local cached variable lastMsgEntity
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
IF sH.lastMsgEntity = NIL THEN RETURN;
LoganBerryEntry.SetAttr[sH.lastMsgEntity, sH.mMIEntryStart, LoganBerryEntry.I2V[to]];
LoganBerry.WriteEntry[db: opsH.db, entry: sH.lastMsgEntity, replace: TRUE];
};
GetMsgDate:
PUBLIC
PROC[opsH: WalnutOpsHandle, msg:
ROPE]
RETURNS[date:
GMT] = {
returns the date by which the message is indexed in the database
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
m: IsEntity = GetMsgEntity[opsH, msg];
IF ~m.exists
THEN { date ¬ BasicTime.nullGMT;
RETURN}
ELSE {
rel: Relship = LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.mInfo, msg]].entry;
date ¬ LoganBerryEntry.V2T[LoganBerryEntry.GetAttr[rel, sH.mDateIs]];
};
};
GetMsgTextInfo:
PUBLIC
PROC[opsH: WalnutOpsHandle, msg:
ROPE]
RETURNS[textStart, textLen, formatLen: INT] = {
returns information needed to get the tioga text for msg from log
m: IsEntity = GetMsgEntity[opsH, msg];
IF ~m.exists
THEN {
textStart ¬ textLen ¬ formatLen ¬ 0;
RETURN;
};
[textStart, textLen, formatLen] ¬ GetTextInfo[opsH.schemaHandle, m.entity];
};
GetMsgText:
PUBLIC
PROC[opsH: WalnutOpsHandle, msg:
ROPE]
RETURNS[textStart, textLen, formatLen:
INT, herald:
ROPE, shortNameLen:
INT] = {
returns information needed to get the tioga text for msg from log (also produce the herald to be used if displaying it in a viewer
m: IsEntity = GetMsgEntity[opsH, msg];
IF ~m.exists
THEN {
textStart ¬ textLen ¬ formatLen ¬ shortNameLen ¬ 0;
RETURN;
};
[textStart, textLen, formatLen, herald, shortNameLen] ¬
GetAllMsgTextInfo[opsH.schemaHandle, m.entity];
};
GetDisplayProps:
PUBLIC
PROC[opsH: WalnutOpsHandle, msg:
ROPE]
RETURNS [hasBeenRead:
BOOL ¬
TRUE, tocEntry:
ROPE ¬
NIL, startOfSubject:
INT ¬ 0] = {
Return the display properties of a message (the hasbeenread flag and the table of contents entry.
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
m: IsEntity = GetMsgEntity[opsH, msg];
IF m.exists
THEN
[hasBeenRead, tocEntry, startOfSubject] ¬ WalnutDB.GetMsgDisplayInfo[opsH, LoganBerryEntry.GetAttr[m.entity, sH.mMIOf]];
};
GetCategories:
PUBLIC
PROC[opsH: WalnutOpsHandle, msg:
ROPE]
RETURNS[msList: LIST OF ROPE] = {
Return the msgSets that the message belongs to.
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
m: IsEntity = GetMsgEntity[opsH, msg];
rel: Relship;
IF ~m.exists THEN RETURN;
rel ¬ LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.cdRelation, msg]].entry;
msList ¬ LoganBerryEntry.GetAllAttrs[rel, sH.cdMsgSet];
};
SizeOfDatabase:
PUBLIC
PROC[opsH: WalnutOpsHandle]
RETURNS[messages, msgSets: INT] = {
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
rVersionInfo: Relship = LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: sH.gVersionInfo].entry;
messages ¬ LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[rVersionInfo, sH.gMsgCount]];
msgSets ¬ LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[rVersionInfo, sH.gMsgSetCount]];
};
GenerateEntriesPlusDate:
PUBLIC
PROC [opsH: WalnutOpsHandle, attr:
ATOM, start:
ROPE ¬
NIL, end:
ROPE ¬
NIL, dateStart:
ROPE ¬
NIL, dateEnd:
ROPE ¬
NIL]
RETURNS [genEnum: GeneralEnumerator ] = {
This only works well on attributes that are indexed in the database (typically the Sender, Date, or Subject attribute). All other attributes result in a full scan. dateStart and dateEnd are completely ignored.
IndexedField:
PROC [db: LoganBerry.OpenDB, key: LoganBerry.AttributeType]
RETURNS [
BOOLEAN ¬
FALSE] ~ {
schema: LoganBerry.SchemaInfo ¬ LoganBerry.Describe[NIL, db];
FOR i:
LIST
OF LoganBerry.IndexInfo ¬ schema.indices, i.rest
WHILE i #
NIL
DO
IF i.first.key = key THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
GenerateEntriesOrFullScan:
PROC [db: LoganBerry.OpenDB, key: LoganBerry.AttributeType, start: LoganBerry.AttributeValue, end: LoganBerry.AttributeValue]
RETURNS [cursor: LoganBerry.Cursor] ~ {
IF key#
NIL
AND IndexedField[db, key]
THEN cursor ¬ LoganBerry.GenerateEntries[db: db, key: key, start: start, end: end]
ELSE cursor ¬ LoganBerry.GenerateEntries[db: db, key: $Key, start: sH.mMsgInfo, end: Rope.Concat[sH.mMsgInfo, "\255"]]; -- full scan
};
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
genEnum ¬ NEW[WalnutDB.GeneralEnumeratorRec];
genEnum.opsH ¬ opsH;
SELECT attr
FROM
$Key => {
genEnum.cursor ¬ GenerateEntriesOrFullScan[db: opsH.db, key: NIL, start: start, end: end];
};
$MsgSetName => {
This always does a full scan. We could get a cursor on the message set, but would need to run an additional query to get the message info for each message in the set. This does not fit well into the current model.
genEnum.cursor ¬ GenerateEntriesOrFullScan[db: opsH.db, key: NIL, start: start, end: end];
};
$MessageText => {
genEnum.cursor ¬ GenerateEntriesOrFullScan[db: opsH.db, key: NIL, start: start, end: end];
};
$From => {
genEnum.cursor ¬ GenerateEntriesOrFullScan[db: opsH.db, key: NIL, start: start, end: end];
};
$To => {
genEnum.cursor ¬ GenerateEntriesOrFullScan[db: opsH.db, key: NIL, start: start, end: end];
};
$Cc => {
genEnum.cursor ¬ GenerateEntriesOrFullScan[db: opsH.db, key: NIL, start: start, end: end];
};
$Date => {
genEnum.cursor ¬ GenerateEntriesOrFullScan[db: opsH.db, key: sH.mMIDate, start: start, end: end];
};
$Sender => {
genEnum.cursor ¬ GenerateEntriesOrFullScan[db: opsH.db, key: sH.mMISender, start: start, end: end];
};
$Subject => {
genEnum.cursor ¬ GenerateEntriesOrFullScan[db: opsH.db, key: sH.mMISubject, start: start, end: end];
};
ENDCASE =>
ERROR WalnutDefs.Error[$db, $invalidQueryAttr,
IO.PutFR1["Attribute is %g", [atom[attr]]] ];
};
NextEntry:
PUBLIC
PROC[genEnum: GeneralEnumerator]
RETURNS[entry: EntryRef ¬
NIL] = {
sH: WalnutSchema.SchemaHandle = genEnum.opsH.schemaHandle;
msgID: ROPE;
e: LoganBerry.Entry ¬ LoganBerry.NextEntry[cursor: genEnum.cursor];
IF e = NIL THEN RETURN[NIL];
msgID ¬ LoganBerryEntry.GetAttr[e, sH.mMIOf];
entry ¬ NEW[WalnutDB.EntryObject];
entry.seFromToCcSuDaMid.sender ¬ LoganBerryEntry.GetAttr[e, sH.mMISender];
entry.seFromToCcSuDaMid.from ¬ GetFromList[genEnum.opsH, msgID];
entry.seFromToCcSuDaMid.to ¬ GetToList[genEnum.opsH, msgID];
entry.seFromToCcSuDaMid.cc ¬ GetCcList[genEnum.opsH, msgID];
entry.seFromToCcSuDaMid.keyword ¬ NIL;
entry.seFromToCcSuDaMid.subject ¬ LoganBerryEntry.GetAttr[e, sH.mMISubject];
entry.seFromToCcSuDaMid.fullSubjectText ¬ LoganBerryEntry.GetAttr[e, sH.mMISubjectText];
entry.seFromToCcSuDaMid.date ¬ LoganBerryEntry.GetAttr[e, sH.mMIDate];
entry.seFromToCcSuDaMid.msgID ¬ msgID;
entry.msgSetName ¬ GetCategories[genEnum.opsH, msgID].first; -- this probably isn't right
};
Internal procedures
ComputeHeraldAndTOC:
PROC[mle: MsgLogEntry]
RETURNS[herald, tocHead:
ROPE, shortNameLen:
INT] = {
date, tocx: ROPE;
starter: ROPE = IF useFromFieldInTOC THEN mle.from ELSE mle.sender;
from: ROPE = IF SendMailOps.IsThisTheCurrentUser[starter] THEN Rope.Concat["To: ", mle.to] ELSE starter;
IF mle.date = BasicTime.nullGMT THEN mle.date ¬ BasicTime.Now[];
date ¬ Rope.Substr[Convert.RopeFromTimeRFC822[mle.date], 0, 9];
tocx ¬ date.Cat[" ", from];
tocHead ¬ SquashRopeIntoWidth[tocx, 165];
herald ¬ RemoveComments[from];
herald ¬ Rope.Cat[herald, " ", date];
shortNameLen ¬ herald.Length[];
herald ¬ Rope.Cat[herald, " ", mle.subject];
IF herald.Length[] > 60 THEN herald ¬ Rope.Concat[herald.Substr[0, 55], " ..."];
IF herald.Length[] < shortNameLen THEN shortNameLen ¬ herald.Length[];
};
SquashRopeIntoWidth:
PROC[s:
ROPE, colWidth:
INT]
RETURNS[
ROPE] =
Truncates s with "..." or expands it with blanks, so that it is about
colWidth characters wide. Not exact, uses a few heuristics here...
BEGIN
blankCount: INT;
width: INT;
BEGIN ENABLE RuntimeError.BoundsFault =>
GOTO doItTheHardWay;
width¬ VFonts.StringWidth[s];
DO
IF width<= colWidth THEN EXIT;
-- truncate
BEGIN
guessLength: INT¬ s.Length[] * colWidth / width;
s¬ Rope.Concat[s.Substr[0, MAX[0, guessLength-4]], "..."];
width¬ VFonts.StringWidth[s];
END;
ENDLOOP;
EXITS
doItTheHardWay => [width, s]¬ DoItTheHardWay[s, colWidth];
END; -- of enable
-- At this point s is shorter than colWidth and we want to extend it with blanks
blankCount¬ ((colWidth - width) / blankWidth) + 1; -- force at least one blank
s¬ Rope.Concat[s, Rope.Substr[blanks, 0, MIN[blankCount, blanks.Length[]]]];
RETURN[s]
END;
DoItTheHardWay:
PROC[s:
ROPE, colWidth:
INT]
RETURNS[width:
INT, s1:
ROPE] = {
thisWidth: INTEGER;
dots: ROPE = "...";
nullWidth: INTEGER = VFonts.CharWidth['\000];
width¬ VFonts.StringWidth[dots];
FOR i:
INT
IN [0 .. s.Length[])
DO
thisWidth¬ VFonts.CharWidth[s.Fetch[i] ! RuntimeError.BoundsFault =>
thisWidth¬ nullWidth ];
width¬ width + thisWidth;
IF width > colWidth
THEN
{ width¬ width - thisWidth;
s1¬ Rope.Concat[s.Substr[0, MAX[0, i-1]], dots];
RETURN
};
ENDLOOP;
s1¬ s.Concat[dots];
};
RemoveComments:
PROC[name:
ROPE]
RETURNS[shortName:
ROPE] = {
start, end: INT;
name ¬ Rope.Concat[base: name, rest: " "];
first remove any "< . . .>" in the name
start ¬ Rope.Find[name, "<"];
IF start > 0
THEN
{ end ¬ Rope.Find[s1: name, s2: ">", pos1: start+1];
IF end > 0 THEN name ¬ Rope.Replace[name, start, end-start+1] };
then do the same for any ( . . . ) in the name
start ¬ Rope.Find[name, "("];
IF start > 0
THEN
{ end ¬ Rope.Find[s1: name, s2: ")", pos1: start+1];
IF end > 0 THEN name ¬ Rope.Replace[name, start, end-start+1] };
shortName ¬ Rope.Substr[name, 0, Rope.Length[name]-1]
};
GetMsgEntity:
PROC[opsH: WalnutOpsHandle, msg:
ROPE]
RETURNS[e: IsEntity] = {
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
IF msg.Length[] = 0
THEN {
sH.lastMsgEntity ¬ NIL;
RETURN[NilEntity];
};
sH.lastMsgEntity ¬ e.entity ¬ LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.mMsgInfo, msg]].entry;
e.exists ¬ sH.lastMsgEntity # NIL;
};
GetAllMsgTextInfo:
PROC[sH: SchemaHandle, m: LoganBerry.Entry]
RETURNS[textStart, textLen, formatLen:
INT, herald:
ROPE, shortNameLen:
INT] = {
rel: Relship = m;
textStart ¬ LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[rel, sH.mMIEntryStart]] + LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[rel, sH.mMITextOffset]];
textLen ¬ LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[rel, sH.mMITextLen]];
formatLen ¬ LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[rel, sH.mMIFormatLen]];
herald ¬ LoganBerryEntry.GetAttr[rel, sH.mMIHerald];
shortNameLen ¬ LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[rel, sH.mMIShortNameLen]];
};
GetTextInfo:
PROC[sH: SchemaHandle, m: LoganBerry.Entry]
RETURNS[textStart, textLen, formatLen: INT] = {
rel: Relship = m;
textStart ¬ LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[rel, sH.mMIEntryStart]] + LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[rel, sH.mMITextOffset]];
textLen¬ LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[rel, sH.mMITextLen]];
formatLen ¬ LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[rel, sH.mMIFormatLen]];
};
SetAllMsgInfo:
PROC[opsH: WalnutOpsHandle, m:
ROPE, mle: MsgLogEntry, herald:
ROPE, shortNameLen:
INT, date: BasicTime.
GMT, sender: Address] = {
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
isInReplyTo: BOOL ¬ FALSE;
sub: ROPE = mle.subject;
subject: Subject;
subName: ROPE;
afterRe: INT ¬ 0;
DO
IF sub.Find["Re: ", afterRe, FALSE] # afterRe THEN EXIT;
afterRe ¬ afterRe + 4;
isInReplyTo ¬ TRUE;
ENDLOOP;
subName ¬ sub.Substr[start: afterRe, len: 20];
IF subName.Length[] = 0 THEN subName ¬ "No Subject Field";
subject ¬ subName;
BEGIN
init: LoganBerry.Entry =
LIST[
[$Key, Rope.Concat[sH.mMsgInfo, m]],
[sH.mMIOf, m],
[sH.mMIHerald, herald],
[sH.mMIShortNameLen, LoganBerryEntry.I2V[shortNameLen]],
[sH.mMIEntryStart, LoganBerryEntry.I2V[mle.entryStart]],
[sH.mMITextOffset, LoganBerryEntry.I2V[mle.textOffset]],
[sH.mMITextLen, LoganBerryEntry.I2V[mle.textLen]],
[sH.mMIFormatLen, LoganBerryEntry.I2V[mle.formatLen]],
[sH.mMIDate, LoganBerryEntry.T2V[date]],
[sH.mMISubject, subject],
[sH.mMISubjectText, Rope.Substr[base: mle.subject, len: 99]],
[sH.mMIIsInReplyTo, LoganBerryEntry.B2V[isInReplyTo]],
[sH.mMISender, sender]
];
LoganBerry.WriteEntry[db: opsH.db, entry: init];
END;
};
SetMsgDisplayInfo:
PROC[opsH: WalnutOpsHandle, m:
ROPE, hasBeenRead:
BOOL, tocHeadEntry:
ROPE] = {
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
init: LoganBerry.Entry =
LIST[
[$Key, Rope.Concat[sH.mDisplayInfo, m]],
[sH.mDIOf, m],
[sH.mDITOCHeadEntry, tocHeadEntry],
[sH.mDIHasBeenRead, LoganBerryEntry.B2V[hasBeenRead]]
];
LoganBerry.WriteEntry[db: opsH.db, entry: init];
};
SetMsgInfo:
PROC[opsH: WalnutOpsHandle, m:
ROPE, date:
GMT, show:
BOOL] = {
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
init: LoganBerry.Entry =
LIST[
[$Key, Rope.Concat[sH.mInfo, m]],
[sH.mInfoOf, m],
[sH.mDateIs, LoganBerryEntry.T2V[date]],
[sH.mShowIs, IF show THEN "NULL" ELSE opsH.schemaHandle.unacceptedEntity]
];
LoganBerry.WriteEntry[db: opsH.db, entry: init];
};
ParseForSender:
PROC[sH: SchemaHandle, adr:
ROPE]
RETURNS[e: Address, defaultReg: ROPE] = {
thisName: ROPE;
thisName ¬ NameInWalnutFormat[adr];
defaultReg ¬ BreakName[thisName].reg;
e ¬ thisName;
};
BreakName:
PROC[name: Rope.
ROPE]
RETURNS[sn, reg: Rope.
ROPE] = {
length: INT = name.Length[];
FOR i:
INT
DECREASING
IN [0..length)
DO
IF name.Fetch[i] = '.
THEN
RETURN[
sn: name.Substr[start: 0, len: i],
reg: name.Substr[start: i+1, len: length-(i+1)] ];
ENDLOOP;
RETURN[sn: name, reg: NIL];
};
NameInWalnutFormat:
PROC[nameIn:
ROPE]
RETURNS[
ROPE] = {
this: ROPE ¬ JustTheName[nameIn];
use ← GvNsMapExtras.GvSyntax[name: this, quoteIfNeeded: FALSE];
IF use = NIL THEN {
append: ROPE;
SELECT GvNsMapExtras.SyntaxOf[this] FROM
gv => append ← ".gv";
ns => append ← ".ns";
ENDCASE => append ← ".unknown";
use ← this.Concat[append];
};
IF this.Fetch[0] = '"
THEN {
-- strip "'s
other: INT = this.Find["\"", 1];
IF other = -1
THEN this ¬ this.Substr[1]
ELSE {
firstPart: ROPE = this.Substr[1, other-1];
IF other # this.Length[] - 1
THEN {
rest: ROPE = this.Substr[other+1];
this ¬ Rope.Concat[firstPart, rest];
}
ELSE this ¬ firstPart;
};
};
RETURN[this];
};
JustTheName:
PROC[nameIn:
ROPE]
RETURNS[name: ROPE] = {
len: INT ¬ Rope.Length[nameIn];
lastChar: CHAR ¬ nameIn.Fetch[len-1];
comment: BOOL ¬ Rope.Match[pattern: "*(*)*", object: nameIn, case: FALSE];
route: BOOL ¬ Rope.Match[pattern: "*<*>*", object: nameIn, case: FALSE];
SELECT
TRUE
FROM
comment => {
len: INT ¬ Rope.Length[nameIn];
posLeft: INT ¬ Rope.Find[nameIn, "("];
posRight: INT ¬ Rope.Find[nameIn, ")", posLeft];
IF posLeft > len-posRight
THEN name ¬ Rope.Substr[base: nameIn, start: 0, len: posLeft-1]
ELSE name ¬ Rope.Substr[base: nameIn, start: posRight+1, len: len-posRight-1];
};
route => {
posLeft: INT ¬ Rope.Find[nameIn, "<"];
posRight: INT ¬ Rope.Find[nameIn, ">", posLeft];
name ¬ Rope.Substr[base: nameIn, start: posLeft+1, len: posRight-posLeft-1];
};
ENDCASE => RETURN[nameIn];
name ¬ StripOffWhiteSpace[name];
IF name.Length[] = 0 THEN RETURN[nameIn];
};
StripOffTrailingWhite
Space:
PROC [r:
ROPE]
RETURNS [
ROPE] = {
len: INT ¬ Rope.Length[r];
skip: ROPE ¬ " \t";
WHILE len > 0
DO
len ¬ len-1;
IF Rope.Find[Rope.FromChar[Rope.Fetch[r, len]], skip] = -1 THEN EXIT;
r ¬ Rope.Substr[r, 0, len];
ENDLOOP;
RETURN[r];
};
StripOffLeadingWhiteSpace:
PROC[rope:
ROPE]
RETURNS [clean:
ROPE] = {
skip: ROPE ¬ " \t";
len: INT ¬ Rope.Length[rope];
first: INT ¬ Rope.SkipOver[rope, 0, skip];
clean ¬ Rope.Substr[rope, first, len];
};
StripOffWhiteSpace:
PROC[rope:
ROPE, stripLeft:
BOOL ¬
TRUE, stripRight:
BOOL ¬
TRUE]
RETURNS [clean:
ROPE] = {
clean ¬ rope;
IF stripLeft THEN clean ¬ StripOffLeadingWhiteSpace[clean];
IF stripRight THEN clean ¬ StripOffTrailingWhiteSpace[clean];
};
ParseForAddressList:
PROC[sH: SchemaHandle, adr:
ROPE, defaultReg:
ROPE]
RETURNS[aList: LIST OF Address] = {
nameList: LIST OF ROPE ¬ NIL;
startOfAdr, charIndex: INT ¬ 0; -- nowAt reset at each call to OneName
aLen: INT = adr.Length[];
NextChar:
PROC
RETURNS [char:
CHAR] = {
IF charIndex >= aLen THEN RETURN[MailParse.endOfInput];
char ¬ adr.Fetch[charIndex];
charIndex ¬ charIndex + 1;
};
IF adr.Length[] = 0
THEN
RETURN[
NIL]
ELSE
DO
ch: CHAR;
this: ROPE;
UNTIL (ch ¬ NextChar[]) = ', OR ch = MailParse.endOfInput DO ENDLOOP;
SELECT ch
FROM
', => this ¬ adr.Substr[startOfAdr, charIndex - startOfAdr -1];
ENDCASE => this ¬ adr.Substr[startOfAdr, charIndex - startOfAdr];
startOfAdr ¬ charIndex;
IF this.Length[] # 0
THEN {
this ¬ NameInWalnutFormat[this];
nameList ¬ CONS[FixReg[this, defaultReg], nameList];
};
IF ch = MailParse.endOfInput THEN EXIT;
ENDLOOP;
FOR nL:
LIST
OF
ROPE ¬ nameList, nL.rest
UNTIL nL =
NIL
DO
ad: Address;
IF nL.first.Length[] = 0 THEN LOOP; -- one more check
ad ¬ nL.first;
aList ¬ CONS[ad, aList];
ENDLOOP;
};
FixReg:
PROC[this, defaultReg:
ROPE]
RETURNS[
ROPE] = {
dotRope: ROPE = ".";
pos: INT = Rope.FindBackward[this, dotRope];
IF pos = -1 THEN RETURN[ Rope.Cat[this, dotRope, defaultReg] ];
RETURN[this];
};
TrySomethingForThisName:
PROC[bad:
ROPE]
RETURNS[something, reg:
ROPE] = {
match, prev: INT;
matchRope: ROPE ¬ "\"";
IF (match ¬ bad.Find["<"]) # -1
THEN matchRope ¬ ">"
ELSE match ¬ bad.Find["\""];
IF match # -1
THEN {
end: INT ¬ bad.Find[matchRope, match+1];
IF end = -1
THEN something ¬ bad
ELSE something ¬ bad.Substr[match+1, end-match-1];
}
ELSE something ¬ bad; -- use it all
match ¬ prev ¬ -1;
UNTIL (match ¬ something.Find[".", prev+1]) = -1 DO prev ¬ match; ENDLOOP;
IF prev # -1 THEN reg ¬ something.Substr[prev+1];
};
SetAddresses:
PROC[opsH: WalnutOpsHandle, m:
ROPE, mle: MsgLogEntry]
RETURNS[sender: Address]= {
sH: SchemaHandle = opsH.schemaHandle;
defaultReg: ROPE;
from: LIST OF Address;
to: LIST OF Address;
cc: LIST OF Address;
[sender, defaultReg] ¬ ParseForSender[sH, mle.sender];
IF opsH.completeSchema
THEN {
init: LoganBerry.Entry;
from ¬ ParseForAddressList[sH, mle.from, defaultReg];
to ¬ ParseForAddressList[sH, mle.to, defaultReg];
cc ¬ ParseForAddressList[sH, mle.cc, defaultReg];
init ¬
LIST[
[$Key, Rope.Concat[sH.toRelation, m]],
[sH.toMsg, m],
[sH.toDate, LoganBerryEntry.T2V[mle.date]]
];
FOR tL:
LIST
OF Address ¬ to, tL.rest
UNTIL tL =
NIL
DO
init ¬ LoganBerryEntry.AddAttr[init, sH.toAddress, tL.first];
ENDLOOP;
LoganBerry.WriteEntry[db: opsH.db, entry: init];
init ¬
LIST[
[$Key, Rope.Concat[sH.ccRelation, m]],
[sH.ccMsg, m],
[sH.ccDate, LoganBerryEntry.T2V[mle.date]]
];
FOR ccL:
LIST
OF Address ¬ cc, ccL.rest
UNTIL ccL =
NIL
DO
init ¬ LoganBerryEntry.AddAttr[init, sH.ccAddress, ccL.first];
ENDLOOP;
LoganBerry.WriteEntry[db: opsH.db, entry: init];
init ¬
LIST[
[$Key, Rope.Concat[sH.fromRelation, m]],
[sH.fromMsg, m],
[sH.fromDate, LoganBerryEntry.T2V[mle.date]]
];
FOR fL:
LIST
OF Address ¬ from, fL.rest
UNTIL fL =
NIL
DO
init ¬ LoganBerryEntry.AddAttr[init, sH.fromAddress, fL.first];
ENDLOOP;
LoganBerry.WriteEntry[db: opsH.db, entry: init];
};
};
GetToList:
PROC[opsH: WalnutOpsHandle, me:
ROPE]
RETURNS[tL:
LIST
OF
ROPE] = {
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
rel: Relship;
rel ¬ LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.toRelation, me]].entry;
tL ¬ LoganBerryEntry.GetAllAttrs[rel, sH.toAddress];
};
GetFromList:
PROC[opsH: WalnutOpsHandle, me:
ROPE]
RETURNS[fL:
LIST
OF
ROPE] = {
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
rel: Relship;
rel ¬ LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.fromRelation, me]].entry;
fL ¬ LoganBerryEntry.GetAllAttrs[rel, sH.fromAddress];
};
GetCcList:
PROC[opsH: WalnutOpsHandle, me:
ROPE]
RETURNS[ccL:
LIST
OF
ROPE] = {
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
rel: Relship;
rel ¬ LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.ccRelation, me]].entry;
ccL ¬ LoganBerryEntry.GetAllAttrs[rel, sH.ccAddress];
};
SetVar: UserProfile.ProfileChangedProc = {
useFromFieldInTOC ¬
UserProfile.Boolean[key: "Walnut.UseFromFieldInTOC", default: FALSE];
};
UserProfile.CallWhenProfileChanges[SetVar];