MoreTextUtils/MoreTextUtils.cp

/*
    File:       MoreTextUtils.cp
 
    Contains:   
 
    Written by: Pete Gontier
 
    Copyright:  Copyright (c) 1998 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):
 
         <6>     20/3/00    Quinn   Added MoreReplaceText and its support routines.  Added
                                    MoreStrLen.  Tidied up a little.
         <5>      1/3/00    Quinn   Minor Carbonation changes.
         <4>     21/4/99    Quinn   Added ValidStringListHandle.
         <3>     2/15/99    PCG     InlineGetHandleSize loses the Inline
         <2>    11/11/98    PCG     fix header
         <1>    11/10/98    PCG     first big re-org at behest of Quinn
 
    Old Change History (most recent first):
 
         <2>    10/23/98    PCG     add GetStringFromLongDouble
         <1>     6/16/98    PCG     initial checkin
*/
 
/////////////////////////////////////////////////////////////////
 
// MoreIsBetter Setup
 
#include "MoreSetup.h"
 
// Our Prototypes
 
#include "MoreTextUtils.h"
 
// Mac OS Interfaces
 
#include <Resources.h>
#include <PLStringFuncs.h>
#include <Fonts.h>
 
// MIB Prototypes
 
#include "MoreMemory.h"
#include "MoreQuickDraw.h"
 
/////////////////////////////////////////////////////////////////
 
pascal OSErr NewStringPtr (ConstStr255Param init, UInt8 maxSize, StringPtr *result)
{
    OSErr err = noErr;
 
    if (!MoreAssert (result && (!init || (*init <= maxSize))))
        err = paramErr;
    else
    {
        *result = StringPtr (NewPtr (1 + (maxSize ? maxSize : (init ? *init : 255))));
 
        if (!*result)
            err = MemError ( );
        else if (init && *init)
            PLstrcpy (*result,init);
        else
            **result = 0;
    }
 
    return err;
}
 
pascal OSErr NewStringListHandle (Handle *h)
{
    DebugStr ("\pThis routine has never been traced.");
 
    if (!MoreAssert (h))
        return nilHandleErr;
    *h = NewHandleClear (sizeof (UInt16));
    if (!*h)
        return MemError ( );
    return noErr;
}
 
pascal OSErr AppendStringToListHandle (ConstStr255Param str, Handle h)
{
    DebugStr ("\pThis routine has never been traced.");
 
    OSErr err = noErr;
 
    if (!MoreAssert (h && *h))
        err = nilHandleErr;
    else
    {
        UInt8 dummyZero;
 
        if (!str)
        {
            dummyZero = 0;
            str = &dummyZero;
        }
 
        if (!(err = PtrAndHand (str, h, *str + 1)))
            (** (UInt16 **) h) += 1;
    }
 
    return err;
}
 
pascal OSErr GetNewStringList (short resID, tStringListP *newStringList)
{
    OSErr err = noErr;
 
    Handle h = GetResource ('STR#',resID);
 
    if (!h)
    {
        err = ResError ( );
        if (!err) err = resNotFound;
    }
    else
    {
        Size handleSize = GetHandleSize (h);
        (void) MoreAssert (MemError ( ) == noErr);
 
        if (handleSize < 2)
            err = paramErr;
        else
        {
            UInt16  stringCount     = ** (UInt16 **) h;
            Size    stringListSize  = sizeof (**newStringList) + (handleSize - 2) +
                                        (stringCount * sizeof (StringPtr));
 
            *newStringList = (tStringListP) NewPtr (stringListSize);
 
            if (!*newStringList)
                err = MemError ( );
            else
            {
                (*newStringList)->count = stringCount;
 
                if (stringCount)
                {
                    UInt16 index = 0;
 
                    StringPtr stringScan = (StringPtr) ((*newStringList)->list + stringCount);
 
                    BlockMoveData (2 + *h, stringScan, handleSize - 2);
 
                    do
                    {
                        (*newStringList)->list [index] = stringScan;
                        stringScan += *stringScan + 1;
                    }
                    while (++index < stringCount);
                }
            }
        }
 
        ReleaseResource (h);
        (void) MoreAssert (ResError ( ) == noErr);
    }
 
    return err;
}
 
pascal Boolean ValidStringListHandle(Handle stringList)
    // See comment in interface part.
{
    Boolean result;
    UInt16 stringCount;
    UInt16 stringIndex;
    UInt8* cursor;
    UInt8* bound;
    ByteCount stringListLength;
 
    result = true;
    if (stringList == NULL || *stringList == NULL || GetHandleSize(stringList) < sizeof(UInt16) ) {
        result = false;
    }
    if (result) {
        stringCount = *((UInt16 *) *stringList);
        
        stringListLength = GetHandleSize(stringList);
        
        // From here down we have to make sure we do nothing to move
        // or purge until we're done with cursor and bound.
        
        cursor = ((UInt8 *)*stringList) + sizeof(UInt16);
        bound  = ((UInt8 *)*stringList) + stringListLength;
        for (stringIndex = 0; stringIndex < stringCount; stringIndex++) {
            if ( cursor < bound ) {
                cursor += *cursor + 1;
            } else {
                result = false;
                break;
            }
        }
        
        if (result) {
            if ( cursor != bound ) {
                result = false;
            }
        }
    }
    
    return result;
}
 
pascal OSErr GetPascalStringFromLongDouble (long double d, SInt8 precision, StringPtr buf)
{
    OSErr err = noErr;
 
    *buf = 0;
 
    //  If client requests more precision than is available, bitch.
 
    if (!MoreAssert (precision < SIGDIGLEN))
        err = paramErr;
    else
    {
        //  If precision is less than 0, client is requesting
        //  as much precision as is available.
 
        if (precision < 0)
            precision = SIGDIGLEN;
 
        //  Try normal notation, and if that overflows,
        //  fall back to scientific notation (bleah!).
 
        decimal dec;
        decform df = { 1, 0, precision };
 
        num2dec (&df,d,&dec);
 
        if (dec.sig.text [0] == '?')
        {
            df.style    = 0;
            df.digits   = SIGDIGLEN;
 
            num2dec (&df,d,&dec);
        }
 
        if (*(dec.sig.text) == '0')
            PLstrcpy (buf,"\p0");
        else if (*(dec.sig.text) == 'I')
            PLstrcpy (buf,"\p[INF]");
        else if (*(dec.sig.text) == 'N')
            PLstrcpy (buf,"\p[NaN]");
        else
        {
            //  Finally, convert it to a pascal string...
 
            dec2str (&df,&dec,(Ptr)buf);
            CopyCStringToPascal( (char *) buf, buf );
 
            //  ...and trim trailing zeroes.
 
//          if (df.style == 1 && dec.exp < 0)
//              while (buf [*buf] == '0')
//                  *buf -= 1;
        }
    }
 
    return err;
}
 
// gDummyPort is a pointer to a dummy GrafPort whose font is always
// the system font.  By setting thePort to this port, we can then safely
// call Script Manager that get script information from the current port.
 
static GrafPtr gDummyPort;
 
extern pascal OSStatus InitMoreTextUtils(void)
    // See comment in header.
{
    OSStatus err;
    GrafPtr  oldPort;
 
    // Preserve the current port.
        
    GetPort(&oldPort);
    
    // Create gDummyPort.
    
    gDummyPort = (GrafPtr) CreateNewPort();
    err = MoreMemError(gDummyPort);
    if (err == noErr) {
    
        // CreateNewPort, if it follows the semantics of the old
        // OpenPort routine, should set thePort to the new port.
        // This assert ensures that behaviour, because the following
        // call to TextFont relies on it.
        
        #if MORE_DEBUG
            {
                GrafPtr junkPort;
                
                GetPort(&junkPort);
                MoreAssertQ(junkPort == gDummyPort);
            }
        #endif
 
        // Set the dummy portÕs font to the system font.
                
        TextFont(systemFont);
    }
    
    SetPort(oldPort);
    
    return err;
}
 
extern pascal void TermMoreTextUtils(void)
    // See comment in header.
{
    if (gDummyPort != nil) {
        DisposePort( (CGrafPtr) gDummyPort);
        gDummyPort = nil;
    }
}
 
extern pascal void MoreReplaceText(Str255 baseText, ConstStr255Param substitutionText, ConstStr15Param key)
    // See comment in header.
{
    OSStatus err;
    GrafPtr  oldPort;
    Handle   baseTextH;
    Handle   substitutionTextH;
    Size     newLength;
    UInt8    *tmpKey;
 
    MoreAssertQ(baseText != nil);
    MoreAssertQ(substitutionText != nil);
    MoreAssertQ(key != nil);
    MoreAssertQ(key[0] <= 15);
    
    GetPort(&oldPort);
    
    baseTextH = nil;
    substitutionTextH = nil;
 
    // Copy the strings into handles, call ReplaceText, then
    // copy the result back into baseText (making sure that
    // there is at most 255 bytes in the resulting string).
        
    err = noErr;
    if (gDummyPort == nil) {
        err = InitMoreTextUtils();
    }
    if (err == noErr) {
        SetPort(gDummyPort);
 
        err = PtrToHand(&baseText[1], &baseTextH, baseText[0]);
    }
    if (err == noErr) {
        err = PtrToHand(&substitutionText[1], &substitutionTextH, substitutionText[0]);
    }
    if (err == noErr) {
        tmpKey = (UInt8 *) key;
        err = ReplaceText(baseTextH, substitutionTextH, tmpKey);
        if (err >= 0) {
            newLength = GetHandleSize(baseTextH);
            if (newLength > 255) {
                newLength = 255;
            }
            baseText[0] = newLength;
            BlockMoveData(*baseTextH, &baseText[1], newLength);
            err = noErr;
        }
    }
    
    // Clean up.
 
    if (baseTextH != nil) {
        DisposeHandle(baseTextH);
        MoreAssertQ(MemError() == noErr);
    }   
    if (substitutionTextH != nil) {
        DisposeHandle(substitutionTextH);
        MoreAssertQ(MemError() == noErr);
    }   
    SetPort(oldPort);
    
    MoreAssert(err == noErr);
}
 
extern pascal ByteCount MoreStrLen(const char *str)
    // See comment in header.
{
    ByteCount result;
    
    result = 0;
    while ( *str != 0 ) {
        result += 1;
        str += 1;
    }
    return result;
}