WatchMe.c

//////////
//
//  File:       WatchMe.c
//
//  Contains:   Application-specific code for WatchMe shell.
//
//  Written by: Tim Monroe
//
//  Copyright:  © 1998 by Apple Computer, Inc., all rights reserved.
//
//  Change History (most recent first):
//
//     <4>      07/30/98    rtm     added WatchMe_GetLaunchVolumeName
//     <3>      07/28/98    rtm     some clean-up; added WatchMe_EncodeURL
//     <2>      07/25/98    rtm     further enhancements; everything working pretty well now
//     <1>      07/23/98    rtm     first file
//
//  Currently, the movie controller knows how to handle absolute URLs but not relative ones.
//  Here our job is to convert a relative URL into an absolute one. By "relative" we mean that
//  a URL is relative to the location of the movie file.
//     
//////////
 
// header files
#include "WatchMe.h"
  
 
//////////
//
// WatchMe_ConvertRelativeToAbsoluteURL 
// Convert a relative URL to an absolute URL.
//
// On entry, theURLHandle is a handle to a block of memory that contains a URL,
// either absolute or relative. If it's absolute, we don't do anything. If it's
// relative, we convert the URL to an absolute one and return a handle to that
// URL in theURLHandle.
//
// On entry, theRefCon is a handle to a window object record. We use it only to
// obtain the full pathname of the movie file. You could simplify this function
// if you passed the full pathname as a parameter.
//
//////////
 
void WatchMe_ConvertRelativeToAbsoluteURL (Handle theURLHandle, long theRefCon)
{
    WindowObject            myWindowObject = (WindowObject)theRefCon;
    char                    *myMovieFilePath = NULL;
    char                    *myMovieFolderURL = NULL;
    char                    *myGivenURL = NULL;
    char                    myAbsoluteURL[MAX_PATH];
    Size                    mySize = 0;
    int                     myIndex, myCount;
    
    //////////
    //
    // copy the URL passed to this function into a C string
    //
    //////////
    
    mySize = GetHandleSize(theURLHandle);
    if (mySize <= 0)
        goto bail;
    
    myGivenURL = malloc(mySize + 1);
    if (myGivenURL == NULL)
        goto bail;
 
    BlockMove(*theURLHandle, myGivenURL, mySize);
    myGivenURL[mySize] = '\0';
    
    //////////
    //
    // if myGivenURL is already absolute, just return
    //
    //////////
    
    if (WatchMe_IsAbsoluteURL(myGivenURL))
        goto bail;
        
    //////////
    //
    // get the full pathname of the movie file
    //
    //////////
    
    if (myWindowObject == NULL)
        goto bail;
        
    myMovieFilePath = WatchMe_FSSpecToFullPath(&(**myWindowObject).fFileFSSpec);
    if (myMovieFilePath == NULL)
        goto bail;
 
    //////////
    //
    // convert the full pathname of the movie file into a URL to the folder enclosing the movie file
    //
    //////////
    
    myMovieFolderURL = WatchMe_FullPathToURL(myMovieFilePath);
    if (myMovieFolderURL == NULL)
        goto bail;
    
    // strip off the file name, to get the folder name
    myIndex = strlen(myMovieFolderURL) - 1;
    while (myMovieFolderURL[myIndex] != kWM_URLSeparator)
        myIndex--;
    
    myMovieFolderURL[myIndex] = '\0';
    
    //////////
    //
    // if we successfully arrived here, myGivenURL is a URL specified relative to myMovieFolderURL
    //
    // there are three cases to consider:
    // (1) myGivenURL is a file in a folder below the folder myMovieFolderURL
    // (2) myGivenURL is a file in a folder above the folder myMovieFolderURL
    // (3) myGivenURL is a file in the folder myMovieFolderURL
    //
    //////////
    
    myAbsoluteURL[0] = '\0';
    
    if ((myGivenURL[0] == '.') && (myGivenURL[1] == kWM_URLSeparator)) {
    
        //////////
        //
        // (1) myGivenURL is a file in a folder below the folder myMovieFolderURL
        //
        // we assume that myGivenURL begins with the string "./" and that the remainder
        // of myGivenURL is a partial URL path with no further "./" or "../" constructs
        //
        //////////
        
        // take all of the existing myMovieFolderURL 
        strcat(myAbsoluteURL, myMovieFolderURL);
        
        // strip off the initial '.' and append the rest of myGivenURL to myAbsoluteURL
        strcat(myAbsoluteURL, &myGivenURL[1]);
 
    } else if ((myGivenURL[0] == '.') && (myGivenURL[1] == '.') && (myGivenURL[2] == kWM_URLSeparator)) {
    
        //////////
        //
        // (2) myGivenURL is a file in a folder above the folder myMovieFolderURL
        //
        // we assume that myGivenURL begins with one or more consecutive occurrences of
        // the string "../" and that the remainder of myGivenURL is a partial URL path
        // with no further "./" or "../" constructs
        //
        //////////
        
        myIndex = 0;
        while ((myGivenURL[myIndex] == '.') && (myGivenURL[myIndex + 1] == '.') && (myGivenURL[myIndex + 2] == kWM_URLSeparator)) {
 
            // truncate myMovieFolderURL at the rightmost path separator
            myCount = strlen(myMovieFolderURL) - 1;
            while (myMovieFolderURL[myCount] != kWM_URLSeparator)
                myCount--;
            myMovieFolderURL[myCount] = '\0';
            
            myIndex += 3;
        }
        
        // concatenate the paths for an absolute URL
        strcat(myAbsoluteURL, myMovieFolderURL);
        strcat(myAbsoluteURL, &myGivenURL[myIndex - 1]);
 
    } else {
    
        //////////
        //
        // (3) myGivenURL is a file in the folder myMovieFolderURL
        //
        //////////
    
        // concatenate the paths for an absolute URL
        strcat(myAbsoluteURL, myMovieFolderURL);        
        strcat(myAbsoluteURL, "/");
        strcat(myAbsoluteURL, myGivenURL);
        
    }
    
    //////////
    //
    // return the absolute URL to the caller
    //
    //////////
    
    mySize = (Size)strlen(myAbsoluteURL);
    SetHandleSize(theURLHandle, mySize);
    if (theURLHandle != NULL)
        BlockMove(myAbsoluteURL, *theURLHandle, mySize);
 
bail:
    // dispose of any storage we allocated
    if (myMovieFilePath != NULL)
        free(myMovieFilePath);
    if (myMovieFolderURL != NULL)
        free(myMovieFolderURL);
    if (myGivenURL != NULL)
        free(myGivenURL);
}
 
 
//////////
//
// WatchMe_GetLaunchVolumeName 
// Return the name of the volume from which this application was launched,
// in a form that can be embedded in a full pathname.
// 
// On Macintosh, this is just the volume's name; on Windows, this is the
// drive letter and colon (and NOT the volume's name).
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////
 
char *WatchMe_GetLaunchVolumeName (void)
{
    char                    *myVolName = NULL;
#if TARGET_OS_MAC
    ProcessSerialNumber     myPSN;
    OSErr                   myErr = noErr;
#endif
 
    myVolName = malloc(MAX_PATH);
    if (myVolName == NULL)
        goto bail;
        
    myVolName[0] = '\0';
 
    //////////
    //
    // on Windows, we can use GetCurrentDirectory to get the name of the current directory
    //
    //////////
#if TARGET_OS_WIN32
    
    GetCurrentDirectory(MAX_PATH, myVolName);
        
#if TESTING_ON_NONCD
    myVolName[0] = 'E';     // just testing on my local machine, which uses "E" for the CD-ROM
#else
    myVolName[0] = toupper(myVolName[0]);
#endif
    
    // add the ":" that terminates drive names
    myVolName[1] = ':';
    myVolName[2] = '\0';
 
#endif // TARGET_OS_WIN32
 
    //////////
    //
    // on Macintosh, we'll use the Process Manager
    //
    //////////
#if TARGET_OS_MAC
    myErr = GetCurrentProcess(&myPSN);
    if (myErr == noErr) {
        ProcessInfoRec      myInfo;
        FSSpec              myFSSpec;
        int                 myPos = 0;
    
        myInfo.processInfoLength = sizeof(ProcessInfoRec);
        myInfo.processName = NULL;
        myInfo.processAppSpec = &myFSSpec;
    
        myErr = GetProcessInformation(&myPSN, &myInfo);
        if (myErr == noErr) {
            // get the full pathname of the application file
            myVolName = WatchMe_FSSpecToFullPath(myInfo.processAppSpec);
            
            // truncate before the first path separator
            myPos = strcspn(myVolName, ":");
            myVolName[myPos] = '\0';
        }
    }
    
#if TESTING_ON_NONCD
    myVolName[0] = '\0';
    strcat(myVolName, "QT3SDK");
#endif
 
#endif // TARGET_OS_MAC
 
bail:
    return(myVolName);
}
 
 
//////////
//
// WatchMe_FSSpecToFullPath 
// Convert an FSSpec into a full native path name.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////
 
static char *WatchMe_FSSpecToFullPath (const FSSpec *theFSSpec)
{
    char        *myPathName = NULL;
#if TARGET_OS_MAC   
    Handle      myHandle = NULL;
    short       myLength;
#endif
 
    if (theFSSpec == 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(theFSSpec, myPathName, MAX_PATH, kFullNativePath);   
#elif TARGET_OS_MAC 
    // on Macintosh, this is easy (thanks to that hardworking Jim Luther)
    WatchMe_FSpGetFullPath(theFSSpec, &myLength, &myHandle);
    BlockMove(*myHandle, myPathName, myLength);
    myPathName[myLength] = '\0';
#endif
 
bail:
    return(myPathName);
}
 
 
//////////
//
// WatchMe_FullPathToURL 
// Convert a full native pathname into an absolute local URL.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////
 
static char *WatchMe_FullPathToURL (char *thePath)
{
    char        *myURL = NULL;  
    UInt16      myIndex;
 
    if (thePath == NULL)
        goto bail;
 
    myURL = malloc(MAX_PATH);
    if (myURL == NULL)
        goto bail;
 
    //////////
    //
    // convert all native path separators into the URL path separator
    //
    //////////
    
    for (myIndex = 0; myIndex < strlen(thePath); myIndex++)
        if (thePath[myIndex] == kWM_PathSeparator)
            thePath[myIndex] = kWM_URLSeparator;
 
    //////////
    //
    // convert any special characters in the URL path into their encoded versions;
    // in theory, the URLs we are passed should already be encoded, but we'll make
    // sure before passing the URL to the movie controller
    //
    //////////
    
    thePath = WatchMe_EncodeURL(thePath);
    
    //////////
    //
    // prepend the appropriate URL head
    //
    //////////
 
    myURL[0] = '\0';
    strcat(myURL, "file:///");
 
    //////////
    //
    // append the converted path name to the URL
    //
    //////////
 
    strcat(myURL, thePath);
    
bail:
    return(myURL);
}
 
 
//////////
//
// WatchMe_EncodeURL 
// Convert any special characters in the specified URL path into their encoded versions.
//
// Currently, only the space character ' ' is treated specially.
//
// Note that this encoding might cause the size of the string to increase; in that case,
// we'll allocate some new storage and free theURL before returning.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////
 
static char *WatchMe_EncodeURL (char *theURL)
{
    char        *myEncodedURL = NULL;   
    char        *mySpecialChar = NULL;  
    UInt16      myPos, myIndex;
 
    if (theURL == NULL)
        goto bail;
 
    // determine whether we actually need to do any work
    mySpecialChar = strchr(theURL, ' ');
    if (mySpecialChar == NULL) {
        myEncodedURL = theURL;          // return the string we were passed
        goto bail;
    }
    
    // theURL contains one or more special characters that need to be encoded;
    // allocate a new string into which we can write the encoded URL
    myEncodedURL = malloc(MAX_PATH);
    if (myEncodedURL == NULL)
        goto bail;
 
    myPos = 0;
    for (myIndex = 0; myIndex < strlen(theURL); myIndex++) {
        if (theURL[myIndex] == ' ') {
            char    myChar;
            
            myEncodedURL[myPos + 0] = '%';          
            myChar = theURL[myIndex] >> 4;
            myEncodedURL[myPos + 1] = myChar + ((myChar <= 9) ? '0' : ('A' - 10));
            myChar = theURL[myIndex] & 0xF;
            myEncodedURL[myPos + 2] = myChar + ((myChar <= 9) ? '0' : ('A' - 10));
            myPos += 3;     
        } else {
            myEncodedURL[myPos] = theURL[myIndex];
            myPos++;        
        }
    }
    
    myEncodedURL[myPos] = '\0';
    free(theURL);
    
bail:
    return(myEncodedURL);
}
 
 
//////////
//
// WatchMe_IsAbsoluteURL 
// Determine whether the specified URL is absolute.
//
//////////
 
static Boolean WatchMe_IsAbsoluteURL (char *theURL)
{
    return(theURL[0] != '.');
}
 
 
//////////
//
// WatchMe_FSpGetFullPath 
// 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.
//
// NOTE: This function is MACINTOSH ONLY.
//
//////////
 
static OSErr WatchMe_FSpGetFullPath (const FSSpec *spec, short *fullPathLength, Handle *fullPath)
{
    OSErr       result;
    FSSpec      tempSpec;
    CInfoPBRec  pb;
    
    *fullPathLength = 0;
    *fullPath = NULL;
    
    /* Make a copy of the input FSSpec that can be modified */
    BlockMoveData(spec, &tempSpec, sizeof(FSSpec));
    
    if ( tempSpec.parID == fsRtParID )
    {
        /* The object is a volume */
        
        /* Add a colon to make it a full pathname */
        ++tempSpec.name[0];
        tempSpec.name[tempSpec.name[0]] = ':';
        
        /* We're done */
        result = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]);
    }
    else
    {
        /* The object isn't a volume */
        
        /* Is the object a file or a directory? */
        pb.dirInfo.ioNamePtr = tempSpec.name;
        pb.dirInfo.ioVRefNum = tempSpec.vRefNum;
        pb.dirInfo.ioDrDirID = tempSpec.parID;
        pb.dirInfo.ioFDirIndex = 0;
        result = PBGetCatInfoSync(&pb);
        if ( result == noErr )
        {
            /* if the object is a directory, append a colon so full pathname ends with colon */
            if ( (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0 )
            {
                ++tempSpec.name[0];
                tempSpec.name[tempSpec.name[0]] = ':';
            }
            
            /* Put the object name in first */
            result = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]);
            if ( result == noErr )
            {
                /* Get the ancestor directory names */
                pb.dirInfo.ioNamePtr = tempSpec.name;
                pb.dirInfo.ioVRefNum = tempSpec.vRefNum;
                pb.dirInfo.ioDrParID = tempSpec.parID;
                do  /* loop until we have an error or find the root directory */
                {
                    pb.dirInfo.ioFDirIndex = -1;
                    pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID;
                    result = PBGetCatInfoSync(&pb);
                    if ( result == noErr )
                    {
                        /* Append colon to directory name */
                        ++tempSpec.name[0];
                        tempSpec.name[tempSpec.name[0]] = ':';
                        
                        /* Add directory name to beginning of fullPath */
                        (void) Munger(*fullPath, 0, NULL, 0, &tempSpec.name[1], tempSpec.name[0]);
                        result = MemError();
                    }
                } while ( (result == noErr) && (pb.dirInfo.ioDrDirID != fsRtDirID) );
            }
        }
    }
    if ( result == noErr )
    {
        /* Return the length */
        *fullPathLength = GetHandleSize(*fullPath);
    }
    else
    {
        /* Dispose of the handle and return NULL and zero length */
        if ( *fullPath != NULL )
        {
            DisposeHandle(*fullPath);
        }
        *fullPath = NULL;
        *fullPathLength = 0;
    }
    
    return ( result );
}