VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/xpcom/io/nsLocalFileOSX.cpp@ 95259

Last change on this file since 95259 was 5692, checked in by vboxsync, 17 years ago

Updated to current seamonkey head.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 71.0 KB
Line 
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 ***** */
41
42#include "nsLocalFile.h"
43#include "nsDirectoryServiceDefs.h"
44
45#include "nsString.h"
46#include "nsReadableUtils.h"
47#include "nsIDirectoryEnumerator.h"
48#include "nsISimpleEnumerator.h"
49#include "nsITimelineService.h"
50#include "nsVoidArray.h"
51
52#include "plbase64.h"
53#include "prmem.h"
54#include "nsCRT.h"
55#include "nsHashKeys.h"
56
57#include "MoreFilesX.h"
58#include "FSCopyObject.h"
59#include "nsAutoBuffer.h"
60#include "nsTraceRefcntImpl.h"
61
62// Mac Includes
63#include <Carbon/Carbon.h>
64
65// Unix Includes
66#include <unistd.h>
67#include <sys/stat.h>
68#include <stdlib.h>
69
70#if !defined(MAC_OS_X_VERSION_10_4) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4
71#define GetAliasSizeFromRecord(aliasRecord) aliasRecord.aliasSize
72#else
73#define GetAliasSizeFromRecord(aliasRecord) GetAliasSizeFromPtr(&aliasRecord)
74#endif
75
76#define CHECK_mBaseRef() \
77 PR_BEGIN_MACRO \
78 if (!mBaseRef) \
79 return NS_ERROR_NOT_INITIALIZED; \
80 PR_END_MACRO
81
82//*****************************************************************************
83// Static Function Prototypes
84//*****************************************************************************
85
86static nsresult MacErrorMapper(OSErr inErr);
87static OSErr FindRunningAppBySignature(OSType aAppSig, ProcessSerialNumber& outPsn);
88static void CopyUTF8toUTF16NFC(const nsACString& aSrc, nsAString& aResult);
89
90//*****************************************************************************
91// Local Helper Classes
92//*****************************************************************************
93
94#pragma mark -
95#pragma mark [FSRef operator==]
96
97bool operator==(const FSRef& lhs, const FSRef& rhs)
98{
99 return (::FSCompareFSRefs(&lhs, &rhs) == noErr);
100}
101
102#pragma mark -
103#pragma mark [StFollowLinksState]
104
105class StFollowLinksState
106{
107 public:
108 StFollowLinksState(nsLocalFile& aFile) :
109 mFile(aFile)
110 {
111 mFile.GetFollowLinks(&mSavedState);
112 }
113
114 StFollowLinksState(nsLocalFile& aFile, PRBool followLinksState) :
115 mFile(aFile)
116 {
117 mFile.GetFollowLinks(&mSavedState);
118 mFile.SetFollowLinks(followLinksState);
119 }
120
121 ~StFollowLinksState()
122 {
123 mFile.SetFollowLinks(mSavedState);
124 }
125
126 private:
127 nsLocalFile& mFile;
128 PRBool mSavedState;
129};
130
131#pragma mark -
132#pragma mark [nsDirEnumerator]
133
134class nsDirEnumerator : public nsISimpleEnumerator,
135 public nsIDirectoryEnumerator
136{
137 public:
138
139 NS_DECL_ISUPPORTS
140
141 nsDirEnumerator() :
142 mIterator(nsnull),
143 mFSRefsArray(nsnull),
144 mArrayCnt(0), mArrayIndex(0)
145 {
146 }
147
148 nsresult Init(nsILocalFileMac* parent)
149 {
150 NS_ENSURE_ARG(parent);
151
152 OSErr err;
153 nsresult rv;
154 FSRef parentRef;
155
156 rv = parent->GetFSRef(&parentRef);
157 if (NS_FAILED(rv))
158 return rv;
159
160 mFSRefsArray = (FSRef *)nsMemory::Alloc(sizeof(FSRef)
161 * kRequestCountPerIteration);
162 if (!mFSRefsArray)
163 return NS_ERROR_OUT_OF_MEMORY;
164
165 err = ::FSOpenIterator(&parentRef, kFSIterateFlat, &mIterator);
166 if (err != noErr)
167 return MacErrorMapper(err);
168
169 return NS_OK;
170 }
171
172 NS_IMETHOD HasMoreElements(PRBool *result)
173 {
174 if (mNext == nsnull) {
175 if (mArrayIndex >= mArrayCnt) {
176 ItemCount actualCnt;
177 OSErr err = ::FSGetCatalogInfoBulk(mIterator,
178 kRequestCountPerIteration,
179 &actualCnt,
180 nsnull,
181 kFSCatInfoNone,
182 nsnull,
183 mFSRefsArray,
184 nsnull,
185 nsnull);
186
187 if (err == noErr || err == errFSNoMoreItems) {
188 mArrayCnt = actualCnt;
189 mArrayIndex = 0;
190 }
191 }
192 if (mArrayIndex < mArrayCnt) {
193 nsLocalFile *newFile = new nsLocalFile;
194 if (!newFile)
195 return NS_ERROR_OUT_OF_MEMORY;
196 FSRef fsRef = mFSRefsArray[mArrayIndex];
197 if (NS_FAILED(newFile->InitWithFSRef(&fsRef)))
198 return NS_ERROR_FAILURE;
199 mArrayIndex++;
200 mNext = newFile;
201 }
202 }
203 *result = mNext != nsnull;
204 if (!*result)
205 Close();
206 return NS_OK;
207 }
208
209 NS_IMETHOD GetNext(nsISupports **result)
210 {
211 NS_ENSURE_ARG_POINTER(result);
212 *result = nsnull;
213
214 nsresult rv;
215 PRBool hasMore;
216 rv = HasMoreElements(&hasMore);
217 if (NS_FAILED(rv)) return rv;
218
219 *result = mNext; // might return nsnull
220 NS_IF_ADDREF(*result);
221
222 mNext = nsnull;
223 return NS_OK;
224 }
225
226 NS_IMETHOD GetNextFile(nsIFile **result)
227 {
228 *result = nsnull;
229 PRBool hasMore = PR_FALSE;
230 nsresult rv = HasMoreElements(&hasMore);
231 if (NS_FAILED(rv) || !hasMore)
232 return rv;
233 *result = mNext;
234 NS_IF_ADDREF(*result);
235 mNext = nsnull;
236 return NS_OK;
237 }
238
239 NS_IMETHOD Close()
240 {
241 if (mIterator) {
242 ::FSCloseIterator(mIterator);
243 mIterator = nsnull;
244 }
245 if (mFSRefsArray) {
246 nsMemory::Free(mFSRefsArray);
247 mFSRefsArray = nsnull;
248 }
249 return NS_OK;
250 }
251
252 private:
253 ~nsDirEnumerator()
254 {
255 Close();
256 }
257
258 protected:
259 // According to Apple doc, request the number of objects
260 // per call that will fit in 4 VM pages.
261 enum {
262 kRequestCountPerIteration = ((4096 * 4) / sizeof(FSRef))
263 };
264
265 nsCOMPtr<nsILocalFileMac> mNext;
266
267 FSIterator mIterator;
268 FSRef *mFSRefsArray;
269 PRInt32 mArrayCnt, mArrayIndex;
270};
271
272NS_IMPL_ISUPPORTS2(nsDirEnumerator, nsISimpleEnumerator, nsIDirectoryEnumerator)
273
274#pragma mark -
275#pragma mark [StAEDesc]
276
277class StAEDesc: public AEDesc
278{
279public:
280 StAEDesc()
281 {
282 descriptorType = typeNull;
283 dataHandle = nil;
284 }
285
286 ~StAEDesc()
287 {
288 ::AEDisposeDesc(this);
289 }
290};
291
292#define FILENAME_BUFFER_SIZE 512
293
294//*****************************************************************************
295// nsLocalFile
296//*****************************************************************************
297
298const char nsLocalFile::kPathSepChar = '/';
299const PRUnichar nsLocalFile::kPathSepUnichar = '/';
300
301// The HFS+ epoch is Jan. 1, 1904 GMT - differs from HFS in which times were local
302// The NSPR epoch is Jan. 1, 1970 GMT
303// 2082844800 is the difference in seconds between those dates
304const PRInt64 nsLocalFile::kJanuaryFirst1970Seconds = 2082844800LL;
305
306#pragma mark -
307#pragma mark [CTORs/DTOR]
308
309nsLocalFile::nsLocalFile() :
310 mBaseRef(nsnull),
311 mTargetRef(nsnull),
312 mCachedFSRefValid(PR_FALSE),
313 mFollowLinks(PR_TRUE),
314 mFollowLinksDirty(PR_TRUE)
315{
316}
317
318nsLocalFile::nsLocalFile(const nsLocalFile& src) :
319 mBaseRef(src.mBaseRef),
320 mTargetRef(src.mTargetRef),
321 mCachedFSRef(src.mCachedFSRef),
322 mCachedFSRefValid(src.mCachedFSRefValid),
323 mFollowLinks(src.mFollowLinks),
324 mFollowLinksDirty(src.mFollowLinksDirty)
325{
326 // A CFURLRef is immutable so no need to copy, just retain.
327 if (mBaseRef)
328 ::CFRetain(mBaseRef);
329 if (mTargetRef)
330 ::CFRetain(mTargetRef);
331}
332
333nsLocalFile::~nsLocalFile()
334{
335 if (mBaseRef)
336 ::CFRelease(mBaseRef);
337 if (mTargetRef)
338 ::CFRelease(mTargetRef);
339}
340
341
342//*****************************************************************************
343// nsLocalFile::nsISupports
344//*****************************************************************************
345#pragma mark -
346#pragma mark [nsISupports]
347
348NS_IMPL_THREADSAFE_ISUPPORTS4(nsLocalFile,
349 nsILocalFileMac,
350 nsILocalFile,
351 nsIFile,
352 nsIHashable)
353
354NS_METHOD nsLocalFile::nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
355{
356 NS_ENSURE_ARG_POINTER(aInstancePtr);
357 NS_ENSURE_NO_AGGREGATION(outer);
358
359 nsLocalFile* inst = new nsLocalFile();
360 if (inst == NULL)
361 return NS_ERROR_OUT_OF_MEMORY;
362
363 nsresult rv = inst->QueryInterface(aIID, aInstancePtr);
364 if (NS_FAILED(rv))
365 {
366 delete inst;
367 return rv;
368 }
369 return NS_OK;
370}
371
372
373//*****************************************************************************
374// nsLocalFile::nsIFile
375//*****************************************************************************
376#pragma mark -
377#pragma mark [nsIFile]
378
379/* void append (in AString node); */
380NS_IMETHODIMP nsLocalFile::Append(const nsAString& aNode)
381{
382 return AppendNative(NS_ConvertUTF16toUTF8(aNode));
383}
384
385/* [noscript] void appendNative (in ACString node); */
386NS_IMETHODIMP nsLocalFile::AppendNative(const nsACString& aNode)
387{
388 // Check we are correctly initialized.
389 CHECK_mBaseRef();
390
391 nsACString::const_iterator start, end;
392 aNode.BeginReading(start);
393 aNode.EndReading(end);
394 if (FindCharInReadable(kPathSepChar, start, end))
395 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
396
397 CFStringRef nodeStrRef = ::CFStringCreateWithCString(kCFAllocatorDefault,
398 PromiseFlatCString(aNode).get(),
399 kCFStringEncodingUTF8);
400 if (nodeStrRef) {
401 CFURLRef newRef = ::CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault,
402 mBaseRef, nodeStrRef, PR_FALSE);
403 ::CFRelease(nodeStrRef);
404 if (newRef) {
405 SetBaseRef(newRef);
406 ::CFRelease(newRef);
407 return NS_OK;
408 }
409 }
410 return NS_ERROR_FAILURE;
411}
412
413/* void normalize (); */
414NS_IMETHODIMP nsLocalFile::Normalize()
415{
416 // Check we are correctly initialized.
417 CHECK_mBaseRef();
418
419 // CFURL doesn't doesn't seem to resolve paths containing relative
420 // components, so we'll nick the stdlib code from nsLocalFileUnix
421 UInt8 path[PATH_MAX] = "";
422 Boolean success;
423 success = ::CFURLGetFileSystemRepresentation(mBaseRef, true, path, PATH_MAX);
424 if (!success)
425 return NS_ERROR_FAILURE;
426
427 char resolved_path[PATH_MAX] = "";
428 char *resolved_path_ptr = nsnull;
429 resolved_path_ptr = realpath((char*)path, resolved_path);
430
431 // if there is an error, the return is null.
432 if (!resolved_path_ptr)
433 return NSRESULT_FOR_ERRNO();
434
435 // Need to know whether we're a directory to create a new CFURLRef
436 PRBool isDirectory;
437 nsresult rv = IsDirectory(&isDirectory);
438 NS_ENSURE_SUCCESS(rv, rv);
439
440 rv = NS_ERROR_FAILURE;
441 CFStringRef pathStrRef =
442 ::CFStringCreateWithCString(kCFAllocatorDefault,
443 resolved_path,
444 kCFStringEncodingUTF8);
445 if (pathStrRef) {
446 CFURLRef newURLRef =
447 ::CFURLCreateWithFileSystemPath(kCFAllocatorDefault, pathStrRef,
448 kCFURLPOSIXPathStyle, isDirectory);
449 if (newURLRef) {
450 SetBaseRef(newURLRef);
451 ::CFRelease(newURLRef);
452 rv = NS_OK;
453 }
454 ::CFRelease(pathStrRef);
455 }
456
457 return rv;
458}
459
460/* void create (in unsigned long type, in unsigned long permissions); */
461NS_IMETHODIMP nsLocalFile::Create(PRUint32 type, PRUint32 permissions)
462{
463 if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
464 return NS_ERROR_FILE_UNKNOWN_TYPE;
465
466 // Check we are correctly initialized.
467 CHECK_mBaseRef();
468
469 nsStringArray nonExtantNodes;
470 CFURLRef pathURLRef = mBaseRef;
471 FSRef pathFSRef;
472 CFStringRef leafStrRef = nsnull;
473 nsAutoBuffer<UniChar, FILENAME_BUFFER_SIZE> buffer;
474 Boolean success;
475
476 // Work backwards through the path to find the last node which
477 // exists. Place the nodes which don't exist in an array and we'll
478 // create those below.
479 while ((success = ::CFURLGetFSRef(pathURLRef, &pathFSRef)) == false) {
480 leafStrRef = ::CFURLCopyLastPathComponent(pathURLRef);
481 if (!leafStrRef)
482 break;
483 CFIndex leafLen = ::CFStringGetLength(leafStrRef);
484 if (!buffer.EnsureElemCapacity(leafLen + 1))
485 break;
486 ::CFStringGetCharacters(leafStrRef, CFRangeMake(0, leafLen), buffer.get());
487 buffer.get()[leafLen] = '\0';
488 nonExtantNodes.AppendString(nsString(nsDependentString(buffer.get())));
489 ::CFRelease(leafStrRef);
490 leafStrRef = nsnull;
491
492 // Get the parent of the leaf for the next go round
493 CFURLRef parent = ::CFURLCreateCopyDeletingLastPathComponent(NULL, pathURLRef);
494 if (!parent)
495 break;
496 if (pathURLRef != mBaseRef)
497 ::CFRelease(pathURLRef);
498 pathURLRef = parent;
499 }
500 if (pathURLRef != mBaseRef)
501 ::CFRelease(pathURLRef);
502 if (leafStrRef != nsnull)
503 ::CFRelease(leafStrRef);
504 if (!success)
505 return NS_ERROR_FAILURE;
506 PRInt32 nodesToCreate = nonExtantNodes.Count();
507 if (nodesToCreate == 0)
508 return NS_ERROR_FILE_ALREADY_EXISTS;
509
510 OSErr err;
511 nsAutoString nextNodeName;
512 for (PRInt32 i = nodesToCreate - 1; i > 0; i--) {
513 nonExtantNodes.StringAt(i, nextNodeName);
514 err = ::FSCreateDirectoryUnicode(&pathFSRef,
515 nextNodeName.Length(),
516 (const UniChar *)nextNodeName.get(),
517 kFSCatInfoNone,
518 nsnull, &pathFSRef, nsnull, nsnull);
519 if (err != noErr)
520 return MacErrorMapper(err);
521 }
522 nonExtantNodes.StringAt(0, nextNodeName);
523 if (type == NORMAL_FILE_TYPE) {
524 err = ::FSCreateFileUnicode(&pathFSRef,
525 nextNodeName.Length(),
526 (const UniChar *)nextNodeName.get(),
527 kFSCatInfoNone,
528 nsnull, nsnull, nsnull);
529 }
530 else {
531 err = ::FSCreateDirectoryUnicode(&pathFSRef,
532 nextNodeName.Length(),
533 (const UniChar *)nextNodeName.get(),
534 kFSCatInfoNone,
535 nsnull, nsnull, nsnull, nsnull);
536 }
537
538 return MacErrorMapper(err);
539}
540
541/* attribute AString leafName; */
542NS_IMETHODIMP nsLocalFile::GetLeafName(nsAString& aLeafName)
543{
544 nsCAutoString nativeString;
545 nsresult rv = GetNativeLeafName(nativeString);
546 if (NS_FAILED(rv))
547 return rv;
548 CopyUTF8toUTF16NFC(nativeString, aLeafName);
549 return NS_OK;
550}
551
552NS_IMETHODIMP nsLocalFile::SetLeafName(const nsAString& aLeafName)
553{
554 return SetNativeLeafName(NS_ConvertUTF16toUTF8(aLeafName));
555}
556
557/* [noscript] attribute ACString nativeLeafName; */
558NS_IMETHODIMP nsLocalFile::GetNativeLeafName(nsACString& aNativeLeafName)
559{
560 // Check we are correctly initialized.
561 CHECK_mBaseRef();
562
563 nsresult rv = NS_ERROR_FAILURE;
564 CFStringRef leafStrRef = ::CFURLCopyLastPathComponent(mBaseRef);
565 if (leafStrRef) {
566 rv = CFStringReftoUTF8(leafStrRef, aNativeLeafName);
567 ::CFRelease(leafStrRef);
568 }
569 return rv;
570}
571
572NS_IMETHODIMP nsLocalFile::SetNativeLeafName(const nsACString& aNativeLeafName)
573{
574 // Check we are correctly initialized.
575 CHECK_mBaseRef();
576
577 nsresult rv = NS_ERROR_FAILURE;
578 CFURLRef parentURLRef = ::CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, mBaseRef);
579 if (parentURLRef) {
580 CFStringRef nodeStrRef = ::CFStringCreateWithCString(kCFAllocatorDefault,
581 PromiseFlatCString(aNativeLeafName).get(),
582 kCFStringEncodingUTF8);
583
584 if (nodeStrRef) {
585 CFURLRef newURLRef = ::CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault,
586 parentURLRef, nodeStrRef, PR_FALSE);
587 if (newURLRef) {
588 SetBaseRef(newURLRef);
589 ::CFRelease(newURLRef);
590 rv = NS_OK;
591 }
592 ::CFRelease(nodeStrRef);
593 }
594 ::CFRelease(parentURLRef);
595 }
596 return rv;
597}
598
599/* void copyTo (in nsIFile newParentDir, in AString newName); */
600NS_IMETHODIMP nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString& newName)
601{
602 return CopyInternal(newParentDir, newName, PR_FALSE);
603}
604
605/* [noscrpit] void CopyToNative (in nsIFile newParentDir, in ACString newName); */
606NS_IMETHODIMP nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString& newName)
607{
608 return CopyInternal(newParentDir, NS_ConvertUTF8toUTF16(newName), PR_FALSE);
609}
610
611/* void copyToFollowingLinks (in nsIFile newParentDir, in AString newName); */
612NS_IMETHODIMP nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString& newName)
613{
614 return CopyInternal(newParentDir, newName, PR_TRUE);
615}
616
617/* [noscript] void copyToFollowingLinksNative (in nsIFile newParentDir, in ACString newName); */
618NS_IMETHODIMP nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString& newName)
619{
620 return CopyInternal(newParentDir, NS_ConvertUTF8toUTF16(newName), PR_TRUE);
621}
622
623/* void moveTo (in nsIFile newParentDir, in AString newName); */
624NS_IMETHODIMP nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString& newName)
625{
626 return MoveToNative(newParentDir, NS_ConvertUTF16toUTF8(newName));
627}
628
629/* [noscript] void moveToNative (in nsIFile newParentDir, in ACString newName); */
630NS_IMETHODIMP nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString& newName)
631{
632 // Check we are correctly initialized.
633 CHECK_mBaseRef();
634
635 StFollowLinksState followLinks(*this, PR_FALSE);
636
637 PRBool isDirectory;
638 nsresult rv = IsDirectory(&isDirectory);
639 if (NS_FAILED(rv))
640 return rv;
641
642 // Get the source path.
643 nsCAutoString srcPath;
644 rv = GetNativePath(srcPath);
645 if (NS_FAILED(rv))
646 return rv;
647
648 // Build the destination path.
649 nsCOMPtr<nsIFile> parentDir = newParentDir;
650 if (!parentDir) {
651 if (newName.IsEmpty())
652 return NS_ERROR_INVALID_ARG;
653 rv = GetParent(getter_AddRefs(parentDir));
654 if (NS_FAILED(rv))
655 return rv;
656 }
657 else {
658 PRBool exists;
659 rv = parentDir->Exists(&exists);
660 if (NS_FAILED(rv))
661 return rv;
662 if (!exists) {
663 rv = parentDir->Create(nsIFile::DIRECTORY_TYPE, 0777);
664 if (NS_FAILED(rv))
665 return rv;
666 }
667 }
668
669 nsCAutoString destPath;
670 rv = parentDir->GetNativePath(destPath);
671 if (NS_FAILED(rv))
672 return rv;
673
674 if (!newName.IsEmpty())
675 destPath.Append(NS_LITERAL_CSTRING("/") + newName);
676 else {
677 nsCAutoString leafName;
678 rv = GetNativeLeafName(leafName);
679 if (NS_FAILED(rv))
680 return rv;
681 destPath.Append(NS_LITERAL_CSTRING("/") + leafName);
682 }
683
684 // Perform the move.
685 if (rename(srcPath.get(), destPath.get()) != 0) {
686 if (errno == EXDEV) {
687 // Can't move across volume (device) boundaries. Copy and remove.
688 rv = CopyToNative(parentDir, newName);
689 if (NS_SUCCEEDED(rv)) {
690 // Permit removal failure.
691 Remove(PR_TRUE);
692 }
693 }
694 else
695 rv = NSRESULT_FOR_ERRNO();
696
697 if (NS_FAILED(rv))
698 return rv;
699 }
700
701 // Update |this| to refer to the moved file.
702 CFURLRef newBaseRef =
703 ::CFURLCreateFromFileSystemRepresentation(NULL, (UInt8*)destPath.get(),
704 destPath.Length(), isDirectory);
705 if (!newBaseRef)
706 return NS_ERROR_FAILURE;
707 SetBaseRef(newBaseRef);
708 ::CFRelease(newBaseRef);
709
710 return rv;
711}
712
713/* void remove (in boolean recursive); */
714NS_IMETHODIMP nsLocalFile::Remove(PRBool recursive)
715{
716 // Check we are correctly initialized.
717 CHECK_mBaseRef();
718
719 // XXX If we're an alias, never remove target
720 StFollowLinksState followLinks(*this, PR_FALSE);
721
722 PRBool isDirectory;
723 nsresult rv = IsDirectory(&isDirectory);
724 if (NS_FAILED(rv))
725 return rv;
726
727 if (recursive && isDirectory) {
728 FSRef fsRef;
729 rv = GetFSRefInternal(fsRef);
730 if (NS_FAILED(rv))
731 return rv;
732
733 // Call MoreFilesX to do a recursive removal.
734 OSStatus err = ::FSDeleteContainer(&fsRef);
735 rv = MacErrorMapper(err);
736 }
737 else {
738 nsCAutoString path;
739 rv = GetNativePath(path);
740 if (NS_FAILED(rv))
741 return rv;
742
743 const char* pathPtr = path.get();
744 int status;
745 if (isDirectory)
746 status = rmdir(pathPtr);
747 else
748 status = unlink(pathPtr);
749
750 if (status != 0)
751 rv = NSRESULT_FOR_ERRNO();
752 }
753
754 mCachedFSRefValid = PR_FALSE;
755 return rv;
756}
757
758/* attribute unsigned long permissions; */
759NS_IMETHODIMP nsLocalFile::GetPermissions(PRUint32 *aPermissions)
760{
761 NS_ENSURE_ARG_POINTER(aPermissions);
762
763 FSRef fsRef;
764 nsresult rv = GetFSRefInternal(fsRef);
765 if (NS_FAILED(rv))
766 return rv;
767
768 FSCatalogInfo catalogInfo;
769 OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoPermissions, &catalogInfo,
770 nsnull, nsnull, nsnull);
771 if (err != noErr)
772 return MacErrorMapper(err);
773 FSPermissionInfo *permPtr = (FSPermissionInfo*)catalogInfo.permissions;
774 *aPermissions = permPtr->mode;
775 return NS_OK;
776}
777
778NS_IMETHODIMP nsLocalFile::SetPermissions(PRUint32 aPermissions)
779{
780 FSRef fsRef;
781 nsresult rv = GetFSRefInternal(fsRef);
782 if (NS_FAILED(rv))
783 return rv;
784
785 FSCatalogInfo catalogInfo;
786 OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoPermissions, &catalogInfo,
787 nsnull, nsnull, nsnull);
788 if (err != noErr)
789 return MacErrorMapper(err);
790 FSPermissionInfo *permPtr = (FSPermissionInfo*)catalogInfo.permissions;
791 permPtr->mode = (UInt16)aPermissions;
792 err = ::FSSetCatalogInfo(&fsRef, kFSCatInfoPermissions, &catalogInfo);
793 return MacErrorMapper(err);
794}
795
796/* attribute unsigned long permissionsOfLink; */
797NS_IMETHODIMP nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink)
798{
799 NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
800 return NS_ERROR_NOT_IMPLEMENTED;
801}
802
803NS_IMETHODIMP nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissionsOfLink)
804{
805 NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
806 return NS_ERROR_NOT_IMPLEMENTED;
807}
808
809/* attribute PRInt64 lastModifiedTime; */
810NS_IMETHODIMP nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModifiedTime)
811{
812 // Check we are correctly initialized.
813 CHECK_mBaseRef();
814
815 NS_ENSURE_ARG_POINTER(aLastModifiedTime);
816
817 FSRef fsRef;
818 nsresult rv = GetFSRefInternal(fsRef);
819 if (NS_FAILED(rv))
820 return rv;
821
822 FSCatalogInfo catalogInfo;
823 OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoContentMod, &catalogInfo,
824 nsnull, nsnull, nsnull);
825 if (err != noErr)
826 return MacErrorMapper(err);
827 *aLastModifiedTime = HFSPlustoNSPRTime(catalogInfo.contentModDate);
828 return NS_OK;
829}
830
831NS_IMETHODIMP nsLocalFile::SetLastModifiedTime(PRInt64 aLastModifiedTime)
832{
833 // Check we are correctly initialized.
834 CHECK_mBaseRef();
835
836 OSErr err;
837 nsresult rv;
838 FSRef fsRef;
839 FSCatalogInfo catalogInfo;
840
841 rv = GetFSRefInternal(fsRef);
842 if (NS_FAILED(rv))
843 return rv;
844
845 FSRef parentRef;
846 PRBool notifyParent;
847
848 /* Get the node flags, the content modification date and time, and the parent ref */
849 err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags + kFSCatInfoContentMod,
850 &catalogInfo, NULL, NULL, &parentRef);
851 if (err != noErr)
852 return MacErrorMapper(err);
853
854 /* Notify the parent if this is a file */
855 notifyParent = (0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask));
856
857 NSPRtoHFSPlusTime(aLastModifiedTime, catalogInfo.contentModDate);
858 err = ::FSSetCatalogInfo(&fsRef, kFSCatInfoContentMod, &catalogInfo);
859 if (err != noErr)
860 return MacErrorMapper(err);
861
862 /* Send a notification for the parent of the file, or for the directory */
863 err = FNNotify(notifyParent ? &parentRef : &fsRef, kFNDirectoryModifiedMessage, kNilOptions);
864 if (err != noErr)
865 return MacErrorMapper(err);
866
867 return NS_OK;
868}
869
870/* attribute PRInt64 lastModifiedTimeOfLink; */
871NS_IMETHODIMP nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModifiedTimeOfLink)
872{
873 NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
874 return NS_ERROR_NOT_IMPLEMENTED;
875}
876NS_IMETHODIMP nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModifiedTimeOfLink)
877{
878 NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
879 return NS_ERROR_NOT_IMPLEMENTED;
880}
881
882/* attribute PRInt64 fileSize; */
883NS_IMETHODIMP nsLocalFile::GetFileSize(PRInt64 *aFileSize)
884{
885 NS_ENSURE_ARG_POINTER(aFileSize);
886 *aFileSize = 0;
887
888 FSRef fsRef;
889 nsresult rv = GetFSRefInternal(fsRef);
890 if (NS_FAILED(rv))
891 return rv;
892
893 FSCatalogInfo catalogInfo;
894 OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags + kFSCatInfoDataSizes, &catalogInfo,
895 nsnull, nsnull, nsnull);
896 if (err != noErr)
897 return MacErrorMapper(err);
898
899 // FSGetCatalogInfo can return a bogus size for directories sometimes, so only
900 // rely on the answer for files
901 if ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) == 0)
902 *aFileSize = catalogInfo.dataLogicalSize;
903 return NS_OK;
904}
905
906NS_IMETHODIMP nsLocalFile::SetFileSize(PRInt64 aFileSize)
907{
908 // Check we are correctly initialized.
909 CHECK_mBaseRef();
910
911 FSRef fsRef;
912 nsresult rv = GetFSRefInternal(fsRef);
913 if (NS_FAILED(rv))
914 return rv;
915
916 SInt16 refNum;
917 OSErr err = ::FSOpenFork(&fsRef, 0, nsnull, fsWrPerm, &refNum);
918 if (err != noErr)
919 return MacErrorMapper(err);
920 err = ::FSSetForkSize(refNum, fsFromStart, aFileSize);
921 ::FSCloseFork(refNum);
922
923 return MacErrorMapper(err);
924}
925
926/* readonly attribute PRInt64 fileSizeOfLink; */
927NS_IMETHODIMP nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSizeOfLink)
928{
929 // Check we are correctly initialized.
930 CHECK_mBaseRef();
931
932 NS_ENSURE_ARG_POINTER(aFileSizeOfLink);
933
934 StFollowLinksState followLinks(*this, PR_FALSE);
935 return GetFileSize(aFileSizeOfLink);
936}
937
938/* readonly attribute AString target; */
939NS_IMETHODIMP nsLocalFile::GetTarget(nsAString& aTarget)
940{
941 NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
942 return NS_ERROR_NOT_IMPLEMENTED;
943}
944
945/* [noscript] readonly attribute ACString nativeTarget; */
946NS_IMETHODIMP nsLocalFile::GetNativeTarget(nsACString& aNativeTarget)
947{
948 NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
949 return NS_ERROR_NOT_IMPLEMENTED;
950}
951
952/* readonly attribute AString path; */
953NS_IMETHODIMP nsLocalFile::GetPath(nsAString& aPath)
954{
955 nsCAutoString nativeString;
956 nsresult rv = GetNativePath(nativeString);
957 if (NS_FAILED(rv))
958 return rv;
959 CopyUTF8toUTF16NFC(nativeString, aPath);
960 return NS_OK;
961}
962
963/* [noscript] readonly attribute ACString nativePath; */
964NS_IMETHODIMP nsLocalFile::GetNativePath(nsACString& aNativePath)
965{
966 // Check we are correctly initialized.
967 CHECK_mBaseRef();
968
969 nsresult rv = NS_ERROR_FAILURE;
970 CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(mBaseRef, kCFURLPOSIXPathStyle);
971 if (pathStrRef) {
972 rv = CFStringReftoUTF8(pathStrRef, aNativePath);
973 ::CFRelease(pathStrRef);
974 }
975 return rv;
976}
977
978/* boolean exists (); */
979NS_IMETHODIMP nsLocalFile::Exists(PRBool *_retval)
980{
981 // Check we are correctly initialized.
982 CHECK_mBaseRef();
983
984 NS_ENSURE_ARG_POINTER(_retval);
985 *_retval = PR_FALSE;
986
987 FSRef fsRef;
988 if (NS_SUCCEEDED(GetFSRefInternal(fsRef, PR_TRUE))) {
989 *_retval = PR_TRUE;
990 }
991
992 return NS_OK;
993}
994
995/* boolean isWritable (); */
996NS_IMETHODIMP nsLocalFile::IsWritable(PRBool *_retval)
997{
998 // Check we are correctly initialized.
999 CHECK_mBaseRef();
1000
1001 NS_ENSURE_ARG_POINTER(_retval);
1002 *_retval = PR_FALSE;
1003
1004 FSRef fsRef;
1005 nsresult rv = GetFSRefInternal(fsRef);
1006 if (NS_FAILED(rv))
1007 return rv;
1008 if (::FSCheckLock(&fsRef) == noErr) {
1009 PRUint32 permissions;
1010 rv = GetPermissions(&permissions);
1011 if (NS_FAILED(rv))
1012 return rv;
1013 *_retval = ((permissions & S_IWUSR) != 0);
1014 }
1015 return NS_OK;
1016}
1017
1018/* boolean isReadable (); */
1019NS_IMETHODIMP nsLocalFile::IsReadable(PRBool *_retval)
1020{
1021 // Check we are correctly initialized.
1022 CHECK_mBaseRef();
1023
1024 NS_ENSURE_ARG_POINTER(_retval);
1025 *_retval = PR_FALSE;
1026
1027 PRUint32 permissions;
1028 nsresult rv = GetPermissions(&permissions);
1029 if (NS_FAILED(rv))
1030 return rv;
1031 *_retval = ((permissions & S_IRUSR) != 0);
1032 return NS_OK;
1033}
1034
1035/* boolean isExecutable (); */
1036NS_IMETHODIMP nsLocalFile::IsExecutable(PRBool *_retval)
1037{
1038 // Check we are correctly initialized.
1039 CHECK_mBaseRef();
1040
1041 NS_ENSURE_ARG_POINTER(_retval);
1042 *_retval = PR_FALSE;
1043
1044 FSRef fsRef;
1045 nsresult rv = GetFSRefInternal(fsRef);
1046 if (NS_FAILED(rv))
1047 return rv;
1048
1049 LSRequestedInfo theInfoRequest = kLSRequestAllInfo;
1050 LSItemInfoRecord theInfo;
1051 if (::LSCopyItemInfoForRef(&fsRef, theInfoRequest, &theInfo) == noErr) {
1052 if ((theInfo.flags & kLSItemInfoIsApplication) != 0)
1053 *_retval = PR_TRUE;
1054 }
1055 return NS_OK;
1056}
1057
1058/* boolean isHidden (); */
1059NS_IMETHODIMP nsLocalFile::IsHidden(PRBool *_retval)
1060{
1061 NS_ENSURE_ARG_POINTER(_retval);
1062 *_retval = PR_FALSE;
1063
1064 FSRef fsRef;
1065 nsresult rv = GetFSRefInternal(fsRef);
1066 if (NS_FAILED(rv))
1067 return rv;
1068
1069 FSCatalogInfo catalogInfo;
1070 HFSUniStr255 leafName;
1071 OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoFinderInfo, &catalogInfo,
1072 &leafName, nsnull, nsnull);
1073 if (err != noErr)
1074 return MacErrorMapper(err);
1075
1076 FileInfo *fInfoPtr = (FileInfo *)(catalogInfo.finderInfo); // Finder flags are in the same place whether we use FileInfo or FolderInfo
1077 if ((fInfoPtr->finderFlags & kIsInvisible) != 0) {
1078 *_retval = PR_TRUE;
1079 }
1080 else {
1081 // If the leaf name begins with a '.', consider it invisible
1082 if (leafName.length >= 1 && leafName.unicode[0] == UniChar('.'))
1083 *_retval = PR_TRUE;
1084 }
1085 return NS_OK;
1086}
1087
1088/* boolean isDirectory (); */
1089NS_IMETHODIMP nsLocalFile::IsDirectory(PRBool *_retval)
1090{
1091 NS_ENSURE_ARG_POINTER(_retval);
1092 *_retval = PR_FALSE;
1093
1094 FSRef fsRef;
1095 nsresult rv = GetFSRefInternal(fsRef, PR_FALSE);
1096 if (NS_FAILED(rv))
1097 return rv;
1098
1099 FSCatalogInfo catalogInfo;
1100 OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags, &catalogInfo,
1101 nsnull, nsnull, nsnull);
1102 if (err != noErr)
1103 return MacErrorMapper(err);
1104 *_retval = ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0);
1105 return NS_OK;
1106}
1107
1108/* boolean isFile (); */
1109NS_IMETHODIMP nsLocalFile::IsFile(PRBool *_retval)
1110{
1111 NS_ENSURE_ARG_POINTER(_retval);
1112 *_retval = PR_FALSE;
1113
1114 FSRef fsRef;
1115 nsresult rv = GetFSRefInternal(fsRef, PR_FALSE);
1116 if (NS_FAILED(rv))
1117 return rv;
1118
1119 FSCatalogInfo catalogInfo;
1120 OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags, &catalogInfo,
1121 nsnull, nsnull, nsnull);
1122 if (err != noErr)
1123 return MacErrorMapper(err);
1124 *_retval = ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) == 0);
1125 return NS_OK;
1126}
1127
1128/* boolean isSymlink (); */
1129NS_IMETHODIMP nsLocalFile::IsSymlink(PRBool *_retval)
1130{
1131 // Check we are correctly initialized.
1132 CHECK_mBaseRef();
1133
1134 NS_ENSURE_ARG(_retval);
1135 *_retval = PR_FALSE;
1136
1137 // Check we are correctly initialized.
1138 CHECK_mBaseRef();
1139
1140 FSRef fsRef;
1141 if (::CFURLGetFSRef(mBaseRef, &fsRef)) {
1142 Boolean isAlias, isFolder;
1143 if (::FSIsAliasFile(&fsRef, &isAlias, &isFolder) == noErr)
1144 *_retval = isAlias;
1145 }
1146 return NS_OK;
1147}
1148
1149/* boolean isSpecial (); */
1150NS_IMETHODIMP nsLocalFile::IsSpecial(PRBool *_retval)
1151{
1152 NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
1153 return NS_ERROR_NOT_IMPLEMENTED;
1154}
1155
1156/* nsIFile clone (); */
1157NS_IMETHODIMP nsLocalFile::Clone(nsIFile **_retval)
1158{
1159 // Just copy-construct ourselves
1160 *_retval = new nsLocalFile(*this);
1161 if (!*_retval)
1162 return NS_ERROR_OUT_OF_MEMORY;
1163
1164 NS_ADDREF(*_retval);
1165
1166 return NS_OK;
1167}
1168
1169/* boolean equals (in nsIFile inFile); */
1170NS_IMETHODIMP nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
1171{
1172 return EqualsInternal(inFile, PR_TRUE, _retval);
1173}
1174
1175nsresult
1176nsLocalFile::EqualsInternal(nsISupports* inFile, PRBool aUpdateCache,
1177 PRBool *_retval)
1178{
1179 NS_ENSURE_ARG_POINTER(_retval);
1180 *_retval = PR_FALSE;
1181
1182 nsCOMPtr<nsILocalFileMac> inMacFile(do_QueryInterface(inFile));
1183 if (!inFile)
1184 return NS_OK;
1185
1186 nsLocalFile* inLF =
1187 static_cast<nsLocalFile*>((nsILocalFileMac*) inMacFile);
1188
1189 // If both exist, compare FSRefs
1190 FSRef thisFSRef, inFSRef;
1191 nsresult rv1 = GetFSRefInternal(thisFSRef, aUpdateCache);
1192 nsresult rv2 = inLF->GetFSRefInternal(inFSRef, aUpdateCache);
1193 if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) {
1194 *_retval = (thisFSRef == inFSRef);
1195 return NS_OK;
1196 }
1197 // If one exists and the other doesn't, not equal
1198 if (rv1 != rv2)
1199 return NS_OK;
1200
1201 // Arg, we have to get their paths and compare
1202 nsCAutoString thisPath, inPath;
1203 if (NS_FAILED(GetNativePath(thisPath)))
1204 return NS_ERROR_FAILURE;
1205 if (NS_FAILED(inMacFile->GetNativePath(inPath)))
1206 return NS_ERROR_FAILURE;
1207 *_retval = thisPath.Equals(inPath);
1208
1209 return NS_OK;
1210}
1211
1212/* boolean contains (in nsIFile inFile, in boolean recur); */
1213NS_IMETHODIMP nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retval)
1214{
1215 // Check we are correctly initialized.
1216 CHECK_mBaseRef();
1217
1218 NS_ENSURE_ARG_POINTER(_retval);
1219 *_retval = PR_FALSE;
1220
1221 PRBool isDir;
1222 nsresult rv = IsDirectory(&isDir);
1223 if (NS_FAILED(rv))
1224 return rv;
1225 if (!isDir)
1226 return NS_OK; // must be a dir to contain someone
1227
1228 nsCAutoString thisPath, inPath;
1229 if (NS_FAILED(GetNativePath(thisPath)) || NS_FAILED(inFile->GetNativePath(inPath)))
1230 return NS_ERROR_FAILURE;
1231 size_t thisPathLen = thisPath.Length();
1232 if ((inPath.Length() > thisPathLen + 1) && (strncasecmp(thisPath.get(), inPath.get(), thisPathLen) == 0)) {
1233 // Now make sure that the |inFile|'s path has a separator at thisPathLen,
1234 // and there's at least one more character after that.
1235 if (inPath[thisPathLen] == kPathSepChar)
1236 *_retval = PR_TRUE;
1237 }
1238 return NS_OK;
1239}
1240
1241/* readonly attribute nsIFile parent; */
1242NS_IMETHODIMP nsLocalFile::GetParent(nsIFile * *aParent)
1243{
1244 NS_ENSURE_ARG_POINTER(aParent);
1245 *aParent = nsnull;
1246
1247 // Check we are correctly initialized.
1248 CHECK_mBaseRef();
1249
1250 nsLocalFile *newFile = nsnull;
1251
1252 // If it can be determined without error that a file does not
1253 // have a parent, return nsnull for the parent and NS_OK as the result.
1254 // See bug 133617.
1255 nsresult rv = NS_OK;
1256 CFURLRef parentURLRef = ::CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, mBaseRef);
1257 if (parentURLRef) {
1258 // If the parent path is longer than file's path then
1259 // CFURLCreateCopyDeletingLastPathComponent must have simply added
1260 // two dots at the end - in this case indicate that there is no parent.
1261 // See bug 332389.
1262 CFStringRef path = ::CFURLGetString(mBaseRef);
1263 CFStringRef newPath = ::CFURLGetString(parentURLRef);
1264 if (::CFStringGetLength(newPath) < ::CFStringGetLength(path)) {
1265 rv = NS_ERROR_FAILURE;
1266 newFile = new nsLocalFile;
1267 if (newFile) {
1268 rv = newFile->InitWithCFURL(parentURLRef);
1269 if (NS_SUCCEEDED(rv)) {
1270 NS_ADDREF(*aParent = newFile);
1271 rv = NS_OK;
1272 }
1273 }
1274 }
1275 ::CFRelease(parentURLRef);
1276 }
1277 return rv;
1278}
1279
1280/* readonly attribute nsISimpleEnumerator directoryEntries; */
1281NS_IMETHODIMP nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator **aDirectoryEntries)
1282{
1283 NS_ENSURE_ARG_POINTER(aDirectoryEntries);
1284 *aDirectoryEntries = nsnull;
1285
1286 nsresult rv;
1287 PRBool isDir;
1288 rv = IsDirectory(&isDir);
1289 if (NS_FAILED(rv))
1290 return rv;
1291 if (!isDir)
1292 return NS_ERROR_FILE_NOT_DIRECTORY;
1293
1294 nsDirEnumerator* dirEnum = new nsDirEnumerator;
1295 if (dirEnum == nsnull)
1296 return NS_ERROR_OUT_OF_MEMORY;
1297 NS_ADDREF(dirEnum);
1298 rv = dirEnum->Init(this);
1299 if (NS_FAILED(rv)) {
1300 NS_RELEASE(dirEnum);
1301 return rv;
1302 }
1303 *aDirectoryEntries = dirEnum;
1304
1305 return NS_OK;
1306}
1307
1308
1309//*****************************************************************************
1310// nsLocalFile::nsILocalFile
1311//*****************************************************************************
1312#pragma mark -
1313#pragma mark [nsILocalFile]
1314
1315/* void initWithPath (in AString filePath); */
1316NS_IMETHODIMP nsLocalFile::InitWithPath(const nsAString& filePath)
1317{
1318 return InitWithNativePath(NS_ConvertUTF16toUTF8(filePath));
1319}
1320
1321/* [noscript] void initWithNativePath (in ACString filePath); */
1322NS_IMETHODIMP nsLocalFile::InitWithNativePath(const nsACString& filePath)
1323{
1324 nsCAutoString fixedPath;
1325 if (Substring(filePath, 0, 2).EqualsLiteral("~/")) {
1326 nsCOMPtr<nsIFile> homeDir;
1327 nsCAutoString homePath;
1328 nsresult rv = NS_GetSpecialDirectory(NS_OS_HOME_DIR,
1329 getter_AddRefs(homeDir));
1330 NS_ENSURE_SUCCESS(rv, rv);
1331 rv = homeDir->GetNativePath(homePath);
1332 NS_ENSURE_SUCCESS(rv, rv);
1333
1334 fixedPath = homePath + Substring(filePath, 1, filePath.Length() - 1);
1335 }
1336 else if (filePath.IsEmpty() || filePath.First() != '/')
1337 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
1338 else
1339 fixedPath.Assign(filePath);
1340
1341 // A path with consecutive '/'s which are not between
1342 // nodes crashes CFURLGetFSRef(). Consecutive '/'s which
1343 // are between actual nodes are OK. So, convert consecutive
1344 // '/'s to a single one.
1345 fixedPath.ReplaceSubstring("//", "/");
1346
1347#if 1 // bird: hack to fix RegistryLocationForSpec issues with /path/to/./components
1348 fixedPath.ReplaceSubstring("/./", "/");
1349 size_t len = fixedPath.Length();
1350 while (len > 2)
1351 {
1352 size_t choplen = 0;
1353 if (!strcmp(fixedPath.get() + len - 2, "/."))
1354 choplen = 2;
1355 else if (!strcmp(fixedPath.get() + len - 1, "/"))
1356 choplen = 1;
1357 else
1358 break;
1359 fixedPath = StringHead(fixedPath, len - choplen);
1360 }
1361 // bird: another hack for the issue with VirtualBoxVM and symlinks...
1362 char tmpBuf[PATH_MAX];
1363 if (realpath(fixedPath.get(), tmpBuf))
1364 fixedPath = tmpBuf;
1365#endif
1366
1367 // On 10.2, huge paths also crash CFURLGetFSRef()
1368 if (fixedPath.Length() > PATH_MAX)
1369 return NS_ERROR_FILE_NAME_TOO_LONG;
1370
1371 CFStringRef pathAsCFString;
1372 CFURLRef pathAsCFURL;
1373
1374 pathAsCFString = ::CFStringCreateWithCString(nsnull, fixedPath.get(), kCFStringEncodingUTF8);
1375 if (!pathAsCFString)
1376 return NS_ERROR_FAILURE;
1377 pathAsCFURL = ::CFURLCreateWithFileSystemPath(nsnull, pathAsCFString, kCFURLPOSIXPathStyle, PR_FALSE);
1378 if (!pathAsCFURL) {
1379 ::CFRelease(pathAsCFString);
1380 return NS_ERROR_FAILURE;
1381 }
1382 SetBaseRef(pathAsCFURL);
1383 ::CFRelease(pathAsCFURL);
1384 ::CFRelease(pathAsCFString);
1385 return NS_OK;
1386}
1387
1388/* void initWithFile (in nsILocalFile aFile); */
1389NS_IMETHODIMP nsLocalFile::InitWithFile(nsILocalFile *aFile)
1390{
1391 NS_ENSURE_ARG(aFile);
1392
1393 nsCOMPtr<nsILocalFileMac> aFileMac(do_QueryInterface(aFile));
1394 if (!aFileMac)
1395 return NS_ERROR_UNEXPECTED;
1396 CFURLRef urlRef;
1397 nsresult rv = aFileMac->GetCFURL(&urlRef);
1398 if (NS_FAILED(rv))
1399 return rv;
1400 rv = InitWithCFURL(urlRef);
1401 ::CFRelease(urlRef);
1402 return rv;
1403}
1404
1405/* attribute PRBool followLinks; */
1406NS_IMETHODIMP nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
1407{
1408 NS_ENSURE_ARG_POINTER(aFollowLinks);
1409
1410 *aFollowLinks = mFollowLinks;
1411 return NS_OK;
1412}
1413
1414NS_IMETHODIMP nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
1415{
1416 if (aFollowLinks != mFollowLinks) {
1417 mFollowLinks = aFollowLinks;
1418 UpdateTargetRef();
1419 }
1420 return NS_OK;
1421}
1422
1423/* [noscript] PRFileDescStar openNSPRFileDesc (in long flags, in long mode); */
1424NS_IMETHODIMP nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval)
1425{
1426 NS_ENSURE_ARG_POINTER(_retval);
1427
1428 nsCAutoString path;
1429 nsresult rv = GetPathInternal(path);
1430 if (NS_FAILED(rv))
1431 return rv;
1432
1433 *_retval = PR_Open(path.get(), flags, mode);
1434 if (! *_retval)
1435 return NS_ErrorAccordingToNSPR();
1436
1437 return NS_OK;
1438}
1439
1440/* [noscript] FILE openANSIFileDesc (in string mode); */
1441NS_IMETHODIMP nsLocalFile::OpenANSIFileDesc(const char *mode, FILE **_retval)
1442{
1443 NS_ENSURE_ARG_POINTER(_retval);
1444
1445 nsCAutoString path;
1446 nsresult rv = GetPathInternal(path);
1447 if (NS_FAILED(rv))
1448 return rv;
1449
1450 *_retval = fopen(path.get(), mode);
1451 if (! *_retval)
1452 return NS_ERROR_FAILURE;
1453
1454 return NS_OK;
1455}
1456
1457/* [noscript] PRLibraryStar load (); */
1458NS_IMETHODIMP nsLocalFile::Load(PRLibrary **_retval)
1459{
1460 // Check we are correctly initialized.
1461 CHECK_mBaseRef();
1462
1463 NS_ENSURE_ARG_POINTER(_retval);
1464
1465 NS_TIMELINE_START_TIMER("PR_LoadLibrary");
1466
1467 nsCAutoString path;
1468 nsresult rv = GetPathInternal(path);
1469 if (NS_FAILED(rv))
1470 return rv;
1471
1472#ifdef NS_BUILD_REFCNT_LOGGING
1473 nsTraceRefcntImpl::SetActivityIsLegal(PR_FALSE);
1474#endif
1475
1476 *_retval = PR_LoadLibrary(path.get());
1477
1478#ifdef NS_BUILD_REFCNT_LOGGING
1479 nsTraceRefcntImpl::SetActivityIsLegal(PR_TRUE);
1480#endif
1481
1482 NS_TIMELINE_STOP_TIMER("PR_LoadLibrary");
1483 NS_TIMELINE_MARK_TIMER1("PR_LoadLibrary", path.get());
1484
1485 if (!*_retval)
1486 return NS_ERROR_FAILURE;
1487
1488 return NS_OK;
1489}
1490
1491/* readonly attribute PRInt64 diskSpaceAvailable; */
1492NS_IMETHODIMP nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
1493{
1494 // Check we are correctly initialized.
1495 CHECK_mBaseRef();
1496
1497 NS_ENSURE_ARG_POINTER(aDiskSpaceAvailable);
1498
1499 FSRef fsRef;
1500 nsresult rv = GetFSRefInternal(fsRef);
1501 if (NS_FAILED(rv))
1502 return rv;
1503
1504 OSErr err;
1505 FSCatalogInfo catalogInfo;
1506 err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoVolume, &catalogInfo,
1507 nsnull, nsnull, nsnull);
1508 if (err != noErr)
1509 return MacErrorMapper(err);
1510
1511 FSVolumeInfo volumeInfo;
1512 err = ::FSGetVolumeInfo(catalogInfo.volume, 0, nsnull, kFSVolInfoSizes,
1513 &volumeInfo, nsnull, nsnull);
1514 if (err != noErr)
1515 return MacErrorMapper(err);
1516
1517 *aDiskSpaceAvailable = volumeInfo.freeBytes;
1518 return NS_OK;
1519}
1520
1521/* void appendRelativePath (in AString relativeFilePath); */
1522NS_IMETHODIMP nsLocalFile::AppendRelativePath(const nsAString& relativeFilePath)
1523{
1524 return AppendRelativeNativePath(NS_ConvertUTF16toUTF8(relativeFilePath));
1525}
1526
1527/* [noscript] void appendRelativeNativePath (in ACString relativeFilePath); */
1528NS_IMETHODIMP nsLocalFile::AppendRelativeNativePath(const nsACString& relativeFilePath)
1529{
1530 if (relativeFilePath.IsEmpty())
1531 return NS_OK;
1532 // No leading '/'
1533 if (relativeFilePath.First() == '/')
1534 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
1535
1536 // Parse the nodes and call Append() for each
1537 nsACString::const_iterator nodeBegin, pathEnd;
1538 relativeFilePath.BeginReading(nodeBegin);
1539 relativeFilePath.EndReading(pathEnd);
1540 nsACString::const_iterator nodeEnd(nodeBegin);
1541
1542 while (nodeEnd != pathEnd) {
1543 FindCharInReadable(kPathSepChar, nodeEnd, pathEnd);
1544 nsresult rv = AppendNative(Substring(nodeBegin, nodeEnd));
1545 if (NS_FAILED(rv))
1546 return rv;
1547 if (nodeEnd != pathEnd) // If there's more left in the string, inc over the '/' nodeEnd is on.
1548 ++nodeEnd;
1549 nodeBegin = nodeEnd;
1550 }
1551 return NS_OK;
1552}
1553
1554/* attribute ACString persistentDescriptor; */
1555NS_IMETHODIMP nsLocalFile::GetPersistentDescriptor(nsACString& aPersistentDescriptor)
1556{
1557 FSRef fsRef;
1558 nsresult rv = GetFSRefInternal(fsRef);
1559 if (NS_FAILED(rv))
1560 return rv;
1561
1562 AliasHandle aliasH;
1563 OSErr err = ::FSNewAlias(nsnull, &fsRef, &aliasH);
1564 if (err != noErr)
1565 return MacErrorMapper(err);
1566
1567 PRUint32 bytes = ::GetHandleSize((Handle) aliasH);
1568 ::HLock((Handle) aliasH);
1569 // Passing nsnull for dest makes NULL-term string
1570 char* buf = PL_Base64Encode((const char*)*aliasH, bytes, nsnull);
1571 ::DisposeHandle((Handle) aliasH);
1572 NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
1573
1574 aPersistentDescriptor = buf;
1575 PR_Free(buf);
1576
1577 return NS_OK;
1578}
1579
1580NS_IMETHODIMP nsLocalFile::SetPersistentDescriptor(const nsACString& aPersistentDescriptor)
1581{
1582 if (aPersistentDescriptor.IsEmpty())
1583 return NS_ERROR_INVALID_ARG;
1584
1585 // Support pathnames as user-supplied descriptors if they begin with '/'
1586 // or '~'. These characters do not collide with the base64 set used for
1587 // encoding alias records.
1588 char first = aPersistentDescriptor.First();
1589 if (first == '/' || first == '~')
1590 return InitWithNativePath(aPersistentDescriptor);
1591
1592 nsresult rv = NS_OK;
1593
1594 PRUint32 dataSize = aPersistentDescriptor.Length();
1595 char* decodedData = PL_Base64Decode(PromiseFlatCString(aPersistentDescriptor).get(), dataSize, nsnull);
1596 if (!decodedData) {
1597 NS_ERROR("SetPersistentDescriptor was given bad data");
1598 return NS_ERROR_FAILURE;
1599 }
1600
1601 // Cast to an alias record and resolve.
1602 AliasRecord aliasHeader = *(AliasPtr)decodedData;
1603 PRInt32 aliasSize = GetAliasSizeFromRecord(aliasHeader);
1604 if (aliasSize > ((PRInt32)dataSize * 3) / 4) { // be paranoid about having too few data
1605 PR_Free(decodedData);
1606 return NS_ERROR_FAILURE;
1607 }
1608
1609 // Move the now-decoded data into the Handle.
1610 // The size of the decoded data is 3/4 the size of the encoded data. See plbase64.h
1611 Handle newHandle = nsnull;
1612 if (::PtrToHand(decodedData, &newHandle, aliasSize) != noErr)
1613 rv = NS_ERROR_OUT_OF_MEMORY;
1614 PR_Free(decodedData);
1615 if (NS_FAILED(rv))
1616 return rv;
1617
1618 Boolean changed;
1619 FSRef resolvedFSRef;
1620 OSErr err = ::FSResolveAlias(nsnull, (AliasHandle)newHandle, &resolvedFSRef, &changed);
1621
1622 rv = MacErrorMapper(err);
1623 DisposeHandle(newHandle);
1624 if (NS_FAILED(rv))
1625 return rv;
1626
1627 return InitWithFSRef(&resolvedFSRef);
1628}
1629
1630/* void reveal (); */
1631NS_IMETHODIMP nsLocalFile::Reveal()
1632{
1633 FSRef fsRefToReveal;
1634 AppleEvent aeEvent = {0, nil};
1635 AppleEvent aeReply = {0, nil};
1636 StAEDesc aeDirDesc, listElem, myAddressDesc, fileList;
1637 OSErr err;
1638 ProcessSerialNumber process;
1639
1640 nsresult rv = GetFSRefInternal(fsRefToReveal);
1641 if (NS_FAILED(rv))
1642 return rv;
1643
1644 err = ::FindRunningAppBySignature ('MACS', process);
1645 if (err == noErr) {
1646 err = ::AECreateDesc(typeProcessSerialNumber, (Ptr)&process, sizeof(process), &myAddressDesc);
1647 if (err == noErr) {
1648 // Create the FinderEvent
1649 err = ::AECreateAppleEvent(kAEMiscStandards, kAEMakeObjectsVisible, &myAddressDesc,
1650 kAutoGenerateReturnID, kAnyTransactionID, &aeEvent);
1651 if (err == noErr) {
1652 // Create the file list
1653 err = ::AECreateList(nil, 0, false, &fileList);
1654 if (err == noErr) {
1655 FSSpec fsSpecToReveal;
1656 err = ::FSRefMakeFSSpec(&fsRefToReveal, &fsSpecToReveal);
1657 if (err == noErr) {
1658 err = ::AEPutPtr(&fileList, 0, typeFSS, &fsSpecToReveal, sizeof(FSSpec));
1659 if (err == noErr) {
1660 err = ::AEPutParamDesc(&aeEvent, keyDirectObject, &fileList);
1661 if (err == noErr) {
1662 err = ::AESend(&aeEvent, &aeReply, kAENoReply, kAENormalPriority, kAEDefaultTimeout, nil, nil);
1663 if (err == noErr)
1664 ::SetFrontProcess(&process);
1665 }
1666 }
1667 }
1668 }
1669 }
1670 }
1671 }
1672
1673 return NS_OK;
1674}
1675
1676/* void launch (); */
1677NS_IMETHODIMP nsLocalFile::Launch()
1678{
1679 FSRef fsRef;
1680 nsresult rv = GetFSRefInternal(fsRef);
1681 if (NS_FAILED(rv))
1682 return rv;
1683
1684 OSErr err = ::LSOpenFSRef(&fsRef, NULL);
1685 return MacErrorMapper(err);
1686}
1687
1688
1689//*****************************************************************************
1690// nsLocalFile::nsILocalFileMac
1691//*****************************************************************************
1692#pragma mark -
1693#pragma mark [nsILocalFileMac]
1694
1695/* void initWithCFURL (in CFURLRef aCFURL); */
1696NS_IMETHODIMP nsLocalFile::InitWithCFURL(CFURLRef aCFURL)
1697{
1698 NS_ENSURE_ARG(aCFURL);
1699
1700 SetBaseRef(aCFURL);
1701 return NS_OK;
1702}
1703
1704/* void initWithFSRef ([const] in FSRefPtr aFSRef); */
1705NS_IMETHODIMP nsLocalFile::InitWithFSRef(const FSRef *aFSRef)
1706{
1707 NS_ENSURE_ARG(aFSRef);
1708 nsresult rv = NS_ERROR_FAILURE;
1709
1710 CFURLRef newURLRef = ::CFURLCreateFromFSRef(kCFAllocatorDefault, aFSRef);
1711 if (newURLRef) {
1712 SetBaseRef(newURLRef);
1713 ::CFRelease(newURLRef);
1714 rv = NS_OK;
1715 }
1716 return rv;
1717}
1718
1719/* void initWithFSSpec ([const] in FSSpecPtr aFileSpec); */
1720NS_IMETHODIMP nsLocalFile::InitWithFSSpec(const FSSpec *aFileSpec)
1721{
1722 NS_ENSURE_ARG(aFileSpec);
1723
1724 FSRef fsRef;
1725 OSErr err = ::FSpMakeFSRef(aFileSpec, &fsRef);
1726 if (err == noErr)
1727 return InitWithFSRef(&fsRef);
1728 else if (err == fnfErr) {
1729 CInfoPBRec pBlock;
1730 FSSpec parentDirSpec;
1731
1732 memset(&pBlock, 0, sizeof(CInfoPBRec));
1733 parentDirSpec.name[0] = 0;
1734 pBlock.dirInfo.ioVRefNum = aFileSpec->vRefNum;
1735 pBlock.dirInfo.ioDrDirID = aFileSpec->parID;
1736 pBlock.dirInfo.ioNamePtr = (StringPtr)parentDirSpec.name;
1737 pBlock.dirInfo.ioFDirIndex = -1; //get info on parID
1738 err = ::PBGetCatInfoSync(&pBlock);
1739 if (err != noErr)
1740 return MacErrorMapper(err);
1741
1742 parentDirSpec.vRefNum = aFileSpec->vRefNum;
1743 parentDirSpec.parID = pBlock.dirInfo.ioDrParID;
1744 err = ::FSpMakeFSRef(&parentDirSpec, &fsRef);
1745 if (err != noErr)
1746 return MacErrorMapper(err);
1747 HFSUniStr255 unicodeName;
1748 err = ::HFSNameGetUnicodeName(aFileSpec->name, kTextEncodingUnknown, &unicodeName);
1749 if (err != noErr)
1750 return MacErrorMapper(err);
1751 nsresult rv = InitWithFSRef(&fsRef);
1752 if (NS_FAILED(rv))
1753 return rv;
1754 return Append(nsDependentString(unicodeName.unicode, unicodeName.length));
1755 }
1756 return MacErrorMapper(err);
1757}
1758
1759/* void initToAppWithCreatorCode (in OSType aAppCreator); */
1760NS_IMETHODIMP nsLocalFile::InitToAppWithCreatorCode(OSType aAppCreator)
1761{
1762 FSRef fsRef;
1763 OSErr err = ::LSFindApplicationForInfo(aAppCreator, nsnull, nsnull, &fsRef, nsnull);
1764 if (err != noErr)
1765 return MacErrorMapper(err);
1766 return InitWithFSRef(&fsRef);
1767}
1768
1769/* CFURLRef getCFURL (); */
1770NS_IMETHODIMP nsLocalFile::GetCFURL(CFURLRef *_retval)
1771{
1772 NS_ENSURE_ARG_POINTER(_retval);
1773 CFURLRef whichURLRef = mFollowLinks ? mTargetRef : mBaseRef;
1774 if (whichURLRef)
1775 ::CFRetain(whichURLRef);
1776 *_retval = whichURLRef;
1777 return whichURLRef ? NS_OK : NS_ERROR_FAILURE;
1778}
1779
1780/* FSRef getFSRef (); */
1781NS_IMETHODIMP nsLocalFile::GetFSRef(FSRef *_retval)
1782{
1783 NS_ENSURE_ARG_POINTER(_retval);
1784 return GetFSRefInternal(*_retval);
1785}
1786
1787/* FSSpec getFSSpec (); */
1788NS_IMETHODIMP nsLocalFile::GetFSSpec(FSSpec *_retval)
1789{
1790 NS_ENSURE_ARG_POINTER(_retval);
1791
1792 // Check we are correctly initialized.
1793 CHECK_mBaseRef();
1794
1795 OSErr err;
1796 FSRef fsRef;
1797 nsresult rv = GetFSRefInternal(fsRef);
1798 if (NS_SUCCEEDED(rv)) {
1799 // If the leaf node exists, things are simple.
1800 err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNone,
1801 nsnull, nsnull, _retval, nsnull);
1802 return MacErrorMapper(err);
1803 }
1804 else if (rv == NS_ERROR_FILE_NOT_FOUND) {
1805 // If the parent of the leaf exists, make an FSSpec from that.
1806 CFURLRef parentURLRef = ::CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, mBaseRef);
1807 if (!parentURLRef)
1808 return NS_ERROR_FAILURE;
1809
1810 err = fnfErr;
1811 if (::CFURLGetFSRef(parentURLRef, &fsRef)) {
1812 FSCatalogInfo catalogInfo;
1813 if ((err = ::FSGetCatalogInfo(&fsRef,
1814 kFSCatInfoVolume + kFSCatInfoNodeID + kFSCatInfoTextEncoding,
1815 &catalogInfo, nsnull, nsnull, nsnull)) == noErr) {
1816 nsAutoString leafName;
1817 if (NS_SUCCEEDED(GetLeafName(leafName))) {
1818 Str31 hfsName;
1819 if ((err = ::UnicodeNameGetHFSName(leafName.Length(),
1820 leafName.get(),
1821 catalogInfo.textEncodingHint,
1822 catalogInfo.nodeID == fsRtDirID,
1823 hfsName)) == noErr)
1824 err = ::FSMakeFSSpec(catalogInfo.volume, catalogInfo.nodeID, hfsName, _retval);
1825 }
1826 }
1827 }
1828 ::CFRelease(parentURLRef);
1829 rv = MacErrorMapper(err);
1830 }
1831 return rv;
1832}
1833
1834/* readonly attribute PRInt64 fileSizeWithResFork; */
1835NS_IMETHODIMP nsLocalFile::GetFileSizeWithResFork(PRInt64 *aFileSizeWithResFork)
1836{
1837 NS_ENSURE_ARG_POINTER(aFileSizeWithResFork);
1838
1839 FSRef fsRef;
1840 nsresult rv = GetFSRefInternal(fsRef);
1841 if (NS_FAILED(rv))
1842 return rv;
1843
1844 FSCatalogInfo catalogInfo;
1845 OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoDataSizes + kFSCatInfoRsrcSizes,
1846 &catalogInfo, nsnull, nsnull, nsnull);
1847 if (err != noErr)
1848 return MacErrorMapper(err);
1849
1850 *aFileSizeWithResFork = catalogInfo.dataLogicalSize + catalogInfo.rsrcLogicalSize;
1851 return NS_OK;
1852}
1853
1854/* attribute OSType fileType; */
1855NS_IMETHODIMP nsLocalFile::GetFileType(OSType *aFileType)
1856{
1857 NS_ENSURE_ARG_POINTER(aFileType);
1858
1859 FSRef fsRef;
1860 nsresult rv = GetFSRefInternal(fsRef);
1861 if (NS_FAILED(rv))
1862 return rv;
1863
1864 FinderInfo fInfo;
1865 OSErr err = ::FSGetFinderInfo(&fsRef, &fInfo, nsnull, nsnull);
1866 if (err != noErr)
1867 return MacErrorMapper(err);
1868 *aFileType = fInfo.file.fileType;
1869 return NS_OK;
1870}
1871
1872NS_IMETHODIMP nsLocalFile::SetFileType(OSType aFileType)
1873{
1874 FSRef fsRef;
1875 nsresult rv = GetFSRefInternal(fsRef);
1876 if (NS_FAILED(rv))
1877 return rv;
1878
1879 OSErr err = ::FSChangeCreatorType(&fsRef, 0, aFileType);
1880 return MacErrorMapper(err);
1881}
1882
1883/* attribute OSType fileCreator; */
1884NS_IMETHODIMP nsLocalFile::GetFileCreator(OSType *aFileCreator)
1885{
1886 NS_ENSURE_ARG_POINTER(aFileCreator);
1887
1888 FSRef fsRef;
1889 nsresult rv = GetFSRefInternal(fsRef);
1890 if (NS_FAILED(rv))
1891 return rv;
1892
1893 FinderInfo fInfo;
1894 OSErr err = ::FSGetFinderInfo(&fsRef, &fInfo, nsnull, nsnull);
1895 if (err != noErr)
1896 return MacErrorMapper(err);
1897 *aFileCreator = fInfo.file.fileCreator;
1898 return NS_OK;
1899}
1900
1901NS_IMETHODIMP nsLocalFile::SetFileCreator(OSType aFileCreator)
1902{
1903 FSRef fsRef;
1904 nsresult rv = GetFSRefInternal(fsRef);
1905 if (NS_FAILED(rv))
1906 return rv;
1907
1908 OSErr err = ::FSChangeCreatorType(&fsRef, aFileCreator, 0);
1909 return MacErrorMapper(err);
1910}
1911
1912/* void setFileTypeAndCreatorFromMIMEType (in string aMIMEType); */
1913NS_IMETHODIMP nsLocalFile::SetFileTypeAndCreatorFromMIMEType(const char *aMIMEType)
1914{
1915 // XXX - This should be cut from the API. Would create an evil dependency.
1916 NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
1917 return NS_ERROR_NOT_IMPLEMENTED;
1918}
1919
1920/* void setFileTypeAndCreatorFromExtension (in string aExtension); */
1921NS_IMETHODIMP nsLocalFile::SetFileTypeAndCreatorFromExtension(const char *aExtension)
1922{
1923 // XXX - This should be cut from the API. Would create an evil dependency.
1924 NS_ERROR("NS_ERROR_NOT_IMPLEMENTED");
1925 return NS_ERROR_NOT_IMPLEMENTED;
1926}
1927
1928/* void launchWithDoc (in nsILocalFile aDocToLoad, in boolean aLaunchInBackground); */
1929NS_IMETHODIMP nsLocalFile::LaunchWithDoc(nsILocalFile *aDocToLoad, PRBool aLaunchInBackground)
1930{
1931 PRBool isExecutable;
1932 nsresult rv = IsExecutable(&isExecutable);
1933 if (NS_FAILED(rv))
1934 return rv;
1935 if (!isExecutable)
1936 return NS_ERROR_FILE_EXECUTION_FAILED;
1937
1938 FSRef appFSRef, docFSRef;
1939 rv = GetFSRefInternal(appFSRef);
1940 if (NS_FAILED(rv))
1941 return rv;
1942
1943 if (aDocToLoad) {
1944 nsCOMPtr<nsILocalFileMac> macDoc = do_QueryInterface(aDocToLoad);
1945 rv = macDoc->GetFSRef(&docFSRef);
1946 if (NS_FAILED(rv))
1947 return rv;
1948 }
1949
1950 LSLaunchFlags theLaunchFlags = kLSLaunchDefaults;
1951 LSLaunchFSRefSpec thelaunchSpec;
1952
1953 if (aLaunchInBackground)
1954 theLaunchFlags |= kLSLaunchDontSwitch;
1955 memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
1956
1957 thelaunchSpec.appRef = &appFSRef;
1958 if (aDocToLoad) {
1959 thelaunchSpec.numDocs = 1;
1960 thelaunchSpec.itemRefs = &docFSRef;
1961 }
1962 thelaunchSpec.launchFlags = theLaunchFlags;
1963
1964 OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, NULL);
1965 if (err != noErr)
1966 return MacErrorMapper(err);
1967
1968 return NS_OK;
1969}
1970
1971/* void openDocWithApp (in nsILocalFile aAppToOpenWith, in boolean aLaunchInBackground); */
1972NS_IMETHODIMP nsLocalFile::OpenDocWithApp(nsILocalFile *aAppToOpenWith, PRBool aLaunchInBackground)
1973{
1974 nsresult rv;
1975 OSErr err;
1976
1977 FSRef docFSRef, appFSRef;
1978 rv = GetFSRefInternal(docFSRef);
1979 if (NS_FAILED(rv))
1980 return rv;
1981
1982 if (aAppToOpenWith) {
1983 nsCOMPtr<nsILocalFileMac> appFileMac = do_QueryInterface(aAppToOpenWith, &rv);
1984 if (!appFileMac)
1985 return rv;
1986
1987 PRBool isExecutable;
1988 rv = appFileMac->IsExecutable(&isExecutable);
1989 if (NS_FAILED(rv))
1990 return rv;
1991 if (!isExecutable)
1992 return NS_ERROR_FILE_EXECUTION_FAILED;
1993
1994 rv = appFileMac->GetFSRef(&appFSRef);
1995 if (NS_FAILED(rv))
1996 return rv;
1997 }
1998 else {
1999 OSType fileCreator;
2000 rv = GetFileCreator(&fileCreator);
2001 if (NS_FAILED(rv))
2002 return rv;
2003
2004 err = ::LSFindApplicationForInfo(fileCreator, nsnull, nsnull, &appFSRef, nsnull);
2005 if (err != noErr)
2006 return MacErrorMapper(err);
2007 }
2008
2009 LSLaunchFlags theLaunchFlags = kLSLaunchDefaults;
2010 LSLaunchFSRefSpec thelaunchSpec;
2011
2012 if (aLaunchInBackground)
2013 theLaunchFlags |= kLSLaunchDontSwitch;
2014 memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
2015
2016 thelaunchSpec.appRef = &appFSRef;
2017 thelaunchSpec.numDocs = 1;
2018 thelaunchSpec.itemRefs = &docFSRef;
2019 thelaunchSpec.launchFlags = theLaunchFlags;
2020
2021 err = ::LSOpenFromRefSpec(&thelaunchSpec, NULL);
2022 if (err != noErr)
2023 return MacErrorMapper(err);
2024
2025 return NS_OK;
2026}
2027
2028/* boolean isPackage (); */
2029NS_IMETHODIMP nsLocalFile::IsPackage(PRBool *_retval)
2030{
2031 NS_ENSURE_ARG(_retval);
2032 *_retval = PR_FALSE;
2033
2034 FSRef fsRef;
2035 nsresult rv = GetFSRefInternal(fsRef);
2036 if (NS_FAILED(rv))
2037 return rv;
2038
2039 FSCatalogInfo catalogInfo;
2040 OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags + kFSCatInfoFinderInfo,
2041 &catalogInfo, nsnull, nsnull, nsnull);
2042 if (err != noErr)
2043 return MacErrorMapper(err);
2044 if ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0) {
2045 FileInfo *fInfoPtr = (FileInfo *)(catalogInfo.finderInfo);
2046 if ((fInfoPtr->finderFlags & kHasBundle) != 0) {
2047 *_retval = PR_TRUE;
2048 }
2049 else {
2050 // Folders ending with ".app" are also considered to
2051 // be packages, even if the top-level folder doesn't have bundle set
2052 nsCAutoString name;
2053 if (NS_SUCCEEDED(rv = GetNativeLeafName(name))) {
2054 const char *extPtr = strrchr(name.get(), '.');
2055 if (extPtr) {
2056 if ((nsCRT::strcasecmp(extPtr, ".app") == 0))
2057 *_retval = PR_TRUE;
2058 }
2059 }
2060 }
2061 }
2062 return NS_OK;
2063}
2064
2065NS_IMETHODIMP
2066nsLocalFile::GetBundleDisplayName(nsAString& outBundleName)
2067{
2068 PRBool isPackage = PR_FALSE;
2069 nsresult rv = IsPackage(&isPackage);
2070 if (NS_FAILED(rv) || !isPackage)
2071 return NS_ERROR_FAILURE;
2072
2073 nsAutoString name;
2074 rv = GetLeafName(name);
2075 if (NS_FAILED(rv))
2076 return rv;
2077
2078 PRInt32 length = name.Length();
2079 if (Substring(name, length - 4, length).EqualsLiteral(".app")) {
2080 // 4 characters in ".app"
2081 outBundleName = Substring(name, 0, length - 4);
2082 }
2083 else
2084 outBundleName = name;
2085
2086 return NS_OK;
2087}
2088
2089NS_IMETHODIMP
2090nsLocalFile::GetBundleIdentifier(nsACString& outBundleIdentifier)
2091{
2092 nsresult rv = NS_ERROR_FAILURE;
2093
2094 CFURLRef urlRef;
2095 if (NS_SUCCEEDED(GetCFURL(&urlRef))) {
2096 CFBundleRef bundle = ::CFBundleCreate(NULL, urlRef);
2097 if (bundle) {
2098 CFStringRef bundleIdentifier = ::CFBundleGetIdentifier(bundle);
2099 if (bundleIdentifier)
2100 rv = CFStringReftoUTF8(bundleIdentifier, outBundleIdentifier);
2101
2102 ::CFRelease(bundle);
2103 }
2104 ::CFRelease(urlRef);
2105 }
2106
2107 return rv;
2108}
2109
2110
2111//*****************************************************************************
2112// nsLocalFile Methods
2113//*****************************************************************************
2114#pragma mark -
2115#pragma mark [Protected Methods]
2116
2117nsresult nsLocalFile::SetBaseRef(CFURLRef aCFURLRef)
2118{
2119 NS_ENSURE_ARG(aCFURLRef);
2120
2121 ::CFRetain(aCFURLRef);
2122 if (mBaseRef)
2123 ::CFRelease(mBaseRef);
2124 mBaseRef = aCFURLRef;
2125
2126 mFollowLinksDirty = PR_TRUE;
2127 UpdateTargetRef();
2128 mCachedFSRefValid = PR_FALSE;
2129 return NS_OK;
2130}
2131
2132nsresult nsLocalFile::UpdateTargetRef()
2133{
2134 // Check we are correctly initialized.
2135 CHECK_mBaseRef();
2136
2137 if (mFollowLinksDirty) {
2138 if (mTargetRef) {
2139 ::CFRelease(mTargetRef);
2140 mTargetRef = nsnull;
2141 }
2142 if (mFollowLinks) {
2143 mTargetRef = mBaseRef;
2144 ::CFRetain(mTargetRef);
2145
2146 FSRef fsRef;
2147 if (::CFURLGetFSRef(mBaseRef, &fsRef)) {
2148 Boolean targetIsFolder, wasAliased;
2149 if (FSResolveAliasFile(&fsRef, true /*resolveAliasChains*/,
2150 &targetIsFolder, &wasAliased) == noErr && wasAliased) {
2151 ::CFRelease(mTargetRef);
2152 mTargetRef = CFURLCreateFromFSRef(NULL, &fsRef);
2153 if (!mTargetRef)
2154 return NS_ERROR_FAILURE;
2155 }
2156 }
2157 mFollowLinksDirty = PR_FALSE;
2158 }
2159 }
2160 return NS_OK;
2161}
2162
2163nsresult nsLocalFile::GetFSRefInternal(FSRef& aFSRef, PRBool bForceUpdateCache)
2164{
2165 if (bForceUpdateCache || !mCachedFSRefValid) {
2166 mCachedFSRefValid = PR_FALSE;
2167 CFURLRef whichURLRef = mFollowLinks ? mTargetRef : mBaseRef;
2168 NS_ENSURE_TRUE(whichURLRef, NS_ERROR_NULL_POINTER);
2169 if (::CFURLGetFSRef(whichURLRef, &mCachedFSRef))
2170 mCachedFSRefValid = PR_TRUE;
2171 }
2172 if (mCachedFSRefValid) {
2173 aFSRef = mCachedFSRef;
2174 return NS_OK;
2175 }
2176 // CFURLGetFSRef only returns a Boolean for success,
2177 // so we have to assume what the error was. This is
2178 // the only probable cause.
2179 return NS_ERROR_FILE_NOT_FOUND;
2180}
2181
2182nsresult nsLocalFile::GetPathInternal(nsACString& path)
2183{
2184 nsresult rv = NS_ERROR_FAILURE;
2185
2186 CFURLRef whichURLRef = mFollowLinks ? mTargetRef : mBaseRef;
2187 NS_ENSURE_TRUE(whichURLRef, NS_ERROR_NULL_POINTER);
2188
2189 CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(whichURLRef, kCFURLPOSIXPathStyle);
2190 if (pathStrRef) {
2191 rv = CFStringReftoUTF8(pathStrRef, path);
2192 ::CFRelease(pathStrRef);
2193 }
2194 return rv;
2195}
2196
2197nsresult nsLocalFile::CopyInternal(nsIFile* aParentDir,
2198 const nsAString& newName,
2199 PRBool followLinks)
2200{
2201 // Check we are correctly initialized.
2202 CHECK_mBaseRef();
2203
2204 StFollowLinksState srcFollowState(*this, followLinks);
2205
2206 nsresult rv;
2207 OSErr err;
2208 FSRef srcFSRef, newFSRef;
2209
2210 rv = GetFSRefInternal(srcFSRef);
2211 if (NS_FAILED(rv))
2212 return rv;
2213
2214 nsCOMPtr<nsIFile> newParentDir = aParentDir;
2215
2216 if (!newParentDir) {
2217 if (newName.IsEmpty())
2218 return NS_ERROR_INVALID_ARG;
2219 rv = GetParent(getter_AddRefs(newParentDir));
2220 if (NS_FAILED(rv))
2221 return rv;
2222 }
2223
2224 // If newParentDir does not exist, create it
2225 PRBool exists;
2226 rv = newParentDir->Exists(&exists);
2227 if (NS_FAILED(rv))
2228 return rv;
2229 if (!exists) {
2230 rv = newParentDir->Create(nsIFile::DIRECTORY_TYPE, 0777);
2231 if (NS_FAILED(rv))
2232 return rv;
2233 }
2234
2235 FSRef destFSRef;
2236 nsCOMPtr<nsILocalFileMac> newParentDirMac(do_QueryInterface(newParentDir));
2237 if (!newParentDirMac)
2238 return NS_ERROR_NO_INTERFACE;
2239 rv = newParentDirMac->GetFSRef(&destFSRef);
2240 if (NS_FAILED(rv))
2241 return rv;
2242
2243 err =
2244 ::FSCopyObject(&srcFSRef, &destFSRef, newName.Length(),
2245 newName.Length() ? PromiseFlatString(newName).get() : NULL,
2246 0, kFSCatInfoNone, false, false, NULL, NULL, &newFSRef);
2247
2248 return MacErrorMapper(err);
2249}
2250
2251const PRInt64 kMillisecsPerSec = 1000LL;
2252const PRInt64 kUTCDateTimeFractionDivisor = 65535LL;
2253
2254PRInt64 nsLocalFile::HFSPlustoNSPRTime(const UTCDateTime& utcTime)
2255{
2256 // Start with seconds since Jan. 1, 1904 GMT
2257 PRInt64 result = ((PRInt64)utcTime.highSeconds << 32) + (PRInt64)utcTime.lowSeconds;
2258 // Subtract to convert to NSPR epoch of 1970
2259 result -= kJanuaryFirst1970Seconds;
2260 // Convert to millisecs
2261 result *= kMillisecsPerSec;
2262 // Convert the fraction to millisecs and add it
2263 result += ((PRInt64)utcTime.fraction * kMillisecsPerSec) / kUTCDateTimeFractionDivisor;
2264
2265 return result;
2266}
2267
2268void nsLocalFile::NSPRtoHFSPlusTime(PRInt64 nsprTime, UTCDateTime& utcTime)
2269{
2270 PRInt64 fraction = nsprTime % kMillisecsPerSec;
2271 PRInt64 seconds = (nsprTime / kMillisecsPerSec) + kJanuaryFirst1970Seconds;
2272 utcTime.highSeconds = (UInt16)((PRUint64)seconds >> 32);
2273 utcTime.lowSeconds = (UInt32)seconds;
2274 utcTime.fraction = (UInt16)((fraction * kUTCDateTimeFractionDivisor) / kMillisecsPerSec);
2275}
2276
2277nsresult nsLocalFile::CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr)
2278{
2279 nsresult rv = NS_ERROR_FAILURE;
2280 CFIndex usedBufLen, inStrLen = ::CFStringGetLength(aInStrRef);
2281 CFIndex charsConverted = ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen),
2282 kCFStringEncodingUTF8, 0, PR_FALSE, nsnull, 0, &usedBufLen);
2283 if (charsConverted == inStrLen) {
2284#if 0 /* bird: too new? */
2285 aOutStr.SetLength(usedBufLen);
2286 if (aOutStr.Length() != (unsigned int)usedBufLen)
2287 return NS_ERROR_OUT_OF_MEMORY;
2288 UInt8 *buffer = (UInt8*) aOutStr.BeginWriting();
2289
2290 ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen),
2291 kCFStringEncodingUTF8, 0, false, buffer, usedBufLen, &usedBufLen);
2292 rv = NS_OK;
2293#else
2294 nsAutoBuffer<UInt8, FILENAME_BUFFER_SIZE> buffer;
2295 if (buffer.EnsureElemCapacity(usedBufLen + 1)) {
2296 ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen),
2297 kCFStringEncodingUTF8, 0, false, buffer.get(), usedBufLen, &usedBufLen);
2298 buffer.get()[usedBufLen] = '\0';
2299 aOutStr.Assign(nsDependentCString((char*)buffer.get()));
2300 rv = NS_OK;
2301 }
2302#endif
2303 }
2304 return rv;
2305}
2306
2307// nsIHashable
2308
2309NS_IMETHODIMP
2310nsLocalFile::Equals(nsIHashable* aOther, PRBool *aResult)
2311{
2312 return EqualsInternal(aOther, PR_FALSE, aResult);
2313}
2314
2315NS_IMETHODIMP
2316nsLocalFile::GetHashCode(PRUint32 *aResult)
2317{
2318 CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(mBaseRef, kCFURLPOSIXPathStyle);
2319 nsCAutoString path;
2320 CFStringReftoUTF8(pathStrRef, path);
2321 ::CFRelease(pathStrRef);
2322 *aResult = HashString(path);
2323 return NS_OK;
2324}
2325
2326//*****************************************************************************
2327// Global Functions
2328//*****************************************************************************
2329#pragma mark -
2330#pragma mark [Global Functions]
2331
2332void nsLocalFile::GlobalInit()
2333{
2334}
2335
2336void nsLocalFile::GlobalShutdown()
2337{
2338}
2339
2340nsresult NS_NewLocalFile(const nsAString& path, PRBool followLinks, nsILocalFile* *result)
2341{
2342 nsLocalFile* file = new nsLocalFile;
2343 if (file == nsnull)
2344 return NS_ERROR_OUT_OF_MEMORY;
2345 NS_ADDREF(file);
2346
2347 file->SetFollowLinks(followLinks);
2348
2349 if (!path.IsEmpty()) {
2350 nsresult rv = file->InitWithPath(path);
2351 if (NS_FAILED(rv)) {
2352 NS_RELEASE(file);
2353 return rv;
2354 }
2355 }
2356 *result = file;
2357 return NS_OK;
2358}
2359
2360nsresult NS_NewNativeLocalFile(const nsACString& path, PRBool followLinks, nsILocalFile **result)
2361{
2362 return NS_NewLocalFile(NS_ConvertUTF8toUTF16(path), followLinks, result);
2363}
2364
2365nsresult NS_NewLocalFileWithFSSpec(const FSSpec* inSpec, PRBool followLinks, nsILocalFileMac **result)
2366{
2367 nsLocalFile* file = new nsLocalFile();
2368 if (file == nsnull)
2369 return NS_ERROR_OUT_OF_MEMORY;
2370 NS_ADDREF(file);
2371
2372 file->SetFollowLinks(followLinks);
2373
2374 nsresult rv = file->InitWithFSSpec(inSpec);
2375 if (NS_FAILED(rv)) {
2376 NS_RELEASE(file);
2377 return rv;
2378 }
2379 *result = file;
2380 return NS_OK;
2381}
2382
2383nsresult NS_NewLocalFileWithFSRef(const FSRef* aFSRef, PRBool aFollowLinks, nsILocalFileMac** result)
2384{
2385 nsLocalFile* file = new nsLocalFile();
2386 if (file == nsnull)
2387 return NS_ERROR_OUT_OF_MEMORY;
2388 NS_ADDREF(file);
2389
2390 file->SetFollowLinks(aFollowLinks);
2391
2392 nsresult rv = file->InitWithFSRef(aFSRef);
2393 if (NS_FAILED(rv)) {
2394 NS_RELEASE(file);
2395 return rv;
2396 }
2397 *result = file;
2398 return NS_OK;
2399}
2400
2401//*****************************************************************************
2402// Static Functions
2403//*****************************************************************************
2404
2405static nsresult MacErrorMapper(OSErr inErr)
2406{
2407 nsresult outErr;
2408
2409 switch (inErr)
2410 {
2411 case noErr:
2412 outErr = NS_OK;
2413 break;
2414
2415 case fnfErr:
2416 case afpObjectNotFound:
2417 case afpDirNotFound:
2418 outErr = NS_ERROR_FILE_NOT_FOUND;
2419 break;
2420
2421 case dupFNErr:
2422 case afpObjectExists:
2423 outErr = NS_ERROR_FILE_ALREADY_EXISTS;
2424 break;
2425
2426 case dskFulErr:
2427 case afpDiskFull:
2428 outErr = NS_ERROR_FILE_DISK_FULL;
2429 break;
2430
2431 case fLckdErr:
2432 case afpVolLocked:
2433 outErr = NS_ERROR_FILE_IS_LOCKED;
2434 break;
2435
2436 case afpAccessDenied:
2437 outErr = NS_ERROR_FILE_ACCESS_DENIED;
2438 break;
2439
2440 case afpDirNotEmpty:
2441 outErr = NS_ERROR_FILE_DIR_NOT_EMPTY;
2442 break;
2443
2444 // Can't find good map for some
2445 case bdNamErr:
2446 outErr = NS_ERROR_FAILURE;
2447 break;
2448
2449 default:
2450 outErr = NS_ERROR_FAILURE;
2451 break;
2452 }
2453 return outErr;
2454}
2455
2456static OSErr FindRunningAppBySignature(OSType aAppSig, ProcessSerialNumber& outPsn)
2457{
2458 ProcessInfoRec info;
2459 OSErr err = noErr;
2460
2461 outPsn.highLongOfPSN = 0;
2462 outPsn.lowLongOfPSN = kNoProcess;
2463
2464 while (PR_TRUE)
2465 {
2466 err = ::GetNextProcess(&outPsn);
2467 if (err == procNotFound)
2468 break;
2469 if (err != noErr)
2470 return err;
2471 info.processInfoLength = sizeof(ProcessInfoRec);
2472 info.processName = nil;
2473 info.processAppSpec = nil;
2474 err = ::GetProcessInformation(&outPsn, &info);
2475 if (err != noErr)
2476 return err;
2477
2478 if (info.processSignature == aAppSig)
2479 return noErr;
2480 }
2481 return procNotFound;
2482}
2483
2484// Convert a UTF-8 string to a UTF-16 string while normalizing to
2485// Normalization Form C (composed Unicode). We need this because
2486// Mac OS X file system uses NFD (Normalization Form D : decomposed Unicode)
2487// while most other OS', server-side programs usually expect NFC.
2488
2489typedef void (*UnicodeNormalizer) (CFMutableStringRef, CFStringNormalizationForm);
2490static void CopyUTF8toUTF16NFC(const nsACString& aSrc, nsAString& aResult)
2491{
2492 static PRBool sChecked = PR_FALSE;
2493 static UnicodeNormalizer sUnicodeNormalizer = NULL;
2494
2495 // CFStringNormalize was not introduced until Mac OS 10.2
2496 if (!sChecked) {
2497 CFBundleRef carbonBundle =
2498 CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Carbon"));
2499 if (carbonBundle)
2500 sUnicodeNormalizer = (UnicodeNormalizer)
2501 ::CFBundleGetFunctionPointerForName(carbonBundle,
2502 CFSTR("CFStringNormalize"));
2503 sChecked = PR_TRUE;
2504 }
2505
2506 if (!sUnicodeNormalizer) { // OS X 10.2 or earlier
2507 CopyUTF8toUTF16(aSrc, aResult);
2508 return;
2509 }
2510
2511 const nsAFlatCString &inFlatSrc = PromiseFlatCString(aSrc);
2512
2513 // The number of 16bit code units in a UTF-16 string will never be
2514 // larger than the number of bytes in the corresponding UTF-8 string.
2515 CFMutableStringRef inStr =
2516 ::CFStringCreateMutable(NULL, inFlatSrc.Length());
2517
2518 if (!inStr) {
2519 CopyUTF8toUTF16(aSrc, aResult);
2520 return;
2521 }
2522
2523 ::CFStringAppendCString(inStr, inFlatSrc.get(), kCFStringEncodingUTF8);
2524
2525 sUnicodeNormalizer(inStr, kCFStringNormalizationFormC);
2526
2527 CFIndex length = CFStringGetLength(inStr);
2528 const UniChar* chars = CFStringGetCharactersPtr(inStr);
2529
2530 if (chars)
2531 aResult.Assign(chars, length);
2532 else {
2533 nsAutoBuffer<UniChar, FILENAME_BUFFER_SIZE> buffer;
2534 if (!buffer.EnsureElemCapacity(length))
2535 CopyUTF8toUTF16(aSrc, aResult);
2536 else {
2537 CFStringGetCharacters(inStr, CFRangeMake(0, length), buffer.get());
2538 aResult.Assign(buffer.get(), length);
2539 }
2540 }
2541 CFRelease(inStr);
2542}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette