Sources/MSAECompare.c

// MSAECompare.c
//
// Original version by Jon Lansdell and Nigel Humphreys.
// 4.0 and 3.1 updates by Greg Sutton.
// ©Apple Computer Inc 1996, all rights reserved.
 
/*
    Changes for 4.0
    
    29-Feb-96 : GS : Added ability to compare menu and menu item properties.
    22-Apr-96 : GS : Comparing text begins, ends, and contains is now true if equal.
*/
 
#include "MSAECompare.h"
 
#include <AEPackObject.h>
#include "MSGlobals.h"
#include "MSUtils.h"
#include "MSAEUtils.h"
#include "MSWindow.h"
#include "MSFile.h"
#include "MSAppleEvents.h"
 
#include "MSToken.h"
#include "MSAECountElements.h"
#include "MSAEGetData.h"
 
#include <string.h>
 
 
#pragma segment AppleEvent
 
 
OSErr   InstallObjectCallbacks(void)
{
    OSErr   err;
 
    err = AESetObjectCallbacks(NewOSLCompareProc(MyCompareProc),
                                NewOSLCountProc(MyCountProc),
                                NULL, NULL, NULL, NULL, NULL);
                                
    return(err);
}
 
 
pascal OSErr    MyCompareProc(DescType oper, const AEDesc* obj1, const AEDesc* obj2, Boolean* result)
{
    AEDesc      desc1    = {typeNull, NULL};
    AEDesc      desc2    = {typeNull, NULL};
    AEDesc      tempDesc = {typeNull, NULL};
    OSErr       err;
    
    err = ExtractData(obj1, &desc1);
    if (err != noErr) goto done;
    err = ExtractData(obj2, &desc2);
    if (err != noErr) goto done;
 
    // Make sure the 2 data types are the same
    if (desc1.descriptorType != desc2.descriptorType)
    {
        err = AEDuplicateDesc(&desc2, &tempDesc);
        if (err != noErr) goto done;
        err = AEDisposeDesc(&desc2);
        err = AECoerceDesc(&tempDesc, desc1.descriptorType, &desc2);
        if (err != noErr)                       // If we can't coerce one way, try
        {                                       // the opposite way.
            err = AEDuplicateDesc(&tempDesc, &desc2);   // We disposed of desc2
            err = AEDisposeDesc(&tempDesc);
            err = AEDuplicateDesc(&desc1, &tempDesc);
            if (err != noErr) goto done;
            err = AEDisposeDesc(&desc1);
            err = AECoerceDesc(&tempDesc, desc2.descriptorType, &desc1);
            if (err != noErr) goto done;
        }
    }
    
    // Now that we know that the 2 types are the same, go ahead and run the compare
    switch(desc1.descriptorType)
    {
        case typeChar:
            err = MyCompareText(oper, &desc1, &desc2, result);
            break;
        
        case typeShortInteger:
        case typeLongInteger:
            err = MyCompareInteger(oper, &desc1, &desc2, result);
            break;
 
        case typeBoolean:
            err = MyCompareBoolean(oper, &desc1, &desc2, result);
            break;
                
        default:
            err = errAEWrongDataType;
    }
    
done:
    if (desc1.dataHandle)
        (void) AEDisposeDesc(&desc1);
    if (desc2.dataHandle)
        (void) AEDisposeDesc(&desc2);
    if (tempDesc.dataHandle)
        (void) AEDisposeDesc(&tempDesc);
    
    return(err);
}
 
 
OSErr   ExtractData(const AEDesc *sourceDesc, AEDesc *theData)
{
    AEDesc      intermediateDesc = {typeNull, NULL};
    OSErr       err;
    
    // This routine can receive: An Object specifier, an object token, a property token, or
    // some data (TEXT, Rectangle, etc.). It needs to convert whatever it's handed into data
    // and return that.
    
    // If we don't have any data, complain
    if (sourceDesc->descriptorType == typeNull || sourceDesc->dataHandle == NULL)
        return(errAENoSuchObject);
    
    // If it's an object specifier, resolve into a token
    if (sourceDesc->descriptorType == typeObjectSpecifier)
        err = AEResolve(sourceDesc, kAEIDoMinimum, &intermediateDesc);
    else    // Otherwise, just copy it
        err = AEDuplicateDesc(sourceDesc, &intermediateDesc);
 
    if (err != noErr) goto done;
    
    // Now that we have a token, read from it
    switch (intermediateDesc.descriptorType)
    {
        case typeMyApplProp:
        case typeMyTextProp:
        case typeMyWindowProp:
        case typeMyMenuProp:
        case typeMyMenuItemProp:
        case typeMyText:
            err = HandleGetData(&intermediateDesc, typeWildCard, theData);
            break;
 
        default:
            // This is probably raw data , so pass it back up the line
            err = AEDuplicateDesc(&intermediateDesc, theData);
    }
 
done:
    if (intermediateDesc.dataHandle)
        (void) AEDisposeDesc(&intermediateDesc);
 
    return(err);
}
 
 
OSErr   MyCompareText(DescType oper, const AEDesc *desc1, const AEDesc *desc2, Boolean *result)
{
    int     compareResult;
    long    compareSize;
    long    textSize1, textSize2;
    char    *testStr, *compareStr;
    OSErr   err = noErr;
    
    textSize1 = GetHandleSize(desc1->dataHandle);
    HLockHi(desc1->dataHandle);
    textSize2 = GetHandleSize(desc2->dataHandle);
    HLockHi(desc2->dataHandle);
    if (textSize1 < textSize2)
    {
        switch (oper)
        {
            case kAEBeginsWith:     // Can't begin or end or contain any string
            case kAEEndsWith:       // longer than itself
            case kAEContains:
                *result = false;
                return(noErr);
        }
    
        compareSize = textSize1;
    }
    else
        compareSize = textSize2;
    
    switch (oper)
    {
        case kAEEndsWith:           // Offset to end of string
            testStr = (char *)(*desc1->dataHandle + textSize1 - textSize2); 
            break;
        
        case kAEContains:
            testStr = NewPtr(textSize1 + 1);
            BlockMove(*desc1->dataHandle, testStr, textSize1);
            testStr[textSize1] = '\0';
            compareStr = NewPtr(textSize2 + 1);
            BlockMove(*desc2->dataHandle, compareStr, textSize2);
            compareStr[textSize2] = '\0';
            *result = (NULL != strstr(testStr,compareStr));
            DisposePtr(testStr);
            DisposePtr(compareStr);
            return(noErr);
        
        default:
            testStr = (char *)*desc1->dataHandle;
    }
    
    compareResult = strncmp(testStr, (char *)*desc2->dataHandle, compareSize);
    
    HUnlock(desc1->dataHandle);
    HUnlock(desc2->dataHandle);
 
    switch (oper)
    {
        case kAEEquals:
            *result = (textSize1 == textSize2 && compareResult == 0);
            break;
 
        case kAEBeginsWith:
        case kAEEndsWith:
            *result = (compareResult == 0);
            break;
 
        case kAELessThan:
            *result = (compareResult < 0);
            break;
        
        case kAELessThanEquals:
            *result = (compareResult <= 0);
            break;
        
        case kAEGreaterThan:
            *result = (compareResult > 0);
            break;
        
        case kAEGreaterThanEquals:
            *result = (compareResult >= 0);
            break;
        
        default:
            err = errAEBadTestKey;
    }
 
    return(err);
}
 
 
OSErr   MyCompareInteger(DescType oper, const AEDesc *desc1, const AEDesc *desc2, Boolean *result)
{
    long    num1, num2;
    AEDesc  longDesc = {typeNull, NULL};
    OSErr   err;
    
    // Make each number is a long integer (in case it's a short integer) before extracting the data
    err = AECoerceDesc(desc1, typeLongInteger, &longDesc);
    if (err != noErr) goto done;
    num1 = **(long **)longDesc.dataHandle;
    (void) AEDisposeDesc(&longDesc);    // We're done with longDesc, so dispose of it
    longDesc.dataHandle = NULL;         // Mark this as disposed
    
    err = AECoerceDesc(desc2, typeLongInteger, &longDesc);
    if (err != noErr) goto done;
    num2 = **(long **)longDesc.dataHandle;
    // No need to dispose of this descriptor, as the code at "done" will do it for us
    
    switch (oper)
    {
        case kAEEquals:
            *result = (num1 == num2);
            break;
        
        case kAELessThan:
            *result = (num1 < num2);
            break;
        
        case kAELessThanEquals:
            *result = (num1 <= num2);
            break;
        
        case kAEGreaterThan:
            *result = (num1 > num2);
            break;
        
        case kAEGreaterThanEquals:
            *result = (num1 >= num2);
            break;
        
        default:
            err = errAEBadTestKey;
    }
    
done:
    if (longDesc.dataHandle)
        (void) AEDisposeDesc(&longDesc);
 
    return err;
} // MyCompareInteger
 
 
OSErr   MyCompareBoolean (DescType oper, const AEDesc *desc1, const AEDesc *desc2, Boolean *result)
{
    Boolean bool1, bool2;
    OSErr   err = noErr;
    
    // Apple events defines a boolean as a 1-byte value containing 1 for TRUE and 0 for FALSE
    // We'll use a test to convert this into C's boolean notation, just to make this easier to
    // debug
    
    bool1 = (**(char **)desc1->dataHandle) != 0;
    bool2 = (**(char **)desc2->dataHandle) != 0;
    
    if (oper == kAEEquals) 
        *result = (bool1 == bool2);
    else
        err = errAEBadTestKey;      // No other tests make sense
 
    return err;
} // MyCompareBoolean