summaryrefslogtreecommitdiffstats
path: root/src/libs/xpcom18a4/xpcom/io/nsLocalFileMac.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:49:04 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:49:04 +0000
commit16f504a9dca3fe3b70568f67b7d41241ae485288 (patch)
treec60f36ada0496ba928b7161059ba5ab1ab224f9d /src/libs/xpcom18a4/xpcom/io/nsLocalFileMac.cpp
parentInitial commit. (diff)
downloadvirtualbox-upstream.tar.xz
virtualbox-upstream.zip
Adding upstream version 7.0.6-dfsg.upstream/7.0.6-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/libs/xpcom18a4/xpcom/io/nsLocalFileMac.cpp')
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsLocalFileMac.cpp3554
1 files changed, 3554 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/xpcom/io/nsLocalFileMac.cpp b/src/libs/xpcom18a4/xpcom/io/nsLocalFileMac.cpp
new file mode 100644
index 00000000..bebc76c1
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsLocalFileMac.cpp
@@ -0,0 +1,3554 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator client code, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Steve Dagley <sdagley@netscape.com>
+ * John R. McMullen
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+#include "nsCOMPtr.h"
+#include "nsMemory.h"
+#include "nsXPIDLString.h"
+
+#include "nsLocalFile.h"
+#include "nsNativeCharsetUtils.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 <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 [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];
+ NS_CopyNativeToUnicode(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()
+ {
+ }
+
+ 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;
+ }
+
+ private:
+ ~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)
+{
+ ClearFSSpec(mSpec);
+ ClearFSSpec(mTargetSpec);
+
+ InitClassStatics();
+ if (sCurrentProcessSignature != 0)
+ mCreator = sCurrentProcessSignature;
+}
+
+nsLocalFile::nsLocalFile(const nsLocalFile& srcFile)
+{
+ *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)
+{
+ 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;
+}
+
+#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)
+{
+ // Just copy-construct ourselves
+ *file = new nsLocalFile(*this);
+ if (!*file)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ 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 = NS_CopyUnicodeToNative(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 = NS_CopyUnicodeToNative(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 = NS_CopyUnicodeToNative(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 = NS_CopyNativeToUnicode(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 = NS_CopyUnicodeToNative(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 = NS_CopyUnicodeToNative(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 = NS_CopyNativeToUnicode(mAppendedPath, ucAppendage);
+ if (NS_FAILED(rv))
+ return rv;
+ ucPathString.Append(ucAppendage);
+ }
+
+ _retval = ucPathString;
+ }
+ else
+#endif
+ {
+ nsCAutoString fsStr;
+
+ if (NS_SUCCEEDED(rv = GetNativePath(fsStr))) {
+ rv = NS_CopyNativeToUnicode(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 = NS_CopyUnicodeToNative(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 = NS_CopyUnicodeToNative(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 = NS_CopyUnicodeToNative(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 = NS_CopyNativeToUnicode(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) == PR_TRUE) &&
+ (::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::GetCFURL(CFURLRef *_retval)
+{
+ nsresult rv = NS_ERROR_FAILURE;
+
+#if TARGET_CARBON
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = nsnull;
+
+ PRBool exists;
+ if (NS_SUCCEEDED(Exists(&exists)) && exists)
+ {
+ FSRef fsRef;
+ FSSpec fsSpec = mFollowLinks ? mTargetSpec : mSpec;
+ if (::FSpMakeFSRef(&fsSpec, &fsRef) == noErr)
+ {
+ *_retval = ::CFURLCreateFromFSRef(NULL, &fsRef);
+ if (*_retval)
+ return NS_OK;
+ }
+ }
+ else
+ {
+ nsCAutoString tempPath;
+ if (NS_SUCCEEDED(GetNativePath(tempPath)))
+ {
+ CFStringRef pathStrRef = ::CFStringCreateWithCString(NULL, tempPath.get(), kCFStringEncodingMacRoman);
+ if (!pathStrRef)
+ return NS_ERROR_FAILURE;
+ *_retval = ::CFURLCreateWithFileSystemPath(NULL, pathStrRef, kCFURLHFSPathStyle, false);
+ ::CFRelease(pathStrRef);
+ if (*_retval)
+ return NS_OK;
+ }
+ }
+#endif
+
+ return rv;
+}
+
+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 = NS_CopyUnicodeToNative(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()
+{
+}