Back to index

lightning-sunbird  0.9+nobinonly
nsLocalFileMac.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code, released
00016  * March 31, 1998.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998-1999
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Steve Dagley <sdagley@netscape.com>
00025  *   John R. McMullen
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 
00042 #include "nsCOMPtr.h"
00043 #include "nsMemory.h"
00044 #include "nsXPIDLString.h" 
00045 
00046 #include "nsLocalFile.h"
00047 #include "nsNativeCharsetUtils.h"
00048 #include "nsISimpleEnumerator.h"
00049 #include "nsIComponentManager.h"
00050 #include "nsIInternetConfigService.h"
00051 #include "nsIMIMEInfo.h"
00052 #include "prtypes.h"
00053 #include "prerror.h"
00054 
00055 #include "nsReadableUtils.h"
00056 #include "nsITimelineService.h"
00057 
00058 #ifdef XP_MACOSX
00059 #include "nsXPIDLString.h"
00060 
00061 #include "private/pprio.h"
00062 #else
00063 #include "pprio.h" // Include this rather than prio.h so we get def of PR_ImportFile
00064 #endif
00065 #include "prmem.h"
00066 #include "plbase64.h"
00067 
00068 #include "FullPath.h"
00069 #include "FileCopy.h"
00070 #include "MoreFilesExtras.h"
00071 #include "DirectoryCopy.h"
00072 #include <Script.h>
00073 #include <Processes.h>
00074 #include <StringCompare.h>
00075 #include <Resources.h>
00076 
00077 #include <AppleEvents.h>
00078 #include <AEDataModel.h>
00079 #include <AERegistry.h>
00080 #include <Gestalt.h>
00081 
00082 #include <Math64.h>
00083 #include <Aliases.h>
00084 #include <Folders.h>
00085 #include <Gestalt.h>
00086 #include "macDirectoryCopy.h"
00087 
00088 #include <limits.h>
00089 
00090 // Stupid @#$% header looks like its got extern mojo but it doesn't really
00091 extern "C"
00092 {
00093 #ifndef XP_MACOSX
00094 // BADPINK - this MSL header doesn't exist under macosx :-(
00095 #include <FSp_fopen.h>
00096 #endif
00097 }
00098 
00099 #if TARGET_CARBON
00100 #include <CodeFragments.h>  // Needed for definition of kUnresolvedCFragSymbolAddress
00101 #include <LaunchServices.h>
00102 #endif
00103 
00104 #pragma mark [Constants]
00105 
00106 const OSType kDefaultCreator = 'MOSS';
00107 
00108 #pragma mark -
00109 #pragma mark [nsPathParser]
00110 
00111 class nsPathParser
00112 {
00113 public:
00114     nsPathParser(const nsACString &path);
00115     
00116     ~nsPathParser()
00117     {
00118         if (mAllocatedBuffer)
00119             nsMemory::Free(mAllocatedBuffer);
00120     }
00121     
00122     const char* First()
00123     {
00124         return nsCRT::strtok(mBuffer, ":", &mNewString);
00125     }
00126     const char* Next()
00127     {
00128         return nsCRT::strtok(mNewString, ":", &mNewString);
00129     }
00130     const char* Remainder()
00131     {
00132         return mNewString;
00133     }
00134 
00135 private:    
00136     char        mAutoBuffer[512];
00137     char        *mAllocatedBuffer;
00138     char        *mBuffer, *mNewString;
00139 };
00140 
00141 nsPathParser::nsPathParser(const nsACString &inPath) :
00142     mAllocatedBuffer(nsnull), mNewString(nsnull)
00143 {
00144     PRUint32 inPathLen = inPath.Length();
00145     if (inPathLen >= sizeof(mAutoBuffer)) {
00146         mAllocatedBuffer = (char *)nsMemory::Alloc(inPathLen + 1);
00147         mBuffer = mAllocatedBuffer;
00148     }
00149     else
00150        mBuffer = mAutoBuffer;
00151     
00152     // copy inPath into mBuffer    
00153     nsACString::const_iterator start, end;
00154     inPath.BeginReading(start);
00155     inPath.EndReading(end);
00156        
00157        PRUint32 size, offset = 0;
00158     for ( ; start != end; start.advance(size)) {
00159         const char* buf = start.get();
00160         size = start.size_forward();
00161         memcpy(mBuffer + offset, buf, size);
00162         offset += size;
00163     }
00164     mBuffer[offset] = '\0';
00165 }
00166 
00167 #pragma mark -
00168 #pragma mark [static util funcs]
00169 
00170 static inline void ClearFSSpec(FSSpec& aSpec)
00171 {
00172     aSpec.vRefNum = 0;
00173     aSpec.parID = 0;
00174     aSpec.name[0] = 0;
00175 }
00176 
00177 
00178 // Simple func to map Mac OS errors into nsresults
00179 static nsresult MacErrorMapper(OSErr inErr)
00180 {
00181     nsresult outErr;
00182     
00183     switch (inErr)
00184     {
00185         case noErr:
00186             outErr = NS_OK;
00187             break;
00188 
00189         case fnfErr:
00190             outErr = NS_ERROR_FILE_NOT_FOUND;
00191             break;
00192 
00193         case dupFNErr:
00194             outErr = NS_ERROR_FILE_ALREADY_EXISTS;
00195             break;
00196         
00197         case dskFulErr:
00198             outErr = NS_ERROR_FILE_DISK_FULL;
00199             break;
00200         
00201         case fLckdErr:
00202             outErr = NS_ERROR_FILE_IS_LOCKED;
00203             break;
00204         
00205         // Can't find good map for some
00206         case bdNamErr:
00207             outErr = NS_ERROR_FAILURE;
00208             break;
00209 
00210         default:    
00211             outErr = NS_ERROR_FAILURE;
00212             break;
00213     }
00214     return outErr;
00215 }
00216 
00217 
00218 
00219 /*----------------------------------------------------------------------------
00220     IsEqualFSSpec 
00221     
00222     Compare two canonical FSSpec records.
00223             
00224     Entry:  file1 = pointer to first FSSpec record.
00225             file2 = pointer to second FSSpec record.
00226     
00227     Exit:   function result = true if the FSSpec records are equal.
00228 ----------------------------------------------------------------------------*/
00229 
00230 static PRBool IsEqualFSSpec(const FSSpec& file1, const FSSpec& file2)
00231 {
00232     return
00233         file1.vRefNum == file2.vRefNum &&
00234         file1.parID == file2.parID &&
00235         EqualString(file1.name, file2.name, false, true);
00236 }
00237 
00238 
00239 /*----------------------------------------------------------------------------
00240     GetParentFolderSpec
00241     
00242     Given an FSSpec to a (possibly non-existent) file, get an FSSpec for its
00243     parent directory.
00244     
00245 ----------------------------------------------------------------------------*/
00246 
00247 static OSErr GetParentFolderSpec(const FSSpec& fileSpec, FSSpec& parentDirSpec)
00248 {
00249     CInfoPBRec  pBlock = {0};
00250     OSErr       err = noErr;
00251     
00252     parentDirSpec.name[0] = 0;
00253     
00254     pBlock.dirInfo.ioVRefNum = fileSpec.vRefNum;
00255     pBlock.dirInfo.ioDrDirID = fileSpec.parID;
00256     pBlock.dirInfo.ioNamePtr = (StringPtr)parentDirSpec.name;
00257     pBlock.dirInfo.ioFDirIndex = -1;        //get info on parID
00258     err = PBGetCatInfoSync(&pBlock);
00259     if (err != noErr) return err;
00260     
00261     parentDirSpec.vRefNum = fileSpec.vRefNum;
00262     parentDirSpec.parID = pBlock.dirInfo.ioDrParID;
00263     
00264     return err;
00265 }
00266 
00267 
00268 /*----------------------------------------------------------------------------
00269     VolHasDesktopDB 
00270     
00271     Check to see if a volume supports the new desktop database.
00272     
00273     Entry:  vRefNum = vol ref num of volumn
00274             
00275     Exit:   function result = error code.
00276             *hasDesktop = true if volume has the new desktop database.
00277 ----------------------------------------------------------------------------*/
00278 
00279 static OSErr VolHasDesktopDB (short vRefNum, Boolean *hasDesktop)
00280 {
00281     HParamBlockRec      pb;
00282     GetVolParmsInfoBuffer   info;
00283     OSErr               err = noErr;
00284     
00285     pb.ioParam.ioCompletion = nil;
00286     pb.ioParam.ioNamePtr = nil;
00287     pb.ioParam.ioVRefNum = vRefNum;
00288     pb.ioParam.ioBuffer = (Ptr)&info;
00289     pb.ioParam.ioReqCount = sizeof(info);
00290     err = PBHGetVolParmsSync(&pb);
00291     *hasDesktop = err == noErr && (info.vMAttrib & (1L << bHasDesktopMgr)) != 0;
00292     return err;
00293 }
00294 
00295 
00296 /*----------------------------------------------------------------------------
00297     GetLastModDateTime
00298     
00299     Get the last mod date and time of a file.
00300     
00301     Entry:  fSpec = pointer to file spec.
00302     
00303     Exit:   function result = error code.
00304             *lastModDateTime = last mod date and time.
00305 ----------------------------------------------------------------------------*/
00306 
00307 static OSErr GetLastModDateTime(const FSSpec *fSpec, unsigned long *lastModDateTime)
00308 {
00309     CInfoPBRec  pBlock;
00310     OSErr       err = noErr;
00311     
00312     pBlock.hFileInfo.ioNamePtr = (StringPtr)fSpec->name;
00313     pBlock.hFileInfo.ioVRefNum = fSpec->vRefNum;
00314     pBlock.hFileInfo.ioFDirIndex = 0;
00315     pBlock.hFileInfo.ioDirID = fSpec->parID;
00316     err = PBGetCatInfoSync(&pBlock);
00317     if (err != noErr) return err;
00318     *lastModDateTime = pBlock.hFileInfo.ioFlMdDat;
00319     return noErr;
00320 }
00321 
00322 
00323 /*----------------------------------------------------------------------------
00324     FindAppOnVolume 
00325     
00326     Find an application on a volume.
00327     
00328     Entry:  sig = application signature.
00329             vRefNum = vol ref num
00330             
00331     Exit:   function result = error code
00332                 = afpItemNotFound if app not found on vol.
00333             *file = file spec for application on volume.
00334 ----------------------------------------------------------------------------*/
00335 
00336 static OSErr FindAppOnVolume (OSType sig, short vRefNum, FSSpec *file)
00337 {
00338     DTPBRec     pb;
00339     OSErr       err = noErr;
00340     short       ioDTRefNum, i;
00341     FInfo       fInfo;
00342     FSSpec      candidate;
00343     unsigned long lastModDateTime, maxLastModDateTime;
00344 
00345     memset(&pb, 0, sizeof(DTPBRec));
00346     pb.ioCompletion = nil;
00347     pb.ioVRefNum = vRefNum;
00348     pb.ioNamePtr = nil;
00349     err = PBDTGetPath(&pb);
00350     if (err != noErr) return err;
00351     ioDTRefNum = pb.ioDTRefNum;
00352 
00353     memset(&pb, 0, sizeof(DTPBRec));
00354     pb.ioCompletion = nil;
00355     pb.ioIndex = 0;
00356     pb.ioFileCreator = sig;
00357     pb.ioNamePtr = file->name;
00358     pb.ioDTRefNum = ioDTRefNum;
00359     err = PBDTGetAPPLSync(&pb);
00360     
00361     if (err == fnfErr || err == paramErr) return afpItemNotFound;
00362     if (err != noErr) return err;
00363 
00364     file->vRefNum = vRefNum;
00365     file->parID = pb.ioAPPLParID;
00366     
00367     err = FSpGetFInfo(file, &fInfo);
00368     if (err == noErr) return noErr;
00369     
00370     i = 1;
00371     maxLastModDateTime = 0;
00372     while (true)
00373     {
00374         memset(&pb, 0, sizeof(DTPBRec)); 
00375         pb.ioCompletion = nil;
00376         pb.ioIndex = i;
00377         pb.ioFileCreator = sig;
00378         pb.ioNamePtr = candidate.name;
00379         pb.ioDTRefNum = ioDTRefNum;
00380         err = PBDTGetAPPLSync(&pb);
00381         if (err != noErr) break;
00382         candidate.vRefNum = vRefNum;
00383         candidate.parID = pb.ioAPPLParID;
00384         err = GetLastModDateTime(file, &lastModDateTime);
00385         if (err == noErr) {
00386             if (lastModDateTime > maxLastModDateTime) {
00387                 maxLastModDateTime = lastModDateTime;
00388                 *file = candidate;
00389             }
00390         }
00391         i++;
00392     }
00393     
00394     return maxLastModDateTime > 0 ? noErr : afpItemNotFound;
00395 }
00396 
00397 
00398 /*----------------------------------------------------------------------------
00399     GetIndVolume 
00400     
00401     Get a volume reference number by volume index.
00402     
00403     Entry:  index = volume index
00404             
00405     Exit:   function result = error code.
00406             *vRefNum = vol ref num of indexed volume.
00407 ----------------------------------------------------------------------------*/
00408 
00409 static OSErr GetIndVolume(short index, short *vRefNum)
00410 {
00411     HParamBlockRec pb;
00412     Str63 volumeName;
00413     OSErr err = noErr;
00414     
00415     pb.volumeParam.ioCompletion = nil;
00416     pb.volumeParam.ioNamePtr = volumeName;
00417     pb.volumeParam.ioVolIndex = index;
00418     
00419     err = PBHGetVInfoSync(&pb);
00420     
00421     *vRefNum = pb.volumeParam.ioVRefNum;
00422     return err;
00423 }
00424 
00425 
00426 // Private NSPR functions
00427 static unsigned long gJanuaryFirst1970Seconds = 0;
00428 /*
00429  * The geographic location and time zone information of a Mac
00430  * are stored in extended parameter RAM.  The ReadLocation
00431  * produdure uses the geographic location record, MachineLocation,
00432  * to read the geographic location and time zone information in
00433  * extended parameter RAM.
00434  *
00435  * Because serial port and SLIP conflict with ReadXPram calls,
00436  * we cache the call here.
00437  *
00438  * Caveat: this caching will give the wrong result if a session
00439  * extend across the DST changeover time.
00440  */
00441 
00442 static void MyReadLocation(MachineLocation *loc)
00443 {
00444     static MachineLocation storedLoc;
00445     static Boolean didReadLocation = false;
00446     
00447     if (!didReadLocation) {
00448         ReadLocation(&storedLoc);
00449         didReadLocation = true;
00450     }
00451     *loc = storedLoc;
00452 }
00453 
00454 static long GMTDelta(void)
00455 {
00456     MachineLocation loc;
00457     long gmtDelta;
00458 
00459     MyReadLocation(&loc);
00460     gmtDelta = loc.u.gmtDelta & 0x00ffffff;
00461     if (gmtDelta & 0x00800000) {    /* test sign extend bit */
00462         gmtDelta |= 0xff000000;
00463     }
00464     return gmtDelta;
00465 }
00466 
00467 static void MacintoshInitializeTime(void)
00468 {
00469     /*
00470      * The NSPR epoch is midnight, Jan. 1, 1970 GMT.
00471      *
00472      * At midnight Jan. 1, 1970 GMT, the local time was
00473      *     midnight Jan. 1, 1970 + GMTDelta().
00474      *
00475      * Midnight Jan. 1, 1970 is 86400 * (365 * (1970 - 1904) + 17)
00476      *     = 2082844800 seconds since the Mac epoch.
00477      * (There were 17 leap years from 1904 to 1970.)
00478      *
00479      * So the NSPR epoch is 2082844800 + GMTDelta() seconds since
00480      * the Mac epoch.  Whew! :-)
00481      */
00482     gJanuaryFirst1970Seconds = 2082844800 + GMTDelta();
00483 }
00484 
00485 static nsresult ConvertMacTimeToMilliseconds( PRInt64* aLastModifiedTime, PRUint32 timestamp )
00486 {
00487     if ( gJanuaryFirst1970Seconds == 0)
00488         MacintoshInitializeTime();
00489     timestamp -= gJanuaryFirst1970Seconds;
00490     PRTime usecPerSec, dateInMicroSeconds;
00491     LL_I2L(dateInMicroSeconds, timestamp);
00492     LL_I2L(usecPerSec, PR_MSEC_PER_SEC);
00493     LL_MUL(*aLastModifiedTime, usecPerSec, dateInMicroSeconds);
00494     return NS_OK;
00495 }
00496 
00497 static nsresult ConvertMillisecondsToMacTime(PRInt64 aTime, PRUint32 *aOutMacTime)
00498 {
00499     NS_ENSURE_ARG( aOutMacTime );
00500         
00501     PRTime usecPerSec, dateInSeconds;
00502     dateInSeconds = LL_ZERO;
00503     
00504     LL_I2L(usecPerSec, PR_MSEC_PER_SEC);
00505     LL_DIV(dateInSeconds, aTime, usecPerSec); // dateInSeconds = aTime/1,000
00506     LL_L2UI(*aOutMacTime, dateInSeconds);
00507     *aOutMacTime += 2082844800; // date + Mac epoch
00508 
00509     return NS_OK;
00510 }
00511 
00512 static void myPLstrcpy(Str255 dst, const char* src)
00513 {
00514     int srcLength = strlen(src);
00515     NS_ASSERTION(srcLength <= 255, "Oops, Str255 can't hold >255 chars");
00516     if (srcLength > 255)
00517         srcLength = 255;
00518     dst[0] = srcLength;
00519     memcpy(&dst[1], src, srcLength);
00520 }
00521 
00522 static void myPLstrncpy(Str255 dst, const char* src, int inMax)
00523 {
00524     int srcLength = strlen(src);
00525     if (srcLength > inMax)
00526         srcLength = inMax;
00527     dst[0] = srcLength;
00528     memcpy(&dst[1], src, srcLength);
00529 }
00530 
00531 /*
00532     NS_TruncNodeName
00533     
00534     Utility routine to do a mid-trunc on a potential file name so that it is
00535     no longer than 31 characters.  Until we move to the HFS+ APIs we need this
00536     to come up with legal Mac file names.
00537 
00538     Entry:  aNode = initial file name
00539             outBuf = scratch buffer for the truncated name (MUST be >= 32 characters)
00540 
00541     Exit:   function result = pointer to truncated name.  Will be either aNode or outBuf.
00542 
00543 */
00544 const char* NS_TruncNodeName(const char *aNode, char *outBuf)
00545 {
00546     PRUint32 nodeLen;
00547     if ((nodeLen = strlen(aNode)) > 31)
00548     {
00549         static PRBool sInitialized = PR_FALSE;
00550         static CharByteTable sTable;
00551         // Init to "..." in case we fail to get the ellipsis token
00552         static char sEllipsisTokenStr[4] = { '.', '.', '.', 0 };
00553         static PRUint8 sEllipsisTokenLen = 3;
00554                 
00555         if (!sInitialized)
00556         {
00557             // Entries in the table are:
00558             // 0 == 1 byte char
00559             // 1 == 2 byte char
00560             FillParseTable(sTable, smSystemScript);
00561             
00562             Handle itl4ResHandle = nsnull;
00563             long offset, len;
00564             ::GetIntlResourceTable(smSystemScript, smUnTokenTable, &itl4ResHandle, &offset, &len);
00565             if (itl4ResHandle)
00566             {
00567                 UntokenTable *untokenTableRec = (UntokenTable *)(*itl4ResHandle + offset);
00568                 if (untokenTableRec->lastToken >= tokenEllipsis)
00569                 {
00570                     offset += untokenTableRec->index[tokenEllipsis];
00571                     char *tokenStr = (*itl4ResHandle + offset);
00572                     sEllipsisTokenLen = tokenStr[0];
00573                     memcpy(sEllipsisTokenStr, &tokenStr[1], sEllipsisTokenLen);
00574                 }
00575                 ::ReleaseResource(itl4ResHandle);
00576             }
00577             sInitialized = PR_TRUE;
00578         }
00579 
00580         PRInt32 halfLen = (31 - sEllipsisTokenLen) / 2;
00581         PRInt32 charSize = 0, srcPos, destPos;
00582         for (srcPos = 0; srcPos + charSize <= halfLen; srcPos += charSize)
00583             charSize = sTable[aNode[srcPos]] ? 2 : 1;
00584                     
00585         memcpy(outBuf, aNode, srcPos);
00586         memcpy(outBuf + srcPos, sEllipsisTokenStr, sEllipsisTokenLen);
00587         destPos = srcPos + sEllipsisTokenLen;
00588         
00589         for (; srcPos < nodeLen - halfLen; srcPos += charSize)
00590             charSize = sTable[aNode[srcPos]] ? 2 : 1;
00591             
00592         memcpy(outBuf + destPos, aNode + srcPos, nodeLen - srcPos);
00593         destPos += (nodeLen - srcPos);
00594         outBuf[destPos] = '\0';
00595         return outBuf;
00596     }
00597     return aNode;
00598 }
00599 
00607 static OSErr HFSPlusGetRawPath(const FSSpec& inSpec, nsAString& outStr)
00608 {
00609     OSErr err;
00610     nsAutoString ucPathString;
00611     
00612     outStr.Truncate(0);
00613     
00614     FSRef nodeRef;
00615     FSCatalogInfo catalogInfo;
00616     catalogInfo.parentDirID = 0;
00617     err = ::FSpMakeFSRef(&inSpec, &nodeRef);
00618     
00619     if (err == fnfErr) {
00620         FSSpec parentDirSpec;               
00621         err = GetParentFolderSpec(inSpec, parentDirSpec);
00622         if (err == noErr) {
00623             const char *startPtr = (const char*)&inSpec.name[1];
00624             NS_CopyNativeToUnicode(Substring(startPtr, startPtr + PRUint32(inSpec.name[0])), outStr);
00625             err = ::FSpMakeFSRef(&parentDirSpec, &nodeRef);
00626         }
00627     }
00628 
00629     while (err == noErr && catalogInfo.parentDirID != fsRtParID) {
00630         HFSUniStr255 nodeName;
00631         FSRef parentRef;
00632         err = ::FSGetCatalogInfo(&nodeRef,
00633                                  kFSCatInfoNodeFlags + kFSCatInfoParentDirID,
00634                                  &catalogInfo,
00635                                  &nodeName,
00636                                  nsnull,
00637                                  &parentRef);
00638         if (err == noErr)
00639         {
00640             if (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask)
00641                 nodeName.unicode[nodeName.length++] = PRUnichar(':');
00642             const PRUnichar* nodeNameUni = (const PRUnichar*) nodeName.unicode;
00643             outStr.Insert(Substring(nodeNameUni, nodeNameUni + nodeName.length), 0);
00644             nodeRef = parentRef;
00645         }
00646     }
00647     return err;
00648 }
00649 
00650 
00651 // The R**co FSSpec resolver -
00652 //  it slices, it dices, it juliannes fries and it even creates FSSpecs out of whatever you feed it
00653 // This function will take a path and a starting FSSpec and generate a FSSpec to represent
00654 // the target of the two.  If the intial FSSpec is null the path alone will be resolved
00655 static OSErr ResolvePathAndSpec(const char * filePath, FSSpec *inSpec, PRBool createDirs, FSSpec *outSpec)
00656 {
00657     OSErr   err = noErr;
00658     size_t  inLength = strlen(filePath);
00659     Boolean isRelative = (filePath && inSpec);
00660     FSSpec  tempSpec;
00661     Str255  ppath;
00662     Boolean isDirectory;
00663     
00664     if (isRelative && inSpec)
00665     {
00666         outSpec->vRefNum = inSpec->vRefNum;
00667         outSpec->parID = inSpec->parID;
00668         
00669         if (inSpec->name[0] != 0)
00670         {
00671             long theDirID;
00672             
00673             err = FSpGetDirectoryID(inSpec, &theDirID, &isDirectory);
00674         
00675             if (err == noErr  &&  isDirectory)
00676                 outSpec->parID = theDirID;
00677             else if (err == fnfErr && createDirs)
00678             {
00679                 err = FSpDirCreate(inSpec, smCurrentScript, &theDirID);
00680                 if (err == noErr)
00681                     outSpec->parID = theDirID;
00682                 else if (err == fnfErr)
00683                     err = dirNFErr; 
00684             }
00685         }
00686     }
00687     else
00688     {
00689         outSpec->vRefNum = 0;
00690         outSpec->parID = 0;
00691     }
00692     
00693     if (err)
00694         return err;
00695     
00696     // Try making an FSSpec from the path
00697     if (inLength < 255)
00698     {
00699         // Use tempSpec as dest because if FSMakeFSSpec returns dirNFErr, it
00700         // will reset the dest spec and we'll lose what we determined above.
00701         
00702         myPLstrcpy(ppath, filePath);
00703         err = ::FSMakeFSSpec(outSpec->vRefNum, outSpec->parID, ppath, &tempSpec);
00704         if (err == noErr || err == fnfErr)
00705             *outSpec = tempSpec;
00706     }
00707     else if (!isRelative)
00708     {
00709         err = ::FSpLocationFromFullPath(inLength, filePath, outSpec);
00710     }
00711     else
00712     {   // If the path is relative and >255 characters we need to manually walk the
00713         // path appending each node to the initial FSSpec so to reach that code we
00714         // set the err to bdNamErr and fall into the code below
00715         err = bdNamErr;
00716     }
00717     
00718     // If we successfully created a spec then leave
00719     if (err == noErr)
00720         return err;
00721     
00722     // We get here when the directory hierarchy needs to be created or we're resolving
00723     // a relative path >255 characters long
00724     if (err == dirNFErr || err == bdNamErr)
00725     {
00726         const char* path = filePath;
00727         
00728         if (!isRelative)    // If path is relative, we still need vRefNum & parID.
00729         {
00730             outSpec->vRefNum = 0;
00731             outSpec->parID = 0;
00732         }
00733 
00734         do
00735         {
00736             // Locate the colon that terminates the node.
00737             // But if we've a partial path (starting with a colon), find the second one.
00738             const char* nextColon = strchr(path + (*path == ':'), ':');
00739             // Well, if there are no more colons, point to the end of the string.
00740             if (!nextColon)
00741                 nextColon = path + strlen(path);
00742 
00743             // Make a pascal string out of this node.  Include initial
00744             // and final colon, if any!
00745             myPLstrncpy(ppath, path, nextColon - path + 1);
00746             
00747             // Use this string as a relative path using the directory created
00748             // on the previous round (or directory 0,0 on the first round).
00749             err = ::FSMakeFSSpec(outSpec->vRefNum, outSpec->parID, ppath, outSpec);
00750 
00751             // If this was the leaf node, then we are done.
00752             if (!*nextColon)
00753                 break;
00754 
00755             // Since there's more to go, we have to get the directory ID, which becomes
00756             // the parID for the next round.
00757             if (err == noErr)
00758             {
00759                 // The directory (or perhaps a file) exists. Find its dirID.
00760                 long dirID;
00761                 err = ::FSpGetDirectoryID(outSpec, &dirID, &isDirectory);
00762                 if (!isDirectory)
00763                     err = dupFNErr; // oops! a file exists with that name.
00764                 if (err != noErr)
00765                     break;          // bail if we've got an error
00766                 outSpec->parID = dirID;
00767             }
00768             else if ((err == fnfErr) && createDirs)
00769             {
00770                 // If we got "file not found" and we're allowed to create directories 
00771                 // then we need to create one
00772                 err = ::FSpDirCreate(outSpec, smCurrentScript, &outSpec->parID);
00773                 // For some reason, this usually returns fnfErr, even though it works.
00774                 if (err == fnfErr)
00775                     err = noErr;
00776             }
00777             if (err != noErr)
00778                 break;
00779             path = nextColon; // next round
00780         } while (true);
00781     }
00782     
00783     return err;
00784 }
00785 
00786 
00787 #pragma mark -
00788 #pragma mark [StFollowLinksState]
00789 class StFollowLinksState
00790 {
00791   public:
00792     StFollowLinksState(nsILocalFile *aFile) :
00793         mFile(aFile)
00794     {
00795         NS_ASSERTION(mFile, "StFollowLinksState passed a NULL file.");
00796         if (mFile)
00797             mFile->GetFollowLinks(&mSavedState);
00798     }
00799 
00800     StFollowLinksState(nsILocalFile *aFile, PRBool followLinksState) :
00801         mFile(aFile)
00802     {
00803         NS_ASSERTION(mFile, "StFollowLinksState passed a NULL file.");
00804         if (mFile) {
00805             mFile->GetFollowLinks(&mSavedState);
00806             mFile->SetFollowLinks(followLinksState);
00807         }
00808     }
00809 
00810     ~StFollowLinksState()
00811     {
00812         if (mFile)
00813             mFile->SetFollowLinks(mSavedState);
00814     }
00815     
00816   private:
00817     nsCOMPtr<nsILocalFile> mFile;
00818     PRBool mSavedState;
00819 };
00820 
00821 #pragma mark -
00822 #pragma mark [nsDirEnumerator]
00823 class nsDirEnumerator : public nsISimpleEnumerator
00824 {
00825     public:
00826 
00827         NS_DECL_ISUPPORTS
00828 
00829         nsDirEnumerator()
00830         {
00831         }
00832 
00833         nsresult Init(nsILocalFileMac* parent) 
00834         {
00835             NS_ENSURE_ARG(parent);
00836             nsresult rv;
00837             FSSpec fileSpec;
00838 
00839             rv = parent->GetFSSpec(&fileSpec);
00840             if (NS_FAILED(rv))
00841                 return rv;
00842 
00843             OSErr err;
00844             Boolean isDirectory;
00845             
00846             err = ::FSpGetDirectoryID(&fileSpec, &mDirID, &isDirectory);
00847             if (err || !isDirectory)
00848                 return NS_ERROR_FILE_NOT_DIRECTORY;
00849                             
00850             mCatInfo.hFileInfo.ioNamePtr = mItemName;
00851             mCatInfo.hFileInfo.ioVRefNum = fileSpec.vRefNum;
00852             mItemIndex = 1;
00853                     
00854             return NS_OK;
00855         }
00856 
00857         NS_IMETHOD HasMoreElements(PRBool *result) 
00858         {
00859             nsresult rv = NS_OK;
00860             if (mNext == nsnull) 
00861             {
00862                 mItemName[0] = 0;
00863                 mCatInfo.dirInfo.ioFDirIndex = mItemIndex;
00864                 mCatInfo.dirInfo.ioDrDirID = mDirID;
00865                 
00866                 OSErr err = ::PBGetCatInfoSync(&mCatInfo);
00867                 if (err == fnfErr)
00868                 {
00869                     // end of dir entries
00870                     *result = PR_FALSE;
00871                     return  NS_OK;
00872                 }
00873                 
00874                 // Make a new nsILocalFile for the new element
00875                 FSSpec  tempSpec;
00876                 tempSpec.vRefNum = mCatInfo.hFileInfo.ioVRefNum;
00877                 tempSpec.parID = mDirID;
00878                 ::BlockMoveData(mItemName, tempSpec.name, mItemName[0] + 1);
00879                 
00880                 rv = NS_NewLocalFileWithFSSpec(&tempSpec, PR_TRUE, getter_AddRefs(mNext));
00881                 if (NS_FAILED(rv)) 
00882                     return rv;
00883             }
00884             *result = mNext != nsnull;
00885             return NS_OK;
00886         }
00887 
00888         NS_IMETHOD GetNext(nsISupports **result) 
00889         {
00890             NS_ENSURE_ARG_POINTER(result);
00891             *result = nsnull;
00892 
00893             nsresult rv;
00894             PRBool hasMore;
00895             rv = HasMoreElements(&hasMore);
00896             if (NS_FAILED(rv)) return rv;
00897 
00898             *result = mNext;        // might return nsnull
00899             NS_IF_ADDREF(*result);
00900 
00901             mNext = nsnull;
00902             ++mItemIndex;
00903             return NS_OK;
00904         }
00905 
00906     private:
00907         ~nsDirEnumerator() {}
00908 
00909     protected:
00910         nsCOMPtr<nsILocalFileMac>   mNext;
00911 
00912         CInfoPBRec              mCatInfo;
00913         short                   mItemIndex;
00914         long                    mDirID;
00915         Str63                   mItemName;
00916 };
00917 
00918 NS_IMPL_ISUPPORTS1(nsDirEnumerator, nsISimpleEnumerator)
00919 
00920 #pragma mark -
00921 
00922 OSType nsLocalFile::sCurrentProcessSignature = 0;
00923 PRBool nsLocalFile::sHasHFSPlusAPIs = PR_FALSE;
00924 PRBool nsLocalFile::sRunningOSX = PR_FALSE;
00925 
00926 #pragma mark [CTOR/DTOR]
00927 nsLocalFile::nsLocalFile() :
00928     mFollowLinks(PR_TRUE),
00929     mFollowLinksDirty(PR_TRUE),
00930     mSpecDirty(PR_TRUE),
00931     mCatInfoDirty(PR_TRUE),
00932     mType('TEXT'),
00933     mCreator(kDefaultCreator)
00934 {
00935     ClearFSSpec(mSpec);
00936     ClearFSSpec(mTargetSpec);
00937     
00938     InitClassStatics();
00939     if (sCurrentProcessSignature != 0)
00940         mCreator = sCurrentProcessSignature;
00941 }
00942 
00943 nsLocalFile::nsLocalFile(const nsLocalFile& srcFile)
00944 {
00945     *this = srcFile;
00946 }
00947 
00948 nsLocalFile::nsLocalFile(const FSSpec& aSpec, const nsACString& aAppendedPath) :
00949     mFollowLinks(PR_TRUE),
00950     mFollowLinksDirty(PR_TRUE),
00951     mSpecDirty(PR_TRUE),
00952     mSpec(aSpec),
00953     mAppendedPath(aAppendedPath),
00954     mCatInfoDirty(PR_TRUE),
00955     mType('TEXT'),
00956     mCreator(kDefaultCreator)
00957 {
00958     ClearFSSpec(mTargetSpec);
00959     
00960     InitClassStatics();
00961     if (sCurrentProcessSignature != 0)
00962         mCreator = sCurrentProcessSignature;
00963 }
00964 
00965 nsLocalFile& nsLocalFile::operator=(const nsLocalFile& rhs)
00966 {
00967     mFollowLinks = rhs.mFollowLinks;
00968     mFollowLinksDirty = rhs.mFollowLinksDirty;
00969     mSpecDirty = rhs.mSpecDirty;
00970     mSpec = rhs.mSpec;
00971     mAppendedPath = rhs.mAppendedPath;
00972     mTargetSpec = rhs.mTargetSpec;
00973     mCatInfoDirty = rhs.mCatInfoDirty;
00974     mType = rhs.mType;
00975     mCreator = rhs.mCreator;
00976 
00977     if (!rhs.mCatInfoDirty)
00978         mCachedCatInfo = rhs.mCachedCatInfo;
00979 
00980     return *this;
00981 }
00982 
00983 #pragma mark -
00984 #pragma mark [nsISupports interface implementation]
00985 
00986 NS_IMPL_THREADSAFE_ISUPPORTS3(nsLocalFile,
00987                               nsILocalFileMac,
00988                               nsILocalFile,
00989                               nsIFile)
00990 
00991 NS_METHOD
00992 nsLocalFile::nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
00993 {
00994     NS_ENSURE_ARG_POINTER(aInstancePtr);
00995     NS_ENSURE_NO_AGGREGATION(outer);
00996 
00997     nsLocalFile* inst = new nsLocalFile();
00998     if (inst == NULL)
00999         return NS_ERROR_OUT_OF_MEMORY;
01000     
01001     nsresult rv = inst->QueryInterface(aIID, aInstancePtr);
01002     if (NS_FAILED(rv))
01003     {
01004         delete inst;
01005         return rv;
01006     }
01007     return NS_OK;
01008 }
01009 
01010 // This function resets any cached information about the file.
01011 void
01012 nsLocalFile::MakeDirty()
01013 {
01014     mSpecDirty = PR_TRUE;
01015     mFollowLinksDirty = PR_TRUE;
01016     mCatInfoDirty = PR_TRUE;
01017     
01018     ClearFSSpec(mTargetSpec);
01019 }
01020 
01021 
01022 /* attribute PRBool followLinks; */
01023 NS_IMETHODIMP 
01024 nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
01025 {
01026     NS_ENSURE_ARG_POINTER(aFollowLinks);
01027     *aFollowLinks = mFollowLinks;
01028     return NS_OK;
01029 }
01030 
01031 NS_IMETHODIMP 
01032 nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
01033 {
01034     if (aFollowLinks != mFollowLinks)
01035     {
01036         mFollowLinks = aFollowLinks;
01037         mFollowLinksDirty = PR_TRUE;
01038     }
01039     return NS_OK;
01040 }
01041 
01042 NS_IMETHODIMP
01043 nsLocalFile::ResolveAndStat()
01044 {
01045     OSErr   err = noErr;
01046     FSSpec  resolvedSpec;
01047     
01048     // fnfErr means target spec is valid but doesn't exist.
01049     // If the end result is fnfErr, we're cleanly resolved.
01050     
01051     if (mSpecDirty)
01052     {
01053         if (mAppendedPath.Length())
01054         {
01055             err = ResolvePathAndSpec(mAppendedPath.get(), &mSpec, PR_FALSE, &resolvedSpec);
01056             if (err == noErr)
01057                 mAppendedPath.Truncate(0);
01058         }
01059         else
01060             err = ::FSMakeFSSpec(mSpec.vRefNum, mSpec.parID, mSpec.name, &resolvedSpec);
01061            if (err == noErr)
01062            {
01063                mSpec = resolvedSpec;
01064                mSpecDirty = PR_FALSE;
01065            }
01066         mFollowLinksDirty = PR_TRUE;
01067     }
01068     if (mFollowLinksDirty && (err == noErr))
01069     {
01070         if (mFollowLinks)
01071         {
01072             // Resolve the alias to the original file.
01073             resolvedSpec = mSpec;
01074             Boolean resolvedWasFolder, resolvedWasAlias;
01075             err = ::ResolveAliasFile(&resolvedSpec, TRUE, &resolvedWasFolder, &resolvedWasAlias);
01076             if (err == noErr || err == fnfErr) {
01077                 err = noErr;
01078                 mTargetSpec = resolvedSpec;
01079                 mFollowLinksDirty = PR_FALSE;
01080             }
01081         }
01082         else
01083         {
01084             mTargetSpec = mSpec;
01085             mFollowLinksDirty = PR_FALSE;
01086         }
01087         mCatInfoDirty = PR_TRUE;
01088     }       
01089     
01090     return (MacErrorMapper(err));
01091 }
01092     
01093 
01094 NS_IMETHODIMP  
01095 nsLocalFile::Clone(nsIFile **file)
01096 {
01097     // Just copy-construct ourselves
01098     *file = new nsLocalFile(*this);
01099     if (!*file)
01100       return NS_ERROR_OUT_OF_MEMORY;
01101 
01102     NS_ADDREF(*file);
01103     
01104     return NS_OK;
01105 }
01106 
01107 NS_IMETHODIMP  
01108 nsLocalFile::InitWithNativePath(const nsACString &filePath)
01109 {
01110     // The incoming path must be a FULL path
01111 
01112        if (filePath.IsEmpty())
01113               return NS_ERROR_INVALID_ARG;
01114               
01115     MakeDirty();
01116 
01117     // If it starts with a colon, it's invalid
01118     if (filePath.First() == ':')
01119         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
01120     
01121     nsPathParser parser(filePath);
01122     OSErr err;
01123     Str255 pascalNode;
01124     FSSpec nodeSpec;
01125     
01126     const char *root = parser.First();
01127     if (root == nsnull)
01128         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
01129         
01130     // The first component must be an existing volume
01131     myPLstrcpy(pascalNode, root);
01132     pascalNode[++pascalNode[0]] = ':';
01133     err = ::FSMakeFSSpec(0, 0, pascalNode, &nodeSpec);
01134     if (err)
01135         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
01136     
01137     // Build as much of a spec as possible from the rest of the path
01138     // What doesn't exist will be left over in mAppendedPath
01139     const char *nextNode;    
01140     while ((nextNode = parser.Next()) != nsnull) {
01141         long dirID;
01142         Boolean isDir;
01143         err = ::FSpGetDirectoryID(&nodeSpec, &dirID, &isDir);
01144         if (err || !isDir)
01145             break;
01146         myPLstrcpy(pascalNode, nextNode);
01147         err = ::FSMakeFSSpec(nodeSpec.vRefNum, dirID, pascalNode, &nodeSpec);
01148         if (err == fnfErr)
01149             break;
01150     }
01151     mSpec = nodeSpec;
01152     mAppendedPath = parser.Remainder();
01153     
01154     return NS_OK;
01155 }
01156 
01157 NS_IMETHODIMP  
01158 nsLocalFile::InitWithPath(const nsAString &filePath)
01159 {
01160    nsresult rv;
01161    nsCAutoString fsStr;
01162    
01163    if (NS_SUCCEEDED(rv = NS_CopyUnicodeToNative(filePath, fsStr)))
01164      rv = InitWithNativePath(fsStr);
01165 
01166    return rv;
01167 }
01168 
01169 NS_IMETHODIMP  
01170 nsLocalFile::InitWithFile(nsILocalFile *aFile)
01171 {
01172     NS_ENSURE_ARG(aFile);
01173     nsLocalFile *asLocalFile = dynamic_cast<nsLocalFile*>(aFile);
01174     if (!asLocalFile)
01175         return NS_ERROR_NO_INTERFACE; // Well, sort of.
01176     *this = *asLocalFile;
01177     return NS_OK;
01178 }
01179 
01180 NS_IMETHODIMP  
01181 nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval)
01182 {
01183 // Macintosh doesn't really have mode bits, just drop them
01184 #pragma unused (mode)
01185 
01186     NS_ENSURE_ARG(_retval);
01187     
01188     nsresult rv = NS_OK;
01189     FSSpec  spec;
01190     OSErr err = noErr;
01191     
01192     rv = ResolveAndStat();
01193     if (rv == NS_ERROR_FILE_NOT_FOUND && (flags & PR_CREATE_FILE))
01194         rv = NS_OK;
01195         
01196     if (flags & PR_CREATE_FILE) {
01197         rv = Create(nsIFile::NORMAL_FILE_TYPE, 0);
01198         /* If opening with the PR_EXCL flag the existence of the file prior to opening is an error */
01199         if ((flags & PR_EXCL) &&  (rv == NS_ERROR_FILE_ALREADY_EXISTS))
01200             return rv;
01201     }
01202     
01203     rv = GetFSSpec(&spec);
01204     if (NS_FAILED(rv))
01205         return rv;
01206     
01207     SInt8 perm;
01208     if (flags & PR_RDWR)
01209        perm = fsRdWrPerm;
01210     else if (flags & PR_WRONLY)
01211        perm = fsWrPerm;
01212     else
01213        perm = fsRdPerm;
01214 
01215     short refnum;
01216     err = ::FSpOpenDF(&spec, perm, &refnum);
01217 
01218     if (err == noErr && (flags & PR_TRUNCATE))
01219         err = ::SetEOF(refnum, 0);
01220     if (err == noErr && (flags & PR_APPEND))
01221         err = ::SetFPos(refnum, fsFromLEOF, 0);
01222     if (err != noErr)
01223         return MacErrorMapper(err);
01224 
01225     if ((*_retval = PR_ImportFile(refnum)) == 0)
01226         return NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES,(PR_GetError() & 0xFFFF));
01227     
01228     return NS_OK;
01229 }
01230 
01231 NS_IMETHODIMP  
01232 nsLocalFile::OpenANSIFileDesc(const char *mode, FILE * *_retval)
01233 {
01234     NS_ENSURE_ARG(mode);
01235     NS_ENSURE_ARG_POINTER(_retval);
01236     
01237     nsresult rv; 
01238     FSSpec spec;
01239 
01240     rv = ResolveAndStat();
01241     if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
01242         return rv;
01243     
01244     if (mode[0] == 'w' || mode[0] == 'a') // Create if it doesn't exist   
01245     {
01246         if (rv == NS_ERROR_FILE_NOT_FOUND) {
01247             mType = (mode[1] == 'b') ? 'BiNA' : 'TEXT';
01248             rv = Create(nsIFile::NORMAL_FILE_TYPE, 0);
01249             if (NS_FAILED(rv))
01250                 return rv;
01251         }
01252     }
01253 
01254     rv = GetFSSpec(&spec);
01255     if (NS_FAILED(rv))
01256         return rv;
01257                 
01258 #ifdef MACOSX
01259     // FSp_fopen() doesn't exist under macosx :-(
01260     nsXPIDLCString ourPath;
01261     rv = GetPath(getter_Copies(ourPath));
01262     if (NS_FAILED(rv))
01263         return rv;
01264     *_retval = fopen(ourPath, mode);
01265 #else
01266     *_retval = FSp_fopen(&spec, mode);
01267 #endif
01268 
01269     if (*_retval)
01270         return NS_OK;
01271 
01272     return NS_ERROR_FAILURE;
01273 }
01274 
01275 
01276 NS_IMETHODIMP  
01277 nsLocalFile::Create(PRUint32 type, PRUint32 attributes)
01278 { 
01279     OSErr   err;
01280 
01281     if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
01282         return NS_ERROR_FILE_UNKNOWN_TYPE;
01283 
01284     FSSpec newSpec;
01285 
01286     if (mAppendedPath.Length())
01287     {   // We've got an FSSpec and an appended path so pass 'em both to ResolvePathAndSpec
01288         err = ResolvePathAndSpec(mAppendedPath.get(), &mSpec, PR_TRUE, &newSpec);
01289     }
01290     else
01291     {
01292         err = ::FSMakeFSSpec(mSpec.vRefNum, mSpec.parID, mSpec.name, &newSpec);
01293     }
01294     
01295     if (err != noErr && err != fnfErr)
01296         return (MacErrorMapper(err));
01297 
01298     switch (type)
01299     {
01300         case NORMAL_FILE_TYPE:
01301             SetOSTypeAndCreatorFromExtension();
01302             err = ::FSpCreate(&newSpec, mCreator, mType, smCurrentScript);
01303             break;
01304 
01305         case DIRECTORY_TYPE:
01306             {
01307                 long newDirID;
01308                 err = ::FSpDirCreate(&newSpec, smCurrentScript, &newDirID);
01309                 // For some reason, this usually returns fnfErr, even though it works.
01310                 if (err == fnfErr)
01311                     err = noErr;
01312             }
01313             break;
01314         
01315         default:
01316             return NS_ERROR_FILE_UNKNOWN_TYPE;
01317             break;
01318     }
01319 
01320     if (err == noErr)
01321     {
01322         mSpec = mTargetSpec = newSpec;
01323         mAppendedPath.Truncate(0);
01324     }
01325     
01326     return (MacErrorMapper(err));
01327 }
01328 
01329 NS_IMETHODIMP  
01330 nsLocalFile::AppendNative(const nsACString &aNode)
01331 {
01332     if (aNode.IsEmpty())
01333         return NS_OK;
01334     
01335     nsACString::const_iterator start, end;
01336     aNode.BeginReading(start);
01337     aNode.EndReading(end);
01338     if (FindCharInReadable(':', start, end))
01339         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
01340     
01341     MakeDirty();
01342     
01343     char truncBuffer[32];
01344     const char *node = NS_TruncNodeName(PromiseFlatCString(aNode).get(), truncBuffer);
01345     
01346     if (!mAppendedPath.Length())
01347     {
01348         OSErr   err;
01349         Boolean resolvedWasFolder, resolvedWasAlias;
01350         err = ::ResolveAliasFile(&mSpec, TRUE, &resolvedWasFolder, &resolvedWasAlias);
01351         if (err == noErr)
01352         {
01353             long    dirID;
01354             Boolean isDir;
01355 
01356             if (!resolvedWasFolder)
01357                 return NS_ERROR_FILE_NOT_DIRECTORY;
01358             if ((err = ::FSpGetDirectoryID(&mSpec, &dirID, &isDir)) != noErr)
01359                 return MacErrorMapper(err);
01360                 
01361             FSSpec childSpec;    
01362             Str255 pascalNode;
01363             myPLstrcpy(pascalNode, node);
01364             err = ::FSMakeFSSpec(mSpec.vRefNum, dirID, pascalNode, &childSpec);
01365             if (err && err != fnfErr)
01366                 return MacErrorMapper(err);
01367             mSpec = childSpec;
01368         }
01369         else if (err == fnfErr)
01370             mAppendedPath.Assign(node);
01371         else
01372             return MacErrorMapper(err);
01373     }
01374     else
01375     {
01376         if (mAppendedPath.First() != ':')
01377             mAppendedPath.Insert(':', 0);
01378         mAppendedPath.Append(":");
01379         mAppendedPath.Append(node);
01380     }
01381     
01382     return NS_OK;
01383 }
01384 
01385 NS_IMETHODIMP  
01386 nsLocalFile::Append(const nsAString &node)
01387 {
01388    nsresult rv;
01389    nsCAutoString fsStr;
01390    
01391    if (NS_SUCCEEDED(rv = NS_CopyUnicodeToNative(node, fsStr)))
01392      rv = AppendNative(fsStr);
01393 
01394    return rv;
01395 }
01396     
01397 NS_IMETHODIMP  
01398 nsLocalFile::AppendRelativeNativePath(const nsACString &relPath)
01399 {
01400     if (relPath.IsEmpty())
01401         return NS_ERROR_INVALID_ARG;
01402     
01403     nsresult        rv;
01404     nsPathParser    parser(relPath);
01405     const char*     node = parser.First();
01406     
01407     while (node)
01408     {
01409         if (NS_FAILED(rv = AppendNative(nsDependentCString(node))))
01410             return rv;
01411         node = parser.Next();
01412     }
01413         
01414     return NS_OK;
01415 }
01416 
01417 NS_IMETHODIMP  
01418 nsLocalFile::AppendRelativePath(const nsAString &relPath)
01419 {
01420    nsresult rv;
01421    nsCAutoString fsStr;
01422    
01423    if (NS_SUCCEEDED(rv = NS_CopyUnicodeToNative(relPath, fsStr)))
01424      rv = AppendRelativeNativePath(fsStr);
01425 
01426    return rv;
01427 }
01428 
01429 NS_IMETHODIMP  
01430 nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
01431 {
01432     aLeafName.Truncate();
01433  
01434     // See if we've had a path appended
01435     if (mAppendedPath.Length())
01436     {
01437         const char* temp = mAppendedPath.get();
01438         if (temp == nsnull)
01439             return NS_ERROR_FILE_UNRECOGNIZED_PATH;
01440 
01441         const char* leaf = strrchr(temp, ':');
01442         
01443         // if the working path is just a node without any directory delimiters.
01444         if (leaf == nsnull)
01445             leaf = temp;
01446         else
01447             leaf++;
01448 
01449         aLeafName = leaf;
01450     }
01451     else
01452     {
01453         // We don't have an appended path so grab the leaf name from the FSSpec
01454         // Convert the Pascal string to a C string              
01455         PRInt32 len = mSpec.name[0];
01456         char* leafName = (char *)malloc(len + 1);
01457         if (!leafName) return NS_ERROR_OUT_OF_MEMORY;               
01458         ::BlockMoveData(&mSpec.name[1], leafName, len);
01459         leafName[len] = '\0';
01460         aLeafName = leafName;
01461         free(leafName);
01462     }
01463  
01464     return NS_OK;
01465 }
01466 
01467 NS_IMETHODIMP  
01468 nsLocalFile::GetLeafName(nsAString &aLeafName)
01469 {
01470    nsresult rv;
01471    nsCAutoString fsStr;
01472    
01473    if (NS_SUCCEEDED(rv = GetNativeLeafName(fsStr)))
01474      rv = NS_CopyNativeToUnicode(fsStr, aLeafName);
01475    return rv;
01476 }
01477 
01478 NS_IMETHODIMP  
01479 nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
01480 {
01481     if (aLeafName.IsEmpty())
01482         return NS_ERROR_INVALID_ARG;
01483 
01484     MakeDirty();
01485 
01486     char truncBuffer[32];
01487     const char *leafName = NS_TruncNodeName(PromiseFlatCString(aLeafName).get(), truncBuffer);
01488 
01489     if (mAppendedPath.Length())
01490     {   // Lop off the end of the appended path and replace it with the new leaf name
01491         PRInt32 offset = mAppendedPath.RFindChar(':');
01492         if (offset || ((!offset) && (1 < mAppendedPath.Length())))
01493         {
01494             mAppendedPath.Truncate(offset + 1);
01495         }
01496         mAppendedPath.Append(leafName);
01497     }
01498     else
01499     {
01500         // We don't have an appended path so directly modify the FSSpec
01501         myPLstrcpy(mSpec.name, leafName);
01502     }
01503     
01504     return NS_OK;
01505 }
01506 
01507 NS_IMETHODIMP  
01508 nsLocalFile::SetLeafName(const nsAString &aLeafName)
01509 {
01510    nsresult rv;
01511    nsCAutoString fsStr;
01512    
01513    if (NS_SUCCEEDED(rv = NS_CopyUnicodeToNative(aLeafName, fsStr)))
01514      rv = SetNativeLeafName(fsStr);
01515 
01516    return rv;
01517 }
01518 
01519 NS_IMETHODIMP  
01520 nsLocalFile::GetNativePath(nsACString &_retval)
01521 {
01522     _retval.Truncate();
01523 
01524     nsCAutoString fsCharSetPathStr;
01525                 
01526 #if TARGET_CARBON
01527     if (sHasHFSPlusAPIs) // should always be true under Carbon, but in case...
01528     {
01529         OSErr err;
01530         nsresult rv;
01531         nsAutoString ucPathString;
01532         
01533         if ((err = HFSPlusGetRawPath(mSpec, ucPathString)) != noErr)
01534             return MacErrorMapper(err);
01535         rv = NS_CopyUnicodeToNative(ucPathString, fsCharSetPathStr);
01536         if (NS_FAILED(rv))
01537             return rv;
01538     }
01539     else
01540 #endif
01541     {
01542         // Now would be a good time to call the code that makes an FSSpec into a path
01543         short   fullPathLen;
01544         Handle  fullPathHandle;
01545         (void)::FSpGetFullPath(&mSpec, &fullPathLen, &fullPathHandle);
01546         if (!fullPathHandle)
01547             return NS_ERROR_OUT_OF_MEMORY;
01548         
01549         ::HLock(fullPathHandle);        
01550         fsCharSetPathStr.Assign(*fullPathHandle, fullPathLen);
01551         ::DisposeHandle(fullPathHandle);
01552     }
01553 
01554     // We need to make sure that even if we have a path to a
01555     // directory we don't return the trailing colon. It breaks
01556     // the component manager. (Bugzilla bug #26102) 
01557     if (fsCharSetPathStr.Last() == ':')
01558         fsCharSetPathStr.Truncate(fsCharSetPathStr.Length() - 1);
01559     
01560     // Now, tack on mAppendedPath. It never ends in a colon.
01561     if (mAppendedPath.Length())
01562     {
01563         if (mAppendedPath.First() != ':')
01564             fsCharSetPathStr.Append(":");
01565         fsCharSetPathStr.Append(mAppendedPath);
01566     }
01567     
01568     _retval = fsCharSetPathStr;
01569     return NS_OK;
01570 }
01571 
01572 NS_IMETHODIMP  
01573 nsLocalFile::GetPath(nsAString &_retval)
01574 {
01575     nsresult rv = NS_OK;
01576                 
01577 #if TARGET_CARBON
01578     if (sHasHFSPlusAPIs) // should always be true under Carbon, but in case...
01579     {
01580         OSErr err;
01581         nsAutoString ucPathString;
01582         
01583         if ((err = HFSPlusGetRawPath(mSpec, ucPathString)) != noErr)
01584             return MacErrorMapper(err);
01585             
01586         // We need to make sure that even if we have a path to a
01587         // directory we don't return the trailing colon. It breaks
01588         // the component manager. (Bugzilla bug #26102) 
01589         if (ucPathString.Last() == PRUnichar(':'))
01590             ucPathString.Truncate(ucPathString.Length() - 1);
01591         
01592         // Now, tack on mAppendedPath. It never ends in a colon.
01593         if (mAppendedPath.Length())
01594         {
01595             nsAutoString ucAppendage;
01596             if (mAppendedPath.First() != ':')
01597                 ucPathString.Append(PRUnichar(':'));
01598             rv = NS_CopyNativeToUnicode(mAppendedPath, ucAppendage);
01599             if (NS_FAILED(rv))
01600                 return rv;
01601             ucPathString.Append(ucAppendage);
01602         }
01603         
01604         _retval = ucPathString;
01605     }
01606     else
01607 #endif
01608     {
01609         nsCAutoString fsStr;
01610 
01611         if (NS_SUCCEEDED(rv = GetNativePath(fsStr))) {
01612             rv = NS_CopyNativeToUnicode(fsStr, _retval);
01613         }
01614     }
01615     return rv;
01616 }
01617 
01618 nsresult nsLocalFile::MoveCopy( nsIFile* newParentDir, const nsACString &newName, PRBool isCopy, PRBool followLinks )
01619 {
01620     OSErr macErr;
01621     FSSpec srcSpec;
01622     Str255 newPascalName;
01623     nsresult rv;
01624     
01625     StFollowLinksState srcFollowState(this, followLinks);
01626     rv = GetFSSpec(&srcSpec);
01627     if ( NS_FAILED( rv ) )
01628         return rv;  
01629     
01630     // If newParentDir == nsnull, it's a simple rename
01631     if ( !newParentDir )
01632     {
01633         myPLstrncpy( newPascalName, PromiseFlatCString(newName).get(), 255 );
01634         macErr = ::FSpRename( &srcSpec, newPascalName );
01635         return MacErrorMapper( macErr );
01636     }
01637 
01638     nsCOMPtr<nsILocalFileMac> destDir(do_QueryInterface( newParentDir ));
01639     StFollowLinksState destFollowState(destDir, followLinks);
01640     FSSpec destSpec;
01641     rv = destDir->GetFSSpec(&destSpec);
01642     if ( NS_FAILED( rv ) )
01643         return rv;
01644     
01645     long dirID;
01646     Boolean isDirectory;    
01647     macErr = ::FSpGetDirectoryID(&destSpec, &dirID, &isDirectory);      
01648     if ( macErr || !isDirectory )
01649         return NS_ERROR_FILE_DESTINATION_NOT_DIR;
01650 
01651     if ( !newName.IsEmpty() )
01652         myPLstrncpy( newPascalName, PromiseFlatCString(newName).get(), 255);
01653     else
01654         memcpy(newPascalName, srcSpec.name, srcSpec.name[0] + 1);
01655     if ( isCopy )
01656     {
01657         macErr = ::FSpGetDirectoryID(&srcSpec, &dirID, &isDirectory);       
01658         if (macErr == noErr)
01659         {
01660             const PRInt32   kCopyBufferSize = (1024 * 512);   // allocate our own buffer to speed file copies. Bug #103202
01661             OSErr     tempErr;
01662             Handle    copyBufferHand = ::TempNewHandle(kCopyBufferSize, &tempErr);
01663             void*     copyBuffer = nsnull;
01664             PRInt32   copyBufferSize = 0;
01665 
01666             // it's OK if the allocated failed; FSpFileCopy will just fall back on its own internal 16k buffer
01667             if (copyBufferHand) 
01668             {
01669               ::HLock(copyBufferHand);
01670               copyBuffer = *copyBufferHand;
01671               copyBufferSize = kCopyBufferSize;
01672             }
01673 
01674             if ( isDirectory )
01675                 macErr = MacFSpDirectoryCopyRename( &srcSpec, &destSpec, newPascalName, copyBuffer, copyBufferSize, true, NULL );
01676             else
01677                 macErr = ::FSpFileCopy( &srcSpec, &destSpec, newPascalName, copyBuffer, copyBufferSize, true );
01678 
01679             if (copyBufferHand)
01680               ::DisposeHandle(copyBufferHand);
01681         }
01682     }
01683     else
01684     {
01685         macErr= ::FSpMoveRenameCompat(&srcSpec, &destSpec, newPascalName);
01686         if ( macErr == diffVolErr)
01687         {
01688                 // On a different Volume so go for Copy and then delete
01689                 rv = CopyToNative( newParentDir, newName );
01690                 if ( NS_FAILED ( rv ) )
01691                     return rv;
01692                 return Remove( PR_TRUE );
01693         }
01694     }   
01695     return MacErrorMapper( macErr );
01696 }
01697 
01698 NS_IMETHODIMP  
01699 nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString &newName)
01700 {
01701     return MoveCopy( newParentDir, newName, PR_TRUE, PR_FALSE );
01702 }
01703 
01704 NS_IMETHODIMP  
01705 nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
01706 {
01707     if (newName.IsEmpty())
01708         return CopyToNative(newParentDir, EmptyCString());
01709         
01710     nsresult rv;
01711     nsCAutoString fsStr;
01712     if (NS_SUCCEEDED(rv = NS_CopyUnicodeToNative(newName, fsStr)))
01713         rv = CopyToNative(newParentDir, fsStr);
01714     return rv;
01715 }
01716 
01717 NS_IMETHODIMP  
01718 nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString &newName)
01719 {
01720     return MoveCopy( newParentDir, newName, PR_TRUE, PR_TRUE );
01721 }
01722 
01723 NS_IMETHODIMP  
01724 nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
01725 {
01726     if (newName.IsEmpty())
01727         return CopyToFollowingLinksNative(newParentDir, EmptyCString());
01728 
01729     nsresult rv;
01730     nsCAutoString fsStr;
01731     if (NS_SUCCEEDED(rv = NS_CopyUnicodeToNative(newName, fsStr)))
01732         rv = CopyToFollowingLinksNative(newParentDir, fsStr);
01733     return rv;
01734 }
01735 
01736 NS_IMETHODIMP  
01737 nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString &newName)
01738 {
01739     return MoveCopy( newParentDir, newName, PR_FALSE, PR_FALSE );
01740 }
01741 
01742 NS_IMETHODIMP  
01743 nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
01744 {
01745     if (newName.IsEmpty())
01746         return MoveToNative(newParentDir, EmptyCString());
01747         
01748     nsresult rv;
01749     nsCAutoString fsStr;
01750     if (NS_SUCCEEDED(rv = NS_CopyUnicodeToNative(newName, fsStr)))
01751         rv = MoveToNative(newParentDir, fsStr);
01752     return rv;
01753 }
01754 
01755 NS_IMETHODIMP  
01756 nsLocalFile::Load(PRLibrary * *_retval)
01757 {
01758     PRBool isFile;
01759     nsresult rv = IsFile(&isFile);
01760 
01761     if (NS_FAILED(rv))
01762         return rv;
01763     
01764     if (! isFile)
01765         return NS_ERROR_FILE_IS_DIRECTORY;
01766 
01767   NS_TIMELINE_START_TIMER("PR_LoadLibrary");
01768 
01769 #if !TARGET_CARBON      
01770     // This call to SystemTask is here to give the OS time to grow its
01771     // FCB (file control block) list, which it seems to be unable to
01772     // do unless we yield some time to the OS. See bugs 64978 & 70543
01773     // for the whole story.
01774     ::SystemTask();
01775 #endif
01776 
01777     // Use the new PR_LoadLibraryWithFlags which allows us to use a FSSpec
01778     PRLibSpec libSpec;
01779     libSpec.type = PR_LibSpec_MacIndexedFragment;
01780     libSpec.value.mac_indexed_fragment.fsspec = &mTargetSpec;
01781     libSpec.value.mac_indexed_fragment.index = 0;
01782     *_retval =  PR_LoadLibraryWithFlags(libSpec, 0);
01783     
01784   NS_TIMELINE_STOP_TIMER("PR_LoadLibrary");
01785   NS_TIMELINE_MARK_TIMER("PR_LoadLibrary");
01786 
01787     if (*_retval)
01788         return NS_OK;
01789 
01790     return NS_ERROR_NULL_POINTER;
01791 }
01792 
01793 NS_IMETHODIMP  
01794 nsLocalFile::Remove(PRBool recursive)
01795 {
01796     OSErr err;
01797     nsresult rv;
01798     FSSpec specToDelete;
01799     PRBool isDir;
01800     
01801     StFollowLinksState(this, PR_FALSE);
01802     
01803     rv = IsDirectory(&isDir); // Calls ResolveAndStat()
01804     if (NS_FAILED(rv))
01805         return rv;  
01806     rv = GetFSSpec(&specToDelete);
01807     if (NS_FAILED(rv))
01808         return rv;
01809     
01810     if (isDir && recursive)
01811         err = ::DeleteDirectory( specToDelete.vRefNum, specToDelete.parID, specToDelete.name );
01812     else
01813         err = ::HDelete( specToDelete.vRefNum, specToDelete.parID, specToDelete.name );
01814     
01815     return MacErrorMapper( err );
01816 }
01817 
01818 NS_IMETHODIMP  
01819 nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModifiedTime)
01820 {
01821     NS_ENSURE_ARG(aLastModifiedTime);
01822     *aLastModifiedTime = 0;
01823     
01824     nsresult rv = ResolveAndStat();
01825     if ( NS_FAILED( rv ) )
01826         return rv;      
01827     rv = UpdateCachedCatInfo(PR_TRUE);
01828     if ( NS_FAILED( rv ) )
01829         return rv;
01830     
01831     // The mod date is in the same spot for files and dirs.
01832     return ConvertMacTimeToMilliseconds( aLastModifiedTime, mCachedCatInfo.hFileInfo.ioFlMdDat );
01833 }
01834 
01835 NS_IMETHODIMP  
01836 nsLocalFile::SetLastModifiedTime(PRInt64 aLastModifiedTime)
01837 {
01838     nsresult rv = ResolveAndStat();
01839     if ( NS_FAILED(rv) )
01840         return rv;
01841     
01842     PRUint32 macTime = 0;
01843     OSErr err = noErr;
01844     
01845     ConvertMillisecondsToMacTime(aLastModifiedTime, &macTime);
01846     
01847     if (NS_SUCCEEDED(rv = UpdateCachedCatInfo(PR_TRUE)))
01848     {
01849         if (mCachedCatInfo.hFileInfo.ioFlAttrib & ioDirMask)
01850         {
01851             mCachedCatInfo.dirInfo.ioDrMdDat = macTime; 
01852             mCachedCatInfo.dirInfo.ioDrParID = mFollowLinks ? mTargetSpec.parID : mSpec.parID;
01853         }
01854         else
01855         {
01856             mCachedCatInfo.hFileInfo.ioFlMdDat = macTime;
01857             mCachedCatInfo.hFileInfo.ioDirID = mFollowLinks ? mTargetSpec.parID : mSpec.parID;
01858         }   
01859             
01860         err = ::PBSetCatInfoSync(&mCachedCatInfo);
01861         if (err != noErr)
01862             return MacErrorMapper(err);
01863     }
01864     
01865     return rv;
01866 }
01867 
01868 NS_IMETHODIMP  
01869 nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModifiedTime)
01870 {
01871     NS_ENSURE_ARG(aLastModifiedTime);
01872     
01873     nsresult rv;
01874     PRBool isLink;
01875     
01876     rv = IsSymlink(&isLink);
01877     if (NS_FAILED(rv))
01878         return rv;
01879     if (!isLink)
01880         return NS_ERROR_FAILURE;
01881     
01882     StFollowLinksState followState(this, PR_FALSE);
01883     return GetLastModifiedTime(aLastModifiedTime);
01884 }
01885 
01886 NS_IMETHODIMP  
01887 nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModifiedTime)
01888 {
01889     nsresult rv;
01890     PRBool isLink;
01891     
01892     rv = IsSymlink(&isLink);
01893     if (NS_FAILED(rv))
01894         return rv;
01895     if (!isLink)
01896         return NS_ERROR_FAILURE;
01897     
01898     StFollowLinksState followState(this, PR_FALSE);
01899     return SetLastModifiedTime(aLastModifiedTime);
01900 }
01901 
01902 
01903 NS_IMETHODIMP  
01904 nsLocalFile::GetFileSize(PRInt64 *aFileSize)
01905 {
01906     NS_ENSURE_ARG(aFileSize);
01907     nsresult rv;
01908     
01909     *aFileSize = LL_Zero();
01910     
01911     if (NS_SUCCEEDED(rv = ResolveAndStat()) && NS_SUCCEEDED(rv = UpdateCachedCatInfo(PR_TRUE)))
01912     {
01913         if (!(mCachedCatInfo.hFileInfo.ioFlAttrib & ioDirMask))
01914         {
01915             long dataSize = mCachedCatInfo.hFileInfo.ioFlLgLen;
01916             long resSize = mCachedCatInfo.hFileInfo.ioFlRLgLen;
01917             
01918             // For now we've only got 32 bits of file size info
01919             PRInt64 dataInt64 = LL_Zero();
01920             PRInt64 resInt64 = LL_Zero();
01921             
01922             // WARNING!!!!!!
01923             // 
01924             // For now we do NOT add the data and resource fork sizes as there are several
01925             // assumptions in the code (notably in form submit) that only the data fork is
01926             // used. 
01927             // LL_I2L(resInt64, resSize);
01928             
01929             LL_I2L(dataInt64, dataSize);
01930             
01931             LL_ADD((*aFileSize), dataInt64, resInt64);
01932         }
01933         // leave size at zero for dirs
01934     }
01935             
01936     return rv;
01937 }
01938 
01939 
01940 NS_IMETHODIMP  
01941 nsLocalFile::SetFileSize(PRInt64 aFileSize)
01942 {
01943     nsresult rv = ResolveAndStat();
01944     if (NS_FAILED(rv))
01945         return rv;
01946         
01947     short   refNum;
01948     OSErr   err;
01949     PRInt32 aNewLength;
01950     
01951     LL_L2I(aNewLength, aFileSize);
01952     
01953     // Need to open the file to set the size
01954     if (::FSpOpenDF(&mTargetSpec, fsWrPerm, &refNum) != noErr)
01955         return NS_ERROR_FILE_ACCESS_DENIED;
01956 
01957     err = ::SetEOF(refNum, aNewLength);
01958         
01959     // Close the file unless we got an error that it was already closed
01960     if (err != fnOpnErr)
01961         (void)::FSClose(refNum);
01962         
01963     if (err != noErr)
01964         return MacErrorMapper(err);
01965         
01966     return MacErrorMapper(err);
01967 }
01968 
01969 NS_IMETHODIMP  
01970 nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSize)
01971 {
01972     NS_ENSURE_ARG(aFileSize);
01973 
01974     StFollowLinksState followState(this, PR_FALSE);
01975     return GetFileSize(aFileSize);
01976 }
01977 
01978 NS_IMETHODIMP  
01979 nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
01980 {
01981     NS_ENSURE_ARG(aDiskSpaceAvailable);
01982         
01983     PRInt64 space64Bits;
01984 
01985     LL_I2L(space64Bits , LONG_MAX);
01986     
01987     nsresult rv = ResolveAndStat();
01988     if (NS_FAILED(rv))
01989         return rv;
01990 
01991     XVolumeParam    pb;
01992     pb.ioCompletion = nsnull;
01993     pb.ioVolIndex = 0;
01994     pb.ioNamePtr = nsnull;
01995     pb.ioVRefNum = mFollowLinks ? mTargetSpec.vRefNum : mSpec.vRefNum;
01996     
01997     // we should check if this call is available
01998     OSErr err = ::PBXGetVolInfoSync(&pb);
01999     
02000     if (err == noErr)
02001     {
02002         const UnsignedWide& freeBytes = UInt64ToUnsignedWide(pb.ioVFreeBytes);
02003 #ifdef HAVE_LONG_LONG
02004     space64Bits = UnsignedWideToUInt64(freeBytes);
02005 #else
02006         space64Bits.lo = freeBytes.lo;
02007         space64Bits.hi = freeBytes.hi;
02008 #endif
02009     }
02010         
02011     *aDiskSpaceAvailable = space64Bits;
02012 
02013     return NS_OK;
02014 }
02015 
02016 NS_IMETHODIMP  
02017 nsLocalFile::GetParent(nsIFile * *aParent)
02018 {
02019     NS_ENSURE_ARG_POINTER(aParent);
02020     *aParent = nsnull;
02021     
02022     nsresult rv = NS_OK;
02023     PRInt32 offset;
02024 
02025     nsCOMPtr<nsILocalFileMac> localFile;
02026     PRInt32     appendedLen = mAppendedPath.Length();
02027     OSErr       err;
02028                 
02029     if (!appendedLen || (appendedLen == 1 && mAppendedPath.CharAt(0) == ':'))
02030     {
02031         rv = ResolveAndStat();
02032         //if the file does not exist, does not mean that the parent does not.
02033         if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
02034             return rv;
02035 
02036         CInfoPBRec  pBlock = {0};
02037         FSSpec      parentFolderSpec;
02038         parentFolderSpec.name[0] = 0;
02039 
02040         pBlock.dirInfo.ioVRefNum = mSpec.vRefNum;
02041         pBlock.dirInfo.ioDrDirID = mSpec.parID;
02042         pBlock.dirInfo.ioNamePtr = (StringPtr)parentFolderSpec.name;
02043         pBlock.dirInfo.ioFDirIndex = -1;        //get info on parID
02044         err = PBGetCatInfoSync(&pBlock);
02045         if (err != noErr) 
02046             return MacErrorMapper(err);
02047         parentFolderSpec.vRefNum = mSpec.vRefNum;
02048         parentFolderSpec.parID = pBlock.dirInfo.ioDrParID;
02049         
02050         localFile = new nsLocalFile;
02051         if (!localFile)
02052             return NS_ERROR_OUT_OF_MEMORY;
02053         rv = localFile->InitWithFSSpec(&parentFolderSpec);
02054         if (NS_FAILED(rv))
02055             return rv;
02056     }
02057     else
02058     {
02059         // trim off the last component of the appended path
02060         // construct a new file from our spec + trimmed path
02061 
02062         nsCAutoString parentAppendage(mAppendedPath);
02063         
02064         if (parentAppendage.Last() == ':')
02065             parentAppendage.Truncate(appendedLen - 1);
02066         if ((offset = parentAppendage.RFindChar(':')) != -1)
02067             parentAppendage.Truncate(offset);
02068         else
02069             parentAppendage.Truncate(0);
02070             
02071         localFile = new nsLocalFile(mSpec, parentAppendage);
02072         if (!localFile)
02073             return NS_ERROR_OUT_OF_MEMORY;
02074     }
02075     *aParent = localFile;
02076     NS_ADDREF(*aParent);
02077 
02078     return rv;
02079 }
02080 
02081 
02082 NS_IMETHODIMP  
02083 nsLocalFile::Exists(PRBool *_retval)
02084 {
02085     NS_ENSURE_ARG(_retval);
02086     *_retval = PR_FALSE;
02087     
02088     nsresult rv = ResolveAndStat();
02089     if (NS_SUCCEEDED(rv)) {
02090         if (NS_SUCCEEDED(UpdateCachedCatInfo(PR_TRUE)))
02091             *_retval = PR_TRUE;
02092     }
02093         
02094     return NS_OK;
02095 }
02096 
02097 NS_IMETHODIMP  
02098 nsLocalFile::IsPackage(PRBool *outIsPackage)
02099 {
02100     NS_ENSURE_ARG(outIsPackage);
02101     *outIsPackage = PR_FALSE;
02102 
02103     // Note: IsDirectory() calls ResolveAndStat() & UpdateCachedCatInfo
02104     PRBool isDir;
02105     nsresult rv = IsDirectory(&isDir);
02106     if (NS_FAILED(rv)) return rv;
02107 
02108     *outIsPackage = ((mCachedCatInfo.dirInfo.ioFlAttrib & kioFlAttribDirMask) &&
02109                      (mCachedCatInfo.dirInfo.ioDrUsrWds.frFlags & kHasBundle));
02110 
02111     if ((!*outIsPackage) && isDir)
02112     {
02113         // Believe it or not, folders ending with ".app" are also considered
02114         // to be packages, even if the top-level folder doesn't have bundle set
02115         nsCAutoString name;
02116         if (NS_SUCCEEDED(rv = GetNativeLeafName(name)))
02117         {
02118             const char *extPtr = strrchr(name.get(), '.');
02119             if (extPtr)
02120             {
02121                 if (!nsCRT::strcasecmp(extPtr, ".app"))
02122                 {
02123                     *outIsPackage = PR_TRUE;
02124                 }
02125             }
02126         }
02127     }
02128 
02129     return NS_OK;
02130 }
02131 
02132 NS_IMETHODIMP  
02133 nsLocalFile::IsWritable(PRBool *outIsWritable)
02134 {
02135     NS_ENSURE_ARG(outIsWritable);
02136     *outIsWritable = PR_TRUE;
02137 
02138     nsresult rv = ResolveAndStat();
02139     if (NS_FAILED(rv)) return rv;
02140     
02141     rv = UpdateCachedCatInfo(PR_TRUE);
02142     if (NS_FAILED(rv)) return rv;
02143     
02144     *outIsWritable = !(mCachedCatInfo.hFileInfo.ioFlAttrib & kioFlAttribLockedMask);
02145     return NS_OK;
02146 }
02147 
02148 NS_IMETHODIMP  
02149 nsLocalFile::IsReadable(PRBool *_retval)
02150 {
02151     NS_ENSURE_ARG(_retval);
02152 
02153     // is it ever not readable on Mac?
02154     *_retval = PR_TRUE;
02155     return NS_OK;
02156 }
02157 
02158 
02159 NS_IMETHODIMP  
02160 nsLocalFile::IsExecutable(PRBool *outIsExecutable)
02161 {
02162     NS_ENSURE_ARG(outIsExecutable);
02163     *outIsExecutable = PR_FALSE;    // Assume failure
02164 
02165     nsresult rv = ResolveAndStat();
02166     if (NS_FAILED(rv)) return rv;
02167 
02168 #if TARGET_CARBON
02169     // If we're running under OS X ask LaunchServices if we're executable
02170     if (sRunningOSX)
02171     {
02172         if ( (UInt32)LSCopyItemInfoForRef != (UInt32)kUnresolvedCFragSymbolAddress )
02173         {
02174             FSRef   theRef;
02175             LSRequestedInfo theInfoRequest = kLSRequestAllInfo;
02176             LSItemInfoRecord theInfo;
02177             
02178             if (::FSpMakeFSRef(&mTargetSpec, &theRef) == noErr)
02179             {
02180                 if (::LSCopyItemInfoForRef(&theRef, theInfoRequest, &theInfo) == noErr)
02181                 {
02182                     if ((theInfo.flags & kLSItemInfoIsApplication) != 0)
02183                         *outIsExecutable = PR_TRUE;
02184                 }
02185             }
02186         }
02187     }
02188     else
02189 #endif
02190     {
02191     OSType fileType;
02192     rv = GetFileType(&fileType);
02193     if (NS_FAILED(rv)) return rv;
02194     
02195     *outIsExecutable = (fileType == 'APPL' || fileType == 'appe' || fileType == 'FNDR');
02196     }
02197     
02198     return NS_OK;
02199 }
02200 
02201 
02202 NS_IMETHODIMP  
02203 nsLocalFile::IsDirectory(PRBool *outIsDir)
02204 {
02205     NS_ENSURE_ARG(outIsDir);
02206     *outIsDir = PR_FALSE;
02207 
02208     nsresult rv = ResolveAndStat();
02209     if (NS_FAILED(rv)) return rv;
02210     
02211     rv = UpdateCachedCatInfo(PR_FALSE);
02212     if (NS_FAILED(rv)) return rv;
02213     
02214     *outIsDir = (mCachedCatInfo.hFileInfo.ioFlAttrib & ioDirMask) != 0;
02215     return NS_OK;
02216 }
02217 
02218 NS_IMETHODIMP  
02219 nsLocalFile::IsFile(PRBool *outIsFile)
02220 {
02221     NS_ENSURE_ARG(outIsFile);
02222     *outIsFile = PR_FALSE;
02223 
02224     nsresult rv = ResolveAndStat();
02225     if (NS_FAILED(rv)) return rv;
02226     
02227     rv = UpdateCachedCatInfo(PR_FALSE);
02228     if (NS_FAILED(rv)) return rv;
02229 
02230     *outIsFile = (mCachedCatInfo.hFileInfo.ioFlAttrib & ioDirMask) == 0;
02231     return NS_OK;
02232 }
02233 
02234 NS_IMETHODIMP  
02235 nsLocalFile::IsHidden(PRBool *_retval)
02236 {
02237     NS_ENSURE_ARG(_retval);
02238     *_retval = PR_FALSE;
02239 
02240     nsresult rv = ResolveAndStat();
02241     if (NS_FAILED(rv)) return rv;
02242 
02243     rv = UpdateCachedCatInfo(PR_FALSE);
02244     if (NS_FAILED(rv)) return rv;
02245     
02246     *_retval = (mCachedCatInfo.hFileInfo.ioFlFndrInfo.fdFlags & kIsInvisible) != 0;
02247 
02248     if (sRunningOSX)
02249     {
02250         // on Mac OS X, also follow Unix "convention" where files
02251         // beginning with a period are considered to be hidden
02252         nsCAutoString name;
02253         if (NS_SUCCEEDED(rv = GetNativeLeafName(name)))
02254         {
02255             if (name.First() == '.')
02256             {
02257                 *_retval = PR_TRUE;
02258             }
02259         }
02260     }
02261 
02262     return NS_OK;
02263 }
02264 
02265 NS_IMETHODIMP
02266 nsLocalFile::IsSymlink(PRBool *_retval)
02267 {
02268     NS_ENSURE_ARG(_retval);
02269     *_retval = PR_FALSE;
02270 
02271     nsresult rv = ResolveAndStat();
02272     if (NS_FAILED(rv)) return rv;
02273 
02274     Boolean isAlias, isFolder;
02275     if (::IsAliasFile(&mSpec, &isAlias, &isFolder) == noErr)
02276         *_retval = isAlias;
02277     return NS_OK;
02278 }
02279 
02280 NS_IMETHODIMP
02281 nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
02282 {
02283     NS_ENSURE_ARG(inFile);
02284     NS_ENSURE_ARG(_retval);
02285     *_retval = PR_FALSE;
02286 
02287     // Building paths is expensive. If we can get the FSSpecs of
02288     // both (they or their parents exist) just compare the specs.
02289     nsCOMPtr<nsILocalFileMac> inMacFile(do_QueryInterface(inFile));
02290     FSSpec fileSpec, inFileSpec;
02291     if (NS_SUCCEEDED(GetFSSpec(&fileSpec)) && inMacFile && NS_SUCCEEDED(inMacFile->GetFSSpec(&inFileSpec)))
02292         *_retval = IsEqualFSSpec(fileSpec, inFileSpec);
02293     else
02294     {
02295         nsCAutoString filePath;
02296         GetNativePath(filePath);
02297     
02298         nsXPIDLCString inFilePath;
02299         inFile->GetNativePath(inFilePath);
02300         
02301         if (nsCRT::strcasecmp(inFilePath.get(), filePath.get()) == 0)
02302             *_retval = PR_TRUE;
02303     }
02304     return NS_OK;
02305 }
02306 
02307 NS_IMETHODIMP
02308 nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *outContains)
02309 {
02310   /* Note here that we make no attempt to deal with the problem
02311      of folder aliases. Doing a 'Contains' test and dealing with
02312      folder aliases is Hard. Think about it.
02313   */
02314     *outContains = PR_FALSE;
02315 
02316     PRBool isDir;
02317     nsresult rv = IsDirectory(&isDir);    // need to cache this
02318     if (NS_FAILED(rv)) return rv;
02319     if (!isDir) return NS_OK;   // must be a dir to contain someone
02320 
02321     nsCOMPtr<nsILocalFileMac> macFile(do_QueryInterface(inFile));
02322     if (!macFile) return NS_OK;     // trying to compare non-local with local file
02323 
02324     FSSpec  mySpec = mSpec;
02325     FSSpec  compareSpec;
02326 
02327     // NOTE: we're not resolving inFile if it was an alias
02328     StFollowLinksState followState(macFile, PR_FALSE);
02329     rv = macFile->GetFSSpec(&compareSpec);
02330     if (NS_FAILED(rv)) return rv;
02331 
02332     // if they are on different volumes, bail
02333     if (mSpec.vRefNum != compareSpec.vRefNum)
02334         return NS_OK;
02335 
02336     // if recur == true, test every parent, otherwise just test the first one
02337     // (yes, recur does not get set in this loop)
02338     OSErr   err = noErr;
02339     do
02340     {
02341         FSSpec  parentFolderSpec;
02342         err = GetParentFolderSpec(compareSpec, parentFolderSpec);
02343         if (err != noErr) break;                // we reached the top   
02344 
02345         if (IsEqualFSSpec(parentFolderSpec, mySpec))
02346         {
02347             *outContains = PR_TRUE;
02348             break;
02349         }
02350         
02351         compareSpec = parentFolderSpec;
02352     } while (recur);
02353       
02354     return NS_OK;
02355 }
02356 
02357 
02358 
02359 NS_IMETHODIMP
02360 nsLocalFile::GetNativeTarget(nsACString &_retval)
02361 {   
02362     _retval.Truncate();
02363     
02364     PRBool symLink;
02365     
02366     nsresult rv = IsSymlink(&symLink);
02367     if (NS_FAILED(rv))
02368         return rv;
02369 
02370     if (!symLink)
02371         return NS_ERROR_FILE_INVALID_PATH;
02372         
02373     StFollowLinksState followState(this, PR_TRUE);
02374     return GetNativePath(_retval);
02375 }
02376 
02377 NS_IMETHODIMP
02378 nsLocalFile::GetTarget(nsAString &_retval)
02379 {   
02380    nsresult rv;
02381    nsCAutoString fsStr;
02382    
02383    if (NS_SUCCEEDED(rv = GetNativeTarget(fsStr))) {
02384      rv = NS_CopyNativeToUnicode(fsStr, _retval);
02385    }
02386    return rv;
02387 }
02388 
02389 NS_IMETHODIMP
02390 nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator * *entries)
02391 {
02392     nsresult rv;
02393     
02394     *entries = nsnull;
02395 
02396     PRBool isDir;
02397     rv = IsDirectory(&isDir);
02398     if (NS_FAILED(rv)) 
02399         return rv;
02400     if (!isDir)
02401         return NS_ERROR_FILE_NOT_DIRECTORY;
02402 
02403     nsDirEnumerator* dirEnum = new nsDirEnumerator();
02404     if (dirEnum == nsnull)
02405         return NS_ERROR_OUT_OF_MEMORY;
02406     NS_ADDREF(dirEnum);
02407     rv = dirEnum->Init(this);
02408     if (NS_FAILED(rv)) 
02409     {
02410         NS_RELEASE(dirEnum);
02411         return rv;
02412     }
02413     
02414     *entries = dirEnum;
02415     return NS_OK;
02416 }
02417 
02418 NS_IMETHODIMP
02419 nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
02420 {
02421    aPersistentDescriptor.Truncate();
02422    
02423    nsresult  rv = ResolveAndStat();
02424    if ( NS_FAILED( rv ) )
02425      return rv;
02426 
02427    AliasHandle    aliasH;
02428    OSErr err = ::NewAlias(nil, &mTargetSpec, &aliasH);
02429    if (err != noErr)
02430      return MacErrorMapper(err);
02431 
02432    PRUint32 bytes = ::GetHandleSize((Handle) aliasH);
02433    HLock((Handle) aliasH);
02434    char* buf = PL_Base64Encode((const char*)*aliasH, bytes, nsnull); // Passing nsnull for dest makes NULL-term string
02435    ::DisposeHandle((Handle) aliasH);
02436    NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
02437 
02438    aPersistentDescriptor = buf;
02439    PR_Free(buf);
02440 
02441    return NS_OK;
02442 }
02443 
02444 NS_IMETHODIMP
02445 nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
02446 {
02447    if (aPersistentDescriptor.IsEmpty())
02448       return NS_ERROR_INVALID_ARG;
02449    
02450    nsresult rv = NS_OK;
02451 
02452    PRUint32 dataSize = aPersistentDescriptor.Length();
02453    char* decodedData = PL_Base64Decode(PromiseFlatCString(aPersistentDescriptor).get(), dataSize, nsnull);
02454    // Cast to an alias record and resolve.
02455    AliasHandle aliasH = nsnull;
02456    if (::PtrToHand(decodedData, &(Handle)aliasH, (dataSize * 3) / 4) != noErr)
02457       rv = NS_ERROR_OUT_OF_MEMORY;
02458    PR_Free(decodedData);
02459    NS_ENSURE_SUCCESS(rv, rv);
02460 
02461    Boolean changed;
02462    FSSpec resolvedSpec;
02463    OSErr err;
02464    err = ::ResolveAlias(nsnull, aliasH, &resolvedSpec, &changed);
02465    if (err == fnfErr) // resolvedSpec is valid in this case
02466       err = noErr;
02467    rv = MacErrorMapper(err);
02468    DisposeHandle((Handle) aliasH);
02469    NS_ENSURE_SUCCESS(rv, rv);
02470  
02471    return InitWithFSSpec(&resolvedSpec);   
02472 }
02473 
02474 #pragma mark -
02475 
02476 // a stack-based, exception safe class for an AEDesc
02477 
02478 #pragma mark
02479 class StAEDesc: public AEDesc
02480 {
02481 public:
02482                     StAEDesc()
02483                     {
02484                         descriptorType = typeNull;
02485                         dataHandle = nil;
02486                     }
02487                                         
02488                     ~StAEDesc()
02489                     {
02490                         ::AEDisposeDesc(this);
02491                     }
02492 
02493                     void Clear()
02494                     {
02495                         ::AEDisposeDesc(this);
02496                         descriptorType = typeNull;
02497                         dataHandle = nil;
02498                     }
02499                                         
02500 private:
02501     // disallow copies and assigns
02502                             StAEDesc(const StAEDesc& rhs);      // copy constructor
02503     StAEDesc&   operator= (const StAEDesc&rhs);     // throws OSErrs
02504 
02505 };
02506 
02507 #pragma mark -
02508 #pragma mark [Utility methods]
02509 
02510 
02511 nsresult nsLocalFile::UpdateCachedCatInfo(PRBool forceUpdate) 
02512 {
02513     if (!mCatInfoDirty && !forceUpdate)
02514         return NS_OK;
02515         
02516     FSSpec spectoUse = mFollowLinks ? mTargetSpec : mSpec;
02517     mCachedCatInfo.hFileInfo.ioCompletion = nsnull;
02518     mCachedCatInfo.hFileInfo.ioFDirIndex = 0; // use dirID and name
02519     mCachedCatInfo.hFileInfo.ioVRefNum = spectoUse.vRefNum;
02520     mCachedCatInfo.hFileInfo.ioDirID = spectoUse.parID;
02521     mCachedCatInfo.hFileInfo.ioNamePtr = spectoUse.name;
02522 
02523     OSErr err = ::PBGetCatInfoSync(&mCachedCatInfo);
02524     if (err == noErr)
02525     {
02526         mCatInfoDirty = PR_FALSE;
02527         return NS_OK;
02528     }
02529     return MacErrorMapper(err);
02530 }
02531 
02532 
02533 nsresult nsLocalFile::FindRunningAppBySignature (OSType aAppSig, FSSpec& outSpec, ProcessSerialNumber& outPsn)
02534 {
02535     ProcessInfoRec  info;
02536     FSSpec                  tempFSSpec;
02537     OSErr                   err = noErr;
02538     
02539     outPsn.highLongOfPSN = 0;
02540     outPsn.lowLongOfPSN  = kNoProcess;
02541 
02542     while (PR_TRUE)
02543     {
02544         err = ::GetNextProcess(&outPsn);
02545         if (err == procNotFound) break;
02546         if (err != noErr) return NS_ERROR_FAILURE;
02547         info.processInfoLength = sizeof(ProcessInfoRec);
02548         info.processName = nil;
02549         info.processAppSpec = &tempFSSpec;
02550         err = ::GetProcessInformation(&outPsn, &info);
02551         if (err != noErr) return NS_ERROR_FAILURE;
02552         
02553         if (info.processSignature == aAppSig)
02554         {
02555             outSpec = tempFSSpec;
02556             return NS_OK;
02557         }
02558     }
02559     
02560     return NS_ERROR_FILE_NOT_FOUND;     // really process not found
02561 }
02562 
02563 
02564 nsresult nsLocalFile::FindRunningAppByFSSpec(const FSSpec& appSpec, ProcessSerialNumber& outPsn)
02565 {
02566     ProcessInfoRec  info;
02567     FSSpec                  tempFSSpec;
02568     OSErr                   err = noErr;
02569     
02570     outPsn.highLongOfPSN = 0;
02571     outPsn.lowLongOfPSN  = kNoProcess;
02572 
02573     while (PR_TRUE)
02574     {
02575         err = ::GetNextProcess(&outPsn);
02576         if (err == procNotFound) break;     
02577         if (err != noErr) return NS_ERROR_FAILURE;
02578         info.processInfoLength = sizeof(ProcessInfoRec);
02579         info.processName = nil;
02580         info.processAppSpec = &tempFSSpec;
02581         err = ::GetProcessInformation(&outPsn, &info);
02582         if (err != noErr) return NS_ERROR_FAILURE;
02583         
02584         if (IsEqualFSSpec(appSpec, *info.processAppSpec))
02585         {
02586             return NS_OK;
02587         }
02588     }
02589     
02590     return NS_ERROR_FILE_NOT_FOUND;     // really process not found
02591 }
02592 
02593 
02594 nsresult nsLocalFile::FindAppOnLocalVolumes(OSType sig, FSSpec &outSpec)
02595 {
02596     OSErr   err;
02597     
02598     // get the system volume
02599     long        systemFolderDirID;
02600     short       sysVRefNum;
02601     err = FindFolder(kOnSystemDisk, kSystemFolderType, false, &sysVRefNum, &systemFolderDirID);
02602     if (err != noErr) return NS_ERROR_FAILURE;
02603 
02604     short   vRefNum = sysVRefNum;
02605     short   index = 0;
02606     
02607     while (true)
02608     {
02609         if (index == 0 || vRefNum != sysVRefNum)
02610         {
02611             // should we avoid AppleShare volumes?
02612             
02613             Boolean hasDesktopDB;
02614             err = VolHasDesktopDB(vRefNum, &hasDesktopDB);
02615             if (err != noErr) return err;
02616             if (hasDesktopDB)
02617             {
02618                 err = FindAppOnVolume(sig, vRefNum, &outSpec);
02619                 if (err != afpItemNotFound) return err;
02620             }
02621         }
02622         index++;
02623         err = GetIndVolume(index, &vRefNum);
02624         if (err == nsvErr) return fnfErr;
02625         if (err != noErr) return err;
02626     }
02627 
02628     return NS_OK;
02629 }
02630 
02631 #define aeSelectionKeyword  'fsel'
02632 #define kAEOpenSelection    'sope'
02633 #define kAERevealSelection  'srev'
02634 #define kFinderType         'FNDR'
02635 
02636 NS_IMETHODIMP nsLocalFile::Launch()
02637 {
02638   AppleEvent        aeEvent = {0, nil};
02639   AppleEvent        aeReply = {0, nil};
02640   StAEDesc          aeDirDesc, listElem, myAddressDesc, fileList;
02641   FSSpec                dirSpec, appSpec;
02642   AliasHandle           DirAlias, FileAlias;
02643   OSErr             errorResult = noErr;
02644   ProcessSerialNumber   process;
02645   
02646   // for launching a file, we'll use mTargetSpec (which is both a resolved spec and a resolved alias)
02647   ResolveAndStat();
02648     
02649 #if TARGET_CARBON
02650     if (sRunningOSX)
02651     { // We're running under Mac OS X, LaunchServices here we come
02652 
02653         // First we make sure the LaunchServices routine we want is implemented
02654         if ( (UInt32)LSOpenFSRef != (UInt32)kUnresolvedCFragSymbolAddress )
02655         {
02656             FSRef   theRef;
02657             if (::FSpMakeFSRef(&mTargetSpec, &theRef) == noErr)
02658             {
02659                 (void)::LSOpenFSRef(&theRef, NULL);
02660             }
02661         }
02662     }
02663     else
02664 #endif
02665     { // We're running under Mac OS 8.x/9.x, use the Finder Luke
02666   nsresult rv = FindRunningAppBySignature ('MACS', appSpec, process);
02667   if (NS_SUCCEEDED(rv))
02668   { 
02669     errorResult = AECreateDesc(typeProcessSerialNumber, (Ptr)&process, sizeof(process), &myAddressDesc);
02670     if (errorResult == noErr)
02671     {
02672       /* Create the FinderEvent */
02673       errorResult = AECreateAppleEvent(kFinderType, kAEOpenSelection, &myAddressDesc, kAutoGenerateReturnID, kAnyTransactionID,
02674                                        &aeEvent);   
02675       if (errorResult == noErr) 
02676       {
02677         errorResult = FSMakeFSSpec(mTargetSpec.vRefNum, mTargetSpec.parID, nil, &dirSpec);
02678         NewAlias(nil, &dirSpec, &DirAlias);
02679         /* Create alias for file */
02680         NewAlias(nil, &mTargetSpec, &FileAlias);
02681         
02682         /* Create the file  list */
02683          errorResult = AECreateList(nil, 0, false, &fileList);
02684         /*  create the folder  descriptor */
02685         HLock((Handle)DirAlias);
02686         errorResult = AECreateDesc(typeAlias, (Ptr)*DirAlias, GetHandleSize((Handle)DirAlias), &aeDirDesc);
02687         HUnlock((Handle)DirAlias);
02688         if (errorResult == noErr)
02689         {
02690           errorResult = AEPutParamDesc(&aeEvent, keyDirectObject, &aeDirDesc);
02691           if ( errorResult == noErr) 
02692           {
02693             /*  create the file descriptor and add to aliasList */
02694             HLock((Handle)FileAlias);
02695             errorResult = AECreateDesc(typeAlias, (Ptr)*FileAlias, GetHandleSize((Handle)FileAlias), &listElem);
02696             HLock((Handle)FileAlias);
02697             if (errorResult == noErr)
02698             {
02699               errorResult = AEPutDesc(&fileList, 0, &listElem);
02700               if (errorResult == noErr)
02701               {
02702                 /* Add the file alias list to the event */
02703                 errorResult = AEPutParamDesc(&aeEvent, aeSelectionKeyword, &fileList);
02704                 if (errorResult == noErr)
02705                     AESend(&aeEvent, &aeReply, kAEWaitReply + kAENeverInteract 
02706                                   + kAECanSwitchLayer, kAEHighPriority, kAEDefaultTimeout, nil, nil);
02707               }
02708             }
02709           }
02710         }
02711       }
02712     }
02713   }
02714     }
02715                       
02716     return NS_OK;
02717 }
02718 
02719 NS_IMETHODIMP nsLocalFile::Reveal()
02720 {
02721   FSSpec            specToReveal;
02722   AppleEvent        aeEvent = {0, nil};
02723   AppleEvent        aeReply = {0, nil};
02724   StAEDesc          aeDirDesc, listElem, myAddressDesc, fileList;
02725   OSErr             errorResult = noErr;
02726   ProcessSerialNumber   process;
02727   FSSpec appSpec;
02728     
02729   nsresult rv = ResolveAndStat();
02730   if (NS_FAILED(rv))
02731     return rv;
02732   rv = GetFSSpec(&specToReveal); // Pay attention to followLinks
02733   if (NS_FAILED(rv))
02734     return rv;
02735   
02736   rv = FindRunningAppBySignature ('MACS', appSpec, process);
02737   if (NS_SUCCEEDED(rv))
02738   { 
02739     errorResult = AECreateDesc(typeProcessSerialNumber, (Ptr)&process, sizeof(process), &myAddressDesc);
02740     if (errorResult == noErr)
02741     {
02742       /* Create the FinderEvent */
02743 #if TARGET_CARBON
02744     // The Finder under OS X uses a different event to reveal
02745     if (sRunningOSX)
02746       errorResult = AECreateAppleEvent(kAEMiscStandards, kAEMakeObjectsVisible, &myAddressDesc, kAutoGenerateReturnID, kAnyTransactionID,
02747                                        &aeEvent);   
02748     else
02749 #endif
02750       errorResult = AECreateAppleEvent(kFinderType, kAERevealSelection, &myAddressDesc, kAutoGenerateReturnID, kAnyTransactionID,
02751                                        &aeEvent);   
02752       if (errorResult == noErr) 
02753       {
02754         /* Create the file  list */
02755         errorResult = AECreateList(nil, 0, false, &fileList);
02756         if (errorResult == noErr) 
02757         {
02758           errorResult = AEPutPtr(&fileList, 0, typeFSS, &specToReveal, sizeof(FSSpec));
02759           
02760           if (errorResult == noErr)
02761           {
02762 #if TARGET_CARBON
02763             // When we're sending the event under OS X the FSSpec must be a keyDirectObject
02764             if (sRunningOSX)
02765               errorResult = AEPutParamDesc(&aeEvent, keyDirectObject, &fileList);
02766             else
02767 #endif
02768             errorResult = AEPutParamDesc(&aeEvent,keySelection, &fileList);
02769 
02770             if (errorResult == noErr)
02771             {
02772               errorResult = AESend(&aeEvent, &aeReply, kAENoReply, kAENormalPriority, kAEDefaultTimeout, nil, nil);
02773               if (errorResult == noErr)
02774                 SetFrontProcess(&process);
02775             }
02776           }
02777         }
02778       }
02779     }
02780   }
02781     
02782   return NS_OK;
02783 }
02784 
02785 nsresult nsLocalFile::MyLaunchAppWithDoc(const FSSpec& appSpec, const FSSpec* aDocToLoad, PRBool aLaunchInBackground)
02786 {
02787     ProcessSerialNumber thePSN = {0};
02788     StAEDesc            target;
02789     StAEDesc            docDesc;
02790     StAEDesc            launchDesc;
02791     StAEDesc            docList;
02792     AppleEvent      theEvent = {0, nil};
02793     AppleEvent      theReply = {0, nil};
02794     OSErr               err = noErr;
02795     Boolean             autoParamValue = false;
02796     Boolean             running = false;
02797     nsresult            rv = NS_OK;
02798     
02799 #if TARGET_CARBON
02800     if (sRunningOSX)
02801     { // Under Mac OS X we'll use LaunchServices
02802         
02803         // First we make sure the LaunchServices routine we want is implemented
02804         if ( (UInt32)LSOpenFromRefSpec != (UInt32)kUnresolvedCFragSymbolAddress )
02805         {
02806             FSRef   appRef;
02807             FSRef   docRef;
02808             LSLaunchFlags       theLaunchFlags = kLSLaunchDefaults;
02809             LSLaunchFSRefSpec   thelaunchSpec;
02810             
02811             if (::FSpMakeFSRef(&appSpec, &appRef) != noErr)
02812                 return NS_ERROR_FAILURE;
02813             
02814             if (aDocToLoad)
02815                 if (::FSpMakeFSRef(aDocToLoad, &docRef) != noErr)
02816                     return NS_ERROR_FAILURE;
02817             
02818             if (aLaunchInBackground)
02819                 theLaunchFlags |= kLSLaunchDontSwitch;
02820                 
02821             memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
02822             
02823             thelaunchSpec.appRef = &appRef;
02824             if (aDocToLoad)
02825             {
02826                 thelaunchSpec.numDocs = 1;
02827                 thelaunchSpec.itemRefs = &docRef;
02828             }
02829             thelaunchSpec.launchFlags = theLaunchFlags;
02830             
02831             err = ::LSOpenFromRefSpec(&thelaunchSpec, NULL);
02832             NS_ASSERTION((err != noErr), "Error calling LSOpenFromRefSpec");
02833             if (err != noErr) return NS_ERROR_FAILURE;
02834         }
02835     }
02836     else
02837 #endif
02838     { // The old fashioned way for Mac OS 8.x/9.x
02839     rv = FindRunningAppByFSSpec(appSpec, thePSN);
02840     running = NS_SUCCEEDED(rv);
02841     
02842     err = AECreateDesc(typeProcessSerialNumber, &thePSN, sizeof(thePSN), &target); 
02843     if (err != noErr) return NS_ERROR_FAILURE;
02844     
02845     err = AECreateAppleEvent(kCoreEventClass, aDocToLoad ? kAEOpenDocuments : kAEOpenApplication, &target,
02846                             kAutoGenerateReturnID, kAnyTransactionID, &theEvent);
02847     if (err != noErr) return NS_ERROR_FAILURE;
02848     
02849     if (aDocToLoad)
02850     {
02851         err = AECreateList(nil, 0, false, &docList);
02852         if (err != noErr) return NS_ERROR_FAILURE;
02853         
02854         err = AECreateDesc(typeFSS, aDocToLoad, sizeof(FSSpec), &docDesc);
02855         if (err != noErr) return NS_ERROR_FAILURE;
02856         
02857         err = AEPutDesc(&docList, 0, &docDesc);
02858         if (err != noErr) return NS_ERROR_FAILURE;
02859         
02860         err = AEPutParamDesc(&theEvent, keyDirectObject, &docList);
02861         if (err != noErr) return NS_ERROR_FAILURE;
02862     }
02863     
02864     if (running)
02865     {
02866         err = AESend(&theEvent, &theReply, kAENoReply, kAENormalPriority, kNoTimeOut, nil, nil);
02867         if (err != noErr) return NS_ERROR_FAILURE;
02868         
02869         if (!aLaunchInBackground)
02870         {
02871             err = ::SetFrontProcess(&thePSN);
02872             if (err != noErr) return NS_ERROR_FAILURE;
02873         }
02874     }
02875     else
02876     {
02877         LaunchParamBlockRec launchThis = {0};
02878         PRUint16                        launchControlFlags = (launchContinue | launchNoFileFlags);
02879         if (aLaunchInBackground)
02880             launchControlFlags |= launchDontSwitch;
02881         
02882         err = AECoerceDesc(&theEvent, typeAppParameters, &launchDesc);
02883         if (err != noErr) return NS_ERROR_FAILURE;
02884 
02885         launchThis.launchAppSpec = (FSSpecPtr)&appSpec;
02886 #if TARGET_CARBON && ACCESSOR_CALLS_ARE_FUNCTIONS
02887         ::AEGetDescData(&launchDesc, &launchThis.launchAppParameters, sizeof(launchThis.launchAppParameters));
02888 #else
02889         // no need to lock this handle.
02890             launchThis.launchAppParameters = (AppParametersPtr) *(launchDesc.dataHandle);
02891 #endif
02892         launchThis.launchBlockID = extendedBlock;
02893         launchThis.launchEPBLength = extendedBlockLen;
02894         launchThis.launchFileFlags = 0;
02895         launchThis.launchControlFlags = launchControlFlags;
02896         err = ::LaunchApplication(&launchThis);
02897         if (err != noErr) return NS_ERROR_FAILURE;
02898         
02899         // let's be nice and wait until it's running
02900         const PRUint32  kMaxTimeToWait = 60;        // wait 1 sec max
02901         PRUint32                endTicks = ::TickCount() + kMaxTimeToWait;
02902 
02903         PRBool                  foundApp = PR_FALSE;
02904         
02905         do
02906         {
02907             EventRecord     theEvent;
02908             (void)WaitNextEvent(nullEvent, &theEvent, 1, NULL);
02909 
02910             ProcessSerialNumber     psn;
02911             foundApp = NS_SUCCEEDED(FindRunningAppByFSSpec(appSpec, psn));
02912         
02913         } while (!foundApp && (::TickCount() <= endTicks));
02914 
02915         NS_ASSERTION(foundApp, "Failed to find app after launching it");
02916     }
02917     
02918     if (theEvent.dataHandle != nil) AEDisposeDesc(&theEvent);
02919     if (theReply.dataHandle != nil) AEDisposeDesc(&theReply);
02920     }
02921 
02922     return NS_OK;
02923 }
02924 
02925 
02926 #pragma mark -
02927 #pragma mark [Methods that will not be implemented on Mac]
02928 
02929 NS_IMETHODIMP
02930 nsLocalFile::Normalize()
02931 {
02932     return NS_ERROR_NOT_IMPLEMENTED;
02933 }
02934 
02935 NS_IMETHODIMP  
02936 nsLocalFile::GetPermissions(PRUint32 *aPermissions)
02937 {
02938     return NS_ERROR_NOT_IMPLEMENTED;
02939 }
02940 
02941 NS_IMETHODIMP  
02942 nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink)
02943 {
02944     return NS_ERROR_NOT_IMPLEMENTED;
02945 }
02946 
02947 
02948 NS_IMETHODIMP  
02949 nsLocalFile::SetPermissions(PRUint32 aPermissions)
02950 {
02951     return NS_ERROR_NOT_IMPLEMENTED;
02952 }
02953 
02954 NS_IMETHODIMP  
02955 nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissions)
02956 {
02957     return NS_ERROR_NOT_IMPLEMENTED;
02958 }
02959 
02960 NS_IMETHODIMP  
02961 nsLocalFile::IsSpecial(PRBool *_retval)
02962 {
02963     return NS_ERROR_NOT_IMPLEMENTED;
02964 }
02965 
02966 #pragma mark -
02967 #pragma mark [nsILocalFileMac]
02968 // Implementation of Mac specific finctions from nsILocalFileMac
02969 
02970 
02971 NS_IMETHODIMP nsLocalFile::InitWithCFURL(CFURLRef aCFURL)
02972 {
02973     nsresult rv = NS_ERROR_FAILURE;
02974     
02975 #if TARGET_CARBON
02976     NS_ENSURE_ARG(aCFURL);
02977     
02978     // CFURLGetFSRef can only succeed if the entire path exists.
02979     FSRef fsRef;
02980     if (::CFURLGetFSRef(aCFURL, &fsRef) == PR_TRUE)
02981         rv = InitWithFSRef(&fsRef);
02982     else
02983     {
02984       CFURLRef parentURL = ::CFURLCreateCopyDeletingLastPathComponent(NULL, aCFURL);
02985       if (!parentURL)
02986         return NS_ERROR_FAILURE;
02987 
02988       // Get the FSRef from the parent and the FSSpec from that
02989       FSRef parentFSRef;
02990       FSSpec parentFSSpec;
02991       if ((::CFURLGetFSRef(parentURL, &parentFSRef) == PR_TRUE) &&
02992           (::FSGetCatalogInfo(&parentFSRef, kFSCatInfoNone,
02993                              nsnull, nsnull, &parentFSSpec, nsnull) == noErr))
02994       {
02995         // Get the leaf name of the file and turn it into a string HFS can use.
02996         CFStringRef fileNameRef = ::CFURLCopyLastPathComponent(aCFURL);
02997         if (fileNameRef)
02998         {
02999           TextEncoding theEncoding;
03000           if (::UpgradeScriptInfoToTextEncoding(smSystemScript, 
03001                                                 kTextLanguageDontCare,
03002                                                 kTextRegionDontCare,
03003                                                 NULL,
03004                                                 &theEncoding) != noErr)
03005             theEncoding = kTextEncodingMacRoman;
03006 
03007           char  origName[256];
03008           char  truncBuf[32];
03009           if (::CFStringGetCString(fileNameRef, origName, sizeof(origName), theEncoding))
03010           {
03011             MakeDirty();
03012             mSpec = parentFSSpec;
03013             mAppendedPath = NS_TruncNodeName(origName, truncBuf);
03014             rv = NS_OK;
03015           }
03016           ::CFRelease(fileNameRef);
03017         }
03018       }
03019       ::CFRelease(parentURL);
03020     }
03021 #endif
03022 
03023     return rv;
03024 }
03025 
03026 
03027 NS_IMETHODIMP nsLocalFile::InitWithFSRef(const FSRef * aFSRef)
03028 {
03029     nsresult rv = NS_ERROR_FAILURE;
03030 
03031 #if TARGET_CARBON
03032     NS_ENSURE_ARG(aFSRef);
03033     
03034     FSSpec fsSpec;
03035     OSErr err = ::FSGetCatalogInfo(aFSRef, kFSCatInfoNone, nsnull,
03036                                    nsnull, &fsSpec, nsnull);
03037     if (err == noErr)
03038         rv = InitWithFSSpec(&fsSpec);
03039     else
03040         rv = MacErrorMapper(err);
03041 #endif
03042 
03043     return rv;
03044 }
03045 
03046 
03047 NS_IMETHODIMP nsLocalFile::InitWithFSSpec(const FSSpec *fileSpec)
03048 {
03049     MakeDirty();
03050     mSpec          = *fileSpec;
03051     mTargetSpec    = *fileSpec;
03052     mAppendedPath  = "";
03053     return NS_OK;
03054 }
03055 
03056 
03057 NS_IMETHODIMP nsLocalFile::InitToAppWithCreatorCode(OSType aAppCreator)
03058 {
03059     FSSpec              appSpec;
03060     ProcessSerialNumber psn;
03061     
03062 #if TARGET_CARBON
03063     if (sRunningOSX)
03064     { // If we're running under OS X use LaunchServices to determine the app
03065       // corresponding to the creator code
03066         if ( (UInt32)LSFindApplicationForInfo != (UInt32)kUnresolvedCFragSymbolAddress )
03067         {
03068             FSRef    theRef;
03069             if (::LSFindApplicationForInfo(aAppCreator, NULL, NULL, &theRef, NULL) == noErr)
03070             {
03071                 FSCatalogInfoBitmap   whichInfo = kFSCatInfoNone;
03072                 
03073                 if (::FSGetCatalogInfo(&theRef, whichInfo, NULL, NULL, &appSpec, NULL) == noErr)
03074                     return InitWithFSSpec(&appSpec);
03075             }
03076             
03077             // If we get here we didn't find an app
03078             return NS_ERROR_FILE_NOT_FOUND;
03079         }
03080     }
03081 #endif
03082 
03083     // is the app running?
03084     nsresult rv = FindRunningAppBySignature(aAppCreator, appSpec, psn);
03085     if (rv == NS_ERROR_FILE_NOT_FOUND)
03086     {
03087         // we have to look on disk
03088         rv = FindAppOnLocalVolumes(aAppCreator, appSpec);
03089         if (NS_FAILED(rv)) return rv;
03090     }
03091     else if (NS_FAILED(rv))
03092         return rv;
03093 
03094     // init with the spec here
03095     return InitWithFSSpec(&appSpec);
03096 }
03097 
03098 NS_IMETHODIMP nsLocalFile::GetCFURL(CFURLRef *_retval)
03099 {
03100     nsresult rv = NS_ERROR_FAILURE;
03101 
03102 #if TARGET_CARBON
03103     NS_ENSURE_ARG_POINTER(_retval);
03104     *_retval = nsnull;
03105     
03106     PRBool exists;
03107     if (NS_SUCCEEDED(Exists(&exists)) && exists)
03108     {
03109         FSRef fsRef;
03110         FSSpec fsSpec = mFollowLinks ? mTargetSpec : mSpec;
03111         if (::FSpMakeFSRef(&fsSpec, &fsRef) == noErr)
03112         {
03113             *_retval = ::CFURLCreateFromFSRef(NULL, &fsRef);
03114             if (*_retval)
03115                 return NS_OK;    
03116         }
03117     }
03118     else
03119     {
03120         nsCAutoString tempPath;
03121         if (NS_SUCCEEDED(GetNativePath(tempPath)))
03122         {
03123             CFStringRef pathStrRef = ::CFStringCreateWithCString(NULL, tempPath.get(), kCFStringEncodingMacRoman);
03124             if (!pathStrRef)
03125                 return NS_ERROR_FAILURE;
03126             *_retval = ::CFURLCreateWithFileSystemPath(NULL, pathStrRef, kCFURLHFSPathStyle, false);
03127             ::CFRelease(pathStrRef);
03128             if (*_retval)
03129                 return NS_OK;    
03130         }
03131     }
03132 #endif
03133 
03134     return rv;
03135 }
03136 
03137 NS_IMETHODIMP nsLocalFile::GetFSRef(FSRef *_retval)
03138 {
03139     nsresult rv = NS_ERROR_FAILURE;
03140   
03141 #if TARGET_CARBON
03142     NS_ENSURE_ARG_POINTER(_retval);
03143     
03144     FSSpec fsSpec;
03145     rv = GetFSSpec(&fsSpec);
03146     if (NS_SUCCEEDED(rv))
03147         rv = MacErrorMapper(::FSpMakeFSRef(&fsSpec, _retval));
03148 #endif
03149 
03150     return rv;
03151 }
03152 
03153 NS_IMETHODIMP nsLocalFile::GetFSSpec(FSSpec *fileSpec)
03154 {
03155     NS_ENSURE_ARG(fileSpec);
03156     nsresult rv = ResolveAndStat();
03157     if (rv == NS_ERROR_FILE_NOT_FOUND)
03158         rv = NS_OK;
03159     if (NS_SUCCEEDED(rv))
03160         *fileSpec = mFollowLinks ? mTargetSpec : mSpec;
03161     
03162     return NS_OK;
03163 }
03164 
03165 NS_IMETHODIMP nsLocalFile::GetFileType(OSType *aFileType)
03166 {
03167     NS_ENSURE_ARG(aFileType);
03168 
03169     FSSpec fileSpec;
03170     (void)GetFSSpec(&fileSpec); 
03171     
03172     FInfo info;
03173     OSErr err = ::FSpGetFInfo(&fileSpec, &info);
03174     if (err != noErr)
03175     {
03176         *aFileType = mType;
03177         return NS_ERROR_FILE_NOT_FOUND;
03178     }
03179     
03180     *aFileType = info.fdType;
03181     
03182     return NS_OK;
03183 }
03184 
03185 NS_IMETHODIMP nsLocalFile::SetFileType(OSType aFileType)
03186 {
03187     mType = aFileType;
03188 
03189     FSSpec fileSpec;
03190     (void)GetFSSpec(&fileSpec); 
03191     
03192     FInfo info;
03193     OSErr err = ::FSpGetFInfo(&fileSpec, &info);
03194     if (err != noErr)
03195         return NS_ERROR_FILE_NOT_FOUND;
03196             
03197     info.fdType = aFileType;    
03198     err = ::FSpSetFInfo(&fileSpec, &info);
03199     if (err != noErr)
03200         return NS_ERROR_FILE_ACCESS_DENIED;
03201     
03202     return NS_OK;
03203 }
03204 
03205 NS_IMETHODIMP nsLocalFile::GetFileCreator(OSType *aCreator)
03206 {
03207     NS_ENSURE_ARG(aCreator);
03208     
03209     FSSpec fileSpec;
03210     (void)GetFSSpec(&fileSpec); 
03211     
03212     FInfo info;
03213     OSErr err = ::FSpGetFInfo(&fileSpec, &info);
03214     if (err != noErr)
03215     {
03216         *aCreator = mCreator;
03217         return NS_ERROR_FILE_NOT_FOUND;
03218     }
03219     
03220     *aCreator = info.fdCreator; 
03221     
03222     return NS_OK;
03223 }
03224 
03225 NS_IMETHODIMP nsLocalFile::SetFileCreator(OSType aCreator)
03226 {
03227     if (aCreator == CURRENT_PROCESS_CREATOR)
03228         aCreator = sCurrentProcessSignature;
03229         
03230     mCreator = aCreator;
03231 
03232     FSSpec fileSpec;
03233     (void)GetFSSpec(&fileSpec); 
03234     
03235     FInfo info;
03236     OSErr err = ::FSpGetFInfo(&fileSpec, &info);
03237     if (err != noErr)
03238         return NS_ERROR_FILE_NOT_FOUND;
03239             
03240     info.fdCreator = aCreator;  
03241     err = ::FSpSetFInfo(&fileSpec, &info);
03242     if (err != noErr)
03243         return NS_ERROR_FILE_ACCESS_DENIED;
03244     
03245     return NS_OK;
03246 }
03247 
03248 NS_IMETHODIMP nsLocalFile::SetFileTypeAndCreatorFromExtension(const char *aExtension)
03249 {
03250     NS_ENSURE_ARG(aExtension);
03251     return SetOSTypeAndCreatorFromExtension(aExtension);
03252 }
03253 
03254 NS_IMETHODIMP nsLocalFile::SetFileTypeAndCreatorFromMIMEType(const char *aMIMEType)
03255 {
03256     NS_ENSURE_ARG(aMIMEType);
03257 
03258     nsresult rv;
03259     nsCOMPtr<nsIInternetConfigService> icService(do_GetService
03260         (NS_INTERNETCONFIGSERVICE_CONTRACTID, &rv));
03261         
03262     if (NS_SUCCEEDED(rv))
03263     {
03264         nsCOMPtr<nsIMIMEInfo> mimeInfo;
03265         PRUint32 fileType = 'TEXT';
03266         PRUint32 fileCreator = nsILocalFileMac::CURRENT_PROCESS_CREATOR;
03267     
03268         rv = icService->FillInMIMEInfo(aMIMEType, 
03269                 nsnull, getter_AddRefs(mimeInfo));
03270         if (NS_SUCCEEDED(rv))
03271             rv = mimeInfo->GetMacType(&fileType);
03272         if (NS_SUCCEEDED(rv))
03273             rv = mimeInfo->GetMacCreator(&fileCreator);
03274         if (NS_SUCCEEDED(rv))
03275             rv = SetFileType(fileType);
03276         if (NS_SUCCEEDED(rv))
03277             rv  = SetFileCreator(fileCreator);    
03278     }
03279     
03280     return rv;
03281 }
03282 
03283 NS_IMETHODIMP  
03284 nsLocalFile::GetFileSizeWithResFork(PRInt64 *aFileSize)
03285 {
03286     NS_ENSURE_ARG(aFileSize);
03287     
03288     *aFileSize = LL_Zero();
03289 
03290     ResolveAndStat();
03291     
03292     long dataSize = 0;
03293     long resSize = 0;
03294     
03295     OSErr err = FSpGetFileSize(&mTargetSpec, &dataSize, &resSize);
03296                                
03297     if (err != noErr)
03298         return MacErrorMapper(err);
03299     
03300     // For now we've only got 32 bits of file size info
03301     PRInt64 dataInt64 = LL_Zero();
03302     PRInt64 resInt64 = LL_Zero();
03303     
03304     // Combine the size of the resource and data forks
03305     LL_I2L(resInt64, resSize);
03306     LL_I2L(dataInt64, dataSize);
03307     LL_ADD((*aFileSize), dataInt64, resInt64);
03308     
03309     return NS_OK;
03310 }
03311 
03312 
03313 // this nsLocalFile points to the app. We want to launch it, optionally with the document.
03314 NS_IMETHODIMP
03315 nsLocalFile::LaunchWithDoc(nsILocalFile* aDocToLoad, PRBool aLaunchInBackground)
03316 {
03317     // are we launchable?
03318     PRBool isExecutable;
03319     nsresult rv = IsExecutable(&isExecutable);
03320     if (NS_FAILED(rv)) return rv;
03321     if (!isExecutable) return NS_ERROR_FILE_EXECUTION_FAILED;
03322     
03323     FSSpec          docSpec;
03324     FSSpecPtr       docSpecPtr = nsnull;
03325     
03326     nsCOMPtr<nsILocalFileMac> macDoc = do_QueryInterface(aDocToLoad);
03327     if (macDoc)
03328     {
03329         rv = macDoc->GetFSSpec(&docSpec); // XXX GetTargetFSSpec
03330         if (NS_FAILED(rv)) return rv;
03331         
03332         docSpecPtr = &docSpec;
03333     }
03334     
03335     FSSpec  appSpec;
03336     rv = GetFSSpec(&appSpec); // XXX GetResolvedFSSpec
03337     if (NS_FAILED(rv)) return rv;
03338     
03339     rv = MyLaunchAppWithDoc(appSpec, docSpecPtr, aLaunchInBackground);
03340     return rv;
03341 }
03342 
03343 
03344 NS_IMETHODIMP
03345 nsLocalFile::OpenDocWithApp(nsILocalFile* aAppToOpenWith, PRBool aLaunchInBackground)
03346 {
03347     // if aAppToOpenWith is nil, we have to find the app from the creator code
03348     // of the document
03349     nsresult rv = NS_OK;
03350     
03351     FSSpec      appSpec;
03352     
03353     if (aAppToOpenWith)
03354     {
03355         nsCOMPtr<nsILocalFileMac> appFileMac = do_QueryInterface(aAppToOpenWith, &rv);
03356         if (!appFileMac) return rv;
03357     
03358         rv = appFileMac->GetFSSpec(&appSpec); // XXX GetTargetFSSpec
03359         if (NS_FAILED(rv)) return rv;
03360 
03361         // is it launchable?
03362         PRBool isExecutable;
03363         rv = aAppToOpenWith->IsExecutable(&isExecutable);
03364         if (NS_FAILED(rv)) return rv;
03365         if (!isExecutable) return NS_ERROR_FILE_EXECUTION_FAILED;
03366     }
03367     else
03368     {
03369         // look for one
03370         OSType  fileCreator;
03371         rv = GetFileCreator(&fileCreator);
03372         if (NS_FAILED(rv)) return rv;
03373         
03374         // just make one on the stack
03375         nsLocalFile localAppFile;
03376         rv = localAppFile.InitToAppWithCreatorCode(fileCreator);
03377         if (NS_FAILED(rv)) return rv;
03378         
03379         rv = localAppFile.GetFSSpec(&appSpec); // GetTargetFSSpec
03380         if (NS_FAILED(rv)) return rv;
03381     }
03382     
03383     FSSpec      docSpec;
03384     rv = GetFSSpec(&docSpec); // XXX GetResolvedFSSpec
03385     if (NS_FAILED(rv)) return rv;
03386 
03387     rv = MyLaunchAppWithDoc(appSpec, &docSpec, aLaunchInBackground);
03388     return rv;
03389 }
03390 
03391 nsresult nsLocalFile::SetOSTypeAndCreatorFromExtension(const char* extension)
03392 {
03393     nsresult rv;
03394     
03395     nsCAutoString localExtBuf;
03396     const char *extPtr;
03397     
03398     if (!extension)
03399     {
03400         rv = GetNativeLeafName(localExtBuf);
03401         extPtr = strrchr(localExtBuf.get(), '.');
03402         if (!extPtr)
03403             return NS_ERROR_FAILURE;
03404         ++extPtr;   
03405     }
03406     else
03407     {
03408         extPtr = extension;
03409         if (*extPtr == '.')
03410             ++extPtr;
03411     }
03412     
03413     nsCOMPtr<nsIInternetConfigService> icService = 
03414              do_GetService(NS_INTERNETCONFIGSERVICE_CONTRACTID, &rv);
03415     if (NS_SUCCEEDED(rv))
03416     {
03417         nsCOMPtr<nsIMIMEInfo> mimeInfo;
03418         rv = icService->GetMIMEInfoFromExtension(extPtr, getter_AddRefs(mimeInfo));
03419         if (NS_SUCCEEDED(rv))
03420         {
03421             PRUint32 osType;
03422             rv = mimeInfo->GetMacType(&osType);
03423             if (NS_SUCCEEDED(rv))
03424                 mType = osType;
03425             PRBool skip;
03426             rv = ExtensionIsOnExceptionList(extPtr, &skip);
03427             if (NS_SUCCEEDED(rv) && !skip)
03428             {
03429                 rv = mimeInfo->GetMacCreator(&osType);
03430                 if (NS_SUCCEEDED(rv))
03431                     mCreator = osType;
03432             }
03433         }
03434     }
03435     return rv;
03436 }
03437 
03438 nsresult nsLocalFile::ExtensionIsOnExceptionList(const char *extension, PRBool *onList)
03439 {
03440     // Probably want to make a global list somewhere in the future
03441     // for now, just check for "html" and "htm"
03442     
03443     *onList = PR_FALSE;
03444     
03445     if (!nsCRT::strcasecmp(extension, "html") ||
03446         !nsCRT::strcasecmp(extension, "htm"))
03447         *onList = PR_TRUE;
03448     return NS_OK;
03449 }
03450 
03451 
03452 void nsLocalFile::InitClassStatics()
03453 {
03454     OSErr err;
03455     
03456 
03457     if (sCurrentProcessSignature == 0)
03458     {
03459         ProcessSerialNumber psn;
03460         ProcessInfoRec  info;
03461         
03462         psn.highLongOfPSN = 0;
03463         psn.lowLongOfPSN  = kCurrentProcess;
03464 
03465         info.processInfoLength = sizeof(ProcessInfoRec);
03466         info.processName = nil;
03467         info.processAppSpec = nil;
03468         err = ::GetProcessInformation(&psn, &info);
03469         if (err == noErr)
03470             sCurrentProcessSignature = info.processSignature;
03471         // Try again next time if error
03472     }
03473     
03474     static PRBool didHFSPlusCheck = PR_FALSE;
03475     if (!didHFSPlusCheck)
03476     {
03477         long response;
03478         err = ::Gestalt(gestaltFSAttr, &response);
03479         sHasHFSPlusAPIs = (err == noErr && (response & (1 << gestaltHasHFSPlusAPIs)) != 0);
03480         didHFSPlusCheck = PR_TRUE;
03481     }
03482     
03483     static PRBool didOSXCheck = PR_FALSE;
03484     if (!didOSXCheck)
03485     {
03486         long version;
03487         sRunningOSX = (::Gestalt(gestaltSystemVersion, &version) == noErr && version >= 0x00001000);
03488         didOSXCheck = PR_TRUE;
03489     }
03490 }
03491 
03492 
03493 #pragma mark -
03494 
03495 // Handy dandy utility create routine for something or the other
03496 nsresult 
03497 NS_NewNativeLocalFile(const nsACString &path, PRBool followLinks, nsILocalFile* *result)
03498 {
03499     nsLocalFile* file = new nsLocalFile();
03500     if (file == nsnull)
03501         return NS_ERROR_OUT_OF_MEMORY;
03502     NS_ADDREF(file);
03503 
03504     file->SetFollowLinks(followLinks);
03505 
03506     if (!path.IsEmpty()) {
03507         nsresult rv = file->InitWithNativePath(path);
03508         if (NS_FAILED(rv)) {
03509             NS_RELEASE(file);
03510             return rv;
03511         }
03512     }
03513     *result = file;
03514     return NS_OK;
03515 }
03516 
03517 nsresult 
03518 NS_NewLocalFile(const nsAString &path, PRBool followLinks, nsILocalFile* *result)
03519 {
03520     nsCAutoString fsCharSetStr;   
03521     nsresult rv = NS_CopyUnicodeToNative(path, fsCharSetStr);
03522     if (NS_FAILED(rv))
03523         return rv;
03524     return NS_NewNativeLocalFile(fsCharSetStr, followLinks, result);
03525 }
03526 
03527 nsresult 
03528 NS_NewLocalFileWithFSSpec(const FSSpec* inSpec, PRBool followLinks, nsILocalFileMac* *result)
03529 {
03530     nsLocalFile* file = new nsLocalFile();
03531     if (file == nsnull)
03532         return NS_ERROR_OUT_OF_MEMORY;
03533     NS_ADDREF(file);
03534 
03535     file->SetFollowLinks(followLinks);
03536 
03537     nsresult rv = file->InitWithFSSpec(inSpec);
03538     if (NS_FAILED(rv)) {
03539         NS_RELEASE(file);
03540         return rv;
03541     }
03542     *result = file;
03543     return NS_OK;
03544 }
03545 
03546 void
03547 nsLocalFile::GlobalInit()
03548 {
03549 }
03550 
03551 void
03552 nsLocalFile::GlobalShutdown()
03553 {
03554 }