/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Netscape Portable Runtime (NSPR).
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998-2000
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <string.h>
#include <Files.h>
#include <Errors.h>
#include <Folders.h>
#include <CodeFragments.h>
#include <Aliases.h>
#include <Resources.h>
#include "IterateDirectory.h" /* MoreFiles */
#include "MacErrorHandling.h"
#include "macdll.h"
#include "mdmac.h"
#include "macio.h"
#include "primpl.h"
#include "plstr.h"
/*
turds used to iterate through the directories looking
for the desired library.
*/
struct GetSharedLibraryFilterProcData
{
Boolean inRecursive;
StringPtr inName;
Boolean outFound;
CFragConnectionID outID;
Ptr outAddress;
OSErr outError;
};
typedef struct GetSharedLibraryFilterProcData GetSharedLibraryFilterProcData;
static pascal void
GetSharedLibraryFilterProc(const CInfoPBRec* const inCpb, Boolean* inWantQuit, void *inFilterData);
/*
NSGetSharedLibrary
Unfortunately CFM doesn't support user specified loader paths,
so we emulate the behavior. Effectively this is a GetSharedLibrary
where the loader path is user defined.
*/
OSErr
NSGetSharedLibrary(Str255 inLibName, CFragConnectionID* outID, Ptr* outMainAddr)
{
char* curLibPath;
char* freeCurLibPath;
OSErr tempErr;
Boolean recursive;
FSSpec curFolder;
GetSharedLibraryFilterProcData filterData;
char *endCurLibPath;
Boolean done;
filterData.outFound = false;
filterData.outID = (CFragConnectionID)(-1);
filterData.outAddress = NULL;
filterData.inName = inLibName;
freeCurLibPath = curLibPath = PR_GetLibraryPath();
if (curLibPath == NULL)
return (cfragNoLibraryErr);
tempErr = cfragNoLibraryErr;
do
{
endCurLibPath = PL_strchr(curLibPath, PR_PATH_SEPARATOR);
done = (endCurLibPath == NULL);
#if 0
// we overload the first character of a path if it's :
// then we want to recursively search that path
// see if path should be recursive
if (*curLibPath == ':')
{
// ':' is an illegal character in the name of a file
// if we start any path with this, we want to allow
// search recursively
curLibPath++;
recursive = true;
}
else
#endif
{
recursive = false;
}
if (!done)
*endCurLibPath = '\0'; // NULL terminate the string
// convert to FSSpec
tempErr = ConvertUnixPathToFSSpec(curLibPath, &curFolder);
// now look in this directory
if (noErr == tempErr)
{
filterData.inRecursive = recursive;
FSpIterateDirectory(&curFolder, recursive ? 0 : 1, &GetSharedLibraryFilterProc, &filterData);
if (filterData.outFound)
{
*outID = filterData.outID;
*outMainAddr = filterData.outAddress;
tempErr = noErr;
break;
}
else
{
tempErr = cfragNoLibraryErr;
}
}
curLibPath = endCurLibPath + 1; // skip to next path (past the '\0');
} while (!done);
free(freeCurLibPath);
return (tempErr);
}
static Boolean
LibInPefContainer(const FSSpec* inSpec, StringPtr inName, UInt32* outCodeOffset, UInt32* outCodeLength);
/*
GetSharedLibraryFilterProc
Callback to FSpIterateDirectory, finds a library with the name matching the
data in inFilterData (of type GetSharedLibraryFilterProcData). Forces a quit
when a match is found.
*/
static pascal void
GetSharedLibraryFilterProc(const CInfoPBRec* const inCpb, Boolean* inWantQuit, void *inFilterData)
{
GetSharedLibraryFilterProcData* pFilterData = (GetSharedLibraryFilterProcData*) inFilterData;
if ((inCpb->hFileInfo.ioFlAttrib & (1 << ioDirFlg)) == 0)
{
FSSpec fragSpec;
OSErr tempErr;
Str255 errName;
Boolean crap;
UInt32 codeOffset;
UInt32 codeLength;
// it's a file
// ¥ fix-me do we really want to allow all 'APPL's' for in which to find this library?
switch (inCpb->hFileInfo.ioFlFndrInfo.fdType)
{
case kCFragLibraryFileType:
case 'APPL':
tempErr = FSMakeFSSpec(inCpb->hFileInfo.ioVRefNum, inCpb->hFileInfo.ioFlParID, inCpb->hFileInfo.ioNamePtr, &fragSpec);
// this shouldn't fail
if (noErr != tempErr)
{
return;
}
// resolve an alias if this was one
tempErr = ResolveAliasFile(&fragSpec, true, &crap, &crap);
// if got here we have a shlb (or app-like shlb)
if (noErr != tempErr)
{
// probably couldn't resolve an alias
return;
}
break;
default:
return;
}
// see if this symbol is in this fragment
if (LibInPefContainer(&fragSpec, pFilterData->inName, &codeOffset, &codeLength))
tempErr = GetDiskFragment(&fragSpec, codeOffset, codeLength, fragSpec.name, kLoadCFrag, &pFilterData->outID, &pFilterData->outAddress, errName);
else
return;
// stop if we found a library by that name
if (noErr == tempErr)
{
*inWantQuit = true;
pFilterData->outFound = true;
pFilterData->outError = tempErr;
}
}
// FSpIterateDirectory will automagically call us for subsequent sub-dirs if necessary
}
/*
LibInPefContainer
Tell whether library inName is contained it the file pointed to by inSpec.
Return the codeOffset and codeLength information, for a subsequent
call to GetDiskFragment.
*/
static Boolean
LibInPefContainer(const FSSpec* inSpec, StringPtr inName, UInt32* outCodeOffset, UInt32* outCodeLength)
{
short refNum;
CFragResourceHandle hCfrg;
CFragResourceMember* pCurItem;
UInt32 curLibIndex;
Boolean found;
// asume we didn't find it
found = false;
// open the resource fork, if we can't bail
refNum = FSpOpenResFile(inSpec, fsRdPerm);
require(-1 != refNum, Exit);
// grab out the alias record, if it's not there bail
hCfrg = (CFragResourceHandle) Get1Resource(kCFragResourceType, kCFragResourceID);
require(NULL != hCfrg, CloseResourceAndExit);
HLock((Handle)hCfrg);
// get ptr to first item
pCurItem = &(*hCfrg)->firstMember;
for (curLibIndex = 0; curLibIndex < (*hCfrg)->memberCount; curLibIndex++)
{
// is this our library?
if ((pCurItem->name[0] == inName[0]) &&
(strncmp((char*) inName + 1, (char*) pCurItem->name + 1, PR_MIN(pCurItem->name[0], inName[0])) == 0))
{
*outCodeOffset = pCurItem->offset;
*outCodeLength = pCurItem->length;
found = true;
}
// skip to next one
pCurItem = (CFragResourceMember*) ((char*) pCurItem + pCurItem->memberSize);
}
HUnlock((Handle)hCfrg);
CloseResourceAndExit:
CloseResFile(refNum);
Exit:
return (found);
}
/*
NSFindSymbol
Workaround bug in CFM FindSymbol (in at least 7.5.5) where symbols with lengths
greater than 63 chars cause a "paramErr". We iterate through all symbols
in the library to find the desired symbol.
*/
OSErr
NSFindSymbol(CFragConnectionID inID, Str255 inSymName, Ptr* outMainAddr, CFragSymbolClass *outSymClass)
{
OSErr err;
if (inSymName[0] > 63)
{
/*
if there are greater than 63 characters in the
name, CFM FindSymbol fails, so let's iterate through all
of the symbols in the fragment and grab it
that way.
*/
long symbolCount;
Str255 curSymName;
long curIndex;
Boolean found;
found = false;
err = CountSymbols(inID, &symbolCount);
if (noErr == err)
{
/* now iterate through all the symbols in the library */
/* per DTS the indices apparently go 0 to n-1 */
for (curIndex = 0; (curIndex <= symbolCount - 1 && !found); curIndex++)
{
err = GetIndSymbol(inID, curIndex, curSymName, outMainAddr, outSymClass);
if (noErr == err && curSymName[0] == inSymName[0] && !strncmp((char*)curSymName + 1, (char*)inSymName + 1, curSymName[0]))
{
/* found our symbol */
found = true;
}
}
/* if we didn't find it set the error code so below it won't take this symbol */
if (!found)
err = cfragNoSymbolErr;
}
}
else
{
err = FindSymbol(inID, inSymName, outMainAddr, outSymClass);
}
return (err);
}
#pragma mark -
/*-----------------------------------------------------------------
GetNamedFragmentOffsets
Get the offsets into the data fork of the named fragment,
by reading the 'cfrg' resoruce.
-----------------------------------------------------------------*/
OSErr GetNamedFragmentOffsets(const FSSpec *fileSpec, const char* fragmentName,
UInt32 *outOffset, UInt32 *outLength)
{
CFragResourceHandle cFragHandle;
short fileRefNum;
OSErr err = noErr;
fileRefNum = FSpOpenResFile(fileSpec, fsRdPerm);
err = ResError();
if (err != noErr) return err;
cFragHandle = (CFragResourceHandle)Get1Resource(kCFragResourceType, kCFragResourceID);
if (!cFragHandle)
{
err = resNotFound;
goto done;
}
/* nothing here moves memory, so no need to lock the handle */
err = cfragNoLibraryErr; /* in case of failure */
*outOffset = 0;
*outLength = 0;
/* Now look for the named fragment */
if ((**cFragHandle).memberCount > 0)
{
CFragResourceMemberPtr memberPtr;
UInt16 i;
for ( i = 0, memberPtr = &(**cFragHandle).firstMember;
i < (**cFragHandle).memberCount;
i ++, memberPtr = (CFragResourceMemberPtr)((char *)memberPtr + memberPtr->memberSize))
{
char memberName[256];
UInt16 nameLen = PR_MIN(memberPtr->name[0], 255);
// avoid malloc here for speed
strncpy(memberName, (char *)&memberPtr->name[1], nameLen);
memberName[nameLen] = '\0';
// fragment names are case insensitive, so act like the system
if (PL_strcasecmp(memberName, fragmentName) == 0)
{
*outOffset = memberPtr->offset;
*outLength = memberPtr->length;
err = noErr;
break;
}
}
}
/* Resource handle will go away when the res fork is closed */
done:
CloseResFile(fileRefNum);
return err;
}
/*-----------------------------------------------------------------
GetIndexedFragmentOffsets
Get the offsets into the data fork of the indexed fragment,
by reading the 'cfrg' resoruce.
-----------------------------------------------------------------*/
OSErr GetIndexedFragmentOffsets(const FSSpec *fileSpec, UInt32 fragmentIndex,
UInt32 *outOffset, UInt32 *outLength, char **outFragmentName)
{
CFragResourceHandle cFragHandle;
short fileRefNum;
OSErr err = noErr;
fileRefNum = FSpOpenResFile(fileSpec, fsRdPerm);
err = ResError();
if (err != noErr) return err;
cFragHandle = (CFragResourceHandle)Get1Resource(kCFragResourceType, kCFragResourceID);
if (!cFragHandle)
{
err = resNotFound;
goto done;
}
err = cfragNoLibraryErr; /* in case of failure */
*outOffset = 0;
*outLength = 0;
*outFragmentName = NULL;
/* the CStrFromPStr mallocs, so might move memory */
HLock((Handle)cFragHandle);
/* Now look for the named fragment */
if ((**cFragHandle).memberCount > 0)
{
CFragResourceMemberPtr memberPtr;
UInt16 i;
for ( i = 0, memberPtr = &(**cFragHandle).firstMember;
i < (**cFragHandle).memberCount;
i ++, memberPtr = (CFragResourceMemberPtr)((char *)memberPtr + memberPtr->memberSize))
{
if (i == fragmentIndex)
{
char *fragmentStr;
CStrFromPStr(memberPtr->name, &fragmentStr);
if (!fragmentStr) /* test for allocation failure */
{
err = memFullErr;
break;
}
*outFragmentName = fragmentStr;
*outOffset = memberPtr->offset;
*outLength = memberPtr->length;
err = noErr;
break;
}
}
}
HUnlock((Handle)cFragHandle);
/* Resource handle will go away when the res fork is closed */
done:
CloseResFile(fileRefNum);
return err;
}
/*-----------------------------------------------------------------
NSLoadNamedFragment
Load the named fragment from the specified file. Aliases must
have been resolved by this point.
-----------------------------------------------------------------*/
OSErr NSLoadNamedFragment(const FSSpec *fileSpec, const char* fragmentName, CFragConnectionID *outConnectionID)
{
UInt32 fragOffset, fragLength;
short fragNameLength;
Ptr main;
Str255 fragName;
Str255 errName;
OSErr err;
err = GetNamedFragmentOffsets(fileSpec, fragmentName, &fragOffset, &fragLength);
if (err != noErr) return err;
// convert fragment name to pascal string
fragNameLength = strlen(fragmentName);
if (fragNameLength > 255)
fragNameLength = 255;
BlockMoveData(fragmentName, &fragName[1], fragNameLength);
fragName[0] = fragNameLength;
// Note that we pass the fragment name as the 4th param to GetDiskFragment.
// This value affects the ability of debuggers, and the Talkback system,
// to match code fragments with symbol files
err = GetDiskFragment(fileSpec, fragOffset, fragLength, fragName,
kLoadCFrag, outConnectionID, &main, errName);
return err;
}
/*-----------------------------------------------------------------
NSLoadIndexedFragment
Load the indexed fragment from the specified file. Aliases must
have been resolved by this point.
*outFragName is a malloc'd block containing the fragment name,
if returning noErr.
-----------------------------------------------------------------*/
OSErr NSLoadIndexedFragment(const FSSpec *fileSpec, PRUint32 fragmentIndex,
char** outFragName, CFragConnectionID *outConnectionID)
{
UInt32 fragOffset, fragLength;
char *fragNameBlock = NULL;
Ptr main;
Str255 fragName = "\p";
Str255 errName;
OSErr err;
*outFragName = NULL;
err = GetIndexedFragmentOffsets(fileSpec, fragmentIndex, &fragOffset, &fragLength, &fragNameBlock);
if (err != noErr) return err;
if (fragNameBlock)
{
UInt32 nameLen = strlen(fragNameBlock);
if (nameLen > 63)
nameLen = 63;
BlockMoveData(fragNameBlock, &fragName[1], nameLen);
fragName[0] = nameLen;
}
// Note that we pass the fragment name as the 4th param to GetDiskFragment.
// This value affects the ability of debuggers, and the Talkback system,
// to match code fragments with symbol files
err = GetDiskFragment(fileSpec, fragOffset, fragLength, fragName,
kLoadCFrag, outConnectionID, &main, errName);
if (err != noErr)
{
free(fragNameBlock);
return err;
}
*outFragName = fragNameBlock;
return noErr;
}
syntax highlighted by Code2HTML, v. 0.9.1