Back to index

lightning-sunbird  0.9+nobinonly
nsLocalFileOSX.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2001, 2002
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *  Conrad Carlen <ccarlen@netscape.com>
00024  *  Jungshik Shin <jshin@mailaps.org>
00025  *  Asaf Romano <mozilla.mano@sent.com>
00026  *  Mark Mentovai <mark@moxienet.com>
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either the GNU General Public License Version 2 or later (the "GPL"), or
00030  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 #include "nsLocalFile.h"
00043 #include "nsDirectoryServiceDefs.h"
00044 
00045 #include "nsString.h"
00046 #include "nsReadableUtils.h"
00047 #include "nsIDirectoryEnumerator.h"
00048 #include "nsISimpleEnumerator.h"
00049 #include "nsITimelineService.h"
00050 #include "nsVoidArray.h"
00051 
00052 #include "plbase64.h"
00053 #include "prmem.h"
00054 #include "nsCRT.h"
00055 
00056 #include "MoreFilesX.h"
00057 #include "FSCopyObject.h"
00058 #include "nsAutoBuffer.h"
00059 
00060 // Mac Includes
00061 #include <Carbon/Carbon.h>
00062 
00063 // Unix Includes
00064 #include <unistd.h>
00065 #include <sys/stat.h>
00066 
00067 #if !defined(MAC_OS_X_VERSION_10_4) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4
00068 #define GetAliasSizeFromRecord(aliasRecord) aliasRecord.aliasSize
00069 #else
00070 #define GetAliasSizeFromRecord(aliasRecord) GetAliasSizeFromPtr(&aliasRecord)
00071 #endif
00072 
00073 //*****************************************************************************
00074 //  Static Function Prototypes
00075 //*****************************************************************************
00076 
00077 static nsresult MacErrorMapper(OSErr inErr);
00078 static OSErr FindRunningAppBySignature(OSType aAppSig, ProcessSerialNumber& outPsn);
00079 static void CopyUTF8toUTF16NFC(const nsACString& aSrc, nsAString& aResult);
00080 
00081 //*****************************************************************************
00082 //  Local Helper Classes
00083 //*****************************************************************************
00084 
00085 #pragma mark -
00086 #pragma mark [FSRef operator==]
00087 
00088 bool operator==(const FSRef& lhs, const FSRef& rhs)
00089 {
00090   return (::FSCompareFSRefs(&lhs, &rhs) == noErr);
00091 }
00092 
00093 #pragma mark -
00094 #pragma mark [StFollowLinksState]
00095 
00096 class StFollowLinksState
00097 {
00098   public:
00099     StFollowLinksState(nsLocalFile& aFile) :
00100         mFile(aFile)
00101     {
00102         mFile.GetFollowLinks(&mSavedState);
00103     }
00104 
00105     StFollowLinksState(nsLocalFile& aFile, PRBool followLinksState) :
00106         mFile(aFile)
00107     {
00108         mFile.GetFollowLinks(&mSavedState);
00109         mFile.SetFollowLinks(followLinksState);
00110     }
00111 
00112     ~StFollowLinksState()
00113     {
00114         mFile.SetFollowLinks(mSavedState);
00115     }
00116     
00117   private:
00118     nsLocalFile& mFile;
00119     PRBool mSavedState;
00120 };
00121 
00122 #pragma mark -
00123 #pragma mark [nsDirEnumerator]
00124 
00125 class nsDirEnumerator : public nsISimpleEnumerator,
00126                         public nsIDirectoryEnumerator
00127 {
00128     public:
00129 
00130         NS_DECL_ISUPPORTS
00131 
00132         nsDirEnumerator() :
00133           mIterator(nsnull),
00134           mFSRefsArray(nsnull),
00135           mArrayCnt(0), mArrayIndex(0)
00136         {
00137         }
00138 
00139         nsresult Init(nsILocalFileMac* parent) 
00140         {
00141           NS_ENSURE_ARG(parent);
00142           
00143           OSErr err;
00144           nsresult rv;
00145           FSRef parentRef;
00146           
00147           rv = parent->GetFSRef(&parentRef);
00148           if (NS_FAILED(rv))
00149             return rv;
00150           
00151           mFSRefsArray = (FSRef *)nsMemory::Alloc(sizeof(FSRef)
00152                                                   * kRequestCountPerIteration);
00153           if (!mFSRefsArray)
00154             return NS_ERROR_OUT_OF_MEMORY;
00155           
00156           err = ::FSOpenIterator(&parentRef, kFSIterateFlat, &mIterator);
00157           if (err != noErr)
00158             return MacErrorMapper(err);
00159                               
00160           return NS_OK;
00161         }
00162 
00163         NS_IMETHOD HasMoreElements(PRBool *result) 
00164         {
00165           if (mNext == nsnull) {
00166             if (mArrayIndex >= mArrayCnt) {
00167               ItemCount actualCnt;
00168               OSErr err = ::FSGetCatalogInfoBulk(mIterator,
00169                                            kRequestCountPerIteration,
00170                                            &actualCnt,
00171                                            nsnull,
00172                                            kFSCatInfoNone,
00173                                            nsnull,
00174                                            mFSRefsArray,
00175                                            nsnull,
00176                                            nsnull);
00177             
00178               if (err == noErr || err == errFSNoMoreItems) {
00179                 mArrayCnt = actualCnt;
00180                 mArrayIndex = 0;
00181               }
00182             }
00183             if (mArrayIndex < mArrayCnt) {
00184               nsLocalFile *newFile = new nsLocalFile;
00185               if (!newFile)
00186                 return NS_ERROR_OUT_OF_MEMORY;
00187               FSRef fsRef = mFSRefsArray[mArrayIndex];
00188               if (NS_FAILED(newFile->InitWithFSRef(&fsRef)))
00189                 return NS_ERROR_FAILURE;
00190               mArrayIndex++;
00191               mNext = newFile;
00192             } 
00193           }
00194           *result = mNext != nsnull;
00195           if (!*result)
00196             Close();
00197           return NS_OK;
00198         }
00199 
00200         NS_IMETHOD GetNext(nsISupports **result) 
00201         {
00202             NS_ENSURE_ARG_POINTER(result);
00203             *result = nsnull;
00204 
00205             nsresult rv;
00206             PRBool hasMore;
00207             rv = HasMoreElements(&hasMore);
00208             if (NS_FAILED(rv)) return rv;
00209 
00210             *result = mNext;        // might return nsnull
00211             NS_IF_ADDREF(*result);
00212 
00213             mNext = nsnull;
00214             return NS_OK;
00215         }
00216 
00217         NS_IMETHOD GetNextFile(nsIFile **result)
00218         {
00219             *result = nsnull;
00220             PRBool hasMore = PR_FALSE;
00221             nsresult rv = HasMoreElements(&hasMore);
00222             if (NS_FAILED(rv) || !hasMore)
00223                 return rv;
00224             *result = mNext;
00225             NS_IF_ADDREF(*result);
00226             mNext = nsnull;
00227             return NS_OK;
00228         }
00229 
00230         NS_IMETHOD Close()
00231         {
00232           if (mIterator) {
00233             ::FSCloseIterator(mIterator);
00234             mIterator = nsnull;
00235           }
00236           if (mFSRefsArray) {
00237             nsMemory::Free(mFSRefsArray);
00238             mFSRefsArray = nsnull;
00239           }
00240           return NS_OK;
00241         }
00242 
00243     private:
00244         ~nsDirEnumerator() 
00245         {
00246           Close();
00247         }
00248 
00249     protected:
00250         // According to Apple doc, request the number of objects
00251         // per call that will fit in 4 VM pages.
00252         enum {
00253           kRequestCountPerIteration = ((4096 * 4) / sizeof(FSRef))
00254         };
00255         
00256         nsCOMPtr<nsILocalFileMac>   mNext;
00257         
00258         FSIterator              mIterator;
00259         FSRef                   *mFSRefsArray;
00260         PRInt32                 mArrayCnt, mArrayIndex;
00261 };
00262 
00263 NS_IMPL_ISUPPORTS2(nsDirEnumerator, nsISimpleEnumerator, nsIDirectoryEnumerator)
00264 
00265 #pragma mark -
00266 #pragma mark [StAEDesc]
00267 
00268 class StAEDesc: public AEDesc
00269 {
00270 public:
00271     StAEDesc()
00272     {
00273       descriptorType = typeNull;
00274       dataHandle = nil;
00275     }
00276               
00277     ~StAEDesc()
00278     {
00279       ::AEDisposeDesc(this);
00280     }
00281 };
00282 
00283 #define FILENAME_BUFFER_SIZE 512
00284 
00285 //*****************************************************************************
00286 //  nsLocalFile
00287 //*****************************************************************************
00288 
00289 const char      nsLocalFile::kPathSepChar = '/';
00290 const PRUnichar nsLocalFile::kPathSepUnichar = '/';
00291 
00292 // The HFS+ epoch is Jan. 1, 1904 GMT - differs from HFS in which times were local
00293 // The NSPR epoch is Jan. 1, 1970 GMT
00294 // 2082844800 is the difference in seconds between those dates
00295 const PRInt64   nsLocalFile::kJanuaryFirst1970Seconds = 2082844800LL;
00296 
00297 #pragma mark -
00298 #pragma mark [CTORs/DTOR]
00299 
00300 nsLocalFile::nsLocalFile() :
00301   mBaseRef(nsnull),
00302   mTargetRef(nsnull),
00303   mCachedFSRefValid(PR_FALSE),
00304   mFollowLinks(PR_TRUE),
00305   mFollowLinksDirty(PR_TRUE)
00306 {
00307 }
00308 
00309 nsLocalFile::nsLocalFile(const nsLocalFile& src) :
00310   mBaseRef(src.mBaseRef),
00311   mTargetRef(src.mTargetRef),
00312   mCachedFSRef(src.mCachedFSRef),
00313   mCachedFSRefValid(src.mCachedFSRefValid),
00314   mFollowLinks(src.mFollowLinks),
00315   mFollowLinksDirty(src.mFollowLinksDirty)
00316 {
00317   // A CFURLRef is immutable so no need to copy, just retain.
00318   if (mBaseRef)
00319     ::CFRetain(mBaseRef);
00320   if (mTargetRef)
00321     ::CFRetain(mTargetRef);
00322 }
00323 
00324 nsLocalFile::~nsLocalFile()
00325 {
00326   if (mBaseRef)
00327     ::CFRelease(mBaseRef);
00328   if (mTargetRef)
00329     ::CFRelease(mTargetRef);
00330 }
00331 
00332 
00333 //*****************************************************************************
00334 //  nsLocalFile::nsISupports
00335 //*****************************************************************************
00336 #pragma mark -
00337 #pragma mark [nsISupports]
00338 
00339 NS_IMPL_THREADSAFE_ISUPPORTS4(nsLocalFile,
00340                               nsILocalFileMac,
00341                               nsILocalFile,
00342                               nsIFile,
00343                               nsILocalFileMac_MOZILLA_1_8_BRANCH)
00344                               
00345 NS_METHOD nsLocalFile::nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
00346 {
00347   NS_ENSURE_ARG_POINTER(aInstancePtr);
00348   NS_ENSURE_NO_AGGREGATION(outer);
00349 
00350   nsLocalFile* inst = new nsLocalFile();
00351   if (inst == NULL)
00352     return NS_ERROR_OUT_OF_MEMORY;
00353   
00354   nsresult rv = inst->QueryInterface(aIID, aInstancePtr);
00355   if (NS_FAILED(rv))
00356   {
00357     delete inst;
00358     return rv;
00359   }
00360   return NS_OK;
00361 }
00362 
00363 
00364 //*****************************************************************************
00365 //  nsLocalFile::nsIFile
00366 //*****************************************************************************
00367 #pragma mark -
00368 #pragma mark [nsIFile]
00369 
00370 /* void append (in AString node); */
00371 NS_IMETHODIMP nsLocalFile::Append(const nsAString& aNode)
00372 {
00373   return AppendNative(NS_ConvertUCS2toUTF8(aNode));
00374 }
00375 
00376 /* [noscript] void appendNative (in ACString node); */
00377 NS_IMETHODIMP nsLocalFile::AppendNative(const nsACString& aNode)
00378 {
00379   if (!mBaseRef)
00380     return NS_ERROR_NOT_INITIALIZED;
00381 
00382   nsACString::const_iterator start, end;
00383   aNode.BeginReading(start);
00384   aNode.EndReading(end);
00385   if (FindCharInReadable(kPathSepChar, start, end))
00386     return NS_ERROR_FILE_UNRECOGNIZED_PATH;
00387 
00388   CFStringRef nodeStrRef = ::CFStringCreateWithCString(kCFAllocatorDefault,
00389                                   PromiseFlatCString(aNode).get(),
00390                                   kCFStringEncodingUTF8);
00391   if (nodeStrRef) {
00392     CFURLRef newRef = ::CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault,
00393                                   mBaseRef, nodeStrRef, PR_FALSE);
00394     ::CFRelease(nodeStrRef);
00395     if (newRef) {
00396       SetBaseRef(newRef);
00397       ::CFRelease(newRef);
00398       return NS_OK;
00399     }
00400   }
00401   return NS_ERROR_FAILURE;
00402 }
00403 
00404 /* void normalize (); */
00405 NS_IMETHODIMP nsLocalFile::Normalize()
00406 {
00407     return NS_OK;
00408 }
00409 
00410 /* void create (in unsigned long type, in unsigned long permissions); */
00411 NS_IMETHODIMP nsLocalFile::Create(PRUint32 type, PRUint32 permissions)
00412 {
00413   if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
00414     return NS_ERROR_FILE_UNKNOWN_TYPE;
00415   if (!mBaseRef)
00416     return NS_ERROR_NOT_INITIALIZED;
00417   
00418   nsStringArray nonExtantNodes;
00419   CFURLRef pathURLRef = mBaseRef;
00420   FSRef pathFSRef;
00421   CFStringRef leafStrRef = nsnull;
00422   nsAutoBuffer<UniChar, FILENAME_BUFFER_SIZE> buffer;
00423   Boolean success;
00424   
00425   // Work backwards through the path to find the last node which
00426   // exists. Place the nodes which don't exist in an array and we'll
00427   // create those below.
00428   while ((success = ::CFURLGetFSRef(pathURLRef, &pathFSRef)) == false) {
00429     leafStrRef = ::CFURLCopyLastPathComponent(pathURLRef);
00430     if (!leafStrRef)
00431       break;
00432     CFIndex leafLen = ::CFStringGetLength(leafStrRef);
00433     if (!buffer.EnsureElemCapacity(leafLen + 1))
00434       break;
00435     ::CFStringGetCharacters(leafStrRef, CFRangeMake(0, leafLen), buffer.get());
00436     buffer.get()[leafLen] = '\0';
00437     nonExtantNodes.AppendString(nsString(nsDependentString(buffer.get())));
00438     ::CFRelease(leafStrRef);
00439     leafStrRef = nsnull;
00440     
00441     // Get the parent of the leaf for the next go round
00442     CFURLRef parent = ::CFURLCreateCopyDeletingLastPathComponent(NULL, pathURLRef);
00443     if (!parent)
00444       break;
00445     if (pathURLRef != mBaseRef)
00446       ::CFRelease(pathURLRef);
00447     pathURLRef = parent;
00448   }
00449   if (pathURLRef != mBaseRef)
00450     ::CFRelease(pathURLRef);
00451   if (leafStrRef != nsnull)
00452     ::CFRelease(leafStrRef);
00453   if (!success)
00454     return NS_ERROR_FAILURE;
00455   PRInt32 nodesToCreate = nonExtantNodes.Count();
00456   if (nodesToCreate == 0)
00457     return NS_ERROR_FILE_ALREADY_EXISTS;
00458   
00459   OSErr err;    
00460   nsAutoString nextNodeName;
00461   for (PRInt32 i = nodesToCreate - 1; i > 0; i--) {
00462     nonExtantNodes.StringAt(i, nextNodeName);
00463     err = ::FSCreateDirectoryUnicode(&pathFSRef,
00464                                       nextNodeName.Length(),
00465                                       (const UniChar *)nextNodeName.get(),
00466                                       kFSCatInfoNone,
00467                                       nsnull, &pathFSRef, nsnull, nsnull);
00468     if (err != noErr)
00469       return MacErrorMapper(err);
00470   }
00471   nonExtantNodes.StringAt(0, nextNodeName);
00472   if (type == NORMAL_FILE_TYPE) {
00473     err = ::FSCreateFileUnicode(&pathFSRef,
00474                                 nextNodeName.Length(),
00475                                 (const UniChar *)nextNodeName.get(),
00476                                 kFSCatInfoNone,
00477                                 nsnull, nsnull, nsnull);
00478   }
00479   else {
00480     err = ::FSCreateDirectoryUnicode(&pathFSRef,
00481                                     nextNodeName.Length(),
00482                                     (const UniChar *)nextNodeName.get(),
00483                                     kFSCatInfoNone,
00484                                     nsnull, nsnull, nsnull, nsnull);
00485   }
00486             
00487   return MacErrorMapper(err);
00488 }
00489 
00490 /* attribute AString leafName; */
00491 NS_IMETHODIMP nsLocalFile::GetLeafName(nsAString& aLeafName)
00492 {
00493   nsCAutoString nativeString;
00494   nsresult rv = GetNativeLeafName(nativeString);
00495   if (NS_FAILED(rv))
00496     return rv;
00497   CopyUTF8toUTF16NFC(nativeString, aLeafName);
00498   return NS_OK;
00499 }
00500 
00501 NS_IMETHODIMP nsLocalFile::SetLeafName(const nsAString& aLeafName)
00502 {
00503   return SetNativeLeafName(NS_ConvertUCS2toUTF8(aLeafName));
00504 }
00505 
00506 /* [noscript] attribute ACString nativeLeafName; */
00507 NS_IMETHODIMP nsLocalFile::GetNativeLeafName(nsACString& aNativeLeafName)
00508 {
00509   if (!mBaseRef)
00510     return NS_ERROR_NOT_INITIALIZED;
00511   nsresult rv = NS_ERROR_FAILURE;
00512   CFStringRef leafStrRef = ::CFURLCopyLastPathComponent(mBaseRef);
00513   if (leafStrRef) {
00514     rv = CFStringReftoUTF8(leafStrRef, aNativeLeafName);
00515     ::CFRelease(leafStrRef);
00516   }
00517   return rv;                                  
00518 }
00519 
00520 NS_IMETHODIMP nsLocalFile::SetNativeLeafName(const nsACString& aNativeLeafName)
00521 {
00522   if (!mBaseRef)
00523     return NS_ERROR_NOT_INITIALIZED;
00524   nsresult rv = NS_ERROR_FAILURE;
00525   CFURLRef parentURLRef = ::CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, mBaseRef);
00526   if (parentURLRef) {
00527     CFStringRef nodeStrRef = ::CFStringCreateWithCString(kCFAllocatorDefault,
00528                                     PromiseFlatCString(aNativeLeafName).get(),
00529                                     kCFStringEncodingUTF8);
00530 
00531     if (nodeStrRef) {
00532       CFURLRef newURLRef = ::CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault,
00533                                     parentURLRef, nodeStrRef, PR_FALSE);
00534       if (newURLRef) {
00535         SetBaseRef(newURLRef);
00536         ::CFRelease(newURLRef);
00537         rv = NS_OK;
00538       }
00539       ::CFRelease(nodeStrRef);
00540     }
00541     ::CFRelease(parentURLRef);
00542   }
00543   return rv;
00544 }
00545 
00546 /* void copyTo (in nsIFile newParentDir, in AString newName); */
00547 NS_IMETHODIMP nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString& newName)
00548 {
00549   return CopyInternal(newParentDir, newName, PR_FALSE);
00550 }
00551 
00552 /* [noscrpit] void CopyToNative (in nsIFile newParentDir, in ACString newName); */
00553 NS_IMETHODIMP nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString& newName)
00554 {
00555   return CopyInternal(newParentDir, NS_ConvertUTF8toUCS2(newName), PR_FALSE);
00556 }
00557 
00558 /* void copyToFollowingLinks (in nsIFile newParentDir, in AString newName); */
00559 NS_IMETHODIMP nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString& newName)
00560 {
00561   return CopyInternal(newParentDir, newName, PR_TRUE);
00562 }
00563 
00564 /* [noscript] void copyToFollowingLinksNative (in nsIFile newParentDir, in ACString newName); */
00565 NS_IMETHODIMP nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString& newName)
00566 {
00567   return CopyInternal(newParentDir, NS_ConvertUTF8toUCS2(newName), PR_TRUE);
00568 }
00569 
00570 /* void moveTo (in nsIFile newParentDir, in AString newName); */
00571 NS_IMETHODIMP nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString& newName)
00572 {
00573   return MoveToNative(newParentDir, NS_ConvertUCS2toUTF8(newName));
00574 }
00575 
00576 /* [noscript] void moveToNative (in nsIFile newParentDir, in ACString newName); */
00577 NS_IMETHODIMP nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString& newName)
00578 {
00579   StFollowLinksState followLinks(*this, PR_FALSE);
00580 
00581   PRBool isDirectory;
00582   nsresult rv = IsDirectory(&isDirectory);
00583   if (NS_FAILED(rv))
00584     return rv;
00585 
00586   // Get the source path.
00587   nsCAutoString srcPath;
00588   rv = GetNativePath(srcPath);
00589   if (NS_FAILED(rv))
00590     return rv;
00591 
00592   // Build the destination path.
00593   nsCOMPtr<nsIFile> parentDir = newParentDir;
00594   if (!parentDir) {
00595     if (newName.IsEmpty())
00596       return NS_ERROR_INVALID_ARG;
00597     rv = GetParent(getter_AddRefs(parentDir));
00598     if (NS_FAILED(rv))
00599       return rv;   
00600   }
00601   else {
00602     PRBool exists;
00603     rv = parentDir->Exists(&exists);
00604     if (NS_FAILED(rv))
00605       return rv;
00606     if (!exists) {
00607       rv = parentDir->Create(nsIFile::DIRECTORY_TYPE, 0777);
00608       if (NS_FAILED(rv))
00609         return rv;
00610     }
00611   }
00612 
00613   nsCAutoString destPath;
00614   rv = parentDir->GetNativePath(destPath);
00615   if (NS_FAILED(rv))
00616     return rv;
00617 
00618   if (!newName.IsEmpty())
00619     destPath.Append(NS_LITERAL_CSTRING("/") + newName);
00620   else {
00621     nsCAutoString leafName;
00622     rv = GetNativeLeafName(leafName);
00623     if (NS_FAILED(rv))
00624       return rv;
00625     destPath.Append(NS_LITERAL_CSTRING("/") + leafName);
00626   }
00627 
00628   // Perform the move.
00629   if (rename(srcPath.get(), destPath.get()) != 0) {
00630     if (errno == EXDEV) {
00631       // Can't move across volume (device) boundaries.  Copy and remove.
00632       rv = CopyToNative(parentDir, newName);
00633       if (NS_SUCCEEDED(rv)) {
00634         // Permit removal failure.
00635         Remove(PR_TRUE);
00636       }
00637     }
00638     else
00639       rv = NSRESULT_FOR_ERRNO();
00640 
00641     if (NS_FAILED(rv))
00642       return rv;
00643   }
00644 
00645   // Update |this| to refer to the moved file.
00646   CFURLRef newBaseRef =
00647    ::CFURLCreateFromFileSystemRepresentation(NULL, (UInt8*)destPath.get(),
00648                                              destPath.Length(), isDirectory);
00649   if (!newBaseRef)
00650     return NS_ERROR_FAILURE;
00651   SetBaseRef(newBaseRef);
00652   ::CFRelease(newBaseRef);
00653 
00654   return rv;
00655 }
00656 
00657 /* void remove (in boolean recursive); */
00658 NS_IMETHODIMP nsLocalFile::Remove(PRBool recursive)
00659 {
00660   // XXX If we're an alias, never remove target
00661   StFollowLinksState followLinks(*this, PR_FALSE);
00662 
00663   PRBool isDirectory;
00664   nsresult rv = IsDirectory(&isDirectory);
00665   if (NS_FAILED(rv))
00666     return rv;
00667 
00668   if (recursive && isDirectory) {
00669     FSRef fsRef;
00670     rv = GetFSRefInternal(fsRef);
00671     if (NS_FAILED(rv))
00672       return rv;
00673 
00674     // Call MoreFilesX to do a recursive removal.
00675     OSStatus err = ::FSDeleteContainer(&fsRef);
00676     rv = MacErrorMapper(err);
00677   }
00678   else {
00679     nsCAutoString path;
00680     rv = GetNativePath(path);
00681     if (NS_FAILED(rv))
00682       return rv;
00683 
00684     const char* pathPtr = path.get();
00685     int status;
00686     if (isDirectory)
00687       status = rmdir(pathPtr);
00688     else
00689       status = unlink(pathPtr);
00690 
00691     if (status != 0)
00692       rv = NSRESULT_FOR_ERRNO();
00693   }
00694 
00695   mCachedFSRefValid = PR_FALSE;
00696   return rv;
00697 }
00698 
00699 /* attribute unsigned long permissions; */
00700 NS_IMETHODIMP nsLocalFile::GetPermissions(PRUint32 *aPermissions)
00701 {
00702   NS_ENSURE_ARG_POINTER(aPermissions);
00703   
00704   FSRef fsRef;
00705   nsresult rv = GetFSRefInternal(fsRef);
00706   if (NS_FAILED(rv))
00707     return rv;
00708     
00709   FSCatalogInfo catalogInfo;
00710   OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoPermissions, &catalogInfo,
00711                   nsnull, nsnull, nsnull);
00712   if (err != noErr)
00713     return MacErrorMapper(err);
00714   FSPermissionInfo *permPtr = (FSPermissionInfo*)catalogInfo.permissions;
00715   *aPermissions = permPtr->mode;
00716   return NS_OK;
00717 }
00718 
00719 NS_IMETHODIMP nsLocalFile::SetPermissions(PRUint32 aPermissions)
00720 {
00721   FSRef fsRef;
00722   nsresult rv = GetFSRefInternal(fsRef);
00723   if (NS_FAILED(rv))
00724     return rv;
00725   
00726   FSCatalogInfo catalogInfo;
00727   OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoPermissions, &catalogInfo,
00728                   nsnull, nsnull, nsnull);
00729   if (err != noErr)
00730     return MacErrorMapper(err);
00731   FSPermissionInfo *permPtr = (FSPermissionInfo*)catalogInfo.permissions;
00732   permPtr->mode = (UInt16)aPermissions;
00733   err = ::FSSetCatalogInfo(&fsRef, kFSCatInfoPermissions, &catalogInfo);
00734   return MacErrorMapper(err);
00735 }
00736 
00737 /* attribute unsigned long permissionsOfLink; */
00738 NS_IMETHODIMP nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink)
00739 {
00740     NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
00741     return NS_ERROR_NOT_IMPLEMENTED;
00742 }
00743 
00744 NS_IMETHODIMP nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissionsOfLink)
00745 {
00746     NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
00747     return NS_ERROR_NOT_IMPLEMENTED;
00748 }
00749 
00750 /* attribute PRInt64 lastModifiedTime; */
00751 NS_IMETHODIMP nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModifiedTime)
00752 {
00753   NS_ENSURE_ARG_POINTER(aLastModifiedTime);
00754   
00755   FSRef fsRef;
00756   nsresult rv = GetFSRefInternal(fsRef);
00757   if (NS_FAILED(rv))
00758     return rv;
00759     
00760   FSCatalogInfo catalogInfo;
00761   OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoContentMod, &catalogInfo,
00762                                 nsnull, nsnull, nsnull);
00763   if (err != noErr)
00764     return MacErrorMapper(err);
00765   *aLastModifiedTime = HFSPlustoNSPRTime(catalogInfo.contentModDate);  
00766   return NS_OK;
00767 }
00768 
00769 NS_IMETHODIMP nsLocalFile::SetLastModifiedTime(PRInt64 aLastModifiedTime)
00770 {
00771   OSErr err;
00772   nsresult rv;
00773   FSRef fsRef;
00774   FSCatalogInfo catalogInfo;
00775 
00776   rv = GetFSRefInternal(fsRef);
00777   if (NS_FAILED(rv))
00778     return rv;
00779 
00780   FSRef parentRef;
00781   PRBool notifyParent;
00782 
00783   /* Get the node flags, the content modification date and time, and the parent ref */
00784   err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags + kFSCatInfoContentMod,
00785                            &catalogInfo, NULL, NULL, &parentRef);
00786   if (err != noErr)
00787     return MacErrorMapper(err);
00788   
00789   /* Notify the parent if this is a file */
00790   notifyParent = (0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask));
00791 
00792   NSPRtoHFSPlusTime(aLastModifiedTime, catalogInfo.contentModDate);
00793   err = ::FSSetCatalogInfo(&fsRef, kFSCatInfoContentMod, &catalogInfo);
00794   if (err != noErr)
00795     return MacErrorMapper(err);
00796 
00797   /* Send a notification for the parent of the file, or for the directory */
00798   err = FNNotify(notifyParent ? &parentRef : &fsRef, kFNDirectoryModifiedMessage, kNilOptions);
00799   if (err != noErr)
00800     return MacErrorMapper(err);
00801 
00802   return NS_OK;
00803 }
00804 
00805 /* attribute PRInt64 lastModifiedTimeOfLink; */
00806 NS_IMETHODIMP nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModifiedTimeOfLink)
00807 {
00808     NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
00809     return NS_ERROR_NOT_IMPLEMENTED;
00810 }
00811 NS_IMETHODIMP nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModifiedTimeOfLink)
00812 {
00813     NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
00814     return NS_ERROR_NOT_IMPLEMENTED;
00815 }
00816 
00817 /* attribute PRInt64 fileSize; */
00818 NS_IMETHODIMP nsLocalFile::GetFileSize(PRInt64 *aFileSize)
00819 {
00820   NS_ENSURE_ARG_POINTER(aFileSize);
00821   *aFileSize = 0;
00822   
00823   FSRef fsRef;
00824   nsresult rv = GetFSRefInternal(fsRef);
00825   if (NS_FAILED(rv))
00826     return rv;
00827       
00828   FSCatalogInfo catalogInfo;
00829   OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags + kFSCatInfoDataSizes, &catalogInfo,
00830                                   nsnull, nsnull, nsnull);
00831   if (err != noErr)
00832     return MacErrorMapper(err);
00833   
00834   // FSGetCatalogInfo can return a bogus size for directories sometimes, so only
00835   // rely on the answer for files
00836   if ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) == 0)
00837       *aFileSize = catalogInfo.dataLogicalSize;
00838   return NS_OK;
00839 }
00840 
00841 NS_IMETHODIMP nsLocalFile::SetFileSize(PRInt64 aFileSize)
00842 {
00843   FSRef fsRef;
00844   nsresult rv = GetFSRefInternal(fsRef);
00845   if (NS_FAILED(rv))
00846     return rv;
00847   
00848   SInt16 refNum;    
00849   OSErr err = ::FSOpenFork(&fsRef, 0, nsnull, fsWrPerm, &refNum);
00850   if (err != noErr)
00851     return MacErrorMapper(err);
00852   err = ::FSSetForkSize(refNum, fsFromStart, aFileSize);
00853   ::FSCloseFork(refNum);  
00854   
00855   return MacErrorMapper(err);
00856 }
00857 
00858 /* readonly attribute PRInt64 fileSizeOfLink; */
00859 NS_IMETHODIMP nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSizeOfLink)
00860 {
00861   NS_ENSURE_ARG_POINTER(aFileSizeOfLink);
00862   
00863   StFollowLinksState followLinks(*this, PR_FALSE);
00864   return GetFileSize(aFileSizeOfLink);
00865 }
00866 
00867 /* readonly attribute AString target; */
00868 NS_IMETHODIMP nsLocalFile::GetTarget(nsAString& aTarget)
00869 {
00870     NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
00871     return NS_ERROR_NOT_IMPLEMENTED;
00872 }
00873 
00874 /* [noscript] readonly attribute ACString nativeTarget; */
00875 NS_IMETHODIMP nsLocalFile::GetNativeTarget(nsACString& aNativeTarget)
00876 {
00877     NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
00878     return NS_ERROR_NOT_IMPLEMENTED;
00879 }
00880 
00881 /* readonly attribute AString path; */
00882 NS_IMETHODIMP nsLocalFile::GetPath(nsAString& aPath)
00883 {
00884   nsCAutoString nativeString;
00885   nsresult rv = GetNativePath(nativeString);
00886   if (NS_FAILED(rv))
00887     return rv;
00888   CopyUTF8toUTF16NFC(nativeString, aPath);
00889   return NS_OK;
00890 }
00891 
00892 /* [noscript] readonly attribute ACString nativePath; */
00893 NS_IMETHODIMP nsLocalFile::GetNativePath(nsACString& aNativePath)
00894 {
00895   if (!mBaseRef)
00896     return NS_ERROR_NOT_INITIALIZED;
00897   nsresult rv = NS_ERROR_FAILURE;
00898   CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(mBaseRef, kCFURLPOSIXPathStyle);
00899   if (pathStrRef) {
00900     rv = CFStringReftoUTF8(pathStrRef, aNativePath);
00901     ::CFRelease(pathStrRef);
00902   }
00903   return rv;
00904 }
00905 
00906 /* boolean exists (); */
00907 NS_IMETHODIMP nsLocalFile::Exists(PRBool *_retval)
00908 {
00909   NS_ENSURE_ARG_POINTER(_retval);
00910   *_retval = PR_FALSE;
00911   
00912   FSRef fsRef;
00913   if (NS_SUCCEEDED(GetFSRefInternal(fsRef, PR_TRUE))) {
00914     *_retval = PR_TRUE;
00915   }
00916   
00917   return NS_OK;
00918 }
00919 
00920 /* boolean isWritable (); */
00921 NS_IMETHODIMP nsLocalFile::IsWritable(PRBool *_retval)
00922 {
00923     NS_ENSURE_ARG_POINTER(_retval);
00924     *_retval = PR_FALSE;
00925     
00926     FSRef fsRef;
00927     nsresult rv = GetFSRefInternal(fsRef);
00928     if (NS_FAILED(rv))
00929       return rv;
00930     if (::FSCheckLock(&fsRef) == noErr) {      
00931       PRUint32 permissions;
00932       rv = GetPermissions(&permissions);
00933       if (NS_FAILED(rv))
00934         return rv;
00935       *_retval = ((permissions & S_IWUSR) != 0);
00936     }
00937     return NS_OK;
00938 }
00939 
00940 /* boolean isReadable (); */
00941 NS_IMETHODIMP nsLocalFile::IsReadable(PRBool *_retval)
00942 {
00943     NS_ENSURE_ARG_POINTER(_retval);
00944     *_retval = PR_FALSE;
00945     
00946     PRUint32 permissions;
00947     nsresult rv = GetPermissions(&permissions);
00948     if (NS_FAILED(rv))
00949       return rv;
00950     *_retval = ((permissions & S_IRUSR) != 0);
00951     return NS_OK;
00952 }
00953 
00954 /* boolean isExecutable (); */
00955 NS_IMETHODIMP nsLocalFile::IsExecutable(PRBool *_retval)
00956 {
00957   NS_ENSURE_ARG_POINTER(_retval);
00958   *_retval = PR_FALSE;
00959   
00960   FSRef fsRef;
00961   nsresult rv = GetFSRefInternal(fsRef);
00962   if (NS_FAILED(rv))
00963     return rv;
00964     
00965   LSRequestedInfo theInfoRequest = kLSRequestAllInfo;
00966   LSItemInfoRecord theInfo;
00967   if (::LSCopyItemInfoForRef(&fsRef, theInfoRequest, &theInfo) == noErr) {
00968     if ((theInfo.flags & kLSItemInfoIsApplication) != 0)
00969     *_retval = PR_TRUE;
00970   }
00971   return NS_OK;
00972 }
00973 
00974 /* boolean isHidden (); */
00975 NS_IMETHODIMP nsLocalFile::IsHidden(PRBool *_retval)
00976 {
00977   NS_ENSURE_ARG_POINTER(_retval);
00978   *_retval = PR_FALSE;
00979   
00980   FSRef fsRef;
00981   nsresult rv = GetFSRefInternal(fsRef);
00982   if (NS_FAILED(rv))
00983     return rv;
00984   
00985   FSCatalogInfo catalogInfo;
00986   HFSUniStr255 leafName;  
00987   OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoFinderInfo, &catalogInfo,
00988                                 &leafName, nsnull, nsnull);
00989   if (err != noErr)
00990     return MacErrorMapper(err);
00991       
00992   FileInfo *fInfoPtr = (FileInfo *)(catalogInfo.finderInfo); // Finder flags are in the same place whether we use FileInfo or FolderInfo
00993   if ((fInfoPtr->finderFlags & kIsInvisible) != 0) {
00994     *_retval = PR_TRUE;
00995   }
00996   else {
00997     // If the leaf name begins with a '.', consider it invisible
00998     if (leafName.length >= 1 && leafName.unicode[0] == UniChar('.'))
00999       *_retval = PR_TRUE;
01000   }
01001   return NS_OK;
01002 }
01003 
01004 /* boolean isDirectory (); */
01005 NS_IMETHODIMP nsLocalFile::IsDirectory(PRBool *_retval)
01006 {
01007   NS_ENSURE_ARG_POINTER(_retval);
01008   *_retval = PR_FALSE;
01009   
01010   FSRef fsRef;
01011   nsresult rv = GetFSRefInternal(fsRef, PR_FALSE);
01012   if (NS_FAILED(rv))
01013     return rv;
01014     
01015   FSCatalogInfo catalogInfo;
01016   OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags, &catalogInfo,
01017                                 nsnull, nsnull, nsnull);
01018   if (err != noErr)
01019     return MacErrorMapper(err);
01020   *_retval = ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0);
01021   return NS_OK;
01022 }
01023 
01024 /* boolean isFile (); */
01025 NS_IMETHODIMP nsLocalFile::IsFile(PRBool *_retval)
01026 {
01027   NS_ENSURE_ARG_POINTER(_retval);
01028   *_retval = PR_FALSE;
01029   
01030   FSRef fsRef;
01031   nsresult rv = GetFSRefInternal(fsRef, PR_FALSE);
01032   if (NS_FAILED(rv))
01033     return rv;
01034     
01035   FSCatalogInfo catalogInfo;
01036   OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags, &catalogInfo,
01037                                 nsnull, nsnull, nsnull);
01038   if (err != noErr)
01039     return MacErrorMapper(err);
01040   *_retval = ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) == 0);
01041   return NS_OK;
01042 }
01043 
01044 /* boolean isSymlink (); */
01045 NS_IMETHODIMP nsLocalFile::IsSymlink(PRBool *_retval)
01046 {
01047   NS_ENSURE_ARG(_retval);
01048   *_retval = PR_FALSE;
01049   if (!mBaseRef)
01050     return NS_ERROR_NOT_INITIALIZED;
01051   
01052   FSRef fsRef;
01053   if (::CFURLGetFSRef(mBaseRef, &fsRef)) {
01054     Boolean isAlias, isFolder;
01055     if (::FSIsAliasFile(&fsRef, &isAlias, &isFolder) == noErr)
01056         *_retval = isAlias;
01057   }
01058   return NS_OK;
01059 }
01060 
01061 /* boolean isSpecial (); */
01062 NS_IMETHODIMP nsLocalFile::IsSpecial(PRBool *_retval)
01063 {
01064     NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
01065     return NS_ERROR_NOT_IMPLEMENTED;
01066 }
01067 
01068 /* nsIFile clone (); */
01069 NS_IMETHODIMP nsLocalFile::Clone(nsIFile **_retval)
01070 {
01071     // Just copy-construct ourselves
01072     *_retval = new nsLocalFile(*this);
01073     if (!*_retval)
01074       return NS_ERROR_OUT_OF_MEMORY;
01075 
01076     NS_ADDREF(*_retval);
01077     
01078     return NS_OK;
01079 }
01080 
01081 /* boolean equals (in nsIFile inFile); */
01082 NS_IMETHODIMP nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
01083 {
01084   NS_ENSURE_ARG_POINTER(_retval);
01085   *_retval = PR_FALSE;
01086   
01087   nsCOMPtr<nsILocalFileMac> inMacFile(do_QueryInterface(inFile));
01088   if (!inFile)
01089     return NS_OK;
01090     
01091   // If both exist, compare FSRefs
01092   FSRef thisFSRef, inFSRef;
01093   nsresult rv1 = GetFSRef(&thisFSRef);
01094   nsresult rv2 = inMacFile->GetFSRef(&inFSRef);
01095   if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) {
01096     *_retval = (thisFSRef == inFSRef);
01097     return NS_OK;
01098   }
01099   // If one exists and the other doesn't, not equal  
01100   if (rv1 != rv2)
01101     return NS_OK;
01102     
01103   // Arg, we have to get their paths and compare
01104   nsCAutoString thisPath, inPath;
01105   if (NS_FAILED(GetNativePath(thisPath)))
01106     return NS_ERROR_FAILURE;
01107   if (NS_FAILED(inMacFile->GetNativePath(inPath)))
01108     return NS_ERROR_FAILURE;
01109   *_retval = thisPath.Equals(inPath);
01110   
01111   return NS_OK;
01112 }
01113 
01114 /* boolean contains (in nsIFile inFile, in boolean recur); */
01115 NS_IMETHODIMP nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retval)
01116 {
01117   NS_ENSURE_ARG_POINTER(_retval);
01118   *_retval = PR_FALSE;
01119 
01120   PRBool isDir;
01121   nsresult rv = IsDirectory(&isDir);
01122   if (NS_FAILED(rv))
01123     return rv;
01124   if (!isDir)
01125     return NS_OK;     // must be a dir to contain someone
01126 
01127   nsCAutoString thisPath, inPath;
01128   if (NS_FAILED(GetNativePath(thisPath)) || NS_FAILED(inFile->GetNativePath(inPath)))
01129     return NS_ERROR_FAILURE;
01130   size_t thisPathLen = thisPath.Length();
01131   if ((inPath.Length() > thisPathLen + 1) && (strncasecmp(thisPath.get(), inPath.get(), thisPathLen) == 0)) {
01132     // Now make sure that the |inFile|'s path has a separator at thisPathLen,
01133     // and there's at least one more character after that.
01134     if (inPath[thisPathLen] == kPathSepChar)
01135       *_retval = PR_TRUE;
01136   }  
01137   return NS_OK;
01138 }
01139 
01140 /* readonly attribute nsIFile parent; */
01141 NS_IMETHODIMP nsLocalFile::GetParent(nsIFile * *aParent)
01142 {
01143   NS_ENSURE_ARG_POINTER(aParent);
01144   *aParent = nsnull;
01145   if (!mBaseRef)
01146     return NS_ERROR_NOT_INITIALIZED;
01147   
01148   nsLocalFile *newFile = nsnull;
01149 
01150   // If it can be determined without error that a file does not
01151   // have a parent, return nsnull for the parent and NS_OK as the result.
01152   // See bug 133617.
01153   nsresult rv = NS_OK;
01154   CFURLRef parentURLRef = ::CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, mBaseRef);
01155   if (parentURLRef) {
01156     rv = NS_ERROR_FAILURE;
01157     newFile = new nsLocalFile;
01158     if (newFile) {
01159       rv = newFile->InitWithCFURL(parentURLRef);
01160       if (NS_SUCCEEDED(rv)) {
01161         NS_ADDREF(*aParent = newFile);
01162         rv = NS_OK;
01163       }
01164     }
01165     ::CFRelease(parentURLRef);
01166   }  
01167   return rv;
01168 }
01169 
01170 /* readonly attribute nsISimpleEnumerator directoryEntries; */
01171 NS_IMETHODIMP nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator **aDirectoryEntries)
01172 {
01173   NS_ENSURE_ARG_POINTER(aDirectoryEntries);
01174   *aDirectoryEntries = nsnull;
01175 
01176   nsresult rv;
01177   PRBool isDir;
01178   rv = IsDirectory(&isDir);
01179   if (NS_FAILED(rv)) 
01180     return rv;
01181   if (!isDir)
01182     return NS_ERROR_FILE_NOT_DIRECTORY;
01183 
01184   nsDirEnumerator* dirEnum = new nsDirEnumerator;
01185   if (dirEnum == nsnull)
01186     return NS_ERROR_OUT_OF_MEMORY;
01187   NS_ADDREF(dirEnum);
01188   rv = dirEnum->Init(this);
01189   if (NS_FAILED(rv)) {
01190     NS_RELEASE(dirEnum);
01191     return rv;
01192   }
01193   *aDirectoryEntries = dirEnum;
01194   
01195   return NS_OK;
01196 }
01197 
01198 
01199 //*****************************************************************************
01200 //  nsLocalFile::nsILocalFile
01201 //*****************************************************************************
01202 #pragma mark -
01203 #pragma mark [nsILocalFile]
01204 
01205 /* void initWithPath (in AString filePath); */
01206 NS_IMETHODIMP nsLocalFile::InitWithPath(const nsAString& filePath)
01207 {
01208   return InitWithNativePath(NS_ConvertUCS2toUTF8(filePath));
01209 }
01210 
01211 /* [noscript] void initWithNativePath (in ACString filePath); */
01212 NS_IMETHODIMP nsLocalFile::InitWithNativePath(const nsACString& filePath)
01213 {
01214   nsCAutoString fixedPath;
01215   if (Substring(filePath, 0, 2).EqualsLiteral("~/")) {
01216     nsCOMPtr<nsIFile> homeDir;
01217     nsCAutoString homePath;
01218     nsresult rv = NS_GetSpecialDirectory(NS_OS_HOME_DIR,
01219                                         getter_AddRefs(homeDir));
01220     NS_ENSURE_SUCCESS(rv, rv);
01221     rv = homeDir->GetNativePath(homePath);
01222     NS_ENSURE_SUCCESS(rv, rv);
01223     
01224     fixedPath = homePath + Substring(filePath, 1, filePath.Length() - 1);
01225   }
01226   else if (filePath.IsEmpty() || filePath.First() != '/')
01227     return NS_ERROR_FILE_UNRECOGNIZED_PATH;
01228   else
01229     fixedPath.Assign(filePath);
01230 
01231   // A path with consecutive '/'s which are not between
01232   // nodes crashes CFURLGetFSRef(). Consecutive '/'s which
01233   // are between actual nodes are OK. So, convert consecutive
01234   // '/'s to a single one.
01235   fixedPath.ReplaceSubstring("//", "/");
01236 
01237   // On 10.2, huge paths also crash CFURLGetFSRef()
01238   if (fixedPath.Length() > PATH_MAX)
01239     return NS_ERROR_FILE_NAME_TOO_LONG;
01240 
01241   CFStringRef pathAsCFString;
01242   CFURLRef pathAsCFURL;
01243 
01244   pathAsCFString = ::CFStringCreateWithCString(nsnull, fixedPath.get(), kCFStringEncodingUTF8);
01245   if (!pathAsCFString)
01246     return NS_ERROR_FAILURE;
01247   pathAsCFURL = ::CFURLCreateWithFileSystemPath(nsnull, pathAsCFString, kCFURLPOSIXPathStyle, PR_FALSE);
01248   if (!pathAsCFURL) {
01249     ::CFRelease(pathAsCFString);
01250     return NS_ERROR_FAILURE;
01251   }
01252   SetBaseRef(pathAsCFURL);
01253   ::CFRelease(pathAsCFURL);
01254   ::CFRelease(pathAsCFString);
01255   return NS_OK;
01256 }
01257 
01258 /* void initWithFile (in nsILocalFile aFile); */
01259 NS_IMETHODIMP nsLocalFile::InitWithFile(nsILocalFile *aFile)
01260 {
01261   NS_ENSURE_ARG(aFile);
01262   
01263   nsCOMPtr<nsILocalFileMac> aFileMac(do_QueryInterface(aFile));
01264   if (!aFileMac)
01265     return NS_ERROR_UNEXPECTED;
01266   CFURLRef urlRef;
01267   nsresult rv = aFileMac->GetCFURL(&urlRef);
01268   if (NS_FAILED(rv))
01269     return rv;
01270   rv = InitWithCFURL(urlRef);
01271   ::CFRelease(urlRef);
01272   return rv;
01273 }
01274 
01275 /* attribute PRBool followLinks; */
01276 NS_IMETHODIMP nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
01277 {
01278   NS_ENSURE_ARG_POINTER(aFollowLinks);
01279   
01280   *aFollowLinks = mFollowLinks;
01281   return NS_OK;
01282 }
01283 
01284 NS_IMETHODIMP nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
01285 {
01286   if (aFollowLinks != mFollowLinks) {
01287     mFollowLinks = aFollowLinks;
01288     UpdateTargetRef();
01289   }
01290   return NS_OK;
01291 }
01292 
01293 /* [noscript] PRFileDescStar openNSPRFileDesc (in long flags, in long mode); */
01294 NS_IMETHODIMP nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval)
01295 {
01296   NS_ENSURE_ARG_POINTER(_retval);
01297 
01298   nsCAutoString path;
01299   nsresult rv = GetPathInternal(path);
01300   if (NS_FAILED(rv))
01301     return rv;
01302     
01303   *_retval = PR_Open(path.get(), flags, mode);
01304   if (! *_retval)
01305     return NS_ErrorAccordingToNSPR();
01306 
01307   return NS_OK;
01308 }
01309 
01310 /* [noscript] FILE openANSIFileDesc (in string mode); */
01311 NS_IMETHODIMP nsLocalFile::OpenANSIFileDesc(const char *mode, FILE **_retval)
01312 {
01313   NS_ENSURE_ARG_POINTER(_retval);
01314 
01315   nsCAutoString path;
01316   nsresult rv = GetPathInternal(path);
01317   if (NS_FAILED(rv))
01318     return rv;
01319     
01320   *_retval = fopen(path.get(), mode);
01321   if (! *_retval)
01322     return NS_ERROR_FAILURE;
01323 
01324   return NS_OK;
01325 }
01326 
01327 /* [noscript] PRLibraryStar load (); */
01328 NS_IMETHODIMP nsLocalFile::Load(PRLibrary **_retval)
01329 {
01330   NS_ENSURE_ARG_POINTER(_retval);
01331 
01332   NS_TIMELINE_START_TIMER("PR_LoadLibrary");
01333 
01334   nsCAutoString path;
01335   nsresult rv = GetPathInternal(path);
01336   if (NS_FAILED(rv))
01337     return rv;
01338 
01339   *_retval = PR_LoadLibrary(path.get());
01340 
01341   NS_TIMELINE_STOP_TIMER("PR_LoadLibrary");
01342   NS_TIMELINE_MARK_TIMER1("PR_LoadLibrary", path.get());
01343 
01344   if (!*_retval)
01345     return NS_ERROR_FAILURE;
01346   
01347   return NS_OK;
01348 }
01349 
01350 /* readonly attribute PRInt64 diskSpaceAvailable; */
01351 NS_IMETHODIMP nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
01352 {
01353   NS_ENSURE_ARG_POINTER(aDiskSpaceAvailable);
01354   
01355   FSRef fsRef;
01356   nsresult rv = GetFSRefInternal(fsRef);
01357   if (NS_FAILED(rv))
01358     return rv;
01359     
01360   OSErr err;
01361   FSCatalogInfo catalogInfo;
01362   err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoVolume, &catalogInfo,
01363                            nsnull, nsnull, nsnull);
01364   if (err != noErr)
01365     return MacErrorMapper(err);
01366   
01367   FSVolumeInfo volumeInfo;  
01368   err = ::FSGetVolumeInfo(catalogInfo.volume, 0, nsnull, kFSVolInfoSizes,
01369                           &volumeInfo, nsnull, nsnull);
01370   if (err != noErr)
01371     return MacErrorMapper(err);
01372     
01373   *aDiskSpaceAvailable = volumeInfo.freeBytes;
01374   return NS_OK;
01375 }
01376 
01377 /* void appendRelativePath (in AString relativeFilePath); */
01378 NS_IMETHODIMP nsLocalFile::AppendRelativePath(const nsAString& relativeFilePath)
01379 {
01380   return AppendRelativeNativePath(NS_ConvertUCS2toUTF8(relativeFilePath));
01381 }
01382 
01383 /* [noscript] void appendRelativeNativePath (in ACString relativeFilePath); */
01384 NS_IMETHODIMP nsLocalFile::AppendRelativeNativePath(const nsACString& relativeFilePath)
01385 {  
01386   if (relativeFilePath.IsEmpty())
01387     return NS_OK;
01388   // No leading '/' 
01389   if (relativeFilePath.First() == '/')
01390     return NS_ERROR_FILE_UNRECOGNIZED_PATH;
01391 
01392   // Parse the nodes and call Append() for each
01393   nsACString::const_iterator nodeBegin, pathEnd;
01394   relativeFilePath.BeginReading(nodeBegin);
01395   relativeFilePath.EndReading(pathEnd);
01396   nsACString::const_iterator nodeEnd(nodeBegin);
01397   
01398   while (nodeEnd != pathEnd) {
01399     FindCharInReadable(kPathSepChar, nodeEnd, pathEnd);
01400     nsresult rv = AppendNative(Substring(nodeBegin, nodeEnd));
01401     if (NS_FAILED(rv))
01402       return rv;
01403     if (nodeEnd != pathEnd) // If there's more left in the string, inc over the '/' nodeEnd is on.
01404       ++nodeEnd;
01405     nodeBegin = nodeEnd;
01406   }
01407   return NS_OK;
01408 }
01409 
01410 /* attribute ACString persistentDescriptor; */
01411 NS_IMETHODIMP nsLocalFile::GetPersistentDescriptor(nsACString& aPersistentDescriptor)
01412 {
01413   FSRef fsRef;
01414   nsresult rv = GetFSRefInternal(fsRef);
01415   if (NS_FAILED(rv))
01416     return rv;
01417     
01418   AliasHandle aliasH;
01419   OSErr err = ::FSNewAlias(nsnull, &fsRef, &aliasH);
01420   if (err != noErr)
01421     return MacErrorMapper(err);
01422     
01423    PRUint32 bytes = ::GetHandleSize((Handle) aliasH);
01424    ::HLock((Handle) aliasH);
01425    // Passing nsnull for dest makes NULL-term string
01426    char* buf = PL_Base64Encode((const char*)*aliasH, bytes, nsnull);
01427    ::DisposeHandle((Handle) aliasH);
01428    NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
01429    
01430    aPersistentDescriptor = buf;
01431    PR_Free(buf);
01432 
01433   return NS_OK;
01434 }
01435 
01436 NS_IMETHODIMP nsLocalFile::SetPersistentDescriptor(const nsACString& aPersistentDescriptor)
01437 {
01438   if (aPersistentDescriptor.IsEmpty())
01439     return NS_ERROR_INVALID_ARG;
01440 
01441   // Support pathnames as user-supplied descriptors if they begin with '/'
01442   // or '~'.  These characters do not collide with the base64 set used for
01443   // encoding alias records.
01444   char first = aPersistentDescriptor.First();
01445   if (first == '/' || first == '~')
01446     return InitWithNativePath(aPersistentDescriptor);
01447 
01448   nsresult rv = NS_OK;
01449   
01450   PRUint32 dataSize = aPersistentDescriptor.Length();    
01451   char* decodedData = PL_Base64Decode(PromiseFlatCString(aPersistentDescriptor).get(), dataSize, nsnull);
01452   if (!decodedData) {
01453     NS_ERROR("SetPersistentDescriptor was given bad data");
01454     return NS_ERROR_FAILURE;
01455   }
01456   
01457   // Cast to an alias record and resolve.
01458   AliasRecord aliasHeader = *(AliasPtr)decodedData;
01459   PRInt32 aliasSize = GetAliasSizeFromRecord(aliasHeader);
01460   if (aliasSize > (dataSize * 3) / 4) { // be paranoid about having too few data
01461     PR_Free(decodedData);
01462     return NS_ERROR_FAILURE;
01463   }
01464   
01465   // Move the now-decoded data into the Handle.
01466   // The size of the decoded data is 3/4 the size of the encoded data. See plbase64.h
01467   Handle      newHandle = nsnull;
01468   if (::PtrToHand(decodedData, &newHandle, aliasSize) != noErr)
01469     rv = NS_ERROR_OUT_OF_MEMORY;
01470   PR_Free(decodedData);
01471   if (NS_FAILED(rv))
01472     return rv;
01473 
01474   Boolean changed;
01475   FSRef resolvedFSRef;
01476   OSErr err = ::FSResolveAlias(nsnull, (AliasHandle)newHandle, &resolvedFSRef, &changed);
01477     
01478   rv = MacErrorMapper(err);
01479   DisposeHandle(newHandle);
01480   if (NS_FAILED(rv))
01481     return rv;
01482  
01483   return InitWithFSRef(&resolvedFSRef);
01484 }
01485 
01486 /* void reveal (); */
01487 NS_IMETHODIMP nsLocalFile::Reveal()
01488 {
01489   FSRef             fsRefToReveal;
01490   AppleEvent        aeEvent = {0, nil};
01491   AppleEvent        aeReply = {0, nil};
01492   StAEDesc          aeDirDesc, listElem, myAddressDesc, fileList;
01493   OSErr             err;
01494   ProcessSerialNumber   process;
01495     
01496   nsresult rv = GetFSRefInternal(fsRefToReveal);
01497   if (NS_FAILED(rv))
01498     return rv;
01499   
01500   err = ::FindRunningAppBySignature ('MACS', process);
01501   if (err == noErr) { 
01502     err = ::AECreateDesc(typeProcessSerialNumber, (Ptr)&process, sizeof(process), &myAddressDesc);
01503     if (err == noErr) {
01504       // Create the FinderEvent
01505       err = ::AECreateAppleEvent(kAEMiscStandards, kAEMakeObjectsVisible, &myAddressDesc,
01506                         kAutoGenerateReturnID, kAnyTransactionID, &aeEvent);   
01507       if (err == noErr) {
01508         // Create the file list
01509         err = ::AECreateList(nil, 0, false, &fileList);
01510         if (err == noErr) {
01511           FSSpec fsSpecToReveal;
01512           err = ::FSRefMakeFSSpec(&fsRefToReveal, &fsSpecToReveal);
01513           if (err == noErr) {
01514             err = ::AEPutPtr(&fileList, 0, typeFSS, &fsSpecToReveal, sizeof(FSSpec));
01515             if (err == noErr) {
01516               err = ::AEPutParamDesc(&aeEvent, keyDirectObject, &fileList);
01517               if (err == noErr) {
01518                 err = ::AESend(&aeEvent, &aeReply, kAENoReply, kAENormalPriority, kAEDefaultTimeout, nil, nil);
01519                 if (err == noErr)
01520                   ::SetFrontProcess(&process);
01521               }
01522             }
01523           }
01524         }
01525       }
01526     }
01527   }
01528     
01529   return NS_OK;
01530 }
01531 
01532 /* void launch (); */
01533 NS_IMETHODIMP nsLocalFile::Launch()
01534 {
01535   FSRef fsRef;
01536   nsresult rv = GetFSRefInternal(fsRef);
01537   if (NS_FAILED(rv))
01538     return rv;
01539 
01540   OSErr err = ::LSOpenFSRef(&fsRef, NULL);
01541   return MacErrorMapper(err);
01542 }
01543 
01544 
01545 //*****************************************************************************
01546 //  nsLocalFile::nsILocalFileMac
01547 //*****************************************************************************
01548 #pragma mark -
01549 #pragma mark [nsILocalFileMac]
01550 
01551 /* void initWithCFURL (in CFURLRef aCFURL); */
01552 NS_IMETHODIMP nsLocalFile::InitWithCFURL(CFURLRef aCFURL)
01553 {
01554   NS_ENSURE_ARG(aCFURL);
01555   
01556   SetBaseRef(aCFURL);
01557   return NS_OK;
01558 }
01559 
01560 /* void initWithFSRef ([const] in FSRefPtr aFSRef); */
01561 NS_IMETHODIMP nsLocalFile::InitWithFSRef(const FSRef *aFSRef)
01562 {
01563   NS_ENSURE_ARG(aFSRef);
01564   nsresult rv = NS_ERROR_FAILURE;
01565   
01566   CFURLRef newURLRef = ::CFURLCreateFromFSRef(kCFAllocatorDefault, aFSRef);
01567   if (newURLRef) {
01568     SetBaseRef(newURLRef);
01569     ::CFRelease(newURLRef);
01570     rv = NS_OK;
01571   }
01572   return rv;
01573 }
01574 
01575 /* void initWithFSSpec ([const] in FSSpecPtr aFileSpec); */
01576 NS_IMETHODIMP nsLocalFile::InitWithFSSpec(const FSSpec *aFileSpec)
01577 {
01578   NS_ENSURE_ARG(aFileSpec);
01579   
01580   FSRef fsRef;
01581   OSErr err = ::FSpMakeFSRef(aFileSpec, &fsRef);
01582   if (err == noErr)
01583     return InitWithFSRef(&fsRef);
01584   else if (err == fnfErr) {
01585     CInfoPBRec  pBlock;
01586     FSSpec parentDirSpec;
01587     
01588     memset(&pBlock, 0, sizeof(CInfoPBRec));
01589     parentDirSpec.name[0] = 0;
01590     pBlock.dirInfo.ioVRefNum = aFileSpec->vRefNum;
01591     pBlock.dirInfo.ioDrDirID = aFileSpec->parID;
01592     pBlock.dirInfo.ioNamePtr = (StringPtr)parentDirSpec.name;
01593     pBlock.dirInfo.ioFDirIndex = -1;        //get info on parID
01594     err = ::PBGetCatInfoSync(&pBlock);
01595     if (err != noErr)
01596       return MacErrorMapper(err);
01597     
01598     parentDirSpec.vRefNum = aFileSpec->vRefNum;
01599     parentDirSpec.parID = pBlock.dirInfo.ioDrParID;
01600     err = ::FSpMakeFSRef(&parentDirSpec, &fsRef);
01601     if (err != noErr)
01602       return MacErrorMapper(err);
01603     HFSUniStr255 unicodeName;
01604     err = ::HFSNameGetUnicodeName(aFileSpec->name, kTextEncodingUnknown, &unicodeName);
01605     if (err != noErr)
01606       return MacErrorMapper(err);
01607     nsresult rv = InitWithFSRef(&fsRef);
01608     if (NS_FAILED(rv))
01609       return rv;
01610     return Append(nsDependentString(unicodeName.unicode, unicodeName.length));  
01611   }
01612   return MacErrorMapper(err);
01613 }
01614 
01615 /* void initToAppWithCreatorCode (in OSType aAppCreator); */
01616 NS_IMETHODIMP nsLocalFile::InitToAppWithCreatorCode(OSType aAppCreator)
01617 {
01618   FSRef fsRef;
01619   OSErr err = ::LSFindApplicationForInfo(aAppCreator, nsnull, nsnull, &fsRef, nsnull);
01620   if (err != noErr)
01621     return MacErrorMapper(err);
01622   return InitWithFSRef(&fsRef);
01623 }
01624 
01625 /* CFURLRef getCFURL (); */
01626 NS_IMETHODIMP nsLocalFile::GetCFURL(CFURLRef *_retval)
01627 {
01628   NS_ENSURE_ARG_POINTER(_retval);
01629   CFURLRef whichURLRef = mFollowLinks ? mTargetRef : mBaseRef;
01630   if (whichURLRef)
01631     ::CFRetain(whichURLRef);
01632   *_retval = whichURLRef;
01633   return whichURLRef ? NS_OK : NS_ERROR_FAILURE;
01634 }
01635 
01636 /* FSRef getFSRef (); */
01637 NS_IMETHODIMP nsLocalFile::GetFSRef(FSRef *_retval)
01638 {
01639   NS_ENSURE_ARG_POINTER(_retval);
01640   return GetFSRefInternal(*_retval);
01641 }
01642 
01643 /* FSSpec getFSSpec (); */
01644 NS_IMETHODIMP nsLocalFile::GetFSSpec(FSSpec *_retval)
01645 {
01646   NS_ENSURE_ARG_POINTER(_retval);
01647   if (!mBaseRef)
01648     return NS_ERROR_NOT_INITIALIZED;
01649   
01650   OSErr err;
01651   FSRef fsRef;
01652   nsresult rv = GetFSRefInternal(fsRef);
01653   if (NS_SUCCEEDED(rv)) {
01654     // If the leaf node exists, things are simple.
01655     err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNone,
01656               nsnull, nsnull, _retval, nsnull);
01657     return MacErrorMapper(err); 
01658   }
01659   else if (rv == NS_ERROR_FILE_NOT_FOUND) {
01660     // If the parent of the leaf exists, make an FSSpec from that.
01661     CFURLRef parentURLRef = ::CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, mBaseRef);
01662     if (!parentURLRef)
01663       return NS_ERROR_FAILURE;
01664 
01665     err = fnfErr;
01666     if (::CFURLGetFSRef(parentURLRef, &fsRef)) {
01667       FSCatalogInfo catalogInfo;
01668       if ((err = ::FSGetCatalogInfo(&fsRef,
01669                         kFSCatInfoVolume + kFSCatInfoNodeID + kFSCatInfoTextEncoding,
01670                         &catalogInfo, nsnull, nsnull, nsnull)) == noErr) {
01671         nsAutoString leafName;
01672         if (NS_SUCCEEDED(GetLeafName(leafName))) {
01673           Str31 hfsName;
01674           if ((err = ::UnicodeNameGetHFSName(leafName.Length(),
01675                           leafName.get(),
01676                           catalogInfo.textEncodingHint,
01677                           catalogInfo.nodeID == fsRtDirID,
01678                           hfsName)) == noErr)
01679             err = ::FSMakeFSSpec(catalogInfo.volume, catalogInfo.nodeID, hfsName, _retval);        
01680         }
01681       }
01682     }
01683     ::CFRelease(parentURLRef);
01684     rv = MacErrorMapper(err);
01685   }
01686   return rv;
01687 }
01688 
01689 /* readonly attribute PRInt64 fileSizeWithResFork; */
01690 NS_IMETHODIMP nsLocalFile::GetFileSizeWithResFork(PRInt64 *aFileSizeWithResFork)
01691 {
01692   NS_ENSURE_ARG_POINTER(aFileSizeWithResFork);
01693   
01694   FSRef fsRef;
01695   nsresult rv = GetFSRefInternal(fsRef);
01696   if (NS_FAILED(rv))
01697     return rv;
01698       
01699   FSCatalogInfo catalogInfo;
01700   OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoDataSizes + kFSCatInfoRsrcSizes,
01701                                  &catalogInfo, nsnull, nsnull, nsnull);
01702   if (err != noErr)
01703     return MacErrorMapper(err);
01704     
01705   *aFileSizeWithResFork = catalogInfo.dataLogicalSize + catalogInfo.rsrcLogicalSize;
01706   return NS_OK;
01707 }
01708 
01709 /* attribute OSType fileType; */
01710 NS_IMETHODIMP nsLocalFile::GetFileType(OSType *aFileType)
01711 {
01712   NS_ENSURE_ARG_POINTER(aFileType);
01713   
01714   FSRef fsRef;
01715   nsresult rv = GetFSRefInternal(fsRef);
01716   if (NS_FAILED(rv))
01717     return rv;
01718   
01719   FinderInfo fInfo;  
01720   OSErr err = ::FSGetFinderInfo(&fsRef, &fInfo, nsnull, nsnull);
01721   if (err != noErr)
01722     return MacErrorMapper(err);
01723   *aFileType = fInfo.file.fileType;
01724   return NS_OK;
01725 }
01726 
01727 NS_IMETHODIMP nsLocalFile::SetFileType(OSType aFileType)
01728 {
01729   FSRef fsRef;
01730   nsresult rv = GetFSRefInternal(fsRef);
01731   if (NS_FAILED(rv))
01732     return rv;
01733     
01734   OSErr err = ::FSChangeCreatorType(&fsRef, 0, aFileType);
01735   return MacErrorMapper(err);
01736 }
01737 
01738 /* attribute OSType fileCreator; */
01739 NS_IMETHODIMP nsLocalFile::GetFileCreator(OSType *aFileCreator)
01740 {
01741   NS_ENSURE_ARG_POINTER(aFileCreator);
01742   
01743   FSRef fsRef;
01744   nsresult rv = GetFSRefInternal(fsRef);
01745   if (NS_FAILED(rv))
01746     return rv;
01747   
01748   FinderInfo fInfo;  
01749   OSErr err = ::FSGetFinderInfo(&fsRef, &fInfo, nsnull, nsnull);
01750   if (err != noErr)
01751     return MacErrorMapper(err);
01752   *aFileCreator = fInfo.file.fileCreator;
01753   return NS_OK;
01754 }
01755 
01756 NS_IMETHODIMP nsLocalFile::SetFileCreator(OSType aFileCreator)
01757 {
01758   FSRef fsRef;
01759   nsresult rv = GetFSRefInternal(fsRef);
01760   if (NS_FAILED(rv))
01761     return rv;
01762     
01763   OSErr err = ::FSChangeCreatorType(&fsRef, aFileCreator, 0);
01764   return MacErrorMapper(err);
01765 }
01766 
01767 /* void setFileTypeAndCreatorFromMIMEType (in string aMIMEType); */
01768 NS_IMETHODIMP nsLocalFile::SetFileTypeAndCreatorFromMIMEType(const char *aMIMEType)
01769 {
01770   // XXX - This should be cut from the API. Would create an evil dependency.
01771   NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
01772   return NS_ERROR_NOT_IMPLEMENTED;
01773 }
01774 
01775 /* void setFileTypeAndCreatorFromExtension (in string aExtension); */
01776 NS_IMETHODIMP nsLocalFile::SetFileTypeAndCreatorFromExtension(const char *aExtension)
01777 {
01778   // XXX - This should be cut from the API. Would create an evil dependency.
01779   NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
01780   return NS_ERROR_NOT_IMPLEMENTED;
01781 }
01782 
01783 /* void launchWithDoc (in nsILocalFile aDocToLoad, in boolean aLaunchInBackground); */
01784 NS_IMETHODIMP nsLocalFile::LaunchWithDoc(nsILocalFile *aDocToLoad, PRBool aLaunchInBackground)
01785 {
01786   PRBool isExecutable;
01787   nsresult rv = IsExecutable(&isExecutable);
01788   if (NS_FAILED(rv))
01789     return rv;
01790   if (!isExecutable)
01791     return NS_ERROR_FILE_EXECUTION_FAILED;
01792 
01793   FSRef appFSRef, docFSRef;
01794   rv = GetFSRefInternal(appFSRef);
01795   if (NS_FAILED(rv))
01796     return rv;
01797 
01798   if (aDocToLoad) {
01799     nsCOMPtr<nsILocalFileMac> macDoc = do_QueryInterface(aDocToLoad);
01800     rv = macDoc->GetFSRef(&docFSRef);
01801     if (NS_FAILED(rv))
01802       return rv;
01803   }
01804   
01805   LSLaunchFlags       theLaunchFlags = kLSLaunchDefaults;
01806   LSLaunchFSRefSpec   thelaunchSpec;
01807 
01808   if (aLaunchInBackground)
01809     theLaunchFlags |= kLSLaunchDontSwitch;
01810   memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
01811 
01812   thelaunchSpec.appRef = &appFSRef;
01813   if (aDocToLoad) {
01814     thelaunchSpec.numDocs = 1;
01815     thelaunchSpec.itemRefs = &docFSRef;
01816   }
01817   thelaunchSpec.launchFlags = theLaunchFlags;
01818 
01819   OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, NULL);
01820   if (err != noErr)
01821     return MacErrorMapper(err);
01822 
01823   return NS_OK;
01824 }
01825 
01826 /* void openDocWithApp (in nsILocalFile aAppToOpenWith, in boolean aLaunchInBackground); */
01827 NS_IMETHODIMP nsLocalFile::OpenDocWithApp(nsILocalFile *aAppToOpenWith, PRBool aLaunchInBackground)
01828 {
01829   nsresult rv;
01830   OSErr err;
01831 
01832   FSRef docFSRef, appFSRef;
01833   rv = GetFSRefInternal(docFSRef);
01834   if (NS_FAILED(rv))
01835     return rv;
01836 
01837   if (aAppToOpenWith) {
01838     nsCOMPtr<nsILocalFileMac> appFileMac = do_QueryInterface(aAppToOpenWith, &rv);
01839     if (!appFileMac)
01840       return rv;
01841 
01842     PRBool isExecutable;
01843     rv = appFileMac->IsExecutable(&isExecutable);
01844     if (NS_FAILED(rv))
01845       return rv;
01846     if (!isExecutable)
01847       return NS_ERROR_FILE_EXECUTION_FAILED;
01848     
01849     rv = appFileMac->GetFSRef(&appFSRef);
01850     if (NS_FAILED(rv))
01851       return rv;
01852   }
01853   else {
01854     OSType  fileCreator;
01855     rv = GetFileCreator(&fileCreator);
01856     if (NS_FAILED(rv))
01857       return rv;
01858 
01859     err = ::LSFindApplicationForInfo(fileCreator, nsnull, nsnull, &appFSRef, nsnull);
01860     if (err != noErr)
01861       return MacErrorMapper(err);
01862   }
01863   
01864   LSLaunchFlags       theLaunchFlags = kLSLaunchDefaults;
01865   LSLaunchFSRefSpec   thelaunchSpec;
01866 
01867   if (aLaunchInBackground)
01868   theLaunchFlags |= kLSLaunchDontSwitch;
01869   memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
01870 
01871   thelaunchSpec.appRef = &appFSRef;
01872   thelaunchSpec.numDocs = 1;
01873   thelaunchSpec.itemRefs = &docFSRef;
01874   thelaunchSpec.launchFlags = theLaunchFlags;
01875 
01876   err = ::LSOpenFromRefSpec(&thelaunchSpec, NULL);
01877   if (err != noErr)
01878     return MacErrorMapper(err);
01879 
01880   return NS_OK;
01881 }
01882 
01883 /* boolean isPackage (); */
01884 NS_IMETHODIMP nsLocalFile::IsPackage(PRBool *_retval)
01885 {
01886   NS_ENSURE_ARG(_retval);
01887   *_retval = PR_FALSE;
01888   
01889   FSRef fsRef;
01890   nsresult rv = GetFSRefInternal(fsRef);
01891   if (NS_FAILED(rv))
01892     return rv;
01893 
01894   FSCatalogInfo catalogInfo;
01895   OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags + kFSCatInfoFinderInfo,
01896                                  &catalogInfo, nsnull, nsnull, nsnull);
01897   if (err != noErr)
01898     return MacErrorMapper(err);
01899   if ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0) {
01900     FileInfo *fInfoPtr = (FileInfo *)(catalogInfo.finderInfo);
01901     if ((fInfoPtr->finderFlags & kHasBundle) != 0) {
01902       *_retval = PR_TRUE;
01903     }
01904     else {
01905      // Folders ending with ".app" are also considered to
01906      // be packages, even if the top-level folder doesn't have bundle set
01907       nsCAutoString name;
01908       if (NS_SUCCEEDED(rv = GetNativeLeafName(name))) {
01909         const char *extPtr = strrchr(name.get(), '.');
01910         if (extPtr) {
01911           if ((nsCRT::strcasecmp(extPtr, ".app") == 0))
01912             *_retval = PR_TRUE;
01913         }
01914       }
01915     }
01916   }
01917   return NS_OK;
01918 }
01919 
01920 NS_IMETHODIMP
01921 nsLocalFile::GetBundleDisplayName(nsAString& outBundleName)
01922 {
01923   PRBool isPackage = PR_FALSE;
01924   nsresult rv = IsPackage(&isPackage);
01925   if (NS_FAILED(rv) || !isPackage)
01926     return NS_ERROR_FAILURE;
01927   
01928   nsAutoString name;
01929   rv = GetLeafName(name);
01930   if (NS_FAILED(rv))
01931     return rv;
01932   
01933   PRInt32 length = name.Length();
01934   if (Substring(name, length - 4, length).EqualsLiteral(".app")) {
01935     // 4 characters in ".app"
01936     outBundleName = Substring(name, 0, length - 4);
01937   }
01938   else
01939     outBundleName = name;
01940     
01941   return NS_OK;
01942 }
01943 
01944 //*****************************************************************************
01945 //  nsLocalFile Methods
01946 //*****************************************************************************
01947 #pragma mark -
01948 #pragma mark [Protected Methods]
01949 
01950 nsresult nsLocalFile::SetBaseRef(CFURLRef aCFURLRef)
01951 {
01952   NS_ENSURE_ARG(aCFURLRef);
01953   
01954   ::CFRetain(aCFURLRef);
01955   if (mBaseRef)
01956     ::CFRelease(mBaseRef);
01957   mBaseRef = aCFURLRef;
01958 
01959   mFollowLinksDirty = PR_TRUE;  
01960   UpdateTargetRef();
01961   mCachedFSRefValid = PR_FALSE;
01962   return NS_OK;
01963 }
01964 
01965 nsresult nsLocalFile::UpdateTargetRef()
01966 {
01967   if (!mBaseRef)
01968     return NS_ERROR_NOT_INITIALIZED;
01969   
01970   if (mFollowLinksDirty) {
01971     if (mTargetRef) {
01972       ::CFRelease(mTargetRef);
01973       mTargetRef = nsnull;
01974     }
01975     if (mFollowLinks) {
01976       mTargetRef = mBaseRef;
01977       ::CFRetain(mTargetRef);
01978 
01979       FSRef fsRef;
01980       if (::CFURLGetFSRef(mBaseRef, &fsRef)) {
01981         Boolean targetIsFolder, wasAliased;
01982         if (FSResolveAliasFile(&fsRef, true /*resolveAliasChains*/, 
01983             &targetIsFolder, &wasAliased) == noErr && wasAliased) {
01984           ::CFRelease(mTargetRef);
01985           mTargetRef = CFURLCreateFromFSRef(NULL, &fsRef);
01986           if (!mTargetRef)
01987             return NS_ERROR_FAILURE;
01988         }
01989       }
01990       mFollowLinksDirty = PR_FALSE;
01991     }
01992   }
01993   return NS_OK;
01994 }
01995 
01996 nsresult nsLocalFile::GetFSRefInternal(FSRef& aFSRef, PRBool bForceUpdateCache)
01997 {
01998   if (bForceUpdateCache || !mCachedFSRefValid) {
01999     mCachedFSRefValid = PR_FALSE;
02000     CFURLRef whichURLRef = mFollowLinks ? mTargetRef : mBaseRef;
02001     NS_ENSURE_TRUE(whichURLRef, NS_ERROR_NULL_POINTER);
02002     if (::CFURLGetFSRef(whichURLRef, &mCachedFSRef))
02003       mCachedFSRefValid = PR_TRUE;
02004   }
02005   if (mCachedFSRefValid) {
02006     aFSRef = mCachedFSRef;
02007     return NS_OK;
02008   }
02009   // CFURLGetFSRef only returns a Boolean for success,
02010   // so we have to assume what the error was. This is
02011   // the only probable cause.
02012   return NS_ERROR_FILE_NOT_FOUND;
02013 }
02014 
02015 nsresult nsLocalFile::GetPathInternal(nsACString& path)
02016 {
02017   nsresult rv = NS_ERROR_FAILURE;
02018   
02019   CFURLRef whichURLRef = mFollowLinks ? mTargetRef : mBaseRef;
02020   NS_ENSURE_TRUE(whichURLRef, NS_ERROR_NULL_POINTER);
02021    
02022   CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(whichURLRef, kCFURLPOSIXPathStyle);
02023   if (pathStrRef) {
02024     rv = CFStringReftoUTF8(pathStrRef, path);
02025     ::CFRelease(pathStrRef);
02026   }
02027   return rv;
02028 }
02029 
02030 nsresult nsLocalFile::CopyInternal(nsIFile* aParentDir,
02031                                    const nsAString& newName,
02032                                    PRBool followLinks)
02033 {
02034   StFollowLinksState srcFollowState(*this, followLinks);
02035 
02036   nsresult rv;
02037   OSErr err;
02038   FSRef srcFSRef, newFSRef;
02039 
02040   rv = GetFSRefInternal(srcFSRef);
02041   if (NS_FAILED(rv))
02042     return rv;
02043 
02044   nsCOMPtr<nsIFile> newParentDir = aParentDir;
02045 
02046   if (!newParentDir) {
02047     if (newName.IsEmpty())
02048       return NS_ERROR_INVALID_ARG;
02049     rv = GetParent(getter_AddRefs(newParentDir));
02050     if (NS_FAILED(rv))
02051       return rv;    
02052   }
02053 
02054   // If newParentDir does not exist, create it
02055   PRBool exists;
02056   rv = newParentDir->Exists(&exists);
02057   if (NS_FAILED(rv))
02058     return rv;
02059   if (!exists) {
02060     rv = newParentDir->Create(nsIFile::DIRECTORY_TYPE, 0777);
02061     if (NS_FAILED(rv))
02062       return rv;
02063   }
02064 
02065   FSRef destFSRef;
02066   nsCOMPtr<nsILocalFileMac> newParentDirMac(do_QueryInterface(newParentDir));
02067   if (!newParentDirMac)
02068     return NS_ERROR_NO_INTERFACE;
02069   rv = newParentDirMac->GetFSRef(&destFSRef);
02070   if (NS_FAILED(rv))
02071     return rv;
02072 
02073   err =
02074    ::FSCopyObject(&srcFSRef, &destFSRef, newName.Length(),
02075                   newName.Length() ? PromiseFlatString(newName).get() : NULL,
02076                   0, kFSCatInfoNone, false, false, NULL, NULL, &newFSRef);
02077 
02078   return MacErrorMapper(err);
02079 }
02080 
02081 const PRInt64 kMilisecsPerSec = 1000LL;
02082 const PRInt64 kUTCDateTimeFractionDivisor = 65535LL;
02083 
02084 PRInt64 nsLocalFile::HFSPlustoNSPRTime(const UTCDateTime& utcTime)
02085 {
02086   // Start with seconds since Jan. 1, 1904 GMT
02087   PRInt64 result = ((PRInt64)utcTime.highSeconds << 32) + (PRInt64)utcTime.lowSeconds; 
02088   // Subtract to convert to NSPR epoch of 1970
02089   result -= kJanuaryFirst1970Seconds;
02090   // Convert to milisecs
02091   result *= kMilisecsPerSec;
02092   // Convert the fraction to milisecs and add it
02093   result += ((PRInt64)utcTime.fraction * kMilisecsPerSec) / kUTCDateTimeFractionDivisor;
02094 
02095   return result;
02096 }
02097 
02098 void nsLocalFile::NSPRtoHFSPlusTime(PRInt64 nsprTime, UTCDateTime& utcTime)
02099 {
02100   PRInt64 fraction = nsprTime % kMilisecsPerSec;
02101   PRInt64 seconds = (nsprTime / kMilisecsPerSec) + kJanuaryFirst1970Seconds;
02102   utcTime.highSeconds = (UInt16)((PRUint64)seconds >> 32);
02103   utcTime.lowSeconds = (UInt32)seconds;
02104   utcTime.fraction = (UInt16)((fraction * kUTCDateTimeFractionDivisor) / kMilisecsPerSec);
02105 }
02106 
02107 nsresult nsLocalFile::CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr)
02108 {
02109   nsresult rv = NS_ERROR_FAILURE;
02110   CFIndex usedBufLen, inStrLen = ::CFStringGetLength(aInStrRef);
02111   CFIndex charsConverted = ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen),
02112                               kCFStringEncodingUTF8, 0, PR_FALSE, nsnull, 0, &usedBufLen);
02113   if (charsConverted == inStrLen) {
02114     nsAutoBuffer<UInt8, FILENAME_BUFFER_SIZE> buffer;
02115     if (buffer.EnsureElemCapacity(usedBufLen + 1)) {
02116       ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen),
02117           kCFStringEncodingUTF8, 0, false, buffer.get(), usedBufLen, &usedBufLen);
02118       buffer.get()[usedBufLen] = '\0';
02119       aOutStr.Assign(nsDependentCString((char*)buffer.get()));
02120       rv = NS_OK;
02121     }
02122   }
02123   return rv;
02124 }
02125 
02126 //*****************************************************************************
02127 //  Global Functions
02128 //*****************************************************************************
02129 #pragma mark -
02130 #pragma mark [Global Functions]
02131 
02132 void nsLocalFile::GlobalInit()
02133 {
02134 }
02135 
02136 void nsLocalFile::GlobalShutdown()
02137 {
02138 }
02139 
02140 nsresult NS_NewLocalFile(const nsAString& path, PRBool followLinks, nsILocalFile* *result)
02141 {
02142     nsLocalFile* file = new nsLocalFile;
02143     if (file == nsnull)
02144         return NS_ERROR_OUT_OF_MEMORY;
02145     NS_ADDREF(file);
02146 
02147     file->SetFollowLinks(followLinks);
02148 
02149     if (!path.IsEmpty()) {
02150         nsresult rv = file->InitWithPath(path);
02151         if (NS_FAILED(rv)) {
02152             NS_RELEASE(file);
02153             return rv;
02154         }
02155     }
02156     *result = file;
02157     return NS_OK;
02158 }
02159 
02160 nsresult NS_NewNativeLocalFile(const nsACString& path, PRBool followLinks, nsILocalFile **result)
02161 {
02162     return NS_NewLocalFile(NS_ConvertUTF8toUCS2(path), followLinks, result);
02163 }
02164 
02165 nsresult NS_NewLocalFileWithFSSpec(const FSSpec* inSpec, PRBool followLinks, nsILocalFileMac **result)
02166 {
02167     nsLocalFile* file = new nsLocalFile();
02168     if (file == nsnull)
02169         return NS_ERROR_OUT_OF_MEMORY;
02170     NS_ADDREF(file);
02171 
02172     file->SetFollowLinks(followLinks);
02173 
02174     nsresult rv = file->InitWithFSSpec(inSpec);
02175     if (NS_FAILED(rv)) {
02176         NS_RELEASE(file);
02177         return rv;
02178     }
02179     *result = file;
02180     return NS_OK;
02181 }
02182 
02183 //*****************************************************************************
02184 //  Static Functions
02185 //*****************************************************************************
02186 
02187 static nsresult MacErrorMapper(OSErr inErr)
02188 {
02189     nsresult outErr;
02190     
02191     switch (inErr)
02192     {
02193         case noErr:
02194             outErr = NS_OK;
02195             break;
02196 
02197         case fnfErr:
02198             outErr = NS_ERROR_FILE_NOT_FOUND;
02199             break;
02200 
02201         case dupFNErr:
02202             outErr = NS_ERROR_FILE_ALREADY_EXISTS;
02203             break;
02204         
02205         case dskFulErr:
02206             outErr = NS_ERROR_FILE_DISK_FULL;
02207             break;
02208         
02209         case fLckdErr:
02210             outErr = NS_ERROR_FILE_IS_LOCKED;
02211             break;
02212         
02213         // Can't find good map for some
02214         case bdNamErr:
02215             outErr = NS_ERROR_FAILURE;
02216             break;
02217 
02218         default:    
02219             outErr = NS_ERROR_FAILURE;
02220             break;
02221     }
02222     return outErr;
02223 }
02224 
02225 static OSErr FindRunningAppBySignature(OSType aAppSig, ProcessSerialNumber& outPsn)
02226 {
02227   ProcessInfoRec info;
02228   OSErr err = noErr;
02229   
02230   outPsn.highLongOfPSN = 0;
02231   outPsn.lowLongOfPSN  = kNoProcess;
02232   
02233   while (PR_TRUE)
02234   {
02235     err = ::GetNextProcess(&outPsn);
02236     if (err == procNotFound)
02237       break;
02238     if (err != noErr)
02239       return err;
02240     info.processInfoLength = sizeof(ProcessInfoRec);
02241     info.processName = nil;
02242     info.processAppSpec = nil;
02243     err = ::GetProcessInformation(&outPsn, &info);
02244     if (err != noErr)
02245       return err;
02246     
02247     if (info.processSignature == aAppSig)
02248       return noErr;
02249   }
02250   return procNotFound;
02251 }
02252 
02253 // Convert a UTF-8 string to a UTF-16 string while normalizing to
02254 // Normalization Form C (composed Unicode). We need this because
02255 // Mac OS X file system uses NFD (Normalization Form D : decomposed Unicode)
02256 // while most other OS', server-side programs usually expect NFC.
02257 
02258 typedef void (*UnicodeNormalizer) (CFMutableStringRef, CFStringNormalizationForm);
02259 static void CopyUTF8toUTF16NFC(const nsACString& aSrc, nsAString& aResult)
02260 {
02261     static PRBool sChecked = PR_FALSE;
02262     static UnicodeNormalizer sUnicodeNormalizer = NULL;
02263 
02264     // CFStringNormalize was not introduced until Mac OS 10.2
02265     if (!sChecked) {
02266         CFBundleRef carbonBundle =
02267             CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Carbon"));
02268         if (carbonBundle)
02269             sUnicodeNormalizer = (UnicodeNormalizer)
02270                 ::CFBundleGetFunctionPointerForName(carbonBundle,
02271                                                     CFSTR("CFStringNormalize"));
02272         sChecked = PR_TRUE;
02273     }
02274 
02275     if (!sUnicodeNormalizer) {  // OS X 10.2 or earlier
02276         CopyUTF8toUTF16(aSrc, aResult);
02277         return;  
02278     }
02279 
02280     const nsAFlatCString &inFlatSrc = PromiseFlatCString(aSrc);
02281 
02282     // The number of 16bit code units in a UTF-16 string will never be
02283     // larger than the number of bytes in the corresponding UTF-8 string.
02284     CFMutableStringRef inStr =
02285         ::CFStringCreateMutable(NULL, inFlatSrc.Length());
02286 
02287     if (!inStr) {
02288         CopyUTF8toUTF16(aSrc, aResult);
02289         return;  
02290     }
02291      
02292     ::CFStringAppendCString(inStr, inFlatSrc.get(), kCFStringEncodingUTF8); 
02293 
02294     sUnicodeNormalizer(inStr, kCFStringNormalizationFormC);
02295 
02296     CFIndex length = CFStringGetLength(inStr);
02297     const UniChar* chars = CFStringGetCharactersPtr(inStr);
02298 
02299     if (chars) 
02300         aResult.Assign(chars, length);
02301     else {
02302         nsAutoBuffer<UniChar, FILENAME_BUFFER_SIZE> buffer;
02303         if (!buffer.EnsureElemCapacity(length))
02304             CopyUTF8toUTF16(aSrc, aResult);
02305         else {
02306             CFStringGetCharacters(inStr, CFRangeMake(0, length), buffer.get());
02307             aResult.Assign(buffer.get(), length);
02308         }
02309     }
02310     CFRelease(inStr);
02311 }