VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/xpcom/io/nsLocalFileMac.cpp@ 44770

最後變更 在這個檔案從44770是 1,由 vboxsync 提交於 55 年 前

import

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 98.3 KB
 
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 Communicator client code, released
16 * March 31, 1998.
17 *
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998-1999
21 * the Initial Developer. All Rights Reserved.
22 *
23 * Contributor(s):
24 * Steve Dagley <[email protected]>
25 * John R. McMullen
26 *
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
38 *
39 * ***** END LICENSE BLOCK ***** */
40
41
42#include "nsCOMPtr.h"
43#include "nsMemory.h"
44#include "nsXPIDLString.h"
45
46#include "nsLocalFile.h"
47#include "nsNativeCharsetUtils.h"
48#include "nsISimpleEnumerator.h"
49#include "nsIComponentManager.h"
50#include "nsIInternetConfigService.h"
51#include "nsIMIMEInfo.h"
52#include "prtypes.h"
53#include "prerror.h"
54
55#include "nsReadableUtils.h"
56#include "nsITimelineService.h"
57
58#ifdef XP_MACOSX
59#include "nsXPIDLString.h"
60
61#include "private/pprio.h"
62#else
63#include "pprio.h" // Include this rather than prio.h so we get def of PR_ImportFile
64#endif
65#include "prmem.h"
66#include "plbase64.h"
67
68#include "FullPath.h"
69#include "FileCopy.h"
70#include "MoreFilesExtras.h"
71#include "DirectoryCopy.h"
72#include <Script.h>
73#include <Processes.h>
74#include <StringCompare.h>
75#include <Resources.h>
76
77#include <AppleEvents.h>
78#include <AEDataModel.h>
79#include <AERegistry.h>
80#include <Gestalt.h>
81
82#include <Math64.h>
83#include <Aliases.h>
84#include <Folders.h>
85#include <Gestalt.h>
86#include "macDirectoryCopy.h"
87
88#include <limits.h>
89
90// Stupid @#$% header looks like its got extern mojo but it doesn't really
91extern "C"
92{
93#ifndef XP_MACOSX
94// BADPINK - this MSL header doesn't exist under macosx :-(
95#include <FSp_fopen.h>
96#endif
97}
98
99#if TARGET_CARBON
100#include <CodeFragments.h> // Needed for definition of kUnresolvedCFragSymbolAddress
101#include <LaunchServices.h>
102#endif
103
104#pragma mark [Constants]
105
106const OSType kDefaultCreator = 'MOSS';
107
108#pragma mark -
109#pragma mark [nsPathParser]
110
111class nsPathParser
112{
113public:
114 nsPathParser(const nsACString &path);
115
116 ~nsPathParser()
117 {
118 if (mAllocatedBuffer)
119 nsMemory::Free(mAllocatedBuffer);
120 }
121
122 const char* First()
123 {
124 return nsCRT::strtok(mBuffer, ":", &mNewString);
125 }
126 const char* Next()
127 {
128 return nsCRT::strtok(mNewString, ":", &mNewString);
129 }
130 const char* Remainder()
131 {
132 return mNewString;
133 }
134
135private:
136 char mAutoBuffer[512];
137 char *mAllocatedBuffer;
138 char *mBuffer, *mNewString;
139};
140
141nsPathParser::nsPathParser(const nsACString &inPath) :
142 mAllocatedBuffer(nsnull), mNewString(nsnull)
143{
144 PRUint32 inPathLen = inPath.Length();
145 if (inPathLen >= sizeof(mAutoBuffer)) {
146 mAllocatedBuffer = (char *)nsMemory::Alloc(inPathLen + 1);
147 mBuffer = mAllocatedBuffer;
148 }
149 else
150 mBuffer = mAutoBuffer;
151
152 // copy inPath into mBuffer
153 nsACString::const_iterator start, end;
154 inPath.BeginReading(start);
155 inPath.EndReading(end);
156
157 PRUint32 size, offset = 0;
158 for ( ; start != end; start.advance(size)) {
159 const char* buf = start.get();
160 size = start.size_forward();
161 memcpy(mBuffer + offset, buf, size);
162 offset += size;
163 }
164 mBuffer[offset] = '\0';
165}
166
167#pragma mark -
168#pragma mark [static util funcs]
169
170static inline void ClearFSSpec(FSSpec& aSpec)
171{
172 aSpec.vRefNum = 0;
173 aSpec.parID = 0;
174 aSpec.name[0] = 0;
175}
176
177
178// Simple func to map Mac OS errors into nsresults
179static nsresult MacErrorMapper(OSErr inErr)
180{
181 nsresult outErr;
182
183 switch (inErr)
184 {
185 case noErr:
186 outErr = NS_OK;
187 break;
188
189 case fnfErr:
190 outErr = NS_ERROR_FILE_NOT_FOUND;
191 break;
192
193 case dupFNErr:
194 outErr = NS_ERROR_FILE_ALREADY_EXISTS;
195 break;
196
197 case dskFulErr:
198 outErr = NS_ERROR_FILE_DISK_FULL;
199 break;
200
201 case fLckdErr:
202 outErr = NS_ERROR_FILE_IS_LOCKED;
203 break;
204
205 // Can't find good map for some
206 case bdNamErr:
207 outErr = NS_ERROR_FAILURE;
208 break;
209
210 default:
211 outErr = NS_ERROR_FAILURE;
212 break;
213 }
214 return outErr;
215}
216
217
218
219/*----------------------------------------------------------------------------
220 IsEqualFSSpec
221
222 Compare two canonical FSSpec records.
223
224 Entry: file1 = pointer to first FSSpec record.
225 file2 = pointer to second FSSpec record.
226
227 Exit: function result = true if the FSSpec records are equal.
228----------------------------------------------------------------------------*/
229
230static PRBool IsEqualFSSpec(const FSSpec& file1, const FSSpec& file2)
231{
232 return
233 file1.vRefNum == file2.vRefNum &&
234 file1.parID == file2.parID &&
235 EqualString(file1.name, file2.name, false, true);
236}
237
238
239/*----------------------------------------------------------------------------
240 GetParentFolderSpec
241
242 Given an FSSpec to a (possibly non-existent) file, get an FSSpec for its
243 parent directory.
244
245----------------------------------------------------------------------------*/
246
247static OSErr GetParentFolderSpec(const FSSpec& fileSpec, FSSpec& parentDirSpec)
248{
249 CInfoPBRec pBlock = {0};
250 OSErr err = noErr;
251
252 parentDirSpec.name[0] = 0;
253
254 pBlock.dirInfo.ioVRefNum = fileSpec.vRefNum;
255 pBlock.dirInfo.ioDrDirID = fileSpec.parID;
256 pBlock.dirInfo.ioNamePtr = (StringPtr)parentDirSpec.name;
257 pBlock.dirInfo.ioFDirIndex = -1; //get info on parID
258 err = PBGetCatInfoSync(&pBlock);
259 if (err != noErr) return err;
260
261 parentDirSpec.vRefNum = fileSpec.vRefNum;
262 parentDirSpec.parID = pBlock.dirInfo.ioDrParID;
263
264 return err;
265}
266
267
268/*----------------------------------------------------------------------------
269 VolHasDesktopDB
270
271 Check to see if a volume supports the new desktop database.
272
273 Entry: vRefNum = vol ref num of volumn
274
275 Exit: function result = error code.
276 *hasDesktop = true if volume has the new desktop database.
277----------------------------------------------------------------------------*/
278
279static OSErr VolHasDesktopDB (short vRefNum, Boolean *hasDesktop)
280{
281 HParamBlockRec pb;
282 GetVolParmsInfoBuffer info;
283 OSErr err = noErr;
284
285 pb.ioParam.ioCompletion = nil;
286 pb.ioParam.ioNamePtr = nil;
287 pb.ioParam.ioVRefNum = vRefNum;
288 pb.ioParam.ioBuffer = (Ptr)&info;
289 pb.ioParam.ioReqCount = sizeof(info);
290 err = PBHGetVolParmsSync(&pb);
291 *hasDesktop = err == noErr && (info.vMAttrib & (1L << bHasDesktopMgr)) != 0;
292 return err;
293}
294
295
296/*----------------------------------------------------------------------------
297 GetLastModDateTime
298
299 Get the last mod date and time of a file.
300
301 Entry: fSpec = pointer to file spec.
302
303 Exit: function result = error code.
304 *lastModDateTime = last mod date and time.
305----------------------------------------------------------------------------*/
306
307static OSErr GetLastModDateTime(const FSSpec *fSpec, unsigned long *lastModDateTime)
308{
309 CInfoPBRec pBlock;
310 OSErr err = noErr;
311
312 pBlock.hFileInfo.ioNamePtr = (StringPtr)fSpec->name;
313 pBlock.hFileInfo.ioVRefNum = fSpec->vRefNum;
314 pBlock.hFileInfo.ioFDirIndex = 0;
315 pBlock.hFileInfo.ioDirID = fSpec->parID;
316 err = PBGetCatInfoSync(&pBlock);
317 if (err != noErr) return err;
318 *lastModDateTime = pBlock.hFileInfo.ioFlMdDat;
319 return noErr;
320}
321
322
323/*----------------------------------------------------------------------------
324 FindAppOnVolume
325
326 Find an application on a volume.
327
328 Entry: sig = application signature.
329 vRefNum = vol ref num
330
331 Exit: function result = error code
332 = afpItemNotFound if app not found on vol.
333 *file = file spec for application on volume.
334----------------------------------------------------------------------------*/
335
336static OSErr FindAppOnVolume (OSType sig, short vRefNum, FSSpec *file)
337{
338 DTPBRec pb;
339 OSErr err = noErr;
340 short ioDTRefNum, i;
341 FInfo fInfo;
342 FSSpec candidate;
343 unsigned long lastModDateTime, maxLastModDateTime;
344
345 memset(&pb, 0, sizeof(DTPBRec));
346 pb.ioCompletion = nil;
347 pb.ioVRefNum = vRefNum;
348 pb.ioNamePtr = nil;
349 err = PBDTGetPath(&pb);
350 if (err != noErr) return err;
351 ioDTRefNum = pb.ioDTRefNum;
352
353 memset(&pb, 0, sizeof(DTPBRec));
354 pb.ioCompletion = nil;
355 pb.ioIndex = 0;
356 pb.ioFileCreator = sig;
357 pb.ioNamePtr = file->name;
358 pb.ioDTRefNum = ioDTRefNum;
359 err = PBDTGetAPPLSync(&pb);
360
361 if (err == fnfErr || err == paramErr) return afpItemNotFound;
362 if (err != noErr) return err;
363
364 file->vRefNum = vRefNum;
365 file->parID = pb.ioAPPLParID;
366
367 err = FSpGetFInfo(file, &fInfo);
368 if (err == noErr) return noErr;
369
370 i = 1;
371 maxLastModDateTime = 0;
372 while (true)
373 {
374 memset(&pb, 0, sizeof(DTPBRec));
375 pb.ioCompletion = nil;
376 pb.ioIndex = i;
377 pb.ioFileCreator = sig;
378 pb.ioNamePtr = candidate.name;
379 pb.ioDTRefNum = ioDTRefNum;
380 err = PBDTGetAPPLSync(&pb);
381 if (err != noErr) break;
382 candidate.vRefNum = vRefNum;
383 candidate.parID = pb.ioAPPLParID;
384 err = GetLastModDateTime(file, &lastModDateTime);
385 if (err == noErr) {
386 if (lastModDateTime > maxLastModDateTime) {
387 maxLastModDateTime = lastModDateTime;
388 *file = candidate;
389 }
390 }
391 i++;
392 }
393
394 return maxLastModDateTime > 0 ? noErr : afpItemNotFound;
395}
396
397
398/*----------------------------------------------------------------------------
399 GetIndVolume
400
401 Get a volume reference number by volume index.
402
403 Entry: index = volume index
404
405 Exit: function result = error code.
406 *vRefNum = vol ref num of indexed volume.
407----------------------------------------------------------------------------*/
408
409static OSErr GetIndVolume(short index, short *vRefNum)
410{
411 HParamBlockRec pb;
412 Str63 volumeName;
413 OSErr err = noErr;
414
415 pb.volumeParam.ioCompletion = nil;
416 pb.volumeParam.ioNamePtr = volumeName;
417 pb.volumeParam.ioVolIndex = index;
418
419 err = PBHGetVInfoSync(&pb);
420
421 *vRefNum = pb.volumeParam.ioVRefNum;
422 return err;
423}
424
425
426// Private NSPR functions
427static unsigned long gJanuaryFirst1970Seconds = 0;
428/*
429 * The geographic location and time zone information of a Mac
430 * are stored in extended parameter RAM. The ReadLocation
431 * produdure uses the geographic location record, MachineLocation,
432 * to read the geographic location and time zone information in
433 * extended parameter RAM.
434 *
435 * Because serial port and SLIP conflict with ReadXPram calls,
436 * we cache the call here.
437 *
438 * Caveat: this caching will give the wrong result if a session
439 * extend across the DST changeover time.
440 */
441
442static void MyReadLocation(MachineLocation *loc)
443{
444 static MachineLocation storedLoc;
445 static Boolean didReadLocation = false;
446
447 if (!didReadLocation) {
448 ReadLocation(&storedLoc);
449 didReadLocation = true;
450 }
451 *loc = storedLoc;
452}
453
454static long GMTDelta(void)
455{
456 MachineLocation loc;
457 long gmtDelta;
458
459 MyReadLocation(&loc);
460 gmtDelta = loc.u.gmtDelta & 0x00ffffff;
461 if (gmtDelta & 0x00800000) { /* test sign extend bit */
462 gmtDelta |= 0xff000000;
463 }
464 return gmtDelta;
465}
466
467static void MacintoshInitializeTime(void)
468{
469 /*
470 * The NSPR epoch is midnight, Jan. 1, 1970 GMT.
471 *
472 * At midnight Jan. 1, 1970 GMT, the local time was
473 * midnight Jan. 1, 1970 + GMTDelta().
474 *
475 * Midnight Jan. 1, 1970 is 86400 * (365 * (1970 - 1904) + 17)
476 * = 2082844800 seconds since the Mac epoch.
477 * (There were 17 leap years from 1904 to 1970.)
478 *
479 * So the NSPR epoch is 2082844800 + GMTDelta() seconds since
480 * the Mac epoch. Whew! :-)
481 */
482 gJanuaryFirst1970Seconds = 2082844800 + GMTDelta();
483}
484
485static nsresult ConvertMacTimeToMilliseconds( PRInt64* aLastModifiedTime, PRUint32 timestamp )
486{
487 if ( gJanuaryFirst1970Seconds == 0)
488 MacintoshInitializeTime();
489 timestamp -= gJanuaryFirst1970Seconds;
490 PRTime usecPerSec, dateInMicroSeconds;
491 LL_I2L(dateInMicroSeconds, timestamp);
492 LL_I2L(usecPerSec, PR_MSEC_PER_SEC);
493 LL_MUL(*aLastModifiedTime, usecPerSec, dateInMicroSeconds);
494 return NS_OK;
495}
496
497static nsresult ConvertMillisecondsToMacTime(PRInt64 aTime, PRUint32 *aOutMacTime)
498{
499 NS_ENSURE_ARG( aOutMacTime );
500
501 PRTime usecPerSec, dateInSeconds;
502 dateInSeconds = LL_ZERO;
503
504 LL_I2L(usecPerSec, PR_MSEC_PER_SEC);
505 LL_DIV(dateInSeconds, aTime, usecPerSec); // dateInSeconds = aTime/1,000
506 LL_L2UI(*aOutMacTime, dateInSeconds);
507 *aOutMacTime += 2082844800; // date + Mac epoch
508
509 return NS_OK;
510}
511
512static void myPLstrcpy(Str255 dst, const char* src)
513{
514 int srcLength = strlen(src);
515 NS_ASSERTION(srcLength <= 255, "Oops, Str255 can't hold >255 chars");
516 if (srcLength > 255)
517 srcLength = 255;
518 dst[0] = srcLength;
519 memcpy(&dst[1], src, srcLength);
520}
521
522static void myPLstrncpy(Str255 dst, const char* src, int inMax)
523{
524 int srcLength = strlen(src);
525 if (srcLength > inMax)
526 srcLength = inMax;
527 dst[0] = srcLength;
528 memcpy(&dst[1], src, srcLength);
529}
530
531/*
532 NS_TruncNodeName
533
534 Utility routine to do a mid-trunc on a potential file name so that it is
535 no longer than 31 characters. Until we move to the HFS+ APIs we need this
536 to come up with legal Mac file names.
537
538 Entry: aNode = initial file name
539 outBuf = scratch buffer for the truncated name (MUST be >= 32 characters)
540
541 Exit: function result = pointer to truncated name. Will be either aNode or outBuf.
542
543*/
544const char* NS_TruncNodeName(const char *aNode, char *outBuf)
545{
546 PRUint32 nodeLen;
547 if ((nodeLen = strlen(aNode)) > 31)
548 {
549 static PRBool sInitialized = PR_FALSE;
550 static CharByteTable sTable;
551 // Init to "..." in case we fail to get the ellipsis token
552 static char sEllipsisTokenStr[4] = { '.', '.', '.', 0 };
553 static PRUint8 sEllipsisTokenLen = 3;
554
555 if (!sInitialized)
556 {
557 // Entries in the table are:
558 // 0 == 1 byte char
559 // 1 == 2 byte char
560 FillParseTable(sTable, smSystemScript);
561
562 Handle itl4ResHandle = nsnull;
563 long offset, len;
564 ::GetIntlResourceTable(smSystemScript, smUnTokenTable, &itl4ResHandle, &offset, &len);
565 if (itl4ResHandle)
566 {
567 UntokenTable *untokenTableRec = (UntokenTable *)(*itl4ResHandle + offset);
568 if (untokenTableRec->lastToken >= tokenEllipsis)
569 {
570 offset += untokenTableRec->index[tokenEllipsis];
571 char *tokenStr = (*itl4ResHandle + offset);
572 sEllipsisTokenLen = tokenStr[0];
573 memcpy(sEllipsisTokenStr, &tokenStr[1], sEllipsisTokenLen);
574 }
575 ::ReleaseResource(itl4ResHandle);
576 }
577 sInitialized = PR_TRUE;
578 }
579
580 PRInt32 halfLen = (31 - sEllipsisTokenLen) / 2;
581 PRInt32 charSize = 0, srcPos, destPos;
582 for (srcPos = 0; srcPos + charSize <= halfLen; srcPos += charSize)
583 charSize = sTable[aNode[srcPos]] ? 2 : 1;
584
585 memcpy(outBuf, aNode, srcPos);
586 memcpy(outBuf + srcPos, sEllipsisTokenStr, sEllipsisTokenLen);
587 destPos = srcPos + sEllipsisTokenLen;
588
589 for (; srcPos < nodeLen - halfLen; srcPos += charSize)
590 charSize = sTable[aNode[srcPos]] ? 2 : 1;
591
592 memcpy(outBuf + destPos, aNode + srcPos, nodeLen - srcPos);
593 destPos += (nodeLen - srcPos);
594 outBuf[destPos] = '\0';
595 return outBuf;
596 }
597 return aNode;
598}
599
600/**
601 * HFSPlusGetRawPath returns the path for an FSSpec as a unicode string.
602 *
603 * The reason for this routine instead of just calling FSRefMakePath is
604 * (1) inSpec does not have to exist
605 * (2) FSRefMakePath uses '/' as the separator under OSX and ':' under OS9
606 */
607static OSErr HFSPlusGetRawPath(const FSSpec& inSpec, nsAString& outStr)
608{
609 OSErr err;
610 nsAutoString ucPathString;
611
612 outStr.Truncate(0);
613
614 FSRef nodeRef;
615 FSCatalogInfo catalogInfo;
616 catalogInfo.parentDirID = 0;
617 err = ::FSpMakeFSRef(&inSpec, &nodeRef);
618
619 if (err == fnfErr) {
620 FSSpec parentDirSpec;
621 err = GetParentFolderSpec(inSpec, parentDirSpec);
622 if (err == noErr) {
623 const char *startPtr = (const char*)&inSpec.name[1];
624 NS_CopyNativeToUnicode(Substring(startPtr, startPtr + PRUint32(inSpec.name[0])), outStr);
625 err = ::FSpMakeFSRef(&parentDirSpec, &nodeRef);
626 }
627 }
628
629 while (err == noErr && catalogInfo.parentDirID != fsRtParID) {
630 HFSUniStr255 nodeName;
631 FSRef parentRef;
632 err = ::FSGetCatalogInfo(&nodeRef,
633 kFSCatInfoNodeFlags + kFSCatInfoParentDirID,
634 &catalogInfo,
635 &nodeName,
636 nsnull,
637 &parentRef);
638 if (err == noErr)
639 {
640 if (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask)
641 nodeName.unicode[nodeName.length++] = PRUnichar(':');
642 const PRUnichar* nodeNameUni = (const PRUnichar*) nodeName.unicode;
643 outStr.Insert(Substring(nodeNameUni, nodeNameUni + nodeName.length), 0);
644 nodeRef = parentRef;
645 }
646 }
647 return err;
648}
649
650
651// The R**co FSSpec resolver -
652// it slices, it dices, it juliannes fries and it even creates FSSpecs out of whatever you feed it
653// This function will take a path and a starting FSSpec and generate a FSSpec to represent
654// the target of the two. If the intial FSSpec is null the path alone will be resolved
655static OSErr ResolvePathAndSpec(const char * filePath, FSSpec *inSpec, PRBool createDirs, FSSpec *outSpec)
656{
657 OSErr err = noErr;
658 size_t inLength = strlen(filePath);
659 Boolean isRelative = (filePath && inSpec);
660 FSSpec tempSpec;
661 Str255 ppath;
662 Boolean isDirectory;
663
664 if (isRelative && inSpec)
665 {
666 outSpec->vRefNum = inSpec->vRefNum;
667 outSpec->parID = inSpec->parID;
668
669 if (inSpec->name[0] != 0)
670 {
671 long theDirID;
672
673 err = FSpGetDirectoryID(inSpec, &theDirID, &isDirectory);
674
675 if (err == noErr && isDirectory)
676 outSpec->parID = theDirID;
677 else if (err == fnfErr && createDirs)
678 {
679 err = FSpDirCreate(inSpec, smCurrentScript, &theDirID);
680 if (err == noErr)
681 outSpec->parID = theDirID;
682 else if (err == fnfErr)
683 err = dirNFErr;
684 }
685 }
686 }
687 else
688 {
689 outSpec->vRefNum = 0;
690 outSpec->parID = 0;
691 }
692
693 if (err)
694 return err;
695
696 // Try making an FSSpec from the path
697 if (inLength < 255)
698 {
699 // Use tempSpec as dest because if FSMakeFSSpec returns dirNFErr, it
700 // will reset the dest spec and we'll lose what we determined above.
701
702 myPLstrcpy(ppath, filePath);
703 err = ::FSMakeFSSpec(outSpec->vRefNum, outSpec->parID, ppath, &tempSpec);
704 if (err == noErr || err == fnfErr)
705 *outSpec = tempSpec;
706 }
707 else if (!isRelative)
708 {
709 err = ::FSpLocationFromFullPath(inLength, filePath, outSpec);
710 }
711 else
712 { // If the path is relative and >255 characters we need to manually walk the
713 // path appending each node to the initial FSSpec so to reach that code we
714 // set the err to bdNamErr and fall into the code below
715 err = bdNamErr;
716 }
717
718 // If we successfully created a spec then leave
719 if (err == noErr)
720 return err;
721
722 // We get here when the directory hierarchy needs to be created or we're resolving
723 // a relative path >255 characters long
724 if (err == dirNFErr || err == bdNamErr)
725 {
726 const char* path = filePath;
727
728 if (!isRelative) // If path is relative, we still need vRefNum & parID.
729 {
730 outSpec->vRefNum = 0;
731 outSpec->parID = 0;
732 }
733
734 do
735 {
736 // Locate the colon that terminates the node.
737 // But if we've a partial path (starting with a colon), find the second one.
738 const char* nextColon = strchr(path + (*path == ':'), ':');
739 // Well, if there are no more colons, point to the end of the string.
740 if (!nextColon)
741 nextColon = path + strlen(path);
742
743 // Make a pascal string out of this node. Include initial
744 // and final colon, if any!
745 myPLstrncpy(ppath, path, nextColon - path + 1);
746
747 // Use this string as a relative path using the directory created
748 // on the previous round (or directory 0,0 on the first round).
749 err = ::FSMakeFSSpec(outSpec->vRefNum, outSpec->parID, ppath, outSpec);
750
751 // If this was the leaf node, then we are done.
752 if (!*nextColon)
753 break;
754
755 // Since there's more to go, we have to get the directory ID, which becomes
756 // the parID for the next round.
757 if (err == noErr)
758 {
759 // The directory (or perhaps a file) exists. Find its dirID.
760 long dirID;
761 err = ::FSpGetDirectoryID(outSpec, &dirID, &isDirectory);
762 if (!isDirectory)
763 err = dupFNErr; // oops! a file exists with that name.
764 if (err != noErr)
765 break; // bail if we've got an error
766 outSpec->parID = dirID;
767 }
768 else if ((err == fnfErr) && createDirs)
769 {
770 // If we got "file not found" and we're allowed to create directories
771 // then we need to create one
772 err = ::FSpDirCreate(outSpec, smCurrentScript, &outSpec->parID);
773 // For some reason, this usually returns fnfErr, even though it works.
774 if (err == fnfErr)
775 err = noErr;
776 }
777 if (err != noErr)
778 break;
779 path = nextColon; // next round
780 } while (true);
781 }
782
783 return err;
784}
785
786
787#pragma mark -
788#pragma mark [StFollowLinksState]
789class StFollowLinksState
790{
791 public:
792 StFollowLinksState(nsILocalFile *aFile) :
793 mFile(aFile)
794 {
795 NS_ASSERTION(mFile, "StFollowLinksState passed a NULL file.");
796 if (mFile)
797 mFile->GetFollowLinks(&mSavedState);
798 }
799
800 StFollowLinksState(nsILocalFile *aFile, PRBool followLinksState) :
801 mFile(aFile)
802 {
803 NS_ASSERTION(mFile, "StFollowLinksState passed a NULL file.");
804 if (mFile) {
805 mFile->GetFollowLinks(&mSavedState);
806 mFile->SetFollowLinks(followLinksState);
807 }
808 }
809
810 ~StFollowLinksState()
811 {
812 if (mFile)
813 mFile->SetFollowLinks(mSavedState);
814 }
815
816 private:
817 nsCOMPtr<nsILocalFile> mFile;
818 PRBool mSavedState;
819};
820
821#pragma mark -
822#pragma mark [nsDirEnumerator]
823class nsDirEnumerator : public nsISimpleEnumerator
824{
825 public:
826
827 NS_DECL_ISUPPORTS
828
829 nsDirEnumerator()
830 {
831 }
832
833 nsresult Init(nsILocalFileMac* parent)
834 {
835 NS_ENSURE_ARG(parent);
836 nsresult rv;
837 FSSpec fileSpec;
838
839 rv = parent->GetFSSpec(&fileSpec);
840 if (NS_FAILED(rv))
841 return rv;
842
843 OSErr err;
844 Boolean isDirectory;
845
846 err = ::FSpGetDirectoryID(&fileSpec, &mDirID, &isDirectory);
847 if (err || !isDirectory)
848 return NS_ERROR_FILE_NOT_DIRECTORY;
849
850 mCatInfo.hFileInfo.ioNamePtr = mItemName;
851 mCatInfo.hFileInfo.ioVRefNum = fileSpec.vRefNum;
852 mItemIndex = 1;
853
854 return NS_OK;
855 }
856
857 NS_IMETHOD HasMoreElements(PRBool *result)
858 {
859 nsresult rv = NS_OK;
860 if (mNext == nsnull)
861 {
862 mItemName[0] = 0;
863 mCatInfo.dirInfo.ioFDirIndex = mItemIndex;
864 mCatInfo.dirInfo.ioDrDirID = mDirID;
865
866 OSErr err = ::PBGetCatInfoSync(&mCatInfo);
867 if (err == fnfErr)
868 {
869 // end of dir entries
870 *result = PR_FALSE;
871 return NS_OK;
872 }
873
874 // Make a new nsILocalFile for the new element
875 FSSpec tempSpec;
876 tempSpec.vRefNum = mCatInfo.hFileInfo.ioVRefNum;
877 tempSpec.parID = mDirID;
878 ::BlockMoveData(mItemName, tempSpec.name, mItemName[0] + 1);
879
880 rv = NS_NewLocalFileWithFSSpec(&tempSpec, PR_TRUE, getter_AddRefs(mNext));
881 if (NS_FAILED(rv))
882 return rv;
883 }
884 *result = mNext != nsnull;
885 return NS_OK;
886 }
887
888 NS_IMETHOD GetNext(nsISupports **result)
889 {
890 NS_ENSURE_ARG_POINTER(result);
891 *result = nsnull;
892
893 nsresult rv;
894 PRBool hasMore;
895 rv = HasMoreElements(&hasMore);
896 if (NS_FAILED(rv)) return rv;
897
898 *result = mNext; // might return nsnull
899 NS_IF_ADDREF(*result);
900
901 mNext = nsnull;
902 ++mItemIndex;
903 return NS_OK;
904 }
905
906 private:
907 ~nsDirEnumerator() {}
908
909 protected:
910 nsCOMPtr<nsILocalFileMac> mNext;
911
912 CInfoPBRec mCatInfo;
913 short mItemIndex;
914 long mDirID;
915 Str63 mItemName;
916};
917
918NS_IMPL_ISUPPORTS1(nsDirEnumerator, nsISimpleEnumerator)
919
920#pragma mark -
921
922OSType nsLocalFile::sCurrentProcessSignature = 0;
923PRBool nsLocalFile::sHasHFSPlusAPIs = PR_FALSE;
924PRBool nsLocalFile::sRunningOSX = PR_FALSE;
925
926#pragma mark [CTOR/DTOR]
927nsLocalFile::nsLocalFile() :
928 mFollowLinks(PR_TRUE),
929 mFollowLinksDirty(PR_TRUE),
930 mSpecDirty(PR_TRUE),
931 mCatInfoDirty(PR_TRUE),
932 mType('TEXT'),
933 mCreator(kDefaultCreator)
934{
935 ClearFSSpec(mSpec);
936 ClearFSSpec(mTargetSpec);
937
938 InitClassStatics();
939 if (sCurrentProcessSignature != 0)
940 mCreator = sCurrentProcessSignature;
941}
942
943nsLocalFile::nsLocalFile(const nsLocalFile& srcFile)
944{
945 *this = srcFile;
946}
947
948nsLocalFile::nsLocalFile(const FSSpec& aSpec, const nsACString& aAppendedPath) :
949 mFollowLinks(PR_TRUE),
950 mFollowLinksDirty(PR_TRUE),
951 mSpecDirty(PR_TRUE),
952 mSpec(aSpec),
953 mAppendedPath(aAppendedPath),
954 mCatInfoDirty(PR_TRUE),
955 mType('TEXT'),
956 mCreator(kDefaultCreator)
957{
958 ClearFSSpec(mTargetSpec);
959
960 InitClassStatics();
961 if (sCurrentProcessSignature != 0)
962 mCreator = sCurrentProcessSignature;
963}
964
965nsLocalFile& nsLocalFile::operator=(const nsLocalFile& rhs)
966{
967 mFollowLinks = rhs.mFollowLinks;
968 mFollowLinksDirty = rhs.mFollowLinksDirty;
969 mSpecDirty = rhs.mSpecDirty;
970 mSpec = rhs.mSpec;
971 mAppendedPath = rhs.mAppendedPath;
972 mTargetSpec = rhs.mTargetSpec;
973 mCatInfoDirty = rhs.mCatInfoDirty;
974 mType = rhs.mType;
975 mCreator = rhs.mCreator;
976
977 if (!rhs.mCatInfoDirty)
978 mCachedCatInfo = rhs.mCachedCatInfo;
979
980 return *this;
981}
982
983#pragma mark -
984#pragma mark [nsISupports interface implementation]
985
986NS_IMPL_THREADSAFE_ISUPPORTS3(nsLocalFile,
987 nsILocalFileMac,
988 nsILocalFile,
989 nsIFile)
990
991NS_METHOD
992nsLocalFile::nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
993{
994 NS_ENSURE_ARG_POINTER(aInstancePtr);
995 NS_ENSURE_NO_AGGREGATION(outer);
996
997 nsLocalFile* inst = new nsLocalFile();
998 if (inst == NULL)
999 return NS_ERROR_OUT_OF_MEMORY;
1000
1001 nsresult rv = inst->QueryInterface(aIID, aInstancePtr);
1002 if (NS_FAILED(rv))
1003 {
1004 delete inst;
1005 return rv;
1006 }
1007 return NS_OK;
1008}
1009
1010// This function resets any cached information about the file.
1011void
1012nsLocalFile::MakeDirty()
1013{
1014 mSpecDirty = PR_TRUE;
1015 mFollowLinksDirty = PR_TRUE;
1016 mCatInfoDirty = PR_TRUE;
1017
1018 ClearFSSpec(mTargetSpec);
1019}
1020
1021
1022/* attribute PRBool followLinks; */
1023NS_IMETHODIMP
1024nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
1025{
1026 NS_ENSURE_ARG_POINTER(aFollowLinks);
1027 *aFollowLinks = mFollowLinks;
1028 return NS_OK;
1029}
1030
1031NS_IMETHODIMP
1032nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
1033{
1034 if (aFollowLinks != mFollowLinks)
1035 {
1036 mFollowLinks = aFollowLinks;
1037 mFollowLinksDirty = PR_TRUE;
1038 }
1039 return NS_OK;
1040}
1041
1042NS_IMETHODIMP
1043nsLocalFile::ResolveAndStat()
1044{
1045 OSErr err = noErr;
1046 FSSpec resolvedSpec;
1047
1048 // fnfErr means target spec is valid but doesn't exist.
1049 // If the end result is fnfErr, we're cleanly resolved.
1050
1051 if (mSpecDirty)
1052 {
1053 if (mAppendedPath.Length())
1054 {
1055 err = ResolvePathAndSpec(mAppendedPath.get(), &mSpec, PR_FALSE, &resolvedSpec);
1056 if (err == noErr)
1057 mAppendedPath.Truncate(0);
1058 }
1059 else
1060 err = ::FSMakeFSSpec(mSpec.vRefNum, mSpec.parID, mSpec.name, &resolvedSpec);
1061 if (err == noErr)
1062 {
1063 mSpec = resolvedSpec;
1064 mSpecDirty = PR_FALSE;
1065 }
1066 mFollowLinksDirty = PR_TRUE;
1067 }
1068 if (mFollowLinksDirty && (err == noErr))
1069 {
1070 if (mFollowLinks)
1071 {
1072 // Resolve the alias to the original file.
1073 resolvedSpec = mSpec;
1074 Boolean resolvedWasFolder, resolvedWasAlias;
1075 err = ::ResolveAliasFile(&resolvedSpec, TRUE, &resolvedWasFolder, &resolvedWasAlias);
1076 if (err == noErr || err == fnfErr) {
1077 err = noErr;
1078 mTargetSpec = resolvedSpec;
1079 mFollowLinksDirty = PR_FALSE;
1080 }
1081 }
1082 else
1083 {
1084 mTargetSpec = mSpec;
1085 mFollowLinksDirty = PR_FALSE;
1086 }
1087 mCatInfoDirty = PR_TRUE;
1088 }
1089
1090 return (MacErrorMapper(err));
1091}
1092
1093
1094NS_IMETHODIMP
1095nsLocalFile::Clone(nsIFile **file)
1096{
1097 // Just copy-construct ourselves
1098 *file = new nsLocalFile(*this);
1099 if (!*file)
1100 return NS_ERROR_OUT_OF_MEMORY;
1101
1102 NS_ADDREF(*file);
1103
1104 return NS_OK;
1105}
1106
1107NS_IMETHODIMP
1108nsLocalFile::InitWithNativePath(const nsACString &filePath)
1109{
1110 // The incoming path must be a FULL path
1111
1112 if (filePath.IsEmpty())
1113 return NS_ERROR_INVALID_ARG;
1114
1115 MakeDirty();
1116
1117 // If it starts with a colon, it's invalid
1118 if (filePath.First() == ':')
1119 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
1120
1121 nsPathParser parser(filePath);
1122 OSErr err;
1123 Str255 pascalNode;
1124 FSSpec nodeSpec;
1125
1126 const char *root = parser.First();
1127 if (root == nsnull)
1128 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
1129
1130 // The first component must be an existing volume
1131 myPLstrcpy(pascalNode, root);
1132 pascalNode[++pascalNode[0]] = ':';
1133 err = ::FSMakeFSSpec(0, 0, pascalNode, &nodeSpec);
1134 if (err)
1135 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
1136
1137 // Build as much of a spec as possible from the rest of the path
1138 // What doesn't exist will be left over in mAppendedPath
1139 const char *nextNode;
1140 while ((nextNode = parser.Next()) != nsnull) {
1141 long dirID;
1142 Boolean isDir;
1143 err = ::FSpGetDirectoryID(&nodeSpec, &dirID, &isDir);
1144 if (err || !isDir)
1145 break;
1146 myPLstrcpy(pascalNode, nextNode);
1147 err = ::FSMakeFSSpec(nodeSpec.vRefNum, dirID, pascalNode, &nodeSpec);
1148 if (err == fnfErr)
1149 break;
1150 }
1151 mSpec = nodeSpec;
1152 mAppendedPath = parser.Remainder();
1153
1154 return NS_OK;
1155}
1156
1157NS_IMETHODIMP
1158nsLocalFile::InitWithPath(const nsAString &filePath)
1159{
1160 nsresult rv;
1161 nsCAutoString fsStr;
1162
1163 if (NS_SUCCEEDED(rv = NS_CopyUnicodeToNative(filePath, fsStr)))
1164 rv = InitWithNativePath(fsStr);
1165
1166 return rv;
1167}
1168
1169NS_IMETHODIMP
1170nsLocalFile::InitWithFile(nsILocalFile *aFile)
1171{
1172 NS_ENSURE_ARG(aFile);
1173 nsLocalFile *asLocalFile = dynamic_cast<nsLocalFile*>(aFile);
1174 if (!asLocalFile)
1175 return NS_ERROR_NO_INTERFACE; // Well, sort of.
1176 *this = *asLocalFile;
1177 return NS_OK;
1178}
1179
1180NS_IMETHODIMP
1181nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval)
1182{
1183// Macintosh doesn't really have mode bits, just drop them
1184#pragma unused (mode)
1185
1186 NS_ENSURE_ARG(_retval);
1187
1188 nsresult rv = NS_OK;
1189 FSSpec spec;
1190 OSErr err = noErr;
1191
1192 rv = ResolveAndStat();
1193 if (rv == NS_ERROR_FILE_NOT_FOUND && (flags & PR_CREATE_FILE))
1194 rv = NS_OK;
1195
1196 if (flags & PR_CREATE_FILE) {
1197 rv = Create(nsIFile::NORMAL_FILE_TYPE, 0);
1198 /* If opening with the PR_EXCL flag the existence of the file prior to opening is an error */
1199 if ((flags & PR_EXCL) && (rv == NS_ERROR_FILE_ALREADY_EXISTS))
1200 return rv;
1201 }
1202
1203 rv = GetFSSpec(&spec);
1204 if (NS_FAILED(rv))
1205 return rv;
1206
1207 SInt8 perm;
1208 if (flags & PR_RDWR)
1209 perm = fsRdWrPerm;
1210 else if (flags & PR_WRONLY)
1211 perm = fsWrPerm;
1212 else
1213 perm = fsRdPerm;
1214
1215 short refnum;
1216 err = ::FSpOpenDF(&spec, perm, &refnum);
1217
1218 if (err == noErr && (flags & PR_TRUNCATE))
1219 err = ::SetEOF(refnum, 0);
1220 if (err == noErr && (flags & PR_APPEND))
1221 err = ::SetFPos(refnum, fsFromLEOF, 0);
1222 if (err != noErr)
1223 return MacErrorMapper(err);
1224
1225 if ((*_retval = PR_ImportFile(refnum)) == 0)
1226 return NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES,(PR_GetError() & 0xFFFF));
1227
1228 return NS_OK;
1229}
1230
1231NS_IMETHODIMP
1232nsLocalFile::OpenANSIFileDesc(const char *mode, FILE * *_retval)
1233{
1234 NS_ENSURE_ARG(mode);
1235 NS_ENSURE_ARG_POINTER(_retval);
1236
1237 nsresult rv;
1238 FSSpec spec;
1239
1240 rv = ResolveAndStat();
1241 if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
1242 return rv;
1243
1244 if (mode[0] == 'w' || mode[0] == 'a') // Create if it doesn't exist
1245 {
1246 if (rv == NS_ERROR_FILE_NOT_FOUND) {
1247 mType = (mode[1] == 'b') ? 'BiNA' : 'TEXT';
1248 rv = Create(nsIFile::NORMAL_FILE_TYPE, 0);
1249 if (NS_FAILED(rv))
1250 return rv;
1251 }
1252 }
1253
1254 rv = GetFSSpec(&spec);
1255 if (NS_FAILED(rv))
1256 return rv;
1257
1258#ifdef MACOSX
1259 // FSp_fopen() doesn't exist under macosx :-(
1260 nsXPIDLCString ourPath;
1261 rv = GetPath(getter_Copies(ourPath));
1262 if (NS_FAILED(rv))
1263 return rv;
1264 *_retval = fopen(ourPath, mode);
1265#else
1266 *_retval = FSp_fopen(&spec, mode);
1267#endif
1268
1269 if (*_retval)
1270 return NS_OK;
1271
1272 return NS_ERROR_FAILURE;
1273}
1274
1275
1276NS_IMETHODIMP
1277nsLocalFile::Create(PRUint32 type, PRUint32 attributes)
1278{
1279 OSErr err;
1280
1281 if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
1282 return NS_ERROR_FILE_UNKNOWN_TYPE;
1283
1284 FSSpec newSpec;
1285
1286 if (mAppendedPath.Length())
1287 { // We've got an FSSpec and an appended path so pass 'em both to ResolvePathAndSpec
1288 err = ResolvePathAndSpec(mAppendedPath.get(), &mSpec, PR_TRUE, &newSpec);
1289 }
1290 else
1291 {
1292 err = ::FSMakeFSSpec(mSpec.vRefNum, mSpec.parID, mSpec.name, &newSpec);
1293 }
1294
1295 if (err != noErr && err != fnfErr)
1296 return (MacErrorMapper(err));
1297
1298 switch (type)
1299 {
1300 case NORMAL_FILE_TYPE:
1301 SetOSTypeAndCreatorFromExtension();
1302 err = ::FSpCreate(&newSpec, mCreator, mType, smCurrentScript);
1303 break;
1304
1305 case DIRECTORY_TYPE:
1306 {
1307 long newDirID;
1308 err = ::FSpDirCreate(&newSpec, smCurrentScript, &newDirID);
1309 // For some reason, this usually returns fnfErr, even though it works.
1310 if (err == fnfErr)
1311 err = noErr;
1312 }
1313 break;
1314
1315 default:
1316 return NS_ERROR_FILE_UNKNOWN_TYPE;
1317 break;
1318 }
1319
1320 if (err == noErr)
1321 {
1322 mSpec = mTargetSpec = newSpec;
1323 mAppendedPath.Truncate(0);
1324 }
1325
1326 return (MacErrorMapper(err));
1327}
1328
1329NS_IMETHODIMP
1330nsLocalFile::AppendNative(const nsACString &aNode)
1331{
1332 if (aNode.IsEmpty())
1333 return NS_OK;
1334
1335 nsACString::const_iterator start, end;
1336 aNode.BeginReading(start);
1337 aNode.EndReading(end);
1338 if (FindCharInReadable(':', start, end))
1339 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
1340
1341 MakeDirty();
1342
1343 char truncBuffer[32];
1344 const char *node = NS_TruncNodeName(PromiseFlatCString(aNode).get(), truncBuffer);
1345
1346 if (!mAppendedPath.Length())
1347 {
1348 OSErr err;
1349 Boolean resolvedWasFolder, resolvedWasAlias;
1350 err = ::ResolveAliasFile(&mSpec, TRUE, &resolvedWasFolder, &resolvedWasAlias);
1351 if (err == noErr)
1352 {
1353 long dirID;
1354 Boolean isDir;
1355
1356 if (!resolvedWasFolder)
1357 return NS_ERROR_FILE_NOT_DIRECTORY;
1358 if ((err = ::FSpGetDirectoryID(&mSpec, &dirID, &isDir)) != noErr)
1359 return MacErrorMapper(err);
1360
1361 FSSpec childSpec;
1362 Str255 pascalNode;
1363 myPLstrcpy(pascalNode, node);
1364 err = ::FSMakeFSSpec(mSpec.vRefNum, dirID, pascalNode, &childSpec);
1365 if (err && err != fnfErr)
1366 return MacErrorMapper(err);
1367 mSpec = childSpec;
1368 }
1369 else if (err == fnfErr)
1370 mAppendedPath.Assign(node);
1371 else
1372 return MacErrorMapper(err);
1373 }
1374 else
1375 {
1376 if (mAppendedPath.First() != ':')
1377 mAppendedPath.Insert(':', 0);
1378 mAppendedPath.Append(":");
1379 mAppendedPath.Append(node);
1380 }
1381
1382 return NS_OK;
1383}
1384
1385NS_IMETHODIMP
1386nsLocalFile::Append(const nsAString &node)
1387{
1388 nsresult rv;
1389 nsCAutoString fsStr;
1390
1391 if (NS_SUCCEEDED(rv = NS_CopyUnicodeToNative(node, fsStr)))
1392 rv = AppendNative(fsStr);
1393
1394 return rv;
1395}
1396
1397NS_IMETHODIMP
1398nsLocalFile::AppendRelativeNativePath(const nsACString &relPath)
1399{
1400 if (relPath.IsEmpty())
1401 return NS_ERROR_INVALID_ARG;
1402
1403 nsresult rv;
1404 nsPathParser parser(relPath);
1405 const char* node = parser.First();
1406
1407 while (node)
1408 {
1409 if (NS_FAILED(rv = AppendNative(nsDependentCString(node))))
1410 return rv;
1411 node = parser.Next();
1412 }
1413
1414 return NS_OK;
1415}
1416
1417NS_IMETHODIMP
1418nsLocalFile::AppendRelativePath(const nsAString &relPath)
1419{
1420 nsresult rv;
1421 nsCAutoString fsStr;
1422
1423 if (NS_SUCCEEDED(rv = NS_CopyUnicodeToNative(relPath, fsStr)))
1424 rv = AppendRelativeNativePath(fsStr);
1425
1426 return rv;
1427}
1428
1429NS_IMETHODIMP
1430nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
1431{
1432 aLeafName.Truncate();
1433
1434 // See if we've had a path appended
1435 if (mAppendedPath.Length())
1436 {
1437 const char* temp = mAppendedPath.get();
1438 if (temp == nsnull)
1439 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
1440
1441 const char* leaf = strrchr(temp, ':');
1442
1443 // if the working path is just a node without any directory delimeters.
1444 if (leaf == nsnull)
1445 leaf = temp;
1446 else
1447 leaf++;
1448
1449 aLeafName = leaf;
1450 }
1451 else
1452 {
1453 // We don't have an appended path so grab the leaf name from the FSSpec
1454 // Convert the Pascal string to a C string
1455 PRInt32 len = mSpec.name[0];
1456 char* leafName = (char *)malloc(len + 1);
1457 if (!leafName) return NS_ERROR_OUT_OF_MEMORY;
1458 ::BlockMoveData(&mSpec.name[1], leafName, len);
1459 leafName[len] = '\0';
1460 aLeafName = leafName;
1461 free(leafName);
1462 }
1463
1464 return NS_OK;
1465}
1466
1467NS_IMETHODIMP
1468nsLocalFile::GetLeafName(nsAString &aLeafName)
1469{
1470 nsresult rv;
1471 nsCAutoString fsStr;
1472
1473 if (NS_SUCCEEDED(rv = GetNativeLeafName(fsStr)))
1474 rv = NS_CopyNativeToUnicode(fsStr, aLeafName);
1475 return rv;
1476}
1477
1478NS_IMETHODIMP
1479nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
1480{
1481 if (aLeafName.IsEmpty())
1482 return NS_ERROR_INVALID_ARG;
1483
1484 MakeDirty();
1485
1486 char truncBuffer[32];
1487 const char *leafName = NS_TruncNodeName(PromiseFlatCString(aLeafName).get(), truncBuffer);
1488
1489 if (mAppendedPath.Length())
1490 { // Lop off the end of the appended path and replace it with the new leaf name
1491 PRInt32 offset = mAppendedPath.RFindChar(':');
1492 if (offset || ((!offset) && (1 < mAppendedPath.Length())))
1493 {
1494 mAppendedPath.Truncate(offset + 1);
1495 }
1496 mAppendedPath.Append(leafName);
1497 }
1498 else
1499 {
1500 // We don't have an appended path so directly modify the FSSpec
1501 myPLstrcpy(mSpec.name, leafName);
1502 }
1503
1504 return NS_OK;
1505}
1506
1507NS_IMETHODIMP
1508nsLocalFile::SetLeafName(const nsAString &aLeafName)
1509{
1510 nsresult rv;
1511 nsCAutoString fsStr;
1512
1513 if (NS_SUCCEEDED(rv = NS_CopyUnicodeToNative(aLeafName, fsStr)))
1514 rv = SetNativeLeafName(fsStr);
1515
1516 return rv;
1517}
1518
1519NS_IMETHODIMP
1520nsLocalFile::GetNativePath(nsACString &_retval)
1521{
1522 _retval.Truncate();
1523
1524 nsCAutoString fsCharSetPathStr;
1525
1526#if TARGET_CARBON
1527 if (sHasHFSPlusAPIs) // should always be true under Carbon, but in case...
1528 {
1529 OSErr err;
1530 nsresult rv;
1531 nsAutoString ucPathString;
1532
1533 if ((err = HFSPlusGetRawPath(mSpec, ucPathString)) != noErr)
1534 return MacErrorMapper(err);
1535 rv = NS_CopyUnicodeToNative(ucPathString, fsCharSetPathStr);
1536 if (NS_FAILED(rv))
1537 return rv;
1538 }
1539 else
1540#endif
1541 {
1542 // Now would be a good time to call the code that makes an FSSpec into a path
1543 short fullPathLen;
1544 Handle fullPathHandle;
1545 (void)::FSpGetFullPath(&mSpec, &fullPathLen, &fullPathHandle);
1546 if (!fullPathHandle)
1547 return NS_ERROR_OUT_OF_MEMORY;
1548
1549 ::HLock(fullPathHandle);
1550 fsCharSetPathStr.Assign(*fullPathHandle, fullPathLen);
1551 ::DisposeHandle(fullPathHandle);
1552 }
1553
1554 // We need to make sure that even if we have a path to a
1555 // directory we don't return the trailing colon. It breaks
1556 // the component manager. (Bugzilla bug #26102)
1557 if (fsCharSetPathStr.Last() == ':')
1558 fsCharSetPathStr.Truncate(fsCharSetPathStr.Length() - 1);
1559
1560 // Now, tack on mAppendedPath. It never ends in a colon.
1561 if (mAppendedPath.Length())
1562 {
1563 if (mAppendedPath.First() != ':')
1564 fsCharSetPathStr.Append(":");
1565 fsCharSetPathStr.Append(mAppendedPath);
1566 }
1567
1568 _retval = fsCharSetPathStr;
1569 return NS_OK;
1570}
1571
1572NS_IMETHODIMP
1573nsLocalFile::GetPath(nsAString &_retval)
1574{
1575 nsresult rv = NS_OK;
1576
1577#if TARGET_CARBON
1578 if (sHasHFSPlusAPIs) // should always be true under Carbon, but in case...
1579 {
1580 OSErr err;
1581 nsAutoString ucPathString;
1582
1583 if ((err = HFSPlusGetRawPath(mSpec, ucPathString)) != noErr)
1584 return MacErrorMapper(err);
1585
1586 // We need to make sure that even if we have a path to a
1587 // directory we don't return the trailing colon. It breaks
1588 // the component manager. (Bugzilla bug #26102)
1589 if (ucPathString.Last() == PRUnichar(':'))
1590 ucPathString.Truncate(ucPathString.Length() - 1);
1591
1592 // Now, tack on mAppendedPath. It never ends in a colon.
1593 if (mAppendedPath.Length())
1594 {
1595 nsAutoString ucAppendage;
1596 if (mAppendedPath.First() != ':')
1597 ucPathString.Append(PRUnichar(':'));
1598 rv = NS_CopyNativeToUnicode(mAppendedPath, ucAppendage);
1599 if (NS_FAILED(rv))
1600 return rv;
1601 ucPathString.Append(ucAppendage);
1602 }
1603
1604 _retval = ucPathString;
1605 }
1606 else
1607#endif
1608 {
1609 nsCAutoString fsStr;
1610
1611 if (NS_SUCCEEDED(rv = GetNativePath(fsStr))) {
1612 rv = NS_CopyNativeToUnicode(fsStr, _retval);
1613 }
1614 }
1615 return rv;
1616}
1617
1618nsresult nsLocalFile::MoveCopy( nsIFile* newParentDir, const nsACString &newName, PRBool isCopy, PRBool followLinks )
1619{
1620 OSErr macErr;
1621 FSSpec srcSpec;
1622 Str255 newPascalName;
1623 nsresult rv;
1624
1625 StFollowLinksState srcFollowState(this, followLinks);
1626 rv = GetFSSpec(&srcSpec);
1627 if ( NS_FAILED( rv ) )
1628 return rv;
1629
1630 // If newParentDir == nsnull, it's a simple rename
1631 if ( !newParentDir )
1632 {
1633 myPLstrncpy( newPascalName, PromiseFlatCString(newName).get(), 255 );
1634 macErr = ::FSpRename( &srcSpec, newPascalName );
1635 return MacErrorMapper( macErr );
1636 }
1637
1638 nsCOMPtr<nsILocalFileMac> destDir(do_QueryInterface( newParentDir ));
1639 StFollowLinksState destFollowState(destDir, followLinks);
1640 FSSpec destSpec;
1641 rv = destDir->GetFSSpec(&destSpec);
1642 if ( NS_FAILED( rv ) )
1643 return rv;
1644
1645 long dirID;
1646 Boolean isDirectory;
1647 macErr = ::FSpGetDirectoryID(&destSpec, &dirID, &isDirectory);
1648 if ( macErr || !isDirectory )
1649 return NS_ERROR_FILE_DESTINATION_NOT_DIR;
1650
1651 if ( !newName.IsEmpty() )
1652 myPLstrncpy( newPascalName, PromiseFlatCString(newName).get(), 255);
1653 else
1654 memcpy(newPascalName, srcSpec.name, srcSpec.name[0] + 1);
1655 if ( isCopy )
1656 {
1657 macErr = ::FSpGetDirectoryID(&srcSpec, &dirID, &isDirectory);
1658 if (macErr == noErr)
1659 {
1660 const PRInt32 kCopyBufferSize = (1024 * 512); // allocate our own buffer to speed file copies. Bug #103202
1661 OSErr tempErr;
1662 Handle copyBufferHand = ::TempNewHandle(kCopyBufferSize, &tempErr);
1663 void* copyBuffer = nsnull;
1664 PRInt32 copyBufferSize = 0;
1665
1666 // it's OK if the allocated failed; FSpFileCopy will just fall back on its own internal 16k buffer
1667 if (copyBufferHand)
1668 {
1669 ::HLock(copyBufferHand);
1670 copyBuffer = *copyBufferHand;
1671 copyBufferSize = kCopyBufferSize;
1672 }
1673
1674 if ( isDirectory )
1675 macErr = MacFSpDirectoryCopyRename( &srcSpec, &destSpec, newPascalName, copyBuffer, copyBufferSize, true, NULL );
1676 else
1677 macErr = ::FSpFileCopy( &srcSpec, &destSpec, newPascalName, copyBuffer, copyBufferSize, true );
1678
1679 if (copyBufferHand)
1680 ::DisposeHandle(copyBufferHand);
1681 }
1682 }
1683 else
1684 {
1685 macErr= ::FSpMoveRenameCompat(&srcSpec, &destSpec, newPascalName);
1686 if ( macErr == diffVolErr)
1687 {
1688 // On a different Volume so go for Copy and then delete
1689 rv = CopyToNative( newParentDir, newName );
1690 if ( NS_FAILED ( rv ) )
1691 return rv;
1692 return Remove( PR_TRUE );
1693 }
1694 }
1695 return MacErrorMapper( macErr );
1696}
1697
1698NS_IMETHODIMP
1699nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString &newName)
1700{
1701 return MoveCopy( newParentDir, newName, PR_TRUE, PR_FALSE );
1702}
1703
1704NS_IMETHODIMP
1705nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
1706{
1707 if (newName.IsEmpty())
1708 return CopyToNative(newParentDir, nsCString());
1709
1710 nsresult rv;
1711 nsCAutoString fsStr;
1712 if (NS_SUCCEEDED(rv = NS_CopyUnicodeToNative(newName, fsStr)))
1713 rv = CopyToNative(newParentDir, fsStr);
1714 return rv;
1715}
1716
1717NS_IMETHODIMP
1718nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString &newName)
1719{
1720 return MoveCopy( newParentDir, newName, PR_TRUE, PR_TRUE );
1721}
1722
1723NS_IMETHODIMP
1724nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
1725{
1726 if (newName.IsEmpty())
1727 return CopyToFollowingLinksNative(newParentDir, nsCString());
1728
1729 nsresult rv;
1730 nsCAutoString fsStr;
1731 if (NS_SUCCEEDED(rv = NS_CopyUnicodeToNative(newName, fsStr)))
1732 rv = CopyToFollowingLinksNative(newParentDir, fsStr);
1733 return rv;
1734}
1735
1736NS_IMETHODIMP
1737nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString &newName)
1738{
1739 return MoveCopy( newParentDir, newName, PR_FALSE, PR_FALSE );
1740}
1741
1742NS_IMETHODIMP
1743nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
1744{
1745 if (newName.IsEmpty())
1746 return MoveToNative(newParentDir, nsCString());
1747
1748 nsresult rv;
1749 nsCAutoString fsStr;
1750 if (NS_SUCCEEDED(rv = NS_CopyUnicodeToNative(newName, fsStr)))
1751 rv = MoveToNative(newParentDir, fsStr);
1752 return rv;
1753}
1754
1755NS_IMETHODIMP
1756nsLocalFile::Load(PRLibrary * *_retval)
1757{
1758 PRBool isFile;
1759 nsresult rv = IsFile(&isFile);
1760
1761 if (NS_FAILED(rv))
1762 return rv;
1763
1764 if (! isFile)
1765 return NS_ERROR_FILE_IS_DIRECTORY;
1766
1767 NS_TIMELINE_START_TIMER("PR_LoadLibrary");
1768
1769#if !TARGET_CARBON
1770 // This call to SystemTask is here to give the OS time to grow its
1771 // FCB (file control block) list, which it seems to be unable to
1772 // do unless we yield some time to the OS. See bugs 64978 & 70543
1773 // for the whole story.
1774 ::SystemTask();
1775#endif
1776
1777 // Use the new PR_LoadLibraryWithFlags which allows us to use a FSSpec
1778 PRLibSpec libSpec;
1779 libSpec.type = PR_LibSpec_MacIndexedFragment;
1780 libSpec.value.mac_indexed_fragment.fsspec = &mTargetSpec;
1781 libSpec.value.mac_indexed_fragment.index = 0;
1782 *_retval = PR_LoadLibraryWithFlags(libSpec, 0);
1783
1784 NS_TIMELINE_STOP_TIMER("PR_LoadLibrary");
1785 NS_TIMELINE_MARK_TIMER("PR_LoadLibrary");
1786
1787 if (*_retval)
1788 return NS_OK;
1789
1790 return NS_ERROR_NULL_POINTER;
1791}
1792
1793NS_IMETHODIMP
1794nsLocalFile::Remove(PRBool recursive)
1795{
1796 OSErr err;
1797 nsresult rv;
1798 FSSpec specToDelete;
1799 PRBool isDir;
1800
1801 StFollowLinksState(this, PR_FALSE);
1802
1803 rv = IsDirectory(&isDir); // Calls ResolveAndStat()
1804 if (NS_FAILED(rv))
1805 return rv;
1806 rv = GetFSSpec(&specToDelete);
1807 if (NS_FAILED(rv))
1808 return rv;
1809
1810 if (isDir && recursive)
1811 err = ::DeleteDirectory( specToDelete.vRefNum, specToDelete.parID, specToDelete.name );
1812 else
1813 err = ::HDelete( specToDelete.vRefNum, specToDelete.parID, specToDelete.name );
1814
1815 return MacErrorMapper( err );
1816}
1817
1818NS_IMETHODIMP
1819nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModifiedTime)
1820{
1821 NS_ENSURE_ARG(aLastModifiedTime);
1822 *aLastModifiedTime = 0;
1823
1824 nsresult rv = ResolveAndStat();
1825 if ( NS_FAILED( rv ) )
1826 return rv;
1827 rv = UpdateCachedCatInfo(PR_TRUE);
1828 if ( NS_FAILED( rv ) )
1829 return rv;
1830
1831 // The mod date is in the same spot for files and dirs.
1832 return ConvertMacTimeToMilliseconds( aLastModifiedTime, mCachedCatInfo.hFileInfo.ioFlMdDat );
1833}
1834
1835NS_IMETHODIMP
1836nsLocalFile::SetLastModifiedTime(PRInt64 aLastModifiedTime)
1837{
1838 nsresult rv = ResolveAndStat();
1839 if ( NS_FAILED(rv) )
1840 return rv;
1841
1842 PRUint32 macTime = 0;
1843 OSErr err = noErr;
1844
1845 ConvertMillisecondsToMacTime(aLastModifiedTime, &macTime);
1846
1847 if (NS_SUCCEEDED(rv = UpdateCachedCatInfo(PR_TRUE)))
1848 {
1849 if (mCachedCatInfo.hFileInfo.ioFlAttrib & ioDirMask)
1850 {
1851 mCachedCatInfo.dirInfo.ioDrMdDat = macTime;
1852 mCachedCatInfo.dirInfo.ioDrParID = mFollowLinks ? mTargetSpec.parID : mSpec.parID;
1853 }
1854 else
1855 {
1856 mCachedCatInfo.hFileInfo.ioFlMdDat = macTime;
1857 mCachedCatInfo.hFileInfo.ioDirID = mFollowLinks ? mTargetSpec.parID : mSpec.parID;
1858 }
1859
1860 err = ::PBSetCatInfoSync(&mCachedCatInfo);
1861 if (err != noErr)
1862 return MacErrorMapper(err);
1863 }
1864
1865 return rv;
1866}
1867
1868NS_IMETHODIMP
1869nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModifiedTime)
1870{
1871 NS_ENSURE_ARG(aLastModifiedTime);
1872
1873 nsresult rv;
1874 PRBool isLink;
1875
1876 rv = IsSymlink(&isLink);
1877 if (NS_FAILED(rv))
1878 return rv;
1879 if (!isLink)
1880 return NS_ERROR_FAILURE;
1881
1882 StFollowLinksState followState(this, PR_FALSE);
1883 return GetLastModifiedTime(aLastModifiedTime);
1884}
1885
1886NS_IMETHODIMP
1887nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModifiedTime)
1888{
1889 nsresult rv;
1890 PRBool isLink;
1891
1892 rv = IsSymlink(&isLink);
1893 if (NS_FAILED(rv))
1894 return rv;
1895 if (!isLink)
1896 return NS_ERROR_FAILURE;
1897
1898 StFollowLinksState followState(this, PR_FALSE);
1899 return SetLastModifiedTime(aLastModifiedTime);
1900}
1901
1902
1903NS_IMETHODIMP
1904nsLocalFile::GetFileSize(PRInt64 *aFileSize)
1905{
1906 NS_ENSURE_ARG(aFileSize);
1907 nsresult rv;
1908
1909 *aFileSize = LL_Zero();
1910
1911 if (NS_SUCCEEDED(rv = ResolveAndStat()) && NS_SUCCEEDED(rv = UpdateCachedCatInfo(PR_TRUE)))
1912 {
1913 if (!(mCachedCatInfo.hFileInfo.ioFlAttrib & ioDirMask))
1914 {
1915 long dataSize = mCachedCatInfo.hFileInfo.ioFlLgLen;
1916 long resSize = mCachedCatInfo.hFileInfo.ioFlRLgLen;
1917
1918 // For now we've only got 32 bits of file size info
1919 PRInt64 dataInt64 = LL_Zero();
1920 PRInt64 resInt64 = LL_Zero();
1921
1922 // WARNING!!!!!!
1923 //
1924 // For now we do NOT add the data and resource fork sizes as there are several
1925 // assumptions in the code (notably in form submit) that only the data fork is
1926 // used.
1927 // LL_I2L(resInt64, resSize);
1928
1929 LL_I2L(dataInt64, dataSize);
1930
1931 LL_ADD((*aFileSize), dataInt64, resInt64);
1932 }
1933 // leave size at zero for dirs
1934 }
1935
1936 return rv;
1937}
1938
1939
1940NS_IMETHODIMP
1941nsLocalFile::SetFileSize(PRInt64 aFileSize)
1942{
1943 nsresult rv = ResolveAndStat();
1944 if (NS_FAILED(rv))
1945 return rv;
1946
1947 short refNum;
1948 OSErr err;
1949 PRInt32 aNewLength;
1950
1951 LL_L2I(aNewLength, aFileSize);
1952
1953 // Need to open the file to set the size
1954 if (::FSpOpenDF(&mTargetSpec, fsWrPerm, &refNum) != noErr)
1955 return NS_ERROR_FILE_ACCESS_DENIED;
1956
1957 err = ::SetEOF(refNum, aNewLength);
1958
1959 // Close the file unless we got an error that it was already closed
1960 if (err != fnOpnErr)
1961 (void)::FSClose(refNum);
1962
1963 if (err != noErr)
1964 return MacErrorMapper(err);
1965
1966 return MacErrorMapper(err);
1967}
1968
1969NS_IMETHODIMP
1970nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSize)
1971{
1972 NS_ENSURE_ARG(aFileSize);
1973
1974 StFollowLinksState followState(this, PR_FALSE);
1975 return GetFileSize(aFileSize);
1976}
1977
1978NS_IMETHODIMP
1979nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
1980{
1981 NS_ENSURE_ARG(aDiskSpaceAvailable);
1982
1983 PRInt64 space64Bits;
1984
1985 LL_I2L(space64Bits , LONG_MAX);
1986
1987 nsresult rv = ResolveAndStat();
1988 if (NS_FAILED(rv))
1989 return rv;
1990
1991 XVolumeParam pb;
1992 pb.ioCompletion = nsnull;
1993 pb.ioVolIndex = 0;
1994 pb.ioNamePtr = nsnull;
1995 pb.ioVRefNum = mFollowLinks ? mTargetSpec.vRefNum : mSpec.vRefNum;
1996
1997 // we should check if this call is available
1998 OSErr err = ::PBXGetVolInfoSync(&pb);
1999
2000 if (err == noErr)
2001 {
2002 const UnsignedWide& freeBytes = UInt64ToUnsignedWide(pb.ioVFreeBytes);
2003#ifdef HAVE_LONG_LONG
2004 space64Bits = UnsignedWideToUInt64(freeBytes);
2005#else
2006 space64Bits.lo = freeBytes.lo;
2007 space64Bits.hi = freeBytes.hi;
2008#endif
2009 }
2010
2011 *aDiskSpaceAvailable = space64Bits;
2012
2013 return NS_OK;
2014}
2015
2016NS_IMETHODIMP
2017nsLocalFile::GetParent(nsIFile * *aParent)
2018{
2019 NS_ENSURE_ARG_POINTER(aParent);
2020 *aParent = nsnull;
2021
2022 nsresult rv = NS_OK;
2023 PRInt32 offset;
2024
2025 nsCOMPtr<nsILocalFileMac> localFile;
2026 PRInt32 appendedLen = mAppendedPath.Length();
2027 OSErr err;
2028
2029 if (!appendedLen || (appendedLen == 1 && mAppendedPath.CharAt(0) == ':'))
2030 {
2031 rv = ResolveAndStat();
2032 //if the file does not exist, does not mean that the parent does not.
2033 if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
2034 return rv;
2035
2036 CInfoPBRec pBlock = {0};
2037 FSSpec parentFolderSpec;
2038 parentFolderSpec.name[0] = 0;
2039
2040 pBlock.dirInfo.ioVRefNum = mSpec.vRefNum;
2041 pBlock.dirInfo.ioDrDirID = mSpec.parID;
2042 pBlock.dirInfo.ioNamePtr = (StringPtr)parentFolderSpec.name;
2043 pBlock.dirInfo.ioFDirIndex = -1; //get info on parID
2044 err = PBGetCatInfoSync(&pBlock);
2045 if (err != noErr)
2046 return MacErrorMapper(err);
2047 parentFolderSpec.vRefNum = mSpec.vRefNum;
2048 parentFolderSpec.parID = pBlock.dirInfo.ioDrParID;
2049
2050 localFile = new nsLocalFile;
2051 if (!localFile)
2052 return NS_ERROR_OUT_OF_MEMORY;
2053 rv = localFile->InitWithFSSpec(&parentFolderSpec);
2054 if (NS_FAILED(rv))
2055 return rv;
2056 }
2057 else
2058 {
2059 // trim off the last component of the appended path
2060 // construct a new file from our spec + trimmed path
2061
2062 nsCAutoString parentAppendage(mAppendedPath);
2063
2064 if (parentAppendage.Last() == ':')
2065 parentAppendage.Truncate(appendedLen - 1);
2066 if ((offset = parentAppendage.RFindChar(':')) != -1)
2067 parentAppendage.Truncate(offset);
2068 else
2069 parentAppendage.Truncate(0);
2070
2071 localFile = new nsLocalFile(mSpec, parentAppendage);
2072 if (!localFile)
2073 return NS_ERROR_OUT_OF_MEMORY;
2074 }
2075 *aParent = localFile;
2076 NS_ADDREF(*aParent);
2077
2078 return rv;
2079}
2080
2081
2082NS_IMETHODIMP
2083nsLocalFile::Exists(PRBool *_retval)
2084{
2085 NS_ENSURE_ARG(_retval);
2086 *_retval = PR_FALSE;
2087
2088 nsresult rv = ResolveAndStat();
2089 if (NS_SUCCEEDED(rv)) {
2090 if (NS_SUCCEEDED(UpdateCachedCatInfo(PR_TRUE)))
2091 *_retval = PR_TRUE;
2092 }
2093
2094 return NS_OK;
2095}
2096
2097NS_IMETHODIMP
2098nsLocalFile::IsPackage(PRBool *outIsPackage)
2099{
2100 NS_ENSURE_ARG(outIsPackage);
2101 *outIsPackage = PR_FALSE;
2102
2103 // Note: IsDirectory() calls ResolveAndStat() & UpdateCachedCatInfo
2104 PRBool isDir;
2105 nsresult rv = IsDirectory(&isDir);
2106 if (NS_FAILED(rv)) return rv;
2107
2108 *outIsPackage = ((mCachedCatInfo.dirInfo.ioFlAttrib & kioFlAttribDirMask) &&
2109 (mCachedCatInfo.dirInfo.ioDrUsrWds.frFlags & kHasBundle));
2110
2111 if ((!*outIsPackage) && isDir)
2112 {
2113 // Believe it or not, folders ending with ".app" are also considered
2114 // to be packages, even if the top-level folder doesn't have bundle set
2115 nsCAutoString name;
2116 if (NS_SUCCEEDED(rv = GetNativeLeafName(name)))
2117 {
2118 const char *extPtr = strrchr(name.get(), '.');
2119 if (extPtr)
2120 {
2121 if (!nsCRT::strcasecmp(extPtr, ".app"))
2122 {
2123 *outIsPackage = PR_TRUE;
2124 }
2125 }
2126 }
2127 }
2128
2129 return NS_OK;
2130}
2131
2132NS_IMETHODIMP
2133nsLocalFile::IsWritable(PRBool *outIsWritable)
2134{
2135 NS_ENSURE_ARG(outIsWritable);
2136 *outIsWritable = PR_TRUE;
2137
2138 nsresult rv = ResolveAndStat();
2139 if (NS_FAILED(rv)) return rv;
2140
2141 rv = UpdateCachedCatInfo(PR_TRUE);
2142 if (NS_FAILED(rv)) return rv;
2143
2144 *outIsWritable = !(mCachedCatInfo.hFileInfo.ioFlAttrib & kioFlAttribLockedMask);
2145 return NS_OK;
2146}
2147
2148NS_IMETHODIMP
2149nsLocalFile::IsReadable(PRBool *_retval)
2150{
2151 NS_ENSURE_ARG(_retval);
2152
2153 // is it ever not readable on Mac?
2154 *_retval = PR_TRUE;
2155 return NS_OK;
2156}
2157
2158
2159NS_IMETHODIMP
2160nsLocalFile::IsExecutable(PRBool *outIsExecutable)
2161{
2162 NS_ENSURE_ARG(outIsExecutable);
2163 *outIsExecutable = PR_FALSE; // Assume failure
2164
2165 nsresult rv = ResolveAndStat();
2166 if (NS_FAILED(rv)) return rv;
2167
2168#if TARGET_CARBON
2169 // If we're running under OS X ask LaunchServices if we're executable
2170 if (sRunningOSX)
2171 {
2172 if ( (UInt32)LSCopyItemInfoForRef != (UInt32)kUnresolvedCFragSymbolAddress )
2173 {
2174 FSRef theRef;
2175 LSRequestedInfo theInfoRequest = kLSRequestAllInfo;
2176 LSItemInfoRecord theInfo;
2177
2178 if (::FSpMakeFSRef(&mTargetSpec, &theRef) == noErr)
2179 {
2180 if (::LSCopyItemInfoForRef(&theRef, theInfoRequest, &theInfo) == noErr)
2181 {
2182 if ((theInfo.flags & kLSItemInfoIsApplication) != 0)
2183 *outIsExecutable = PR_TRUE;
2184 }
2185 }
2186 }
2187 }
2188 else
2189#endif
2190 {
2191 OSType fileType;
2192 rv = GetFileType(&fileType);
2193 if (NS_FAILED(rv)) return rv;
2194
2195 *outIsExecutable = (fileType == 'APPL' || fileType == 'appe' || fileType == 'FNDR');
2196 }
2197
2198 return NS_OK;
2199}
2200
2201
2202NS_IMETHODIMP
2203nsLocalFile::IsDirectory(PRBool *outIsDir)
2204{
2205 NS_ENSURE_ARG(outIsDir);
2206 *outIsDir = PR_FALSE;
2207
2208 nsresult rv = ResolveAndStat();
2209 if (NS_FAILED(rv)) return rv;
2210
2211 rv = UpdateCachedCatInfo(PR_FALSE);
2212 if (NS_FAILED(rv)) return rv;
2213
2214 *outIsDir = (mCachedCatInfo.hFileInfo.ioFlAttrib & ioDirMask) != 0;
2215 return NS_OK;
2216}
2217
2218NS_IMETHODIMP
2219nsLocalFile::IsFile(PRBool *outIsFile)
2220{
2221 NS_ENSURE_ARG(outIsFile);
2222 *outIsFile = PR_FALSE;
2223
2224 nsresult rv = ResolveAndStat();
2225 if (NS_FAILED(rv)) return rv;
2226
2227 rv = UpdateCachedCatInfo(PR_FALSE);
2228 if (NS_FAILED(rv)) return rv;
2229
2230 *outIsFile = (mCachedCatInfo.hFileInfo.ioFlAttrib & ioDirMask) == 0;
2231 return NS_OK;
2232}
2233
2234NS_IMETHODIMP
2235nsLocalFile::IsHidden(PRBool *_retval)
2236{
2237 NS_ENSURE_ARG(_retval);
2238 *_retval = PR_FALSE;
2239
2240 nsresult rv = ResolveAndStat();
2241 if (NS_FAILED(rv)) return rv;
2242
2243 rv = UpdateCachedCatInfo(PR_FALSE);
2244 if (NS_FAILED(rv)) return rv;
2245
2246 *_retval = (mCachedCatInfo.hFileInfo.ioFlFndrInfo.fdFlags & kIsInvisible) != 0;
2247
2248 if (sRunningOSX)
2249 {
2250 // on Mac OS X, also follow Unix "convention" where files
2251 // beginning with a period are considered to be hidden
2252 nsCAutoString name;
2253 if (NS_SUCCEEDED(rv = GetNativeLeafName(name)))
2254 {
2255 if (name.First() == '.')
2256 {
2257 *_retval = PR_TRUE;
2258 }
2259 }
2260 }
2261
2262 return NS_OK;
2263}
2264
2265NS_IMETHODIMP
2266nsLocalFile::IsSymlink(PRBool *_retval)
2267{
2268 NS_ENSURE_ARG(_retval);
2269 *_retval = PR_FALSE;
2270
2271 nsresult rv = ResolveAndStat();
2272 if (NS_FAILED(rv)) return rv;
2273
2274 Boolean isAlias, isFolder;
2275 if (::IsAliasFile(&mSpec, &isAlias, &isFolder) == noErr)
2276 *_retval = isAlias;
2277 return NS_OK;
2278}
2279
2280NS_IMETHODIMP
2281nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
2282{
2283 NS_ENSURE_ARG(inFile);
2284 NS_ENSURE_ARG(_retval);
2285 *_retval = PR_FALSE;
2286
2287 // Building paths is expensive. If we can get the FSSpecs of
2288 // both (they or their parents exist) just compare the specs.
2289 nsCOMPtr<nsILocalFileMac> inMacFile(do_QueryInterface(inFile));
2290 FSSpec fileSpec, inFileSpec;
2291 if (NS_SUCCEEDED(GetFSSpec(&fileSpec)) && inMacFile && NS_SUCCEEDED(inMacFile->GetFSSpec(&inFileSpec)))
2292 *_retval = IsEqualFSSpec(fileSpec, inFileSpec);
2293 else
2294 {
2295 nsCAutoString filePath;
2296 GetNativePath(filePath);
2297
2298 nsXPIDLCString inFilePath;
2299 inFile->GetNativePath(inFilePath);
2300
2301 if (nsCRT::strcasecmp(inFilePath.get(), filePath.get()) == 0)
2302 *_retval = PR_TRUE;
2303 }
2304 return NS_OK;
2305}
2306
2307NS_IMETHODIMP
2308nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *outContains)
2309{
2310 /* Note here that we make no attempt to deal with the problem
2311 of folder aliases. Doing a 'Contains' test and dealing with
2312 folder aliases is Hard. Think about it.
2313 */
2314 *outContains = PR_FALSE;
2315
2316 PRBool isDir;
2317 nsresult rv = IsDirectory(&isDir); // need to cache this
2318 if (NS_FAILED(rv)) return rv;
2319 if (!isDir) return NS_OK; // must be a dir to contain someone
2320
2321 nsCOMPtr<nsILocalFileMac> macFile(do_QueryInterface(inFile));
2322 if (!macFile) return NS_OK; // trying to compare non-local with local file
2323
2324 FSSpec mySpec = mSpec;
2325 FSSpec compareSpec;
2326
2327 // NOTE: we're not resolving inFile if it was an alias
2328 StFollowLinksState followState(macFile, PR_FALSE);
2329 rv = macFile->GetFSSpec(&compareSpec);
2330 if (NS_FAILED(rv)) return rv;
2331
2332 // if they are on different volumes, bail
2333 if (mSpec.vRefNum != compareSpec.vRefNum)
2334 return NS_OK;
2335
2336 // if recur == true, test every parent, otherwise just test the first one
2337 // (yes, recur does not get set in this loop)
2338 OSErr err = noErr;
2339 do
2340 {
2341 FSSpec parentFolderSpec;
2342 err = GetParentFolderSpec(compareSpec, parentFolderSpec);
2343 if (err != noErr) break; // we reached the top
2344
2345 if (IsEqualFSSpec(parentFolderSpec, mySpec))
2346 {
2347 *outContains = PR_TRUE;
2348 break;
2349 }
2350
2351 compareSpec = parentFolderSpec;
2352 } while (recur);
2353
2354 return NS_OK;
2355}
2356
2357
2358
2359NS_IMETHODIMP
2360nsLocalFile::GetNativeTarget(nsACString &_retval)
2361{
2362 _retval.Truncate();
2363
2364 PRBool symLink;
2365
2366 nsresult rv = IsSymlink(&symLink);
2367 if (NS_FAILED(rv))
2368 return rv;
2369
2370 if (!symLink)
2371 return NS_ERROR_FILE_INVALID_PATH;
2372
2373 StFollowLinksState followState(this, PR_TRUE);
2374 return GetNativePath(_retval);
2375}
2376
2377NS_IMETHODIMP
2378nsLocalFile::GetTarget(nsAString &_retval)
2379{
2380 nsresult rv;
2381 nsCAutoString fsStr;
2382
2383 if (NS_SUCCEEDED(rv = GetNativeTarget(fsStr))) {
2384 rv = NS_CopyNativeToUnicode(fsStr, _retval);
2385 }
2386 return rv;
2387}
2388
2389NS_IMETHODIMP
2390nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator * *entries)
2391{
2392 nsresult rv;
2393
2394 *entries = nsnull;
2395
2396 PRBool isDir;
2397 rv = IsDirectory(&isDir);
2398 if (NS_FAILED(rv))
2399 return rv;
2400 if (!isDir)
2401 return NS_ERROR_FILE_NOT_DIRECTORY;
2402
2403 nsDirEnumerator* dirEnum = new nsDirEnumerator();
2404 if (dirEnum == nsnull)
2405 return NS_ERROR_OUT_OF_MEMORY;
2406 NS_ADDREF(dirEnum);
2407 rv = dirEnum->Init(this);
2408 if (NS_FAILED(rv))
2409 {
2410 NS_RELEASE(dirEnum);
2411 return rv;
2412 }
2413
2414 *entries = dirEnum;
2415 return NS_OK;
2416}
2417
2418NS_IMETHODIMP
2419nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
2420{
2421 aPersistentDescriptor.Truncate();
2422
2423 nsresult rv = ResolveAndStat();
2424 if ( NS_FAILED( rv ) )
2425 return rv;
2426
2427 AliasHandle aliasH;
2428 OSErr err = ::NewAlias(nil, &mTargetSpec, &aliasH);
2429 if (err != noErr)
2430 return MacErrorMapper(err);
2431
2432 PRUint32 bytes = ::GetHandleSize((Handle) aliasH);
2433 HLock((Handle) aliasH);
2434 char* buf = PL_Base64Encode((const char*)*aliasH, bytes, nsnull); // Passing nsnull for dest makes NULL-term string
2435 ::DisposeHandle((Handle) aliasH);
2436 NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
2437
2438 aPersistentDescriptor = buf;
2439 PR_Free(buf);
2440
2441 return NS_OK;
2442}
2443
2444NS_IMETHODIMP
2445nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
2446{
2447 if (aPersistentDescriptor.IsEmpty())
2448 return NS_ERROR_INVALID_ARG;
2449
2450 nsresult rv = NS_OK;
2451
2452 PRUint32 dataSize = aPersistentDescriptor.Length();
2453 char* decodedData = PL_Base64Decode(PromiseFlatCString(aPersistentDescriptor).get(), dataSize, nsnull);
2454 // Cast to an alias record and resolve.
2455 AliasHandle aliasH = nsnull;
2456 if (::PtrToHand(decodedData, &(Handle)aliasH, (dataSize * 3) / 4) != noErr)
2457 rv = NS_ERROR_OUT_OF_MEMORY;
2458 PR_Free(decodedData);
2459 NS_ENSURE_SUCCESS(rv, rv);
2460
2461 Boolean changed;
2462 FSSpec resolvedSpec;
2463 OSErr err;
2464 err = ::ResolveAlias(nsnull, aliasH, &resolvedSpec, &changed);
2465 if (err == fnfErr) // resolvedSpec is valid in this case
2466 err = noErr;
2467 rv = MacErrorMapper(err);
2468 DisposeHandle((Handle) aliasH);
2469 NS_ENSURE_SUCCESS(rv, rv);
2470
2471 return InitWithFSSpec(&resolvedSpec);
2472}
2473
2474#pragma mark -
2475
2476// a stack-based, exception safe class for an AEDesc
2477
2478#pragma mark
2479class StAEDesc: public AEDesc
2480{
2481public:
2482 StAEDesc()
2483 {
2484 descriptorType = typeNull;
2485 dataHandle = nil;
2486 }
2487
2488 ~StAEDesc()
2489 {
2490 ::AEDisposeDesc(this);
2491 }
2492
2493 void Clear()
2494 {
2495 ::AEDisposeDesc(this);
2496 descriptorType = typeNull;
2497 dataHandle = nil;
2498 }
2499
2500private:
2501 // disallow copies and assigns
2502 StAEDesc(const StAEDesc& rhs); // copy constructor
2503 StAEDesc& operator= (const StAEDesc&rhs); // throws OSErrs
2504
2505};
2506
2507#pragma mark -
2508#pragma mark [Utility methods]
2509
2510
2511nsresult nsLocalFile::UpdateCachedCatInfo(PRBool forceUpdate)
2512{
2513 if (!mCatInfoDirty && !forceUpdate)
2514 return NS_OK;
2515
2516 FSSpec spectoUse = mFollowLinks ? mTargetSpec : mSpec;
2517 mCachedCatInfo.hFileInfo.ioCompletion = nsnull;
2518 mCachedCatInfo.hFileInfo.ioFDirIndex = 0; // use dirID and name
2519 mCachedCatInfo.hFileInfo.ioVRefNum = spectoUse.vRefNum;
2520 mCachedCatInfo.hFileInfo.ioDirID = spectoUse.parID;
2521 mCachedCatInfo.hFileInfo.ioNamePtr = spectoUse.name;
2522
2523 OSErr err = ::PBGetCatInfoSync(&mCachedCatInfo);
2524 if (err == noErr)
2525 {
2526 mCatInfoDirty = PR_FALSE;
2527 return NS_OK;
2528 }
2529 return MacErrorMapper(err);
2530}
2531
2532
2533nsresult nsLocalFile::FindRunningAppBySignature (OSType aAppSig, FSSpec& outSpec, ProcessSerialNumber& outPsn)
2534{
2535 ProcessInfoRec info;
2536 FSSpec tempFSSpec;
2537 OSErr err = noErr;
2538
2539 outPsn.highLongOfPSN = 0;
2540 outPsn.lowLongOfPSN = kNoProcess;
2541
2542 while (PR_TRUE)
2543 {
2544 err = ::GetNextProcess(&outPsn);
2545 if (err == procNotFound) break;
2546 if (err != noErr) return NS_ERROR_FAILURE;
2547 info.processInfoLength = sizeof(ProcessInfoRec);
2548 info.processName = nil;
2549 info.processAppSpec = &tempFSSpec;
2550 err = ::GetProcessInformation(&outPsn, &info);
2551 if (err != noErr) return NS_ERROR_FAILURE;
2552
2553 if (info.processSignature == aAppSig)
2554 {
2555 outSpec = tempFSSpec;
2556 return NS_OK;
2557 }
2558 }
2559
2560 return NS_ERROR_FILE_NOT_FOUND; // really process not found
2561}
2562
2563
2564nsresult nsLocalFile::FindRunningAppByFSSpec(const FSSpec& appSpec, ProcessSerialNumber& outPsn)
2565{
2566 ProcessInfoRec info;
2567 FSSpec tempFSSpec;
2568 OSErr err = noErr;
2569
2570 outPsn.highLongOfPSN = 0;
2571 outPsn.lowLongOfPSN = kNoProcess;
2572
2573 while (PR_TRUE)
2574 {
2575 err = ::GetNextProcess(&outPsn);
2576 if (err == procNotFound) break;
2577 if (err != noErr) return NS_ERROR_FAILURE;
2578 info.processInfoLength = sizeof(ProcessInfoRec);
2579 info.processName = nil;
2580 info.processAppSpec = &tempFSSpec;
2581 err = ::GetProcessInformation(&outPsn, &info);
2582 if (err != noErr) return NS_ERROR_FAILURE;
2583
2584 if (IsEqualFSSpec(appSpec, *info.processAppSpec))
2585 {
2586 return NS_OK;
2587 }
2588 }
2589
2590 return NS_ERROR_FILE_NOT_FOUND; // really process not found
2591}
2592
2593
2594nsresult nsLocalFile::FindAppOnLocalVolumes(OSType sig, FSSpec &outSpec)
2595{
2596 OSErr err;
2597
2598 // get the system volume
2599 long systemFolderDirID;
2600 short sysVRefNum;
2601 err = FindFolder(kOnSystemDisk, kSystemFolderType, false, &sysVRefNum, &systemFolderDirID);
2602 if (err != noErr) return NS_ERROR_FAILURE;
2603
2604 short vRefNum = sysVRefNum;
2605 short index = 0;
2606
2607 while (true)
2608 {
2609 if (index == 0 || vRefNum != sysVRefNum)
2610 {
2611 // should we avoid AppleShare volumes?
2612
2613 Boolean hasDesktopDB;
2614 err = VolHasDesktopDB(vRefNum, &hasDesktopDB);
2615 if (err != noErr) return err;
2616 if (hasDesktopDB)
2617 {
2618 err = FindAppOnVolume(sig, vRefNum, &outSpec);
2619 if (err != afpItemNotFound) return err;
2620 }
2621 }
2622 index++;
2623 err = GetIndVolume(index, &vRefNum);
2624 if (err == nsvErr) return fnfErr;
2625 if (err != noErr) return err;
2626 }
2627
2628 return NS_OK;
2629}
2630
2631#define aeSelectionKeyword 'fsel'
2632#define kAEOpenSelection 'sope'
2633#define kAERevealSelection 'srev'
2634#define kFinderType 'FNDR'
2635
2636NS_IMETHODIMP nsLocalFile::Launch()
2637{
2638 AppleEvent aeEvent = {0, nil};
2639 AppleEvent aeReply = {0, nil};
2640 StAEDesc aeDirDesc, listElem, myAddressDesc, fileList;
2641 FSSpec dirSpec, appSpec;
2642 AliasHandle DirAlias, FileAlias;
2643 OSErr errorResult = noErr;
2644 ProcessSerialNumber process;
2645
2646 // for launching a file, we'll use mTargetSpec (which is both a resolved spec and a resolved alias)
2647 ResolveAndStat();
2648
2649#if TARGET_CARBON
2650 if (sRunningOSX)
2651 { // We're running under Mac OS X, LaunchServices here we come
2652
2653 // First we make sure the LaunchServices routine we want is implemented
2654 if ( (UInt32)LSOpenFSRef != (UInt32)kUnresolvedCFragSymbolAddress )
2655 {
2656 FSRef theRef;
2657 if (::FSpMakeFSRef(&mTargetSpec, &theRef) == noErr)
2658 {
2659 (void)::LSOpenFSRef(&theRef, NULL);
2660 }
2661 }
2662 }
2663 else
2664#endif
2665 { // We're running under Mac OS 8.x/9.x, use the Finder Luke
2666 nsresult rv = FindRunningAppBySignature ('MACS', appSpec, process);
2667 if (NS_SUCCEEDED(rv))
2668 {
2669 errorResult = AECreateDesc(typeProcessSerialNumber, (Ptr)&process, sizeof(process), &myAddressDesc);
2670 if (errorResult == noErr)
2671 {
2672 /* Create the FinderEvent */
2673 errorResult = AECreateAppleEvent(kFinderType, kAEOpenSelection, &myAddressDesc, kAutoGenerateReturnID, kAnyTransactionID,
2674 &aeEvent);
2675 if (errorResult == noErr)
2676 {
2677 errorResult = FSMakeFSSpec(mTargetSpec.vRefNum, mTargetSpec.parID, nil, &dirSpec);
2678 NewAlias(nil, &dirSpec, &DirAlias);
2679 /* Create alias for file */
2680 NewAlias(nil, &mTargetSpec, &FileAlias);
2681
2682 /* Create the file list */
2683 errorResult = AECreateList(nil, 0, false, &fileList);
2684 /* create the folder descriptor */
2685 HLock((Handle)DirAlias);
2686 errorResult = AECreateDesc(typeAlias, (Ptr)*DirAlias, GetHandleSize((Handle)DirAlias), &aeDirDesc);
2687 HUnlock((Handle)DirAlias);
2688 if (errorResult == noErr)
2689 {
2690 errorResult = AEPutParamDesc(&aeEvent, keyDirectObject, &aeDirDesc);
2691 if ( errorResult == noErr)
2692 {
2693 /* create the file descriptor and add to aliasList */
2694 HLock((Handle)FileAlias);
2695 errorResult = AECreateDesc(typeAlias, (Ptr)*FileAlias, GetHandleSize((Handle)FileAlias), &listElem);
2696 HLock((Handle)FileAlias);
2697 if (errorResult == noErr)
2698 {
2699 errorResult = AEPutDesc(&fileList, 0, &listElem);
2700 if (errorResult == noErr)
2701 {
2702 /* Add the file alias list to the event */
2703 errorResult = AEPutParamDesc(&aeEvent, aeSelectionKeyword, &fileList);
2704 if (errorResult == noErr)
2705 AESend(&aeEvent, &aeReply, kAEWaitReply + kAENeverInteract
2706 + kAECanSwitchLayer, kAEHighPriority, kAEDefaultTimeout, nil, nil);
2707 }
2708 }
2709 }
2710 }
2711 }
2712 }
2713 }
2714 }
2715
2716 return NS_OK;
2717}
2718
2719NS_IMETHODIMP nsLocalFile::Reveal()
2720{
2721 FSSpec specToReveal;
2722 AppleEvent aeEvent = {0, nil};
2723 AppleEvent aeReply = {0, nil};
2724 StAEDesc aeDirDesc, listElem, myAddressDesc, fileList;
2725 OSErr errorResult = noErr;
2726 ProcessSerialNumber process;
2727 FSSpec appSpec;
2728
2729 nsresult rv = ResolveAndStat();
2730 if (NS_FAILED(rv))
2731 return rv;
2732 rv = GetFSSpec(&specToReveal); // Pay attention to followLinks
2733 if (NS_FAILED(rv))
2734 return rv;
2735
2736 rv = FindRunningAppBySignature ('MACS', appSpec, process);
2737 if (NS_SUCCEEDED(rv))
2738 {
2739 errorResult = AECreateDesc(typeProcessSerialNumber, (Ptr)&process, sizeof(process), &myAddressDesc);
2740 if (errorResult == noErr)
2741 {
2742 /* Create the FinderEvent */
2743#if TARGET_CARBON
2744 // The Finder under OS X uses a different event to reveal
2745 if (sRunningOSX)
2746 errorResult = AECreateAppleEvent(kAEMiscStandards, kAEMakeObjectsVisible, &myAddressDesc, kAutoGenerateReturnID, kAnyTransactionID,
2747 &aeEvent);
2748 else
2749#endif
2750 errorResult = AECreateAppleEvent(kFinderType, kAERevealSelection, &myAddressDesc, kAutoGenerateReturnID, kAnyTransactionID,
2751 &aeEvent);
2752 if (errorResult == noErr)
2753 {
2754 /* Create the file list */
2755 errorResult = AECreateList(nil, 0, false, &fileList);
2756 if (errorResult == noErr)
2757 {
2758 errorResult = AEPutPtr(&fileList, 0, typeFSS, &specToReveal, sizeof(FSSpec));
2759
2760 if (errorResult == noErr)
2761 {
2762#if TARGET_CARBON
2763 // When we're sending the event under OS X the FSSpec must be a keyDirectObject
2764 if (sRunningOSX)
2765 errorResult = AEPutParamDesc(&aeEvent, keyDirectObject, &fileList);
2766 else
2767#endif
2768 errorResult = AEPutParamDesc(&aeEvent,keySelection, &fileList);
2769
2770 if (errorResult == noErr)
2771 {
2772 errorResult = AESend(&aeEvent, &aeReply, kAENoReply, kAENormalPriority, kAEDefaultTimeout, nil, nil);
2773 if (errorResult == noErr)
2774 SetFrontProcess(&process);
2775 }
2776 }
2777 }
2778 }
2779 }
2780 }
2781
2782 return NS_OK;
2783}
2784
2785nsresult nsLocalFile::MyLaunchAppWithDoc(const FSSpec& appSpec, const FSSpec* aDocToLoad, PRBool aLaunchInBackground)
2786{
2787 ProcessSerialNumber thePSN = {0};
2788 StAEDesc target;
2789 StAEDesc docDesc;
2790 StAEDesc launchDesc;
2791 StAEDesc docList;
2792 AppleEvent theEvent = {0, nil};
2793 AppleEvent theReply = {0, nil};
2794 OSErr err = noErr;
2795 Boolean autoParamValue = false;
2796 Boolean running = false;
2797 nsresult rv = NS_OK;
2798
2799#if TARGET_CARBON
2800 if (sRunningOSX)
2801 { // Under Mac OS X we'll use LaunchServices
2802
2803 // First we make sure the LaunchServices routine we want is implemented
2804 if ( (UInt32)LSOpenFromRefSpec != (UInt32)kUnresolvedCFragSymbolAddress )
2805 {
2806 FSRef appRef;
2807 FSRef docRef;
2808 LSLaunchFlags theLaunchFlags = kLSLaunchDefaults;
2809 LSLaunchFSRefSpec thelaunchSpec;
2810
2811 if (::FSpMakeFSRef(&appSpec, &appRef) != noErr)
2812 return NS_ERROR_FAILURE;
2813
2814 if (aDocToLoad)
2815 if (::FSpMakeFSRef(aDocToLoad, &docRef) != noErr)
2816 return NS_ERROR_FAILURE;
2817
2818 if (aLaunchInBackground)
2819 theLaunchFlags |= kLSLaunchDontSwitch;
2820
2821 memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
2822
2823 thelaunchSpec.appRef = &appRef;
2824 if (aDocToLoad)
2825 {
2826 thelaunchSpec.numDocs = 1;
2827 thelaunchSpec.itemRefs = &docRef;
2828 }
2829 thelaunchSpec.launchFlags = theLaunchFlags;
2830
2831 err = ::LSOpenFromRefSpec(&thelaunchSpec, NULL);
2832 NS_ASSERTION((err != noErr), "Error calling LSOpenFromRefSpec");
2833 if (err != noErr) return NS_ERROR_FAILURE;
2834 }
2835 }
2836 else
2837#endif
2838 { // The old fashioned way for Mac OS 8.x/9.x
2839 rv = FindRunningAppByFSSpec(appSpec, thePSN);
2840 running = NS_SUCCEEDED(rv);
2841
2842 err = AECreateDesc(typeProcessSerialNumber, &thePSN, sizeof(thePSN), &target);
2843 if (err != noErr) return NS_ERROR_FAILURE;
2844
2845 err = AECreateAppleEvent(kCoreEventClass, aDocToLoad ? kAEOpenDocuments : kAEOpenApplication, &target,
2846 kAutoGenerateReturnID, kAnyTransactionID, &theEvent);
2847 if (err != noErr) return NS_ERROR_FAILURE;
2848
2849 if (aDocToLoad)
2850 {
2851 err = AECreateList(nil, 0, false, &docList);
2852 if (err != noErr) return NS_ERROR_FAILURE;
2853
2854 err = AECreateDesc(typeFSS, aDocToLoad, sizeof(FSSpec), &docDesc);
2855 if (err != noErr) return NS_ERROR_FAILURE;
2856
2857 err = AEPutDesc(&docList, 0, &docDesc);
2858 if (err != noErr) return NS_ERROR_FAILURE;
2859
2860 err = AEPutParamDesc(&theEvent, keyDirectObject, &docList);
2861 if (err != noErr) return NS_ERROR_FAILURE;
2862 }
2863
2864 if (running)
2865 {
2866 err = AESend(&theEvent, &theReply, kAENoReply, kAENormalPriority, kNoTimeOut, nil, nil);
2867 if (err != noErr) return NS_ERROR_FAILURE;
2868
2869 if (!aLaunchInBackground)
2870 {
2871 err = ::SetFrontProcess(&thePSN);
2872 if (err != noErr) return NS_ERROR_FAILURE;
2873 }
2874 }
2875 else
2876 {
2877 LaunchParamBlockRec launchThis = {0};
2878 PRUint16 launchControlFlags = (launchContinue | launchNoFileFlags);
2879 if (aLaunchInBackground)
2880 launchControlFlags |= launchDontSwitch;
2881
2882 err = AECoerceDesc(&theEvent, typeAppParameters, &launchDesc);
2883 if (err != noErr) return NS_ERROR_FAILURE;
2884
2885 launchThis.launchAppSpec = (FSSpecPtr)&appSpec;
2886#if TARGET_CARBON && ACCESSOR_CALLS_ARE_FUNCTIONS
2887 ::AEGetDescData(&launchDesc, &launchThis.launchAppParameters, sizeof(launchThis.launchAppParameters));
2888#else
2889 // no need to lock this handle.
2890 launchThis.launchAppParameters = (AppParametersPtr) *(launchDesc.dataHandle);
2891#endif
2892 launchThis.launchBlockID = extendedBlock;
2893 launchThis.launchEPBLength = extendedBlockLen;
2894 launchThis.launchFileFlags = 0;
2895 launchThis.launchControlFlags = launchControlFlags;
2896 err = ::LaunchApplication(&launchThis);
2897 if (err != noErr) return NS_ERROR_FAILURE;
2898
2899 // let's be nice and wait until it's running
2900 const PRUint32 kMaxTimeToWait = 60; // wait 1 sec max
2901 PRUint32 endTicks = ::TickCount() + kMaxTimeToWait;
2902
2903 PRBool foundApp = PR_FALSE;
2904
2905 do
2906 {
2907 EventRecord theEvent;
2908 (void)WaitNextEvent(nullEvent, &theEvent, 1, NULL);
2909
2910 ProcessSerialNumber psn;
2911 foundApp = NS_SUCCEEDED(FindRunningAppByFSSpec(appSpec, psn));
2912
2913 } while (!foundApp && (::TickCount() <= endTicks));
2914
2915 NS_ASSERTION(foundApp, "Failed to find app after launching it");
2916 }
2917
2918 if (theEvent.dataHandle != nil) AEDisposeDesc(&theEvent);
2919 if (theReply.dataHandle != nil) AEDisposeDesc(&theReply);
2920 }
2921
2922 return NS_OK;
2923}
2924
2925
2926#pragma mark -
2927#pragma mark [Methods that will not be implemented on Mac]
2928
2929NS_IMETHODIMP
2930nsLocalFile::Normalize()
2931{
2932 return NS_ERROR_NOT_IMPLEMENTED;
2933}
2934
2935NS_IMETHODIMP
2936nsLocalFile::GetPermissions(PRUint32 *aPermissions)
2937{
2938 return NS_ERROR_NOT_IMPLEMENTED;
2939}
2940
2941NS_IMETHODIMP
2942nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink)
2943{
2944 return NS_ERROR_NOT_IMPLEMENTED;
2945}
2946
2947
2948NS_IMETHODIMP
2949nsLocalFile::SetPermissions(PRUint32 aPermissions)
2950{
2951 return NS_ERROR_NOT_IMPLEMENTED;
2952}
2953
2954NS_IMETHODIMP
2955nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissions)
2956{
2957 return NS_ERROR_NOT_IMPLEMENTED;
2958}
2959
2960NS_IMETHODIMP
2961nsLocalFile::IsSpecial(PRBool *_retval)
2962{
2963 return NS_ERROR_NOT_IMPLEMENTED;
2964}
2965
2966#pragma mark -
2967#pragma mark [nsILocalFileMac]
2968// Implementation of Mac specific finctions from nsILocalFileMac
2969
2970
2971NS_IMETHODIMP nsLocalFile::InitWithCFURL(CFURLRef aCFURL)
2972{
2973 nsresult rv = NS_ERROR_FAILURE;
2974
2975#if TARGET_CARBON
2976 NS_ENSURE_ARG(aCFURL);
2977
2978 // CFURLGetFSRef can only succeed if the entire path exists.
2979 FSRef fsRef;
2980 if (::CFURLGetFSRef(aCFURL, &fsRef) == PR_TRUE)
2981 rv = InitWithFSRef(&fsRef);
2982 else
2983 {
2984 CFURLRef parentURL = ::CFURLCreateCopyDeletingLastPathComponent(NULL, aCFURL);
2985 if (!parentURL)
2986 return NS_ERROR_FAILURE;
2987
2988 // Get the FSRef from the parent and the FSSpec from that
2989 FSRef parentFSRef;
2990 FSSpec parentFSSpec;
2991 if ((::CFURLGetFSRef(parentURL, &parentFSRef) == PR_TRUE) &&
2992 (::FSGetCatalogInfo(&parentFSRef, kFSCatInfoNone,
2993 nsnull, nsnull, &parentFSSpec, nsnull) == noErr))
2994 {
2995 // Get the leaf name of the file and turn it into a string HFS can use.
2996 CFStringRef fileNameRef = ::CFURLCopyLastPathComponent(aCFURL);
2997 if (fileNameRef)
2998 {
2999 TextEncoding theEncoding;
3000 if (::UpgradeScriptInfoToTextEncoding(smSystemScript,
3001 kTextLanguageDontCare,
3002 kTextRegionDontCare,
3003 NULL,
3004 &theEncoding) != noErr)
3005 theEncoding = kTextEncodingMacRoman;
3006
3007 char origName[256];
3008 char truncBuf[32];
3009 if (::CFStringGetCString(fileNameRef, origName, sizeof(origName), theEncoding))
3010 {
3011 MakeDirty();
3012 mSpec = parentFSSpec;
3013 mAppendedPath = NS_TruncNodeName(origName, truncBuf);
3014 rv = NS_OK;
3015 }
3016 ::CFRelease(fileNameRef);
3017 }
3018 }
3019 ::CFRelease(parentURL);
3020 }
3021#endif
3022
3023 return rv;
3024}
3025
3026
3027NS_IMETHODIMP nsLocalFile::InitWithFSRef(const FSRef * aFSRef)
3028{
3029 nsresult rv = NS_ERROR_FAILURE;
3030
3031#if TARGET_CARBON
3032 NS_ENSURE_ARG(aFSRef);
3033
3034 FSSpec fsSpec;
3035 OSErr err = ::FSGetCatalogInfo(aFSRef, kFSCatInfoNone, nsnull,
3036 nsnull, &fsSpec, nsnull);
3037 if (err == noErr)
3038 rv = InitWithFSSpec(&fsSpec);
3039 else
3040 rv = MacErrorMapper(err);
3041#endif
3042
3043 return rv;
3044}
3045
3046
3047NS_IMETHODIMP nsLocalFile::InitWithFSSpec(const FSSpec *fileSpec)
3048{
3049 MakeDirty();
3050 mSpec = *fileSpec;
3051 mTargetSpec = *fileSpec;
3052 mAppendedPath = "";
3053 return NS_OK;
3054}
3055
3056
3057NS_IMETHODIMP nsLocalFile::InitToAppWithCreatorCode(OSType aAppCreator)
3058{
3059 FSSpec appSpec;
3060 ProcessSerialNumber psn;
3061
3062#if TARGET_CARBON
3063 if (sRunningOSX)
3064 { // If we're running under OS X use LaunchServices to determine the app
3065 // corresponding to the creator code
3066 if ( (UInt32)LSFindApplicationForInfo != (UInt32)kUnresolvedCFragSymbolAddress )
3067 {
3068 FSRef theRef;
3069 if (::LSFindApplicationForInfo(aAppCreator, NULL, NULL, &theRef, NULL) == noErr)
3070 {
3071 FSCatalogInfoBitmap whichInfo = kFSCatInfoNone;
3072
3073 if (::FSGetCatalogInfo(&theRef, whichInfo, NULL, NULL, &appSpec, NULL) == noErr)
3074 return InitWithFSSpec(&appSpec);
3075 }
3076
3077 // If we get here we didn't find an app
3078 return NS_ERROR_FILE_NOT_FOUND;
3079 }
3080 }
3081#endif
3082
3083 // is the app running?
3084 nsresult rv = FindRunningAppBySignature(aAppCreator, appSpec, psn);
3085 if (rv == NS_ERROR_FILE_NOT_FOUND)
3086 {
3087 // we have to look on disk
3088 rv = FindAppOnLocalVolumes(aAppCreator, appSpec);
3089 if (NS_FAILED(rv)) return rv;
3090 }
3091 else if (NS_FAILED(rv))
3092 return rv;
3093
3094 // init with the spec here
3095 return InitWithFSSpec(&appSpec);
3096}
3097
3098NS_IMETHODIMP nsLocalFile::GetCFURL(CFURLRef *_retval)
3099{
3100 nsresult rv = NS_ERROR_FAILURE;
3101
3102#if TARGET_CARBON
3103 NS_ENSURE_ARG_POINTER(_retval);
3104 *_retval = nsnull;
3105
3106 PRBool exists;
3107 if (NS_SUCCEEDED(Exists(&exists)) && exists)
3108 {
3109 FSRef fsRef;
3110 FSSpec fsSpec = mFollowLinks ? mTargetSpec : mSpec;
3111 if (::FSpMakeFSRef(&fsSpec, &fsRef) == noErr)
3112 {
3113 *_retval = ::CFURLCreateFromFSRef(NULL, &fsRef);
3114 if (*_retval)
3115 return NS_OK;
3116 }
3117 }
3118 else
3119 {
3120 nsCAutoString tempPath;
3121 if (NS_SUCCEEDED(GetNativePath(tempPath)))
3122 {
3123 CFStringRef pathStrRef = ::CFStringCreateWithCString(NULL, tempPath.get(), kCFStringEncodingMacRoman);
3124 if (!pathStrRef)
3125 return NS_ERROR_FAILURE;
3126 *_retval = ::CFURLCreateWithFileSystemPath(NULL, pathStrRef, kCFURLHFSPathStyle, false);
3127 ::CFRelease(pathStrRef);
3128 if (*_retval)
3129 return NS_OK;
3130 }
3131 }
3132#endif
3133
3134 return rv;
3135}
3136
3137NS_IMETHODIMP nsLocalFile::GetFSRef(FSRef *_retval)
3138{
3139 nsresult rv = NS_ERROR_FAILURE;
3140
3141#if TARGET_CARBON
3142 NS_ENSURE_ARG_POINTER(_retval);
3143
3144 FSSpec fsSpec;
3145 rv = GetFSSpec(&fsSpec);
3146 if (NS_SUCCEEDED(rv))
3147 rv = MacErrorMapper(::FSpMakeFSRef(&fsSpec, _retval));
3148#endif
3149
3150 return rv;
3151}
3152
3153NS_IMETHODIMP nsLocalFile::GetFSSpec(FSSpec *fileSpec)
3154{
3155 NS_ENSURE_ARG(fileSpec);
3156 nsresult rv = ResolveAndStat();
3157 if (rv == NS_ERROR_FILE_NOT_FOUND)
3158 rv = NS_OK;
3159 if (NS_SUCCEEDED(rv))
3160 *fileSpec = mFollowLinks ? mTargetSpec : mSpec;
3161
3162 return NS_OK;
3163}
3164
3165NS_IMETHODIMP nsLocalFile::GetFileType(OSType *aFileType)
3166{
3167 NS_ENSURE_ARG(aFileType);
3168
3169 FSSpec fileSpec;
3170 (void)GetFSSpec(&fileSpec);
3171
3172 FInfo info;
3173 OSErr err = ::FSpGetFInfo(&fileSpec, &info);
3174 if (err != noErr)
3175 {
3176 *aFileType = mType;
3177 return NS_ERROR_FILE_NOT_FOUND;
3178 }
3179
3180 *aFileType = info.fdType;
3181
3182 return NS_OK;
3183}
3184
3185NS_IMETHODIMP nsLocalFile::SetFileType(OSType aFileType)
3186{
3187 mType = aFileType;
3188
3189 FSSpec fileSpec;
3190 (void)GetFSSpec(&fileSpec);
3191
3192 FInfo info;
3193 OSErr err = ::FSpGetFInfo(&fileSpec, &info);
3194 if (err != noErr)
3195 return NS_ERROR_FILE_NOT_FOUND;
3196
3197 info.fdType = aFileType;
3198 err = ::FSpSetFInfo(&fileSpec, &info);
3199 if (err != noErr)
3200 return NS_ERROR_FILE_ACCESS_DENIED;
3201
3202 return NS_OK;
3203}
3204
3205NS_IMETHODIMP nsLocalFile::GetFileCreator(OSType *aCreator)
3206{
3207 NS_ENSURE_ARG(aCreator);
3208
3209 FSSpec fileSpec;
3210 (void)GetFSSpec(&fileSpec);
3211
3212 FInfo info;
3213 OSErr err = ::FSpGetFInfo(&fileSpec, &info);
3214 if (err != noErr)
3215 {
3216 *aCreator = mCreator;
3217 return NS_ERROR_FILE_NOT_FOUND;
3218 }
3219
3220 *aCreator = info.fdCreator;
3221
3222 return NS_OK;
3223}
3224
3225NS_IMETHODIMP nsLocalFile::SetFileCreator(OSType aCreator)
3226{
3227 if (aCreator == CURRENT_PROCESS_CREATOR)
3228 aCreator = sCurrentProcessSignature;
3229
3230 mCreator = aCreator;
3231
3232 FSSpec fileSpec;
3233 (void)GetFSSpec(&fileSpec);
3234
3235 FInfo info;
3236 OSErr err = ::FSpGetFInfo(&fileSpec, &info);
3237 if (err != noErr)
3238 return NS_ERROR_FILE_NOT_FOUND;
3239
3240 info.fdCreator = aCreator;
3241 err = ::FSpSetFInfo(&fileSpec, &info);
3242 if (err != noErr)
3243 return NS_ERROR_FILE_ACCESS_DENIED;
3244
3245 return NS_OK;
3246}
3247
3248NS_IMETHODIMP nsLocalFile::SetFileTypeAndCreatorFromExtension(const char *aExtension)
3249{
3250 NS_ENSURE_ARG(aExtension);
3251 return SetOSTypeAndCreatorFromExtension(aExtension);
3252}
3253
3254NS_IMETHODIMP nsLocalFile::SetFileTypeAndCreatorFromMIMEType(const char *aMIMEType)
3255{
3256 NS_ENSURE_ARG(aMIMEType);
3257
3258 nsresult rv;
3259 nsCOMPtr<nsIInternetConfigService> icService(do_GetService
3260 (NS_INTERNETCONFIGSERVICE_CONTRACTID, &rv));
3261
3262 if (NS_SUCCEEDED(rv))
3263 {
3264 nsCOMPtr<nsIMIMEInfo> mimeInfo;
3265 PRUint32 fileType = 'TEXT';
3266 PRUint32 fileCreator = nsILocalFileMac::CURRENT_PROCESS_CREATOR;
3267
3268 rv = icService->FillInMIMEInfo(aMIMEType,
3269 nsnull, getter_AddRefs(mimeInfo));
3270 if (NS_SUCCEEDED(rv))
3271 rv = mimeInfo->GetMacType(&fileType);
3272 if (NS_SUCCEEDED(rv))
3273 rv = mimeInfo->GetMacCreator(&fileCreator);
3274 if (NS_SUCCEEDED(rv))
3275 rv = SetFileType(fileType);
3276 if (NS_SUCCEEDED(rv))
3277 rv = SetFileCreator(fileCreator);
3278 }
3279
3280 return rv;
3281}
3282
3283NS_IMETHODIMP
3284nsLocalFile::GetFileSizeWithResFork(PRInt64 *aFileSize)
3285{
3286 NS_ENSURE_ARG(aFileSize);
3287
3288 *aFileSize = LL_Zero();
3289
3290 ResolveAndStat();
3291
3292 long dataSize = 0;
3293 long resSize = 0;
3294
3295 OSErr err = FSpGetFileSize(&mTargetSpec, &dataSize, &resSize);
3296
3297 if (err != noErr)
3298 return MacErrorMapper(err);
3299
3300 // For now we've only got 32 bits of file size info
3301 PRInt64 dataInt64 = LL_Zero();
3302 PRInt64 resInt64 = LL_Zero();
3303
3304 // Combine the size of the resource and data forks
3305 LL_I2L(resInt64, resSize);
3306 LL_I2L(dataInt64, dataSize);
3307 LL_ADD((*aFileSize), dataInt64, resInt64);
3308
3309 return NS_OK;
3310}
3311
3312
3313// this nsLocalFile points to the app. We want to launch it, optionally with the document.
3314NS_IMETHODIMP
3315nsLocalFile::LaunchWithDoc(nsILocalFile* aDocToLoad, PRBool aLaunchInBackground)
3316{
3317 // are we launchable?
3318 PRBool isExecutable;
3319 nsresult rv = IsExecutable(&isExecutable);
3320 if (NS_FAILED(rv)) return rv;
3321 if (!isExecutable) return NS_ERROR_FILE_EXECUTION_FAILED;
3322
3323 FSSpec docSpec;
3324 FSSpecPtr docSpecPtr = nsnull;
3325
3326 nsCOMPtr<nsILocalFileMac> macDoc = do_QueryInterface(aDocToLoad);
3327 if (macDoc)
3328 {
3329 rv = macDoc->GetFSSpec(&docSpec); // XXX GetTargetFSSpec
3330 if (NS_FAILED(rv)) return rv;
3331
3332 docSpecPtr = &docSpec;
3333 }
3334
3335 FSSpec appSpec;
3336 rv = GetFSSpec(&appSpec); // XXX GetResolvedFSSpec
3337 if (NS_FAILED(rv)) return rv;
3338
3339 rv = MyLaunchAppWithDoc(appSpec, docSpecPtr, aLaunchInBackground);
3340 return rv;
3341}
3342
3343
3344NS_IMETHODIMP
3345nsLocalFile::OpenDocWithApp(nsILocalFile* aAppToOpenWith, PRBool aLaunchInBackground)
3346{
3347 // if aAppToOpenWith is nil, we have to find the app from the creator code
3348 // of the document
3349 nsresult rv = NS_OK;
3350
3351 FSSpec appSpec;
3352
3353 if (aAppToOpenWith)
3354 {
3355 nsCOMPtr<nsILocalFileMac> appFileMac = do_QueryInterface(aAppToOpenWith, &rv);
3356 if (!appFileMac) return rv;
3357
3358 rv = appFileMac->GetFSSpec(&appSpec); // XXX GetTargetFSSpec
3359 if (NS_FAILED(rv)) return rv;
3360
3361 // is it launchable?
3362 PRBool isExecutable;
3363 rv = aAppToOpenWith->IsExecutable(&isExecutable);
3364 if (NS_FAILED(rv)) return rv;
3365 if (!isExecutable) return NS_ERROR_FILE_EXECUTION_FAILED;
3366 }
3367 else
3368 {
3369 // look for one
3370 OSType fileCreator;
3371 rv = GetFileCreator(&fileCreator);
3372 if (NS_FAILED(rv)) return rv;
3373
3374 // just make one on the stack
3375 nsLocalFile localAppFile;
3376 rv = localAppFile.InitToAppWithCreatorCode(fileCreator);
3377 if (NS_FAILED(rv)) return rv;
3378
3379 rv = localAppFile.GetFSSpec(&appSpec); // GetTargetFSSpec
3380 if (NS_FAILED(rv)) return rv;
3381 }
3382
3383 FSSpec docSpec;
3384 rv = GetFSSpec(&docSpec); // XXX GetResolvedFSSpec
3385 if (NS_FAILED(rv)) return rv;
3386
3387 rv = MyLaunchAppWithDoc(appSpec, &docSpec, aLaunchInBackground);
3388 return rv;
3389}
3390
3391nsresult nsLocalFile::SetOSTypeAndCreatorFromExtension(const char* extension)
3392{
3393 nsresult rv;
3394
3395 nsCAutoString localExtBuf;
3396 const char *extPtr;
3397
3398 if (!extension)
3399 {
3400 rv = GetNativeLeafName(localExtBuf);
3401 extPtr = strrchr(localExtBuf.get(), '.');
3402 if (!extPtr)
3403 return NS_ERROR_FAILURE;
3404 ++extPtr;
3405 }
3406 else
3407 {
3408 extPtr = extension;
3409 if (*extPtr == '.')
3410 ++extPtr;
3411 }
3412
3413 nsCOMPtr<nsIInternetConfigService> icService =
3414 do_GetService(NS_INTERNETCONFIGSERVICE_CONTRACTID, &rv);
3415 if (NS_SUCCEEDED(rv))
3416 {
3417 nsCOMPtr<nsIMIMEInfo> mimeInfo;
3418 rv = icService->GetMIMEInfoFromExtension(extPtr, getter_AddRefs(mimeInfo));
3419 if (NS_SUCCEEDED(rv))
3420 {
3421 PRUint32 osType;
3422 rv = mimeInfo->GetMacType(&osType);
3423 if (NS_SUCCEEDED(rv))
3424 mType = osType;
3425 PRBool skip;
3426 rv = ExtensionIsOnExceptionList(extPtr, &skip);
3427 if (NS_SUCCEEDED(rv) && !skip)
3428 {
3429 rv = mimeInfo->GetMacCreator(&osType);
3430 if (NS_SUCCEEDED(rv))
3431 mCreator = osType;
3432 }
3433 }
3434 }
3435 return rv;
3436}
3437
3438nsresult nsLocalFile::ExtensionIsOnExceptionList(const char *extension, PRBool *onList)
3439{
3440 // Probably want to make a global list somewhere in the future
3441 // for now, just check for "html" and "htm"
3442
3443 *onList = PR_FALSE;
3444
3445 if (!nsCRT::strcasecmp(extension, "html") ||
3446 !nsCRT::strcasecmp(extension, "htm"))
3447 *onList = PR_TRUE;
3448 return NS_OK;
3449}
3450
3451
3452void nsLocalFile::InitClassStatics()
3453{
3454 OSErr err;
3455
3456
3457 if (sCurrentProcessSignature == 0)
3458 {
3459 ProcessSerialNumber psn;
3460 ProcessInfoRec info;
3461
3462 psn.highLongOfPSN = 0;
3463 psn.lowLongOfPSN = kCurrentProcess;
3464
3465 info.processInfoLength = sizeof(ProcessInfoRec);
3466 info.processName = nil;
3467 info.processAppSpec = nil;
3468 err = ::GetProcessInformation(&psn, &info);
3469 if (err == noErr)
3470 sCurrentProcessSignature = info.processSignature;
3471 // Try again next time if error
3472 }
3473
3474 static PRBool didHFSPlusCheck = PR_FALSE;
3475 if (!didHFSPlusCheck)
3476 {
3477 long response;
3478 err = ::Gestalt(gestaltFSAttr, &response);
3479 sHasHFSPlusAPIs = (err == noErr && (response & (1 << gestaltHasHFSPlusAPIs)) != 0);
3480 didHFSPlusCheck = PR_TRUE;
3481 }
3482
3483 static PRBool didOSXCheck = PR_FALSE;
3484 if (!didOSXCheck)
3485 {
3486 long version;
3487 sRunningOSX = (::Gestalt(gestaltSystemVersion, &version) == noErr && version >= 0x00001000);
3488 didOSXCheck = PR_TRUE;
3489 }
3490}
3491
3492
3493#pragma mark -
3494
3495// Handy dandy utility create routine for something or the other
3496nsresult
3497NS_NewNativeLocalFile(const nsACString &path, PRBool followLinks, nsILocalFile* *result)
3498{
3499 nsLocalFile* file = new nsLocalFile();
3500 if (file == nsnull)
3501 return NS_ERROR_OUT_OF_MEMORY;
3502 NS_ADDREF(file);
3503
3504 file->SetFollowLinks(followLinks);
3505
3506 if (!path.IsEmpty()) {
3507 nsresult rv = file->InitWithNativePath(path);
3508 if (NS_FAILED(rv)) {
3509 NS_RELEASE(file);
3510 return rv;
3511 }
3512 }
3513 *result = file;
3514 return NS_OK;
3515}
3516
3517nsresult
3518NS_NewLocalFile(const nsAString &path, PRBool followLinks, nsILocalFile* *result)
3519{
3520 nsCAutoString fsCharSetStr;
3521 nsresult rv = NS_CopyUnicodeToNative(path, fsCharSetStr);
3522 if (NS_FAILED(rv))
3523 return rv;
3524 return NS_NewNativeLocalFile(fsCharSetStr, followLinks, result);
3525}
3526
3527nsresult
3528NS_NewLocalFileWithFSSpec(const FSSpec* inSpec, PRBool followLinks, nsILocalFileMac* *result)
3529{
3530 nsLocalFile* file = new nsLocalFile();
3531 if (file == nsnull)
3532 return NS_ERROR_OUT_OF_MEMORY;
3533 NS_ADDREF(file);
3534
3535 file->SetFollowLinks(followLinks);
3536
3537 nsresult rv = file->InitWithFSSpec(inSpec);
3538 if (NS_FAILED(rv)) {
3539 NS_RELEASE(file);
3540 return rv;
3541 }
3542 *result = file;
3543 return NS_OK;
3544}
3545
3546void
3547nsLocalFile::GlobalInit()
3548{
3549}
3550
3551void
3552nsLocalFile::GlobalShutdown()
3553{
3554}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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