diff --git a/xpcom/io/nsFileSpecMac.cpp b/xpcom/io/nsFileSpecMac.cpp index 5c0f586915bc..8a6f4101e613 100644 --- a/xpcom/io/nsFileSpecMac.cpp +++ b/xpcom/io/nsFileSpecMac.cpp @@ -83,8 +83,7 @@ namespace MacFileHelpers FSSpec& ioSpec, Boolean inCreateDirs); char* PathNameFromFSSpec( - const FSSpec& inSpec, - Boolean wantLeafName ); + const FSSpec& inSpec ); OSErr CreateFolderInFolder( short refNum, // Parent directory/volume long dirID, @@ -516,100 +515,31 @@ OSErr MacFileHelpers::FSSpecFromUnixPath( } // MacFileHelpers::FSSpecFromLocalUnixPath //----------------------------------- -char* MacFileHelpers::PathNameFromFSSpec( const FSSpec& inSpec, Boolean wantLeafName ) +char* MacFileHelpers::PathNameFromFSSpec( const FSSpec& inSpec ) // Returns a full pathname to the given file // Returned value is allocated with new [], and must be freed with delete [] -// This is taken from FSpGetFullPath in MoreFiles, except that we need to tolerate -// fnfErr. +// For consistency and to work under OS X this creates an nsILocalFileMac and has it do the work. //----------------------------------- { char* result = nil; - OSErr err = noErr; - - short fullPathLength = 0; - Handle fullPath = nsnull; - - FSSpec tempSpec = inSpec; - if ( tempSpec.parID == fsRtParID ) - { - /* The object is a volume */ - - /* Add a colon to make it a full pathname */ - tempSpec.name[++tempSpec.name[0]] = ':'; - - /* We're done */ - err = PtrToHand(&tempSpec.name[1], &fullPath, tempSpec.name[0]); - } - else - { - /* The object isn't a volume */ - - CInfoPBRec pb = { 0 }; - Str63 dummyFileName; - MacFileHelpers::PLstrcpy(dummyFileName, "\pG'day!"); + nsresult rv; - /* Is the object a file or a directory? */ - pb.dirInfo.ioNamePtr = (! tempSpec.name[0]) ? (StringPtr)dummyFileName : tempSpec.name; - pb.dirInfo.ioVRefNum = tempSpec.vRefNum; - pb.dirInfo.ioDrDirID = tempSpec.parID; - pb.dirInfo.ioFDirIndex = 0; - err = PBGetCatInfoSync(&pb); - if ( err == noErr || err == fnfErr) - { - // if the object is a directory, append a colon so full pathname ends with colon - // Beware of the "illegal spec" case that Netscape uses (empty name string). In - // this case, we don't want the colon. - if ( err == noErr && tempSpec.name[0] && (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0 ) - { - ++tempSpec.name[0]; - tempSpec.name[tempSpec.name[0]] = ':'; - } - - /* Put the object name in first */ - err = PtrToHand(&tempSpec.name[1], &fullPath, tempSpec.name[0]); - if ( err == 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; - err = PBGetCatInfoSync(&pb); - if ( err == 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, nsnull, 0, &tempSpec.name[1], tempSpec.name[0]); - err = MemError(); - } - } while ( err == noErr && pb.dirInfo.ioDrDirID != fsRtDirID ); - } - } - } - if ( err != noErr && err != fnfErr) - goto Clean; + FSSpec nonConstSpec = inSpec; + nsXPIDLCString path; + nsCOMPtr macFile; + + rv = NS_NewLocalFileWithFSSpec(&nonConstSpec, PR_TRUE, getter_AddRefs(macFile)); + if (NS_FAILED(rv)) return nsnull; + nsCOMPtr localFile(do_QueryInterface(macFile, &rv)); + if (NS_FAILED(rv)) return nsnull; + rv = localFile->GetPath(getter_Copies(path)); + if (NS_FAILED(rv)) return nsnull; + PRInt32 strLen = path.Length(); + result = new char [strLen + 1]; + if (!result) return nsnull; + memcpy(result, path.get(), strLen); + result[ strLen ] = 0; - fullPathLength = GetHandleSize(fullPath); - err = noErr; - int allocSize = 1 + fullPathLength; - // We only want the leaf name if it's the root directory or wantLeafName is true. - if (inSpec.parID != fsRtParID && !wantLeafName) - allocSize -= inSpec.name[0]; - result = new char[allocSize]; - if (!result) - goto Clean; - memcpy(result, *fullPath, allocSize - 1); - result[ allocSize - 1 ] = 0; -Clean: - if (fullPath) - DisposeHandle(fullPath); - NS_ASSERTION(result, "Out of memory"); // OOPS! very bad. return result; } // MacFileHelpers::PathNameFromFSSpec @@ -1238,7 +1168,7 @@ const char* nsFileSpec::GetCString() const { if (mPath.IsEmpty()) { - char* path = MacFileHelpers::PathNameFromFSSpec(mSpec, true); + char* path = MacFileHelpers::PathNameFromFSSpec(mSpec); if (path != NULL) { const_cast(this)->mPath = path; // operator =() copies the string!!! delete[] path; @@ -1318,7 +1248,7 @@ nsFilePath::nsFilePath(const nsFileURL& inOther) void nsFilePath::operator = (const nsFileSpec& inSpec) //---------------------------------------------------------------------------------------- { - char * path = MacFileHelpers::PathNameFromFSSpec(inSpec, true); + char * path = MacFileHelpers::PathNameFromFSSpec(inSpec); path = MacFileHelpers::EncodeMacPath(path, true, false); mPath = path; nsCRT::free(path); @@ -1329,7 +1259,7 @@ void nsFilePath::operator = (const nsFileSpec& inSpec) void nsFilePath::operator = (const nsFileURL& inOther) //---------------------------------------------------------------------------------------- { - char * path = MacFileHelpers::PathNameFromFSSpec(inOther.mFileSpec, true); + char * path = MacFileHelpers::PathNameFromFSSpec(inOther.mFileSpec); path = MacFileHelpers::EncodeMacPath(path, true, false); mPath = path; nsCRT::free(path); @@ -1406,7 +1336,7 @@ void nsFileURL::operator = (const nsFileSpec& inOther) //---------------------------------------------------------------------------------------- { mFileSpec = inOther; - char* path = MacFileHelpers::PathNameFromFSSpec( mFileSpec, true ); + char* path = MacFileHelpers::PathNameFromFSSpec( mFileSpec ); char* encodedPath = MacFileHelpers::EncodeMacPath(path, true, true); nsSimpleCharString encodedURL(kFileURLPrefix); encodedURL += encodedPath; diff --git a/xpcom/io/nsLocalFileMac.cpp b/xpcom/io/nsLocalFileMac.cpp index 43e42b42c515..66b4bfae7f19 100644 --- a/xpcom/io/nsLocalFileMac.cpp +++ b/xpcom/io/nsLocalFileMac.cpp @@ -59,6 +59,8 @@ #include #include #include +#include +#include #include #include @@ -692,6 +694,171 @@ static OSErr ResolvePathAndSpec(const char * filePath, FSSpec *inSpec, PRBool cr return err; } +#pragma mark - +#pragma mark [nsFSStringConversionMac] + +class nsFSStringConversionMac { +public: + static nsresult UCSToFS(const nsAString& aIn, nsACString& aOut); + static nsresult FSToUCS(const nsACString& ain, nsAString& aOut); + + static void CleanUp(); + +private: + static TextEncoding GetSystemEncoding(); + static nsresult PrepareEncoder(); + static nsresult PrepareDecoder(); + + static UnicodeToTextInfo sEncoderInfo; + static TextToUnicodeInfo sDecoderInfo; +}; + +UnicodeToTextInfo nsFSStringConversionMac::sEncoderInfo = nsnull; +TextToUnicodeInfo nsFSStringConversionMac::sDecoderInfo = nsnull; + +nsresult nsFSStringConversionMac::UCSToFS(const nsAString& aIn, nsACString& aOut) +{ + nsresult rv = PrepareEncoder(); + if (NS_FAILED(rv)) return rv; + + OSStatus err = noErr; + char stackBuffer[512]; + + aOut.Truncate(0); + nsReadingIterator done_reading; + aIn.EndReading(done_reading); + + // for each chunk of |aIn|... + PRUint32 fragmentLength = 0; + nsReadingIterator iter; + for (aIn.BeginReading(iter); iter != done_reading && err == noErr; iter.advance(PRInt32(fragmentLength))) + { + fragmentLength = PRUint32(iter.size_forward()); + UInt32 bytesLeft = fragmentLength * sizeof(UniChar); + nsReadingIterator sub_iter(iter); + + do { + UInt32 bytesRead = 0, bytesWritten = 0; + err = ::ConvertFromUnicodeToText(sEncoderInfo, + bytesLeft, + (const UniChar*)sub_iter.get(), + kUnicodeUseFallbacksMask | kUnicodeLooseMappingsMask, + 0, nsnull, nsnull, nsnull, + sizeof(stackBuffer), + &bytesRead, + &bytesWritten, + stackBuffer); + if (err == kTECUsedFallbacksStatus) + err = noErr; + else if (err == kTECOutputBufferFullStatus) { + bytesLeft -= bytesRead; + sub_iter.advance(bytesRead / sizeof(UniChar)); + } + aOut.Append(stackBuffer, bytesWritten); + } + while (err == kTECOutputBufferFullStatus); + } + return (err == noErr) ? NS_OK : NS_ERROR_FAILURE; +} + +nsresult nsFSStringConversionMac::FSToUCS(const nsACString& aIn, nsAString& aOut) +{ + nsresult rv = PrepareDecoder(); + if (NS_FAILED(rv)) return rv; + + OSStatus err = noErr; + UniChar stackBuffer[512]; + + aOut.Truncate(0); + nsReadingIterator done_reading; + aIn.EndReading(done_reading); + + // for each chunk of |aIn|... + PRUint32 fragmentLength = 0; + nsReadingIterator iter; + for (aIn.BeginReading(iter); iter != done_reading && err == noErr; iter.advance(PRInt32(fragmentLength))) + { + fragmentLength = PRUint32(iter.size_forward()); + UInt32 bytesLeft = fragmentLength; + nsReadingIterator sub_iter(iter); + + do { + UInt32 bytesRead = 0, bytesWritten = 0; + err = ::ConvertFromTextToUnicode(sDecoderInfo, + bytesLeft, + sub_iter.get(), + kUnicodeUseFallbacksMask | kUnicodeLooseMappingsMask, + 0, nsnull, nsnull, nsnull, + sizeof(stackBuffer), + &bytesRead, + &bytesWritten, + stackBuffer); + if (err == kTECUsedFallbacksStatus) + err = noErr; + else if (err == kTECOutputBufferFullStatus) { + bytesLeft -= bytesRead; + sub_iter.advance(bytesRead); + } + aOut.Append((PRUnichar *)stackBuffer, bytesWritten / sizeof(PRUnichar)); + } + while (err == kTECOutputBufferFullStatus); + } + return (err == noErr) ? NS_OK : NS_ERROR_FAILURE; +} + +void nsFSStringConversionMac::CleanUp() +{ + if (sDecoderInfo) { + ::DisposeTextToUnicodeInfo(&sDecoderInfo); + sDecoderInfo = nsnull; + } + if (sEncoderInfo) { + ::DisposeUnicodeToTextInfo(&sEncoderInfo); + sEncoderInfo = nsnull; + } +} + +TextEncoding nsFSStringConversionMac::GetSystemEncoding() +{ + OSStatus err; + TextEncoding theEncoding; + + err = ::UpgradeScriptInfoToTextEncoding(smSystemScript, kTextLanguageDontCare, + kTextRegionDontCare, NULL, &theEncoding); + + if (err != noErr) + theEncoding = kTextEncodingMacRoman; + + return theEncoding; +} + +nsresult nsFSStringConversionMac::PrepareEncoder() +{ + nsresult rv = NS_OK; + if (!sEncoderInfo) { + OSStatus err; + err = ::CreateUnicodeToTextInfoByEncoding(GetSystemEncoding(), &sEncoderInfo); + if (err) + rv = NS_ERROR_FAILURE; + } + return rv; +} + +nsresult nsFSStringConversionMac::PrepareDecoder() +{ + nsresult rv = NS_OK; + if (!sDecoderInfo) { + OSStatus err; + err = ::CreateTextToUnicodeInfoByEncoding(GetSystemEncoding(), &sDecoderInfo); + if (err) + rv = NS_ERROR_FAILURE; + } + return rv; +} + + + + #pragma mark - #pragma mark [nsDirEnumerator] class nsDirEnumerator : public nsISimpleEnumerator @@ -804,7 +971,8 @@ NS_IMPL_ISUPPORTS1(nsDirEnumerator, nsISimpleEnumerator) #pragma mark - -OSType nsLocalFile::mgCurrentProcessSignature = 0; +OSType nsLocalFile::sCurrentProcessSignature = 0; +PRBool nsLocalFile::sHasHFSPlusAPIs = PR_FALSE; #pragma mark [CTOR/DTOR] nsLocalFile::nsLocalFile() @@ -820,9 +988,9 @@ nsLocalFile::nsLocalFile() MakeDirty(); ClearFSSpec(mSpec); - DetermineCurrentProcessCreator(); - if (mgCurrentProcessSignature != 0) - mCreator = mgCurrentProcessSignature; + InitClassStatics(); + if (sCurrentProcessSignature != 0) + mCreator = sCurrentProcessSignature; } nsLocalFile::~nsLocalFile() @@ -1407,24 +1575,82 @@ nsLocalFile::GetPath(char **_retval) break; case eInitWithFSSpec: - { // Now would be a good time to call the code that makes an FSSpec into a path - short fullPathLen; - Handle fullPathHandle; - ResolveAndStat(PR_TRUE); - (void)::FSpGetFullPath(&mResolvedSpec, &fullPathLen, &fullPathHandle); - if (!fullPathHandle) - return NS_ERROR_OUT_OF_MEMORY; - - char* fullPath = (char *)nsMemory::Alloc(fullPathLen + 1); - if (!fullPath) - return NS_ERROR_OUT_OF_MEMORY; - - ::HLock(fullPathHandle); - nsCRT::memcpy(fullPath, *fullPathHandle, fullPathLen); - fullPath[fullPathLen] = '\0'; - - *_retval = fullPath; - ::DisposeHandle(fullPathHandle); + { + ResolveAndStat(PR_TRUE); + +#if TARGET_CARBON + if (sHasHFSPlusAPIs) // should always be true under Carbon, but in case... + { + OSErr err; + nsresult rv; + nsAutoString ucPathString; + nsCAutoString fsCharSetPathStr; + + FSRef nodeRef; + FSCatalogInfo catalogInfo; + catalogInfo.parentDirID = 0; + err = ::FSpMakeFSRef(&mResolvedSpec, &nodeRef); + + if (err == fnfErr) { + FSSpec parentDirSpec; + err = GetParentFolderSpec(mResolvedSpec, parentDirSpec); + if (err == noErr) { + nsDependentCString leafName((char *)&mResolvedSpec.name[1], PRUint32(mResolvedSpec.name[0])); + nsFSStringConversionMac::FSToUCS(leafName, ucPathString); + err = ::FSpMakeFSRef(&parentDirSpec, &nodeRef); + } + } + + while (err == noErr && catalogInfo.parentDirID != fsRtParID) { + HFSUniStr255 nodeName; + FSRef parentRef; + err = ::FSGetCatalogInfo(&nodeRef, + kFSCatInfoNodeFlags + kFSCatInfoParentDirID, + &catalogInfo, + &nodeName, + nsnull, + &parentRef); + if (err == noErr) + { + if (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) + nodeName.unicode[nodeName.length++] = PRUnichar(':'); + nsDependentString nodeNameStr((PRUnichar *)nodeName.unicode, (PRUint32)nodeName.length); + ucPathString.Insert(nodeNameStr, 0); + nodeRef = parentRef; + } + } + rv = MacErrorMapper(err); + if (NS_FAILED(rv)) + return rv; + rv = nsFSStringConversionMac::UCSToFS(ucPathString, fsCharSetPathStr); + if (NS_FAILED(rv)) + return rv; + // When nsIFile params are all nsAString&, we won't have to do this + *_retval = fsCharSetPathStr.ToNewCString(); + if (!*_retval) + return NS_ERROR_OUT_OF_MEMORY; + } + else +#endif + { + // Now would be a good time to call the code that makes an FSSpec into a path + short fullPathLen; + Handle fullPathHandle; + (void)::FSpGetFullPath(&mResolvedSpec, &fullPathLen, &fullPathHandle); + if (!fullPathHandle) + return NS_ERROR_OUT_OF_MEMORY; + + char* fullPath = (char *)nsMemory::Alloc(fullPathLen + 1); + if (!fullPath) + return NS_ERROR_OUT_OF_MEMORY; + + ::HLock(fullPathHandle); + nsCRT::memcpy(fullPath, *fullPathHandle, fullPathLen); + fullPath[fullPathLen] = '\0'; + + *_retval = fullPath; + ::DisposeHandle(fullPathHandle); + } break; } @@ -2911,7 +3137,7 @@ NS_IMETHODIMP nsLocalFile::SetFileTypeAndCreator(OSType type, OSType creator) return NS_ERROR_FILE_NOT_FOUND; if (creator == CURRENT_PROCESS_CREATOR) - creator = (mgCurrentProcessSignature != 0) ? mgCurrentProcessSignature : kDefaultCreator; + creator = (sCurrentProcessSignature != 0) ? sCurrentProcessSignature : kDefaultCreator; // See if the user specified a type or creator before changing from what was read if (type) @@ -3128,13 +3354,13 @@ nsresult nsLocalFile::ExtensionIsOnExceptionList(const char *extension, PRBool * } -nsresult nsLocalFile::DetermineCurrentProcessCreator() +void nsLocalFile::InitClassStatics() { - nsresult rv = NS_OK; + OSErr err; - if (mgCurrentProcessSignature == 0) + + if (sCurrentProcessSignature == 0) { - OSErr err; ProcessSerialNumber psn; ProcessInfoRec info; @@ -3146,14 +3372,21 @@ nsresult nsLocalFile::DetermineCurrentProcessCreator() info.processAppSpec = nil; err = ::GetProcessInformation(&psn, &info); if (err == noErr) - mgCurrentProcessSignature = info.processSignature; + sCurrentProcessSignature = info.processSignature; // Try again next time if error - else - rv = MacErrorMapper(err); } - return NS_OK; + + static PRBool didHFSPlusCheck = PR_FALSE; + if (!didHFSPlusCheck) + { + long response; + err = ::Gestalt(gestaltFSAttr, &response); + sHasHFSPlusAPIs = (err == noErr && (response & (1 << gestaltHasHFSPlusAPIs)) != 0); + didHFSPlusCheck = PR_TRUE; + } } + #pragma mark - // Handy dandy utility create routine for something or the other diff --git a/xpcom/io/nsLocalFileMac.h b/xpcom/io/nsLocalFileMac.h index ff826bc7d23c..4de200f57f07 100644 --- a/xpcom/io/nsLocalFileMac.h +++ b/xpcom/io/nsLocalFileMac.h @@ -103,9 +103,7 @@ protected: nsresult SetOSTypeAndCreatorFromExtension(const char* extension = nsnull); nsresult ExtensionIsOnExceptionList(const char *extension, PRBool *onList); - - static nsresult DetermineCurrentProcessCreator(); - + private: // It's important we keep track of how we were initialized @@ -143,8 +141,10 @@ private: OSType mType, mCreator; - static OSType mgCurrentProcessSignature; - + static void InitClassStatics(); + + static OSType sCurrentProcessSignature; + static PRBool sHasHFSPlusAPIs; }; #endif