Bug 438394 ? land workaround patch for unkillable hangs when loading VerifiedDownload...
[wine-gecko.git] / xpcom / io / nsLocalFileOSX.mm
blob670bfc2ab7f73be1ca77081354916e33accd6f9b
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 
2 /* ***** BEGIN LICENSE BLOCK *****
3  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  * http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  *
15  * The Original Code is mozilla.org code.
16  *
17  * The Initial Developer of the Original Code is
18  * Netscape Communications Corporation.
19  * Portions created by the Initial Developer are Copyright (C) 2001, 2002
20  * the Initial Developer. All Rights Reserved.
21  *
22  * Contributor(s):
23  *  Conrad Carlen <ccarlen@netscape.com>
24  *  Jungshik Shin <jshin@mailaps.org>
25  *  Asaf Romano <mozilla.mano@sent.com>
26  *  Mark Mentovai <mark@moxienet.com>
27  *
28  * Alternatively, the contents of this file may be used under the terms of
29  * either the GNU General Public License Version 2 or later (the "GPL"), or
30  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31  * in which case the provisions of the GPL or the LGPL are applicable instead
32  * of those above. If you wish to allow use of your version of this file only
33  * under the terms of either the GPL or the LGPL, and not to allow others to
34  * use your version of this file under the terms of the MPL, indicate your
35  * decision by deleting the provisions above and replace them with the notice
36  * and other provisions required by the GPL or the LGPL. If you do not delete
37  * the provisions above, a recipient may use your version of this file under
38  * the terms of any one of the MPL, the GPL or the LGPL.
39  *
40  * ***** END LICENSE BLOCK ***** */
42 #include "nsLocalFile.h"
43 #include "nsDirectoryServiceDefs.h"
45 #include "nsObjCExceptions.h"
46 #include "nsString.h"
47 #include "nsReadableUtils.h"
48 #include "nsIDirectoryEnumerator.h"
49 #include "nsISimpleEnumerator.h"
50 #include "nsITimelineService.h"
51 #include "nsVoidArray.h"
53 #include "plbase64.h"
54 #include "prmem.h"
55 #include "nsCRT.h"
56 #include "nsHashKeys.h"
58 #include "MoreFilesX.h"
59 #include "FSCopyObject.h"
60 #include "nsTArray.h"
61 #include "nsTraceRefcntImpl.h"
63 // Mac Includes
64 #include <Carbon/Carbon.h>
65 #import <Cocoa/Cocoa.h>
67 // Unix Includes
68 #include <unistd.h>
69 #include <sys/stat.h>
70 #include <stdlib.h>
72 #if !defined(MAC_OS_X_VERSION_10_4) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4
73 #define GetAliasSizeFromRecord(aliasRecord) aliasRecord.aliasSize
74 #else
75 #define GetAliasSizeFromRecord(aliasRecord) GetAliasSizeFromPtr(&aliasRecord)
76 #endif
78 #define CHECK_mBaseRef()                        \
79     PR_BEGIN_MACRO                              \
80         if (!mBaseRef)                          \
81             return NS_ERROR_NOT_INITIALIZED;    \
82     PR_END_MACRO
84 //*****************************************************************************
85 //  Static Function Prototypes
86 //*****************************************************************************
88 static nsresult MacErrorMapper(OSErr inErr);
89 static OSErr FindRunningAppBySignature(OSType aAppSig, ProcessSerialNumber& outPsn);
90 static void CopyUTF8toUTF16NFC(const nsACString& aSrc, nsAString& aResult);
92 //*****************************************************************************
93 //  Local Helper Classes
94 //*****************************************************************************
96 #pragma mark -
97 #pragma mark [FSRef operator==]
99 bool operator==(const FSRef& lhs, const FSRef& rhs)
101   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
103   return (::FSCompareFSRefs(&lhs, &rhs) == noErr);
105   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false);
108 #pragma mark -
109 #pragma mark [StFollowLinksState]
111 class StFollowLinksState
113   public:
114     StFollowLinksState(nsLocalFile& aFile) :
115         mFile(aFile)
116     {
117         mFile.GetFollowLinks(&mSavedState);
118     }
120     StFollowLinksState(nsLocalFile& aFile, PRBool followLinksState) :
121         mFile(aFile)
122     {
123         mFile.GetFollowLinks(&mSavedState);
124         mFile.SetFollowLinks(followLinksState);
125     }
127     ~StFollowLinksState()
128     {
129         mFile.SetFollowLinks(mSavedState);
130     }
131     
132   private:
133     nsLocalFile& mFile;
134     PRBool mSavedState;
137 #pragma mark -
138 #pragma mark [nsDirEnumerator]
140 class nsDirEnumerator : public nsISimpleEnumerator,
141                         public nsIDirectoryEnumerator
143     public:
145         NS_DECL_ISUPPORTS
147         nsDirEnumerator() :
148           mIterator(nsnull),
149           mFSRefsArray(nsnull),
150           mArrayCnt(0), mArrayIndex(0)
151         {
152         }
154         nsresult Init(nsILocalFileMac* parent) 
155         {
156           NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
158           NS_ENSURE_ARG(parent);
159           
160           OSErr err;
161           nsresult rv;
162           FSRef parentRef;
163           
164           rv = parent->GetFSRef(&parentRef);
165           if (NS_FAILED(rv))
166             return rv;
167           
168           mFSRefsArray = (FSRef *)nsMemory::Alloc(sizeof(FSRef)
169                                                   * kRequestCountPerIteration);
170           if (!mFSRefsArray)
171             return NS_ERROR_OUT_OF_MEMORY;
172           
173           err = ::FSOpenIterator(&parentRef, kFSIterateFlat, &mIterator);
174           if (err != noErr)
175             return MacErrorMapper(err);
176                               
177           return NS_OK;
179           NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
180         }
182         NS_IMETHOD HasMoreElements(PRBool *result) 
183         {
184           NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
186           if (!mIterator || !mFSRefsArray) {
187             *result = PR_FALSE;
188             return NS_OK;
189           }
191           if (mNext == nsnull) {
192             if (mArrayIndex >= mArrayCnt) {
193               ItemCount actualCnt;
194               OSErr err = ::FSGetCatalogInfoBulk(mIterator,
195                                            kRequestCountPerIteration,
196                                            &actualCnt,
197                                            nsnull,
198                                            kFSCatInfoNone,
199                                            nsnull,
200                                            mFSRefsArray,
201                                            nsnull,
202                                            nsnull);
203             
204               if (err == noErr || err == errFSNoMoreItems) {
205                 mArrayCnt = actualCnt;
206                 mArrayIndex = 0;
207               }
208             }
210             if (mArrayIndex < mArrayCnt) {
211               nsLocalFile *newFile = new nsLocalFile;
212               if (!newFile)
213                 return NS_ERROR_OUT_OF_MEMORY;
214               FSRef fsRef = mFSRefsArray[mArrayIndex];
215               if (NS_FAILED(newFile->InitWithFSRef(&fsRef)))
216                 return NS_ERROR_FAILURE;
217               mArrayIndex++;
218               mNext = newFile;
219             } 
220           }
222           *result = mNext != nsnull;
223           if (!*result)
224             Close();
226           return NS_OK;
228           NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
229         }
231         NS_IMETHOD GetNext(nsISupports **result) 
232         {
233             NS_ENSURE_ARG_POINTER(result);
234             *result = nsnull;
236             nsresult rv;
237             PRBool hasMore;
238             rv = HasMoreElements(&hasMore);
239             if (NS_FAILED(rv)) return rv;
241             *result = mNext;        // might return nsnull
242             NS_IF_ADDREF(*result);
244             mNext = nsnull;
245             return NS_OK;
246         }
248         NS_IMETHOD GetNextFile(nsIFile **result)
249         {
250             *result = nsnull;
251             PRBool hasMore = PR_FALSE;
252             nsresult rv = HasMoreElements(&hasMore);
253             if (NS_FAILED(rv) || !hasMore)
254                 return rv;
255             *result = mNext;
256             NS_IF_ADDREF(*result);
257             mNext = nsnull;
258             return NS_OK;
259         }
261         NS_IMETHOD Close()
262         {
263           NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
265           if (mIterator) {
266             ::FSCloseIterator(mIterator);
267             mIterator = nsnull;
268           }
269           if (mFSRefsArray) {
270             nsMemory::Free(mFSRefsArray);
271             mFSRefsArray = nsnull;
272           }
273           return NS_OK;
275           NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
276         }
278     private:
279         ~nsDirEnumerator() 
280         {
281           Close();
282         }
284     protected:
285         // According to Apple doc, request the number of objects
286         // per call that will fit in 4 VM pages.
287         enum {
288           kRequestCountPerIteration = ((4096 * 4) / sizeof(FSRef))
289         };
290         
291         nsCOMPtr<nsILocalFileMac>   mNext;
292         
293         FSIterator              mIterator;
294         FSRef                   *mFSRefsArray;
295         PRInt32                 mArrayCnt, mArrayIndex;
298 NS_IMPL_ISUPPORTS2(nsDirEnumerator, nsISimpleEnumerator, nsIDirectoryEnumerator)
300 #pragma mark -
301 #pragma mark [StAEDesc]
303 class StAEDesc: public AEDesc
305 public:
306     StAEDesc()
307     {
308       descriptorType = typeNull;
309       dataHandle = nil;
310     }
311               
312     ~StAEDesc()
313     {
314       NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
316       ::AEDisposeDesc(this);
318       NS_OBJC_END_TRY_ABORT_BLOCK;
319     }
322 #define FILENAME_BUFFER_SIZE 512
324 //*****************************************************************************
325 //  nsLocalFile
326 //*****************************************************************************
328 const char      nsLocalFile::kPathSepChar = '/';
329 const PRUnichar nsLocalFile::kPathSepUnichar = '/';
331 // The HFS+ epoch is Jan. 1, 1904 GMT - differs from HFS in which times were local
332 // The NSPR epoch is Jan. 1, 1970 GMT
333 // 2082844800 is the difference in seconds between those dates
334 const PRInt64   nsLocalFile::kJanuaryFirst1970Seconds = 2082844800LL;
336 #pragma mark -
337 #pragma mark [CTORs/DTOR]
339 nsLocalFile::nsLocalFile() :
340   mBaseRef(nsnull),
341   mTargetRef(nsnull),
342   mCachedFSRefValid(PR_FALSE),
343   mFollowLinks(PR_TRUE),
344   mFollowLinksDirty(PR_TRUE)
348 nsLocalFile::nsLocalFile(const nsLocalFile& src) :
349   mBaseRef(src.mBaseRef),
350   mTargetRef(src.mTargetRef),
351   mCachedFSRef(src.mCachedFSRef),
352   mCachedFSRefValid(src.mCachedFSRefValid),
353   mFollowLinks(src.mFollowLinks),
354   mFollowLinksDirty(src.mFollowLinksDirty)
356   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
358   // A CFURLRef is immutable so no need to copy, just retain.
359   if (mBaseRef)
360     ::CFRetain(mBaseRef);
361   if (mTargetRef)
362     ::CFRetain(mTargetRef);
364   NS_OBJC_END_TRY_ABORT_BLOCK;
367 nsLocalFile::~nsLocalFile()
369   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
371   if (mBaseRef)
372     ::CFRelease(mBaseRef);
373   if (mTargetRef)
374     ::CFRelease(mTargetRef);
376   NS_OBJC_END_TRY_ABORT_BLOCK;
380 //*****************************************************************************
381 //  nsLocalFile::nsISupports
382 //*****************************************************************************
383 #pragma mark -
384 #pragma mark [nsISupports]
386 NS_IMPL_THREADSAFE_ISUPPORTS4(nsLocalFile,
387                               nsILocalFileMac,
388                               nsILocalFile,
389                               nsIFile,
390                               nsIHashable)
391                               
392 NS_IMETHODIMP nsLocalFile::nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
394   NS_ENSURE_ARG_POINTER(aInstancePtr);
395   NS_ENSURE_NO_AGGREGATION(outer);
397   nsLocalFile* inst = new nsLocalFile();
398   if (inst == NULL)
399     return NS_ERROR_OUT_OF_MEMORY;
400   
401   nsresult rv = inst->QueryInterface(aIID, aInstancePtr);
402   if (NS_FAILED(rv))
403   {
404     delete inst;
405     return rv;
406   }
407   return NS_OK;
411 //*****************************************************************************
412 //  nsLocalFile::nsIFile
413 //*****************************************************************************
414 #pragma mark -
415 #pragma mark [nsIFile]
417 /* void append (in AString node); */
418 NS_IMETHODIMP nsLocalFile::Append(const nsAString& aNode)
420   return AppendNative(NS_ConvertUTF16toUTF8(aNode));
423 /* [noscript] void appendNative (in ACString node); */
424 NS_IMETHODIMP nsLocalFile::AppendNative(const nsACString& aNode)
426   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
428   // Check we are correctly initialized.
429   CHECK_mBaseRef();
431   nsACString::const_iterator start, end;
432   aNode.BeginReading(start);
433   aNode.EndReading(end);
434   if (FindCharInReadable(kPathSepChar, start, end))
435     return NS_ERROR_FILE_UNRECOGNIZED_PATH;
437   CFStringRef nodeStrRef = ::CFStringCreateWithCString(kCFAllocatorDefault,
438                                   PromiseFlatCString(aNode).get(),
439                                   kCFStringEncodingUTF8);
440   if (nodeStrRef) {
441     CFURLRef newRef = ::CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault,
442                                   mBaseRef, nodeStrRef, PR_FALSE);
443     ::CFRelease(nodeStrRef);
444     if (newRef) {
445       SetBaseRef(newRef);
446       ::CFRelease(newRef);
447       return NS_OK;
448     }
449   }
450   return NS_ERROR_FAILURE;
452   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
455 /* void normalize (); */
456 NS_IMETHODIMP nsLocalFile::Normalize()
458   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
460   // Check we are correctly initialized.
461   CHECK_mBaseRef();
463   // CFURL doesn't doesn't seem to resolve paths containing relative
464   // components, so we'll nick the stdlib code from nsLocalFileUnix
465   UInt8 path[PATH_MAX] = "";
466   Boolean success;
467   success = ::CFURLGetFileSystemRepresentation(mBaseRef, true, path, PATH_MAX);
468   if (!success)
469     return NS_ERROR_FAILURE;
471   char resolved_path[PATH_MAX] = "";
472   char *resolved_path_ptr = nsnull;
473   resolved_path_ptr = realpath((char*)path, resolved_path);
475   // if there is an error, the return is null.
476   if (!resolved_path_ptr)
477       return NSRESULT_FOR_ERRNO();
479   // Need to know whether we're a directory to create a new CFURLRef
480   PRBool isDirectory;
481   nsresult rv = IsDirectory(&isDirectory);
482   NS_ENSURE_SUCCESS(rv, rv);
484   rv = NS_ERROR_FAILURE;
485   CFStringRef pathStrRef =
486     ::CFStringCreateWithCString(kCFAllocatorDefault,
487                                 resolved_path,
488                                 kCFStringEncodingUTF8);
489   if (pathStrRef) {
490     CFURLRef newURLRef =
491       ::CFURLCreateWithFileSystemPath(kCFAllocatorDefault, pathStrRef,
492                                       kCFURLPOSIXPathStyle, isDirectory);
493     if (newURLRef) {
494       SetBaseRef(newURLRef);
495       ::CFRelease(newURLRef);
496       rv = NS_OK;
497     }
498     ::CFRelease(pathStrRef);
499   }
501   return rv;
503   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
506 /* void create (in unsigned long type, in unsigned long permissions); */
507 NS_IMETHODIMP nsLocalFile::Create(PRUint32 type, PRUint32 permissions)
509   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
511   if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
512     return NS_ERROR_FILE_UNKNOWN_TYPE;
514   // Check we are correctly initialized.
515   CHECK_mBaseRef();
516   
517   nsStringArray nonExtantNodes;
518   CFURLRef pathURLRef = mBaseRef;
519   FSRef pathFSRef;
520   CFStringRef leafStrRef = nsnull;
521   nsAutoTArray<UniChar, FILENAME_BUFFER_SIZE> buffer;
522   Boolean success;
523   
524   // Work backwards through the path to find the last node which
525   // exists. Place the nodes which don't exist in an array and we'll
526   // create those below.
527   while ((success = ::CFURLGetFSRef(pathURLRef, &pathFSRef)) == false) {
528     leafStrRef = ::CFURLCopyLastPathComponent(pathURLRef);
529     if (!leafStrRef)
530       break;
531     CFIndex leafLen = ::CFStringGetLength(leafStrRef);
532     if (!buffer.SetLength(leafLen + 1))
533       break;
534     ::CFStringGetCharacters(leafStrRef, CFRangeMake(0, leafLen), buffer.Elements());
535     buffer[leafLen] = '\0';
536     nonExtantNodes.AppendString(nsString(nsDependentString(buffer.Elements())));
537     ::CFRelease(leafStrRef);
538     leafStrRef = nsnull;
539     
540     // Get the parent of the leaf for the next go round
541     CFURLRef parent = ::CFURLCreateCopyDeletingLastPathComponent(NULL, pathURLRef);
542     if (!parent)
543       break;
544     if (pathURLRef != mBaseRef)
545       ::CFRelease(pathURLRef);
546     pathURLRef = parent;
547   }
548   if (pathURLRef != mBaseRef)
549     ::CFRelease(pathURLRef);
550   if (leafStrRef != nsnull)
551     ::CFRelease(leafStrRef);
552   if (!success)
553     return NS_ERROR_FAILURE;
554   PRInt32 nodesToCreate = nonExtantNodes.Count();
555   if (nodesToCreate == 0)
556     return NS_ERROR_FILE_ALREADY_EXISTS;
557   
558   OSErr err;    
559   nsAutoString nextNodeName;
560   for (PRInt32 i = nodesToCreate - 1; i > 0; i--) {
561     nonExtantNodes.StringAt(i, nextNodeName);
562     err = ::FSCreateDirectoryUnicode(&pathFSRef,
563                                       nextNodeName.Length(),
564                                       (const UniChar *)nextNodeName.get(),
565                                       kFSCatInfoNone,
566                                       nsnull, &pathFSRef, nsnull, nsnull);
567     if (err != noErr)
568       return MacErrorMapper(err);
569   }
570   nonExtantNodes.StringAt(0, nextNodeName);
571   if (type == NORMAL_FILE_TYPE) {
572     err = ::FSCreateFileUnicode(&pathFSRef,
573                                 nextNodeName.Length(),
574                                 (const UniChar *)nextNodeName.get(),
575                                 kFSCatInfoNone,
576                                 nsnull, nsnull, nsnull);
577   }
578   else {
579     err = ::FSCreateDirectoryUnicode(&pathFSRef,
580                                     nextNodeName.Length(),
581                                     (const UniChar *)nextNodeName.get(),
582                                     kFSCatInfoNone,
583                                     nsnull, nsnull, nsnull, nsnull);
584   }
585             
586   return MacErrorMapper(err);
588   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
591 /* attribute AString leafName; */
592 NS_IMETHODIMP nsLocalFile::GetLeafName(nsAString& aLeafName)
594   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
596   nsCAutoString nativeString;
597   nsresult rv = GetNativeLeafName(nativeString);
598   if (NS_FAILED(rv))
599     return rv;
600   CopyUTF8toUTF16NFC(nativeString, aLeafName);
601   return NS_OK;
603   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
606 NS_IMETHODIMP nsLocalFile::SetLeafName(const nsAString& aLeafName)
608   return SetNativeLeafName(NS_ConvertUTF16toUTF8(aLeafName));
611 /* [noscript] attribute ACString nativeLeafName; */
612 NS_IMETHODIMP nsLocalFile::GetNativeLeafName(nsACString& aNativeLeafName)
614   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
616   // Check we are correctly initialized.
617   CHECK_mBaseRef();
619   nsresult rv = NS_ERROR_FAILURE;
620   CFStringRef leafStrRef = ::CFURLCopyLastPathComponent(mBaseRef);
621   if (leafStrRef) {
622     rv = CFStringReftoUTF8(leafStrRef, aNativeLeafName);
623     ::CFRelease(leafStrRef);
624   }
625   return rv;
627   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
630 NS_IMETHODIMP nsLocalFile::SetNativeLeafName(const nsACString& aNativeLeafName)
632   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
634   // Check we are correctly initialized.
635   CHECK_mBaseRef();
637   nsresult rv = NS_ERROR_FAILURE;
638   CFURLRef parentURLRef = ::CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, mBaseRef);
639   if (parentURLRef) {
640     CFStringRef nodeStrRef = ::CFStringCreateWithCString(kCFAllocatorDefault,
641                                     PromiseFlatCString(aNativeLeafName).get(),
642                                     kCFStringEncodingUTF8);
644     if (nodeStrRef) {
645       CFURLRef newURLRef = ::CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault,
646                                     parentURLRef, nodeStrRef, PR_FALSE);
647       if (newURLRef) {
648         SetBaseRef(newURLRef);
649         ::CFRelease(newURLRef);
650         rv = NS_OK;
651       }
652       ::CFRelease(nodeStrRef);
653     }
654     ::CFRelease(parentURLRef);
655   }
656   return rv;
658   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
661 /* void copyTo (in nsIFile newParentDir, in AString newName); */
662 NS_IMETHODIMP nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString& newName)
664   return CopyInternal(newParentDir, newName, PR_FALSE);
667 /* [noscrpit] void CopyToNative (in nsIFile newParentDir, in ACString newName); */
668 NS_IMETHODIMP nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString& newName)
670   return CopyInternal(newParentDir, NS_ConvertUTF8toUTF16(newName), PR_FALSE);
673 /* void copyToFollowingLinks (in nsIFile newParentDir, in AString newName); */
674 NS_IMETHODIMP nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString& newName)
676   return CopyInternal(newParentDir, newName, PR_TRUE);
679 /* [noscript] void copyToFollowingLinksNative (in nsIFile newParentDir, in ACString newName); */
680 NS_IMETHODIMP nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString& newName)
682   return CopyInternal(newParentDir, NS_ConvertUTF8toUTF16(newName), PR_TRUE);
685 /* void moveTo (in nsIFile newParentDir, in AString newName); */
686 NS_IMETHODIMP nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString& newName)
688   return MoveToNative(newParentDir, NS_ConvertUTF16toUTF8(newName));
691 /* [noscript] void moveToNative (in nsIFile newParentDir, in ACString newName); */
692 NS_IMETHODIMP nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString& newName)
694   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
696   // Check we are correctly initialized.
697   CHECK_mBaseRef();
699   StFollowLinksState followLinks(*this, PR_FALSE);
701   PRBool isDirectory;
702   nsresult rv = IsDirectory(&isDirectory);
703   if (NS_FAILED(rv))
704     return rv;
706   // Get the source path.
707   nsCAutoString srcPath;
708   rv = GetNativePath(srcPath);
709   if (NS_FAILED(rv))
710     return rv;
712   // Build the destination path.
713   nsCOMPtr<nsIFile> parentDir = newParentDir;
714   if (!parentDir) {
715     if (newName.IsEmpty())
716       return NS_ERROR_INVALID_ARG;
717     rv = GetParent(getter_AddRefs(parentDir));
718     if (NS_FAILED(rv))
719       return rv;   
720   }
721   else {
722     PRBool exists;
723     rv = parentDir->Exists(&exists);
724     if (NS_FAILED(rv))
725       return rv;
726     if (!exists) {
727       rv = parentDir->Create(nsIFile::DIRECTORY_TYPE, 0777);
728       if (NS_FAILED(rv))
729         return rv;
730     }
731   }
733   nsCAutoString destPath;
734   rv = parentDir->GetNativePath(destPath);
735   if (NS_FAILED(rv))
736     return rv;
738   if (!newName.IsEmpty())
739     destPath.Append(NS_LITERAL_CSTRING("/") + newName);
740   else {
741     nsCAutoString leafName;
742     rv = GetNativeLeafName(leafName);
743     if (NS_FAILED(rv))
744       return rv;
745     destPath.Append(NS_LITERAL_CSTRING("/") + leafName);
746   }
748   // Perform the move.
749   if (rename(srcPath.get(), destPath.get()) != 0) {
750     if (errno == EXDEV) {
751       // Can't move across volume (device) boundaries.  Copy and remove.
752       rv = CopyToNative(parentDir, newName);
753       if (NS_SUCCEEDED(rv)) {
754         // Permit removal failure.
755         Remove(PR_TRUE);
756       }
757     }
758     else
759       rv = NSRESULT_FOR_ERRNO();
761     if (NS_FAILED(rv))
762       return rv;
763   }
765   // Update |this| to refer to the moved file.
766   CFURLRef newBaseRef =
767    ::CFURLCreateFromFileSystemRepresentation(NULL, (UInt8*)destPath.get(),
768                                              destPath.Length(), isDirectory);
769   if (!newBaseRef)
770     return NS_ERROR_FAILURE;
771   SetBaseRef(newBaseRef);
772   ::CFRelease(newBaseRef);
774   return rv;
776   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
779 /* void remove (in boolean recursive); */
780 NS_IMETHODIMP nsLocalFile::Remove(PRBool recursive)
782   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
784   // Check we are correctly initialized.
785   CHECK_mBaseRef();
787   // XXX If we're an alias, never remove target
788   StFollowLinksState followLinks(*this, PR_FALSE);
790   PRBool isDirectory;
791   nsresult rv = IsDirectory(&isDirectory);
792   if (NS_FAILED(rv))
793     return rv;
795   if (recursive && isDirectory) {
796     FSRef fsRef;
797     rv = GetFSRefInternal(fsRef);
798     if (NS_FAILED(rv))
799       return rv;
801     // Call MoreFilesX to do a recursive removal.
802     OSStatus err = ::FSDeleteContainer(&fsRef);
803     rv = MacErrorMapper(err);
804   }
805   else {
806     nsCAutoString path;
807     rv = GetNativePath(path);
808     if (NS_FAILED(rv))
809       return rv;
811     const char* pathPtr = path.get();
812     int status;
813     if (isDirectory)
814       status = rmdir(pathPtr);
815     else
816       status = unlink(pathPtr);
818     if (status != 0)
819       rv = NSRESULT_FOR_ERRNO();
820   }
822   mCachedFSRefValid = PR_FALSE;
823   return rv;
825   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
828 /* attribute unsigned long permissions; */
829 NS_IMETHODIMP nsLocalFile::GetPermissions(PRUint32 *aPermissions)
831   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
833   NS_ENSURE_ARG_POINTER(aPermissions);
834   
835   FSRef fsRef;
836   nsresult rv = GetFSRefInternal(fsRef);
837   if (NS_FAILED(rv))
838     return rv;
839     
840   FSCatalogInfo catalogInfo;
841   OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoPermissions, &catalogInfo,
842                   nsnull, nsnull, nsnull);
843   if (err != noErr)
844     return MacErrorMapper(err);
845   FSPermissionInfo *permPtr = (FSPermissionInfo*)catalogInfo.permissions;
846   *aPermissions = permPtr->mode;
847   return NS_OK;
849   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
852 NS_IMETHODIMP nsLocalFile::SetPermissions(PRUint32 aPermissions)
854   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
856   FSRef fsRef;
857   nsresult rv = GetFSRefInternal(fsRef);
858   if (NS_FAILED(rv))
859     return rv;
860   
861   FSCatalogInfo catalogInfo;
862   OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoPermissions, &catalogInfo,
863                   nsnull, nsnull, nsnull);
864   if (err != noErr)
865     return MacErrorMapper(err);
866   FSPermissionInfo *permPtr = (FSPermissionInfo*)catalogInfo.permissions;
867   permPtr->mode = (UInt16)aPermissions;
868   err = ::FSSetCatalogInfo(&fsRef, kFSCatInfoPermissions, &catalogInfo);
869   return MacErrorMapper(err);
871   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
874 /* attribute unsigned long permissionsOfLink; */
875 NS_IMETHODIMP nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink)
877     NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
878     return NS_ERROR_NOT_IMPLEMENTED;
881 NS_IMETHODIMP nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissionsOfLink)
883     NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
884     return NS_ERROR_NOT_IMPLEMENTED;
887 /* attribute PRInt64 lastModifiedTime; */
888 NS_IMETHODIMP nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModifiedTime)
890   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
892   // Check we are correctly initialized.
893   CHECK_mBaseRef();
895   NS_ENSURE_ARG_POINTER(aLastModifiedTime);
896   
897   FSRef fsRef;
898   nsresult rv = GetFSRefInternal(fsRef);
899   if (NS_FAILED(rv))
900     return rv;
901     
902   FSCatalogInfo catalogInfo;
903   OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoContentMod, &catalogInfo,
904                                 nsnull, nsnull, nsnull);
905   if (err != noErr)
906     return MacErrorMapper(err);
907   *aLastModifiedTime = HFSPlustoNSPRTime(catalogInfo.contentModDate);  
908   return NS_OK;
910   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
913 NS_IMETHODIMP nsLocalFile::SetLastModifiedTime(PRInt64 aLastModifiedTime)
915   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
917   // Check we are correctly initialized.
918   CHECK_mBaseRef();
920   OSErr err;
921   nsresult rv;
922   FSRef fsRef;
923   FSCatalogInfo catalogInfo;
925   rv = GetFSRefInternal(fsRef);
926   if (NS_FAILED(rv))
927     return rv;
929   FSRef parentRef;
930   PRBool notifyParent;
932   /* Get the node flags, the content modification date and time, and the parent ref */
933   err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags + kFSCatInfoContentMod,
934                            &catalogInfo, NULL, NULL, &parentRef);
935   if (err != noErr)
936     return MacErrorMapper(err);
937   
938   /* Notify the parent if this is a file */
939   notifyParent = (0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask));
941   NSPRtoHFSPlusTime(aLastModifiedTime, catalogInfo.contentModDate);
942   err = ::FSSetCatalogInfo(&fsRef, kFSCatInfoContentMod, &catalogInfo);
943   if (err != noErr)
944     return MacErrorMapper(err);
946   /* Send a notification for the parent of the file, or for the directory */
947   err = FNNotify(notifyParent ? &parentRef : &fsRef, kFNDirectoryModifiedMessage, kNilOptions);
948   if (err != noErr)
949     return MacErrorMapper(err);
951   return NS_OK;
953   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
956 /* attribute PRInt64 lastModifiedTimeOfLink; */
957 NS_IMETHODIMP nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModifiedTimeOfLink)
959     NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
960     return NS_ERROR_NOT_IMPLEMENTED;
962 NS_IMETHODIMP nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModifiedTimeOfLink)
964     NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
965     return NS_ERROR_NOT_IMPLEMENTED;
968 /* attribute PRInt64 fileSize; */
969 NS_IMETHODIMP nsLocalFile::GetFileSize(PRInt64 *aFileSize)
971   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
973   NS_ENSURE_ARG_POINTER(aFileSize);
974   *aFileSize = 0;
975   
976   FSRef fsRef;
977   nsresult rv = GetFSRefInternal(fsRef);
978   if (NS_FAILED(rv))
979     return rv;
980       
981   FSCatalogInfo catalogInfo;
982   OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags + kFSCatInfoDataSizes, &catalogInfo,
983                                   nsnull, nsnull, nsnull);
984   if (err != noErr)
985     return MacErrorMapper(err);
986   
987   // FSGetCatalogInfo can return a bogus size for directories sometimes, so only
988   // rely on the answer for files
989   if ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) == 0)
990       *aFileSize = catalogInfo.dataLogicalSize;
991   return NS_OK;
993   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
996 NS_IMETHODIMP nsLocalFile::SetFileSize(PRInt64 aFileSize)
998   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1000   // Check we are correctly initialized.
1001   CHECK_mBaseRef();
1003   FSRef fsRef;
1004   nsresult rv = GetFSRefInternal(fsRef);
1005   if (NS_FAILED(rv))
1006     return rv;
1007   
1008   SInt16 refNum;    
1009   OSErr err = ::FSOpenFork(&fsRef, 0, nsnull, fsWrPerm, &refNum);
1010   if (err != noErr)
1011     return MacErrorMapper(err);
1012   err = ::FSSetForkSize(refNum, fsFromStart, aFileSize);
1013   ::FSCloseFork(refNum);  
1014   
1015   return MacErrorMapper(err);
1017   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1020 /* readonly attribute PRInt64 fileSizeOfLink; */
1021 NS_IMETHODIMP nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSizeOfLink)
1023   // Check we are correctly initialized.
1024   CHECK_mBaseRef();
1026   NS_ENSURE_ARG_POINTER(aFileSizeOfLink);
1027   
1028   StFollowLinksState followLinks(*this, PR_FALSE);
1029   return GetFileSize(aFileSizeOfLink);
1032 /* readonly attribute AString target; */
1033 NS_IMETHODIMP nsLocalFile::GetTarget(nsAString& aTarget)
1035   nsCAutoString nativeString;
1036   nsresult rv = GetNativeTarget(nativeString);
1037   if (NS_FAILED(rv))
1038     return rv;
1039   CopyUTF8toUTF16NFC(nativeString, aTarget);
1040   return NS_OK;
1043 /* [noscript] readonly attribute ACString nativeTarget; */
1044 NS_IMETHODIMP nsLocalFile::GetNativeTarget(nsACString& aNativeTarget)
1046   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1048   if (!mTargetRef)
1049     return NS_ERROR_NOT_INITIALIZED;
1050   nsresult rv = NS_ERROR_FAILURE;
1051   CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(mTargetRef, kCFURLPOSIXPathStyle);
1052   if (pathStrRef) {
1053     rv = CFStringReftoUTF8(pathStrRef, aNativeTarget);
1054     ::CFRelease(pathStrRef);
1055   }
1056   return rv;
1058   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1061 /* readonly attribute AString path; */
1062 NS_IMETHODIMP nsLocalFile::GetPath(nsAString& aPath)
1064   nsCAutoString nativeString;
1065   nsresult rv = GetNativePath(nativeString);
1066   if (NS_FAILED(rv))
1067     return rv;
1068   CopyUTF8toUTF16NFC(nativeString, aPath);
1069   return NS_OK;
1072 /* [noscript] readonly attribute ACString nativePath; */
1073 NS_IMETHODIMP nsLocalFile::GetNativePath(nsACString& aNativePath)
1075   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1077   // Check we are correctly initialized.
1078   CHECK_mBaseRef();
1080   nsresult rv = NS_ERROR_FAILURE;
1081   CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(mBaseRef, kCFURLPOSIXPathStyle);
1082   if (pathStrRef) {
1083     rv = CFStringReftoUTF8(pathStrRef, aNativePath);
1084     ::CFRelease(pathStrRef);
1085   }
1086   return rv;
1088   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1091 /* boolean exists (); */
1092 NS_IMETHODIMP nsLocalFile::Exists(PRBool *_retval)
1094   // Check we are correctly initialized.
1095   CHECK_mBaseRef();
1097   NS_ENSURE_ARG_POINTER(_retval);
1098   *_retval = PR_FALSE;
1099   
1100   FSRef fsRef;
1101   if (NS_SUCCEEDED(GetFSRefInternal(fsRef, PR_TRUE))) {
1102     *_retval = PR_TRUE;
1103   }
1104   
1105   return NS_OK;
1108 /* boolean isWritable (); */
1109 NS_IMETHODIMP nsLocalFile::IsWritable(PRBool *_retval)
1111     NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1113     // Check we are correctly initialized.
1114     CHECK_mBaseRef();
1116     NS_ENSURE_ARG_POINTER(_retval);
1117     *_retval = PR_FALSE;
1118     
1119     FSRef fsRef;
1120     nsresult rv = GetFSRefInternal(fsRef);
1121     if (NS_FAILED(rv))
1122       return rv;
1123     if (::FSCheckLock(&fsRef) == noErr) {      
1124       PRUint32 permissions;
1125       rv = GetPermissions(&permissions);
1126       if (NS_FAILED(rv))
1127         return rv;
1128       *_retval = ((permissions & S_IWUSR) != 0);
1129     }
1130     return NS_OK;
1132     NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1135 /* boolean isReadable (); */
1136 NS_IMETHODIMP nsLocalFile::IsReadable(PRBool *_retval)
1138     // Check we are correctly initialized.
1139     CHECK_mBaseRef();
1141     NS_ENSURE_ARG_POINTER(_retval);
1142     *_retval = PR_FALSE;
1143     
1144     PRUint32 permissions;
1145     nsresult rv = GetPermissions(&permissions);
1146     if (NS_FAILED(rv))
1147       return rv;
1148     *_retval = ((permissions & S_IRUSR) != 0);
1149     return NS_OK;
1152 /* boolean isExecutable (); */
1153 NS_IMETHODIMP nsLocalFile::IsExecutable(PRBool *_retval)
1155   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1157   // Check we are correctly initialized.
1158   CHECK_mBaseRef();
1160   NS_ENSURE_ARG_POINTER(_retval);
1161   *_retval = PR_FALSE;
1162   
1163   FSRef fsRef;
1164   nsresult rv = GetFSRefInternal(fsRef);
1165   if (NS_FAILED(rv))
1166     return rv;
1167     
1168   LSRequestedInfo theInfoRequest = kLSRequestAllInfo;
1169   LSItemInfoRecord theInfo;
1170   if (::LSCopyItemInfoForRef(&fsRef, theInfoRequest, &theInfo) == noErr) {
1171     if ((theInfo.flags & kLSItemInfoIsApplication) != 0)
1172     *_retval = PR_TRUE;
1173   }
1174   return NS_OK;
1176   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1179 /* boolean isHidden (); */
1180 NS_IMETHODIMP nsLocalFile::IsHidden(PRBool *_retval)
1182   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1184   NS_ENSURE_ARG_POINTER(_retval);
1185   *_retval = PR_FALSE;
1186   
1187   FSRef fsRef;
1188   nsresult rv = GetFSRefInternal(fsRef);
1189   if (NS_FAILED(rv))
1190     return rv;
1191   
1192   FSCatalogInfo catalogInfo;
1193   HFSUniStr255 leafName;  
1194   OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoFinderInfo, &catalogInfo,
1195                                 &leafName, nsnull, nsnull);
1196   if (err != noErr)
1197     return MacErrorMapper(err);
1198       
1199   FileInfo *fInfoPtr = (FileInfo *)(catalogInfo.finderInfo); // Finder flags are in the same place whether we use FileInfo or FolderInfo
1200   if ((fInfoPtr->finderFlags & kIsInvisible) != 0) {
1201     *_retval = PR_TRUE;
1202   }
1203   else {
1204     // If the leaf name begins with a '.', consider it invisible
1205     if (leafName.length >= 1 && leafName.unicode[0] == UniChar('.'))
1206       *_retval = PR_TRUE;
1207   }
1208   return NS_OK;
1210   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1213 /* boolean isDirectory (); */
1214 NS_IMETHODIMP nsLocalFile::IsDirectory(PRBool *_retval)
1216   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1218   NS_ENSURE_ARG_POINTER(_retval);
1219   *_retval = PR_FALSE;
1220   
1221   FSRef fsRef;
1222   nsresult rv = GetFSRefInternal(fsRef, PR_FALSE);
1223   if (NS_FAILED(rv))
1224     return rv;
1225     
1226   FSCatalogInfo catalogInfo;
1227   OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags, &catalogInfo,
1228                                 nsnull, nsnull, nsnull);
1229   if (err != noErr)
1230     return MacErrorMapper(err);
1231   *_retval = ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0);
1232   return NS_OK;
1234   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1237 /* boolean isFile (); */
1238 NS_IMETHODIMP nsLocalFile::IsFile(PRBool *_retval)
1240   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1242   NS_ENSURE_ARG_POINTER(_retval);
1243   *_retval = PR_FALSE;
1244   
1245   FSRef fsRef;
1246   nsresult rv = GetFSRefInternal(fsRef, PR_FALSE);
1247   if (NS_FAILED(rv))
1248     return rv;
1249     
1250   FSCatalogInfo catalogInfo;
1251   OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags, &catalogInfo,
1252                                 nsnull, nsnull, nsnull);
1253   if (err != noErr)
1254     return MacErrorMapper(err);
1255   *_retval = ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) == 0);
1256   return NS_OK;
1258   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1261 /* boolean isSymlink (); */
1262 NS_IMETHODIMP nsLocalFile::IsSymlink(PRBool *_retval)
1264   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1266   // Check we are correctly initialized.
1267   CHECK_mBaseRef();
1269   NS_ENSURE_ARG(_retval);
1270   *_retval = PR_FALSE;
1272   // Check we are correctly initialized.
1273   CHECK_mBaseRef();
1275   FSRef fsRef;
1276   if (::CFURLGetFSRef(mBaseRef, &fsRef)) {
1277     Boolean isAlias, isFolder;
1278     if (::FSIsAliasFile(&fsRef, &isAlias, &isFolder) == noErr)
1279         *_retval = isAlias;
1280   }
1281   return NS_OK;
1283   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1286 /* boolean isSpecial (); */
1287 NS_IMETHODIMP nsLocalFile::IsSpecial(PRBool *_retval)
1289     NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
1290     return NS_ERROR_NOT_IMPLEMENTED;
1293 /* nsIFile clone (); */
1294 NS_IMETHODIMP nsLocalFile::Clone(nsIFile **_retval)
1296     // Just copy-construct ourselves
1297     *_retval = new nsLocalFile(*this);
1298     if (!*_retval)
1299       return NS_ERROR_OUT_OF_MEMORY;
1301     NS_ADDREF(*_retval);
1302     
1303     return NS_OK;
1306 /* boolean equals (in nsIFile inFile); */
1307 NS_IMETHODIMP nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
1309     return EqualsInternal(inFile, PR_TRUE, _retval);
1312 nsresult
1313 nsLocalFile::EqualsInternal(nsISupports* inFile, PRBool aUpdateCache,
1314                             PRBool *_retval)
1316   NS_ENSURE_ARG_POINTER(_retval);
1317   *_retval = PR_FALSE;
1318   
1319   nsCOMPtr<nsILocalFileMac> inMacFile(do_QueryInterface(inFile));
1320   if (!inFile)
1321     return NS_OK;
1322     
1323   nsLocalFile* inLF =
1324       static_cast<nsLocalFile*>((nsILocalFileMac*) inMacFile);
1326   // If both exist, compare FSRefs
1327   FSRef thisFSRef, inFSRef;
1328   nsresult rv1 = GetFSRefInternal(thisFSRef, aUpdateCache);
1329   nsresult rv2 = inLF->GetFSRefInternal(inFSRef, aUpdateCache);
1330   if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) {
1331     *_retval = (thisFSRef == inFSRef);
1332     return NS_OK;
1333   }
1334   // If one exists and the other doesn't, not equal  
1335   if (rv1 != rv2)
1336     return NS_OK;
1337     
1338   // Arg, we have to get their paths and compare
1339   nsCAutoString thisPath, inPath;
1340   if (NS_FAILED(GetNativePath(thisPath)))
1341     return NS_ERROR_FAILURE;
1342   if (NS_FAILED(inMacFile->GetNativePath(inPath)))
1343     return NS_ERROR_FAILURE;
1344   *_retval = thisPath.Equals(inPath);
1345   
1346   return NS_OK;
1349 /* boolean contains (in nsIFile inFile, in boolean recur); */
1350 NS_IMETHODIMP nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retval)
1352   // Check we are correctly initialized.
1353   CHECK_mBaseRef();
1355   NS_ENSURE_ARG_POINTER(_retval);
1356   *_retval = PR_FALSE;
1358   PRBool isDir;
1359   nsresult rv = IsDirectory(&isDir);
1360   if (NS_FAILED(rv))
1361     return rv;
1362   if (!isDir)
1363     return NS_OK;     // must be a dir to contain someone
1365   nsCAutoString thisPath, inPath;
1366   if (NS_FAILED(GetNativePath(thisPath)) || NS_FAILED(inFile->GetNativePath(inPath)))
1367     return NS_ERROR_FAILURE;
1368   size_t thisPathLen = thisPath.Length();
1369   if ((inPath.Length() > thisPathLen + 1) && (strncasecmp(thisPath.get(), inPath.get(), thisPathLen) == 0)) {
1370     // Now make sure that the |inFile|'s path has a separator at thisPathLen,
1371     // and there's at least one more character after that.
1372     if (inPath[thisPathLen] == kPathSepChar)
1373       *_retval = PR_TRUE;
1374   }  
1375   return NS_OK;
1378 /* readonly attribute nsIFile parent; */
1379 NS_IMETHODIMP nsLocalFile::GetParent(nsIFile * *aParent)
1381   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1383   NS_ENSURE_ARG_POINTER(aParent);
1384   *aParent = nsnull;
1386   // Check we are correctly initialized.
1387   CHECK_mBaseRef();
1389   nsLocalFile *newFile = nsnull;
1391   // If it can be determined without error that a file does not
1392   // have a parent, return nsnull for the parent and NS_OK as the result.
1393   // See bug 133617.
1394   nsresult rv = NS_OK;
1395   CFURLRef parentURLRef = ::CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, mBaseRef);
1396   if (parentURLRef) {
1397     // If the parent path is longer than file's path then 
1398     // CFURLCreateCopyDeletingLastPathComponent must have simply added
1399     // two dots at the end - in this case indicate that there is no parent.
1400     // See bug 332389.
1401     CFStringRef path = ::CFURLGetString(mBaseRef);
1402     CFStringRef newPath = ::CFURLGetString(parentURLRef);
1403     if (::CFStringGetLength(newPath) < ::CFStringGetLength(path)) {
1404       rv = NS_ERROR_FAILURE;
1405       newFile = new nsLocalFile;
1406       if (newFile) {
1407         rv = newFile->InitWithCFURL(parentURLRef);
1408         if (NS_SUCCEEDED(rv)) {
1409           NS_ADDREF(*aParent = newFile);
1410           rv = NS_OK;
1411         }
1412       }
1413     }
1414     ::CFRelease(parentURLRef);
1415   }
1416   return rv;
1418   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1421 /* readonly attribute nsISimpleEnumerator directoryEntries; */
1422 NS_IMETHODIMP nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator **aDirectoryEntries)
1424   NS_ENSURE_ARG_POINTER(aDirectoryEntries);
1425   *aDirectoryEntries = nsnull;
1427   nsresult rv;
1428   PRBool isDir;
1429   rv = IsDirectory(&isDir);
1430   if (NS_FAILED(rv)) 
1431     return rv;
1432   if (!isDir)
1433     return NS_ERROR_FILE_NOT_DIRECTORY;
1435   nsDirEnumerator* dirEnum = new nsDirEnumerator;
1436   if (dirEnum == nsnull)
1437     return NS_ERROR_OUT_OF_MEMORY;
1438   NS_ADDREF(dirEnum);
1439   rv = dirEnum->Init(this);
1440   if (NS_FAILED(rv)) {
1441     NS_RELEASE(dirEnum);
1442     return rv;
1443   }
1444   *aDirectoryEntries = dirEnum;
1445   
1446   return NS_OK;
1450 //*****************************************************************************
1451 //  nsLocalFile::nsILocalFile
1452 //*****************************************************************************
1453 #pragma mark -
1454 #pragma mark [nsILocalFile]
1456 /* void initWithPath (in AString filePath); */
1457 NS_IMETHODIMP nsLocalFile::InitWithPath(const nsAString& filePath)
1459   return InitWithNativePath(NS_ConvertUTF16toUTF8(filePath));
1462 /* [noscript] void initWithNativePath (in ACString filePath); */
1463 NS_IMETHODIMP nsLocalFile::InitWithNativePath(const nsACString& filePath)
1465   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1467   nsCAutoString fixedPath;
1468   if (Substring(filePath, 0, 2).EqualsLiteral("~/")) {
1469     nsCOMPtr<nsIFile> homeDir;
1470     nsCAutoString homePath;
1471     nsresult rv = NS_GetSpecialDirectory(NS_OS_HOME_DIR,
1472                                         getter_AddRefs(homeDir));
1473     NS_ENSURE_SUCCESS(rv, rv);
1474     rv = homeDir->GetNativePath(homePath);
1475     NS_ENSURE_SUCCESS(rv, rv);
1476     
1477     fixedPath = homePath + Substring(filePath, 1, filePath.Length() - 1);
1478   }
1479   else if (filePath.IsEmpty() || filePath.First() != '/')
1480     return NS_ERROR_FILE_UNRECOGNIZED_PATH;
1481   else
1482     fixedPath.Assign(filePath);
1484   // A path with consecutive '/'s which are not between
1485   // nodes crashes CFURLGetFSRef(). Consecutive '/'s which
1486   // are between actual nodes are OK. So, convert consecutive
1487   // '/'s to a single one.
1488   fixedPath.ReplaceSubstring("//", "/");
1490   // On 10.2, huge paths also crash CFURLGetFSRef()
1491   if (fixedPath.Length() > PATH_MAX)
1492     return NS_ERROR_FILE_NAME_TOO_LONG;
1494   CFStringRef pathAsCFString;
1495   CFURLRef pathAsCFURL;
1497   pathAsCFString = ::CFStringCreateWithCString(nsnull, fixedPath.get(), kCFStringEncodingUTF8);
1498   if (!pathAsCFString)
1499     return NS_ERROR_FAILURE;
1500   pathAsCFURL = ::CFURLCreateWithFileSystemPath(nsnull, pathAsCFString, kCFURLPOSIXPathStyle, PR_FALSE);
1501   if (!pathAsCFURL) {
1502     ::CFRelease(pathAsCFString);
1503     return NS_ERROR_FAILURE;
1504   }
1505   SetBaseRef(pathAsCFURL);
1506   ::CFRelease(pathAsCFURL);
1507   ::CFRelease(pathAsCFString);
1508   return NS_OK;
1510   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1513 /* void initWithFile (in nsILocalFile aFile); */
1514 NS_IMETHODIMP nsLocalFile::InitWithFile(nsILocalFile *aFile)
1516   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1518   NS_ENSURE_ARG(aFile);
1519   
1520   nsCOMPtr<nsILocalFileMac> aFileMac(do_QueryInterface(aFile));
1521   if (!aFileMac)
1522     return NS_ERROR_UNEXPECTED;
1523   CFURLRef urlRef;
1524   nsresult rv = aFileMac->GetCFURL(&urlRef);
1525   if (NS_FAILED(rv))
1526     return rv;
1527   rv = InitWithCFURL(urlRef);
1528   ::CFRelease(urlRef);
1529   return rv;
1531   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1534 /* attribute PRBool followLinks; */
1535 NS_IMETHODIMP nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
1537   NS_ENSURE_ARG_POINTER(aFollowLinks);
1538   
1539   *aFollowLinks = mFollowLinks;
1540   return NS_OK;
1543 NS_IMETHODIMP nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
1545   if (aFollowLinks != mFollowLinks) {
1546     mFollowLinks = aFollowLinks;
1547     UpdateTargetRef();
1548   }
1549   return NS_OK;
1552 /* [noscript] PRFileDescStar openNSPRFileDesc (in long flags, in long mode); */
1553 NS_IMETHODIMP nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval)
1555   NS_ENSURE_ARG_POINTER(_retval);
1557   nsCAutoString path;
1558   nsresult rv = GetPathInternal(path);
1559   if (NS_FAILED(rv))
1560     return rv;
1561     
1562   *_retval = PR_Open(path.get(), flags, mode);
1563   if (! *_retval)
1564     return NS_ErrorAccordingToNSPR();
1566   return NS_OK;
1569 /* [noscript] FILE openANSIFileDesc (in string mode); */
1570 NS_IMETHODIMP nsLocalFile::OpenANSIFileDesc(const char *mode, FILE **_retval)
1572   NS_ENSURE_ARG_POINTER(_retval);
1574   nsCAutoString path;
1575   nsresult rv = GetPathInternal(path);
1576   if (NS_FAILED(rv))
1577     return rv;
1578     
1579   *_retval = fopen(path.get(), mode);
1580   if (! *_retval)
1581     return NS_ERROR_FAILURE;
1583   return NS_OK;
1586 /* [noscript] PRLibraryStar load (); */
1587 NS_IMETHODIMP nsLocalFile::Load(PRLibrary **_retval)
1589   // Check we are correctly initialized.
1590   CHECK_mBaseRef();
1592   NS_ENSURE_ARG_POINTER(_retval);
1594   NS_TIMELINE_START_TIMER("PR_LoadLibrary");
1596   nsCAutoString path;
1597   nsresult rv = GetPathInternal(path);
1598   if (NS_FAILED(rv))
1599     return rv;
1601 #ifdef NS_BUILD_REFCNT_LOGGING
1602   nsTraceRefcntImpl::SetActivityIsLegal(PR_FALSE);
1603 #endif
1605   *_retval = PR_LoadLibrary(path.get());
1607 #ifdef NS_BUILD_REFCNT_LOGGING
1608   nsTraceRefcntImpl::SetActivityIsLegal(PR_TRUE);
1609 #endif
1611   NS_TIMELINE_STOP_TIMER("PR_LoadLibrary");
1612   NS_TIMELINE_MARK_TIMER1("PR_LoadLibrary", path.get());
1614   if (!*_retval)
1615     return NS_ERROR_FAILURE;
1616   
1617   return NS_OK;
1620 /* readonly attribute PRInt64 diskSpaceAvailable; */
1621 NS_IMETHODIMP nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
1623   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1625   // Check we are correctly initialized.
1626   CHECK_mBaseRef();
1628   NS_ENSURE_ARG_POINTER(aDiskSpaceAvailable);
1629   
1630   FSRef fsRef;
1631   nsresult rv = GetFSRefInternal(fsRef);
1632   if (NS_FAILED(rv))
1633     return rv;
1634     
1635   OSErr err;
1636   FSCatalogInfo catalogInfo;
1637   err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoVolume, &catalogInfo,
1638                            nsnull, nsnull, nsnull);
1639   if (err != noErr)
1640     return MacErrorMapper(err);
1641   
1642   FSVolumeInfo volumeInfo;  
1643   err = ::FSGetVolumeInfo(catalogInfo.volume, 0, nsnull, kFSVolInfoSizes,
1644                           &volumeInfo, nsnull, nsnull);
1645   if (err != noErr)
1646     return MacErrorMapper(err);
1647     
1648   *aDiskSpaceAvailable = volumeInfo.freeBytes;
1649   return NS_OK;
1651   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1654 /* void appendRelativePath (in AString relativeFilePath); */
1655 NS_IMETHODIMP nsLocalFile::AppendRelativePath(const nsAString& relativeFilePath)
1657   return AppendRelativeNativePath(NS_ConvertUTF16toUTF8(relativeFilePath));
1660 /* [noscript] void appendRelativeNativePath (in ACString relativeFilePath); */
1661 NS_IMETHODIMP nsLocalFile::AppendRelativeNativePath(const nsACString& relativeFilePath)
1662 {  
1663   if (relativeFilePath.IsEmpty())
1664     return NS_OK;
1665   // No leading '/' 
1666   if (relativeFilePath.First() == '/')
1667     return NS_ERROR_FILE_UNRECOGNIZED_PATH;
1669   // Parse the nodes and call Append() for each
1670   nsACString::const_iterator nodeBegin, pathEnd;
1671   relativeFilePath.BeginReading(nodeBegin);
1672   relativeFilePath.EndReading(pathEnd);
1673   nsACString::const_iterator nodeEnd(nodeBegin);
1674   
1675   while (nodeEnd != pathEnd) {
1676     FindCharInReadable(kPathSepChar, nodeEnd, pathEnd);
1677     nsresult rv = AppendNative(Substring(nodeBegin, nodeEnd));
1678     if (NS_FAILED(rv))
1679       return rv;
1680     if (nodeEnd != pathEnd) // If there's more left in the string, inc over the '/' nodeEnd is on.
1681       ++nodeEnd;
1682     nodeBegin = nodeEnd;
1683   }
1684   return NS_OK;
1687 /* attribute ACString persistentDescriptor; */
1688 NS_IMETHODIMP nsLocalFile::GetPersistentDescriptor(nsACString& aPersistentDescriptor)
1690   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1692   FSRef fsRef;
1693   nsresult rv = GetFSRefInternal(fsRef);
1694   if (NS_FAILED(rv))
1695     return rv;
1696     
1697   AliasHandle aliasH;
1698   OSErr err = ::FSNewAlias(nsnull, &fsRef, &aliasH);
1699   if (err != noErr)
1700     return MacErrorMapper(err);
1701     
1702    PRUint32 bytes = ::GetHandleSize((Handle) aliasH);
1703    ::HLock((Handle) aliasH);
1704    // Passing nsnull for dest makes NULL-term string
1705    char* buf = PL_Base64Encode((const char*)*aliasH, bytes, nsnull);
1706    ::DisposeHandle((Handle) aliasH);
1707    NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
1708    
1709    aPersistentDescriptor = buf;
1710    PR_Free(buf);
1712   return NS_OK;
1714   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1717 NS_IMETHODIMP nsLocalFile::SetPersistentDescriptor(const nsACString& aPersistentDescriptor)
1719   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1721   if (aPersistentDescriptor.IsEmpty())
1722     return NS_ERROR_INVALID_ARG;
1724   // Support pathnames as user-supplied descriptors if they begin with '/'
1725   // or '~'.  These characters do not collide with the base64 set used for
1726   // encoding alias records.
1727   char first = aPersistentDescriptor.First();
1728   if (first == '/' || first == '~')
1729     return InitWithNativePath(aPersistentDescriptor);
1731   nsresult rv = NS_OK;
1732   
1733   PRUint32 dataSize = aPersistentDescriptor.Length();    
1734   char* decodedData = PL_Base64Decode(PromiseFlatCString(aPersistentDescriptor).get(), dataSize, nsnull);
1735   if (!decodedData) {
1736     NS_ERROR("SetPersistentDescriptor was given bad data");
1737     return NS_ERROR_FAILURE;
1738   }
1739   
1740   // Cast to an alias record and resolve.
1741   AliasRecord aliasHeader = *(AliasPtr)decodedData;
1742   PRInt32 aliasSize = GetAliasSizeFromRecord(aliasHeader);
1743   if (aliasSize > ((PRInt32)dataSize * 3) / 4) { // be paranoid about having too few data
1744     PR_Free(decodedData);
1745     return NS_ERROR_FAILURE;
1746   }
1747   
1748   // Move the now-decoded data into the Handle.
1749   // The size of the decoded data is 3/4 the size of the encoded data. See plbase64.h
1750   Handle  newHandle = nsnull;
1751   if (::PtrToHand(decodedData, &newHandle, aliasSize) != noErr)
1752     rv = NS_ERROR_OUT_OF_MEMORY;
1753   PR_Free(decodedData);
1754   if (NS_FAILED(rv))
1755     return rv;
1757   Boolean changed;
1758   FSRef resolvedFSRef;
1759   OSErr err = ::FSResolveAlias(nsnull, (AliasHandle)newHandle, &resolvedFSRef, &changed);
1760     
1761   rv = MacErrorMapper(err);
1762   DisposeHandle(newHandle);
1763   if (NS_FAILED(rv))
1764     return rv;
1766   return InitWithFSRef(&resolvedFSRef);
1768   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1771 /* void reveal (); */
1772 NS_IMETHODIMP nsLocalFile::Reveal()
1774   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1776   FSRef             fsRefToReveal;
1777   AppleEvent        aeEvent = {0, nil};
1778   AppleEvent        aeReply = {0, nil};
1779   StAEDesc          aeDirDesc, listElem, myAddressDesc, fileList;
1780   OSErr             err;
1781   ProcessSerialNumber   process;
1782     
1783   nsresult rv = GetFSRefInternal(fsRefToReveal);
1784   if (NS_FAILED(rv))
1785     return rv;
1786   
1787   err = ::FindRunningAppBySignature ('MACS', process);
1788   if (err == noErr) { 
1789     err = ::AECreateDesc(typeProcessSerialNumber, (Ptr)&process, sizeof(process), &myAddressDesc);
1790     if (err == noErr) {
1791       // Create the FinderEvent
1792       err = ::AECreateAppleEvent(kAEMiscStandards, kAEMakeObjectsVisible, &myAddressDesc,
1793                         kAutoGenerateReturnID, kAnyTransactionID, &aeEvent);   
1794       if (err == noErr) {
1795         // Create the file list
1796         err = ::AECreateList(nil, 0, false, &fileList);
1797         if (err == noErr) {
1798           FSSpec fsSpecToReveal;
1799           err = ::FSRefMakeFSSpec(&fsRefToReveal, &fsSpecToReveal);
1800           if (err == noErr) {
1801             err = ::AEPutPtr(&fileList, 0, typeFSS, &fsSpecToReveal, sizeof(FSSpec));
1802             if (err == noErr) {
1803               err = ::AEPutParamDesc(&aeEvent, keyDirectObject, &fileList);
1804               if (err == noErr) {
1805                 err = ::AESend(&aeEvent, &aeReply, kAENoReply, kAENormalPriority, kAEDefaultTimeout, nil, nil);
1806                 if (err == noErr)
1807                   ::SetFrontProcess(&process);
1808               }
1809             }
1810           }
1811         }
1812       }
1813     }
1814   }
1815     
1816   return NS_OK;
1818   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1821 /* void launch (); */
1822 NS_IMETHODIMP nsLocalFile::Launch()
1824   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1826   FSRef fsRef;
1827   nsresult rv = GetFSRefInternal(fsRef);
1828   if (NS_FAILED(rv))
1829     return rv;
1831   OSErr err = ::LSOpenFSRef(&fsRef, NULL);
1832   return MacErrorMapper(err);
1834   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1838 //*****************************************************************************
1839 //  nsLocalFile::nsILocalFileMac
1840 //*****************************************************************************
1841 #pragma mark -
1842 #pragma mark [nsILocalFileMac]
1844 /* void initWithCFURL (in CFURLRef aCFURL); */
1845 NS_IMETHODIMP nsLocalFile::InitWithCFURL(CFURLRef aCFURL)
1847   NS_ENSURE_ARG(aCFURL);
1848   
1849   SetBaseRef(aCFURL);
1850   return NS_OK;
1853 /* void initWithFSRef ([const] in FSRefPtr aFSRef); */
1854 NS_IMETHODIMP nsLocalFile::InitWithFSRef(const FSRef *aFSRef)
1856   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1858   NS_ENSURE_ARG(aFSRef);
1859   nsresult rv = NS_ERROR_FAILURE;
1860   
1861   CFURLRef newURLRef = ::CFURLCreateFromFSRef(kCFAllocatorDefault, aFSRef);
1862   if (newURLRef) {
1863     SetBaseRef(newURLRef);
1864     ::CFRelease(newURLRef);
1865     rv = NS_OK;
1866   }
1867   return rv;
1869   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1872 /* void initWithFSSpec ([const] in FSSpecPtr aFileSpec); */
1873 NS_IMETHODIMP nsLocalFile::InitWithFSSpec(const FSSpec *aFileSpec)
1875   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1877   NS_ENSURE_ARG(aFileSpec);
1878   
1879   FSRef fsRef;
1880   OSErr err = ::FSpMakeFSRef(aFileSpec, &fsRef);
1881   if (err == noErr)
1882     return InitWithFSRef(&fsRef);
1883   else if (err == fnfErr) {
1884     CInfoPBRec  pBlock;
1885     FSSpec parentDirSpec;
1886     
1887     memset(&pBlock, 0, sizeof(CInfoPBRec));
1888     parentDirSpec.name[0] = 0;
1889     pBlock.dirInfo.ioVRefNum = aFileSpec->vRefNum;
1890     pBlock.dirInfo.ioDrDirID = aFileSpec->parID;
1891     pBlock.dirInfo.ioNamePtr = (StringPtr)parentDirSpec.name;
1892     pBlock.dirInfo.ioFDirIndex = -1;        //get info on parID
1893     err = ::PBGetCatInfoSync(&pBlock);
1894     if (err != noErr)
1895       return MacErrorMapper(err);
1896     
1897     parentDirSpec.vRefNum = aFileSpec->vRefNum;
1898     parentDirSpec.parID = pBlock.dirInfo.ioDrParID;
1899     err = ::FSpMakeFSRef(&parentDirSpec, &fsRef);
1900     if (err != noErr)
1901       return MacErrorMapper(err);
1902     HFSUniStr255 unicodeName;
1903     err = ::HFSNameGetUnicodeName(aFileSpec->name, kTextEncodingUnknown, &unicodeName);
1904     if (err != noErr)
1905       return MacErrorMapper(err);
1906     nsresult rv = InitWithFSRef(&fsRef);
1907     if (NS_FAILED(rv))
1908       return rv;
1909     return Append(nsDependentString(unicodeName.unicode, unicodeName.length));  
1910   }
1911   return MacErrorMapper(err);
1913   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1916 /* void initToAppWithCreatorCode (in OSType aAppCreator); */
1917 NS_IMETHODIMP nsLocalFile::InitToAppWithCreatorCode(OSType aAppCreator)
1919   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1921   FSRef fsRef;
1922   OSErr err = ::LSFindApplicationForInfo(aAppCreator, nsnull, nsnull, &fsRef, nsnull);
1923   if (err != noErr)
1924     return MacErrorMapper(err);
1925   return InitWithFSRef(&fsRef);
1927   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1930 /* CFURLRef getCFURL (); */
1931 NS_IMETHODIMP nsLocalFile::GetCFURL(CFURLRef *_retval)
1933   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1935   NS_ENSURE_ARG_POINTER(_retval);
1936   CFURLRef whichURLRef = mFollowLinks ? mTargetRef : mBaseRef;
1937   if (whichURLRef)
1938     ::CFRetain(whichURLRef);
1939   *_retval = whichURLRef;
1940   return whichURLRef ? NS_OK : NS_ERROR_FAILURE;
1942   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1945 /* FSRef getFSRef (); */
1946 NS_IMETHODIMP nsLocalFile::GetFSRef(FSRef *_retval)
1948   NS_ENSURE_ARG_POINTER(_retval);
1949   return GetFSRefInternal(*_retval);
1952 /* FSSpec getFSSpec (); */
1953 NS_IMETHODIMP nsLocalFile::GetFSSpec(FSSpec *_retval)
1954 {  
1955   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1957   NS_ENSURE_ARG_POINTER(_retval);
1959   // Check we are correctly initialized.
1960   CHECK_mBaseRef();
1962   OSErr err;
1963   FSRef fsRef;
1964   nsresult rv = GetFSRefInternal(fsRef);
1965   if (NS_SUCCEEDED(rv)) {
1966     // If the leaf node exists, things are simple.
1967     err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNone,
1968               nsnull, nsnull, _retval, nsnull);
1969     return MacErrorMapper(err); 
1970   }
1971   else if (rv == NS_ERROR_FILE_NOT_FOUND) {
1972     // If the parent of the leaf exists, make an FSSpec from that.
1973     CFURLRef parentURLRef = ::CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, mBaseRef);
1974     if (!parentURLRef)
1975       return NS_ERROR_FAILURE;
1977     err = fnfErr;
1978     if (::CFURLGetFSRef(parentURLRef, &fsRef)) {
1979       FSCatalogInfo catalogInfo;
1980       if ((err = ::FSGetCatalogInfo(&fsRef,
1981                         kFSCatInfoVolume + kFSCatInfoNodeID + kFSCatInfoTextEncoding,
1982                         &catalogInfo, nsnull, nsnull, nsnull)) == noErr) {
1983         nsAutoString leafName;
1984         if (NS_SUCCEEDED(GetLeafName(leafName))) {
1985           Str31 hfsName;
1986           if ((err = ::UnicodeNameGetHFSName(leafName.Length(),
1987                           leafName.get(),
1988                           catalogInfo.textEncodingHint,
1989                           catalogInfo.nodeID == fsRtDirID,
1990                           hfsName)) == noErr)
1991             err = ::FSMakeFSSpec(catalogInfo.volume, catalogInfo.nodeID, hfsName, _retval);        
1992         }
1993       }
1994     }
1995     ::CFRelease(parentURLRef);
1996     rv = MacErrorMapper(err);
1997   }
1998   return rv;
2000   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2003 /* readonly attribute PRInt64 fileSizeWithResFork; */
2004 NS_IMETHODIMP nsLocalFile::GetFileSizeWithResFork(PRInt64 *aFileSizeWithResFork)
2006   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2008   NS_ENSURE_ARG_POINTER(aFileSizeWithResFork);
2009   
2010   FSRef fsRef;
2011   nsresult rv = GetFSRefInternal(fsRef);
2012   if (NS_FAILED(rv))
2013     return rv;
2014       
2015   FSCatalogInfo catalogInfo;
2016   OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoDataSizes + kFSCatInfoRsrcSizes,
2017                                  &catalogInfo, nsnull, nsnull, nsnull);
2018   if (err != noErr)
2019     return MacErrorMapper(err);
2020     
2021   *aFileSizeWithResFork = catalogInfo.dataLogicalSize + catalogInfo.rsrcLogicalSize;
2022   return NS_OK;
2024   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2027 /* attribute OSType fileType; */
2028 NS_IMETHODIMP nsLocalFile::GetFileType(OSType *aFileType)
2030   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2032   NS_ENSURE_ARG_POINTER(aFileType);
2033   
2034   FSRef fsRef;
2035   nsresult rv = GetFSRefInternal(fsRef);
2036   if (NS_FAILED(rv))
2037     return rv;
2038   
2039   FinderInfo fInfo;  
2040   OSErr err = ::FSGetFinderInfo(&fsRef, &fInfo, nsnull, nsnull);
2041   if (err != noErr)
2042     return MacErrorMapper(err);
2043   *aFileType = fInfo.file.fileType;
2044   return NS_OK;
2046   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2049 NS_IMETHODIMP nsLocalFile::SetFileType(OSType aFileType)
2051   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2053   FSRef fsRef;
2054   nsresult rv = GetFSRefInternal(fsRef);
2055   if (NS_FAILED(rv))
2056     return rv;
2057     
2058   OSErr err = ::FSChangeCreatorType(&fsRef, 0, aFileType);
2059   return MacErrorMapper(err);
2061   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2064 /* attribute OSType fileCreator; */
2065 NS_IMETHODIMP nsLocalFile::GetFileCreator(OSType *aFileCreator)
2067   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2069   NS_ENSURE_ARG_POINTER(aFileCreator);
2070   
2071   FSRef fsRef;
2072   nsresult rv = GetFSRefInternal(fsRef);
2073   if (NS_FAILED(rv))
2074     return rv;
2075   
2076   FinderInfo fInfo;  
2077   OSErr err = ::FSGetFinderInfo(&fsRef, &fInfo, nsnull, nsnull);
2078   if (err != noErr)
2079     return MacErrorMapper(err);
2080   *aFileCreator = fInfo.file.fileCreator;
2081   return NS_OK;
2083   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2086 NS_IMETHODIMP nsLocalFile::SetFileCreator(OSType aFileCreator)
2088   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2090   FSRef fsRef;
2091   nsresult rv = GetFSRefInternal(fsRef);
2092   if (NS_FAILED(rv))
2093     return rv;
2094     
2095   OSErr err = ::FSChangeCreatorType(&fsRef, aFileCreator, 0);
2096   return MacErrorMapper(err);
2098   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2101 /* void setFileTypeAndCreatorFromMIMEType (in string aMIMEType); */
2102 NS_IMETHODIMP nsLocalFile::SetFileTypeAndCreatorFromMIMEType(const char *aMIMEType)
2104   // XXX - This should be cut from the API. Would create an evil dependency.
2105   NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
2106   return NS_ERROR_NOT_IMPLEMENTED;
2109 /* void setFileTypeAndCreatorFromExtension (in string aExtension); */
2110 NS_IMETHODIMP nsLocalFile::SetFileTypeAndCreatorFromExtension(const char *aExtension)
2112   // XXX - This should be cut from the API. Would create an evil dependency.
2113   NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
2114   return NS_ERROR_NOT_IMPLEMENTED;
2117 /* void launchWithDoc (in nsILocalFile aDocToLoad, in boolean aLaunchInBackground); */
2118 NS_IMETHODIMP nsLocalFile::LaunchWithDoc(nsILocalFile *aDocToLoad, PRBool aLaunchInBackground)
2120   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2122   PRBool isExecutable;
2123   nsresult rv = IsExecutable(&isExecutable);
2124   if (NS_FAILED(rv))
2125     return rv;
2126   if (!isExecutable)
2127     return NS_ERROR_FILE_EXECUTION_FAILED;
2129   FSRef appFSRef, docFSRef;
2130   rv = GetFSRefInternal(appFSRef);
2131   if (NS_FAILED(rv))
2132     return rv;
2134   if (aDocToLoad) {
2135     nsCOMPtr<nsILocalFileMac> macDoc = do_QueryInterface(aDocToLoad);
2136     rv = macDoc->GetFSRef(&docFSRef);
2137     if (NS_FAILED(rv))
2138       return rv;
2139   }
2140   
2141   LSLaunchFlags       theLaunchFlags = kLSLaunchDefaults;
2142   LSLaunchFSRefSpec   thelaunchSpec;
2144   if (aLaunchInBackground)
2145     theLaunchFlags |= kLSLaunchDontSwitch;
2146   memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
2148   thelaunchSpec.appRef = &appFSRef;
2149   if (aDocToLoad) {
2150     thelaunchSpec.numDocs = 1;
2151     thelaunchSpec.itemRefs = &docFSRef;
2152   }
2153   thelaunchSpec.launchFlags = theLaunchFlags;
2155   OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, NULL);
2156   if (err != noErr)
2157     return MacErrorMapper(err);
2159   return NS_OK;
2161   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2164 /* void openDocWithApp (in nsILocalFile aAppToOpenWith, in boolean aLaunchInBackground); */
2165 NS_IMETHODIMP nsLocalFile::OpenDocWithApp(nsILocalFile *aAppToOpenWith, PRBool aLaunchInBackground)
2167   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2169   nsresult rv;
2170   OSErr err;
2172   FSRef docFSRef, appFSRef;
2173   rv = GetFSRefInternal(docFSRef);
2174   if (NS_FAILED(rv))
2175     return rv;
2177   if (aAppToOpenWith) {
2178     nsCOMPtr<nsILocalFileMac> appFileMac = do_QueryInterface(aAppToOpenWith, &rv);
2179     if (!appFileMac)
2180       return rv;
2182     PRBool isExecutable;
2183     rv = appFileMac->IsExecutable(&isExecutable);
2184     if (NS_FAILED(rv))
2185       return rv;
2186     if (!isExecutable)
2187       return NS_ERROR_FILE_EXECUTION_FAILED;
2188     
2189     rv = appFileMac->GetFSRef(&appFSRef);
2190     if (NS_FAILED(rv))
2191       return rv;
2192   }
2193   else {
2194     OSType  fileCreator;
2195     rv = GetFileCreator(&fileCreator);
2196     if (NS_FAILED(rv))
2197       return rv;
2199     err = ::LSFindApplicationForInfo(fileCreator, nsnull, nsnull, &appFSRef, nsnull);
2200     if (err != noErr)
2201       return MacErrorMapper(err);
2202   }
2203   
2204   LSLaunchFlags       theLaunchFlags = kLSLaunchDefaults;
2205   LSLaunchFSRefSpec   thelaunchSpec;
2207   if (aLaunchInBackground)
2208   theLaunchFlags |= kLSLaunchDontSwitch;
2209   memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
2211   thelaunchSpec.appRef = &appFSRef;
2212   thelaunchSpec.numDocs = 1;
2213   thelaunchSpec.itemRefs = &docFSRef;
2214   thelaunchSpec.launchFlags = theLaunchFlags;
2216   err = ::LSOpenFromRefSpec(&thelaunchSpec, NULL);
2217   if (err != noErr)
2218     return MacErrorMapper(err);
2220   return NS_OK;
2222   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2225 /* boolean isPackage (); */
2226 NS_IMETHODIMP nsLocalFile::IsPackage(PRBool *_retval)
2228   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2230   NS_ENSURE_ARG(_retval);
2231   *_retval = PR_FALSE;
2232   
2233   FSRef fsRef;
2234   nsresult rv = GetFSRefInternal(fsRef);
2235   if (NS_FAILED(rv))
2236     return rv;
2238   FSCatalogInfo catalogInfo;
2239   OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags + kFSCatInfoFinderInfo,
2240                                  &catalogInfo, nsnull, nsnull, nsnull);
2241   if (err != noErr)
2242     return MacErrorMapper(err);
2243   if ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0) {
2244     FileInfo *fInfoPtr = (FileInfo *)(catalogInfo.finderInfo);
2245     if ((fInfoPtr->finderFlags & kHasBundle) != 0) {
2246       *_retval = PR_TRUE;
2247     }
2248     else {
2249      // Folders ending with ".app" are also considered to
2250      // be packages, even if the top-level folder doesn't have bundle set
2251       nsCAutoString name;
2252       if (NS_SUCCEEDED(rv = GetNativeLeafName(name))) {
2253         const char *extPtr = strrchr(name.get(), '.');
2254         if (extPtr) {
2255           if ((nsCRT::strcasecmp(extPtr, ".app") == 0))
2256             *_retval = PR_TRUE;
2257         }
2258       }
2259     }
2260   }
2261   return NS_OK;
2263   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2266 NS_IMETHODIMP
2267 nsLocalFile::GetBundleDisplayName(nsAString& outBundleName)
2269   PRBool isPackage = PR_FALSE;
2270   nsresult rv = IsPackage(&isPackage);
2271   if (NS_FAILED(rv) || !isPackage)
2272     return NS_ERROR_FAILURE;
2273   
2274   nsAutoString name;
2275   rv = GetLeafName(name);
2276   if (NS_FAILED(rv))
2277     return rv;
2278   
2279   PRInt32 length = name.Length();
2280   if (Substring(name, length - 4, length).EqualsLiteral(".app")) {
2281     // 4 characters in ".app"
2282     outBundleName = Substring(name, 0, length - 4);
2283   }
2284   else
2285     outBundleName = name;
2286   
2287   return NS_OK;
2290 NS_IMETHODIMP
2291 nsLocalFile::GetBundleIdentifier(nsACString& outBundleIdentifier)
2293   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2295   nsresult rv = NS_ERROR_FAILURE;
2297   CFURLRef urlRef;
2298   if (NS_SUCCEEDED(GetCFURL(&urlRef))) {
2299     CFBundleRef bundle = ::CFBundleCreate(NULL, urlRef);
2300     if (bundle) {
2301       CFStringRef bundleIdentifier = ::CFBundleGetIdentifier(bundle);
2302       if (bundleIdentifier)
2303         rv = CFStringReftoUTF8(bundleIdentifier, outBundleIdentifier);
2305       ::CFRelease(bundle);
2306     }
2307     ::CFRelease(urlRef);
2308   }
2310   return rv;
2312   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2316 //*****************************************************************************
2317 //  nsLocalFile Methods
2318 //*****************************************************************************
2319 #pragma mark -
2320 #pragma mark [Protected Methods]
2322 nsresult nsLocalFile::SetBaseRef(CFURLRef aCFURLRef)
2324   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2326   NS_ENSURE_ARG(aCFURLRef);
2327   
2328   ::CFRetain(aCFURLRef);
2329   if (mBaseRef)
2330     ::CFRelease(mBaseRef);
2331   mBaseRef = aCFURLRef;
2333   mFollowLinksDirty = PR_TRUE;  
2334   UpdateTargetRef();
2335   mCachedFSRefValid = PR_FALSE;
2336   return NS_OK;
2338   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2341 nsresult nsLocalFile::UpdateTargetRef()
2343   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2345   // Check we are correctly initialized.
2346   CHECK_mBaseRef();
2347   
2348   if (mFollowLinksDirty) {
2349     if (mTargetRef) {
2350       ::CFRelease(mTargetRef);
2351       mTargetRef = nsnull;
2352     }
2353     if (mFollowLinks) {
2354       mTargetRef = mBaseRef;
2355       ::CFRetain(mTargetRef);
2357       FSRef fsRef;
2358       if (::CFURLGetFSRef(mBaseRef, &fsRef)) {
2359         Boolean targetIsFolder, wasAliased;
2360         if (FSResolveAliasFile(&fsRef, true /*resolveAliasChains*/, 
2361             &targetIsFolder, &wasAliased) == noErr && wasAliased) {
2362           ::CFRelease(mTargetRef);
2363           mTargetRef = CFURLCreateFromFSRef(NULL, &fsRef);
2364           if (!mTargetRef)
2365             return NS_ERROR_FAILURE;
2366         }
2367       }
2368       mFollowLinksDirty = PR_FALSE;
2369     }
2370   }
2371   return NS_OK;
2373   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2376 nsresult nsLocalFile::GetFSRefInternal(FSRef& aFSRef, PRBool bForceUpdateCache)
2378   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2380   if (bForceUpdateCache || !mCachedFSRefValid) {
2381     mCachedFSRefValid = PR_FALSE;
2382     CFURLRef whichURLRef = mFollowLinks ? mTargetRef : mBaseRef;
2383     NS_ENSURE_TRUE(whichURLRef, NS_ERROR_NULL_POINTER);
2384     if (::CFURLGetFSRef(whichURLRef, &mCachedFSRef))
2385       mCachedFSRefValid = PR_TRUE;
2386   }
2387   if (mCachedFSRefValid) {
2388     aFSRef = mCachedFSRef;
2389     return NS_OK;
2390   }
2391   // CFURLGetFSRef only returns a Boolean for success,
2392   // so we have to assume what the error was. This is
2393   // the only probable cause.
2394   return NS_ERROR_FILE_NOT_FOUND;
2396   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2399 nsresult nsLocalFile::GetPathInternal(nsACString& path)
2401   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2403   nsresult rv = NS_ERROR_FAILURE;
2404   
2405   CFURLRef whichURLRef = mFollowLinks ? mTargetRef : mBaseRef;
2406   NS_ENSURE_TRUE(whichURLRef, NS_ERROR_NULL_POINTER);
2407    
2408   CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(whichURLRef, kCFURLPOSIXPathStyle);
2409   if (pathStrRef) {
2410     rv = CFStringReftoUTF8(pathStrRef, path);
2411     ::CFRelease(pathStrRef);
2412   }
2413   return rv;
2415   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2418 nsresult nsLocalFile::CopyInternal(nsIFile* aParentDir,
2419                                    const nsAString& newName,
2420                                    PRBool followLinks)
2422   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2424   // Check we are correctly initialized.
2425   CHECK_mBaseRef();
2427   StFollowLinksState srcFollowState(*this, followLinks);
2429   nsresult rv;
2430   OSErr err;
2431   FSRef srcFSRef, newFSRef;
2433   rv = GetFSRefInternal(srcFSRef);
2434   if (NS_FAILED(rv))
2435     return rv;
2437   nsCOMPtr<nsIFile> newParentDir = aParentDir;
2439   if (!newParentDir) {
2440     if (newName.IsEmpty())
2441       return NS_ERROR_INVALID_ARG;
2442     rv = GetParent(getter_AddRefs(newParentDir));
2443     if (NS_FAILED(rv))
2444       return rv;    
2445   }
2447   // If newParentDir does not exist, create it
2448   PRBool exists;
2449   rv = newParentDir->Exists(&exists);
2450   if (NS_FAILED(rv))
2451     return rv;
2452   if (!exists) {
2453     rv = newParentDir->Create(nsIFile::DIRECTORY_TYPE, 0777);
2454     if (NS_FAILED(rv))
2455       return rv;
2456   }
2458   FSRef destFSRef;
2459   nsCOMPtr<nsILocalFileMac> newParentDirMac(do_QueryInterface(newParentDir));
2460   if (!newParentDirMac)
2461     return NS_ERROR_NO_INTERFACE;
2462   rv = newParentDirMac->GetFSRef(&destFSRef);
2463   if (NS_FAILED(rv))
2464     return rv;
2466   err =
2467    ::FSCopyObject(&srcFSRef, &destFSRef, newName.Length(),
2468                   newName.Length() ? PromiseFlatString(newName).get() : NULL,
2469                   0, kFSCatInfoNone, false, false, NULL, NULL, &newFSRef);
2471   return MacErrorMapper(err);
2473   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2476 const PRInt64 kMillisecsPerSec = 1000LL;
2477 const PRInt64 kUTCDateTimeFractionDivisor = 65535LL;
2479 PRInt64 nsLocalFile::HFSPlustoNSPRTime(const UTCDateTime& utcTime)
2481   // Start with seconds since Jan. 1, 1904 GMT
2482   PRInt64 result = ((PRInt64)utcTime.highSeconds << 32) + (PRInt64)utcTime.lowSeconds; 
2483   // Subtract to convert to NSPR epoch of 1970
2484   result -= kJanuaryFirst1970Seconds;
2485   // Convert to millisecs
2486   result *= kMillisecsPerSec;
2487   // Convert the fraction to millisecs and add it
2488   result += ((PRInt64)utcTime.fraction * kMillisecsPerSec) / kUTCDateTimeFractionDivisor;
2490   return result;
2493 void nsLocalFile::NSPRtoHFSPlusTime(PRInt64 nsprTime, UTCDateTime& utcTime)
2495   PRInt64 fraction = nsprTime % kMillisecsPerSec;
2496   PRInt64 seconds = (nsprTime / kMillisecsPerSec) + kJanuaryFirst1970Seconds;
2497   utcTime.highSeconds = (UInt16)((PRUint64)seconds >> 32);
2498   utcTime.lowSeconds = (UInt32)seconds;
2499   utcTime.fraction = (UInt16)((fraction * kUTCDateTimeFractionDivisor) / kMillisecsPerSec);
2502 nsresult nsLocalFile::CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr)
2504   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2506   nsresult rv = NS_ERROR_FAILURE;
2507   CFIndex usedBufLen, inStrLen = ::CFStringGetLength(aInStrRef);
2508   CFIndex charsConverted = ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen),
2509                               kCFStringEncodingUTF8, 0, PR_FALSE, nsnull, 0, &usedBufLen);
2510   if (charsConverted == inStrLen) {
2511     aOutStr.SetLength(usedBufLen);
2512     if (aOutStr.Length() != (unsigned int)usedBufLen)
2513       return NS_ERROR_OUT_OF_MEMORY;
2514     UInt8 *buffer = (UInt8*) aOutStr.BeginWriting();
2516     ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen),
2517                        kCFStringEncodingUTF8, 0, false, buffer, usedBufLen, &usedBufLen);
2518     rv = NS_OK;
2519   }
2520   return rv;
2522   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2525 // nsIHashable
2527 NS_IMETHODIMP
2528 nsLocalFile::Equals(nsIHashable* aOther, PRBool *aResult)
2530     return EqualsInternal(aOther, PR_FALSE, aResult);
2533 NS_IMETHODIMP
2534 nsLocalFile::GetHashCode(PRUint32 *aResult)
2536     NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2538     CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(mBaseRef, kCFURLPOSIXPathStyle);
2539     nsCAutoString path;
2540     CFStringReftoUTF8(pathStrRef, path);
2541     ::CFRelease(pathStrRef);
2542     *aResult = HashString(path);
2543     return NS_OK;
2545     NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2548 //*****************************************************************************
2549 //  Global Functions
2550 //*****************************************************************************
2551 #pragma mark -
2552 #pragma mark [Global Functions]
2554 void nsLocalFile::GlobalInit()
2558 void nsLocalFile::GlobalShutdown()
2562 nsresult NS_NewLocalFile(const nsAString& path, PRBool followLinks, nsILocalFile* *result)
2564     nsLocalFile* file = new nsLocalFile;
2565     if (file == nsnull)
2566         return NS_ERROR_OUT_OF_MEMORY;
2567     NS_ADDREF(file);
2569     file->SetFollowLinks(followLinks);
2571     if (!path.IsEmpty()) {
2572         nsresult rv = file->InitWithPath(path);
2573         if (NS_FAILED(rv)) {
2574             NS_RELEASE(file);
2575             return rv;
2576         }
2577     }
2578     *result = file;
2579     return NS_OK;
2582 nsresult NS_NewNativeLocalFile(const nsACString& path, PRBool followLinks, nsILocalFile **result)
2584     return NS_NewLocalFile(NS_ConvertUTF8toUTF16(path), followLinks, result);
2587 nsresult NS_NewLocalFileWithFSSpec(const FSSpec* inSpec, PRBool followLinks, nsILocalFileMac **result)
2589     nsLocalFile* file = new nsLocalFile();
2590     if (file == nsnull)
2591         return NS_ERROR_OUT_OF_MEMORY;
2592     NS_ADDREF(file);
2594     file->SetFollowLinks(followLinks);
2596     nsresult rv = file->InitWithFSSpec(inSpec);
2597     if (NS_FAILED(rv)) {
2598         NS_RELEASE(file);
2599         return rv;
2600     }
2601     *result = file;
2602     return NS_OK;
2605 nsresult NS_NewLocalFileWithFSRef(const FSRef* aFSRef, PRBool aFollowLinks, nsILocalFileMac** result)
2607     nsLocalFile* file = new nsLocalFile();
2608     if (file == nsnull)
2609         return NS_ERROR_OUT_OF_MEMORY;
2610     NS_ADDREF(file);
2612     file->SetFollowLinks(aFollowLinks);
2614     nsresult rv = file->InitWithFSRef(aFSRef);
2615     if (NS_FAILED(rv)) {
2616         NS_RELEASE(file);
2617         return rv;
2618     }
2619     *result = file;
2620     return NS_OK;
2623 //*****************************************************************************
2624 //  Static Functions
2625 //*****************************************************************************
2627 static nsresult MacErrorMapper(OSErr inErr)
2629     nsresult outErr;
2630     
2631     switch (inErr)
2632     {
2633         case noErr:
2634             outErr = NS_OK;
2635             break;
2637         case fnfErr:
2638         case afpObjectNotFound:
2639         case afpDirNotFound:
2640             outErr = NS_ERROR_FILE_NOT_FOUND;
2641             break;
2643         case dupFNErr:
2644         case afpObjectExists:
2645             outErr = NS_ERROR_FILE_ALREADY_EXISTS;
2646             break;
2647         
2648         case dskFulErr:
2649         case afpDiskFull:
2650             outErr = NS_ERROR_FILE_DISK_FULL;
2651             break;
2652         
2653         case fLckdErr:
2654         case afpVolLocked:
2655             outErr = NS_ERROR_FILE_IS_LOCKED;
2656             break;
2657         
2658         case afpAccessDenied:
2659             outErr = NS_ERROR_FILE_ACCESS_DENIED;
2660             break;
2662         case afpDirNotEmpty:
2663             outErr = NS_ERROR_FILE_DIR_NOT_EMPTY;
2664             break;
2665             
2666         // Can't find good map for some
2667         case bdNamErr:
2668             outErr = NS_ERROR_FAILURE;
2669             break;
2671         default:    
2672             outErr = NS_ERROR_FAILURE;
2673             break;
2674     }
2675     return outErr;
2678 static OSErr FindRunningAppBySignature(OSType aAppSig, ProcessSerialNumber& outPsn)
2680   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
2682   ProcessInfoRec info;
2683   OSErr err = noErr;
2684   
2685   outPsn.highLongOfPSN = 0;
2686   outPsn.lowLongOfPSN  = kNoProcess;
2687   
2688   while (PR_TRUE)
2689   {
2690     err = ::GetNextProcess(&outPsn);
2691     if (err == procNotFound)
2692       break;
2693     if (err != noErr)
2694       return err;
2695     info.processInfoLength = sizeof(ProcessInfoRec);
2696     info.processName = nil;
2697     info.processAppSpec = nil;
2698     err = ::GetProcessInformation(&outPsn, &info);
2699     if (err != noErr)
2700       return err;
2701     
2702     if (info.processSignature == aAppSig)
2703       return noErr;
2704   }
2705   return procNotFound;
2707   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(procNotFound);
2710 // Convert a UTF-8 string to a UTF-16 string while normalizing to
2711 // Normalization Form C (composed Unicode). We need this because
2712 // Mac OS X file system uses NFD (Normalization Form D : decomposed Unicode)
2713 // while most other OS', server-side programs usually expect NFC.
2715 typedef void (*UnicodeNormalizer) (CFMutableStringRef, CFStringNormalizationForm);
2716 static void CopyUTF8toUTF16NFC(const nsACString& aSrc, nsAString& aResult)
2718     NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2720     static PRBool sChecked = PR_FALSE;
2721     static UnicodeNormalizer sUnicodeNormalizer = NULL;
2723     // CFStringNormalize was not introduced until Mac OS 10.2
2724     if (!sChecked) {
2725         CFBundleRef carbonBundle =
2726             CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Carbon"));
2727         if (carbonBundle)
2728             sUnicodeNormalizer = (UnicodeNormalizer)
2729                 ::CFBundleGetFunctionPointerForName(carbonBundle,
2730                                                     CFSTR("CFStringNormalize"));
2731         sChecked = PR_TRUE;
2732     }
2734     if (!sUnicodeNormalizer) {  // OS X 10.2 or earlier
2735         CopyUTF8toUTF16(aSrc, aResult);
2736         return;  
2737     }
2739     const nsAFlatCString &inFlatSrc = PromiseFlatCString(aSrc);
2741     // The number of 16bit code units in a UTF-16 string will never be
2742     // larger than the number of bytes in the corresponding UTF-8 string.
2743     CFMutableStringRef inStr =
2744         ::CFStringCreateMutable(NULL, inFlatSrc.Length());
2746     if (!inStr) {
2747         CopyUTF8toUTF16(aSrc, aResult);
2748         return;  
2749     }
2750      
2751     ::CFStringAppendCString(inStr, inFlatSrc.get(), kCFStringEncodingUTF8); 
2753     sUnicodeNormalizer(inStr, kCFStringNormalizationFormC);
2755     CFIndex length = CFStringGetLength(inStr);
2756     const UniChar* chars = CFStringGetCharactersPtr(inStr);
2758     if (chars) 
2759         aResult.Assign(chars, length);
2760     else {
2761         nsAutoTArray<UniChar, FILENAME_BUFFER_SIZE> buffer;
2762         if (!buffer.SetLength(length))
2763             CopyUTF8toUTF16(aSrc, aResult);
2764         else {
2765             CFStringGetCharacters(inStr, CFRangeMake(0, length), buffer.Elements());
2766             aResult.Assign(buffer.Elements(), length);
2767         }
2768     }
2769     CFRelease(inStr);
2771     NS_OBJC_END_TRY_ABORT_BLOCK;