1 line
115 KiB
C
Executable File
1 line
115 KiB
C
Executable File
/* 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,"e);
|
||
|
||
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,"e);
|
||
|
||
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);
|
||
}
|
||
|