Common Files/URLUtilities.c

//////////
//
//  File:       URLUtilities.c
//
//  Contains:   Some utilities for working with URLs.
//              All utilities start with the prefix "URLUtils_".
//
//  Written by: Tim Monroe
//
//  Copyright:  © 1998 by Apple Computer, Inc., all rights reserved.
//
//  Change History (most recent first):
//
//     <7>      02/09/00    rtm     removed "static" keyword from URLUtils_EncodeString and _Decode;
//                                  added URLUtils_IsUnsafeChar; tweaked definitions of other character
//                                  functions per RFC 1738
//     <6>      02/22/99    rtm     added URLUtils_HaveBrowserOpenURL
//     <5>      12/26/98    rtm     added URLUtils_LocationFromFullPath
//     <4>      12/23/98    rtm     added URLUtils_FSSpecToFullNativePath, URLUtils_FSSpecToURL, and
//                                  URLUtils_URLToFSSpec; fixed bug in URLUtils_DecodeString
//     <3>      12/07/98    rtm     modified URLUtils_NewMovieFromURL to take flags and ID parameters;
//                                  added URLUtils_URLToFullNativePath
//     <2>      12/06/98    rtm     more work; finished URLUtils_FullNativePathToURL
//     <1>      12/04/98    rtm     first file
//
//  The structure of URLs assumed in these functions is based on that specified in
//  the Network Working Group RFC 2396 (August 1998) by Tim Berners-Lee (who else?)
//  et al. In addition, I have adopted the terminology used in that document.
//
//  The basic structure of an absolute URL is this:
//
//                      <scheme>://<authority><path>?<query>
//   
//////////
 
// header files
 
#ifndef __URLUtilities__
#include "URLUtilities.h"
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Syntax utilities.
//
// Use these functions to retrieve the distinguishable parts of a URL.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
//////////
//
// URLUtils_GetScheme
// Return the scheme portion of the specified (absolute) URL. If that URL is not absolute, return NULL.
//
// The scheme of a URL is the leading portion of a URL. It specifies the protocol to be used to access
// the named resource. RFC 2396 specifies that a scheme begin with an alphabetic character and contain
// only alphanumerics and '+', '-', '.'; we don't check for this yet.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////
 
char *URLUtils_GetScheme (char *theURL)
{
    char    *myScheme = NULL;
    short   myLength = 0;
    short   myIndex;
    
    // make sure we were passed an absolute URL
    if (!URLUtils_IsAbsoluteURL(theURL))
        return(myScheme);
 
    // find the length of the scheme
    while (theURL[myLength] != kURLSchemeSeparator)
        myLength++;
    
    myScheme = malloc(myLength + 1);
    if (myScheme != NULL) {
        for (myIndex = 0; myIndex < myLength; myIndex++)
            myScheme[myIndex] = URLUtils_ToLowercase(theURL[myIndex]);
            
        myScheme[myLength] = '\0';
    }
    
    return(myScheme);
}
 
 
//////////
//
// URLUtils_GetAuthority
// Return the naming authority portion of the specified (absolute) URL. If that URL is not absolute,
// return NULL.
//
// The authority is preceded by a double slash and is terminated by the following slash, question mark,
// or null character. In some cases, the authority portion of a URL is empty; in those cases, we return
// a non-NULL pointer to an empty string.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////
 
char *URLUtils_GetAuthority (char *theURL)
{
    char    *myAuthority = NULL;
    char    *myString = NULL;
    short   myLength = 0;
    
    // make sure we were passed an absolute URL
    if (!URLUtils_IsAbsoluteURL(theURL))
        return(myAuthority);
 
    // find the place in theURL where the authority prefix begins
    myString = URLUtils_GetAuthBegin(theURL);
    if (myString != NULL) {
        // get the length of the authority portion
        myLength = strcspn(myString + strlen(kURLAuthPrefix), kURLAuthSuffix);
        
        myAuthority = malloc(myLength + 1);
        if (myAuthority != NULL) {
            strncpy(myAuthority, myString + strlen(kURLAuthPrefix), myLength);
            myAuthority[myLength] = '\0';
        }
    }
 
    return(myAuthority);
}
 
 
//////////
//
// URLUtils_GetPath
// Return the path portion of the specified (absolute) URL. If that URL is not absolute, return NULL.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////
 
char *URLUtils_GetPath (char *theURL)
{
    char    *myPath = NULL;
    char    *myString = NULL;
    short   myLength = 0;
    
    // make sure we were passed an absolute URL
    if (!URLUtils_IsAbsoluteURL(theURL))
        return(myPath);
 
    // find the place in theURL where the path prefix begins
    myString = URLUtils_GetPathBegin(theURL);
    if (myString != NULL) {
        // get the length of the path portion
        myLength = strcspn(myString, kURLPathSuffix);
        
        myPath = malloc(myLength + 1);
        if (myPath != NULL) {
            strncpy(myPath, myString, myLength);
            myPath[myLength] = '\0';
        }
    }
 
    return(myPath);
}
 
 
//////////
//
// URLUtils_GetQuery
// Return the query portion of the specified (absolute) URL. If that URL is not absolute, return NULL.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////
 
char *URLUtils_GetQuery (char *theURL)
{
    char    *myQuery = NULL;
    char    *myString = NULL;
    
    // make sure we were passed an absolute URL
    if (!URLUtils_IsAbsoluteURL(theURL))
        return(myQuery);
 
    // find the place in theURL where the query begins
    myString = URLUtils_GetQueryBegin(theURL);
    if (myString != NULL) {
        myQuery = malloc(strlen(myString) + 1);
        if (myQuery != NULL) {
            strcpy(myQuery, myString);
            myQuery[strlen(myString)] = '\0';
        }
    }
 
    return(myQuery);
}
 
 
//////////
//
// URLUtils_GetAuthBegin
// Return a pointer to the beginning of the authority portion of the specified URL.
//
//////////
 
static char *URLUtils_GetAuthBegin (char *theURL)
{
    return(strstr(theURL, kURLAuthPrefix));
}
 
 
//////////
//
// URLUtils_GetPathBegin
// Return a pointer to the beginning of the path portion of the specified URL.
//
//////////
 
static char *URLUtils_GetPathBegin (char *theURL)
{
    char    *myString = NULL;
    short   myLength = 0;
    
    myString = URLUtils_GetAuthBegin(theURL);
    if (myString != NULL) {
        // get the length of the authority portion
        myLength = strcspn(myString + strlen(kURLAuthPrefix), kURLAuthSuffix);
        
        // determine the beginning of the path portion
        myString += (myLength + strlen(kURLAuthPrefix));
    }
    
    return(myString);
}
 
 
//////////
//
// URLUtils_GetQueryBegin
// Return a pointer to the beginning of the query portion of the specified URL.
//
//////////
 
static char *URLUtils_GetQueryBegin (char *theURL)
{
    char    *myString = NULL;
    
    myString = strchr(theURL, kURLQuerySeparator);
    if (myString != NULL)
        myString++;         // to skip over the query separator
    
    return(myString);
}
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// FSSpec/URL/Pathname conversion utilities.
//
// Use these functions to convert among FSSpecs, URLs, and pathnames.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
//////////
//
// URLUtils_FullNativePathToURL 
// Convert a full native pathname into a local file URL.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////
 
char *URLUtils_FullNativePathToURL (char *thePath)
{
    char        *myURL = NULL;  
    char        *myScratchStr = NULL;   
    char        *myEncodedStr = NULL;   
    short       myIndex;
    
    if (thePath == NULL)
        goto bail;
 
    //////////
    //
    // transform thePath as required by the target operating system
    //
    // on MacOS, a full pathname is of the form <vol>:<dir>:...:<dir>:<name>
    // to convert this into a form appropriate to URLs, we need only convert
    // the colon (:) into the URL separator (/)
    //
    // on Windows, a full pathname has the form <vol>:\<dir>\<dir>\...\<name>
    // to convert this into a form appropriate to URLs, we need to convert
    // the colon (:) into '|' and the backslash (\) into the URL separator (/)
    //
    //////////
    
    myScratchStr = malloc(strlen(thePath) + 1);
    if (myScratchStr == NULL)
        goto bail;
    
    for (myIndex = 0; myIndex <= strlen(thePath); myIndex++)
        if (thePath[myIndex] == kFilePathSeparator)
            myScratchStr[myIndex] = kURLPathSeparator;
#if TARGET_OS_WIN32
        else if (thePath[myIndex] == kWinVolumeNameChar)
            myScratchStr[myIndex] = kURLVolumeNameChar;
#endif
        else
            myScratchStr[myIndex] = thePath[myIndex];
 
    //////////
    //
    // encode the transformed string
    //
    //////////
    
    myEncodedStr = URLUtils_EncodeString(myScratchStr);
    if (myEncodedStr == NULL)
        goto bail;
 
    myURL = malloc(strlen(myEncodedStr) + strlen(kFilePrefix) + strlen(kLocalhostAuth) + strlen("/") + 1);
    if (myURL == NULL)
        goto bail;
        
    //////////
    //
    // prepend the appropriate URL head
    //
    //////////
    
    myURL[0] = '\0';
    strcat(myURL, kFilePrefix);
    strcat(myURL, kLocalhostAuth);
    strcat(myURL, "/");
 
    //////////
    //
    // append the converted and encoded path name to the URL head
    //
    //////////
    
    strcat(myURL, myEncodedStr);
    
bail:
    free(myScratchStr);
    free(myEncodedStr);
        
    return(myURL);
}
 
 
//////////
//
// URLUtils_URLToFullNativePath 
// Convert a local file URL into a full native pathname.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////
 
char *URLUtils_URLToFullNativePath (char *theURL)
{
    char        *myScratchStr = NULL;   
    char        *myDecodedStr = NULL;   
    short       myIndex;
 
    if (theURL == NULL)
        goto bail;
 
    // make sure we were passed a file URL; the URL must begin with the file prefix
    myScratchStr = strstr(theURL, kFilePrefix);
    if ((myScratchStr == NULL) || (myScratchStr != theURL))
        goto bail;
    
    // strip off the URL head
    myScratchStr += strlen(kFilePrefix);
    
    // strip off the authority portion, if it's non-empty
    if (strstr(theURL, kLocalhostStr) == myScratchStr)
        myScratchStr += strlen(kLocalhostStr);
        
    // strip off the authority portion, if it's just '/'
    if (myScratchStr[0] == kURLPathSeparator)
        myScratchStr++;
    
    // decode the path string
    myDecodedStr = URLUtils_DecodeString(myScratchStr);
    if (myDecodedStr == NULL)
        goto bail;
 
    // transform the decoded path as required by the target operating system
    for (myIndex = 0; myIndex <= strlen(myDecodedStr); myIndex++)
        if (myDecodedStr[myIndex] == kURLPathSeparator)
            myDecodedStr[myIndex] = kFilePathSeparator;
#if TARGET_OS_WIN32
        else if (myDecodedStr[myIndex] == kURLVolumeNameChar)
            myDecodedStr[myIndex] = kWinVolumeNameChar;
#endif
 
bail:
    return(myDecodedStr);
}
 
 
//////////
//
// URLUtils_FullNativePathToFSSpec
// Convert a full native pathname into an FSSpec.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////
 
FSSpecPtr URLUtils_FullNativePathToFSSpec (char *thePath)
{
    FSSpecPtr       myFSSpecPtr = malloc(sizeof(FSSpec));
    StringPtr       myStringPtr = NULL;
    
    if (myFSSpecPtr == NULL)
        return(myFSSpecPtr);
        
    myStringPtr = URLUtils_ConvertCToPascalString(thePath);
 
    if (strlen(thePath) < 255)
        FSMakeFSSpec(0, 0L, myStringPtr, myFSSpecPtr);
    else
        URLUtils_LocationFromFullPath(strlen(thePath), thePath, myFSSpecPtr);
        
    free(myStringPtr);
    
    return(myFSSpecPtr);
}
 
 
//////////
//
// URLUtils_FSSpecToFullNativePath
// Convert an FSSpec into a full native pathname.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////
 
char *URLUtils_FSSpecToFullNativePath (const FSSpecPtr theFSSpecPtr)
{
    char            *myPathName = NULL;
#if TARGET_OS_MAC   
    Handle          myHandle = NULL;
    short           myLength = 0;
#endif
 
    if (theFSSpecPtr == NULL)
        goto bail;
 
    myPathName = malloc(MAX_PATH);
    if (myPathName == NULL)
        goto bail;
 
#if TARGET_OS_WIN32
    // on Windows, this is easy (thanks to those hardworking QuickTime engineers)
    FSSpecToNativePathName(theFSSpecPtr, myPathName, MAX_PATH, kFullNativePath);    
#elif TARGET_OS_MAC 
    // on Macintosh, this is easy (thanks to that hardworking Jim Luther)
    URLUtils_FSpecGetFullPath(theFSSpecPtr, &myLength, &myHandle);
    BlockMove(*myHandle, myPathName, myLength);
    myPathName[myLength] = '\0';
    
    DisposeHandle(myHandle);
#endif
 
bail:
    return(myPathName);
}
 
 
//////////
//
// URLUtils_FSSpecToURL
// Convert an FSSpec into a local file URL.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////
 
char *URLUtils_FSSpecToURL (const FSSpecPtr theFSSpecPtr)
{
    char        *myPathName = NULL;
    char        *myURL = NULL;
    
    myPathName = URLUtils_FSSpecToFullNativePath(theFSSpecPtr);
    myURL = URLUtils_FullNativePathToURL(myPathName);
    
    free(myPathName);
    return(myURL);
}
 
 
//////////
//
// URLUtils_URLToFSSpec
// Convert a local file URL into an FSSpec.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////
 
FSSpecPtr URLUtils_URLToFSSpec (char *theURL)
{
    char        *myPathName = NULL;
    FSSpecPtr   myFSSpecPtr = malloc(sizeof(FSSpec));
    
    if (myFSSpecPtr == NULL)
        return(myFSSpecPtr);
 
    myPathName = URLUtils_URLToFullNativePath(theURL);
    myFSSpecPtr = URLUtils_FullNativePathToFSSpec(myPathName);
    
    free(myPathName);
    return(myFSSpecPtr);
}
 
 
//////////
//
// URLUtils_FSpecGetFullPath 
// Get a full path name from an FSSpec.
//
// This is straight out of MoreFiles 1.4 by Jim Luther; the only thing I did was to change the name
// and make the coding style consistent with the rest of this file.
//
// NOTE: This function is MACINTOSH ONLY.
//
//////////
 
static OSErr URLUtils_FSpecGetFullPath (const FSSpecPtr theFSSpecPtr, short *theFullPathLength, Handle *theFullPath)
{
    FSSpec          myTempSpec;
    CInfoPBRec      myPBRec;
    OSErr           myErr = noErr;
    
    *theFullPathLength = 0;
    *theFullPath = NULL;
    
    // make a copy of the input FSSpec that can be modified
    BlockMoveData(theFSSpecPtr, &myTempSpec, sizeof(FSSpec));
    
    if (myTempSpec.parID == fsRtParID) {
        // the object is a volume; add a colon to make it a full pathname
        ++myTempSpec.name[0];
        myTempSpec.name[myTempSpec.name[0]] = kFilePathSeparator;
        
        // we're done
        myErr = PtrToHand(&myTempSpec.name[1], theFullPath, myTempSpec.name[0]);
    } else {
        // the object isn't a volume; is the object a file or a directory?
        myPBRec.dirInfo.ioNamePtr = myTempSpec.name;
        myPBRec.dirInfo.ioVRefNum = myTempSpec.vRefNum;
        myPBRec.dirInfo.ioDrDirID = myTempSpec.parID;
        myPBRec.dirInfo.ioFDirIndex = 0;
        myErr = PBGetCatInfoSync(&myPBRec);
        if (myErr == noErr) {
            // if the object is a directory, append a colon so full pathname ends with colon
            if ((myPBRec.hFileInfo.ioFlAttrib & ioDirMask) != 0) {
                ++myTempSpec.name[0];
                myTempSpec.name[myTempSpec.name[0]] = kFilePathSeparator;
            }
            
            // put the object name in first
            myErr = PtrToHand(&myTempSpec.name[1], theFullPath, myTempSpec.name[0]);
            if (myErr == noErr) {
                // get the ancestor directory names
                myPBRec.dirInfo.ioNamePtr = myTempSpec.name;
                myPBRec.dirInfo.ioVRefNum = myTempSpec.vRefNum;
                myPBRec.dirInfo.ioDrParID = myTempSpec.parID;
                
                // loop until we have an error or find the root directory
                do {
                    myPBRec.dirInfo.ioFDirIndex = -1;
                    myPBRec.dirInfo.ioDrDirID = myPBRec.dirInfo.ioDrParID;
                    myErr = PBGetCatInfoSync(&myPBRec);
                    if (myErr == noErr) {
                        // append colon to directory name
                        ++myTempSpec.name[0];
                        myTempSpec.name[myTempSpec.name[0]] = kFilePathSeparator;
                        
                        // add directory name to beginning of theFullPath
                        (void)Munger(*theFullPath, 0, NULL, 0, &myTempSpec.name[1], myTempSpec.name[0]);
                        myErr = MemError();
                    }
                } while ((myErr == noErr) && (myPBRec.dirInfo.ioDrDirID != fsRtDirID));
            }
        }
    }
    if (myErr == noErr) {
        // return the length
        *theFullPathLength = GetHandleSize(*theFullPath);
    } else {
        // dispose of the handle and return NULL and zero length
        if (*theFullPath != NULL)
            DisposeHandle(*theFullPath);
        *theFullPath = NULL;
        *theFullPathLength = 0;
    }
    
    return(myErr);
}
 
 
//////////
//
// URLUtils_LocationFromFullPath 
// Get a full path name from an FSSpec.
//
// This is straight out of MoreFiles 1.4 by Jim Luther; the only thing I did was to change the name
// and make the coding style consistent with the rest of this file.
//
//////////
 
static OSErr URLUtils_LocationFromFullPath (short theFullPathLength, const void *theFullPath, FSSpecPtr theFSSpecPtr)
{
    AliasHandle     myAliasHandle;
    Boolean         myWasChanged;
    Str32           myString;
    OSErr           myErr;
    
    // create a minimal alias from the full pathname
    myString[0] = 0;    // null string to indicate no zone or server name
    myErr = NewAliasMinimalFromFullPath(theFullPathLength, theFullPath, myString, myString, &myAliasHandle);
    if (myErr == noErr) {
        // let the Alias Manager resolve the alias
        myErr = ResolveAlias(NULL, myAliasHandle, theFSSpecPtr, &myWasChanged);
        DisposeHandle((Handle)myAliasHandle);
    }
    
    return(myErr);
}
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Relative URL utilities.
//
// Use these functions to manage relative URLs.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
// TO BE PROVIDED
 
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Movie utilities.
//
// Use these functions to open movies or web pages addressed by URLs.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
//////////
//
// URLUtils_NewMovieFromURL
// Open the movie file referenced by the specified uniform resource locator (URL).
//
//////////
 
Movie URLUtils_NewMovieFromURL (char *theURL, short theFlags, short *theID)
{
    Movie       myMovie = NULL;
    Handle      myHandle = NULL;
    Size        mySize = 0;
    
    //////////
    //
    // copy the specified URL into a handle
    //
    //////////
    
    // get the size of the URL, plus the terminating null byte
    mySize = (Size)strlen(theURL) + 1;
    if (mySize == 0)
        goto bail;
    
    // allocate a new handle
    myHandle = NewHandleClear(mySize);
    if (myHandle == NULL)
        goto bail;
 
    // copy the URL into the handle
    BlockMove(theURL, *myHandle, mySize);
 
    //////////
    //
    // instantiate a movie from the specified URL
    //
    // the data reference that is passed to NewMovieFromDataRef is a handle
    // containing the text of the URL, *with* a terminating null byte
    //
    //////////
    
    NewMovieFromDataRef(&myMovie, theFlags, theID, myHandle, URLDataHandlerSubType);
 
bail:
    if (myHandle != NULL)
        DisposeHandle(myHandle);
        
    return(myMovie);
}
 
 
//////////
//
// URLUtils_HaveBrowserOpenURL
// Tell the user's default web browser to open the specified uniform resource locator (URL).
//
//////////
 
OSErr URLUtils_HaveBrowserOpenURL (char *theURL)
{
    MovieController     myMC = NULL;
    Handle              myHandle = NULL;
    Size                mySize = 0;
    OSErr               myErr = noErr;
    
    //////////
    //
    // copy the specified URL into a handle
    //
    //////////
    
    // get the size of the URL, plus the terminating null byte
    mySize = (Size)strlen(theURL) + 1;
    if (mySize == 0)
        goto bail;
    
    // allocate a new handle
    myHandle = NewHandleClear(mySize);
    if (myHandle == NULL)
        goto bail;
 
    // copy the URL into the handle
    BlockMove(theURL, *myHandle, mySize);
 
    //////////
    //
    // instantiate a movie controller and send it an mcActionLinkToURL message
    //
    //////////
    
    myErr = OpenADefaultComponent(MovieControllerComponentType, 0, &myMC);
    if (myErr != noErr)
        goto bail;
        
    myErr = MCDoAction(myMC, mcActionLinkToURL, (void *)myHandle);
 
bail:
    if (myHandle != NULL)
        DisposeHandle(myHandle);
        
    if (myMC != NULL)
        CloseComponent(myMC);
        
    return(myErr);
}
 
 
//////////
//
// URLUtils_GetURLBasename
// Return the basename of the specified URL.
//
// The basename of a URL is the portion of the URL following the rightmost URL separator. This function
// is useful for setting window titles of movies opened using the URL data handler to the basename of a
// URL (just like MoviePlayer does).
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////
 
char *URLUtils_GetURLBasename (char *theURL)
{
    char    *myBasename = NULL;
    short   myLength = 0;
    short   myIndex;
 
    // make sure we got a URL passed in
    if (theURL == NULL)
        goto bail;
        
    // get the length of the URL
    myLength = strlen(theURL);
    
    // find the position of the rightmost URL path separator in theURL
    if (strchr(theURL, kURLPathSeparator) != NULL) {
 
        myIndex = myLength - 1;
        while (theURL[myIndex] != kURLPathSeparator)
            myIndex--;
            
        // calculate the length of the basename
        myLength = myLength - myIndex - 1;
 
    } else {
        // there is no rightmost URL path separator in theURL;
        // set myIndex so that myIndex + 1 == 0, for the call to BlockMove below
        myIndex = -1;
    }
    
    // allocate space to hold the string that we return to the caller
    myBasename = malloc(myLength + 1);
    if (myBasename == NULL)
        goto bail;
        
    // copy into myBasename the substring of theURL from myIndex + 1 to the end
    BlockMove(&theURL[myIndex + 1], myBasename, myLength);
    myBasename[myLength] = '\0';
    
bail:   
    return(myBasename);
}
 
 
//////////
//
// URLUtils_IsAbsoluteURL
// Is the specified string an absolute URL?
//
// An absolute URL must begin with an alphabetic character, and it must contain
// the URL scheme separator.
//
//////////
 
Boolean URLUtils_IsAbsoluteURL (char *theURL)
{
    if (theURL == NULL)
        return(false);
        
    if (URLUtils_IsAlphabetic(theURL[0]))
        if (strchr(theURL, kURLSchemeSeparator) != NULL)
            return(true);
 
    return(false);
}
 
 
//////////
//
// URLUtils_IsRelativeURL
// Is the specified string a relative URL?
//
//////////
 
Boolean URLUtils_IsRelativeURL (char *theURL)
{
    return(!URLUtils_IsAbsoluteURL(theURL));
}
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Character encoding utilities.
//
// Use these functions to encode or decode "escaped" characters within URLs. See RFC 1738 (found at the URL
// http://www.faqs.org/rfcs/rfc1738.html) for details on this character encoding. Here's the crucial nugget:
// "Octets must be encoded if they have no corresponding graphic character within the US-ASCII coded character
// set, if the use of the corresponding character is unsafe, or if the corresponding character is reserved for
// some other interpretation within the particular URL scheme."
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
//////////
//
// URLUtils_IsReservedChar
// Is the specified character a reserved character?
//
//////////
 
static Boolean URLUtils_IsReservedChar (char theChar)
{
    if ((theChar == ';') || (theChar == '/') || (theChar == '?') || (theChar == ':') || (theChar == '@') ||
        (theChar == '&') || (theChar == '='))
        return(true);
 
    return(false);
}
 
 
//////////
//
// URLUtils_IsDelimiterChar
// Is the specified character a delimiter?
//
//////////
 
static Boolean URLUtils_IsDelimiterChar (char theChar)
{
    if ((theChar == '<') || (theChar == '>') || (theChar == '#'))
        return(true);
 
    return(false);
}
 
 
//////////
//
// URLUtils_IsUnsafeChar
// Is the specified character an unsafe character (which might get modified during transmission)?
//
//////////
 
static Boolean URLUtils_IsUnsafeChar (char theChar)
{
    if ((theChar == '{') || (theChar == '}') || (theChar == '|') || (theChar == '\\') || (theChar == '^') ||
        (theChar == '~') || (theChar == '[') || (theChar == ']') || (theChar == '\`') || (theChar == '%'))
        return(true);
 
    return(false);
}
 
 
//////////
//
// URLUtils_IsPunctMarkChar
// Is the specified character a punctuation mark or similar symbol?
//
//////////
 
static Boolean URLUtils_IsPunctMarkChar (char theChar)
{
    if ((theChar == '-') || (theChar == '_') || (theChar == '.') || (theChar == '!') || (theChar == '~') ||
        (theChar == '*') || (theChar == '(') || (theChar == ')') || (theChar == '"') || (theChar == '\''))
        return(true);
 
    return(false);
}
 
 
//////////
//
// URLUtils_IsEncodableChar
// Is the specified character a character that should be encoded?
//
//////////
 
static Boolean URLUtils_IsEncodableChar (char theChar)
{
    // all control characters and high-ASCII characters are encodable
    if ((theChar <= 0x1f) || (theChar >= 0x7f))
        return(true);
    
    // the space character is encodable
    if (theChar == 0x20)
        return(true);
    
    // all delimiters are encodable
    if (URLUtils_IsDelimiterChar(theChar))
        return(true);
    
    // all unsafe characters are encodable
    if (URLUtils_IsUnsafeChar(theChar))
        return(true);
    
    return(false);
}
 
 
//////////
//
// URLUtils_EncodeString 
// Convert any special characters in the specified string into their encoded versions.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////
 
char *URLUtils_EncodeString (char *theString)
{
    char        *myEncodedStr = NULL;   
    char        myChar;
    short       myCount = 0;
    short       myIndex;
 
    if (theString == NULL)
        goto bail;
 
    // count the number of special characters that need converting
    for (myIndex = 0; myIndex < strlen(theString); myIndex++)
        if (URLUtils_IsEncodableChar(theString[myIndex]))
            myCount++;
 
    // allocate a character string of the proper size;
    // each encoded character increases the length of the original string by 2 bytes
    myEncodedStr = malloc(strlen(theString) + (myCount * 2) + 1);
    if (myEncodedStr == NULL)
        goto bail;
 
    // traverse the URL, encoding encodable characters as we go
    for (myCount = 0, myIndex = 0; myIndex < strlen(theString); myIndex++) {
        if (URLUtils_IsEncodableChar(theString[myIndex])) {
            myEncodedStr[myCount + 0] = kURLEscapeCharacter;
            myChar = (theString[myIndex] >> 4) & 0x0f;
            myEncodedStr[myCount + 1] = myChar + ((myChar <= 9) ? '0' : ('A' - 10));
            myChar = theString[myIndex] & 0x0f;
            myEncodedStr[myCount + 2] = myChar + ((myChar <= 9) ? '0' : ('A' - 10));
            myCount += 3;       
        } else {
            myEncodedStr[myCount] = theString[myIndex];
            myCount++;      
        }
    }
    
    myEncodedStr[myCount] = '\0';
    
bail:
    return(myEncodedStr);
}
 
 
//////////
//
// URLUtils_DecodeString 
// Convert any encoded characters in the specified string into their unencoded versions.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////
 
char *URLUtils_DecodeString (char *theString)
{
    char        *myDecodedStr = NULL;   
    char        myChar, myTemp1, myTemp2;
    short       myCount = 0;
    short       myIndex;
    short       myLength;
 
    if (theString == NULL)
        goto bail;
 
    // count the number of escape characters in the string
    for (myIndex = 0; myIndex < strlen(theString); myIndex++)
        if (theString[myIndex] == kURLEscapeCharacter)
            myCount++;
 
    // allocate a character string of the proper size;
    // each encoded character decreases the length of the original string by 2 bytes
    myLength = strlen(theString) + 1 - (myCount * 2);
    myDecodedStr = malloc(myLength);
    if (myDecodedStr == NULL)
        goto bail;
 
    // traverse the URL, decoding encoded characters as we go
    for (myCount = 0, myIndex = 0; myIndex < myLength; myIndex++) {
        if (theString[myCount] == kURLEscapeCharacter) {
        
            // make sure that any hex digits are uppercase
            myTemp1 = URLUtils_ToUppercase(theString[myCount + 1]);
            myTemp2 = URLUtils_ToUppercase(theString[myCount + 2]);
        
            myChar = ((myTemp1 - ((myTemp1 <= '9' ? '0' : 'A' - 10))) << 4) & 0xf0;
            myChar += (myTemp2 - ((myTemp2 <= '9' ? '0' : 'A' - 10))) & 0x0f;
            myDecodedStr[myIndex] = myChar;
            myCount += 3;
        } else {
            myDecodedStr[myIndex] = theString[myCount];
            myCount++;
        }
    }
    
    myDecodedStr[myLength] = '\0';
    
bail:
    return(myDecodedStr);
}
 
 
//////////
//
// URLUtils_ConvertCToPascalString
// Convert a C string into a Pascal string.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////
 
StringPtr URLUtils_ConvertCToPascalString (char *theString)
{
    StringPtr   myString = malloc(strlen(theString) + 1);
    short       myIndex = 0;
 
    while (theString[myIndex] != '\0') {
        myString[myIndex + 1] = theString[myIndex];
        myIndex++;
    }
    
    myString[0] = (unsigned char)myIndex;
    
    return(myString);
}
 
 
 
#endif  // ifndef __URLUtilities__