eudora-mac/nickmng.c

1 line
115 KiB
C
Executable File
Raw Permalink Blame History

/* Copyright (c) 2017, Computer History Museum
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted (subject to
the limitations in the disclaimer below) provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Computer History Museum nor the names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE
COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE. */
#include "nickmng.h"
#define FILE_NUM 2
/* Copyright (c) 1990-1992 by the University of Illinois Board of Trustees */
/************************************************************************
* routines to manage the nickname list
************************************************************************/
#pragma segment NickMng
/************************************************************************
* 7/17/96 ALB
* Performance update for very large nickname files.
* - Each nickname is no longer stored in a separate handle. They
* are all stored in one handle with an offset to each one
* - Addresses, notes, and view data are no longer kept in a separate handle
************************************************************************/
/************************************************************************
* The aliases list has the following structure:
* <length byte>name-of-alias<2 length bytes>expansion-of-alias...
* The aliases file contains lines of the form:
* "alias" name-of-alias expansion of alias<newline>
* Newlines may be escaped with "\".
* Lines not beginning with "alias" will be ignored
************************************************************************/
/************************************************************************
* private functions
************************************************************************/
typedef struct
{
Str31 name;
long offset;
} AliasSortType, *AliasSortPtr, **AliasSortHandle;
typedef struct
{
long hashName;
long hashAddress;
// long createDate; (eventually)
// long modDate; (eventually)
// long usageDate; (eventually)
long cacheDate;
long addressOffset;
long notesOffset;
NicknameFlagsType flags; // New for 5.0
} NickTOCStruct;
typedef struct
{
long offset;
short nickIndex;
} NickOffSetSortType;
Boolean NeatenLine(UPtr line, long *len);
void CheckForNicknameBogosity (short which);
Boolean SaveIndNickFile(short which, Boolean saveChangeBits);
Boolean SaveFileFast (short which, Boolean saveChangeBits);
int NickOffsetCompare(NickOffSetSortType *n1, NickOffSetSortType *n2);
void NickOffsetSwap(NickOffSetSortType *n1, NickOffSetSortType *n2);
PStr StripQualifiersAndHonorifics (PStr name, PStr strippedName);
OSErr AddHandleToAddressHashes(TextAddrHandle sourceHandle,AccuPtr a);
int SortAddrNameCompare(UPtr *s1, UPtr *s2);
#define This (*Aliases)[which]
short TotalNumOfNicks;
OSErr ReadNicknames(short which);
Boolean SplitNicknames (short ab);
OSErr KillNickTOC(FSSpecPtr spec);
OSErr ReadNickTOC(short which);
OSErr WriteNickTOC(short which);
Boolean NickFileOkForFastSave(short which);
void SaveDirtyPictures (short ab);
long NickMatchFoundLo(NickStructHandle theNicknames,long theNicknamesLen,long hashName,PStr theName,short which);
/************************************************************************
* ReadNicknames - read the list of aliases
* Can be called either to read the nicknames initially or simply to make
* sure they're all in memory
************************************************************************/
OSErr ReadNicknames(short which)
{
BinAddrHandle shortAddress;
FSSpec spec;
OSErr err = noErr;
Str255 line;
short type;
Boolean exLine=False;
long expOffset=0;
long len;
Str31 aliasCmd, noteCmd;
Str31 currentCmd,tempName;
LineIOD lid;
long i,count;
long currNickIndex;
long hashName;
Boolean doingAlias,doingNote;
OSErr theMemErr = noErr;
long nameOffset = 0;
Accumulator nameAcc,dataAcc,lineAcc;
char lookingFor;
long firstOffset;
long lineOffset;
long curOffset;
Boolean firstRead = This.hNames==nil; // are we reading for the first time? (or filling in)
Boolean lastExLine;
/*
* Setup section
*/
Zero(nameAcc);
Zero(dataAcc);
Zero(lineAcc);
// put existing handles into accumulators
nameAcc.data = This.hNames;
if (nameAcc.data) nameAcc.size = nameAcc.offset = GetHandleSize(nameAcc.data);
dataAcc.data = (void*)This.theData;
if (dataAcc.data) dataAcc.size = dataAcc.offset = GetHandleSize(dataAcc.data);
//Find the file, open it for reading, get the command names
spec = This.spec;
This.collapsed = FindSTRNIndex(NickFileCollapseStrn,spec.name) > 0;
GetRString(aliasCmd,ALIAS_CMD);
GetRString(noteCmd,NOTE_CMD);
if (err=FSpOpenLine(&spec,fsRdPerm,&lid))
return(err==fnfErr ? noErr : FileSystemError(OPEN_ALIAS,spec.name,err));
// accumulator to hold the line we're working on
if (theMemErr = AccuInit(&lineAcc))
goto hitMemError;
/*
* the main reading loop; read lines until no more
*/
curOffset = firstOffset = lastExLine = 0;
while ((type=GetLine(line,sizeof(line),&len,&lid))>0)
{
lastExLine = exLine;
lineOffset = curOffset; // offset of beginning of line
curOffset += len; // this is the offset of the next line
exLine = line[len-1]!='\015';
// Record the offset where we first start collecting data
if (lineAcc.offset==0) firstOffset = lineOffset;
// if the last line was escaped or we're looking at a partial line, just accumulate
if (lastExLine || exLine) // If the line was escaped or we're in the middle of a line
{
// first, fix up the line
exLine = NeatenLine(line,&len) || exLine;
// if the previous line was escaped and this line doesn't begin
// with a space, add one now
if (lastExLine && type!=LINE_MIDDLE && !issep(*line))
if (theMemErr=AccuAddChar(&lineAcc,' ')) goto hitMemError;
// if this line is further extended, append it now
// if it's not, it will get appended inside the next if
if (exLine && (theMemErr=AccuAddPtr(&lineAcc,line,len)))
goto hitMemError;
}
if (!exLine)
{
// Previous line was not escaped. Is this one?
exLine = NeatenLine(line,&len) || exLine;
// and append the current line to the line accumulator
if (theMemErr=AccuAddPtr(&lineAcc,line,len)) goto hitMemError;
// if the line was escaped, just go round again; we want to deal with
// full lines only
if (exLine) continue;
// ok, now we know that we have a full line
// if not empty, do parsing and add nickname here
if (lineAcc.offset)
{
// first string of non-space characters is the command name
for (i=0;i<sizeof(currentCmd) - 1;i++)
if ((*lineAcc.data)[i] == ' ') break;
MakePStr(currentCmd,*lineAcc.data,i);
/*
* find the nickname
*/
// first, skip over spaces
while (i<lineAcc.offset && (*lineAcc.data)[i]==' ') i++;
// if the character we found is not a quote, then back up one,
// because we want i to be the position just BEFORE the nickname
if ((*lineAcc.data)[i]!='"') i--;
// If we found a quote, we'll be looking for a quote to finish the nickname
lookingFor = (*lineAcc.data)[i];
for (count=i+1;count<lineAcc.offset;count++)
{
if ((*lineAcc.data)[count] == lookingFor)
break;
}
// Eureka! Copy the name into a pascal string and find its hash
MakePStr(tempName,(*lineAcc.data)+i+1,count-i-1);
SanitizeFN(tempName,tempName,NICK_STORED_BAD_CHAR,NICK_STORED_REP_CHAR,false);
hashName = NickHash(tempName);
if (lookingFor=='"') count++; // skip the quote later when looking
// at the body of the line
/*
* process the contents
*/
// Is this an alias command or a note command?
doingAlias = StringSame(aliasCmd,currentCmd);
doingNote = doingAlias ? false : StringSame(noteCmd,currentCmd);
if ((doingAlias || doingNote) && *tempName) // We have a valid command and a valid nickname
{
// Add to toc if necessary
if ((currNickIndex = NickMatchFoundLo(This.theData,dataAcc.offset,hashName,tempName,which)) < 0 ) // Scan list for nickname
{
ASSERT(firstRead);
if (firstRead)
{
// Nickname doesn't exist...create new one and clear it out
// If it doesn't exist, add it
NickStruct nickInfo;
// Append the name
if (theMemErr = AccuAddPtr(&nameAcc,&tempName,*tempName + 1))
goto hitMemError;
// Set nickname info
nickInfo.hashName = hashName;
nickInfo.hashAddress = -1;
nickInfo.addressesDirty = false;
nickInfo.notesDirty = false;
nickInfo.pornography = false;
nickInfo.deleted = false;
nickInfo.group = false;
nickInfo.theAddresses = nil;
nickInfo.theNotes = nil;
nickInfo.nameTOCOffset = nameOffset;
nameOffset += *tempName+1;
if (doingAlias) // Command is alias
{
// Put offset of address in file into nickname
nickInfo.addressOffset = firstOffset + 1;
// Initialize notes offset to something not valid
nickInfo.notesOffset = -1;
// Hash the addresses in semi-complicated fashion
shortAddress = nil;
// As a last check... see if the address is already present in a nickname file
if (!SuckPtrAddresses (&shortAddress, LDRef (lineAcc.data) + count + 1, lineAcc.offset - count - 1, false, true, false, nil))
nickInfo.hashAddress = NickHashString (LDRef (shortAddress));
nickInfo.group = ContainsMultipleAddresses (shortAddress);
UL (lineAcc.data);
ZapHandle (shortAddress);
}
if (doingNote) // Command is note
{
// Put offset of notes in file into nickname
nickInfo.notesOffset = firstOffset + 1;
// Initialize address offset to something not valid
nickInfo.addressOffset = -1;
}
// Add nickname info to array handle
if (theMemErr = AccuAddPtr(&dataAcc,(void*)&nickInfo,sizeof(NickStruct)))
goto hitMemError;
currNickIndex = dataAcc.offset/sizeof(NickStruct) - 1;
}
}
// fill in data if necessary
if (This.theData) {
if (doingAlias && (*(This.theData))[currNickIndex].addressOffset==-1)
{
// Put offset of address in file into nickname
(*(This.theData))[currNickIndex].addressOffset = firstOffset + 1;
// trim off the command and alias
BMD(*lineAcc.data+count+1,*lineAcc.data,lineAcc.offset-count-1);
lineAcc.offset -= count + 1;
AccuTrim(&lineAcc);
// And fill in the data
(*(This.theData))[currNickIndex].theAddresses = lineAcc.data;
Zero(lineAcc);
}
if (doingNote && (*(This.theData))[currNickIndex].notesOffset==-1)
{
// Put offset of notes in file into nickname
(*(This.theData))[currNickIndex].notesOffset = firstOffset + 1;
// trim off the command and alias
BMD(*lineAcc.data+count+1,*lineAcc.data,lineAcc.offset-count-1);
lineAcc.offset -= count + 1;
AccuTrim(&lineAcc);
// And fill in the data
(*(This.theData))[currNickIndex].theNotes = lineAcc.data;
Zero(lineAcc);
}
}
}
lineAcc.offset = 0;
}
}
}
/*
* cleanup
*/
// close out a little stuff
AccuZap(lineAcc);
CloseLine(&lid);
// report any errors
if (type || err)
{
AccuZap(dataAcc);
AccuZap(nameAcc);
return(err ? WarnUser(ALLO_ALIAS,err) :
FileSystemError(READ_ALIAS,spec.name,type));
}
// Success!! install the toc and names handles
if (firstRead)
{
AccuTrim(&dataAcc);
AccuTrim(&nameAcc);
This.hNames = nameAcc.data;
This.theData = (void*)dataAcc.data;
CheckForNicknameBogosity (which);
}
return(noErr);
hitMemError:
err = WarnUser(ALLO_ALIAS,theMemErr);
CloseLine(&lid);
AccuZap(lineAcc);
// kill these only on first read; on reread leave existing handles alone!
if (firstRead)
{
AccuZap(dataAcc);
AccuZap(nameAcc);
}
return (err);
}
void CheckForNicknameBogosity (short which)
{
Str255 beautifulName;
short count,
index;
Boolean bogus;
bogus = false;
count = This.theData ? (GetHandleSize_ (This.theData) / sizeof (NickStruct)) : 0;
for (index = 0; !bogus && index < count; ++index) {
BeautifyFrom (GetNicknameNamePStr (which, index, beautifulName));
if (!(*(This.theData))[index].deleted && (*(This.theData))[index].hashName != NickHash (beautifulName))
bogus = true;
}
This.containsBogusNicks = bogus;
}
/************************************************************************
* NickMatchFound - find a given nickname in a nickname structure,
* i.e. a nickname file
* //SD - I rearranged things a bit for efficiency
************************************************************************/
long NickMatchFound(NickStructHandle theNicknames,long hashName,PStr theName,short which)
{
return (NickMatchFoundLo(theNicknames,GetHandleSize_(theNicknames),hashName,theName,which));
}
/************************************************************************
* NickMatchFoundLo - find a given nickname in a nickname structure,
* i.e. a nickname file
* //SD - I rearranged things a bit for efficiency
************************************************************************/
long NickMatchFoundLo(NickStructHandle theNicknames,long theNicknamesLen,long hashName,PStr theName,short which)
{
long i;
Str31 tempStr, tempName;
long stop;
Boolean needStringMatch = false;
long matched = -1;
NickStruct *theStruct, *endStruct;
if (!theNicknames)
return (-1);
if (*theName > sizeof(Str31) - 1)
return (-1);
stop = (theNicknamesLen/sizeof(NickStruct));
endStruct = (*theNicknames)+stop;
for (theStruct=*theNicknames; theStruct<endStruct; theStruct++)
if (theStruct->hashName == hashName && !theStruct->deleted)
if (matched==-1) matched = theStruct-*theNicknames;
else {needStringMatch = True; break;}
if (!needStringMatch) return(matched);
PSCopy(tempName,theName);
*tempName = RemoveChar(' ',tempName+1,*tempName);
for (i=0;i<stop;i++)
{
if ((*theNicknames)[i].hashName == hashName && !(*theNicknames)[i].deleted)
{
GetNicknameNamePStr(which,i,tempStr);
*tempStr = RemoveChar(' ',tempStr+1,*tempStr);
if (StringSame(tempName,tempStr))
return (i);
}
}
return (-1);
}
/************************************************************************
* NickAddressMatchFound - find a given address in a nickname structure,
* i.e. a nickname file
************************************************************************/
long NickAddressMatchFound (NickStructHandle theNicknames, long hashAddress, PStr theAddress, short which)
{
BinAddrHandle addresses;
NickStruct *theStruct,
*endStruct;
Handle theAddresses;
Str255 tempStr,
tempAddress;
long stop,
matched,
i;
Boolean needStringMatch;
if (!theNicknames)
return (-1);
matched = -1;
needStringMatch = false;
stop = GetHandleSize_ (theNicknames) / sizeof (NickStruct);
endStruct = (*theNicknames) + stop;
for (theStruct = *theNicknames; theStruct < endStruct; theStruct++)
if (theStruct->hashAddress == hashAddress && !theStruct->deleted)
if (matched == -1)
matched = theStruct - *theNicknames;
else {
needStringMatch = true;
break;
}
if (!needStringMatch)
return (matched);
PSCopy(tempAddress, theAddress);
*tempAddress = RemoveChar (' ', tempAddress + 1, *tempAddress);
for (i = 0; i < stop; i++)
if ((*theNicknames)[i].hashAddress == hashAddress && !(*theNicknames)[i].deleted)
if (theAddresses = GetNicknameData(which,i,true,true)) {
SuckPtrAddresses (&addresses, LDRef (theAddresses), GetHandleSize (theAddresses), False, False, False, nil);
UL (theAddresses);
if (addresses) {
PCopy (tempStr, *addresses);
*tempStr = RemoveChar (' ', tempStr + 1, *tempStr);
ZapHandle (addresses);
if (StringSame (tempAddress, tempStr))
return (i);
}
}
return (-1);
}
/************************************************************************
* RegenerateAliases - make sure the alias list is in memory
************************************************************************/
OSErr RegenerateAliases(short which,Boolean rebuild)
{
OSErr err = noErr;
UHandle hand;
if (rebuild) ZapAliasFile(which);
if (!This.theData) // If handle for data doesn't exist, create it
{
hand = NuHTempOK(0L);
if (!hand) err = WarnUser(ALLO_ALIAS,MemError());
This.theData = (NickStructHandle)hand;
HNoPurge_(This.theData);
}
else // Handle exists
{
if (!rebuild)
return (noErr);
}
if (!err)
{
// Check to see if the TOC exists in the resource. If so, read it into the structure.
// If not, read in the nicknames and then write out the TOC
if (!rebuild)
err = ReadNickTOC(which);
if (err != noErr || rebuild)
{
ZapHandle(This.theData);
hand = NuHTempOK(0L);
if (!hand) err = WarnUser(ALLO_ALIAS,MemError());
This.theData = (NickStructHandle)hand;
HNoPurge_(This.theData);
err = ReadNicknames(which);
if (!err && !This.ro) {
WriteNickTOC(which);
#ifdef VCARD
if (!IsPluginAddressBook (which) && !IsPersonalAddressBook (which) && SplitNicknames (which))
#else
if (!IsPluginAddressBook (which) && SplitNicknames (which))
#endif
{
SetAliasDirty(which);
SaveIndNickFile (which, true);
}
}
}
}
if (err)
ZapHandle(This.theData);
return(err);
}
Boolean SplitNicknames (short ab)
{
Str255 name;
Handle notes;
short nick,
totalNicks;
SInt8 oldHState;
Boolean notesChanged;
notesChanged = false;
totalNicks = (*Aliases)[ab].theData ? (GetHandleSize_((*Aliases)[ab].theData) / sizeof (NickStruct)) : 0;
for (nick = 0; nick < totalNicks; ++nick) {
if (notes = GetNicknameData (ab, nick, false, true)) {
oldHState = HGetState (notes);
if (MaybeApplySplittingAlgorithm (notes) && !(*Aliases)[ab].ro) {
ReplaceNicknameNotes (ab, GetNicknameNamePStr (ab, nick, name), notes);
notesChanged = true;
}
else
// If we did not split the name, restore the note's previous purge state
HSetState (notes, oldHState);
}
}
return (notesChanged);
}
//
// MaybeApplySplittingAlgorithm
//
// Apply our name splitting algorithm to the notes if we have a full name,
// and we have no first or last name. This grows the notes handle and returns
// true when a split has taken place. Likewise, if we have a first and/or
// last name, but no full name, employ our joining algorithm to build the
// full name.
//
Boolean MaybeApplySplittingAlgorithm (Handle notes)
{
Str255 nameTag,
firstTag,
lastTag,
realName,
firstName,
lastName;
UPtr notesPtr,
newPtr,
attribute,
value;
Size notesSize;
long attributeLength,
valueLength;
Boolean notesChanged;
notesChanged = false;
*realName = 0;
*firstName = 0;
*lastName = 0;
GetRString (nameTag, ABReservedTagsStrn + abTagName);
GetRString (firstTag, ABReservedTagsStrn + abTagFirst);
GetRString (lastTag, ABReservedTagsStrn + abTagLast);
// Walk through the 'notes', looking for attribute/value pairs we care about.
notesSize = GetHandleSize (notes);
notesPtr = LDRef (notes);
while ((!*realName || !*firstName || !*lastName) && (newPtr = ParseAttributeValuePair (notesPtr, notesSize - (notesPtr - *notes), &attribute, &attributeLength, &value, &valueLength))) {
// Check to see if we hit a tag we care about
if (*nameTag == attributeLength && !memcmp (&nameTag[1], attribute, attributeLength))
MakePPtr (realName, value, valueLength);
else
if (*firstTag == attributeLength && !memcmp (&firstTag[1], attribute, attributeLength))
MakePPtr (firstName, value, valueLength);
else
if (*lastTag == attributeLength && !memcmp (&lastTag[1], attribute, attributeLength))
MakePPtr (lastName, value, valueLength);
notesPtr = newPtr;
}
UL (notes);
// If we have a real name, but neither a first or last name, apply our splitting algorithm
if (*realName && !*firstName && !*lastName) {
ParseFirstLast (realName, firstName, lastName);
// Append the first and last names to the end of our notes
if (*firstName)
if (!AddAttributeValuePair (notes, firstTag, &firstName[1], *firstName))
notesChanged = true;
if (*lastName)
if (!AddAttributeValuePair (notes, lastTag, &lastName[1], *lastName))
notesChanged = true;
}
else
// If we have a first and/or last name, but no real name, create one
if (!*realName && (*firstName || *lastName)) {
// Apply joining algorithm
JoinFirstLast (realName, firstName, lastName);
if (*realName)
if (!AddAttributeValuePair (notes, nameTag, &realName[1], *realName))
notesChanged = true;
}
return (notesChanged);
}
//
// GetTaggedFieldValue
//
// Pull a tagged value out of the notes portion of a nickname
//
Handle GetTaggedFieldValue (short ab, short nick, PStr tag)
{
return (GetTaggedFieldValueInNotes (GetNicknameData (ab, nick, false, true), tag));
}
Handle GetTaggedFieldValueInNotes (Handle notes, PStr tag)
{
Handle hValue;
UPtr attribute,
value,
notesPtr,
newPtr;
Size notesSize;
long attributeLength,
valueLength;
Boolean found;
hValue = nil;
if (notes) {
// Walk through the 'notes', looking for attribute/value pairs. We'll set the value of any object
// we find
notesSize = GetHandleSize (notes);
notesPtr = LDRef (notes);
found = false;
while (!found && (newPtr = ParseAttributeValuePair (notesPtr, notesSize - (notesPtr - *notes), &attribute, &attributeLength, &value, &valueLength))) {
// Is this the tag we're looking for?
if (*tag == attributeLength && !memcmp (&tag[1], attribute, attributeLength))
found = true;
else
notesPtr = newPtr;
}
if (found)
{
PtrToHand (value, &hValue, valueLength);
Tr(hValue,"\002",">");
}
UL (notes);
}
return (hValue);
}
PStr GetTaggedFieldValueStr (short ab, short nick, PStr tag, PStr value)
{
return (GetTaggedFieldValueStrInNotes (GetNicknameData (ab, nick, false, true), tag, value));
}
PStr GetTaggedFieldValueStrInNotes (Handle notes, PStr tag, PStr value)
{
Str255 attribute;
UPtr notesPtr,
newPtr;
Size notesSize;
Boolean found;
*value = 0;
if (notes) {
// Walk through the 'notes', looking for attribute/value pairs.
notesSize = GetHandleSize (notes);
notesPtr = LDRef (notes);
found = false;
while (!found && (newPtr = ParseAttributeValuePairStr (notesPtr, notesSize - (notesPtr - *notes), attribute, value))) {
// Is this the tag we're looking for?
if (StringSame (tag, attribute))
found = true;
else
notesPtr = newPtr;
}
if (!found)
*value = 0;
else
TrLo(value+1,*value,"\002",">");
UL (notes);
}
return (value);
}
OSErr SetTaggedFieldValue (short ab, short nick, PStr tag, PStr value, NickFieldSetValueType setValue, short separatorIndex, Boolean *ignored)
{
Handle notes;
OSErr theError;
theError = noErr;
if (notes = GetNicknameData (ab, nick, false, true))
theError = SetTaggedFieldValueInNotes (notes, tag, &value[1], *value, setValue, separatorIndex, ignored);
return (theError);
}
OSErr SetTaggedFieldValueInNotes (Handle notes, PStr tag, Ptr value, long length, NickFieldSetValueType setValue, short separatorIndex, Boolean *ignored)
{
Str255 concatString;
UPtr attribute,
originalValue,
notesPtr,
newPtr;
OSErr theError;
Size notesSize;
long attributeLength,
originalValueLength;
Boolean found;
TrLo(value,length,">","\002");
theError = noErr;
if (notes) {
// Walk through the 'notes', looking for attribute/value pairs.
notesSize = GetHandleSize (notes);
notesPtr = LDRef (notes);
found = false;
while (!found && (newPtr = ParseAttributeValuePair (notesPtr, notesSize - (notesPtr - *notes), &attribute, &attributeLength, &originalValue, &originalValueLength))) {
// Is this the tag we're looking for?
if ((*tag == attributeLength) && !memcmp (&tag[1], attribute, attributeLength))
found = true;
else
notesPtr = newPtr;
}
UL (notes);
if (found) {
switch (setValue) {
case nickFieldReplaceExisting:
Munger (notes, originalValue - *notes, nil, originalValueLength, value, length);
break;
case nickFieldAppendExisting:
GetRString (concatString, separatorIndex);
if (*concatString) {
Munger (notes, originalValue - *notes + originalValueLength, nil, 0, &concatString[1], *concatString);
originalValueLength += *concatString;
}
Munger (notes, originalValue - *notes + originalValueLength, nil, 0, value, length);
break;
case nickFieldIgnoreExisting:
if (ignored)
*ignored = true;
}
theError = MemError ();
}
else
theError = AddAttributeValuePair (notes, tag, value, length);
}
TrLo(value,length,"\002",">");
return (theError);
}
OSErr SetNicknameChangeBit (Handle notes, ChangeBitType changeBits, Boolean clearFirst)
{
Str255 changeTag,
value;
OSErr theError;
long num;
theError = noErr;
if (notes) {
GetRString (changeTag, ABHiddenTagsStrn + abTagChangeBits);
num = clearFirst ? 0 : GetNicknameChangeBits (notes);
num |= (long) changeBits;
NumToString (num, value);
theError = SetTaggedFieldValueInNotes (notes, changeTag, &value[1], *value, nickFieldReplaceExisting, 0, nil);
}
return (theError);
}
long GetNicknameChangeBits (Handle notes)
{
Str255 changeTag,
value;
long num;
num = 0;
if (notes) {
GetRString (changeTag, ABHiddenTagsStrn + abTagChangeBits);
// Retrieve the current value
GetTaggedFieldValueStrInNotes (notes, changeTag, value);
// Convert it to a number
StringToNum (value, &num);
}
return (num);
}
//
// FindTaggedFieldValue
//
// Find a particular tagged field in a nickname, returning offsets to the attribute and value.
//
Boolean FindTaggedFieldValueOffsets (short ab, short nick, PStr tag, long *attributeOffset, long *attributeLength, long *valueOffset, long *valueLength)
{
Handle notes;
UPtr notesPtr,
newPtr,
attribute,
value;
Size notesSize;
Boolean found;
found = false;
if (notes = GetNicknameData (ab, nick, false, true)) {
// Walk through the 'notes', looking for attribute/value pairs.
notesSize = GetHandleSize (notes);
notesPtr = LDRef (notes);
while (!found && (newPtr = ParseAttributeValuePair (notesPtr, notesSize - (notesPtr - *notes), &attribute, attributeLength, &value, valueLength)))
// Is this the tag we're looking for?
if ((*tag == *attributeLength) && !memcmp (&tag[1], attribute, *attributeLength)) {
*attributeOffset = attribute - *notes;
*valueOffset = value - *notes;
found = true;
}
else
notesPtr = newPtr;
UL (notes);
}
return (found);
}
/************************************************************************
* RegnerateAllAliases - regnerate aliases from all files
************************************************************************/
OSErr RegenerateAllAliases(Boolean rebuild)
{
short which = NAliases;
short err = 0;
Str255 scratch,name;
short i,n;
while (which--)
if (err = RegenerateAliases(which,rebuild))
{
if (which)
{
if (err != -108)
{
PCopy(name,This.spec.name);
ComposeRString(scratch,NICK_FILE_GONE,name);
AlertStr(OK_ALRT,Caution,scratch);
}
n = NAliases;
for (i=which;i<n-1;i++) (*Aliases)[i] = (*Aliases)[i+1];
HUnlock((Handle) Aliases);
SetHandleBig((Handle)Aliases,(n-1)*sizeof(**Aliases));
break;
}
else DieWithError(NO_MAIN_NICK,err);
}
if (!err) AliasRefCount++;
ASSERT(NAliases);
return(err);
}
/************************************************************************
* ZapAliasFile - release memory for all aliases in a file
************************************************************************/
void ZapAliasFile(short which)
{
short i;
NickStructHandle aliases = Aliases ? This.theData : nil;
if (aliases)
{
for (i=0;i<NNicknames;i++)
{
ZapHandle((*aliases)[i].theAddresses); // (jp) 7/31/99 Big ol' hanging leak fixed
ZapHandle((*aliases)[i].theNotes); // (jp) 7/31/99 Big ol' hanging leak fixed
}
ZapHandle(This.theData);
}
if (Aliases) {
ZapHandle(This.hNames);
This.dirty = false;
}
}
/************************************************************************
* ZapAliases - release memory for all aliases
************************************************************************/
void ZapAliases(void)
{
short which = NAliases;
while (which--)
ZapAliasFile(which);
}
/************************************************************************
* ZapAliases - release all the alias hashes
************************************************************************/
void ZapAliasHash(short which)
{
AccuZap(This.addressHashes);
}
/************************************************************************
* ZapPluginAliases - release memory for all plugin aliases
************************************************************************/
void ZapPluginAliases(void)
{
short which = NAliases;
// All of the plugin aliases should be at the end of the list
while (which-- && IsPluginAddressBook (which))
ZapAliasFile(which);
SetHandleBig_(Aliases,(which+1) * sizeof(AliasDesc));
}
/************************************************************************
* NickHash - return a hash value on the lowercase of the name
************************************************************************/
long NickHash(UPtr newName)
{
if (*newName > sizeof(Str31) - 1)
return (-1);
return (NickHashString (newName));
}
// Redundancy ensues... (clean all this up later)
long NickHashString (PStr string)
{
Str255 tempStr;
PCopy (tempStr, string);
*tempStr = RemoveChar (' ', tempStr + 1, *tempStr);
*tempStr = RemoveChar (optSpace, tempStr + 1, *tempStr);
MyLowercaseText (tempStr + 1, *tempStr);
return (Hash (tempStr));
}
long NickHashHandle (Handle h)
{
Str255 tempStr;
Size len;
*tempStr = 0;
if (h) {
len = GetHandleSize (h);
tempStr[0] = MIN (len, (sizeof (tempStr) - 1));
BlockMoveData (*h, &tempStr[1], tempStr[0]);
*tempStr = RemoveChar (' ', tempStr + 1, *tempStr);
MyLowercaseText (tempStr + 1, *tempStr);
}
return (Hash (tempStr));
}
long NickHashRawAddresses (Handle addresses, Boolean *group)
{
BinAddrHandle shortAddress;
long hashValue;
hashValue = -1;
if (addresses) {
// Hash the addresses in semi-complicated fashion
shortAddress = nil;
// As a last check... see if the address is already present in a nickname file
if (!SuckPtrAddresses (&shortAddress, LDRef (addresses), GetHandleSize (addresses), false, false, false, nil))
hashValue = NickHashString (LDRef (shortAddress));
UL (addresses);
*group = ContainsMultipleAddresses (shortAddress);
ZapHandle (shortAddress);
}
return (hashValue);
}
/************************************************************************
* NickGenerateUniqueID - generate a unique ID for a nickname
*
* The ID will put a counter in the upper 16 bits, and Random
* in the lower 16. The counter is, effectively, unique until
* the user resets the 'next id' preference in settings. But
* even with the sequence starting over again, each is matched
* with a random number, so we're in pretty good shape.
************************************************************************/
long NickGenerateUniqueID(void)
{
long id;
id = GetPrefLong (PREF_NEXT_NICK_UNIQUE_ID);
SetPrefLong (PREF_NEXT_NICK_UNIQUE_ID, id + 1);
id <<= 16;
id |= ((long) Random () & 0x0000FFFF);
return (id);
}
OSErr PrepAllAddressBooksForSync (void)
{
OSErr theError;
short totalABs,
ab;
theError = noErr;
totalABs = NAliases;
for (ab = 0; !theError && ab < totalABs; ++ab)
theError = PrepAddressBookForSync (ab);
return (theError);
}
OSErr PrepAddressBookForSync (short ab)
{
Str255 idTag,
changeBitsTag;
OSErr theError;
short totalNicks,
nick;
theError = noErr;
GetRString (idTag, ABHiddenTagsStrn + abTagUniqueID);
GetRString (changeBitsTag, ABHiddenTagsStrn + abTagChangeBits);
totalNicks = (*Aliases)[ab].theData ? (GetHandleSize_ ((*Aliases)[ab].theData) / sizeof (NickStruct)) : 0;
for (nick = 0; !theError && nick < totalNicks; ++nick)
theError = PrepNicknameForSync (ab, nick, idTag, changeBitsTag);
return (theError);
}
OSErr PrepNicknameForSync (short ab, short nick, Str255 idTag, Str255 changeBitsTag)
{
Handle notes;
Str255 idString,
scratch,
name;
OSErr theError;
Boolean notesExist,
replaceNotes;
theError = noErr;
notes = GetNicknameData (ab, nick, false, true);
if (!(notesExist = notes ? true : false))
notes = NuHandle (0);
if (notes) {
replaceNotes = false;
GetTaggedFieldValueStrInNotes (notes, idTag, scratch);
if (!*scratch) {
replaceNotes = true;
NumToString (NickGenerateUniqueID (), idString);
theError = SetTaggedFieldValueInNotes (notes, idTag, &idString[1], *idString, nickFieldReplaceExisting, 0, nil);
}
GetTaggedFieldValueStrInNotes (notes, changeBitsTag, scratch);
if (!theError && !*scratch) {
replaceNotes = true;
theError = SetNicknameChangeBit (notes, changeBitAdded, false);
}
if (!theError && replaceNotes)
ReplaceNicknameNotes (ab, GetNicknameNamePStr (ab, nick, name), notes);
}
if (!notesExist)
ZapHandle (notes);
return (theError);
}
//
// ClearAllAddressBookChangeBits
//
// Clears change bits as specified by the mask.
//
OSErr ClearAllAddressBookChangeBits (long mask)
{
OSErr theError;
short totalABs,
ab;
theError = noErr;
totalABs = NAliases;
for (ab = 0; !theError && ab < totalABs; ++ab)
theError = ClearAddressBookChangeBits (ab, mask);
return (theError);
}
OSErr ClearAddressBookChangeBits (short ab, long mask)
{
OSErr theError;
short totalNicks,
nick;
theError = noErr;
totalNicks = (*Aliases)[ab].theData ? (GetHandleSize_ ((*Aliases)[ab].theData) / sizeof (NickStruct)) : 0;
for (nick = 0; !theError && nick < totalNicks; ++nick)
theError = ClearNicknameChangeBits (ab, nick, mask);
return (theError);
}
OSErr ClearNicknameChangeBits (short ab, short nick, long mask)
{
Handle notes;
Str255 name;
OSErr theError = noErr;
long value;
theError = noErr;
if (notes = GetNicknameData (ab, nick, false, true)) {
value = GetNicknameChangeBits (notes);
if (value != (value & ~mask))
{
value &= ~mask;
theError = SetNicknameChangeBit (notes, value, true);
if (!theError)
ReplaceNicknameNotes (ab, GetNicknameNamePStr (ab, nick, name), notes);
}
}
return (theError);
}
/**********************************************************************
* KillNickTOC - kill the toc for a file
**********************************************************************/
OSErr KillNickTOC(FSSpecPtr spec)
{
OSErr err = noErr;
/*
* if the resource fork is bad or we can't open it, just remove it
*/
err = FSpKillRFork(spec);
return (err);
}
/**********************************************************************
* ReadNickTOC - read the toc for a file
**********************************************************************/
OSErr ReadNickTOC(short which)
{
uLong fileModDate = AFSpGetMod(&This.spec);
uLong TOCModDate;
FSSpec lSpec = This.spec;
Boolean sane;
OSErr err = noErr;
Handle structR = nil,nameR = nil,r1 = nil;
long structSize;
short numberOfNicks = 0;
long currentStructPos = 0,currentNamePos = 0,currNickCount;
short refN=0;
long theSize = 0;
long eof;
NickStructHandle theData;
NickTOCStruct *pTOCInfo,*pTOCEnd;
NickStruct *pNick;
short oldResF = CurResFile();
short oldId;
theData = (*Aliases)[which].theData;
IsAlias(&This.spec,&lSpec);
This.collapsed = FindSTRNIndex(NickFileCollapseStrn,This.spec.name) > 0;
if (err=FSpRFSane(&lSpec,&sane)) return(-1);
if (!sane)
{
FSpKillRFork(&lSpec);
return (-1);
}
else if (-1!=(refN=FSpOpenResFile(&lSpec,fsRdPerm)))
{
Handle hOldTOC;
SetResLoad(False); // Don't load now so we can use temporary memory
if (hOldTOC = Get1Resource(OLD_NICK_TYPE,OLD_NICK_RESID1))
{
// Remove old-style TOC resources
RemoveResource(hOldTOC);
if (hOldTOC = Get1Resource(OLD_NICK_TYPE,OLD_NICK_RESID2))
RemoveResource(hOldTOC);
}
for (oldId=NICK_BASE_RESID;oldId<NICK_RESID;oldId++)
{
if (r1=Get1Resource(NICK_TOC_TYPE,oldId)) RemoveResource(r1);
if (r1=Get1Resource(NICK_NAMES_TYPE,oldId)) RemoveResource(r1);
}
r1 = Get1Resource(NICK_TOC_TYPE,NICK_RESID);
SetResLoad(True);
nameR = Get1Resource(NICK_NAMES_TYPE,NICK_RESID);
ZapHandle(This.hNames);
if (r1 && nameR)
{
DetachResource(nameR); // Need to keep the names in memory after res file closes
This.hNames = nameR; // Save handle to nicknames
structSize = GetResourceSizeOnDisk(r1);
if (err = ResError())
goto theExit;
else
{
structR = NuHTempOK(structSize);
if (!structR)
goto theExit;
else
{
ReadPartialResource(r1,0,LDRef(structR),structSize);
if (err = ResError())
goto theExit;
// See if file has been modified since TOC last modified
BlockMoveData(LDRef(structR) + currentStructPos,&TOCModDate,sizeof(TOCModDate));
if (TOCModDate != fileModDate) // Make sure the TOC is pretty much in synch
{
err = 1;
goto theExit;
}
currentStructPos += sizeof(TOCModDate);
BlockMoveData(*structR + currentStructPos,&numberOfNicks, sizeof(numberOfNicks));
currentStructPos += sizeof(numberOfNicks);
// Verify the size of the resource read in is actually the size of the data we need
theSize = numberOfNicks * sizeof(NickTOCStruct);
if ((numberOfNicks * sizeof(NickTOCStruct) +
sizeof(numberOfNicks) + sizeof(TOCModDate)) != structSize)
{
err = 1;
goto theExit;
}
// Verify that the file must be empty if the toc is
if (!numberOfNicks && FSpDFSize(&lSpec))
{
err = 1;
goto theExit;
}
// Load up the nick array
SetHandleBig((Handle)theData,numberOfNicks * sizeof(NickStruct));
if (MemError()) goto theExit;
WriteZero(*theData,numberOfNicks * sizeof(NickStruct));
eof = FSpDFSize(&lSpec);
pTOCInfo = (NickTOCStruct *)(*structR + currentStructPos);
pTOCEnd = (NickTOCStruct *)(*structR + structSize);
CycleBalls();
pNick = *theData;
// Fill in TOC data
for (currNickCount=0;
pTOCInfo < pTOCEnd && currNickCount < numberOfNicks;
currNickCount++,pTOCInfo++,pNick++)
{
pNick->hashName = pTOCInfo->hashName;
pNick->hashAddress = pTOCInfo->hashAddress;
pNick->group = pTOCInfo->flags & nfMultipleAddresses ? true : false;
if ((pNick->addressOffset = pTOCInfo->addressOffset) > eof) goto theExit; // Bad data
if ((pNick->notesOffset = pTOCInfo->notesOffset) > eof) goto theExit; // Bad data
pNick->nameTOCOffset = currentNamePos;
// Advance to next name
currentNamePos += *(*nameR + currentNamePos) + 1;
}
}
}
CheckForNicknameBogosity (which);
}
else
goto theExit;
}
if (numberOfNicks == 0)
theExit:
numberOfNicks = 0; // Error
if (refN) CloseResFile(refN);
if (nameR) UL(nameR);
if (err) ZapHandle(This.hNames);
ZapHandle(structR);
UseResFile (oldResF);
if (numberOfNicks == 0)
return (-1);
return(noErr);
}
/**********************************************************************
* WriteNickTOC - write the toc for a file
**********************************************************************/
OSErr WriteNickTOC(short which)
{
uLong fileModDate;
FSSpec spec = This.spec;
OSErr err = noErr;
short refN;
NickTOCStruct tempStruct;
short i,totalNicks = NNicknames,realCount = 0;
Handle structR = nil,nameR = nil,tempHandle = nil;
Accumulator structAcc,nameAcc;
NickStruct *pNickInfo;
NickStructHandle theData;
Boolean fSaved = false;
short oldResF = CurResFile();
theData = (*Aliases)[which].theData;
IsAlias(&spec,&spec);
KillNickTOC(&spec);
FSpCreateResFile(&spec,CREATOR,'TEXT',smSystemScript);
fileModDate = AFSpGetMod(&spec);
structR = NuHTempOK(0);
nameR = NuHTempOK(0);
Zero(structAcc);
Zero(nameAcc);
structAcc.data = (Handle)structR;
nameAcc.data = (Handle)nameR;
if (!structR || !nameR) //SD and drop whichever one might not be nil
{
UseResFile (oldResF);
return (-1);
}
realCount = 0;
for (i=0,pNickInfo=(*theData);i<totalNicks;i++,pNickInfo++)
{
if (!pNickInfo->deleted)
realCount++;
}
err = AccuAddPtr(&structAcc,(void*)&fileModDate,sizeof(fileModDate));
if (err)
goto exit;
err = AccuAddPtr(&structAcc,(void*)&realCount,sizeof(realCount));
if (err)
goto exit;
// Build nick TOC struct
for (i=0;i<totalNicks;i++)
{
pNickInfo=&(*theData)[i];
if (!pNickInfo->deleted)
{
Str31 sName;
// Add nick info
tempStruct.hashName = pNickInfo->hashName;
tempStruct.hashAddress = pNickInfo->hashAddress;
tempStruct.addressOffset = pNickInfo->addressOffset;
if (pNickInfo->group)
tempStruct.flags |= nfMultipleAddresses;
else
tempStruct.flags &= ~nfMultipleAddresses;
tempStruct.notesOffset = pNickInfo->notesOffset;
if (err=AccuAddPtr(&structAcc,(void*)&tempStruct,sizeof(tempStruct))) goto exit;
// Add nick name
GetNicknameNamePStr(which,i,sName);
if (err=AccuAddPtr(&nameAcc,sName,*sName+1)) goto exit;
}
}
AccuTrim((void*)&nameAcc);
AccuTrim((void*)&structAcc);
if (-1!=(refN=FSpOpenResFile(&spec,fsRdWrPerm)))
{
AddResource(structR,NICK_TOC_TYPE,NICK_RESID,"");
if (!ResError())
AddResource(nameR,NICK_NAMES_TYPE,NICK_RESID,"");
err = ResError();
if (!err)
{
UpdateResFile(refN);
err = ResError();
fSaved = true;
}
// ALB DetachResource(structR);
// ALB DetachResource(nameR);
CloseResFile(refN);
}
else err = ResError();
if (!err)
err = AFSpSetMod(&spec,fileModDate);
exit:
if (!fSaved)
{
// Failed to save, get rid of the temporary handles
ZapHandle(structR);
ZapHandle(nameR);
}
UseResFile (oldResF);
return (err);
}
/************************************************************************
* NeatenLine - strip the newline from a line; if it was escaped, strip
* the backslash, and return True
************************************************************************/
Boolean NeatenLine(UPtr line, long *len)
{
if (line[*len-1]=='\015') line[--*len] = 0;
if (line[*len-1]=='\\')
{
line[--*len] = 0;
return(True);
}
return(False);
}
/************************************************************************
* AliasExpansion - return pointer to expansion of an alias
************************************************************************/
UPtr AliasExpansion(UPtr data, long offset)
{
UPtr ptr = data+offset;
ptr += *ptr+3;
return(ptr);
}
/************************************************************************
* AddNickToTOC - add nickname to TOC
*
* Note!! This routine makes a copy of the data handle (notes or addresses)
* which is assigned to the TOC. Therefore, there is no need to hang onto
* the handle passed in hData.
************************************************************************/
static short AddNickToTOC(short which,UPtr name,Handle hData,Boolean fFromAddress)
{
long currNickCount;
NickStructHandle aliases = This.theData;
OSErr err = noErr;
NickStruct nickInfo;
Str255 beautifulName;
long nameOffset;
Boolean group;
// Nickname doesn't exist...create new one and clear it out
// If it doesn't exist, add it
HUnlock((Handle)aliases);
currNickCount = NNicknames; // Get nickname count
if (currNickCount > 0)
{
SetHandleBig_((Handle)aliases,(currNickCount + 1)*sizeof(NickStruct)); // Add a nickname
if (err = MemError())
return(WarnUser(ALIAS_NEW_NICK_ERR,err));
}
else
{
ZapHandle(This.theData); // (jp) 7/31/99 Operate on This.theData instead of aliases
aliases = NuHTempOK(sizeof(NickStruct));
if (!aliases)
return(WarnUser(ALIAS_NEW_NICK_ERR,MemError()));
else
This.theData = aliases; // (jp) 7/31/99 The newly created data handle
}
nameOffset = GetHandleSize(This.hNames);
err = PtrPlusHand_(name,This.hNames,*name+1);
if (err)
return(WarnUser(ALIAS_NEW_NICK_ERR,err));
// Fill in the basic data
WriteZero(&nickInfo,sizeof(nickInfo));
nickInfo.hashName = NickHash(name);
nickInfo.hashAddress = 0;
nickInfo.addressOffset = (-1L);
nickInfo.notesOffset = (-1L);
nickInfo.addressesDirty = true;
nickInfo.notesDirty = true;
nickInfo.pornography = false;
nickInfo.group = false;
nickInfo.nameTOCOffset = nameOffset;
if (!This.containsBogusNicks) {
PCopy (beautifulName, name);
BeautifyFrom (beautifulName);
if (nickInfo.hashName != NickHash (beautifulName))
This.containsBogusNicks = true;
}
if (hData) // Put the address information into the nickname
{
Handle hNewData;
hNewData = NuHTempOK(0);
if (!hNewData)
return(WarnUser(ALIAS_NEW_NICK_ERR,MemError()));
err = PtrPlusHand(*hData,hNewData,GetHandleSize_(hData));
if (err)
return(WarnUser(ALIAS_NEW_NICK_ERR,err));
if (fFromAddress) {
// Data is addresses
nickInfo.theAddresses = hNewData;
nickInfo.hashAddress = NickHashRawAddresses (hNewData, &group);
nickInfo.group = group;
}
else
// Data is notes
nickInfo.theNotes = hNewData;
}
// Put nick info in nick array
(*(aliases))[currNickCount] = nickInfo;
// This data CANNOT be purged because it hasn't been written to disk
HNoPurge((Handle)aliases);
UL(aliases);
return(0);
}
/************************************************************************
* AddNickToTOCfromName - add nickname to TOC
************************************************************************/
short AddNickToTOCfromName(short which,UPtr name,Handle addresses)
{
return AddNickToTOC(which,name,addresses,true);
}
/************************************************************************
* AddNickToTOCfromNotes - add nickname to TOC from notes
************************************************************************/
short AddNickToTOCfromNotes(short which,UPtr name,Handle notes)
{
// return AddNickToTOC(which,name,notes,true); // (jp) This is wrong!! We're setting notes, not addresses
return AddNickToTOC(which,name,notes,false);
}
/************************************************************************
* RemoveNamedNickname - remove the named nickname
* The nickname CANNOT be completely removed from memory in case the user
* discards the changes from the window.
************************************************************************/
void RemoveNamedNickname(short which,UPtr name)
{
NickStructHandle aliases = This.theData;
long hashName = NickHash(name);
long index = NickMatchFound(aliases,hashName,name,which); // returns index of match
if (index >= 0 ) // the nickname exists
{
(*((*Aliases)[which].theData))[index].addressesDirty = true;
(*((*Aliases)[which].theData))[index].notesDirty = true;
(*((*Aliases)[which].theData))[index].deleted = true;
}
}
/************************************************************************
* NickUniq - uniquify a nicknames list
************************************************************************/
OSErr NickUniq(TextAddrHandle addresses,PStr sep,Boolean wantErrors)
{
TextAddrHandle cmntOrig=nil, unOrig=nil, cmntNew=nil, unNew=nil;
short err = noErr;
UPtr spot;
UPtr newSpot;
UPtr end;
long size;
Boolean group, groupWas=False;
err = SuckAddresses(&cmntOrig,addresses,True,wantErrors,False,nil);
cmntNew = NuHTempOK(0);
unNew = NuHTempOK(0);
if (!err && cmntNew && unNew && cmntOrig)
{
for (spot = LDRef(cmntOrig);*spot && !err;spot+=*spot+2)
{
err = SuckPtrAddresses(&unOrig,spot+1,*spot,False,wantErrors,False,nil);
if (!err && unOrig && **unOrig)
{
LDRef(unOrig);
group = (*unOrig)[**unOrig]==':';
for (newSpot=LDRef(unNew),end=newSpot+GetHandleSize_(unNew);
newSpot<end && *newSpot;
newSpot+=*newSpot+2)
if (StringSame(newSpot,*unOrig)) break;
UL(unNew);
if (newSpot>=end || !*newSpot)
{
/* did NOT find it */
if (!groupWas && GetHandleSize_(cmntNew)) PtrPlusHand_(sep+1,cmntNew,*sep);
err = PtrPlusHand_(spot+1,cmntNew,*spot);
if (err) break;
err = PtrPlusHand_(*unOrig,unNew,**unOrig+2);
if (err) break;
}
ZapHandle(unOrig);
groupWas = group;
}
}
}
if (!err)
{
size = GetHandleSize_(cmntNew);
HUnlock(addresses);
SetHandleBig_(addresses,size);
if (!(err=MemError()))
BlockMoveData(*cmntNew,*addresses,size);
}
if (err<0 && wantErrors) WarnUser(MEM_ERR,err);
ZapHandle(cmntOrig);
ZapHandle(unOrig);
ZapHandle(cmntNew);
ZapHandle(unNew);
return(err);
}
/************************************************************************
* ReplaceNicknameInfo - replace nickname address or notes
*
* Note!! This routine makes a copy of the data handle (notes or addresses)
* which is assigned to the TOC. Therefore, there is no need to hang onto
* the handle passed in hData.
************************************************************************/
static short ReplaceNicknameInfo(short which,UPtr theName,TextAddrHandle text,Boolean fAddresses)
{
long hashName = NickHash(theName);
long index;
NickStructHandle aliases = This.theData;
OSErr err = noErr;
NickStruct tempNick;
long textSize;
Handle hTemp;
Boolean group;
if (theName && *theName && aliases)
{
index = NickMatchFound(aliases,hashName,theName,which);
if (index<0)
// Not found, add nickname
if (fAddresses)
return (AddNickToTOCfromName(which,theName,text));
else
return (AddNickToTOCfromNotes(which,theName,text));
// Get nick info
tempNick = (*aliases)[index];
if (text)
{
// Make a copy of addresses
textSize = GetHandleSize(text);
if (hTemp = NuHTempOK(textSize))
BlockMoveData(*text,*hTemp,textSize);
else
// Memory error
return (WarnUser(ALIAS_REPLACE_NICK_ERR,MemError()));
}
else
hTemp = nil;
if (fAddresses)
{
// Replace the addresses
if (tempNick.theAddresses)
ZapHandle(tempNick.theAddresses); // Dispose of former addresses
// Don't let the user put crap in here; bug 4519
if (hTemp) TransLitRes(*hTemp,GetHandleSize(hTemp),ktFlatten);
tempNick.theAddresses = hTemp;
tempNick.hashAddress = NickHashRawAddresses (tempNick.theAddresses, &group);
tempNick.group = group;
tempNick.theNotes = GetNicknameData(which,index,false,true); // Make sure notes are loaded
}
else
{
// Replace notes
if (tempNick.theNotes)
ZapHandle(tempNick.theNotes); // Dispose of former notes
tempNick.theNotes = hTemp;
tempNick.theAddresses = GetNicknameData(which,index,true,true); // Make sure addresses are loaded
tempNick.hashAddress = NickHashRawAddresses (tempNick.theAddresses, &group);
tempNick.group = group;
}
if (tempNick.theAddresses)
HNoPurge(tempNick.theAddresses);
if (tempNick.theNotes)
HNoPurge(tempNick.theNotes);
UL(text);
tempNick.addressesDirty = true;
tempNick.notesDirty = true;
// Save temp nick info
(*aliases)[index] = tempNick;
SetAliasDirty(which);
}
return noErr;
}
/************************************************************************
* ReplaceNicknameAddresses - replace one nickname definition with another
************************************************************************/
short ReplaceNicknameAddresses(short which,UPtr theName,TextAddrHandle text)
{
return ReplaceNicknameInfo(which,theName,text,true);
}
/************************************************************************
* ChangeNameOfNick - change the nickname
************************************************************************/
short ChangeNameOfNick(short which,UPtr oldName,UPtr newName)
{
NickStructHandle aliases = This.theData;
long hashName = NickHash(oldName);
long index=-1;
if (oldName && *oldName) index = NickMatchFound(aliases,hashName,oldName,which);
if (index<0) return(-1);
SetNickname (which, index, newName);
return(0);
}
void SetNickname (short ab, short nick, UPtr name)
{
NickStructHandle aliases;
NickStruct *pNick;
Str32 oldName;
long oldOffset,
adjustment,
nickCount,
i;
GetNicknameNamePStr (ab, nick, oldName);
if (aliases = (*Aliases)[ab].theData) {
oldOffset = (*aliases)[nick].nameTOCOffset;
// Replace the name
Munger ((*Aliases)[ab].hNames, oldOffset, nil, *oldName + 1, name, *name + 1);
// New hash value
(*aliases)[nick].hashName = NickHash (name);
// Calculate new name offsets if the new name is a different length
// Only need to adjust those that are past the one we changed
if (adjustment = *name - *oldName) {
nickCount = GetHandleSize_(aliases) / sizeof (NickStruct);
for (i = 0, pNick = *aliases; i < nickCount; i++, pNick++)
if (pNick->nameTOCOffset > oldOffset)
pNick->nameTOCOffset += adjustment;
}
// Mark the nickname as dirty so that the alias and note entries in the nickname file get rewritten
// (9/11/00) We don't really want to do this because the addresses and note might not be in memory, in which
// case we'd falsely save them as empty.
// (*aliases)[nick].addressesDirty = true;
// (*aliases)[nick].notesDirty = true;
}
}
/**********************************************************************
* IsAnyNickname - is the name a nickname?
**********************************************************************/
Boolean IsAnyNickname(PStr name)
{
short which;
for (which=NAliases;which--;) if (IsNickname(name,which)) return(True);
return(False);
}
/************************************************************************
* ReplaceNicknameNotes - replace nickname notes with another
************************************************************************/
short ReplaceNicknameNotes(short which,UPtr theName,TextAddrHandle text)
{
return ReplaceNicknameInfo(which,theName,text,false);
}
/*
* Get the addresses or notes of a given nickname; if not in memory, it will read from disk
*/
Handle GetNicknameData(short which,short index,Boolean wantAddresses,Boolean readFromDisk)
{
Handle tempHandle;
NickStructHandle aliases = This.theData;
FSSpec spec;
Boolean finished = false;
int err;
Str255 line;
short type;
Boolean exLine=False;
long len;
Handle dataHandle;
LineIOD lid;
long theOffset,count;
Str31 theCmd;
Byte lookingFor;
Boolean group;
spec = This.spec;
if (!aliases || index < 0 || which < 0 || index >= NNicknames || (*aliases)[index].deleted)
return (nil);
if (wantAddresses)
tempHandle = (*aliases)[index].theAddresses;
else
tempHandle = (*aliases)[index].theNotes;
if ((*aliases)[index].addressesDirty)
{
if (tempHandle) HNoPurge(tempHandle);
return (tempHandle);
}
if (tempHandle !=nil && *tempHandle !=nil)
return (tempHandle);
if (tempHandle != nil)
ZapHandle(tempHandle);
if (!readFromDisk) return(nil); //SD - Scott, is this ok?
if (wantAddresses)
{
theOffset = (*aliases)[index].addressOffset;
GetRString(theCmd,ALIAS_CMD);
}
else
{
theOffset = (*aliases)[index].notesOffset;
GetRString(theCmd,NOTE_CMD);
}
if ( theOffset >= 0)
{
if (err=FSpOpenLine(&spec,fsRdPerm,&lid))
{
if (err !=fnfErr)
FileSystemError(OPEN_ALIAS,spec.name,err);
return (nil);
}
dataHandle = NuHTempOK(0L);
if (!dataHandle)
{
WarnUser(ALIAS_GET_NICK_DATA_ERR,MemError());
return (nil);
}
/*
* Offset is from the beginning of the line; add in length of the command and length of name
* and two spaces
*/
theOffset += *theCmd + 1;
SeekLine(theOffset - 1,&lid);
type = GetLine(line,sizeof(line),&len,&lid);
if (type==LINE_START)
do
{
// process current line
len = strlen(line);
exLine = NeatenLine(line,&len);
if (exLine && !issep(*line)) // If line was escaped and the first character isn't a space, add one
{
if (err=PtrPlusHand_(" ",dataHandle,1)) break;
}
if (err=PtrPlusHand_(line,dataHandle,len)) break;
// grab the next line; may or may not be ours
type = GetLine(line,sizeof(line),&len,&lid);
if (exLine && type==LINE_START) type = LINE_MIDDLE; // extended line means new line is really part of this line
}
while (type==LINE_MIDDLE);
#ifdef NEVER
while (!finished && (type=GetLine(line,sizeof(line),&len,&lid))>0)
{
if (type==LINE_MIDDLE && len < sizeof(line)-1) // We're not really in the middle of a line...we're at the end
// so we need to handle the completion of the nickname
type = 1;
if (exLine || (type==LINE_MIDDLE)) // If the line was escaped or we're in the middle of a line
{
len = strlen(line);
exLine = NeatenLine(line,&len);
if (exLine && !issep(*line)) // If line was escaped and the first character isn't a space, add one
{
if (err=PtrPlusHand_(" ",dataHandle,1)) break;
}
if (err=PtrPlusHand_(line,dataHandle,len)) break;
}
else
{
if (*line)
if (err=PtrPlusHand_(line,dataHandle,len)) break;
if (len < (sizeof(line) - 1 ) && len > 0) // Got less than a full line of text; therefore we are done
{
finished = true;
break;
}
}
}
#endif
CloseLine(&lid);
if (err)
{
WarnUser(ALIAS_GET_NICK_DATA_ERR,err);
ZapHandle(dataHandle);
return (nil);
}
HLock(dataHandle);
len = GetHandleSize_(dataHandle);
for (count=0;count<len;count++) if ((*dataHandle)[count]!=' ') break;
if ((*dataHandle)[count]=='"') lookingFor = '"';
else lookingFor = ' ';
for (count++;count<len;count++) // Scan for space to find end of alias name
{
if ((*dataHandle)[count] == lookingFor)
break;
}
if (lookingFor=='"') count++;
BlockMoveData(*dataHandle + count + 1,*dataHandle,len-count-1);
SetHandleBig_(dataHandle,len-count-1);
len = len - count - 1;
while (len && (*dataHandle)[len-1]=='\015')
SetHandleBig_(dataHandle,--len);
if (wantAddresses) {
(*aliases)[index].theAddresses = dataHandle;
(*aliases)[index].hashAddress = NickHashRawAddresses (dataHandle, &group);
(*aliases)[index].group = group;
}
else
(*aliases)[index].theNotes = dataHandle;
UL(dataHandle);
return (dataHandle);
}
else return (nil);
}
/*
* Get the name of a given nickname
*/
PStr GetNicknameNamePStr(short which,short index,PStr theName)
{
NickStructHandle aliases = This.theData;
Handle hNames;
*theName = 0; // Initialize to null string
if (!aliases || index < 0 || which < 0 || index >= NNicknames || (*aliases)[index].deleted)
return (theName);
hNames = This.hNames;
if (!hNames || !*hNames)
return theName;
PCopy(theName,*(hNames)+(*aliases)[index].nameTOCOffset);
return (theName);
}
/************************************************************************
* MakeCompNick - make a nickname out of a comp window
************************************************************************/
#ifdef VCARD
void MakeCompNick(MyWindowPtr win, FSSpec *vcardSpec)
#else
void MakeCompNick(MyWindowPtr win)
#endif
{
TextAddrHandle biglist;
OSErr err = noErr;
biglist = NuHTempOK(0);
if (!biglist)
{
WarnUser(ALIAS_NEW_NICK_ERR,MemError());
return;
}
if (err=GatherCompAddresses(win,biglist))
{
if (err!=paramErr) WarnUser(MEM_ERR,err);
return;
}
/*
* and make the nickname...
*/
if (GetHandleSize (biglist) && **biglist)
#ifdef VCARD
NewNick(biglist,vcardSpec,0);
#else
NewNick(biglist,0);
#endif
else NoteUser(NO_ADDRESSES,0);
ZapHandle(biglist);
}
/**********************************************************************
* MakeNickFromSelection - make a nickname from the selected addresses
**********************************************************************/
void MakeNickFromSelection(MyWindowPtr win)
{
Handle list = nil;
Handle text;
Handle textCopy;
long selStart, selEnd;
if (PeteGetTextAndSelection(win->pte,&text,&selStart,&selEnd)
|| selStart>=selEnd)
NoteUser(NO_ADDRESSES,0);
else
{
// ALB 9/5/96, replaced this with SuckPtrAddresses for bug 602
// selEnd = MIN(selEnd,selStart+250);
// list = ZeroHandle(NuHTempBetter(selEnd-selStart+3));
// if (!list)
// {
// WarnUser(ALLO_ALIAS,MemError());
// return;
// }
// **list = len = selEnd-selStart;
// BMD(*text+selStart,*list+1,len);
// (jp) 12/12/00 Translate carriage returns to commas so we can discern individual addresses
textCopy = text;
if (!HandToHand (&textCopy)) {
Tr (textCopy, "\015", ",");
HLock(textCopy);
if (!SuckPtrAddresses(&list,*textCopy+selStart,selEnd-selStart,True,True,False,nil))
{
if (!list || !*list || !**list)
WarnUser(NO_ADDRESSES,0);
else
#ifdef VCARD
NewNick(list,nil,0);
#else
NewNick(list,0);
#endif
ZapHandle(list);
}
HUnlock(textCopy);
}
ZapHandle (textCopy);
}
}
/************************************************************************
* GatherCompAddresses - gather the addresses from a window
************************************************************************/
short GatherCompAddresses(MyWindowPtr win,Handle biglist)
{
Handle littlelist;
MessHandle messH;
static short heads[] = {TO_HEAD,CC_HEAD,BCC_HEAD};
short err=0;
short h;
Uhandle text=nil;
HeadSpec hs;
/*
* I vaant to suck your addresses...
*/
messH = Win2MessH(win);
for (h=0;!err && h<sizeof(heads)/sizeof(short);h++)
{
if (CompHeadFind(messH,heads[h],&hs))
{
if (err=CompHeadGetText(TheBody,&hs,&text)) break;
err=SuckAddresses(&littlelist,text,True,True,False,nil);
if (littlelist)
{
if (**littlelist)
{
long size=GetHandleSize_(biglist);
if (size) SetHandleBig_(biglist,size-1); /* strip final terminator */
if (err=HandAndHand(littlelist,biglist)) break;
}
ZapHandle(littlelist);
ZapHandle(text);
}
}
}
return(err);
}
/************************************************************************
* MakeMessNick - make a nickname out of a message window
************************************************************************/
void MakeMessNick(MyWindowPtr win,short modifiers)
{
#ifdef VCARD
MessHandle messH = (MessHandle)GetMyWindowPrivateData(win);
TOCHandle tocH = (*messH)->tocH;
int sumNum = (*messH)->sumNum;
FSSpec attSpec;
Handle text;
long offset;
Boolean foundVCard;
#endif
TOCHandle out=GetOutTOC();
Boolean all, quote, self;
#ifdef VCARD
// Is a vCard attached to this message?
foundVCard = false;
if (IsVCardAvailable ()) {
CacheMessage (tocH, sumNum);
if (!(text = (*tocH)->sums[sumNum].cache))
return;
HNoPurge (text);
offset = (*tocH)->sums[sumNum].bodyOffset-1;
while (!foundVCard && (0<=(offset = FindAnAttachment(text,offset+1,&attSpec,true,nil,nil,nil)))) {
foundVCard = IsVCardFile (&attSpec);
}
}
#endif
ReplyDefaults(modifiers,&all,&self,&quote);
if (out)
{
Boolean wasDirty = (*out)->win->isDirty;
win = DoReplyMessage(win,all,self,False,False,0,False,False,False);
if (!win) return;
#ifdef VCARD
MakeCompNick(win, foundVCard ? &attSpec : nil);
#else
MakeCompNick(win);
#endif
CloseMyWindow(GetMyWindowWindowPtr(win));
(*out)->win->isDirty = wasDirty;
}
}
/************************************************************************
* MakeMboxNick - make a nickname out of the selected messages in an mbox
************************************************************************/
void MakeMboxNick(MyWindowPtr win,short modifiers)
{
TextAddrHandle addresses=nil;
TOCHandle tocH = (TOCHandle)GetMyWindowPrivateData(win);
OSErr err = GatherBoxAddresses(tocH,modifiers,-1,-1,&addresses,false);
if (!err)
{
if (**addresses)
#ifdef VCARD
NewNick(addresses,nil,0);
#else
NewNick(addresses,0);
#endif
else WarnUser(NO_ADDRESSES,0);
}
ZapHandle(addresses);
}
/************************************************************************
* GatherBoxAddresses - gather addresses from the selected messages in an mbox
************************************************************************/
OSErr GatherBoxAddresses(TOCHandle tocH,short modifiers,short from, short to, UHandle *addresses, Boolean caching)
{
MyWindowPtr messWin,compWin;
short sumNum;
short err = 0;
Boolean all, quote, self;
Boolean selected = from==-1;
if (selected) {from=0;to=(*tocH)->count-1;}
ReplyDefaults(modifiers,&all,&self,&quote);
if (!(*addresses=NuHTempOK(0))) return(MemError());
for (sumNum=from;!err && sumNum<=to;sumNum++)
{
MiniEvents(); if (CommandPeriod) break;
if (!selected || (*tocH)->sums[sumNum].selected)
{
#ifdef IMAP
// if this is an IMAP message, make sure it's been fully fetched.
if (tocH && (*tocH)->imapTOC && !EnsureMsgDownloaded(tocH,sumNum,false)) return (err = 1);
#endif
if (messWin = GetAMessage(tocH,sumNum,nil,nil,False))
{
compWin = MessFlagIsSet(Win2MessH(messWin),FLAG_OUT) ? messWin :
DoReplyMessage(messWin,all,self,False,False,0,False,False,caching);
if (compWin)
{
err=GatherCompAddresses(compWin,*addresses);
if (compWin != messWin) CloseMyWindow(GetMyWindowWindowPtr(compWin));
}
if (!IsWindowVisible(GetMyWindowWindowPtr(messWin))) CloseMyWindow(GetMyWindowWindowPtr(messWin));
}
else err = 1;
}
}
if (CommandPeriod) err = userCancelled;
if (err) ZapHandle(*addresses);
return(err);
}
/************************************************************************
* MakeCboxNick - make a nickname out of the selected messages in Out
************************************************************************/
void MakeCboxNick(MyWindowPtr win)
{
Handle addresses=NuHTempOK(0);
TOCHandle tocH = (TOCHandle)GetMyWindowPrivateData(win);
MyWindowPtr compWin;
short sumNum;
short err = 0;
if (!addresses) return;
for (sumNum=0;!err && sumNum<(*tocH)->count;sumNum++)
{
MiniEvents(); if (CommandPeriod) break;
if ((*tocH)->sums[sumNum].selected)
if (compWin=GetAMessage(tocH,sumNum,nil,nil,False))
{
WindowPtr compWinWP = GetMyWindowWindowPtr (compWin);
err=GatherCompAddresses(compWin,addresses);
if (!IsWindowVisible(compWinWP)) CloseMyWindow(compWinWP);
}
else err = 1;
}
if (!err && !CommandPeriod)
{
if (**addresses)
#ifdef VCARD
NewNick(addresses,nil,0);
#else
NewNick(addresses,0);
#endif
else WarnUser(NO_ADDRESSES,0);
}
ZapHandle(addresses);
}
/************************************************************************
* FlattenListWith - make an address list one to a line
************************************************************************/
void FlattenListWith(Handle h,Byte c)
{
UPtr from, to;
Boolean colon;
from = to = *h;
while (*from)
{
if (from[1]==';' && to!=*h && to[-1]!=':') to--; //backup over separator
while (*++from) *to++ = *from; /* skip length byte, copy string */
colon = from[-1]==':';
from++; /* skip terminator */
if (!colon) *to++ = c; /* and add a separator */
}
if (to > *h) to--;
SetHandleBig_(h,to-*h);
}
/************************************************************************
* CommaList - make an address list have commas
************************************************************************/
void CommaList(Handle h)
{
UPtr from, to;
Boolean colon;
from = to = *h;
while (*from)
{
if (from[1]==';' && to!=*h && to[-1]!=':') to-=2; //backup over separator
while (*++from) *to++ = *from; /* skip length byte, copy string */
colon = from[-1]==':';
from++; /* skip terminator */
if (!colon)
{
*to++ = ','; /* and add a separator */
*to++ = ' '; /* and add a separator */
}
}
if (to > *h) to -= 2;
SetHandleBig_(h,to-*h);
}
/************************************************************************
* SaveAliases - save the edited aliases (if necessary)
* returns False if the operation failed
************************************************************************/
Boolean SaveAliases(Boolean saveChangeBits)
{
short ab;
Boolean fResult;
// Save each address book
ab = NAliases;
fResult = true;
while (ab-- && fResult)
fResult = SaveIndNickFile (ab, saveChangeBits);
// If the address books were successfully saved, tell the Adddres Book window
if (fResult)
ABClean ();
return (fResult);
}
/************************************************************************
* SetAliasDirty - set the dirty bit for an address book, and kill the
* address hashes
************************************************************************/
void SetAliasDirty(short which)
{
This.dirty = true;
ZapAliasHash(which);
}
/************************************************************************
* SaveIndNickFile - save an individual alias file
************************************************************************/
Boolean SaveIndNickFile(short which, Boolean saveChangeBits)
{
Str31 aliasCmd;
Str255 scratch;
int err;
long bytes,offset;
short refN=0;
long i, count;
FSSpec spec,tmpSpec;
Boolean junk;
Handle tempHandle;
short numInMemory;
/*
* do we need to save it?
*/
if (!This.dirty) return(True);
SaveDirtyPictures (which);
/*
* notify the nickname completion stuff that the world is changing
*/
InvalCachedNicknameData();
/*
* make a backup
*/
if (PrefIsSet(PREF_NICK_BACKUP)) NickBackup(&(This.spec));
/*
* If fast save allowed, do one. If it returns false, must do complete save
*/
if (!PrefIsSet(PREF_NO_NICK_FAST_SAVE) && SaveFileFast(which, saveChangeBits))
return(true);
/*
* find the file
*/
spec = This.spec;
if (err=FSpMyResolve(&spec,&junk))
{
FileSystemError(SAVE_ALIAS,spec.name,err);
return(False);
}
/*
* regnerate the aliases, if need be
*/
if (!This.theData || !*This.theData) {WarnUser(SAVE_ALIAS,0);return(False);}
count = NNicknames; // Get nickname count
numInMemory = 0;
// This is going to give us a ROUGH count on the number of nicknames in memory.
// It is possible to have the address info in memory, but not the notes...we'll just count that as
// being in memory.
for (i=0;i<count;i++)
{
// If we actually have some address info and it is in memory, then increase the count
if ((*(This.theData))[i].addressOffset >=0 && (*(This.theData))[i].theAddresses != nil)
numInMemory++;
// If we actually have some notes info and it is in memory, then increase the count
else if ((*(This.theData))[i].notesOffset >=0 && (*(This.theData))[i].theNotes != nil)
numInMemory++;
}
// If we have more than 75% of the nicknames in memory, don't reread them.
if (numInMemory < (count * 3/4))
{
err = ReadNicknames(which);
if (err) return (false);
}
/*
* make && open a temp file
*/
//tmpSpec = spec;
//PCat(tmpSpec.name,GetRString(scratch,TEMP_SUFFIX));
if (err=NewTempSpec(spec.vRefNum,spec.parID,nil,&tmpSpec))
goto done;
if (err=FSpCreate(&tmpSpec,CREATOR,MAILBOX_TYPE,smSystemScript))
{FileSystemError(SAVE_ALIAS,tmpSpec.name,err); goto done;}
if (err=FSpOpenDF(&tmpSpec,fsRdWrPerm,&refN))
{FileSystemError(OPEN_ALIAS,tmpSpec.name,err); goto done;}
count = NNicknames; // Get nickname count
GetRString(aliasCmd,ALIAS_CMD);
PCatC(aliasCmd,' ');
HLock((Handle)This.theData);
for (i=0;!err && i<count;i++)
{
CycleBalls();
if ((*(This.theData))[i].addressesDirty) // Nickname has been modified...use in memory copy
tempHandle = GetNicknameData(which,i,true,false);
else // Nickname hasn't been modified, use on disk copy if necessary
tempHandle = GetNicknameData(which,i,true,true);
HLock(tempHandle);
bytes = (tempHandle && *tempHandle && **tempHandle) ? GetHandleSize_(tempHandle) : 0;
// PCopy(scratch,*((*(This.theData))[i].theName));
GetNicknameNamePStr(which,i,scratch);
if (bytes >0 && !(*(This.theData))[i].deleted)
{
GetFPos(refN,&offset);
(*(This.theData))[i].addressOffset = offset;
}
else {
(*(This.theData))[i].addressOffset = -1;
(*(This.theData))[i].group = false;
}
if ((!(*(This.theData))[i].deleted) && bytes > 0 && !(err = FSWriteP(refN,aliasCmd)))
{
if (PIndex(scratch,' '))
{
PInsert(scratch,sizeof(scratch),"\p\"",scratch+1);
PCatC(scratch,'"');
}
PCatC(scratch,' ');
if (!(err=FSWriteP(refN,scratch)))
{
if (!(err = AWrite(refN,&bytes,*tempHandle)))
{
bytes = 1;
err = AWrite(refN,&bytes,"\015");
}
}
HPurge(tempHandle);
}
HUnlock(tempHandle);
}
GetRString(aliasCmd,NOTE_CMD);
PCatC(aliasCmd,' ');
for (i=0;!err && i<count;i++)
{
if ((*(This.theData))[i].addressesDirty) { // Nickname has been modified...use in memory copy
tempHandle = GetNicknameData(which,i,false,false);
// Make sure the 'modified' change bit is set
if (saveChangeBits && tempHandle && PrefIsSet (PREF_CHANGE_BITS_FOR_CONDUIT))
SetNicknameChangeBit (tempHandle, changeBitModified, false);
}
else
tempHandle = GetNicknameData(which,i,false,true);
//if (!(*(This.theData))[i].deleted)
(*(This.theData))[i].addressesDirty = false;
(*(This.theData))[i].notesDirty = false;
HLock(tempHandle);
bytes = (tempHandle && *tempHandle && **tempHandle) ? GetHandleSize_(tempHandle) : 0;
// PCopy(scratch,*((*(This.theData))[i].theName));
GetNicknameNamePStr(which,i,scratch);
if (bytes >0 && !(*(This.theData))[i].deleted)
{
GetFPos(refN,&offset);
(*(This.theData))[i].notesOffset = offset;
}
else
(*(This.theData))[i].notesOffset = -1;
if ((!(*(This.theData))[i].deleted) && bytes >0 && !(err = FSWriteP(refN,aliasCmd)))
{
if (PIndex(scratch,' '))
{
PInsert(scratch,sizeof(scratch),"\p\"",scratch+1);
PCatC(scratch,'"');
}
PCatC(scratch,' ');
if (!(err=FSWriteP(refN,scratch)))
{
if (!(err = AWrite(refN,&bytes,*tempHandle)))
{
bytes = 1;
err = AWrite(refN,&bytes,"\015");
}
}
HPurge(tempHandle);
}
HUnlock(tempHandle);
if (err) {FileSystemError(SAVE_ALIAS,tmpSpec.name,err); goto done;}
}
UL(This.theData);
GetFPos(refN,&bytes);
SetEOF(refN,bytes);
MyFSClose(refN); refN = 0;
/* do the deed */
if (!err) err = ExchangeAndDel(&tmpSpec,&spec);
if (!err) This.dirty = False;
WriteNickTOC(which);
done:
if (This.theData) UL(This.theData);
if (refN) MyFSClose(refN);
if (err) FSpDelete(&tmpSpec);
return(err==noErr);
}
/************************************************************************
* SaveFileFast - incremental save of the nickname file
* Currently assumes that all notes commands follow all alias commands.
* Fast save must not be done if this isn't the case
************************************************************************/
Boolean SaveFileFast (short which, Boolean saveChangeBits)
{
NickOffSetSortType **addressOffsetHandle;
NickOffSetSortType **notesOffsetHandle;
NickOffSetSortType dummyValue;
short numOfNicks = NNicknames;
long theSize;
short count,tempCount;
FSSpec spec,tmpSpec;
OSErr err = fnfErr;
long bytes;
short tempRefN=0,nickRefN = 0;
Str31 aliasCmd;
long cleanStartOffset,cleanStopOffset,cleanStartIndex,cleanStopIndex;
Boolean junk;
Str255 scratch;
Handle tempHandle = nil;
Boolean dirty;
Boolean deleted;
long offset;
short theIndex,i;
long firstNoteStart;
long bytesToShift;
char theChar;
/*
* first make sure the file is ordered properly
*/
if (!NickFileOkForFastSave(which)) return(false);
addressOffsetHandle=NuHTempOK(sizeof(NickOffSetSortType) * (numOfNicks + 1));
notesOffsetHandle=NuHTempOK(sizeof(NickOffSetSortType) * (numOfNicks + 1));
if (addressOffsetHandle == nil || notesOffsetHandle == nil)
{
err = fnfErr;
goto done;
}
TotalNumOfNicks = numOfNicks;
HLock((Handle)addressOffsetHandle);
HLock((Handle)notesOffsetHandle);
for (count = 0; count < numOfNicks; count++)
{
(*addressOffsetHandle)[count].offset = (*(This.theData))[count].addressOffset;
(*addressOffsetHandle)[count].nickIndex = count;
(*notesOffsetHandle)[count].offset = (*(This.theData))[count].notesOffset;
(*notesOffsetHandle)[count].nickIndex = count;
}
dummyValue.offset = -1000;
dummyValue.nickIndex = numOfNicks + 1000;
theSize = GetHandleSize_(addressOffsetHandle) - sizeof(NickOffSetSortType);
BlockMoveData(&dummyValue,*addressOffsetHandle + (theSize/sizeof(NickOffSetSortType)),sizeof(NickOffSetSortType));
BlockMoveData(&dummyValue,*notesOffsetHandle + (theSize/sizeof(NickOffSetSortType)),sizeof(NickOffSetSortType));
QuickSort((void *)(*addressOffsetHandle),sizeof(NickOffSetSortType),0,numOfNicks,(void*)NickOffsetCompare,(void*)NickOffsetSwap);
SetHandleBig_((Handle)addressOffsetHandle,theSize);
QuickSort((void *)(*notesOffsetHandle),sizeof(NickOffSetSortType),0,numOfNicks,(void*)NickOffsetCompare,(void*)NickOffsetSwap);
SetHandleBig_((Handle)notesOffsetHandle,theSize);
HLock((Handle)addressOffsetHandle);
HLock((Handle)notesOffsetHandle);
firstNoteStart = -1;
// Find the offset of the first non-blank note
for (count=0;count<numOfNicks;count++)
{
theIndex = (*notesOffsetHandle)[count].nickIndex;
firstNoteStart = (*(This.theData))[theIndex].notesOffset;
if (firstNoteStart >= 0)
break;
}
/*
* find the file
*/
spec = This.spec;
if (err=FSpMyResolve(&spec,&junk))
return(False);
/*
* make && open a temp file
*/
// tmpSpec = spec;
// PCat(tmpSpec.name,GetRString(scratch,TEMP_SUFFIX));
if (err=NewTempSpec(spec.vRefNum,spec.parID,nil,&tmpSpec))
goto done;
if (err=FSpCreate(&tmpSpec,CREATOR,MAILBOX_TYPE,smSystemScript))
goto done;
if (err=FSpOpenDF(&tmpSpec,fsRdWrPerm,&tempRefN))
goto done;
if (err=FSpOpenDF(&spec,fsRdPerm,&nickRefN))
goto done;
cleanStartOffset = -1;
cleanStopOffset = -1;
cleanStartIndex = -1;
cleanStopIndex = -1;
tempHandle = NuHTempOK(0);
if (tempHandle == nil)
{
err = fnfErr;
goto done;
}
HLock((Handle)This.theData);
count = 0;
while (count <numOfNicks)
{
cleanStartIndex = count - 1;
do
{
cleanStartIndex++;
if (cleanStartIndex==numOfNicks) break;
theIndex = (*addressOffsetHandle)[cleanStartIndex].nickIndex;
dirty = (*(This.theData))[theIndex].addressesDirty;
deleted = (*(This.theData))[theIndex].deleted;
offset = (*(This.theData))[theIndex].addressOffset;
}
while ((dirty || deleted || offset < 0) && cleanStartIndex<numOfNicks);
if (cleanStartIndex == numOfNicks)
break;
theIndex = (*addressOffsetHandle)[cleanStartIndex].nickIndex;
cleanStartOffset = (*(This.theData))[theIndex].addressOffset;
cleanStopIndex = cleanStartIndex - 1;
do
{
cleanStopIndex++;
if (cleanStopIndex >= numOfNicks)
break;
theIndex = (*addressOffsetHandle)[cleanStopIndex].nickIndex;
dirty = (*(This.theData))[theIndex].addressesDirty;
deleted = (*(This.theData))[theIndex].deleted;
offset = (*(This.theData))[theIndex].addressOffset;
}
while ((!dirty && !deleted && offset >= 0) && cleanStopIndex<numOfNicks);
if (cleanStopIndex >= numOfNicks && !(*((*Aliases)[which].theData))[(*addressOffsetHandle)[numOfNicks - 1].nickIndex].addressesDirty && !(*((*Aliases)[which].theData))[(*addressOffsetHandle)[numOfNicks - 1].nickIndex].deleted)
{
if (firstNoteStart >= 0)
cleanStopOffset = firstNoteStart;
else
GetEOF(nickRefN,&cleanStopOffset);
cleanStopIndex = numOfNicks;
}
else
{
theIndex = (*addressOffsetHandle)[cleanStopIndex].nickIndex;
cleanStopOffset = (*(This.theData))[theIndex].addressOffset;
}
if (cleanStartOffset <= 0 )
cleanStartOffset = 1;
if (err = SetFPos(nickRefN,fsFromStart,cleanStartOffset - 1))
goto done;
GetFPos(tempRefN,&bytes);
bytesToShift = cleanStartOffset - bytes - 1;
bytes = cleanStopOffset-cleanStartOffset;
if (cleanStartOffset >=0 && bytes > 0)
{
long readBytes;
SetHandleBig_(tempHandle,bytes);
if (err = MemError())
goto done;
HLock(tempHandle);
readBytes = bytes;
if (err = ARead(nickRefN,&readBytes,*tempHandle))
goto done;
if (readBytes != bytes)
{
err = readErr;
goto done;
}
if (err = AWrite(tempRefN,&bytes,*tempHandle))
goto done;
HUnlock(tempHandle);
for (tempCount = cleanStartIndex; tempCount < cleanStopIndex; tempCount++)
{
theIndex = (*addressOffsetHandle)[tempCount].nickIndex;
if ((*(This.theData))[theIndex].addressOffset >=0 && !(*(This.theData))[theIndex].deleted)
(*(This.theData))[theIndex].addressOffset-=bytesToShift;
}
}
count = cleanStopIndex + 1;
}
ZapHandle(tempHandle); // leftover from last loop. SD 4/16
SetFPos(tempRefN,fsFromLEOF,-1);
bytes = 1;
ARead(tempRefN,&bytes,&theChar);
GetEOF(tempRefN,&bytes);
SetFPos(tempRefN,fsFromStart,bytes);
if (theChar != '\015' && bytes >0)
{
bytes = 1;
err = AWrite(tempRefN,&bytes,"\015");
}
GetRString(aliasCmd,ALIAS_CMD);
PCatC(aliasCmd,' ');
HLock((Handle)This.theData);
for (i=0;!err && i<numOfNicks;i++)
{
CycleBalls();
if ((*(This.theData))[i].addressesDirty && !(*(This.theData))[i].deleted) // Nickname has been modified...use in memory copy
{
tempHandle = GetNicknameData(which,i,true,false);
HLock(tempHandle);
if (tempHandle != nil && *tempHandle != nil && ** tempHandle != nil)
bytes = GetHandleSize_(tempHandle);
else
bytes = 0;
GetNicknameNamePStr(which,i,scratch);
if (bytes >0 && !(*(This.theData))[i].deleted)
{
GetFPos(tempRefN,&offset);
(*(This.theData))[i].addressOffset = offset;
}
else {
(*(This.theData))[i].addressOffset = -1;
(*(This.theData))[i].group = false;
}
if ((!(*(This.theData))[i].deleted) && bytes > 0 && !(err = FSWriteP(tempRefN,aliasCmd)))
{
if (PIndex(scratch,' '))
{
PInsert(scratch,sizeof(scratch),"\p\"",scratch+1);
PCatC(scratch,'"');
}
PCatC(scratch,' ');
if (!(err=FSWriteP(tempRefN,scratch)))
{
if (!(err = AWrite(tempRefN,&bytes,*tempHandle)))
{
bytes = 1;
err = AWrite(tempRefN,&bytes,"\015");
}
}
HPurge(tempHandle);
}
HUnlock(tempHandle);
}
else if ((*(This.theData))[i].deleted) {
(*(This.theData))[i].addressOffset = -1;
(*(This.theData))[i].group = false;
}
}
tempHandle = NuHTempOK(0);
if (tempHandle == nil)
goto done;
// Do the notes
count = 0;
while (count <numOfNicks)
{
cleanStartIndex = count - 1;
do
{
cleanStartIndex++;
if (cleanStartIndex==numOfNicks) break;
theIndex = (*notesOffsetHandle)[cleanStartIndex].nickIndex;
dirty = (*(This.theData))[theIndex].addressesDirty;
deleted = (*(This.theData))[theIndex].deleted;
offset = (*(This.theData))[theIndex].notesOffset;
}
while ((dirty || deleted || offset < 0) && cleanStartIndex<numOfNicks);
if (cleanStartIndex == numOfNicks)
break;
theIndex = (*notesOffsetHandle)[cleanStartIndex].nickIndex;
cleanStartOffset = (*(This.theData))[theIndex].notesOffset;
cleanStopIndex = cleanStartIndex - 1;
do
{
cleanStopIndex++;
if (cleanStopIndex >= numOfNicks)
break;
theIndex = (*notesOffsetHandle)[cleanStopIndex].nickIndex;
dirty = (*(This.theData))[theIndex].addressesDirty;
deleted = (*(This.theData))[theIndex].deleted;
offset = (*(This.theData))[theIndex].notesOffset;
}
while ((!dirty && !deleted && offset >= 0) && cleanStopIndex<numOfNicks);
if (cleanStopIndex >= numOfNicks && !(*((*Aliases)[which].theData))[(*notesOffsetHandle)[numOfNicks - 1].nickIndex].addressesDirty && !(*((*Aliases)[which].theData))[(*notesOffsetHandle)[numOfNicks - 1].nickIndex].deleted)
{
GetEOF(nickRefN,&cleanStopOffset);
cleanStopIndex = numOfNicks;
}
else
{
theIndex = (*notesOffsetHandle)[cleanStopIndex].nickIndex;
cleanStopOffset = (*(This.theData))[theIndex].notesOffset;
}
if (cleanStartOffset > 0)
{
if (err = SetFPos(nickRefN,fsFromStart,cleanStartOffset - 1))
goto done;
GetFPos(tempRefN,&bytes);
bytesToShift = cleanStartOffset - bytes - 1;
bytes = cleanStopOffset-cleanStartOffset;
if (cleanStartOffset >=0 && bytes >= 0)
{
long readBytes;
SetHandleBig_(tempHandle,bytes);
if (err=MemError())
goto done;
HLock(tempHandle);
readBytes = bytes;
if (err = ARead(nickRefN,&readBytes,*tempHandle))
goto done;
if (readBytes != bytes)
{
err = readErr;
goto done;
}
if (err = AWrite(tempRefN,&bytes,*tempHandle))
goto done;
HUnlock(tempHandle);
for (tempCount = cleanStartIndex; tempCount < cleanStopIndex; tempCount++)
{
theIndex = (*notesOffsetHandle)[tempCount].nickIndex;
if ((*(This.theData))[theIndex].notesOffset >= 0 && !(*(This.theData))[theIndex].deleted)
(*(This.theData))[theIndex].notesOffset-=bytesToShift;
}
}
}
count = cleanStopIndex + 1;
}
ZapHandle(tempHandle);
SetFPos(tempRefN,fsFromLEOF,-1);
bytes = 1;
ARead(tempRefN,&bytes,&theChar);
GetEOF(tempRefN,&bytes);
SetFPos(tempRefN,fsFromStart,bytes);
if (theChar != '\015' && bytes > 0)
{
bytes = 1;
err = AWrite(tempRefN,&bytes,"\015");
}
GetRString(aliasCmd,NOTE_CMD);
PCatC(aliasCmd,' ');
if (This.theData)
{
HLock((Handle)This.theData);
for (i=0;!err && i<numOfNicks;i++)
{
CycleBalls();
if ((*(This.theData))[i].addressesDirty)
{
tempHandle = GetNicknameData(which,i,false,false);
// Make sure the 'modified' change bit is set
if (saveChangeBits && tempHandle && PrefIsSet (PREF_CHANGE_BITS_FOR_CONDUIT))
SetNicknameChangeBit (tempHandle, changeBitModified, false);
(*(This.theData))[i].addressesDirty = false;
(*(This.theData))[i].notesDirty = false; // ...for now
HLock(tempHandle);
if (tempHandle != nil && *tempHandle != nil && **tempHandle != nil)
bytes = GetHandleSize_(tempHandle);
else
bytes = 0;
if (!(*(This.theData))[i].deleted)
GetNicknameNamePStr(which,i,scratch);
if (bytes >0 && !(*(This.theData))[i].deleted)
{
GetFPos(tempRefN,&offset);
(*(This.theData))[i].notesOffset = offset;
}
else
(*(This.theData))[i].notesOffset = -1;
if ((!(*(This.theData))[i].deleted) && bytes > 0 && !(err = FSWriteP(tempRefN,aliasCmd)))
{
if (PIndex(scratch,' '))
{
PInsert(scratch,sizeof(scratch),"\p\"",scratch+1);
PCatC(scratch,'"');
}
PCatC(scratch,' ');
if (!(err=FSWriteP(tempRefN,scratch)))
{
if (!(err = AWrite(tempRefN,&bytes,*tempHandle)))
{
bytes = 1;
err = AWrite(tempRefN,&bytes,"\015");
}
}
}
if (tempHandle)
{
HPurge(tempHandle);
HUnlock(tempHandle);
}
}
}
UL(This.theData);
}
tempHandle = nil;
GetFPos(tempRefN,&bytes);
SetEOF(tempRefN,bytes);
MyFSClose(tempRefN); tempRefN = 0;
MyFSClose(nickRefN); nickRefN = 0;
/* do the deed */
if (!err) err = ExchangeAndDel(&tmpSpec,&spec);
if (!err) This.dirty = False;
if (!err)
WriteNickTOC(which);
done:
if (This.theData) UL(This.theData);
if (tempRefN) MyFSClose(tempRefN);
if (nickRefN) MyFSClose(nickRefN);
if (err) FSpDelete(&tmpSpec);
UL(addressOffsetHandle);
UL(notesOffsetHandle);
ZapHandle(addressOffsetHandle);
ZapHandle(notesOffsetHandle);
UL(This.theData);
return(err==noErr);
}
//
// Nickname photos are initially saved into the temporary items folder.
// When we save the address book, we need to move these files into the
// photo album and modify tthe nickname 'picture' tag to point to the
// new location.
//
void SaveDirtyPictures (short ab)
{
NickStructHandle theData;
NickStructPtr pData;
FInfo fInfo;
FSSpec photoSpec,
urlSpec;
Str255 pictureTag,
nickname,
url;
Handle urlString;
OSErr theError;
short nick,
numNicks;
Boolean alreadyExists;
GetRString (pictureTag, ABReservedTagsStrn + abTagPicture);
theError = noErr;
if (theData = (*Aliases)[ab].theData) {
numNicks = GetHandleSize_ (theData) / sizeof (NickStruct);
for (nick = 0, pData = *theData; nick < numNicks; nick++, pData++)
// Does this nickname have a dirty picture?
if (!pData->deleted)
if (pData->pornography) {
// Grab the current 'file' URL from the notes, and turn it into an FSSpec
if (urlString = GetTaggedFieldValue (ab, nick, pictureTag)) {
theError = URLStringToSpec (urlString, &urlSpec);
// Make an FSSpec for the photo in the Photo Album
if (!theError)
theError = GetPhotoSpec (&photoSpec, ab, nick, &alreadyExists);
if (!theError && !alreadyExists) {
theError = FSpGetFInfo (&urlSpec, &fInfo);
if (!theError)
theError = FSpCreate (&photoSpec, fInfo.fdCreator, fInfo.fdType, smSystemScript);
if (!theError)
theError = FSpSetFInfo (&photoSpec, &fInfo);
}
// Swap specs and delete the temporary
if (!theError)
theError = ExchangeAndDel (&urlSpec, &photoSpec);
// Make a url for the file's permanent location
if (!theError) {
SetTaggedFieldValue (ab, nick, pictureTag, MakeFileURL (url, &photoSpec, 0), nickFieldReplaceExisting, 0, nil);
pData->pornography = false;
}
else
ComposeStdAlert (Caution, NICK_PHOTO_COULD_NOT_SAVE, GetNicknameNamePStr (ab, nick, nickname), theError);
}
}
}
}
//
// URLStringToSpec
//
// Take a URL and turn it into an FSSpec
//
OSErr URLStringToSpec (StringHandle urlString, FSSpec *spec)
{
Str255 fullPath,
proto,
host;
Ptr query;
long urlSize,
origQueryLen,
queryLen;
OSErr theError;
theError = ParseURLPtr (LDRef (urlString), urlSize = GetHandleSize (urlString), proto, host, &query, &queryLen);
origQueryLen = queryLen;
if (!theError)
switch (FindSTRNIndex (ProtocolStrn, proto)) {
case proFile:
TrLo (query, queryLen, "/", ":");
FixURLPtr (query, &queryLen);
MakePPtr (fullPath, query, queryLen);
theError = FSMakeFSSpec (0, 0, fullPath, spec);
break;
}
UL (urlString);
if (!theError) {
SetHandleBig (urlString, urlSize + (queryLen - origQueryLen));
IsAlias (spec, spec);
}
return (theError);
}
/************************************************************************
* NickFileOkForFastSave - see if all addresses come before all notes
************************************************************************/
Boolean NickFileOkForFastSave(short which)
{
short n = NNicknames;
long minNote = 0x7fffffff;
long maxAlias = 0;
long note, alias;
while (n--)
{
note = (*(This.theData))[n].notesOffset;
alias = (*(This.theData))[n].addressOffset;
if (note>=0) minNote = MIN(minNote,note);
if (alias>=0) maxAlias = MAX(maxAlias,alias);
if (minNote <= maxAlias) return(false); // at least one note comes before at least one alias
}
return(true); // all aliases come after all notes
}
/************************************************************************
* NickOffsetCompare - compare two names
************************************************************************/
int NickOffsetCompare(NickOffSetSortType *n1, NickOffSetSortType *n2)
{
int result;
if (n1->nickIndex>=TotalNumOfNicks) return(1);
if (n2->nickIndex>=TotalNumOfNicks) return(-1);
if (n1->offset == n2->offset)
result = 0;
else
result = n1->offset > n2->offset?1:-1;
return(result);
}
/************************************************************************
* NickNameSwap - swap two names
************************************************************************/
void NickOffsetSwap(NickOffSetSortType *n1, NickOffSetSortType *n2)
{
NickOffSetSortType temp;
BlockMoveData(n2,&temp,sizeof(NickOffSetSortType));
BlockMoveData(n1,n2,sizeof(NickOffSetSortType));
BlockMoveData(&temp,n1,sizeof(NickOffSetSortType));
}
/************************************************************************
* AppendTextToNick - append (comma separated) addresses to a nickname
************************************************************************/
OSErr AddTextToNick(short which, PStr name, Handle text,Boolean append)
{
long hashName = NickHash(name);
long index;
NickStructHandle aliases = This.theData;
OSErr err = noErr;
Handle tempHandle;
// AliasWinGonnaSave();
index = NickMatchFound(aliases,hashName,name,which);
if (append)
{
tempHandle = GetNicknameData(which,index,true,true);
err = PtrPlusHand_(",",text,1);
if (!err) err = PtrPlusHand_(LDRef(tempHandle),text,GetHandleSize_(GetNicknameData(which,index,true,true)));
UL(tempHandle);
if (err) {WarnUser(MEM_ERR,err); return(err);}
UL(This.theData);
}
NickUniq(text,"\p,",True);
ReplaceNicknameAddresses(which,name,text);
SetAliasDirty(which);
if (AliasWinIsOpen())
AliasWinRefresh();
else
SaveAliases(true);
return err;
}
/************************************************************************
* ReadNickFileList - get list of nickfiles
************************************************************************/
void ReadNickFileList(FSSpec *pSpec, AddressBookType type, Boolean reread)
{
Str31 name;
CInfoPBRec hfi;
AliasDesc ad;
Boolean multipleNickFiles;
multipleNickFiles = false;
Zero(ad);
ad.type = type;
hfi.hFileInfo.ioNamePtr = name;
hfi.hFileInfo.ioFDirIndex = 0;
while (!DirIterate(pSpec->vRefNum,pSpec->parID,&hfi))
if (hfi.hFileInfo.ioFlFndrInfo.fdType=='TEXT' || hfi.hFileInfo.ioFlFndrInfo.fdCreator==CREATOR)
{
multipleNickFiles = true;
SimpleMakeFSSpec(pSpec->vRefNum,pSpec->parID,name,&ad.spec);
if (!CanWrite(&ad.spec,&ad.ro))
{
ad.ro = !ad.ro; /* opposite sense! */
if (PtrPlusHand_(&ad,Aliases,sizeof(ad))) DieWithError(MEM_ERR,MemError());
if (type == pluginAddressBook && reread) FSpKillRFork(&ad.spec);
}
else
{
Str255 scratch;
ComposeRString(scratch,NICK_FILE_GONE,ad.spec.name);
AlertStr(OK_ALRT,Caution,scratch);
}
}
if (multipleNickFiles)
UseFeature (featureMultipleNicknameFiles);
}
/************************************************************************
* BuildAddressHashes - build a hash table for all the expanded addresses
************************************************************************/
OSErr BuildAddressHashes(short which)
{
Accumulator a;
NickStructHandle nicks = (*Aliases)[which].theData;
short n = NNicknames;
TextAddrHandle tempHandle=nil;
Str31 s;
OSErr err = noErr;
Zero(a);
for (n--;!err && n>=0;n--)
{
CycleBalls();
if (!(*This.theData)[n].deleted)
{
if ((*(This.theData))[n].addressesDirty) // Nickname has been modified...use in memory copy
tempHandle = GetNicknameData(which,n,true,false);
else // Nickname hasn't been modified, use on disk copy if necessary
tempHandle = GetNicknameData(which,n,true,true);
// main addresses
if (tempHandle)
{
err = AddHandleToAddressHashes(tempHandle,&a);
if (!(*(This.theData))[n].addressesDirty) HPurge(tempHandle);
}
// other addresses
if (!err && (tempHandle = GetTaggedFieldValue(which,n,GetRString(s,ABReservedTagsStrn+abTagOtherEmail))))
{
err = AddHandleToAddressHashes(tempHandle,&a);
ZapHandle(tempHandle);
}
}
}
// Did we win?
if (!err)
{
AccuTrim(&a);
(*Aliases)[which].addressHashes = a;
}
else
AccuZap(a);
return err;
}
/************************************************************************
* AddHandleToAddressHashes - add addresses from a handle to a hash accumulator
************************************************************************/
OSErr AddHandleToAddressHashes(TextAddrHandle sourceHandle,AccuPtr a)
{
BinAddrHandle rawHandle=nil;
BinAddrHandle expandedHandle=nil;
Str255 oneAddr;
long spot, size;
uLong hash;
OSErr err = noErr;
if (sourceHandle)
if (!SuckAddresses(&rawHandle,sourceHandle,false,false,true,nil))
if (!ExpandAliases(&expandedHandle,rawHandle,0,false))
{
size = GetHandleSize(expandedHandle)-1;
for (spot=0;!err && spot<size;spot += (*expandedHandle)[spot]+2)
{
PCopy(oneAddr,(*expandedHandle)+spot);
MyLowerStr(oneAddr);
hash = Hash(oneAddr);
err = AccuAddPtr(a,&hash,sizeof(hash));
}
}
ZapHandle(expandedHandle);
ZapHandle(rawHandle);
return err;
}
/************************************************************************
* ReadPluginNickFiles - get list of plugin nickfiles
************************************************************************/
void ReadPluginNickFiles(Boolean reread)
{
FSSpec folderSpec;
ETLGetPluginFolderSpec(&folderSpec,PLUGIN_NICKNAMES);
folderSpec.parID = SpecDirId(&folderSpec);
*folderSpec.name = 0;
ReadNickFileList(&folderSpec,pluginAddressBook,reread);
if (reread) RegenerateAllAliases(false);
}
//
// ParseFirstLast
//
// Algorithm
//
// 1. Set the name pointer to point to the first name (or last if asian)
// 2. Read a token
// <09> No more! Go to step 10
// 3. Is the token an Honorific?
// <09> Yes! Append it onto the honorific string
// <09> Go to step 2
// 4. Is the token a Qualifier?
// <09> Yes! Append it onto the qualifier string
// <09> Go to step 2
// 6. If we've now reached the first comma
// <09> Set the name pointer to point to the last name
// 7. Append the token onto the name pointer
// 8. Switch the name pointer to the "other" name
// 9. Go to step 2
// 10. Did we find any commas?
// <09> No! Make the "other" name
//
PStr ParseFirstLast (PStr realName, PStr firstName, PStr lastName)
{
Str255 qualifiers,
honorifics,
name1,
name2,
token,
tokenCopy;
PStr name;
UPtr spot;
short numQualifiers,
numHonorifics,
numCommas,
numName1,
numName2,
*numPtr;
char lastTokenSeperator,
lastDelimiter;
*name1 = 0;
*name2 = 0;
*qualifiers = 0;
*honorifics = 0;
numQualifiers = 0;
numHonorifics = 0;
numCommas = 0;
numName1 = 0;
numName2 = 0;
lastTokenSeperator = 0;
// Some very lazy people fail to use spaces when representing their name (J.P.Morgan)
// so we'll do the work for them and insert spaces where appropriate
ScanNameForSpaces (realName);
name = name1;
numPtr = &numName1;
// Loop through all the tokens in the name
spot = realName + 1;
while (PToken (realName, token, &spot, " ,")) {
PCopy (tokenCopy, token);
// Cleanup the token a bit before giving the qualifiers and honorifics a shot
PStripChar (tokenCopy, '.');
// Is the token either a name qualifier or a high falutin' honorific?
if (FindSTRNIndex (NameQualifiersStrn, tokenCopy)) {
if (numQualifiers && lastTokenSeperator)
PCatC (qualifiers, lastTokenSeperator);
PCat (qualifiers, token);
++numQualifiers;
}
else
if (FindSTRNIndex (NameHonorificsStrn, tokenCopy)) {
if (numHonorifics && lastTokenSeperator)
PCatC (honorifics, lastTokenSeperator);
PCat (honorifics, token);
++numHonorifics;
}
else {
// The token is just plain ol' unsophisticated text, put it in the name
if (*numPtr && lastTokenSeperator)
PCatC (name, lastDelimiter = lastTokenSeperator);
PCat (name, token);
++(*numPtr);
// If we just processed the first comma, switch to our "other" name
if (*(spot - 1) == ',') {
if (!numCommas) {
name = (name == name1) ? name2 : name1;
numPtr = (numPtr = &numName1) ? &numName2 : &numName1;
}
++numCommas;
}
}
if (spot > realName + 1)
lastTokenSeperator = *(spot - 1);
// Skip white space before the next token
while (spot <= &realName[realName[0]] && *spot == ' ' || *spot == tabChar)
++spot;
}
// Okay, at this point we have stuff in 4 places: name1, name2, qualifiers and honorifics
// We can now go about building the actual first and last names based on whether or not
// we've found a comma and whether or not we're working with eastern vs western style names.
name = name1;
numPtr = &numName1;
// If we found no commas, or the _opposite_ name is empty (indicating that the entire name is
// stuffed into the primary (first) name fiels, then we'll make the opposite (last) name the
// final token appearing in the primary name field (whew!)
if (!numCommas || !((name == name1) ? *name2 : *name1)) {
if (*numPtr > 1) {
short name2len;
spot = PRIndex (name, lastDelimiter);
name2len = *name - (spot - name);
BMD (spot, (name == name1) ? name2 : name1, name2len + 1);
*name2 = name2len;
*name -= (name2len + 1);
}
if ((GetPrefLong(PREF_NICK_GEN_OPTIONS)&kNickGenOptAsian) || IsAllUpper(name1) && !IsAllUpper(name2)) {
PCopy (firstName, name2);
PCopy (lastName, name1);
}
else {
PCopy (firstName, name1);
PCopy (lastName, name2);
}
}
else
if ((GetPrefLong(PREF_NICK_GEN_OPTIONS)&kNickGenOptAsian) || IsAllUpper(name2) && !IsAllUpper(name1)) {
PCopy (firstName, name1);
PCopy (lastName, name2);
}
else {
PCopy (firstName, name2);
PCopy (lastName, name1);
}
// Append qualifiers to the first name
if (numQualifiers) {
PCatC (firstName, ' ');
PCat (firstName, qualifiers);
}
return (realName);
}
PStr JoinFirstLast (PStr fullName, PStr firstName, PStr lastName)
{
Str255 first,
last;
*fullName = 0;
*first = 0;
*last = 0;
// Strip Qualifiers and Honorifics from the first and last names
StripQualifiersAndHonorifics (firstName, first);
StripQualifiersAndHonorifics (lastName, last);
// Join them based on our "international joining preferences"
if ((GetPrefLong(PREF_NICK_GEN_OPTIONS)&kNickGenOptAsian) || IsAllUpper(last) && !IsAllUpper(first)) {
if (*last) PCat (fullName, last);
if (*first) {
if (*last) PCatC (fullName, ' ');
PCat (fullName, first);
}
}
else {
if (*first) PCat (fullName, first);
if (*last) {
if (*first) PCatC (fullName, ' ');
PCat (fullName, last);
}
}
return (fullName);
}
PStr StripQualifiersAndHonorifics (PStr name, PStr strippedName)
{
Str255 token,
tokenCopy;
UPtr spot;
char lastTokenSeperator;
*strippedName = 0;
lastTokenSeperator = 0;
// Loop through all the tokens in the first name
spot = name + 1;
while (PToken (name, token, &spot, " ,")) {
PCopy (tokenCopy, token);
// Cleanup the token a bit before giving the qualifiers and honorifics a shot
PStripChar (tokenCopy, '.');
// If the token neither a name qualifier or honorific, keep what we found
if (!FindSTRNIndex (NameQualifiersStrn, tokenCopy) && !FindSTRNIndex (NameHonorificsStrn, tokenCopy)) {
if (lastTokenSeperator)
PCatC (strippedName, lastTokenSeperator);
PCat (strippedName, token);
}
if (spot > name + 1)
lastTokenSeperator = *(spot - 1);
// Skip white space before the next token
while (spot <= &name[name[0]] && *spot == ' ' || *spot == tabChar)
++spot;
}
return (strippedName);
}
// Walk the text of the name backwards, inserting spaces as we find periods that
// follow runs of two or more characters
PStr ScanNameForSpaces (PStr name)
{
Ptr spot;
short chars;
// don't insert spaces if name contains an @, since it's probably an address
if (PIndex(name,'@')) return name;
chars = 0;
for (spot = name + *name; spot > name; spot--)
switch (*spot) {
case '.':
if (chars > 1) {
PInsertC (name, sizeof (Str255), ' ', spot + 1);
}
case ' ':
chars = 0;
break;
default:
++chars;
}
return (name);
}
/************************************************************************
* MakeUniqueNickname - Make a unique "untitled" nickname
************************************************************************/
void MakeUniqueNickname (short ab, Str31 nickname)
{
NickStructHandle aliases;
Str31 s;
long hashName,
suffix;
Byte saveLen;
if (!*nickname)
GetRString (nickname, UNTITLED_NICKNAME);
if (aliases = (*Aliases)[ab].theData) {
hashName = NickHash (nickname);
suffix = 2;
saveLen = *nickname;
while (NickMatchFound (aliases, hashName, nickname, ab) >= 0) {
*nickname = saveLen;
s[*s = 1] = ' ';
PLCat (s, suffix++);
if (*nickname + *s + 1 < sizeof (Str31) - 1)
PCat (nickname, s);
else {
BlockMoveData (&s[1], &nickname[*nickname - *s + 1], *s);
*nickname = sizeof (Str31) - 1;
}
hashName = NickHash (nickname);
}
}
}
/************************************************************************
* NickBackup - Make a backup of a nickname file
************************************************************************/
OSErr NickBackup(FSSpecPtr spec)
{
OSErr err = noErr;
FSSpec spoolSpec;
DateTimeRec dtr;
Str31 name;
if ((err=SubFolderSpec(SPOOL_FOLDER,&spoolSpec))==noErr)
{
GetTime(&dtr);
FSMakeFSSpec(spoolSpec.vRefNum,spoolSpec.parID,ComposeString(name,"\p%p.%d.%d.%d.%d",spec->name,dtr.day,dtr.hour,dtr.minute,dtr.second),&spoolSpec);
err = FSpDupFile(&spoolSpec,spec,False,False);
}
return (err);
}
//
// GetNicknameTagMap
//
// Retrieve a Nickname Tag Map from a 'TGMP' resource, placing the contents
// into a NicknameTagMapRec. In searching for the proper resource we look
// for a 'TGMP' with a resource name that matches:
// 1. The requested server
// 2. The requested service (if no server match was found)
//
OSErr GetNicknameTagMap (PStr service, PStr server, NicknameTagMapRecPtr tagMapPtr)
{
Handle resource;
Ptr from;
Str255 scratch;
OSErr theError;
short i;
// First, find a 'TGMP' resource
resource = nil;
if (*server)
resource = GetNamedResource (TAG_MAP_TYPE, server);
if (!resource && *service)
resource = GetNamedResource (TAG_MAP_TYPE, service);
if (!resource)
return (ResError ());
// Create handles for service and nickname tags
tagMapPtr->serviceTags = NuHandle (0);
tagMapPtr->nicknameTags = NuHandle (0);
theError = MemError ();
if (!theError) {
// Read the information out of the 'TGMP' resource
from = LDRef (resource);
// Service
from = CopyBytesAndMovePtr (from, &tagMapPtr->service, *from + 1);
// Server
from = CopyBytesAndMovePtr (from, &tagMapPtr->server, *from + 1);
// Count
from = CopyBytesAndMovePtr (from, &tagMapPtr->count, sizeof (tagMapPtr->count));
// The tags
for (i = 0; i < tagMapPtr->count && !theError; ++i) {
from = CopyBytesAndMovePtr (from, scratch, *from + 1);
theError = PtrPlusHand (scratch, tagMapPtr->serviceTags, *scratch + 1);
if (!theError) {
from = CopyBytesAndMovePtr (from, scratch, *from + 1);
theError = PtrPlusHand (scratch, tagMapPtr->nicknameTags, *scratch + 1);
}
}
UL (resource);
}
ReleaseResource (resource);
// Get rid of any memory we've allocated if trouble is brewing
if (theError)
DisposeNicknameTagMap (tagMapPtr);
return (theError);
}
void DisposeNicknameTagMap (NicknameTagMapRecPtr tagMapPtr)
{
if (tagMapPtr) {
ZapHandle (tagMapPtr->serviceTags);
ZapHandle (tagMapPtr->nicknameTags);
}
}
//
// NicknameTag2ServiceTag
//
// Given a pointer to a Nickname Tag Map and a Eudora nickname tag, return the corresponding
// tag for a given service.
//
PStr NicknameTag2ServiceTag (NicknameTagMapRecPtr tagMapPtr, PStr nicknameTag, PStr serviceTag)
{
Ptr serviceTagsPtr,
nicknameTagsPtr;
short i;
*serviceTag = 0;
nicknameTagsPtr = LDRef (tagMapPtr->nicknameTags);
serviceTagsPtr = LDRef (tagMapPtr->serviceTags);
for (i = 0; i < tagMapPtr->count && !*serviceTag; ++i)
if (StringSame (nicknameTagsPtr, nicknameTag))
PCopy (serviceTag, serviceTagsPtr);
else {
nicknameTagsPtr += (*nicknameTagsPtr + 1);
serviceTagsPtr += (*serviceTagsPtr + 1);
}
UL (tagMapPtr->serviceTags);
UL (tagMapPtr->nicknameTags);
return (serviceTag);
}
//
// ServiceTag2NicknameTag
//
// Given a pointer to e Nickname Tag Map and a service tag, return the corresponding
// tag for a Eudora nickname
//
PStr ServiceTag2NicknameTag (NicknameTagMapRecPtr tagMapPtr, PStr serviceTag, PStr nicknameTag)
{
Ptr serviceTagsPtr,
nicknameTagsPtr;
short i;
*nicknameTag = 0;
serviceTagsPtr = LDRef (tagMapPtr->serviceTags);
nicknameTagsPtr = LDRef (tagMapPtr->nicknameTags);
for (i = 0; i < tagMapPtr->count && !*nicknameTag; ++i)
if (StringSame (serviceTagsPtr, serviceTag))
PCopy (nicknameTag, nicknameTagsPtr);
else {
serviceTagsPtr += (*serviceTagsPtr + 1);
nicknameTagsPtr += (*nicknameTagsPtr + 1);
}
UL (tagMapPtr->nicknameTags);
UL (tagMapPtr->serviceTags);
return (nicknameTag);
}
PStr GetIndNicknameTag (NicknameTagMapRecPtr tagMapPtr, short index, PStr nicknameTag)
{
Ptr nicknameTagsPtr;
short i;
*nicknameTag = 0;
nicknameTagsPtr = *tagMapPtr->nicknameTags;
if (index < tagMapPtr->count) {
for (i = 0; i < index; ++i)
nicknameTagsPtr += (*nicknameTagsPtr + 1);
PCopy (nicknameTag, nicknameTagsPtr);
}
return (nicknameTag);
}
short FindServiceTagIndex (NicknameTagMapRecPtr tagMapPtr, PStr serviceTag)
{
Ptr serviceTagsPtr;
short index,
i;
serviceTagsPtr = LDRef (tagMapPtr->serviceTags);
index = 0;
for (i = 1; !index && i <= tagMapPtr->count; ++i)
if (StringSame (serviceTagsPtr, serviceTag))
index = i;
else
serviceTagsPtr += (*serviceTagsPtr + 1);
UL (tagMapPtr->serviceTags);
return (index);
}
PrimaryLocationType GetPrimaryLocation (Handle notes)
{
PrimaryLocationType location;
Str255 tag,
value;
location = noPrimary;
GetTaggedFieldValueStrInNotes (notes, GetRString (tag, ABReservedTagsStrn + abTagPrimary), value);
if (StringSame (value, GetRString (tag, VCardKeywordStrn + vcHome)))
location = homePrimary;
else if (StringSame (value, GetRString (tag, VCardKeywordStrn + vcWork)))
location = workPrimary;
return (location);
}
short FindAddressBookType (AddressBookType type)
{
short addressBooks,
ab;
addressBooks = NAliases;
for (ab = 0; ab < addressBooks; ++ab)
if ((*Aliases)[ab].type == type)
return (ab);
return (-1);
}
#ifdef VCARD
Boolean AnyPersonalNicknames (void)
{
short ab,
nick;
Boolean anyNicks;
anyNicks = false;
ab = FindAddressBookType (personalAddressBook);
if (ValidAddressBook (ab))
if ((*Aliases)[ab].theData) {
nick = GetHandleSize_ ((*Aliases)[ab].theData) / sizeof (NickStruct);
while (!anyNicks && nick--)
if (!(*((*Aliases)[ab].theData))[nick].deleted)
anyNicks = true;
}
return (anyNicks);
}
/************************************************************************
* WhiteListTS - add a message's sender to the whitelist
************************************************************************/
OSErr WhiteListTS(TOCHandle tocH,short sumNum)
{
Str255 scratch;
HeadSpec hs;
TextAddrHandle addr=nil;
Accumulator a;
uLong hash;
if (sumNum<0)
{
Zero(a);
// Examine all the selected messages
for (sumNum=(*tocH)->count-1;sumNum>=0;sumNum--)
{
if ((*tocH)->sums[sumNum].selected)
{
WhiteListTS(tocH,sumNum);
EnsureFromHash(tocH,sumNum);
hash = (*tocH)->sums[sumNum].fromHash;
if (ValidHash(hash) && 0>AccuFindPtr(&a,&hash,sizeof(hash)))
AccuAddPtr(&a,&hash,sizeof(hash));
}
}
// select everything with the same hash
for (sumNum=(*tocH)->count;sumNum--;)
if (!(*tocH)->sums[sumNum].selected)
{
EnsureFromHash(tocH,sumNum);
hash = (*tocH)->sums[sumNum].fromHash;
if (ValidHash(hash) && 0<=AccuFindPtr(&a,&hash,sizeof(hash)))
BoxSetSummarySelected(tocH,sumNum,true);
}
AccuZap(a);
}
else
{
if (!CacheMessage(tocH,sumNum))
{
HNoPurge((*tocH)->sums[sumNum].cache);
HeaderName(FROM_HEAD); // weird--goes into scratch
TrimWhite(scratch);
if (HandleHeadFindStr((*tocH)->sums[sumNum].cache,scratch,&hs))
{
HandleHeadGetText((*tocH)->sums[sumNum].cache,&hs,&addr);
if (addr) WhiteListAddr(addr);
ZapHandle(addr);
}
HPurge((*tocH)->sums[sumNum].cache);
// Now that we've whitelisted the message, bop its junk score
JunkSetScore(tocH,sumNum,JUNK_BECAUSE_WHITE,0);
}
}
return noErr; // la la la la la la la la la la la la la la
}
/************************************************************************
* WhiteListAddr - add a an address to the whitelist
************************************************************************/
OSErr WhiteListAddr(TextAddrHandle addr)
{
Str255 scratch;
Str31 abName;
Str63 nick;
Str255 first, last;
short which=0;
BinAddrHandle binAddr, justAddr;
OSErr err = fnfErr;
// is it there already?
MakePStr(scratch,*addr,GetHandleSize(addr));
if (AppearsInAliasFile(scratch,nil)) return dupFNErr; // already there
// Nope. Add it.
// find which book
if (*GetRString(scratch,WHITELIST_ADDRBOOK))
{
for (which = NAliases-1; which>0; which--)
{
PCopy(abName,(*Aliases)[which].spec.name);
if (StringSame(abName,scratch)) break;
}
}
else which = FindAddressBookType(eudoraAddressBook);
// reformat the address
if (!SuckAddresses(&binAddr,addr,true,false,false,nil))
{
// get a nickname suggestion
NickSuggest(binAddr,nick,scratch,true,JUNK_NICK_FMT);
if (*nick)
if (!SuckAddresses(&justAddr,addr,false,false,false,nil))
{
// fancy names are good
ParseFirstLast(scratch,first,last);
// finally add the darn thing
if (0<=NewNickLow(justAddr, CreateSimpleNotes(scratch,first,last), which, nick, false, nrNone, !NickWinIsOpen()))
ABTickleHardEnoughToMakeYouPuke();
// the rest is gravy...
ZapHandle(justAddr);
}
ZapHandle(binAddr);
}
return err;
}
#endif
/************************************************************************
* UniqBinAddr - make a BinAddrHandle contain only one of each address
************************************************************************/
BinAddrHandle UniqBinAddr(BinAddrHandle addresses)
{
Accumulator hashAcc, addrAcc;
Str255 addr;
long offset = 0;
long len = GetHandleSize(addresses);
Zero(hashAcc);
Zero(addrAcc);
// loop through all the addresses in the bin addr list
for (offset = 0; offset<len-1; offset += (*addresses)[offset]+2)
{
// copy to a string
PCopy(addr,*addresses + offset);
ASSERT(*addr); // shouldn't happen...
// add hash to list of hashes if not there already
if (AddAddressHashUniq(addr,&hashAcc))
{
// wasn't there already, add to output list
PTerminate(addr);
AccuAddPtr(&addrAcc,addr,*addr+2);
}
}
// terminating BinAddrHandle nil
AccuAddChar(&addrAcc,0);
// copy into original handle
SetHandleBig(addresses,addrAcc.offset);
if (!MemError())
BMD(*addrAcc.data,*addresses,addrAcc.offset);
// cleanup
AccuZap(addrAcc);
AccuZap(hashAcc);
return addresses;
}
/************************************************************************
* SortBinAddr - make a BinAddrHandle be in order
************************************************************************/
BinAddrHandle SortBinAddr(BinAddrHandle addresses)
{
short count = CountAddresses(addresses,0);
short i;
Handle addressVector = NuHandle(count*sizeof(UPtr));
BinAddrHandle newAddresses = DupHandle(addresses);
UPtr spot;
UPtr *vSpot;
// fill in a vector of pointers to each address
if (addressVector && newAddresses)
{
vSpot = (UPtr*)*addressVector;
for (spot = LDRef(addresses); *spot; spot += *spot+2)
*vSpot++ = spot;
ASSERT(((long*)*addressVector)[count-1]);
// now sort the vector
QuickSort(LDRef(addressVector),sizeof(UPtr),0,count-1,(void*)SortAddrNameCompare,(void*)PtrSwap);
// copy the list...
spot = *newAddresses;
vSpot = (UPtr*)*addressVector;
for (i=0;i<count;i++)
{
BMD(*vSpot,spot,**vSpot+2);
spot += **vSpot+2;
vSpot++;
}
// Shoot it home
BMD(*newAddresses,*addresses,GetHandleSize(addresses));
}
// cleanup
ZapHandle(newAddresses);
ZapHandle(addressVector);
return addresses;
}
/************************************************************************
* SortAddrNameCompare - compare two addresses, going by last name then first name.
* This is an insanely expensive comparison, but we don't usually sort huge
* lists of addresses and cpu is cheap
************************************************************************/
int SortAddrNameCompare(UPtr *s1, UPtr *s2)
{
Str127 first, last, n1, n2;
PSCopy(n1,*s1);
PSCopy(n2,*s2);
BeautifyFrom(n1);
BeautifyFrom(n2);
ParseFirstLast(n1,first,last);
PCopy(n1,last);
PCat(n1,first);
ParseFirstLast(n2,first,last);
PCopy(n2,last);
PCat(n2,first);
return StringComp(n1,n2);
}