зеркало из https://github.com/mozilla/gecko-dev.git
3679 строки
102 KiB
C++
3679 строки
102 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/*
|
|
* The contents of this file are subject to the Netscape 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/NPL/
|
|
*
|
|
* 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 Mozilla Communicator client code,
|
|
* released March 31, 1998.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape Communications
|
|
* Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Steve Dagley <sdagley@netscape.com>
|
|
* John R. McMullen
|
|
*/
|
|
|
|
|
|
#include "nsCOMPtr.h"
|
|
#include "nsMemory.h"
|
|
#include "nsXPIDLString.h"
|
|
|
|
#include "nsLocalFileMac.h"
|
|
#include "nsISimpleEnumerator.h"
|
|
#include "nsIComponentManager.h"
|
|
#include "nsIInternetConfigService.h"
|
|
#include "nsIMIMEInfo.h"
|
|
#include "prtypes.h"
|
|
#include "prerror.h"
|
|
|
|
#include "nsReadableUtils.h"
|
|
#include "nsITimelineService.h"
|
|
|
|
#ifdef XP_MACOSX
|
|
#include "nsXPIDLString.h"
|
|
|
|
#include "private/pprio.h"
|
|
#else
|
|
#include "pprio.h" // Include this rather than prio.h so we get def of PR_ImportFile
|
|
#endif
|
|
#include "prmem.h"
|
|
#include "plbase64.h"
|
|
|
|
#include "FullPath.h"
|
|
#include "FileCopy.h"
|
|
#include "MoreFilesExtras.h"
|
|
#include "DirectoryCopy.h"
|
|
#include <Script.h>
|
|
#include <Processes.h>
|
|
#include <StringCompare.h>
|
|
#include <Resources.h>
|
|
|
|
#include <AppleEvents.h>
|
|
#include <AEDataModel.h>
|
|
#include <AERegistry.h>
|
|
#include <Gestalt.h>
|
|
#include <UnicodeConverter.h>
|
|
|
|
#include <Math64.h>
|
|
#include <Aliases.h>
|
|
#include <Folders.h>
|
|
#include <Gestalt.h>
|
|
#include "macDirectoryCopy.h"
|
|
|
|
#include <limits.h>
|
|
|
|
// Stupid @#$% header looks like its got extern mojo but it doesn't really
|
|
extern "C"
|
|
{
|
|
#ifndef XP_MACOSX
|
|
// BADPINK - this MSL header doesn't exist under macosx :-(
|
|
#include <FSp_fopen.h>
|
|
#endif
|
|
}
|
|
|
|
#if TARGET_CARBON
|
|
#include <CodeFragments.h> // Needed for definition of kUnresolvedCFragSymbolAddress
|
|
#include <LaunchServices.h>
|
|
#endif
|
|
|
|
#pragma mark [Constants]
|
|
|
|
const OSType kDefaultCreator = 'MOSS';
|
|
|
|
#pragma mark -
|
|
#pragma mark [nsPathParser]
|
|
|
|
class nsPathParser
|
|
{
|
|
public:
|
|
nsPathParser(const nsACString &path);
|
|
|
|
~nsPathParser()
|
|
{
|
|
if (mAllocatedBuffer)
|
|
nsMemory::Free(mAllocatedBuffer);
|
|
}
|
|
|
|
const char* First()
|
|
{
|
|
return nsCRT::strtok(mBuffer, ":", &mNewString);
|
|
}
|
|
const char* Next()
|
|
{
|
|
return nsCRT::strtok(mNewString, ":", &mNewString);
|
|
}
|
|
const char* Remainder()
|
|
{
|
|
return mNewString;
|
|
}
|
|
|
|
private:
|
|
char mAutoBuffer[512];
|
|
char *mAllocatedBuffer;
|
|
char *mBuffer, *mNewString;
|
|
};
|
|
|
|
nsPathParser::nsPathParser(const nsACString &inPath) :
|
|
mAllocatedBuffer(nsnull), mNewString(nsnull)
|
|
{
|
|
PRUint32 inPathLen = inPath.Length();
|
|
if (inPathLen >= sizeof(mAutoBuffer)) {
|
|
mAllocatedBuffer = (char *)nsMemory::Alloc(inPathLen + 1);
|
|
mBuffer = mAllocatedBuffer;
|
|
}
|
|
else
|
|
mBuffer = mAutoBuffer;
|
|
|
|
// copy inPath into mBuffer
|
|
nsACString::const_iterator start, end;
|
|
inPath.BeginReading(start);
|
|
inPath.EndReading(end);
|
|
|
|
PRUint32 size, offset = 0;
|
|
for ( ; start != end; start.advance(size)) {
|
|
const char* buf = start.get();
|
|
size = start.size_forward();
|
|
memcpy(mBuffer + offset, buf, size);
|
|
offset += size;
|
|
}
|
|
mBuffer[offset] = '\0';
|
|
}
|
|
|
|
#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<PRUnichar> done_reading;
|
|
aIn.EndReading(done_reading);
|
|
|
|
// for each chunk of |aIn|...
|
|
PRUint32 fragmentLength = 0;
|
|
nsReadingIterator<PRUnichar> 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<PRUnichar> 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<char> done_reading;
|
|
aIn.EndReading(done_reading);
|
|
|
|
// for each chunk of |aIn|...
|
|
PRUint32 fragmentLength = 0;
|
|
nsReadingIterator<char> iter;
|
|
for (aIn.BeginReading(iter); iter != done_reading && err == noErr; iter.advance(PRInt32(fragmentLength)))
|
|
{
|
|
fragmentLength = PRUint32(iter.size_forward());
|
|
UInt32 bytesLeft = fragmentLength;
|
|
nsReadingIterator<char> 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 [static util funcs]
|
|
|
|
static inline void ClearFSSpec(FSSpec& aSpec)
|
|
{
|
|
aSpec.vRefNum = 0;
|
|
aSpec.parID = 0;
|
|
aSpec.name[0] = 0;
|
|
}
|
|
|
|
|
|
// Simple func to map Mac OS errors into nsresults
|
|
static nsresult MacErrorMapper(OSErr inErr)
|
|
{
|
|
nsresult outErr;
|
|
|
|
switch (inErr)
|
|
{
|
|
case noErr:
|
|
outErr = NS_OK;
|
|
break;
|
|
|
|
case fnfErr:
|
|
outErr = NS_ERROR_FILE_NOT_FOUND;
|
|
break;
|
|
|
|
case dupFNErr:
|
|
outErr = NS_ERROR_FILE_ALREADY_EXISTS;
|
|
break;
|
|
|
|
case dskFulErr:
|
|
outErr = NS_ERROR_FILE_DISK_FULL;
|
|
break;
|
|
|
|
case fLckdErr:
|
|
outErr = NS_ERROR_FILE_IS_LOCKED;
|
|
break;
|
|
|
|
// Can't find good map for some
|
|
case bdNamErr:
|
|
outErr = NS_ERROR_FAILURE;
|
|
break;
|
|
|
|
default:
|
|
outErr = NS_ERROR_FAILURE;
|
|
break;
|
|
}
|
|
return outErr;
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------
|
|
IsEqualFSSpec
|
|
|
|
Compare two canonical FSSpec records.
|
|
|
|
Entry: file1 = pointer to first FSSpec record.
|
|
file2 = pointer to second FSSpec record.
|
|
|
|
Exit: function result = true if the FSSpec records are equal.
|
|
----------------------------------------------------------------------------*/
|
|
|
|
static PRBool IsEqualFSSpec(const FSSpec& file1, const FSSpec& file2)
|
|
{
|
|
return
|
|
file1.vRefNum == file2.vRefNum &&
|
|
file1.parID == file2.parID &&
|
|
EqualString(file1.name, file2.name, false, true);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------------
|
|
GetParentFolderSpec
|
|
|
|
Given an FSSpec to a (possibly non-existent) file, get an FSSpec for its
|
|
parent directory.
|
|
|
|
----------------------------------------------------------------------------*/
|
|
|
|
static OSErr GetParentFolderSpec(const FSSpec& fileSpec, FSSpec& parentDirSpec)
|
|
{
|
|
CInfoPBRec pBlock = {0};
|
|
OSErr err = noErr;
|
|
|
|
parentDirSpec.name[0] = 0;
|
|
|
|
pBlock.dirInfo.ioVRefNum = fileSpec.vRefNum;
|
|
pBlock.dirInfo.ioDrDirID = fileSpec.parID;
|
|
pBlock.dirInfo.ioNamePtr = (StringPtr)parentDirSpec.name;
|
|
pBlock.dirInfo.ioFDirIndex = -1; //get info on parID
|
|
err = PBGetCatInfoSync(&pBlock);
|
|
if (err != noErr) return err;
|
|
|
|
parentDirSpec.vRefNum = fileSpec.vRefNum;
|
|
parentDirSpec.parID = pBlock.dirInfo.ioDrParID;
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------------
|
|
VolHasDesktopDB
|
|
|
|
Check to see if a volume supports the new desktop database.
|
|
|
|
Entry: vRefNum = vol ref num of volumn
|
|
|
|
Exit: function result = error code.
|
|
*hasDesktop = true if volume has the new desktop database.
|
|
----------------------------------------------------------------------------*/
|
|
|
|
static OSErr VolHasDesktopDB (short vRefNum, Boolean *hasDesktop)
|
|
{
|
|
HParamBlockRec pb;
|
|
GetVolParmsInfoBuffer info;
|
|
OSErr err = noErr;
|
|
|
|
pb.ioParam.ioCompletion = nil;
|
|
pb.ioParam.ioNamePtr = nil;
|
|
pb.ioParam.ioVRefNum = vRefNum;
|
|
pb.ioParam.ioBuffer = (Ptr)&info;
|
|
pb.ioParam.ioReqCount = sizeof(info);
|
|
err = PBHGetVolParmsSync(&pb);
|
|
*hasDesktop = err == noErr && (info.vMAttrib & (1L << bHasDesktopMgr)) != 0;
|
|
return err;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------------
|
|
GetLastModDateTime
|
|
|
|
Get the last mod date and time of a file.
|
|
|
|
Entry: fSpec = pointer to file spec.
|
|
|
|
Exit: function result = error code.
|
|
*lastModDateTime = last mod date and time.
|
|
----------------------------------------------------------------------------*/
|
|
|
|
static OSErr GetLastModDateTime(const FSSpec *fSpec, unsigned long *lastModDateTime)
|
|
{
|
|
CInfoPBRec pBlock;
|
|
OSErr err = noErr;
|
|
|
|
pBlock.hFileInfo.ioNamePtr = (StringPtr)fSpec->name;
|
|
pBlock.hFileInfo.ioVRefNum = fSpec->vRefNum;
|
|
pBlock.hFileInfo.ioFDirIndex = 0;
|
|
pBlock.hFileInfo.ioDirID = fSpec->parID;
|
|
err = PBGetCatInfoSync(&pBlock);
|
|
if (err != noErr) return err;
|
|
*lastModDateTime = pBlock.hFileInfo.ioFlMdDat;
|
|
return noErr;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------------
|
|
FindAppOnVolume
|
|
|
|
Find an application on a volume.
|
|
|
|
Entry: sig = application signature.
|
|
vRefNum = vol ref num
|
|
|
|
Exit: function result = error code
|
|
= afpItemNotFound if app not found on vol.
|
|
*file = file spec for application on volume.
|
|
----------------------------------------------------------------------------*/
|
|
|
|
static OSErr FindAppOnVolume (OSType sig, short vRefNum, FSSpec *file)
|
|
{
|
|
DTPBRec pb;
|
|
OSErr err = noErr;
|
|
short ioDTRefNum, i;
|
|
FInfo fInfo;
|
|
FSSpec candidate;
|
|
unsigned long lastModDateTime, maxLastModDateTime;
|
|
|
|
memset(&pb, 0, sizeof(DTPBRec));
|
|
pb.ioCompletion = nil;
|
|
pb.ioVRefNum = vRefNum;
|
|
pb.ioNamePtr = nil;
|
|
err = PBDTGetPath(&pb);
|
|
if (err != noErr) return err;
|
|
ioDTRefNum = pb.ioDTRefNum;
|
|
|
|
memset(&pb, 0, sizeof(DTPBRec));
|
|
pb.ioCompletion = nil;
|
|
pb.ioIndex = 0;
|
|
pb.ioFileCreator = sig;
|
|
pb.ioNamePtr = file->name;
|
|
pb.ioDTRefNum = ioDTRefNum;
|
|
err = PBDTGetAPPLSync(&pb);
|
|
|
|
if (err == fnfErr || err == paramErr) return afpItemNotFound;
|
|
if (err != noErr) return err;
|
|
|
|
file->vRefNum = vRefNum;
|
|
file->parID = pb.ioAPPLParID;
|
|
|
|
err = FSpGetFInfo(file, &fInfo);
|
|
if (err == noErr) return noErr;
|
|
|
|
i = 1;
|
|
maxLastModDateTime = 0;
|
|
while (true)
|
|
{
|
|
memset(&pb, 0, sizeof(DTPBRec));
|
|
pb.ioCompletion = nil;
|
|
pb.ioIndex = i;
|
|
pb.ioFileCreator = sig;
|
|
pb.ioNamePtr = candidate.name;
|
|
pb.ioDTRefNum = ioDTRefNum;
|
|
err = PBDTGetAPPLSync(&pb);
|
|
if (err != noErr) break;
|
|
candidate.vRefNum = vRefNum;
|
|
candidate.parID = pb.ioAPPLParID;
|
|
err = GetLastModDateTime(file, &lastModDateTime);
|
|
if (err == noErr) {
|
|
if (lastModDateTime > maxLastModDateTime) {
|
|
maxLastModDateTime = lastModDateTime;
|
|
*file = candidate;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
|
|
return maxLastModDateTime > 0 ? noErr : afpItemNotFound;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------------
|
|
GetIndVolume
|
|
|
|
Get a volume reference number by volume index.
|
|
|
|
Entry: index = volume index
|
|
|
|
Exit: function result = error code.
|
|
*vRefNum = vol ref num of indexed volume.
|
|
----------------------------------------------------------------------------*/
|
|
|
|
static OSErr GetIndVolume(short index, short *vRefNum)
|
|
{
|
|
HParamBlockRec pb;
|
|
Str63 volumeName;
|
|
OSErr err = noErr;
|
|
|
|
pb.volumeParam.ioCompletion = nil;
|
|
pb.volumeParam.ioNamePtr = volumeName;
|
|
pb.volumeParam.ioVolIndex = index;
|
|
|
|
err = PBHGetVInfoSync(&pb);
|
|
|
|
*vRefNum = pb.volumeParam.ioVRefNum;
|
|
return err;
|
|
}
|
|
|
|
|
|
// Private NSPR functions
|
|
static unsigned long gJanuaryFirst1970Seconds = 0;
|
|
/*
|
|
* The geographic location and time zone information of a Mac
|
|
* are stored in extended parameter RAM. The ReadLocation
|
|
* produdure uses the geographic location record, MachineLocation,
|
|
* to read the geographic location and time zone information in
|
|
* extended parameter RAM.
|
|
*
|
|
* Because serial port and SLIP conflict with ReadXPram calls,
|
|
* we cache the call here.
|
|
*
|
|
* Caveat: this caching will give the wrong result if a session
|
|
* extend across the DST changeover time.
|
|
*/
|
|
|
|
static void MyReadLocation(MachineLocation *loc)
|
|
{
|
|
static MachineLocation storedLoc;
|
|
static Boolean didReadLocation = false;
|
|
|
|
if (!didReadLocation) {
|
|
ReadLocation(&storedLoc);
|
|
didReadLocation = true;
|
|
}
|
|
*loc = storedLoc;
|
|
}
|
|
|
|
static long GMTDelta(void)
|
|
{
|
|
MachineLocation loc;
|
|
long gmtDelta;
|
|
|
|
MyReadLocation(&loc);
|
|
gmtDelta = loc.u.gmtDelta & 0x00ffffff;
|
|
if (gmtDelta & 0x00800000) { /* test sign extend bit */
|
|
gmtDelta |= 0xff000000;
|
|
}
|
|
return gmtDelta;
|
|
}
|
|
|
|
static void MacintoshInitializeTime(void)
|
|
{
|
|
/*
|
|
* The NSPR epoch is midnight, Jan. 1, 1970 GMT.
|
|
*
|
|
* At midnight Jan. 1, 1970 GMT, the local time was
|
|
* midnight Jan. 1, 1970 + GMTDelta().
|
|
*
|
|
* Midnight Jan. 1, 1970 is 86400 * (365 * (1970 - 1904) + 17)
|
|
* = 2082844800 seconds since the Mac epoch.
|
|
* (There were 17 leap years from 1904 to 1970.)
|
|
*
|
|
* So the NSPR epoch is 2082844800 + GMTDelta() seconds since
|
|
* the Mac epoch. Whew! :-)
|
|
*/
|
|
gJanuaryFirst1970Seconds = 2082844800 + GMTDelta();
|
|
}
|
|
|
|
static nsresult ConvertMacTimeToMilliseconds( PRInt64* aLastModifiedTime, PRUint32 timestamp )
|
|
{
|
|
if ( gJanuaryFirst1970Seconds == 0)
|
|
MacintoshInitializeTime();
|
|
timestamp -= gJanuaryFirst1970Seconds;
|
|
PRTime usecPerSec, dateInMicroSeconds;
|
|
LL_I2L(dateInMicroSeconds, timestamp);
|
|
LL_I2L(usecPerSec, PR_MSEC_PER_SEC);
|
|
LL_MUL(*aLastModifiedTime, usecPerSec, dateInMicroSeconds);
|
|
return NS_OK;
|
|
}
|
|
|
|
static nsresult ConvertMillisecondsToMacTime(PRInt64 aTime, PRUint32 *aOutMacTime)
|
|
{
|
|
NS_ENSURE_ARG( aOutMacTime );
|
|
|
|
PRTime usecPerSec, dateInSeconds;
|
|
dateInSeconds = LL_ZERO;
|
|
|
|
LL_I2L(usecPerSec, PR_MSEC_PER_SEC);
|
|
LL_DIV(dateInSeconds, aTime, usecPerSec); // dateInSeconds = aTime/1,000
|
|
LL_L2UI(*aOutMacTime, dateInSeconds);
|
|
*aOutMacTime += 2082844800; // date + Mac epoch
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
static void myPLstrcpy(Str255 dst, const char* src)
|
|
{
|
|
int srcLength = strlen(src);
|
|
NS_ASSERTION(srcLength <= 255, "Oops, Str255 can't hold >255 chars");
|
|
if (srcLength > 255)
|
|
srcLength = 255;
|
|
dst[0] = srcLength;
|
|
memcpy(&dst[1], src, srcLength);
|
|
}
|
|
|
|
static void myPLstrncpy(Str255 dst, const char* src, int inMax)
|
|
{
|
|
int srcLength = strlen(src);
|
|
if (srcLength > inMax)
|
|
srcLength = inMax;
|
|
dst[0] = srcLength;
|
|
memcpy(&dst[1], src, srcLength);
|
|
}
|
|
|
|
/*
|
|
NS_TruncNodeName
|
|
|
|
Utility routine to do a mid-trunc on a potential file name so that it is
|
|
no longer than 31 characters. Until we move to the HFS+ APIs we need this
|
|
to come up with legal Mac file names.
|
|
|
|
Entry: aNode = initial file name
|
|
outBuf = scratch buffer for the truncated name (MUST be >= 32 characters)
|
|
|
|
Exit: function result = pointer to truncated name. Will be either aNode or outBuf.
|
|
|
|
*/
|
|
const char* NS_TruncNodeName(const char *aNode, char *outBuf)
|
|
{
|
|
PRUint32 nodeLen;
|
|
if ((nodeLen = strlen(aNode)) > 31)
|
|
{
|
|
static PRBool sInitialized = PR_FALSE;
|
|
static CharByteTable sTable;
|
|
// Init to "..." in case we fail to get the ellipsis token
|
|
static char sEllipsisTokenStr[4] = { '.', '.', '.', 0 };
|
|
static PRUint8 sEllipsisTokenLen = 3;
|
|
|
|
if (!sInitialized)
|
|
{
|
|
// Entries in the table are:
|
|
// 0 == 1 byte char
|
|
// 1 == 2 byte char
|
|
FillParseTable(sTable, smSystemScript);
|
|
|
|
Handle itl4ResHandle = nsnull;
|
|
long offset, len;
|
|
::GetIntlResourceTable(smSystemScript, smUnTokenTable, &itl4ResHandle, &offset, &len);
|
|
if (itl4ResHandle)
|
|
{
|
|
UntokenTable *untokenTableRec = (UntokenTable *)(*itl4ResHandle + offset);
|
|
if (untokenTableRec->lastToken >= tokenEllipsis)
|
|
{
|
|
offset += untokenTableRec->index[tokenEllipsis];
|
|
char *tokenStr = (*itl4ResHandle + offset);
|
|
sEllipsisTokenLen = tokenStr[0];
|
|
memcpy(sEllipsisTokenStr, &tokenStr[1], sEllipsisTokenLen);
|
|
}
|
|
::ReleaseResource(itl4ResHandle);
|
|
}
|
|
sInitialized = PR_TRUE;
|
|
}
|
|
|
|
PRInt32 halfLen = (31 - sEllipsisTokenLen) / 2;
|
|
PRInt32 charSize = 0, srcPos, destPos;
|
|
for (srcPos = 0; srcPos + charSize <= halfLen; srcPos += charSize)
|
|
charSize = sTable[aNode[srcPos]] ? 2 : 1;
|
|
|
|
memcpy(outBuf, aNode, srcPos);
|
|
memcpy(outBuf + srcPos, sEllipsisTokenStr, sEllipsisTokenLen);
|
|
destPos = srcPos + sEllipsisTokenLen;
|
|
|
|
for (; srcPos < nodeLen - halfLen; srcPos += charSize)
|
|
charSize = sTable[aNode[srcPos]] ? 2 : 1;
|
|
|
|
memcpy(outBuf + destPos, aNode + srcPos, nodeLen - srcPos);
|
|
destPos += (nodeLen - srcPos);
|
|
outBuf[destPos] = '\0';
|
|
return outBuf;
|
|
}
|
|
return aNode;
|
|
}
|
|
|
|
/**
|
|
* HFSPlusGetRawPath returns the path for an FSSpec as a unicode string.
|
|
*
|
|
* The reason for this routine instead of just calling FSRefMakePath is
|
|
* (1) inSpec does not have to exist
|
|
* (2) FSRefMakePath uses '/' as the separator under OSX and ':' under OS9
|
|
*/
|
|
static OSErr HFSPlusGetRawPath(const FSSpec& inSpec, nsAString& outStr)
|
|
{
|
|
OSErr err;
|
|
nsAutoString ucPathString;
|
|
|
|
outStr.Truncate(0);
|
|
|
|
FSRef nodeRef;
|
|
FSCatalogInfo catalogInfo;
|
|
catalogInfo.parentDirID = 0;
|
|
err = ::FSpMakeFSRef(&inSpec, &nodeRef);
|
|
|
|
if (err == fnfErr) {
|
|
FSSpec parentDirSpec;
|
|
err = GetParentFolderSpec(inSpec, parentDirSpec);
|
|
if (err == noErr) {
|
|
const char *startPtr = (const char*)&inSpec.name[1];
|
|
nsFSStringConversionMac::FSToUCS(Substring(startPtr, startPtr + PRUint32(inSpec.name[0])), outStr);
|
|
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(':');
|
|
const PRUnichar* nodeNameUni = (const PRUnichar*) nodeName.unicode;
|
|
outStr.Insert(Substring(nodeNameUni, nodeNameUni + nodeName.length), 0);
|
|
nodeRef = parentRef;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
|
|
// The R**co FSSpec resolver -
|
|
// it slices, it dices, it juliannes fries and it even creates FSSpecs out of whatever you feed it
|
|
// This function will take a path and a starting FSSpec and generate a FSSpec to represent
|
|
// the target of the two. If the intial FSSpec is null the path alone will be resolved
|
|
static OSErr ResolvePathAndSpec(const char * filePath, FSSpec *inSpec, PRBool createDirs, FSSpec *outSpec)
|
|
{
|
|
OSErr err = noErr;
|
|
size_t inLength = strlen(filePath);
|
|
Boolean isRelative = (filePath && inSpec);
|
|
FSSpec tempSpec;
|
|
Str255 ppath;
|
|
Boolean isDirectory;
|
|
|
|
if (isRelative && inSpec)
|
|
{
|
|
outSpec->vRefNum = inSpec->vRefNum;
|
|
outSpec->parID = inSpec->parID;
|
|
|
|
if (inSpec->name[0] != 0)
|
|
{
|
|
long theDirID;
|
|
|
|
err = FSpGetDirectoryID(inSpec, &theDirID, &isDirectory);
|
|
|
|
if (err == noErr && isDirectory)
|
|
outSpec->parID = theDirID;
|
|
else if (err == fnfErr && createDirs)
|
|
{
|
|
err = FSpDirCreate(inSpec, smCurrentScript, &theDirID);
|
|
if (err == noErr)
|
|
outSpec->parID = theDirID;
|
|
else if (err == fnfErr)
|
|
err = dirNFErr;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
outSpec->vRefNum = 0;
|
|
outSpec->parID = 0;
|
|
}
|
|
|
|
if (err)
|
|
return err;
|
|
|
|
// Try making an FSSpec from the path
|
|
if (inLength < 255)
|
|
{
|
|
// Use tempSpec as dest because if FSMakeFSSpec returns dirNFErr, it
|
|
// will reset the dest spec and we'll lose what we determined above.
|
|
|
|
myPLstrcpy(ppath, filePath);
|
|
err = ::FSMakeFSSpec(outSpec->vRefNum, outSpec->parID, ppath, &tempSpec);
|
|
if (err == noErr || err == fnfErr)
|
|
*outSpec = tempSpec;
|
|
}
|
|
else if (!isRelative)
|
|
{
|
|
err = ::FSpLocationFromFullPath(inLength, filePath, outSpec);
|
|
}
|
|
else
|
|
{ // If the path is relative and >255 characters we need to manually walk the
|
|
// path appending each node to the initial FSSpec so to reach that code we
|
|
// set the err to bdNamErr and fall into the code below
|
|
err = bdNamErr;
|
|
}
|
|
|
|
// If we successfully created a spec then leave
|
|
if (err == noErr)
|
|
return err;
|
|
|
|
// We get here when the directory hierarchy needs to be created or we're resolving
|
|
// a relative path >255 characters long
|
|
if (err == dirNFErr || err == bdNamErr)
|
|
{
|
|
const char* path = filePath;
|
|
|
|
if (!isRelative) // If path is relative, we still need vRefNum & parID.
|
|
{
|
|
outSpec->vRefNum = 0;
|
|
outSpec->parID = 0;
|
|
}
|
|
|
|
do
|
|
{
|
|
// Locate the colon that terminates the node.
|
|
// But if we've a partial path (starting with a colon), find the second one.
|
|
const char* nextColon = strchr(path + (*path == ':'), ':');
|
|
// Well, if there are no more colons, point to the end of the string.
|
|
if (!nextColon)
|
|
nextColon = path + strlen(path);
|
|
|
|
// Make a pascal string out of this node. Include initial
|
|
// and final colon, if any!
|
|
myPLstrncpy(ppath, path, nextColon - path + 1);
|
|
|
|
// Use this string as a relative path using the directory created
|
|
// on the previous round (or directory 0,0 on the first round).
|
|
err = ::FSMakeFSSpec(outSpec->vRefNum, outSpec->parID, ppath, outSpec);
|
|
|
|
// If this was the leaf node, then we are done.
|
|
if (!*nextColon)
|
|
break;
|
|
|
|
// Since there's more to go, we have to get the directory ID, which becomes
|
|
// the parID for the next round.
|
|
if (err == noErr)
|
|
{
|
|
// The directory (or perhaps a file) exists. Find its dirID.
|
|
long dirID;
|
|
err = ::FSpGetDirectoryID(outSpec, &dirID, &isDirectory);
|
|
if (!isDirectory)
|
|
err = dupFNErr; // oops! a file exists with that name.
|
|
if (err != noErr)
|
|
break; // bail if we've got an error
|
|
outSpec->parID = dirID;
|
|
}
|
|
else if ((err == fnfErr) && createDirs)
|
|
{
|
|
// If we got "file not found" and we're allowed to create directories
|
|
// then we need to create one
|
|
err = ::FSpDirCreate(outSpec, smCurrentScript, &outSpec->parID);
|
|
// For some reason, this usually returns fnfErr, even though it works.
|
|
if (err == fnfErr)
|
|
err = noErr;
|
|
}
|
|
if (err != noErr)
|
|
break;
|
|
path = nextColon; // next round
|
|
} while (true);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark [StFollowLinksState]
|
|
class StFollowLinksState
|
|
{
|
|
public:
|
|
StFollowLinksState(nsILocalFile *aFile) :
|
|
mFile(aFile)
|
|
{
|
|
NS_ASSERTION(mFile, "StFollowLinksState passed a NULL file.");
|
|
if (mFile)
|
|
mFile->GetFollowLinks(&mSavedState);
|
|
}
|
|
|
|
StFollowLinksState(nsILocalFile *aFile, PRBool followLinksState) :
|
|
mFile(aFile)
|
|
{
|
|
NS_ASSERTION(mFile, "StFollowLinksState passed a NULL file.");
|
|
if (mFile) {
|
|
mFile->GetFollowLinks(&mSavedState);
|
|
mFile->SetFollowLinks(followLinksState);
|
|
}
|
|
}
|
|
|
|
~StFollowLinksState()
|
|
{
|
|
if (mFile)
|
|
mFile->SetFollowLinks(mSavedState);
|
|
}
|
|
|
|
private:
|
|
nsCOMPtr<nsILocalFile> mFile;
|
|
PRBool mSavedState;
|
|
};
|
|
|
|
#pragma mark -
|
|
#pragma mark [nsDirEnumerator]
|
|
class nsDirEnumerator : public nsISimpleEnumerator
|
|
{
|
|
public:
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
nsDirEnumerator()
|
|
{
|
|
NS_INIT_ISUPPORTS();
|
|
}
|
|
|
|
nsresult Init(nsILocalFileMac* parent)
|
|
{
|
|
NS_ENSURE_ARG(parent);
|
|
nsresult rv;
|
|
FSSpec fileSpec;
|
|
|
|
rv = parent->GetFSSpec(&fileSpec);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
OSErr err;
|
|
Boolean isDirectory;
|
|
|
|
err = ::FSpGetDirectoryID(&fileSpec, &mDirID, &isDirectory);
|
|
if (err || !isDirectory)
|
|
return NS_ERROR_FILE_NOT_DIRECTORY;
|
|
|
|
mCatInfo.hFileInfo.ioNamePtr = mItemName;
|
|
mCatInfo.hFileInfo.ioVRefNum = fileSpec.vRefNum;
|
|
mItemIndex = 1;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD HasMoreElements(PRBool *result)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
if (mNext == nsnull)
|
|
{
|
|
mItemName[0] = 0;
|
|
mCatInfo.dirInfo.ioFDirIndex = mItemIndex;
|
|
mCatInfo.dirInfo.ioDrDirID = mDirID;
|
|
|
|
OSErr err = ::PBGetCatInfoSync(&mCatInfo);
|
|
if (err == fnfErr)
|
|
{
|
|
// end of dir entries
|
|
*result = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
|
|
// Make a new nsILocalFile for the new element
|
|
FSSpec tempSpec;
|
|
tempSpec.vRefNum = mCatInfo.hFileInfo.ioVRefNum;
|
|
tempSpec.parID = mDirID;
|
|
::BlockMoveData(mItemName, tempSpec.name, mItemName[0] + 1);
|
|
|
|
rv = NS_NewLocalFileWithFSSpec(&tempSpec, PR_TRUE, getter_AddRefs(mNext));
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
}
|
|
*result = mNext != nsnull;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD GetNext(nsISupports **result)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(result);
|
|
*result = nsnull;
|
|
|
|
nsresult rv;
|
|
PRBool hasMore;
|
|
rv = HasMoreElements(&hasMore);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
*result = mNext; // might return nsnull
|
|
NS_IF_ADDREF(*result);
|
|
|
|
mNext = nsnull;
|
|
++mItemIndex;
|
|
return NS_OK;
|
|
}
|
|
|
|
virtual ~nsDirEnumerator()
|
|
{
|
|
}
|
|
|
|
protected:
|
|
nsCOMPtr<nsILocalFileMac> mNext;
|
|
|
|
CInfoPBRec mCatInfo;
|
|
short mItemIndex;
|
|
long mDirID;
|
|
Str63 mItemName;
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS1(nsDirEnumerator, nsISimpleEnumerator)
|
|
|
|
#pragma mark -
|
|
|
|
OSType nsLocalFile::sCurrentProcessSignature = 0;
|
|
PRBool nsLocalFile::sHasHFSPlusAPIs = PR_FALSE;
|
|
PRBool nsLocalFile::sRunningOSX = PR_FALSE;
|
|
|
|
#pragma mark [CTOR/DTOR]
|
|
nsLocalFile::nsLocalFile() :
|
|
mFollowLinks(PR_TRUE),
|
|
mFollowLinksDirty(PR_TRUE),
|
|
mSpecDirty(PR_TRUE),
|
|
mCatInfoDirty(PR_TRUE),
|
|
mType('TEXT'),
|
|
mCreator(kDefaultCreator)
|
|
{
|
|
NS_INIT_ISUPPORTS();
|
|
|
|
ClearFSSpec(mSpec);
|
|
ClearFSSpec(mTargetSpec);
|
|
|
|
InitClassStatics();
|
|
if (sCurrentProcessSignature != 0)
|
|
mCreator = sCurrentProcessSignature;
|
|
}
|
|
|
|
nsLocalFile::nsLocalFile(const nsLocalFile& srcFile)
|
|
{
|
|
NS_INIT_ISUPPORTS();
|
|
*this = srcFile;
|
|
}
|
|
|
|
nsLocalFile::nsLocalFile(const FSSpec& aSpec, const nsACString& aAppendedPath) :
|
|
mFollowLinks(PR_TRUE),
|
|
mFollowLinksDirty(PR_TRUE),
|
|
mSpecDirty(PR_TRUE),
|
|
mSpec(aSpec),
|
|
mAppendedPath(aAppendedPath),
|
|
mCatInfoDirty(PR_TRUE),
|
|
mType('TEXT'),
|
|
mCreator(kDefaultCreator)
|
|
{
|
|
NS_INIT_ISUPPORTS();
|
|
|
|
ClearFSSpec(mTargetSpec);
|
|
|
|
InitClassStatics();
|
|
if (sCurrentProcessSignature != 0)
|
|
mCreator = sCurrentProcessSignature;
|
|
}
|
|
|
|
nsLocalFile& nsLocalFile::operator=(const nsLocalFile& rhs)
|
|
{
|
|
mFollowLinks = rhs.mFollowLinks;
|
|
mFollowLinksDirty = rhs.mFollowLinksDirty;
|
|
mSpecDirty = rhs.mSpecDirty;
|
|
mSpec = rhs.mSpec;
|
|
mAppendedPath = rhs.mAppendedPath;
|
|
mTargetSpec = rhs.mTargetSpec;
|
|
mCatInfoDirty = rhs.mCatInfoDirty;
|
|
mType = rhs.mType;
|
|
mCreator = rhs.mCreator;
|
|
|
|
if (!rhs.mCatInfoDirty)
|
|
mCachedCatInfo = rhs.mCachedCatInfo;
|
|
|
|
return *this;
|
|
}
|
|
|
|
nsLocalFile::~nsLocalFile()
|
|
{
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark [nsISupports interface implementation]
|
|
|
|
NS_IMPL_THREADSAFE_ISUPPORTS3(nsLocalFile,
|
|
nsILocalFileMac,
|
|
nsILocalFile,
|
|
nsIFile)
|
|
|
|
NS_METHOD
|
|
nsLocalFile::nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aInstancePtr);
|
|
NS_ENSURE_NO_AGGREGATION(outer);
|
|
|
|
nsLocalFile* inst = new nsLocalFile();
|
|
if (inst == NULL)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
nsresult rv = inst->QueryInterface(aIID, aInstancePtr);
|
|
if (NS_FAILED(rv))
|
|
{
|
|
delete inst;
|
|
return rv;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
// This function resets any cached information about the file.
|
|
void
|
|
nsLocalFile::MakeDirty()
|
|
{
|
|
mSpecDirty = PR_TRUE;
|
|
mFollowLinksDirty = PR_TRUE;
|
|
mCatInfoDirty = PR_TRUE;
|
|
|
|
ClearFSSpec(mTargetSpec);
|
|
}
|
|
|
|
|
|
/* attribute PRBool followLinks; */
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aFollowLinks);
|
|
*aFollowLinks = mFollowLinks;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
|
|
{
|
|
if (aFollowLinks != mFollowLinks)
|
|
{
|
|
mFollowLinks = aFollowLinks;
|
|
mFollowLinksDirty = PR_TRUE;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::ResolveAndStat()
|
|
{
|
|
OSErr err = noErr;
|
|
FSSpec resolvedSpec;
|
|
|
|
// fnfErr means target spec is valid but doesn't exist.
|
|
// If the end result is fnfErr, we're cleanly resolved.
|
|
|
|
if (mSpecDirty)
|
|
{
|
|
if (mAppendedPath.Length())
|
|
{
|
|
err = ResolvePathAndSpec(mAppendedPath.get(), &mSpec, PR_FALSE, &resolvedSpec);
|
|
if (err == noErr)
|
|
mAppendedPath.Truncate(0);
|
|
}
|
|
else
|
|
err = ::FSMakeFSSpec(mSpec.vRefNum, mSpec.parID, mSpec.name, &resolvedSpec);
|
|
if (err == noErr)
|
|
{
|
|
mSpec = resolvedSpec;
|
|
mSpecDirty = PR_FALSE;
|
|
}
|
|
mFollowLinksDirty = PR_TRUE;
|
|
}
|
|
if (mFollowLinksDirty && (err == noErr))
|
|
{
|
|
if (mFollowLinks)
|
|
{
|
|
// Resolve the alias to the original file.
|
|
resolvedSpec = mSpec;
|
|
Boolean resolvedWasFolder, resolvedWasAlias;
|
|
err = ::ResolveAliasFile(&resolvedSpec, TRUE, &resolvedWasFolder, &resolvedWasAlias);
|
|
if (err == noErr || err == fnfErr) {
|
|
err = noErr;
|
|
mTargetSpec = resolvedSpec;
|
|
mFollowLinksDirty = PR_FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mTargetSpec = mSpec;
|
|
mFollowLinksDirty = PR_FALSE;
|
|
}
|
|
mCatInfoDirty = PR_TRUE;
|
|
}
|
|
|
|
return (MacErrorMapper(err));
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::Clone(nsIFile **file)
|
|
{
|
|
NS_ENSURE_ARG(file);
|
|
*file = nsnull;
|
|
|
|
// Just copy-construct ourselves
|
|
nsCOMPtr<nsILocalFile> localFile = new nsLocalFile(*this);
|
|
if (localFile == NULL)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
*file = localFile;
|
|
NS_ADDREF(*file);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::InitWithNativePath(const nsACString &filePath)
|
|
{
|
|
// The incoming path must be a FULL path
|
|
|
|
if (filePath.IsEmpty())
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
MakeDirty();
|
|
|
|
// If it starts with a colon, it's invalid
|
|
if (filePath.First() == ':')
|
|
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
|
|
|
|
nsPathParser parser(filePath);
|
|
OSErr err;
|
|
Str255 pascalNode;
|
|
FSSpec nodeSpec;
|
|
|
|
const char *root = parser.First();
|
|
if (root == nsnull)
|
|
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
|
|
|
|
// The first component must be an existing volume
|
|
myPLstrcpy(pascalNode, root);
|
|
pascalNode[++pascalNode[0]] = ':';
|
|
err = ::FSMakeFSSpec(0, 0, pascalNode, &nodeSpec);
|
|
if (err)
|
|
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
|
|
|
|
// Build as much of a spec as possible from the rest of the path
|
|
// What doesn't exist will be left over in mAppendedPath
|
|
const char *nextNode;
|
|
while ((nextNode = parser.Next()) != nsnull) {
|
|
long dirID;
|
|
Boolean isDir;
|
|
err = ::FSpGetDirectoryID(&nodeSpec, &dirID, &isDir);
|
|
if (err || !isDir)
|
|
break;
|
|
myPLstrcpy(pascalNode, nextNode);
|
|
err = ::FSMakeFSSpec(nodeSpec.vRefNum, dirID, pascalNode, &nodeSpec);
|
|
if (err == fnfErr)
|
|
break;
|
|
}
|
|
mSpec = nodeSpec;
|
|
mAppendedPath = parser.Remainder();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::InitWithPath(const nsAString &filePath)
|
|
{
|
|
nsresult rv;
|
|
nsCAutoString fsStr;
|
|
|
|
if (NS_SUCCEEDED(rv = nsFSStringConversionMac::UCSToFS(filePath, fsStr)))
|
|
rv = InitWithNativePath(fsStr);
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::InitWithFile(nsILocalFile *aFile)
|
|
{
|
|
NS_ENSURE_ARG(aFile);
|
|
nsLocalFile *asLocalFile = dynamic_cast<nsLocalFile*>(aFile);
|
|
if (!asLocalFile)
|
|
return NS_ERROR_NO_INTERFACE; // Well, sort of.
|
|
*this = *asLocalFile;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval)
|
|
{
|
|
// Macintosh doesn't really have mode bits, just drop them
|
|
#pragma unused (mode)
|
|
|
|
NS_ENSURE_ARG(_retval);
|
|
|
|
nsresult rv = NS_OK;
|
|
FSSpec spec;
|
|
OSErr err = noErr;
|
|
|
|
rv = ResolveAndStat();
|
|
if (rv == NS_ERROR_FILE_NOT_FOUND && (flags & PR_CREATE_FILE))
|
|
rv = NS_OK;
|
|
|
|
if (flags & PR_CREATE_FILE) {
|
|
rv = Create(nsIFile::NORMAL_FILE_TYPE, 0);
|
|
/* If opening with the PR_EXCL flag the existence of the file prior to opening is an error */
|
|
if ((flags & PR_EXCL) && (rv == NS_ERROR_FILE_ALREADY_EXISTS))
|
|
return rv;
|
|
}
|
|
|
|
rv = GetFSSpec(&spec);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
SInt8 perm;
|
|
if (flags & PR_RDWR)
|
|
perm = fsRdWrPerm;
|
|
else if (flags & PR_WRONLY)
|
|
perm = fsWrPerm;
|
|
else
|
|
perm = fsRdPerm;
|
|
|
|
short refnum;
|
|
err = ::FSpOpenDF(&spec, perm, &refnum);
|
|
|
|
if (err == noErr && (flags & PR_TRUNCATE))
|
|
err = ::SetEOF(refnum, 0);
|
|
if (err == noErr && (flags & PR_APPEND))
|
|
err = ::SetFPos(refnum, fsFromLEOF, 0);
|
|
if (err != noErr)
|
|
return MacErrorMapper(err);
|
|
|
|
if ((*_retval = PR_ImportFile(refnum)) == 0)
|
|
return NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES,(PR_GetError() & 0xFFFF));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::OpenANSIFileDesc(const char *mode, FILE * *_retval)
|
|
{
|
|
NS_ENSURE_ARG(mode);
|
|
NS_ENSURE_ARG_POINTER(_retval);
|
|
|
|
nsresult rv;
|
|
FSSpec spec;
|
|
|
|
rv = ResolveAndStat();
|
|
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
|
|
return rv;
|
|
|
|
if (mode[0] == 'w' || mode[0] == 'a') // Create if it doesn't exist
|
|
{
|
|
if (rv == NS_ERROR_FILE_NOT_FOUND) {
|
|
mType = (mode[1] == 'b') ? 'BiNA' : 'TEXT';
|
|
rv = Create(nsIFile::NORMAL_FILE_TYPE, 0);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
rv = GetFSSpec(&spec);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
#ifdef MACOSX
|
|
// FSp_fopen() doesn't exist under macosx :-(
|
|
nsXPIDLCString ourPath;
|
|
rv = GetPath(getter_Copies(ourPath));
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
*_retval = fopen(ourPath, mode);
|
|
#else
|
|
*_retval = FSp_fopen(&spec, mode);
|
|
#endif
|
|
|
|
if (*_retval)
|
|
return NS_OK;
|
|
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::Create(PRUint32 type, PRUint32 attributes)
|
|
{
|
|
OSErr err;
|
|
|
|
if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
|
|
return NS_ERROR_FILE_UNKNOWN_TYPE;
|
|
|
|
FSSpec newSpec;
|
|
|
|
if (mAppendedPath.Length())
|
|
{ // We've got an FSSpec and an appended path so pass 'em both to ResolvePathAndSpec
|
|
err = ResolvePathAndSpec(mAppendedPath.get(), &mSpec, PR_TRUE, &newSpec);
|
|
}
|
|
else
|
|
{
|
|
err = ::FSMakeFSSpec(mSpec.vRefNum, mSpec.parID, mSpec.name, &newSpec);
|
|
}
|
|
|
|
if (err != noErr && err != fnfErr)
|
|
return (MacErrorMapper(err));
|
|
|
|
switch (type)
|
|
{
|
|
case NORMAL_FILE_TYPE:
|
|
SetOSTypeAndCreatorFromExtension();
|
|
err = ::FSpCreate(&newSpec, mCreator, mType, smCurrentScript);
|
|
break;
|
|
|
|
case DIRECTORY_TYPE:
|
|
{
|
|
long newDirID;
|
|
err = ::FSpDirCreate(&newSpec, smCurrentScript, &newDirID);
|
|
// For some reason, this usually returns fnfErr, even though it works.
|
|
if (err == fnfErr)
|
|
err = noErr;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return NS_ERROR_FILE_UNKNOWN_TYPE;
|
|
break;
|
|
}
|
|
|
|
if (err == noErr)
|
|
{
|
|
mSpec = mTargetSpec = newSpec;
|
|
mAppendedPath.Truncate(0);
|
|
}
|
|
|
|
return (MacErrorMapper(err));
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::AppendNative(const nsACString &aNode)
|
|
{
|
|
if (aNode.IsEmpty())
|
|
return NS_OK;
|
|
|
|
nsACString::const_iterator start, end;
|
|
aNode.BeginReading(start);
|
|
aNode.EndReading(end);
|
|
if (FindCharInReadable(':', start, end))
|
|
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
|
|
|
|
MakeDirty();
|
|
|
|
char truncBuffer[32];
|
|
const char *node = NS_TruncNodeName(PromiseFlatCString(aNode).get(), truncBuffer);
|
|
|
|
if (!mAppendedPath.Length())
|
|
{
|
|
OSErr err;
|
|
Boolean resolvedWasFolder, resolvedWasAlias;
|
|
err = ::ResolveAliasFile(&mSpec, TRUE, &resolvedWasFolder, &resolvedWasAlias);
|
|
if (err == noErr)
|
|
{
|
|
long dirID;
|
|
Boolean isDir;
|
|
|
|
if (!resolvedWasFolder)
|
|
return NS_ERROR_FILE_NOT_DIRECTORY;
|
|
if ((err = ::FSpGetDirectoryID(&mSpec, &dirID, &isDir)) != noErr)
|
|
return MacErrorMapper(err);
|
|
|
|
FSSpec childSpec;
|
|
Str255 pascalNode;
|
|
myPLstrcpy(pascalNode, node);
|
|
err = ::FSMakeFSSpec(mSpec.vRefNum, dirID, pascalNode, &childSpec);
|
|
if (err && err != fnfErr)
|
|
return MacErrorMapper(err);
|
|
mSpec = childSpec;
|
|
}
|
|
else if (err == fnfErr)
|
|
mAppendedPath.Assign(node);
|
|
else
|
|
return MacErrorMapper(err);
|
|
}
|
|
else
|
|
{
|
|
if (mAppendedPath.First() != ':')
|
|
mAppendedPath.Insert(':', 0);
|
|
mAppendedPath.Append(":");
|
|
mAppendedPath.Append(node);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::Append(const nsAString &node)
|
|
{
|
|
nsresult rv;
|
|
nsCAutoString fsStr;
|
|
|
|
if (NS_SUCCEEDED(rv = nsFSStringConversionMac::UCSToFS(node, fsStr)))
|
|
rv = AppendNative(fsStr);
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::AppendRelativeNativePath(const nsACString &relPath)
|
|
{
|
|
if (relPath.IsEmpty())
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
nsresult rv;
|
|
nsPathParser parser(relPath);
|
|
const char* node = parser.First();
|
|
|
|
while (node)
|
|
{
|
|
if (NS_FAILED(rv = AppendNative(nsDependentCString(node))))
|
|
return rv;
|
|
node = parser.Next();
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::AppendRelativePath(const nsAString &relPath)
|
|
{
|
|
nsresult rv;
|
|
nsCAutoString fsStr;
|
|
|
|
if (NS_SUCCEEDED(rv = nsFSStringConversionMac::UCSToFS(relPath, fsStr)))
|
|
rv = AppendRelativeNativePath(fsStr);
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
|
|
{
|
|
aLeafName.Truncate();
|
|
|
|
// See if we've had a path appended
|
|
if (mAppendedPath.Length())
|
|
{
|
|
const char* temp = mAppendedPath.get();
|
|
if (temp == nsnull)
|
|
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
|
|
|
|
const char* leaf = strrchr(temp, ':');
|
|
|
|
// if the working path is just a node without any directory delimeters.
|
|
if (leaf == nsnull)
|
|
leaf = temp;
|
|
else
|
|
leaf++;
|
|
|
|
aLeafName = leaf;
|
|
}
|
|
else
|
|
{
|
|
// We don't have an appended path so grab the leaf name from the FSSpec
|
|
// Convert the Pascal string to a C string
|
|
PRInt32 len = mSpec.name[0];
|
|
char* leafName = (char *)malloc(len + 1);
|
|
if (!leafName) return NS_ERROR_OUT_OF_MEMORY;
|
|
::BlockMoveData(&mSpec.name[1], leafName, len);
|
|
leafName[len] = '\0';
|
|
aLeafName = leafName;
|
|
free(leafName);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetLeafName(nsAString &aLeafName)
|
|
{
|
|
nsresult rv;
|
|
nsCAutoString fsStr;
|
|
|
|
if (NS_SUCCEEDED(rv = GetNativeLeafName(fsStr)))
|
|
rv = nsFSStringConversionMac::FSToUCS(fsStr, aLeafName);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
|
|
{
|
|
if (aLeafName.IsEmpty())
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
MakeDirty();
|
|
|
|
char truncBuffer[32];
|
|
const char *leafName = NS_TruncNodeName(PromiseFlatCString(aLeafName).get(), truncBuffer);
|
|
|
|
if (mAppendedPath.Length())
|
|
{ // Lop off the end of the appended path and replace it with the new leaf name
|
|
PRInt32 offset = mAppendedPath.RFindChar(':');
|
|
if (offset || ((!offset) && (1 < mAppendedPath.Length())))
|
|
{
|
|
mAppendedPath.Truncate(offset + 1);
|
|
}
|
|
mAppendedPath.Append(leafName);
|
|
}
|
|
else
|
|
{
|
|
// We don't have an appended path so directly modify the FSSpec
|
|
myPLstrcpy(mSpec.name, leafName);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::SetLeafName(const nsAString &aLeafName)
|
|
{
|
|
nsresult rv;
|
|
nsCAutoString fsStr;
|
|
|
|
if (NS_SUCCEEDED(rv = nsFSStringConversionMac::UCSToFS(aLeafName, fsStr)))
|
|
rv = SetNativeLeafName(fsStr);
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetNativePath(nsACString &_retval)
|
|
{
|
|
_retval.Truncate();
|
|
|
|
nsCAutoString fsCharSetPathStr;
|
|
|
|
#if TARGET_CARBON
|
|
if (sHasHFSPlusAPIs) // should always be true under Carbon, but in case...
|
|
{
|
|
OSErr err;
|
|
nsresult rv;
|
|
nsAutoString ucPathString;
|
|
|
|
if ((err = HFSPlusGetRawPath(mSpec, ucPathString)) != noErr)
|
|
return MacErrorMapper(err);
|
|
rv = nsFSStringConversionMac::UCSToFS(ucPathString, fsCharSetPathStr);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
}
|
|
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(&mSpec, &fullPathLen, &fullPathHandle);
|
|
if (!fullPathHandle)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
::HLock(fullPathHandle);
|
|
fsCharSetPathStr.Assign(*fullPathHandle, fullPathLen);
|
|
::DisposeHandle(fullPathHandle);
|
|
}
|
|
|
|
// We need to make sure that even if we have a path to a
|
|
// directory we don't return the trailing colon. It breaks
|
|
// the component manager. (Bugzilla bug #26102)
|
|
if (fsCharSetPathStr.Last() == ':')
|
|
fsCharSetPathStr.Truncate(fsCharSetPathStr.Length() - 1);
|
|
|
|
// Now, tack on mAppendedPath. It never ends in a colon.
|
|
if (mAppendedPath.Length())
|
|
{
|
|
if (mAppendedPath.First() != ':')
|
|
fsCharSetPathStr.Append(":");
|
|
fsCharSetPathStr.Append(mAppendedPath);
|
|
}
|
|
|
|
_retval = fsCharSetPathStr;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetPath(nsAString &_retval)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
#if TARGET_CARBON
|
|
if (sHasHFSPlusAPIs) // should always be true under Carbon, but in case...
|
|
{
|
|
OSErr err;
|
|
nsAutoString ucPathString;
|
|
|
|
if ((err = HFSPlusGetRawPath(mSpec, ucPathString)) != noErr)
|
|
return MacErrorMapper(err);
|
|
|
|
// We need to make sure that even if we have a path to a
|
|
// directory we don't return the trailing colon. It breaks
|
|
// the component manager. (Bugzilla bug #26102)
|
|
if (ucPathString.Last() == PRUnichar(':'))
|
|
ucPathString.Truncate(ucPathString.Length() - 1);
|
|
|
|
// Now, tack on mAppendedPath. It never ends in a colon.
|
|
if (mAppendedPath.Length())
|
|
{
|
|
nsAutoString ucAppendage;
|
|
if (mAppendedPath.First() != ':')
|
|
ucPathString.Append(PRUnichar(':'));
|
|
rv = nsFSStringConversionMac::FSToUCS(mAppendedPath, ucAppendage);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
ucPathString.Append(ucAppendage);
|
|
}
|
|
|
|
_retval = ucPathString;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
nsCAutoString fsStr;
|
|
|
|
if (NS_SUCCEEDED(rv = GetNativePath(fsStr))) {
|
|
rv = nsFSStringConversionMac::FSToUCS(fsStr, _retval);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
nsresult nsLocalFile::MoveCopy( nsIFile* newParentDir, const nsACString &newName, PRBool isCopy, PRBool followLinks )
|
|
{
|
|
OSErr macErr;
|
|
FSSpec srcSpec;
|
|
Str255 newPascalName;
|
|
nsresult rv;
|
|
|
|
StFollowLinksState srcFollowState(this, followLinks);
|
|
rv = GetFSSpec(&srcSpec);
|
|
if ( NS_FAILED( rv ) )
|
|
return rv;
|
|
|
|
// If newParentDir == nsnull, it's a simple rename
|
|
if ( !newParentDir )
|
|
{
|
|
myPLstrncpy( newPascalName, PromiseFlatCString(newName).get(), 255 );
|
|
macErr = ::FSpRename( &srcSpec, newPascalName );
|
|
return MacErrorMapper( macErr );
|
|
}
|
|
|
|
nsCOMPtr<nsILocalFileMac> destDir(do_QueryInterface( newParentDir ));
|
|
StFollowLinksState destFollowState(destDir, followLinks);
|
|
FSSpec destSpec;
|
|
rv = destDir->GetFSSpec(&destSpec);
|
|
if ( NS_FAILED( rv ) )
|
|
return rv;
|
|
|
|
long dirID;
|
|
Boolean isDirectory;
|
|
macErr = ::FSpGetDirectoryID(&destSpec, &dirID, &isDirectory);
|
|
if ( macErr || !isDirectory )
|
|
return NS_ERROR_FILE_DESTINATION_NOT_DIR;
|
|
|
|
if ( !newName.IsEmpty() )
|
|
myPLstrncpy( newPascalName, PromiseFlatCString(newName).get(), 255);
|
|
else
|
|
memcpy(newPascalName, srcSpec.name, srcSpec.name[0] + 1);
|
|
if ( isCopy )
|
|
{
|
|
macErr = ::FSpGetDirectoryID(&srcSpec, &dirID, &isDirectory);
|
|
if (macErr == noErr)
|
|
{
|
|
const PRInt32 kCopyBufferSize = (1024 * 512); // allocate our own buffer to speed file copies. Bug #103202
|
|
OSErr tempErr;
|
|
Handle copyBufferHand = ::TempNewHandle(kCopyBufferSize, &tempErr);
|
|
void* copyBuffer = nsnull;
|
|
PRInt32 copyBufferSize = 0;
|
|
|
|
// it's OK if the allocated failed; FSpFileCopy will just fall back on its own internal 16k buffer
|
|
if (copyBufferHand)
|
|
{
|
|
::HLock(copyBufferHand);
|
|
copyBuffer = *copyBufferHand;
|
|
copyBufferSize = kCopyBufferSize;
|
|
}
|
|
|
|
if ( isDirectory )
|
|
macErr = MacFSpDirectoryCopyRename( &srcSpec, &destSpec, newPascalName, copyBuffer, copyBufferSize, true, NULL );
|
|
else
|
|
macErr = ::FSpFileCopy( &srcSpec, &destSpec, newPascalName, copyBuffer, copyBufferSize, true );
|
|
|
|
if (copyBufferHand)
|
|
::DisposeHandle(copyBufferHand);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
macErr= ::FSpMoveRenameCompat(&srcSpec, &destSpec, newPascalName);
|
|
if ( macErr == diffVolErr)
|
|
{
|
|
// On a different Volume so go for Copy and then delete
|
|
rv = CopyToNative( newParentDir, newName );
|
|
if ( NS_FAILED ( rv ) )
|
|
return rv;
|
|
return Remove( PR_TRUE );
|
|
}
|
|
}
|
|
return MacErrorMapper( macErr );
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString &newName)
|
|
{
|
|
return MoveCopy( newParentDir, newName, PR_TRUE, PR_FALSE );
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
|
|
{
|
|
if (newName.IsEmpty())
|
|
return CopyToNative(newParentDir, nsCString());
|
|
|
|
nsresult rv;
|
|
nsCAutoString fsStr;
|
|
if (NS_SUCCEEDED(rv = nsFSStringConversionMac::UCSToFS(newName, fsStr)))
|
|
rv = CopyToNative(newParentDir, fsStr);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString &newName)
|
|
{
|
|
return MoveCopy( newParentDir, newName, PR_TRUE, PR_TRUE );
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
|
|
{
|
|
if (newName.IsEmpty())
|
|
return CopyToFollowingLinksNative(newParentDir, nsCString());
|
|
|
|
nsresult rv;
|
|
nsCAutoString fsStr;
|
|
if (NS_SUCCEEDED(rv = nsFSStringConversionMac::UCSToFS(newName, fsStr)))
|
|
rv = CopyToFollowingLinksNative(newParentDir, fsStr);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString &newName)
|
|
{
|
|
return MoveCopy( newParentDir, newName, PR_FALSE, PR_FALSE );
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
|
|
{
|
|
if (newName.IsEmpty())
|
|
return MoveToNative(newParentDir, nsCString());
|
|
|
|
nsresult rv;
|
|
nsCAutoString fsStr;
|
|
if (NS_SUCCEEDED(rv = nsFSStringConversionMac::UCSToFS(newName, fsStr)))
|
|
rv = MoveToNative(newParentDir, fsStr);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::Load(PRLibrary * *_retval)
|
|
{
|
|
PRBool isFile;
|
|
nsresult rv = IsFile(&isFile);
|
|
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
if (! isFile)
|
|
return NS_ERROR_FILE_IS_DIRECTORY;
|
|
|
|
NS_TIMELINE_START_TIMER("PR_LoadLibrary");
|
|
|
|
#if !TARGET_CARBON
|
|
// This call to SystemTask is here to give the OS time to grow its
|
|
// FCB (file control block) list, which it seems to be unable to
|
|
// do unless we yield some time to the OS. See bugs 64978 & 70543
|
|
// for the whole story.
|
|
::SystemTask();
|
|
#endif
|
|
|
|
// Use the new PR_LoadLibraryWithFlags which allows us to use a FSSpec
|
|
PRLibSpec libSpec;
|
|
libSpec.type = PR_LibSpec_MacIndexedFragment;
|
|
libSpec.value.mac_indexed_fragment.fsspec = &mTargetSpec;
|
|
libSpec.value.mac_indexed_fragment.index = 0;
|
|
*_retval = PR_LoadLibraryWithFlags(libSpec, 0);
|
|
|
|
NS_TIMELINE_STOP_TIMER("PR_LoadLibrary");
|
|
NS_TIMELINE_MARK_TIMER("PR_LoadLibrary");
|
|
|
|
if (*_retval)
|
|
return NS_OK;
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::Remove(PRBool recursive)
|
|
{
|
|
OSErr err;
|
|
nsresult rv;
|
|
FSSpec specToDelete;
|
|
PRBool isDir;
|
|
|
|
StFollowLinksState(this, PR_FALSE);
|
|
|
|
rv = IsDirectory(&isDir); // Calls ResolveAndStat()
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
rv = GetFSSpec(&specToDelete);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
if (isDir && recursive)
|
|
err = ::DeleteDirectory( specToDelete.vRefNum, specToDelete.parID, specToDelete.name );
|
|
else
|
|
err = ::HDelete( specToDelete.vRefNum, specToDelete.parID, specToDelete.name );
|
|
|
|
return MacErrorMapper( err );
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModifiedTime)
|
|
{
|
|
NS_ENSURE_ARG(aLastModifiedTime);
|
|
*aLastModifiedTime = 0;
|
|
|
|
nsresult rv = ResolveAndStat();
|
|
if ( NS_FAILED( rv ) )
|
|
return rv;
|
|
rv = UpdateCachedCatInfo(PR_TRUE);
|
|
if ( NS_FAILED( rv ) )
|
|
return rv;
|
|
|
|
// The mod date is in the same spot for files and dirs.
|
|
return ConvertMacTimeToMilliseconds( aLastModifiedTime, mCachedCatInfo.hFileInfo.ioFlMdDat );
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::SetLastModifiedTime(PRInt64 aLastModifiedTime)
|
|
{
|
|
nsresult rv = ResolveAndStat();
|
|
if ( NS_FAILED(rv) )
|
|
return rv;
|
|
|
|
PRUint32 macTime = 0;
|
|
OSErr err = noErr;
|
|
|
|
ConvertMillisecondsToMacTime(aLastModifiedTime, &macTime);
|
|
|
|
if (NS_SUCCEEDED(rv = UpdateCachedCatInfo(PR_TRUE)))
|
|
{
|
|
if (mCachedCatInfo.hFileInfo.ioFlAttrib & ioDirMask)
|
|
{
|
|
mCachedCatInfo.dirInfo.ioDrMdDat = macTime;
|
|
mCachedCatInfo.dirInfo.ioDrParID = mFollowLinks ? mTargetSpec.parID : mSpec.parID;
|
|
}
|
|
else
|
|
{
|
|
mCachedCatInfo.hFileInfo.ioFlMdDat = macTime;
|
|
mCachedCatInfo.hFileInfo.ioDirID = mFollowLinks ? mTargetSpec.parID : mSpec.parID;
|
|
}
|
|
|
|
err = ::PBSetCatInfoSync(&mCachedCatInfo);
|
|
if (err != noErr)
|
|
return MacErrorMapper(err);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModifiedTime)
|
|
{
|
|
NS_ENSURE_ARG(aLastModifiedTime);
|
|
|
|
nsresult rv;
|
|
PRBool isLink;
|
|
|
|
rv = IsSymlink(&isLink);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
if (!isLink)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
StFollowLinksState followState(this, PR_FALSE);
|
|
return GetLastModifiedTime(aLastModifiedTime);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModifiedTime)
|
|
{
|
|
nsresult rv;
|
|
PRBool isLink;
|
|
|
|
rv = IsSymlink(&isLink);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
if (!isLink)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
StFollowLinksState followState(this, PR_FALSE);
|
|
return SetLastModifiedTime(aLastModifiedTime);
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetFileSize(PRInt64 *aFileSize)
|
|
{
|
|
NS_ENSURE_ARG(aFileSize);
|
|
nsresult rv;
|
|
|
|
*aFileSize = LL_Zero();
|
|
|
|
if (NS_SUCCEEDED(rv = ResolveAndStat()) && NS_SUCCEEDED(rv = UpdateCachedCatInfo(PR_TRUE)))
|
|
{
|
|
if (!(mCachedCatInfo.hFileInfo.ioFlAttrib & ioDirMask))
|
|
{
|
|
long dataSize = mCachedCatInfo.hFileInfo.ioFlLgLen;
|
|
long resSize = mCachedCatInfo.hFileInfo.ioFlRLgLen;
|
|
|
|
// For now we've only got 32 bits of file size info
|
|
PRInt64 dataInt64 = LL_Zero();
|
|
PRInt64 resInt64 = LL_Zero();
|
|
|
|
// WARNING!!!!!!
|
|
//
|
|
// For now we do NOT add the data and resource fork sizes as there are several
|
|
// assumptions in the code (notably in form submit) that only the data fork is
|
|
// used.
|
|
// LL_I2L(resInt64, resSize);
|
|
|
|
LL_I2L(dataInt64, dataSize);
|
|
|
|
LL_ADD((*aFileSize), dataInt64, resInt64);
|
|
}
|
|
// leave size at zero for dirs
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::SetFileSize(PRInt64 aFileSize)
|
|
{
|
|
nsresult rv = ResolveAndStat();
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
short refNum;
|
|
OSErr err;
|
|
PRInt32 aNewLength;
|
|
|
|
LL_L2I(aNewLength, aFileSize);
|
|
|
|
// Need to open the file to set the size
|
|
if (::FSpOpenDF(&mTargetSpec, fsWrPerm, &refNum) != noErr)
|
|
return NS_ERROR_FILE_ACCESS_DENIED;
|
|
|
|
err = ::SetEOF(refNum, aNewLength);
|
|
|
|
// Close the file unless we got an error that it was already closed
|
|
if (err != fnOpnErr)
|
|
(void)::FSClose(refNum);
|
|
|
|
if (err != noErr)
|
|
return MacErrorMapper(err);
|
|
|
|
return MacErrorMapper(err);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSize)
|
|
{
|
|
NS_ENSURE_ARG(aFileSize);
|
|
|
|
StFollowLinksState followState(this, PR_FALSE);
|
|
return GetFileSize(aFileSize);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
|
|
{
|
|
NS_ENSURE_ARG(aDiskSpaceAvailable);
|
|
|
|
PRInt64 space64Bits;
|
|
|
|
LL_I2L(space64Bits , LONG_MAX);
|
|
|
|
nsresult rv = ResolveAndStat();
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
XVolumeParam pb;
|
|
pb.ioCompletion = nsnull;
|
|
pb.ioVolIndex = 0;
|
|
pb.ioNamePtr = nsnull;
|
|
pb.ioVRefNum = mFollowLinks ? mTargetSpec.vRefNum : mSpec.vRefNum;
|
|
|
|
// we should check if this call is available
|
|
OSErr err = ::PBXGetVolInfoSync(&pb);
|
|
|
|
if (err == noErr)
|
|
{
|
|
const UnsignedWide& freeBytes = UInt64ToUnsignedWide(pb.ioVFreeBytes);
|
|
#ifdef HAVE_LONG_LONG
|
|
space64Bits = UnsignedWideToUInt64(freeBytes);
|
|
#else
|
|
space64Bits.lo = freeBytes.lo;
|
|
space64Bits.hi = freeBytes.hi;
|
|
#endif
|
|
}
|
|
|
|
*aDiskSpaceAvailable = space64Bits;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetParent(nsIFile * *aParent)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aParent);
|
|
*aParent = nsnull;
|
|
|
|
nsresult rv = NS_OK;
|
|
PRInt32 offset;
|
|
|
|
nsCOMPtr<nsILocalFileMac> localFile;
|
|
PRInt32 appendedLen = mAppendedPath.Length();
|
|
OSErr err;
|
|
|
|
if (!appendedLen || (appendedLen == 1 && mAppendedPath.CharAt(0) == ':'))
|
|
{
|
|
rv = ResolveAndStat();
|
|
//if the file does not exist, does not mean that the parent does not.
|
|
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
|
|
return rv;
|
|
|
|
CInfoPBRec pBlock = {0};
|
|
FSSpec parentFolderSpec;
|
|
parentFolderSpec.name[0] = 0;
|
|
|
|
pBlock.dirInfo.ioVRefNum = mSpec.vRefNum;
|
|
pBlock.dirInfo.ioDrDirID = mSpec.parID;
|
|
pBlock.dirInfo.ioNamePtr = (StringPtr)parentFolderSpec.name;
|
|
pBlock.dirInfo.ioFDirIndex = -1; //get info on parID
|
|
err = PBGetCatInfoSync(&pBlock);
|
|
if (err != noErr)
|
|
return MacErrorMapper(err);
|
|
parentFolderSpec.vRefNum = mSpec.vRefNum;
|
|
parentFolderSpec.parID = pBlock.dirInfo.ioDrParID;
|
|
|
|
localFile = new nsLocalFile;
|
|
if (!localFile)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
rv = localFile->InitWithFSSpec(&parentFolderSpec);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
}
|
|
else
|
|
{
|
|
// trim off the last component of the appended path
|
|
// construct a new file from our spec + trimmed path
|
|
|
|
nsCAutoString parentAppendage(mAppendedPath);
|
|
|
|
if (parentAppendage.Last() == ':')
|
|
parentAppendage.Truncate(appendedLen - 1);
|
|
if ((offset = parentAppendage.RFindChar(':')) != -1)
|
|
parentAppendage.Truncate(offset);
|
|
else
|
|
parentAppendage.Truncate(0);
|
|
|
|
localFile = new nsLocalFile(mSpec, parentAppendage);
|
|
if (!localFile)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
*aParent = localFile;
|
|
NS_ADDREF(*aParent);
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::Exists(PRBool *_retval)
|
|
{
|
|
NS_ENSURE_ARG(_retval);
|
|
*_retval = PR_FALSE;
|
|
|
|
nsresult rv = ResolveAndStat();
|
|
if (NS_SUCCEEDED(rv)) {
|
|
if (NS_SUCCEEDED(UpdateCachedCatInfo(PR_TRUE)))
|
|
*_retval = PR_TRUE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::IsPackage(PRBool *outIsPackage)
|
|
{
|
|
NS_ENSURE_ARG(outIsPackage);
|
|
*outIsPackage = PR_FALSE;
|
|
|
|
// Note: IsDirectory() calls ResolveAndStat() & UpdateCachedCatInfo
|
|
PRBool isDir;
|
|
nsresult rv = IsDirectory(&isDir);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
*outIsPackage = ((mCachedCatInfo.dirInfo.ioFlAttrib & kioFlAttribDirMask) &&
|
|
(mCachedCatInfo.dirInfo.ioDrUsrWds.frFlags & kHasBundle));
|
|
|
|
if ((!*outIsPackage) && isDir)
|
|
{
|
|
// Believe it or not, folders ending with ".app" are also considered
|
|
// to be packages, even if the top-level folder doesn't have bundle set
|
|
nsCAutoString name;
|
|
if (NS_SUCCEEDED(rv = GetNativeLeafName(name)))
|
|
{
|
|
const char *extPtr = strrchr(name.get(), '.');
|
|
if (extPtr)
|
|
{
|
|
if (!nsCRT::strcasecmp(extPtr, ".app"))
|
|
{
|
|
*outIsPackage = PR_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::IsWritable(PRBool *outIsWritable)
|
|
{
|
|
NS_ENSURE_ARG(outIsWritable);
|
|
*outIsWritable = PR_TRUE;
|
|
|
|
nsresult rv = ResolveAndStat();
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = UpdateCachedCatInfo(PR_TRUE);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
*outIsWritable = !(mCachedCatInfo.hFileInfo.ioFlAttrib & kioFlAttribLockedMask);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::IsReadable(PRBool *_retval)
|
|
{
|
|
NS_ENSURE_ARG(_retval);
|
|
|
|
// is it ever not readable on Mac?
|
|
*_retval = PR_TRUE;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::IsExecutable(PRBool *outIsExecutable)
|
|
{
|
|
NS_ENSURE_ARG(outIsExecutable);
|
|
*outIsExecutable = PR_FALSE; // Assume failure
|
|
|
|
nsresult rv = ResolveAndStat();
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
#if TARGET_CARBON
|
|
// If we're running under OS X ask LaunchServices if we're executable
|
|
if (sRunningOSX)
|
|
{
|
|
if ( (UInt32)LSCopyItemInfoForRef != (UInt32)kUnresolvedCFragSymbolAddress )
|
|
{
|
|
FSRef theRef;
|
|
LSRequestedInfo theInfoRequest = kLSRequestAllInfo;
|
|
LSItemInfoRecord theInfo;
|
|
|
|
if (::FSpMakeFSRef(&mTargetSpec, &theRef) == noErr)
|
|
{
|
|
if (::LSCopyItemInfoForRef(&theRef, theInfoRequest, &theInfo) == noErr)
|
|
{
|
|
if ((theInfo.flags & kLSItemInfoIsApplication) != 0)
|
|
*outIsExecutable = PR_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
OSType fileType;
|
|
rv = GetFileType(&fileType);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
*outIsExecutable = (fileType == 'APPL' || fileType == 'appe' || fileType == 'FNDR');
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::IsDirectory(PRBool *outIsDir)
|
|
{
|
|
NS_ENSURE_ARG(outIsDir);
|
|
*outIsDir = PR_FALSE;
|
|
|
|
nsresult rv = ResolveAndStat();
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = UpdateCachedCatInfo(PR_FALSE);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
*outIsDir = (mCachedCatInfo.hFileInfo.ioFlAttrib & ioDirMask) != 0;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::IsFile(PRBool *outIsFile)
|
|
{
|
|
NS_ENSURE_ARG(outIsFile);
|
|
*outIsFile = PR_FALSE;
|
|
|
|
nsresult rv = ResolveAndStat();
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = UpdateCachedCatInfo(PR_FALSE);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
*outIsFile = (mCachedCatInfo.hFileInfo.ioFlAttrib & ioDirMask) == 0;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::IsHidden(PRBool *_retval)
|
|
{
|
|
NS_ENSURE_ARG(_retval);
|
|
*_retval = PR_FALSE;
|
|
|
|
nsresult rv = ResolveAndStat();
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = UpdateCachedCatInfo(PR_FALSE);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
*_retval = (mCachedCatInfo.hFileInfo.ioFlFndrInfo.fdFlags & kIsInvisible) != 0;
|
|
|
|
if (sRunningOSX)
|
|
{
|
|
// on Mac OS X, also follow Unix "convention" where files
|
|
// beginning with a period are considered to be hidden
|
|
nsCAutoString name;
|
|
if (NS_SUCCEEDED(rv = GetNativeLeafName(name)))
|
|
{
|
|
if (name.First() == '.')
|
|
{
|
|
*_retval = PR_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::IsSymlink(PRBool *_retval)
|
|
{
|
|
NS_ENSURE_ARG(_retval);
|
|
*_retval = PR_FALSE;
|
|
|
|
nsresult rv = ResolveAndStat();
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
Boolean isAlias, isFolder;
|
|
if (::IsAliasFile(&mSpec, &isAlias, &isFolder) == noErr)
|
|
*_retval = isAlias;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
|
|
{
|
|
NS_ENSURE_ARG(inFile);
|
|
NS_ENSURE_ARG(_retval);
|
|
*_retval = PR_FALSE;
|
|
|
|
// Building paths is expensive. If we can get the FSSpecs of
|
|
// both (they or their parents exist) just compare the specs.
|
|
nsCOMPtr<nsILocalFileMac> inMacFile(do_QueryInterface(inFile));
|
|
FSSpec fileSpec, inFileSpec;
|
|
if (NS_SUCCEEDED(GetFSSpec(&fileSpec)) && inMacFile && NS_SUCCEEDED(inMacFile->GetFSSpec(&inFileSpec)))
|
|
*_retval = IsEqualFSSpec(fileSpec, inFileSpec);
|
|
else
|
|
{
|
|
nsCAutoString filePath;
|
|
GetNativePath(filePath);
|
|
|
|
nsXPIDLCString inFilePath;
|
|
inFile->GetNativePath(inFilePath);
|
|
|
|
if (nsCRT::strcasecmp(inFilePath.get(), filePath.get()) == 0)
|
|
*_retval = PR_TRUE;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *outContains)
|
|
{
|
|
/* Note here that we make no attempt to deal with the problem
|
|
of folder aliases. Doing a 'Contains' test and dealing with
|
|
folder aliases is Hard. Think about it.
|
|
*/
|
|
*outContains = PR_FALSE;
|
|
|
|
PRBool isDir;
|
|
nsresult rv = IsDirectory(&isDir); // need to cache this
|
|
if (NS_FAILED(rv)) return rv;
|
|
if (!isDir) return NS_OK; // must be a dir to contain someone
|
|
|
|
nsCOMPtr<nsILocalFileMac> macFile(do_QueryInterface(inFile));
|
|
if (!macFile) return NS_OK; // trying to compare non-local with local file
|
|
|
|
FSSpec mySpec = mSpec;
|
|
FSSpec compareSpec;
|
|
|
|
// NOTE: we're not resolving inFile if it was an alias
|
|
StFollowLinksState followState(macFile, PR_FALSE);
|
|
rv = macFile->GetFSSpec(&compareSpec);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// if they are on different volumes, bail
|
|
if (mSpec.vRefNum != compareSpec.vRefNum)
|
|
return NS_OK;
|
|
|
|
// if recur == true, test every parent, otherwise just test the first one
|
|
// (yes, recur does not get set in this loop)
|
|
OSErr err = noErr;
|
|
do
|
|
{
|
|
FSSpec parentFolderSpec;
|
|
err = GetParentFolderSpec(compareSpec, parentFolderSpec);
|
|
if (err != noErr) break; // we reached the top
|
|
|
|
if (IsEqualFSSpec(parentFolderSpec, mySpec))
|
|
{
|
|
*outContains = PR_TRUE;
|
|
break;
|
|
}
|
|
|
|
compareSpec = parentFolderSpec;
|
|
} while (recur);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetNativeTarget(nsACString &_retval)
|
|
{
|
|
_retval.Truncate();
|
|
|
|
PRBool symLink;
|
|
|
|
nsresult rv = IsSymlink(&symLink);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
if (!symLink)
|
|
return NS_ERROR_FILE_INVALID_PATH;
|
|
|
|
StFollowLinksState followState(this, PR_TRUE);
|
|
return GetNativePath(_retval);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetTarget(nsAString &_retval)
|
|
{
|
|
nsresult rv;
|
|
nsCAutoString fsStr;
|
|
|
|
if (NS_SUCCEEDED(rv = GetNativeTarget(fsStr))) {
|
|
rv = nsFSStringConversionMac::FSToUCS(fsStr, _retval);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator * *entries)
|
|
{
|
|
nsresult rv;
|
|
|
|
*entries = nsnull;
|
|
|
|
PRBool isDir;
|
|
rv = IsDirectory(&isDir);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
if (!isDir)
|
|
return NS_ERROR_FILE_NOT_DIRECTORY;
|
|
|
|
nsDirEnumerator* dirEnum = new nsDirEnumerator();
|
|
if (dirEnum == nsnull)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
NS_ADDREF(dirEnum);
|
|
rv = dirEnum->Init(this);
|
|
if (NS_FAILED(rv))
|
|
{
|
|
NS_RELEASE(dirEnum);
|
|
return rv;
|
|
}
|
|
|
|
*entries = dirEnum;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
|
|
{
|
|
aPersistentDescriptor.Truncate();
|
|
|
|
nsresult rv = ResolveAndStat();
|
|
if ( NS_FAILED( rv ) )
|
|
return rv;
|
|
|
|
AliasHandle aliasH;
|
|
OSErr err = ::NewAlias(nil, &mTargetSpec, &aliasH);
|
|
if (err != noErr)
|
|
return MacErrorMapper(err);
|
|
|
|
PRUint32 bytes = ::GetHandleSize((Handle) aliasH);
|
|
HLock((Handle) aliasH);
|
|
char* buf = PL_Base64Encode((const char*)*aliasH, bytes, nsnull); // Passing nsnull for dest makes NULL-term string
|
|
::DisposeHandle((Handle) aliasH);
|
|
NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
aPersistentDescriptor = buf;
|
|
PR_Free(buf);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
|
|
{
|
|
if (aPersistentDescriptor.IsEmpty())
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
PRUint32 dataSize = aPersistentDescriptor.Length();
|
|
char* decodedData = PL_Base64Decode(PromiseFlatCString(aPersistentDescriptor).get(), dataSize, nsnull);
|
|
// Cast to an alias record and resolve.
|
|
AliasHandle aliasH = nsnull;
|
|
if (::PtrToHand(decodedData, &(Handle)aliasH, (dataSize * 3) / 4) != noErr)
|
|
rv = NS_ERROR_OUT_OF_MEMORY;
|
|
PR_Free(decodedData);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
Boolean changed;
|
|
FSSpec resolvedSpec;
|
|
OSErr err;
|
|
err = ::ResolveAlias(nsnull, aliasH, &resolvedSpec, &changed);
|
|
if (err == fnfErr) // resolvedSpec is valid in this case
|
|
err = noErr;
|
|
rv = MacErrorMapper(err);
|
|
DisposeHandle((Handle) aliasH);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return InitWithFSSpec(&resolvedSpec);
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
// a stack-based, exception safe class for an AEDesc
|
|
|
|
#pragma mark
|
|
class StAEDesc: public AEDesc
|
|
{
|
|
public:
|
|
StAEDesc()
|
|
{
|
|
descriptorType = typeNull;
|
|
dataHandle = nil;
|
|
}
|
|
|
|
~StAEDesc()
|
|
{
|
|
::AEDisposeDesc(this);
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
::AEDisposeDesc(this);
|
|
descriptorType = typeNull;
|
|
dataHandle = nil;
|
|
}
|
|
|
|
private:
|
|
// disallow copies and assigns
|
|
StAEDesc(const StAEDesc& rhs); // copy constructor
|
|
StAEDesc& operator= (const StAEDesc&rhs); // throws OSErrs
|
|
|
|
};
|
|
|
|
#pragma mark -
|
|
#pragma mark [Utility methods]
|
|
|
|
|
|
nsresult nsLocalFile::UpdateCachedCatInfo(PRBool forceUpdate)
|
|
{
|
|
if (!mCatInfoDirty && !forceUpdate)
|
|
return NS_OK;
|
|
|
|
FSSpec spectoUse = mFollowLinks ? mTargetSpec : mSpec;
|
|
mCachedCatInfo.hFileInfo.ioCompletion = nsnull;
|
|
mCachedCatInfo.hFileInfo.ioFDirIndex = 0; // use dirID and name
|
|
mCachedCatInfo.hFileInfo.ioVRefNum = spectoUse.vRefNum;
|
|
mCachedCatInfo.hFileInfo.ioDirID = spectoUse.parID;
|
|
mCachedCatInfo.hFileInfo.ioNamePtr = spectoUse.name;
|
|
|
|
OSErr err = ::PBGetCatInfoSync(&mCachedCatInfo);
|
|
if (err == noErr)
|
|
{
|
|
mCatInfoDirty = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
return MacErrorMapper(err);
|
|
}
|
|
|
|
|
|
nsresult nsLocalFile::FindRunningAppBySignature (OSType aAppSig, FSSpec& outSpec, ProcessSerialNumber& outPsn)
|
|
{
|
|
ProcessInfoRec info;
|
|
FSSpec tempFSSpec;
|
|
OSErr err = noErr;
|
|
|
|
outPsn.highLongOfPSN = 0;
|
|
outPsn.lowLongOfPSN = kNoProcess;
|
|
|
|
while (PR_TRUE)
|
|
{
|
|
err = ::GetNextProcess(&outPsn);
|
|
if (err == procNotFound) break;
|
|
if (err != noErr) return NS_ERROR_FAILURE;
|
|
info.processInfoLength = sizeof(ProcessInfoRec);
|
|
info.processName = nil;
|
|
info.processAppSpec = &tempFSSpec;
|
|
err = ::GetProcessInformation(&outPsn, &info);
|
|
if (err != noErr) return NS_ERROR_FAILURE;
|
|
|
|
if (info.processSignature == aAppSig)
|
|
{
|
|
outSpec = tempFSSpec;
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
return NS_ERROR_FILE_NOT_FOUND; // really process not found
|
|
}
|
|
|
|
|
|
nsresult nsLocalFile::FindRunningAppByFSSpec(const FSSpec& appSpec, ProcessSerialNumber& outPsn)
|
|
{
|
|
ProcessInfoRec info;
|
|
FSSpec tempFSSpec;
|
|
OSErr err = noErr;
|
|
|
|
outPsn.highLongOfPSN = 0;
|
|
outPsn.lowLongOfPSN = kNoProcess;
|
|
|
|
while (PR_TRUE)
|
|
{
|
|
err = ::GetNextProcess(&outPsn);
|
|
if (err == procNotFound) break;
|
|
if (err != noErr) return NS_ERROR_FAILURE;
|
|
info.processInfoLength = sizeof(ProcessInfoRec);
|
|
info.processName = nil;
|
|
info.processAppSpec = &tempFSSpec;
|
|
err = ::GetProcessInformation(&outPsn, &info);
|
|
if (err != noErr) return NS_ERROR_FAILURE;
|
|
|
|
if (IsEqualFSSpec(appSpec, *info.processAppSpec))
|
|
{
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
return NS_ERROR_FILE_NOT_FOUND; // really process not found
|
|
}
|
|
|
|
|
|
nsresult nsLocalFile::FindAppOnLocalVolumes(OSType sig, FSSpec &outSpec)
|
|
{
|
|
OSErr err;
|
|
|
|
// get the system volume
|
|
long systemFolderDirID;
|
|
short sysVRefNum;
|
|
err = FindFolder(kOnSystemDisk, kSystemFolderType, false, &sysVRefNum, &systemFolderDirID);
|
|
if (err != noErr) return NS_ERROR_FAILURE;
|
|
|
|
short vRefNum = sysVRefNum;
|
|
short index = 0;
|
|
|
|
while (true)
|
|
{
|
|
if (index == 0 || vRefNum != sysVRefNum)
|
|
{
|
|
// should we avoid AppleShare volumes?
|
|
|
|
Boolean hasDesktopDB;
|
|
err = VolHasDesktopDB(vRefNum, &hasDesktopDB);
|
|
if (err != noErr) return err;
|
|
if (hasDesktopDB)
|
|
{
|
|
err = FindAppOnVolume(sig, vRefNum, &outSpec);
|
|
if (err != afpItemNotFound) return err;
|
|
}
|
|
}
|
|
index++;
|
|
err = GetIndVolume(index, &vRefNum);
|
|
if (err == nsvErr) return fnfErr;
|
|
if (err != noErr) return err;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
#define aeSelectionKeyword 'fsel'
|
|
#define kAEOpenSelection 'sope'
|
|
#define kAERevealSelection 'srev'
|
|
#define kFinderType 'FNDR'
|
|
|
|
NS_IMETHODIMP nsLocalFile::Launch()
|
|
{
|
|
AppleEvent aeEvent = {0, nil};
|
|
AppleEvent aeReply = {0, nil};
|
|
StAEDesc aeDirDesc, listElem, myAddressDesc, fileList;
|
|
FSSpec dirSpec, appSpec;
|
|
AliasHandle DirAlias, FileAlias;
|
|
OSErr errorResult = noErr;
|
|
ProcessSerialNumber process;
|
|
|
|
// for launching a file, we'll use mTargetSpec (which is both a resolved spec and a resolved alias)
|
|
ResolveAndStat();
|
|
|
|
#if TARGET_CARBON
|
|
if (sRunningOSX)
|
|
{ // We're running under Mac OS X, LaunchServices here we come
|
|
|
|
// First we make sure the LaunchServices routine we want is implemented
|
|
if ( (UInt32)LSOpenFSRef != (UInt32)kUnresolvedCFragSymbolAddress )
|
|
{
|
|
FSRef theRef;
|
|
if (::FSpMakeFSRef(&mTargetSpec, &theRef) == noErr)
|
|
{
|
|
(void)::LSOpenFSRef(&theRef, NULL);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{ // We're running under Mac OS 8.x/9.x, use the Finder Luke
|
|
nsresult rv = FindRunningAppBySignature ('MACS', appSpec, process);
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
errorResult = AECreateDesc(typeProcessSerialNumber, (Ptr)&process, sizeof(process), &myAddressDesc);
|
|
if (errorResult == noErr)
|
|
{
|
|
/* Create the FinderEvent */
|
|
errorResult = AECreateAppleEvent(kFinderType, kAEOpenSelection, &myAddressDesc, kAutoGenerateReturnID, kAnyTransactionID,
|
|
&aeEvent);
|
|
if (errorResult == noErr)
|
|
{
|
|
errorResult = FSMakeFSSpec(mTargetSpec.vRefNum, mTargetSpec.parID, nil, &dirSpec);
|
|
NewAlias(nil, &dirSpec, &DirAlias);
|
|
/* Create alias for file */
|
|
NewAlias(nil, &mTargetSpec, &FileAlias);
|
|
|
|
/* Create the file list */
|
|
errorResult = AECreateList(nil, 0, false, &fileList);
|
|
/* create the folder descriptor */
|
|
HLock((Handle)DirAlias);
|
|
errorResult = AECreateDesc(typeAlias, (Ptr)*DirAlias, GetHandleSize((Handle)DirAlias), &aeDirDesc);
|
|
HUnlock((Handle)DirAlias);
|
|
if (errorResult == noErr)
|
|
{
|
|
errorResult = AEPutParamDesc(&aeEvent, keyDirectObject, &aeDirDesc);
|
|
if ( errorResult == noErr)
|
|
{
|
|
/* create the file descriptor and add to aliasList */
|
|
HLock((Handle)FileAlias);
|
|
errorResult = AECreateDesc(typeAlias, (Ptr)*FileAlias, GetHandleSize((Handle)FileAlias), &listElem);
|
|
HLock((Handle)FileAlias);
|
|
if (errorResult == noErr)
|
|
{
|
|
errorResult = AEPutDesc(&fileList, 0, &listElem);
|
|
if (errorResult == noErr)
|
|
{
|
|
/* Add the file alias list to the event */
|
|
errorResult = AEPutParamDesc(&aeEvent, aeSelectionKeyword, &fileList);
|
|
if (errorResult == noErr)
|
|
AESend(&aeEvent, &aeReply, kAEWaitReply + kAENeverInteract
|
|
+ kAECanSwitchLayer, kAEHighPriority, kAEDefaultTimeout, nil, nil);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsLocalFile::Reveal()
|
|
{
|
|
FSSpec specToReveal;
|
|
AppleEvent aeEvent = {0, nil};
|
|
AppleEvent aeReply = {0, nil};
|
|
StAEDesc aeDirDesc, listElem, myAddressDesc, fileList;
|
|
OSErr errorResult = noErr;
|
|
ProcessSerialNumber process;
|
|
FSSpec appSpec;
|
|
|
|
nsresult rv = ResolveAndStat();
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
rv = GetFSSpec(&specToReveal); // Pay attention to followLinks
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
rv = FindRunningAppBySignature ('MACS', appSpec, process);
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
errorResult = AECreateDesc(typeProcessSerialNumber, (Ptr)&process, sizeof(process), &myAddressDesc);
|
|
if (errorResult == noErr)
|
|
{
|
|
/* Create the FinderEvent */
|
|
#if TARGET_CARBON
|
|
// The Finder under OS X uses a different event to reveal
|
|
if (sRunningOSX)
|
|
errorResult = AECreateAppleEvent(kAEMiscStandards, kAEMakeObjectsVisible, &myAddressDesc, kAutoGenerateReturnID, kAnyTransactionID,
|
|
&aeEvent);
|
|
else
|
|
#endif
|
|
errorResult = AECreateAppleEvent(kFinderType, kAERevealSelection, &myAddressDesc, kAutoGenerateReturnID, kAnyTransactionID,
|
|
&aeEvent);
|
|
if (errorResult == noErr)
|
|
{
|
|
/* Create the file list */
|
|
errorResult = AECreateList(nil, 0, false, &fileList);
|
|
if (errorResult == noErr)
|
|
{
|
|
errorResult = AEPutPtr(&fileList, 0, typeFSS, &specToReveal, sizeof(FSSpec));
|
|
|
|
if (errorResult == noErr)
|
|
{
|
|
#if TARGET_CARBON
|
|
// When we're sending the event under OS X the FSSpec must be a keyDirectObject
|
|
if (sRunningOSX)
|
|
errorResult = AEPutParamDesc(&aeEvent, keyDirectObject, &fileList);
|
|
else
|
|
#endif
|
|
errorResult = AEPutParamDesc(&aeEvent,keySelection, &fileList);
|
|
|
|
if (errorResult == noErr)
|
|
{
|
|
errorResult = AESend(&aeEvent, &aeReply, kAENoReply, kAENormalPriority, kAEDefaultTimeout, nil, nil);
|
|
if (errorResult == noErr)
|
|
SetFrontProcess(&process);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsLocalFile::MyLaunchAppWithDoc(const FSSpec& appSpec, const FSSpec* aDocToLoad, PRBool aLaunchInBackground)
|
|
{
|
|
ProcessSerialNumber thePSN = {0};
|
|
StAEDesc target;
|
|
StAEDesc docDesc;
|
|
StAEDesc launchDesc;
|
|
StAEDesc docList;
|
|
AppleEvent theEvent = {0, nil};
|
|
AppleEvent theReply = {0, nil};
|
|
OSErr err = noErr;
|
|
Boolean autoParamValue = false;
|
|
Boolean running = false;
|
|
nsresult rv = NS_OK;
|
|
|
|
#if TARGET_CARBON
|
|
if (sRunningOSX)
|
|
{ // Under Mac OS X we'll use LaunchServices
|
|
|
|
// First we make sure the LaunchServices routine we want is implemented
|
|
if ( (UInt32)LSOpenFromRefSpec != (UInt32)kUnresolvedCFragSymbolAddress )
|
|
{
|
|
FSRef appRef;
|
|
FSRef docRef;
|
|
LSLaunchFlags theLaunchFlags = kLSLaunchDefaults;
|
|
LSLaunchFSRefSpec thelaunchSpec;
|
|
|
|
if (::FSpMakeFSRef(&appSpec, &appRef) != noErr)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
if (aDocToLoad)
|
|
if (::FSpMakeFSRef(aDocToLoad, &docRef) != noErr)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
if (aLaunchInBackground)
|
|
theLaunchFlags |= kLSLaunchDontSwitch;
|
|
|
|
memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
|
|
|
|
thelaunchSpec.appRef = &appRef;
|
|
if (aDocToLoad)
|
|
{
|
|
thelaunchSpec.numDocs = 1;
|
|
thelaunchSpec.itemRefs = &docRef;
|
|
}
|
|
thelaunchSpec.launchFlags = theLaunchFlags;
|
|
|
|
err = ::LSOpenFromRefSpec(&thelaunchSpec, NULL);
|
|
NS_ASSERTION((err != noErr), "Error calling LSOpenFromRefSpec");
|
|
if (err != noErr) return NS_ERROR_FAILURE;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{ // The old fashioned way for Mac OS 8.x/9.x
|
|
rv = FindRunningAppByFSSpec(appSpec, thePSN);
|
|
running = NS_SUCCEEDED(rv);
|
|
|
|
err = AECreateDesc(typeProcessSerialNumber, &thePSN, sizeof(thePSN), &target);
|
|
if (err != noErr) return NS_ERROR_FAILURE;
|
|
|
|
err = AECreateAppleEvent(kCoreEventClass, aDocToLoad ? kAEOpenDocuments : kAEOpenApplication, &target,
|
|
kAutoGenerateReturnID, kAnyTransactionID, &theEvent);
|
|
if (err != noErr) return NS_ERROR_FAILURE;
|
|
|
|
if (aDocToLoad)
|
|
{
|
|
err = AECreateList(nil, 0, false, &docList);
|
|
if (err != noErr) return NS_ERROR_FAILURE;
|
|
|
|
err = AECreateDesc(typeFSS, aDocToLoad, sizeof(FSSpec), &docDesc);
|
|
if (err != noErr) return NS_ERROR_FAILURE;
|
|
|
|
err = AEPutDesc(&docList, 0, &docDesc);
|
|
if (err != noErr) return NS_ERROR_FAILURE;
|
|
|
|
err = AEPutParamDesc(&theEvent, keyDirectObject, &docList);
|
|
if (err != noErr) return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
if (running)
|
|
{
|
|
err = AESend(&theEvent, &theReply, kAENoReply, kAENormalPriority, kNoTimeOut, nil, nil);
|
|
if (err != noErr) return NS_ERROR_FAILURE;
|
|
|
|
if (!aLaunchInBackground)
|
|
{
|
|
err = ::SetFrontProcess(&thePSN);
|
|
if (err != noErr) return NS_ERROR_FAILURE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LaunchParamBlockRec launchThis = {0};
|
|
PRUint16 launchControlFlags = (launchContinue | launchNoFileFlags);
|
|
if (aLaunchInBackground)
|
|
launchControlFlags |= launchDontSwitch;
|
|
|
|
err = AECoerceDesc(&theEvent, typeAppParameters, &launchDesc);
|
|
if (err != noErr) return NS_ERROR_FAILURE;
|
|
|
|
launchThis.launchAppSpec = (FSSpecPtr)&appSpec;
|
|
#if TARGET_CARBON && ACCESSOR_CALLS_ARE_FUNCTIONS
|
|
::AEGetDescData(&launchDesc, &launchThis.launchAppParameters, sizeof(launchThis.launchAppParameters));
|
|
#else
|
|
// no need to lock this handle.
|
|
launchThis.launchAppParameters = (AppParametersPtr) *(launchDesc.dataHandle);
|
|
#endif
|
|
launchThis.launchBlockID = extendedBlock;
|
|
launchThis.launchEPBLength = extendedBlockLen;
|
|
launchThis.launchFileFlags = 0;
|
|
launchThis.launchControlFlags = launchControlFlags;
|
|
err = ::LaunchApplication(&launchThis);
|
|
if (err != noErr) return NS_ERROR_FAILURE;
|
|
|
|
// let's be nice and wait until it's running
|
|
const PRUint32 kMaxTimeToWait = 60; // wait 1 sec max
|
|
PRUint32 endTicks = ::TickCount() + kMaxTimeToWait;
|
|
|
|
PRBool foundApp = PR_FALSE;
|
|
|
|
do
|
|
{
|
|
EventRecord theEvent;
|
|
(void)WaitNextEvent(nullEvent, &theEvent, 1, NULL);
|
|
|
|
ProcessSerialNumber psn;
|
|
foundApp = NS_SUCCEEDED(FindRunningAppByFSSpec(appSpec, psn));
|
|
|
|
} while (!foundApp && (::TickCount() <= endTicks));
|
|
|
|
NS_ASSERTION(foundApp, "Failed to find app after launching it");
|
|
}
|
|
|
|
if (theEvent.dataHandle != nil) AEDisposeDesc(&theEvent);
|
|
if (theReply.dataHandle != nil) AEDisposeDesc(&theReply);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark [Methods that will not be implemented on Mac]
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::Normalize()
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetPermissions(PRUint32 *aPermissions)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::SetPermissions(PRUint32 aPermissions)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissions)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::IsSpecial(PRBool *_retval)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark [nsILocalFileMac]
|
|
// Implementation of Mac specific finctions from nsILocalFileMac
|
|
|
|
|
|
NS_IMETHODIMP nsLocalFile::InitWithCFURL(CFURLRef aCFURL)
|
|
{
|
|
nsresult rv = NS_ERROR_FAILURE;
|
|
|
|
#if TARGET_CARBON
|
|
NS_ENSURE_ARG(aCFURL);
|
|
|
|
// CFURLGetFSRef can only succeed if the entire path exists.
|
|
FSRef fsRef;
|
|
if (::CFURLGetFSRef(aCFURL, &fsRef) == PR_TRUE)
|
|
rv = InitWithFSRef(&fsRef);
|
|
else
|
|
{
|
|
CFURLRef parentURL = ::CFURLCreateCopyDeletingLastPathComponent(NULL, aCFURL);
|
|
if (!parentURL)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
// Get the FSRef from the parent and the FSSpec from that
|
|
FSRef parentFSRef;
|
|
FSSpec parentFSSpec;
|
|
if (::CFURLGetFSRef(parentURL, &parentFSRef) == noErr &&
|
|
::FSGetCatalogInfo(&parentFSRef, kFSCatInfoNone,
|
|
nsnull, nsnull, &parentFSSpec, nsnull) == noErr)
|
|
{
|
|
// Get the leaf name of the file and turn it into a string HFS can use.
|
|
CFStringRef fileNameRef = ::CFURLCopyLastPathComponent(aCFURL);
|
|
if (fileNameRef)
|
|
{
|
|
TextEncoding theEncoding;
|
|
if (::UpgradeScriptInfoToTextEncoding(smSystemScript,
|
|
kTextLanguageDontCare,
|
|
kTextRegionDontCare,
|
|
NULL,
|
|
&theEncoding) != noErr)
|
|
theEncoding = kTextEncodingMacRoman;
|
|
|
|
char origName[256];
|
|
char truncBuf[32];
|
|
if (::CFStringGetCString(fileNameRef, origName, sizeof(origName), theEncoding))
|
|
{
|
|
MakeDirty();
|
|
mSpec = parentFSSpec;
|
|
mAppendedPath = NS_TruncNodeName(origName, truncBuf);
|
|
rv = NS_OK;
|
|
}
|
|
::CFRelease(fileNameRef);
|
|
}
|
|
}
|
|
::CFRelease(parentURL);
|
|
}
|
|
#endif
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsLocalFile::InitWithFSRef(const FSRef * aFSRef)
|
|
{
|
|
nsresult rv = NS_ERROR_FAILURE;
|
|
|
|
#if TARGET_CARBON
|
|
NS_ENSURE_ARG(aFSRef);
|
|
|
|
FSSpec fsSpec;
|
|
OSErr err = ::FSGetCatalogInfo(aFSRef, kFSCatInfoNone, nsnull,
|
|
nsnull, &fsSpec, nsnull);
|
|
if (err == noErr)
|
|
rv = InitWithFSSpec(&fsSpec);
|
|
else
|
|
rv = MacErrorMapper(err);
|
|
#endif
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsLocalFile::InitWithFSSpec(const FSSpec *fileSpec)
|
|
{
|
|
MakeDirty();
|
|
mSpec = *fileSpec;
|
|
mTargetSpec = *fileSpec;
|
|
mAppendedPath = "";
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsLocalFile::InitToAppWithCreatorCode(OSType aAppCreator)
|
|
{
|
|
FSSpec appSpec;
|
|
ProcessSerialNumber psn;
|
|
|
|
#if TARGET_CARBON
|
|
if (sRunningOSX)
|
|
{ // If we're running under OS X use LaunchServices to determine the app
|
|
// corresponding to the creator code
|
|
if ( (UInt32)LSFindApplicationForInfo != (UInt32)kUnresolvedCFragSymbolAddress )
|
|
{
|
|
FSRef theRef;
|
|
if (::LSFindApplicationForInfo(aAppCreator, NULL, NULL, &theRef, NULL) == noErr)
|
|
{
|
|
FSCatalogInfoBitmap whichInfo = kFSCatInfoNone;
|
|
|
|
if (::FSGetCatalogInfo(&theRef, whichInfo, NULL, NULL, &appSpec, NULL) == noErr)
|
|
return InitWithFSSpec(&appSpec);
|
|
}
|
|
|
|
// If we get here we didn't find an app
|
|
return NS_ERROR_FILE_NOT_FOUND;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// is the app running?
|
|
nsresult rv = FindRunningAppBySignature(aAppCreator, appSpec, psn);
|
|
if (rv == NS_ERROR_FILE_NOT_FOUND)
|
|
{
|
|
// we have to look on disk
|
|
rv = FindAppOnLocalVolumes(aAppCreator, appSpec);
|
|
if (NS_FAILED(rv)) return rv;
|
|
}
|
|
else if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
// init with the spec here
|
|
return InitWithFSSpec(&appSpec);
|
|
}
|
|
|
|
NS_IMETHODIMP nsLocalFile::GetFSRef(FSRef *_retval)
|
|
{
|
|
nsresult rv = NS_ERROR_FAILURE;
|
|
|
|
#if TARGET_CARBON
|
|
NS_ENSURE_ARG_POINTER(_retval);
|
|
|
|
FSSpec fsSpec;
|
|
rv = GetFSSpec(&fsSpec);
|
|
if (NS_SUCCEEDED(rv))
|
|
rv = MacErrorMapper(::FSpMakeFSRef(&fsSpec, _retval));
|
|
#endif
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsLocalFile::GetFSSpec(FSSpec *fileSpec)
|
|
{
|
|
NS_ENSURE_ARG(fileSpec);
|
|
nsresult rv = ResolveAndStat();
|
|
if (rv == NS_ERROR_FILE_NOT_FOUND)
|
|
rv = NS_OK;
|
|
if (NS_SUCCEEDED(rv))
|
|
*fileSpec = mFollowLinks ? mTargetSpec : mSpec;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsLocalFile::GetFileType(OSType *aFileType)
|
|
{
|
|
NS_ENSURE_ARG(aFileType);
|
|
|
|
FSSpec fileSpec;
|
|
(void)GetFSSpec(&fileSpec);
|
|
|
|
FInfo info;
|
|
OSErr err = ::FSpGetFInfo(&fileSpec, &info);
|
|
if (err != noErr)
|
|
{
|
|
*aFileType = mType;
|
|
return NS_ERROR_FILE_NOT_FOUND;
|
|
}
|
|
|
|
*aFileType = info.fdType;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsLocalFile::SetFileType(OSType aFileType)
|
|
{
|
|
mType = aFileType;
|
|
|
|
FSSpec fileSpec;
|
|
(void)GetFSSpec(&fileSpec);
|
|
|
|
FInfo info;
|
|
OSErr err = ::FSpGetFInfo(&fileSpec, &info);
|
|
if (err != noErr)
|
|
return NS_ERROR_FILE_NOT_FOUND;
|
|
|
|
info.fdType = aFileType;
|
|
err = ::FSpSetFInfo(&fileSpec, &info);
|
|
if (err != noErr)
|
|
return NS_ERROR_FILE_ACCESS_DENIED;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsLocalFile::GetFileCreator(OSType *aCreator)
|
|
{
|
|
NS_ENSURE_ARG(aCreator);
|
|
|
|
FSSpec fileSpec;
|
|
(void)GetFSSpec(&fileSpec);
|
|
|
|
FInfo info;
|
|
OSErr err = ::FSpGetFInfo(&fileSpec, &info);
|
|
if (err != noErr)
|
|
{
|
|
*aCreator = mCreator;
|
|
return NS_ERROR_FILE_NOT_FOUND;
|
|
}
|
|
|
|
*aCreator = info.fdCreator;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsLocalFile::SetFileCreator(OSType aCreator)
|
|
{
|
|
if (aCreator == CURRENT_PROCESS_CREATOR)
|
|
aCreator = sCurrentProcessSignature;
|
|
|
|
mCreator = aCreator;
|
|
|
|
FSSpec fileSpec;
|
|
(void)GetFSSpec(&fileSpec);
|
|
|
|
FInfo info;
|
|
OSErr err = ::FSpGetFInfo(&fileSpec, &info);
|
|
if (err != noErr)
|
|
return NS_ERROR_FILE_NOT_FOUND;
|
|
|
|
info.fdCreator = aCreator;
|
|
err = ::FSpSetFInfo(&fileSpec, &info);
|
|
if (err != noErr)
|
|
return NS_ERROR_FILE_ACCESS_DENIED;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsLocalFile::SetFileTypeAndCreatorFromExtension(const char *aExtension)
|
|
{
|
|
NS_ENSURE_ARG(aExtension);
|
|
return SetOSTypeAndCreatorFromExtension(aExtension);
|
|
}
|
|
|
|
NS_IMETHODIMP nsLocalFile::SetFileTypeAndCreatorFromMIMEType(const char *aMIMEType)
|
|
{
|
|
NS_ENSURE_ARG(aMIMEType);
|
|
|
|
nsresult rv;
|
|
nsCOMPtr<nsIInternetConfigService> icService(do_GetService
|
|
(NS_INTERNETCONFIGSERVICE_CONTRACTID, &rv));
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
nsCOMPtr<nsIMIMEInfo> mimeInfo;
|
|
PRUint32 fileType = 'TEXT';
|
|
PRUint32 fileCreator = nsILocalFileMac::CURRENT_PROCESS_CREATOR;
|
|
|
|
rv = icService->FillInMIMEInfo(aMIMEType,
|
|
nsnull, getter_AddRefs(mimeInfo));
|
|
if (NS_SUCCEEDED(rv))
|
|
rv = mimeInfo->GetMacType(&fileType);
|
|
if (NS_SUCCEEDED(rv))
|
|
rv = mimeInfo->GetMacCreator(&fileCreator);
|
|
if (NS_SUCCEEDED(rv))
|
|
rv = SetFileType(fileType);
|
|
if (NS_SUCCEEDED(rv))
|
|
rv = SetFileCreator(fileCreator);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetFileSizeWithResFork(PRInt64 *aFileSize)
|
|
{
|
|
NS_ENSURE_ARG(aFileSize);
|
|
|
|
*aFileSize = LL_Zero();
|
|
|
|
ResolveAndStat();
|
|
|
|
long dataSize = 0;
|
|
long resSize = 0;
|
|
|
|
OSErr err = FSpGetFileSize(&mTargetSpec, &dataSize, &resSize);
|
|
|
|
if (err != noErr)
|
|
return MacErrorMapper(err);
|
|
|
|
// For now we've only got 32 bits of file size info
|
|
PRInt64 dataInt64 = LL_Zero();
|
|
PRInt64 resInt64 = LL_Zero();
|
|
|
|
// Combine the size of the resource and data forks
|
|
LL_I2L(resInt64, resSize);
|
|
LL_I2L(dataInt64, dataSize);
|
|
LL_ADD((*aFileSize), dataInt64, resInt64);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
// this nsLocalFile points to the app. We want to launch it, optionally with the document.
|
|
NS_IMETHODIMP
|
|
nsLocalFile::LaunchWithDoc(nsILocalFile* aDocToLoad, PRBool aLaunchInBackground)
|
|
{
|
|
// are we launchable?
|
|
PRBool isExecutable;
|
|
nsresult rv = IsExecutable(&isExecutable);
|
|
if (NS_FAILED(rv)) return rv;
|
|
if (!isExecutable) return NS_ERROR_FILE_EXECUTION_FAILED;
|
|
|
|
FSSpec docSpec;
|
|
FSSpecPtr docSpecPtr = nsnull;
|
|
|
|
nsCOMPtr<nsILocalFileMac> macDoc = do_QueryInterface(aDocToLoad);
|
|
if (macDoc)
|
|
{
|
|
rv = macDoc->GetFSSpec(&docSpec); // XXX GetTargetFSSpec
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
docSpecPtr = &docSpec;
|
|
}
|
|
|
|
FSSpec appSpec;
|
|
rv = GetFSSpec(&appSpec); // XXX GetResolvedFSSpec
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = MyLaunchAppWithDoc(appSpec, docSpecPtr, aLaunchInBackground);
|
|
return rv;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::OpenDocWithApp(nsILocalFile* aAppToOpenWith, PRBool aLaunchInBackground)
|
|
{
|
|
// if aAppToOpenWith is nil, we have to find the app from the creator code
|
|
// of the document
|
|
nsresult rv = NS_OK;
|
|
|
|
FSSpec appSpec;
|
|
|
|
if (aAppToOpenWith)
|
|
{
|
|
nsCOMPtr<nsILocalFileMac> appFileMac = do_QueryInterface(aAppToOpenWith, &rv);
|
|
if (!appFileMac) return rv;
|
|
|
|
rv = appFileMac->GetFSSpec(&appSpec); // XXX GetTargetFSSpec
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// is it launchable?
|
|
PRBool isExecutable;
|
|
rv = aAppToOpenWith->IsExecutable(&isExecutable);
|
|
if (NS_FAILED(rv)) return rv;
|
|
if (!isExecutable) return NS_ERROR_FILE_EXECUTION_FAILED;
|
|
}
|
|
else
|
|
{
|
|
// look for one
|
|
OSType fileCreator;
|
|
rv = GetFileCreator(&fileCreator);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// just make one on the stack
|
|
nsLocalFile localAppFile;
|
|
rv = localAppFile.InitToAppWithCreatorCode(fileCreator);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = localAppFile.GetFSSpec(&appSpec); // GetTargetFSSpec
|
|
if (NS_FAILED(rv)) return rv;
|
|
}
|
|
|
|
FSSpec docSpec;
|
|
rv = GetFSSpec(&docSpec); // XXX GetResolvedFSSpec
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = MyLaunchAppWithDoc(appSpec, &docSpec, aLaunchInBackground);
|
|
return rv;
|
|
}
|
|
|
|
nsresult nsLocalFile::SetOSTypeAndCreatorFromExtension(const char* extension)
|
|
{
|
|
nsresult rv;
|
|
|
|
nsCAutoString localExtBuf;
|
|
const char *extPtr;
|
|
|
|
if (!extension)
|
|
{
|
|
rv = GetNativeLeafName(localExtBuf);
|
|
extPtr = strrchr(localExtBuf.get(), '.');
|
|
if (!extPtr)
|
|
return NS_ERROR_FAILURE;
|
|
++extPtr;
|
|
}
|
|
else
|
|
{
|
|
extPtr = extension;
|
|
if (*extPtr == '.')
|
|
++extPtr;
|
|
}
|
|
|
|
nsCOMPtr<nsIInternetConfigService> icService =
|
|
do_GetService(NS_INTERNETCONFIGSERVICE_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
nsCOMPtr<nsIMIMEInfo> mimeInfo;
|
|
rv = icService->GetMIMEInfoFromExtension(extPtr, getter_AddRefs(mimeInfo));
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
PRUint32 osType;
|
|
rv = mimeInfo->GetMacType(&osType);
|
|
if (NS_SUCCEEDED(rv))
|
|
mType = osType;
|
|
PRBool skip;
|
|
rv = ExtensionIsOnExceptionList(extPtr, &skip);
|
|
if (NS_SUCCEEDED(rv) && !skip)
|
|
{
|
|
rv = mimeInfo->GetMacCreator(&osType);
|
|
if (NS_SUCCEEDED(rv))
|
|
mCreator = osType;
|
|
}
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
nsresult nsLocalFile::ExtensionIsOnExceptionList(const char *extension, PRBool *onList)
|
|
{
|
|
// Probably want to make a global list somewhere in the future
|
|
// for now, just check for "html" and "htm"
|
|
|
|
*onList = PR_FALSE;
|
|
|
|
if (!nsCRT::strcasecmp(extension, "html") ||
|
|
!nsCRT::strcasecmp(extension, "htm"))
|
|
*onList = PR_TRUE;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
void nsLocalFile::InitClassStatics()
|
|
{
|
|
OSErr err;
|
|
|
|
|
|
if (sCurrentProcessSignature == 0)
|
|
{
|
|
ProcessSerialNumber psn;
|
|
ProcessInfoRec info;
|
|
|
|
psn.highLongOfPSN = 0;
|
|
psn.lowLongOfPSN = kCurrentProcess;
|
|
|
|
info.processInfoLength = sizeof(ProcessInfoRec);
|
|
info.processName = nil;
|
|
info.processAppSpec = nil;
|
|
err = ::GetProcessInformation(&psn, &info);
|
|
if (err == noErr)
|
|
sCurrentProcessSignature = info.processSignature;
|
|
// Try again next time if error
|
|
}
|
|
|
|
static PRBool didHFSPlusCheck = PR_FALSE;
|
|
if (!didHFSPlusCheck)
|
|
{
|
|
long response;
|
|
err = ::Gestalt(gestaltFSAttr, &response);
|
|
sHasHFSPlusAPIs = (err == noErr && (response & (1 << gestaltHasHFSPlusAPIs)) != 0);
|
|
didHFSPlusCheck = PR_TRUE;
|
|
}
|
|
|
|
static PRBool didOSXCheck = PR_FALSE;
|
|
if (!didOSXCheck)
|
|
{
|
|
long version;
|
|
sRunningOSX = (::Gestalt(gestaltSystemVersion, &version) == noErr && version >= 0x00001000);
|
|
didOSXCheck = PR_TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
#pragma mark -
|
|
|
|
// Handy dandy utility create routine for something or the other
|
|
nsresult
|
|
NS_NewNativeLocalFile(const nsACString &path, PRBool followLinks, nsILocalFile* *result)
|
|
{
|
|
nsLocalFile* file = new nsLocalFile();
|
|
if (file == nsnull)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
NS_ADDREF(file);
|
|
|
|
file->SetFollowLinks(followLinks);
|
|
|
|
if (!path.IsEmpty()) {
|
|
nsresult rv = file->InitWithNativePath(path);
|
|
if (NS_FAILED(rv)) {
|
|
NS_RELEASE(file);
|
|
return rv;
|
|
}
|
|
}
|
|
*result = file;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
NS_NewLocalFile(const nsAString &path, PRBool followLinks, nsILocalFile* *result)
|
|
{
|
|
nsCAutoString fsCharSetStr;
|
|
nsresult rv = nsFSStringConversionMac::UCSToFS(path, fsCharSetStr);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
return NS_NewNativeLocalFile(fsCharSetStr, followLinks, result);
|
|
}
|
|
|
|
nsresult
|
|
NS_NewLocalFileWithFSSpec(const FSSpec* inSpec, PRBool followLinks, nsILocalFileMac* *result)
|
|
{
|
|
nsLocalFile* file = new nsLocalFile();
|
|
if (file == nsnull)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
NS_ADDREF(file);
|
|
|
|
file->SetFollowLinks(followLinks);
|
|
|
|
nsresult rv = file->InitWithFSSpec(inSpec);
|
|
if (NS_FAILED(rv)) {
|
|
NS_RELEASE(file);
|
|
return rv;
|
|
}
|
|
*result = file;
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsLocalFile::GlobalInit()
|
|
{
|
|
}
|
|
|
|
void
|
|
nsLocalFile::GlobalShutdown()
|
|
{
|
|
nsFSStringConversionMac::CleanUp();
|
|
}
|