Source/SVAECompare.c

/*
    File:       SVAECompare.c
 
    Contains:   
 
    Written by: Original version by Jon Lansdell and Nigel Humphreys.
                3.1 updates by Greg Sutton.
 
    Copyright:  Copyright © 1995-1999 by Apple Computer, Inc., All Rights Reserved.
 
                You may incorporate this Apple sample source code into your program(s) without
                restriction. This Apple sample source code has been provided "AS IS" and the
                responsibility for its operation is yours. You are not permitted to redistribute
                this Apple sample source code as "Apple sample source code" after having made
                changes. If you're going to re-distribute the source, we require that you make
                it clear in the source that the code was descended from Apple sample source
                code, but that you've made changes.
 
    Change History (most recent first):
                7/20/1999   Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                
 
*/
#include "SVAECompare.h"
 
#include <AEPackObject.h>
#include "SVEditGlobals.h"
#include "SVEditUtils.h"
#include "SVEditAEUtils.h"
#include "SVEditWindow.h"
#include "SVEditFile.h"
#include "SVAppleEvents.h"
 
#include "SVToken.h"
#include "SVAECountElements.h"
#include "SVAEGetData.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 typeMyText:
            err = HandleGetData(&intermediateDesc, 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