Back to index

lightning-sunbird  0.9+nobinonly
nsFileSpecMac.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037  
00038 //       This file is included by nsFile.cp, and includes the Macintosh-specific
00039 //       implementations.
00040 
00041 
00042 #include <string.h>
00043 
00044 #include "prtypes.h"
00045 #include "nscore.h"
00046 
00047 #include "FullPath.h"
00048 #include "FileCopy.h"
00049 #include "MoreFilesExtras.h"
00050 
00051 #include <Aliases.h>
00052 #include <Folders.h>
00053 #include <Math64.h>
00054 #include <TextUtils.h>
00055 #include <Processes.h>
00056 #include <limits.h>         // ULONG_MAX
00057 
00058 #include "nsFileSpec.h"
00059 #include "nsEscape.h"
00060 #include "nsXPIDLString.h"
00061 
00062 
00063 const unsigned char* kAliasHavenFolderName = "\pnsAliasHaven";
00064 
00065 //========================================================================================
00066 namespace MacFileHelpers
00067 //========================================================================================
00068 {
00069        inline void                                      PLstrcpy(Str255 dst, ConstStr255Param src)
00070                                                                {
00071                                                                       memcpy(dst, src, 1 + src[0]);
00072                                                                }
00073 
00074        void                                             PLstrcpy(Str255 dst, const char* src, int inMaxLen=255);
00075        void                                             PLstrncpy(Str255 dst, const char* src, int inMaxLen);
00076 
00077        void                                             SwapSlashColon(char * s);
00078        OSErr                                            FSSpecFromUnixPath(
00079                                                                       const char * unixPath,
00080                                                                       FSSpec& ioSpec,
00081                                                                       Boolean hexDecode,
00082                                                                       Boolean resolveAlias,
00083                                                                       Boolean allowPartial = false,
00084                                                                       Boolean createDirs = false);
00085        char*                                            MacPathFromUnixPath(
00086                                                                       const char* unixPath,
00087                                                                       Boolean hexDecode);
00088        char*                                            EncodeMacPath(
00089                                                                       char* inPath, // NOT const - gets clobbered
00090                                                                       Boolean prependSlash,
00091                                                                       Boolean doEscape );
00092        OSErr                                            FSSpecFromPathname(
00093                                                                       const char* inPathNamePtr,
00094                                                                       FSSpec& ioSpec,
00095                                                                       Boolean inCreateDirs);
00096        char*                                            PathNameFromFSSpec(
00097                                                                       const FSSpec& inSpec );
00098        OSErr                                            CreateFolderInFolder(
00099                                                                       short                       refNum,              // Parent directory/volume
00100                                                                       long                        dirID,
00101                                                                       ConstStr255Param     folderName,      // Name of the new folder
00102                                                                       short&                      outRefNum,      // Volume of the created folder
00103                                                                       long&                       outDirID);      // 
00104 
00105        // Some routines to support an "alias haven" directory.  Aliases in this directory
00106        // are never resolved.      There is a ResolveAlias here that respects that.  This is
00107        // to support attaching of aliases in mail.
00108        void                                             EnsureAliasHaven();
00109        void                                             SetNoResolve(Boolean inResolve);
00110        PRBool                                           IsAliasSafe(const FSSpec& inSpec);
00111        OSErr                                            MakeAliasSafe(FSSpec& inOutSpec);
00112        OSErr                                            ResolveAliasFile(FSSpec& inOutSpec, Boolean& wasAliased);
00113 
00114        Boolean                                                 sNoResolve = false;
00115        long                                             sAliasHavenDirID = 0;
00116        short                                            sAliasHavenVRefNum = 0;
00117 } // namespace MacFileHelpers
00118 
00119 //----------------------------------------------------------------------------------------
00120 void MacFileHelpers::PLstrcpy(Str255 dst, const char* src, int inMax)
00121 //----------------------------------------------------------------------------------------
00122 {
00123        int srcLength = strlen(src);
00124        NS_ASSERTION(srcLength <= inMax, "Oops, string is too long!");
00125        if (srcLength > inMax)
00126               srcLength = inMax;
00127        dst[0] = srcLength;
00128        memcpy(&dst[1], src, srcLength);
00129 }
00130 
00131 //----------------------------------------------------------------------------------------
00132 void MacFileHelpers::PLstrncpy(Str255 dst, const char* src, int inMax)
00133 //----------------------------------------------------------------------------------------
00134 {
00135        int srcLength = strlen(src);
00136        if (srcLength > inMax)
00137               srcLength = inMax;
00138        dst[0] = srcLength;
00139        memcpy(&dst[1], src, srcLength);
00140 }
00141 
00142 //-----------------------------------
00143 void MacFileHelpers::SwapSlashColon(char * s)
00144 //-----------------------------------
00145 
00146 {
00147        while (*s)
00148        {
00149               if (*s == '/')
00150                      *s++ = ':';
00151               else if (*s == ':')
00152                      *s++ = '/';
00153               else
00154                      *s++;
00155        }
00156 } // MacFileHelpers::SwapSlashColon
00157 
00158 //-----------------------------------
00159 char* MacFileHelpers::EncodeMacPath(
00160        char* inPath, // NOT const, gets clobbered
00161        Boolean prependSlash,
00162        Boolean doEscape )
00163 //       Transforms Macintosh style path into Unix one
00164 //       Method: Swap ':' and '/', hex escape the result
00165 //-----------------------------------
00166 {
00167        if (inPath == nsnull)
00168               return nsnull;
00169        int pathSize = strlen(inPath);
00170        
00171        // XP code sometimes chokes if there's a final slash in the unix path.
00172        // Since correct mac paths to folders and volumes will end in ':', strip this
00173        // first.
00174        char* c = inPath + pathSize - 1;
00175        if (*c == ':')
00176        {
00177               *c = 0;
00178               pathSize--;
00179        }
00180 
00181        char * newPath = nsnull;
00182        char * finalPath = nsnull;
00183        
00184        if (prependSlash)
00185        {
00186               newPath = new char[pathSize + 2];
00187               newPath[0] = ':';     // It will be converted to '/'
00188               memcpy(&newPath[1], inPath, pathSize + 1);
00189        }
00190        else
00191        {
00192               newPath = new char[pathSize + 1];
00193               strcpy(newPath, inPath);
00194        }
00195        if (newPath)
00196        {
00197               SwapSlashColon( newPath );
00198               if (doEscape)
00199               {
00200                      finalPath = nsEscape(newPath, url_Path);
00201                      delete [] newPath;
00202               }
00203               else
00204                      finalPath = newPath;
00205        }
00206        delete [] inPath;
00207        return finalPath;
00208 } // MacFileHelpers::EncodeMacPath
00209 
00210 //----------------------------------------------------------------------------------------
00211 inline void MacFileHelpers::SetNoResolve(Boolean inResolve)
00212 //----------------------------------------------------------------------------------------
00213 {
00214        sNoResolve = inResolve;
00215 } // MacFileHelpers::SetNoResolve
00216 
00217 //----------------------------------------------------------------------------------------
00218 OSErr MacFileHelpers::MakeAliasSafe(FSSpec& inOutSpec)
00219 // Pass in the spec of an alias.  This copies the file to the safe haven folder, and
00220 // returns the spec of the copy to the caller
00221 //----------------------------------------------------------------------------------------
00222 {
00223        EnsureAliasHaven();
00224        nsFileSpec dstDirSpec(sAliasHavenVRefNum, sAliasHavenDirID, "\p");
00225 
00226        // Make sure its name is unique
00227        nsFileSpec havenSpec(sAliasHavenVRefNum, sAliasHavenDirID, "\pG'day");
00228        if (havenSpec.Valid())
00229               havenSpec.MakeUnique(inOutSpec.name);
00230        // Copy the file into the haven directory
00231        if (havenSpec.Valid())
00232        {
00233               OSErr err = ::FSpFileCopy(
00234                                           &inOutSpec,
00235                                           dstDirSpec,
00236                                           havenSpec.GetLeafPName(),
00237                                           nil, 0, true);
00238               // Return the spec of the copy to the caller.
00239               if (err != noErr)
00240                      return err;
00241               inOutSpec = havenSpec;
00242        }
00243        return noErr;
00244 } // MacFileHelpers::MakeAliasSafe
00245 
00246 //----------------------------------------------------------------------------------------
00247 char* MacFileHelpers::MacPathFromUnixPath(const char* unixPath, Boolean hexDecode)
00248 //----------------------------------------------------------------------------------------
00249 {
00250        // Relying on the fact that the unix path is always longer than the mac path:
00251        size_t len = strlen(unixPath);
00252        char* result = new char[len + 2]; // ... but allow for the initial colon in a partial name
00253        // REMEMBER: at the end we call SwapSlashColon, so bear that in mind when you see
00254        // this code using '/' as a separator in what is supposed to be a Macintosh path!
00255        if (result)
00256        {
00257               char* dst = result;
00258               const char* src = unixPath;
00259               if (*src == '/')                    // * full path
00260                      src++;
00261               else if (strchr(src, '/') && *src != '.')
00262               {
00263                      // * partial path, and not just a leaf name. The '.' test is there because
00264                      // the loop below will add sufficient colons in that case.
00265                      *dst++ = '/';
00266               }
00267               // Copy src to dst, but watch out for .. and .
00268               char c = '/';
00269               do
00270               {
00271                      char cprev = c; // remember the previous char (initially /)
00272                      c = *src++;
00273                      if (c == '.' && cprev == '/')
00274                      {
00275                             char* dstSaved = dst;
00276                             // Special cases: "." and "..". Convert to ':' and '::'
00277                             *dst++ = '/';  // . becomes :
00278                             c = *src++;
00279                             if (c == '.')
00280                             {
00281                                    *dst++ = '/'; // .. becomes ::
00282                                    c = *src++;
00283                             }
00284                             if (c == '/')
00285                             {
00286                                    // ../   becomes     :: so just skip the slash
00287                                    // ./    becomes     :  "  "          "   "      "
00288                                    src++;
00289                             }
00290                             else if (c)
00291                             {
00292                                    // Oh. A file called ".foo" or "..foo"
00293                                    // Back up and just do the normal thing.
00294                                    src -= (dst - dstSaved);
00295                                    dst = dstSaved;
00296                                    // Since c is not '/', we won't do this stuff on the
00297                                    // next iteration.
00298                             }
00299                             continue;
00300                      }
00301                      else if (c == '/' && cprev == '/')
00302                      {
00303                             // Hmm. A 'run on' path with two slashes right next to each other.
00304                             // This is an illegal path, but, hey, we'll be tough and try to
00305                             // deal with it (especially since '::' has loaded semantics in
00306                             // a Mac path)
00307                             continue;
00308                      }
00309                      *dst++ = c;
00310               } while (c);
00311               if (hexDecode)
00312                      nsUnescape(result);     // Hex Decode
00313               MacFileHelpers::SwapSlashColon(result);
00314        }
00315        return result;
00316 } // MacFileHelpers::MacPathFromUnixPath
00317 
00318 //----------------------------------------------------------------------------------------
00319 OSErr MacFileHelpers::FSSpecFromPathname(
00320        const char* inPathNamePtr,
00321        FSSpec& ioSpec, // used as in-parameter for a relative path.
00322        Boolean inCreateDirs)
00323 // FSSpecFromPathname reverses PathNameFromFSSpec.
00324 // It returns a FSSpec given a c string which is a mac pathname.
00325 //----------------------------------------------------------------------------------------
00326 {
00327        OSErr err;
00328        // Simplify this routine to use FSMakeFSSpec if length < 255. Otherwise use the MoreFiles
00329        // routine FSpLocationFromFullPath, which allocates memory, to handle longer pathnames. 
00330        
00331        short inVRefNum = ioSpec.vRefNum;
00332        long inParID = ioSpec.parID;
00333        
00334        size_t inLength = strlen(inPathNamePtr);
00335        bool isRelative = (strchr(inPathNamePtr, ':') == 0 || *inPathNamePtr == ':');
00336 #ifdef NS_DEBUG
00337        // Attempt to catch people sending unix paths in to routines that expect native ones.
00338        NS_ASSERTION(strchr(inPathNamePtr, '/') == 0,
00339                      "Possible unix path where native path is required");
00340 #endif
00341        if (inLength < 255)
00342        {
00343               Str255 pascalpath;
00344               MacFileHelpers::PLstrcpy(pascalpath, inPathNamePtr);
00345               if (isRelative)
00346                      err = ::FSMakeFSSpec(inVRefNum, inParID, pascalpath, &ioSpec);
00347               else
00348                      err = ::FSMakeFSSpec(0, 0, pascalpath, &ioSpec);
00349        }
00350        else if (!isRelative)
00351               err = FSpLocationFromFullPath(inLength, inPathNamePtr, &ioSpec);
00352        else
00353               err = bdNamErr;
00354 
00355        if ((err == dirNFErr || err == bdNamErr) && inCreateDirs)
00356        {
00357               const char* path = inPathNamePtr;
00358               if (isRelative)
00359               {
00360                      ioSpec.vRefNum = inVRefNum;
00361                      ioSpec.parID = inParID;
00362               }
00363               else
00364               {
00365                      ioSpec.vRefNum = 0;
00366                      ioSpec.parID = 0;
00367               }
00368               do {
00369                      // Locate the colon that terminates the node.
00370                      // But if we've a partial path (starting with a colon), find the second one.
00371                      const char* nextColon = strchr(path + (*path == ':'), ':');
00372                      // Well, if there are no more colons, point to the end of the string.
00373                      if (!nextColon)
00374                             nextColon = path + strlen(path);
00375 
00376                      // Make a pascal string out of this node.  Include initial
00377                      // and final colon, if any!
00378                      Str255 ppath;
00379                      MacFileHelpers::PLstrncpy(ppath, path, nextColon - path + 1);
00380                      
00381                      // Use this string as a relative path using the directory created
00382                      // on the previous round (or directory 0,0 on the first round).
00383                      err = ::FSMakeFSSpec(ioSpec.vRefNum, ioSpec.parID, ppath, &ioSpec);
00384 
00385                      // If this was the leaf node, then we are done.
00386                      if (!*nextColon)
00387                             break;
00388 
00389                      // Since there's more to go, we have to get the directory ID, which becomes
00390                      // the parID for the next round.
00391                      if (err == noErr)
00392                      {
00393                             // The directory (or perhaps a file) exists. Find its dirID.
00394                             long dirID;
00395                             Boolean isDirectory;
00396                             err = ::FSpGetDirectoryID(&ioSpec, &dirID, &isDirectory);
00397                             if (!isDirectory)
00398                                    return dupFNErr; // oops! a file exists with that name.
00399                             if (err)
00400                                    return err;
00401                             ioSpec.parID = dirID;
00402                      }
00403                      else if (err == fnfErr)
00404                      {
00405                             // If we got "file not found", then
00406                             // we need to create a directory.
00407                             err = ::FSpDirCreate(&ioSpec, smCurrentScript, &ioSpec.parID);
00408                             // For some reason, this usually returns fnfErr, even though it works.
00409                             if (err == fnfErr)
00410                                    err = noErr;
00411                      }
00412                      if (err != noErr)
00413                             return err;
00414                      path = nextColon; // next round
00415               } while (1);
00416        }
00417        return err;
00418 } // MacFileHelpers::FSSpecFromPathname
00419 
00420 //----------------------------------------------------------------------------------------
00421 OSErr MacFileHelpers::CreateFolderInFolder(
00422        short                        refNum,       // Parent directory/volume
00423        long                        dirID,
00424        ConstStr255Param     folderName,      // Name of the new folder
00425        short&                        outRefNum,  // Volume of the created folder
00426        long&                        outDirID)      // 
00427 // Creates a folder named 'folderName' inside a folder.
00428 // The errors returned are same as PBDirCreate
00429 //----------------------------------------------------------------------------------------
00430 {
00431        HFileParam hpb;
00432        hpb.ioVRefNum = refNum;
00433        hpb.ioDirID = dirID;
00434        hpb.ioNamePtr = (StringPtr)&folderName;
00435 
00436        OSErr err = PBDirCreateSync((HParmBlkPtr)&hpb);
00437        if (err == noErr)
00438        {
00439               outRefNum = hpb.ioVRefNum;
00440               outDirID = hpb.ioDirID;
00441        }
00442        else
00443        {
00444               outRefNum = 0;
00445               outDirID = 0;
00446        }
00447        return err;
00448 } // MacFileHelpers::CreateFolderInFolder
00449 
00450 //----------------------------------------------------------------------------------------
00451 void MacFileHelpers::EnsureAliasHaven()
00452 //----------------------------------------------------------------------------------------
00453 {
00454        // Alias Haven is a directory in which we never resolve aliases.
00455        if (sAliasHavenVRefNum != 0)
00456               return;
00457 
00458        
00459        FSSpec temp;
00460        if (FindFolder(0, kTemporaryFolderType, true, & temp.vRefNum, &temp.parID) == noErr)
00461        {
00462               CreateFolderInFolder(
00463                      temp.vRefNum,                        // Parent directory/volume
00464                      temp.parID,
00465                      kAliasHavenFolderName,               // Name of the new folder
00466                      sAliasHavenVRefNum,            // Volume of the created folder
00467                      sAliasHavenDirID);            
00468        }
00469 } // MacFileHelpers::EnsureAliasHaven
00470 
00471 //----------------------------------------------------------------------------------------
00472 PRBool MacFileHelpers::IsAliasSafe(const FSSpec& inSpec)
00473 // Returns true if the alias is in the alias haven directory, or if alias resolution
00474 // has been turned off.
00475 //----------------------------------------------------------------------------------------
00476 {
00477        return sNoResolve
00478               || (inSpec.parID == sAliasHavenDirID && inSpec.vRefNum == sAliasHavenVRefNum);
00479 } // MacFileHelpers::IsAliasSafe
00480 
00481 //----------------------------------------------------------------------------------------
00482 OSErr MacFileHelpers::ResolveAliasFile(FSSpec& inOutSpec, Boolean& wasAliased)
00483 //----------------------------------------------------------------------------------------
00484 {
00485        wasAliased = false;
00486        if (IsAliasSafe(inOutSpec))
00487               return noErr;
00488        Boolean dummy;         
00489        return ::ResolveAliasFile(&inOutSpec, TRUE, &dummy, &wasAliased);
00490 } // MacFileHelpers::ResolveAliasFile
00491 
00492 //-----------------------------------
00493 OSErr MacFileHelpers::FSSpecFromUnixPath(
00494        const char * unixPath,
00495        FSSpec& ioSpec,
00496        Boolean hexDecode,
00497        Boolean resolveAlias,
00498        Boolean allowPartial,
00499        Boolean createDirs)
00500 // File spec from URL. Reverses GetURLFromFileSpec 
00501 // Its input is only the <path> part of the URL
00502 // JRM 97/01/08 changed this so that if it's a partial path (doesn't start with '/'),
00503 // then it is combined with inOutSpec's vRefNum and parID to form a new spec.
00504 //-----------------------------------
00505 {
00506        if (unixPath == nsnull)
00507               return badFidErr;
00508        char* macPath = MacPathFromUnixPath(unixPath, hexDecode);
00509        if (!macPath)
00510               return memFullErr;
00511 
00512        OSErr err = noErr;
00513        if (!allowPartial)
00514        {
00515               NS_ASSERTION(*unixPath == '/' /*full path*/, "Not a full Unix path!");
00516        }
00517        err = FSSpecFromPathname(macPath, ioSpec, createDirs);
00518        if (err == fnfErr)
00519               err = noErr;
00520        Boolean dummy;         
00521        if (err == noErr && resolveAlias)   // Added 
00522               err = MacFileHelpers::ResolveAliasFile(ioSpec, dummy);
00523        delete [] macPath;
00524        NS_ASSERTION(err==noErr||err==fnfErr||err==dirNFErr||err==nsvErr, "Not a path!");
00525        return err;
00526 } // MacFileHelpers::FSSpecFromLocalUnixPath
00527 
00528 //-----------------------------------
00529 char* MacFileHelpers::PathNameFromFSSpec( const FSSpec& inSpec )
00530 // Returns a full pathname to the given file
00531 // Returned value is allocated with new [], and must be freed with delete []
00532 // For consistency and to work under OS X this creates an nsILocalFileMac and has it do the work.
00533 //-----------------------------------
00534 {
00535        char* result = nil;
00536        nsresult rv;
00537 
00538     FSSpec nonConstSpec = inSpec;
00539     nsCAutoString path;     
00540     nsCOMPtr<nsILocalFileMac> macFile;
00541     
00542     rv = NS_NewLocalFileWithFSSpec(&nonConstSpec, PR_TRUE, getter_AddRefs(macFile));
00543     if (NS_FAILED(rv)) return nsnull;
00544     nsCOMPtr<nsILocalFile> localFile(do_QueryInterface(macFile, &rv));
00545     if (NS_FAILED(rv)) return nsnull;
00546     rv = localFile->GetNativePath(path);
00547     if (NS_FAILED(rv)) return nsnull;
00548     PRInt32 strLen = path.Length();
00549     result = new char [strLen + 1];
00550     if (!result) return nsnull;
00551        memcpy(result, path.get(), strLen);
00552        result[ strLen ] = 0;
00553 
00554        return result;
00555 } // MacFileHelpers::PathNameFromFSSpec
00556 
00557 
00558 #pragma mark -
00559 
00560 //========================================================================================
00561 //                                   Macintosh nsFileSpec implementation
00562 //========================================================================================
00563 
00564 //----------------------------------------------------------------------------------------
00565 nsFileSpec::nsFileSpec()
00566 //----------------------------------------------------------------------------------------
00567 {
00568 //    NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!");
00569        Clear();
00570 }
00571 
00572 //----------------------------------------------------------------------------------------
00573 nsFileSpec::nsFileSpec(const FSSpec& inSpec, PRBool resolveAlias)
00574 //----------------------------------------------------------------------------------------
00575 : mSpec(inSpec)
00576 , mError(NS_OK)
00577 {
00578 //    NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!");
00579        if (resolveAlias)
00580        {
00581               PRBool dummy;
00582               ResolveSymlink(dummy);
00583        }
00584 }
00585 
00586 //----------------------------------------------------------------------------------------
00587 void nsFileSpec::operator = (const FSSpec& inSpec)
00588 //----------------------------------------------------------------------------------------
00589 {
00590        mSpec = inSpec;
00591        mError = NS_OK;
00592 }
00593 
00594 //----------------------------------------------------------------------------------------
00595 nsFileSpec::nsFileSpec(const nsFileSpec& inSpec)
00596 //----------------------------------------------------------------------------------------
00597 :       mSpec(inSpec.mSpec)
00598 ,       mError(inSpec.Error())
00599 {
00600 //    NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!");
00601 }
00602 
00603 //----------------------------------------------------------------------------------------
00604 nsFileSpec::nsFileSpec(const char* inNativePathString, PRBool inCreateDirs)
00605 //----------------------------------------------------------------------------------------
00606 {
00607 //  NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!");
00608     Clear();         // this sets mError to NS_ERROR_NOT_INITIALIZED
00609 
00610        if (inNativePathString)
00611        {
00612               mError = NS_FILE_RESULT(MacFileHelpers::FSSpecFromPathname(
00613                               inNativePathString, mSpec, inCreateDirs));
00614               if (mError == NS_FILE_RESULT(fnfErr))
00615                      mError = NS_OK;
00616        }
00617 
00618 } // nsFileSpec::nsFileSpec
00619 
00620 //----------------------------------------------------------------------------------------
00621 nsFileSpec::nsFileSpec(const nsString& inNativePathString, PRBool inCreateDirs)
00622 //----------------------------------------------------------------------------------------
00623 {
00624 //    NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!");
00625        Clear();             // this sets mError to NS_ERROR_NOT_INITIALIZED
00626 
00627        mError = NS_FILE_RESULT(
00628               MacFileHelpers::FSSpecFromPathname(
00629                      NS_LossyConvertUCS2toASCII(inNativePathString).get(),
00630                      mSpec, inCreateDirs));
00631        if (mError == NS_FILE_RESULT(fnfErr))
00632               mError = NS_OK;
00633 
00634 } // nsFileSpec::nsFileSpec
00635 
00636 //----------------------------------------------------------------------------------------
00637 nsFileSpec::nsFileSpec(short vRefNum, long parID, ConstStr255Param fileName,  PRBool resolveAlias)
00638 //----------------------------------------------------------------------------------------
00639 {
00640 //    NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!");
00641        mError = NS_FILE_RESULT(::FSMakeFSSpec(vRefNum, parID, fileName, &mSpec));
00642        if (mError == NS_FILE_RESULT(fnfErr))
00643               mError = NS_OK;
00644  
00645        if (resolveAlias)
00646        {
00647               PRBool dummy;
00648               ResolveSymlink(dummy);
00649        }
00650 }
00651 
00652 //----------------------------------------------------------------------------------------
00653 nsFileSpec::nsFileSpec(const nsFilePath& inPath)
00654 //----------------------------------------------------------------------------------------
00655 {
00656 //    NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!");
00657        *this = inPath.GetFileSpec();
00658 }
00659 
00660 //----------------------------------------------------------------------------------------
00661 void nsFileSpec::operator = (const char* inString)
00662 //----------------------------------------------------------------------------------------
00663 {
00664        Clear();             // this sets mError to NS_ERROR_NOT_INITIALIZED
00665 
00666        if (inString)
00667        {
00668               mError = NS_FILE_RESULT(MacFileHelpers::FSSpecFromPathname(inString, mSpec, true));
00669               if (mError == NS_FILE_RESULT(fnfErr))
00670                      mError = NS_OK;
00671        }
00672        
00673 } // nsFileSpec::operator =
00674 
00675 //----------------------------------------------------------------------------------------
00676 void nsFileSpec::operator = (const nsFileSpec& inSpec)
00677 //----------------------------------------------------------------------------------------
00678 {
00679        mPath.SetToEmpty();
00680        mSpec.vRefNum = inSpec.mSpec.vRefNum;
00681        mSpec.parID = inSpec.mSpec.parID;
00682 
00683        PRInt32 copySize = inSpec.mSpec.name[0] + 1;
00684        if (copySize > sizeof(inSpec.mSpec.name))
00685               copySize = sizeof(inSpec.mSpec.name);
00686        memcpy(mSpec.name, inSpec.mSpec.name, copySize);
00687        mError = inSpec.Error();    // note that the error is propagated
00688 } // nsFileSpec::operator =
00689 
00690 //----------------------------------------------------------------------------------------
00691 void nsFileSpec::operator = (const nsFilePath& inPath)
00692 //----------------------------------------------------------------------------------------
00693 {
00694        *this = inPath.GetFileSpec();
00695 } // nsFileSpec::operator =
00696 
00697 //----------------------------------------------------------------------------------------
00698 inline void nsFileSpec::Clear()
00699 //----------------------------------------------------------------------------------------
00700 {
00701        mPath.SetToEmpty();
00702        mSpec.vRefNum = 0;
00703        mSpec.parID = 0;
00704        mSpec.name[0] = 0;
00705        mError = NS_ERROR_NOT_INITIALIZED;
00706 }
00707 
00708 //----------------------------------------------------------------------------------------
00709 PRBool nsFileSpec::Exists() const
00710 //----------------------------------------------------------------------------------------
00711 {
00712   if (NS_FAILED(mError)) return PR_FALSE;
00713        FSSpec temp;
00714        return ::FSMakeFSSpec(mSpec.vRefNum, mSpec.parID, mSpec.name, &temp) == noErr;
00715 } // nsFileSpec::Exists()
00716 
00717 //----------------------------------------------------------------------------------------
00718 void nsFileSpec::GetModDate(TimeStamp& outStamp) const
00719 //----------------------------------------------------------------------------------------
00720 {
00721        CInfoPBRec pb;
00722        if (GetCatInfo(pb) == noErr)
00723               outStamp = ((DirInfo*)&pb)->ioDrMdDat; // The mod date is in the same spot for files and dirs.
00724        else
00725               outStamp = 0;
00726 } // nsFileSpec::GetModDate
00727 
00728 //----------------------------------------------------------------------------------------
00729 PRUint32 nsFileSpec::GetFileSize() const
00730 //----------------------------------------------------------------------------------------
00731 {
00732        CInfoPBRec pb;
00733        if (noErr == GetCatInfo(pb))
00734               return (PRUint32)((HFileInfo*)&pb)->ioFlLgLen;
00735        return 0;
00736 } // nsFileSpec::GetFileSize
00737 
00738 //----------------------------------------------------------------------------------------
00739 void nsFileSpec::SetLeafName(const char* inLeafName)
00740 // In leaf name can actually be a partial path...
00741 //----------------------------------------------------------------------------------------
00742 {
00743        NS_ASSERTION(inLeafName, "Attempt to set leaf name with a null string");
00744        
00745        mPath.SetToEmpty();
00746 
00747        if (inLeafName)
00748        {
00749               // what about long relative paths? Hmm?  We don't have a routine for this anywhere.
00750               Str255 partialPath;
00751               MacFileHelpers::PLstrcpy(partialPath, inLeafName);
00752               mError = NS_FILE_RESULT(
00753                      ::FSMakeFSSpec(mSpec.vRefNum, mSpec.parID, partialPath, &mSpec));
00754               if (mError == NS_FILE_RESULT(fnfErr))
00755                      mError = NS_OK;
00756        }
00757        
00758 } // nsFileSpec::SetLeafName
00759 
00760 //----------------------------------------------------------------------------------------
00761 char* nsFileSpec::GetLeafName() const
00762 // Result needs to be nsCRT::free()ed.
00763 //----------------------------------------------------------------------------------------
00764 {
00765        char leaf[sizeof(mSpec.name)];
00766        memcpy(leaf, &mSpec.name[1], mSpec.name[0]);
00767        leaf[mSpec.name[0]] = '\0';
00768        return nsCRT::strdup(leaf);
00769 } // nsFileSpec::GetLeafName
00770 
00771 //----------------------------------------------------------------------------------------
00772 void nsFileSpec::MakeAliasSafe()
00773 //----------------------------------------------------------------------------------------
00774 {
00775        mPath.SetToEmpty();
00776        mError = NS_FILE_RESULT(MacFileHelpers::MakeAliasSafe(mSpec));
00777 } // nsFileSpec::MakeAliasSafe
00778 
00779 //----------------------------------------------------------------------------------------
00780 void nsFileSpec::MakeUnique(ConstStr255Param inSuggestedLeafName)
00781 //----------------------------------------------------------------------------------------
00782 {
00783        mPath.SetToEmpty();
00784        if (inSuggestedLeafName[0] > 0)
00785               MacFileHelpers::PLstrcpy(mSpec.name, inSuggestedLeafName);
00786 
00787        MakeUnique();
00788 } // nsFileSpec::MakeUnique
00789 
00790 //----------------------------------------------------------------------------------------
00791 PRBool nsFileSpec::IsFile() const
00792 //----------------------------------------------------------------------------------------
00793 {
00794        long dirID;
00795        Boolean isDirectory;
00796        return (noErr == ::FSpGetDirectoryID(&mSpec, &dirID, &isDirectory) && !isDirectory);
00797 } // nsFileSpec::IsFile
00798 
00799 //----------------------------------------------------------------------------------------
00800 PRBool nsFileSpec::IsDirectory() const
00801 //----------------------------------------------------------------------------------------
00802 {
00803        long dirID;
00804        Boolean isDirectory;
00805        return (noErr == ::FSpGetDirectoryID(&mSpec, &dirID, &isDirectory) && isDirectory);
00806 } // nsFileSpec::IsDirectory
00807 
00808 //----------------------------------------------------------------------------------------
00809 PRBool nsFileSpec::IsHidden() const
00810 //----------------------------------------------------------------------------------------
00811 {
00812        CInfoPBRec           cInfo;
00813        PRBool               hidden = PR_FALSE;
00814 
00815        if (noErr == GetCatInfo(cInfo))
00816               if (cInfo.hFileInfo.ioFlFndrInfo.fdFlags & kIsInvisible)
00817                      hidden = PR_TRUE;
00818        
00819        return hidden;
00820 } // nsFileSpec::IsHidden
00821 
00822 //----------------------------------------------------------------------------------------
00823 PRBool nsFileSpec::IsSymlink() const
00824 //----------------------------------------------------------------------------------------
00825 {
00826        CInfoPBRec           cInfo;
00827        PRBool               hidden = PR_FALSE;
00828 
00829        if (noErr == GetCatInfo(cInfo))
00830               if (cInfo.hFileInfo.ioFlFndrInfo.fdFlags & kIsAlias)
00831                      hidden = PR_TRUE;
00832        
00833        return hidden;
00834 } // nsFileSpec::IsSymlink
00835 
00836 //----------------------------------------------------------------------------------------
00837 nsresult nsFileSpec::ResolveSymlink(PRBool& wasAliased)
00838 //----------------------------------------------------------------------------------------
00839 {
00840        Boolean wasAliased2; // Type conversion Boolean <--> PRBool 
00841        OSErr err = MacFileHelpers::ResolveAliasFile(mSpec, wasAliased2); 
00842        if (wasAliased2) 
00843        { 
00844               mError = NS_FILE_RESULT(err); 
00845               wasAliased = PR_TRUE; 
00846        } 
00847        else 
00848               wasAliased = PR_FALSE; 
00849 
00850        return mError;
00851 } // nsFileSpec::ResolveSymlink
00852 
00853 //----------------------------------------------------------------------------------------
00854 void nsFileSpec::GetParent(nsFileSpec& outSpec) const
00855 //----------------------------------------------------------------------------------------
00856 {
00857        if (NS_SUCCEEDED(mError))
00858               outSpec.mError = NS_FILE_RESULT(::FSMakeFSSpec(mSpec.vRefNum, mSpec.parID, nsnull, outSpec));
00859 } // nsFileSpec::GetParent
00860 
00861 //----------------------------------------------------------------------------------------
00862 void nsFileSpec::operator += (const char* inRelativePath)
00863 //----------------------------------------------------------------------------------------
00864 {
00865        NS_ASSERTION(inRelativePath, "Attempt to append relative path with null path");
00866 
00867        // Invalidate the path cache string, since we're changing ourselves.
00868        mPath.SetToEmpty();
00869 
00870        // if we are already bad, don't allow appendage
00871        if (NS_FAILED(Error()))
00872        {
00873          NS_WARNING("trying to append to a bad nsFileSpec");
00874          return;
00875        }
00876        
00877        // Find the dirID of the directory described by this spec
00878        long dirID;
00879        Boolean isDirectory;
00880        mError = NS_FILE_RESULT(::FSpGetDirectoryID(&mSpec, &dirID, &isDirectory));
00881        if (NS_FAILED(mError) || !isDirectory || !inRelativePath)
00882               return;
00883    // mSpec.vRefNum is already correct.
00884        mSpec.parID = dirID;
00885 
00886        // Next, determine if it is a UNIX or Mac style path. Now, Macintosh relative paths
00887        // are either leaf names (in which the distinction between unix and macintosh
00888        // relative paths disappears) or they start with a colon. If we find an initial colon,
00889        // then assume it's a macintosh path.
00890        // If it is a UNIX path (including just a leaf name), we will also look for ':' and
00891        // assert if we find one.
00892        if (*inRelativePath != ':')
00893        {
00894               // Looks like a UNIX path (including possibly just a leaf name)
00895               NS_ASSERTION(strchr(inRelativePath, ':') == nsnull, "Can not determine path type");
00896           // Convert unix path (which is unencoded) to a spec
00897               mError = NS_FILE_RESULT(
00898                      MacFileHelpers::FSSpecFromUnixPath(inRelativePath, mSpec, false, false, true, true));
00899        }
00900        else
00901        {
00902               // We must be a mac path!                         
00903               mError = NS_FILE_RESULT(MacFileHelpers::FSSpecFromPathname(inRelativePath, mSpec, true));
00904        }
00905        if (mError == NS_FILE_RESULT(fnfErr))
00906               mError = NS_OK;
00907        
00908 } // nsFileSpec::operator +=
00909 
00910 //----------------------------------------------------------------------------------------
00911 void nsFileSpec::CreateDirectory(int /* unix mode */)
00912 //----------------------------------------------------------------------------------------
00913 {
00914        long ignoredDirID;
00915        OSErr  err = ::FSpDirCreate(&mSpec, smCurrentScript, &ignoredDirID);
00916        // it's OK if the dir already exists
00917        if (err != noErr && IsDirectory())
00918               err = noErr;
00919               
00920        mError = NS_FILE_RESULT(err);
00921        
00922 } // nsFileSpec::CreateDirectory
00923 
00924 //----------------------------------------------------------------------------------------
00925 void nsFileSpec::Delete(PRBool inRecursive) const
00926 //----------------------------------------------------------------------------------------
00927 {
00928        OSErr anErr;
00929 
00930        nsresult& mutableError = const_cast<nsFileSpec*>(this)->mError;
00931        if (inRecursive)
00932        {
00933               // MoreFilesExtras
00934               anErr = ::DeleteDirectory(
00935                                    mSpec.vRefNum,
00936                                    mSpec.parID,
00937                                    const_cast<unsigned char*>(mSpec.name));
00938        }
00939        else
00940               anErr = ::FSpDelete(&mSpec);
00941        
00942        if (anErr == fnfErr) // deleting a file that doesn't exist isn't an error!
00943               anErr = noErr;
00944        
00945        mutableError = NS_FILE_RESULT(anErr);
00946    
00947 } // nsFileSpec::Delete
00948 
00949 //----------------------------------------------------------------------------------------
00950 void nsFileSpec::RecursiveCopy(nsFileSpec newDir) const
00951 //----------------------------------------------------------------------------------------
00952 {
00953        if (IsDirectory())
00954        {
00955               if (!(newDir.Exists()))
00956               {
00957                      newDir.CreateDirectory();
00958               }
00959 
00960               for (nsDirectoryIterator i(*this, PR_FALSE); i.Exists(); i++)
00961               {
00962                      nsFileSpec& child = (nsFileSpec&)i;
00963 
00964                      if (child.IsDirectory())
00965                      {
00966                             nsFileSpec tmpDirSpec(newDir);
00967 
00968                             char *leafname = child.GetLeafName();
00969                             tmpDirSpec += leafname;
00970                             nsCRT::free(leafname);
00971 
00972                             child.RecursiveCopy(tmpDirSpec);
00973                      }
00974                      else
00975                      {
00976                             child.RecursiveCopy(newDir);
00977                      }
00978               }
00979        }
00980        else
00981        {
00982               nsFileSpec& filePath = (nsFileSpec&) *this;
00983 
00984               if (!(newDir.Exists()))
00985               {
00986                      newDir.CreateDirectory();
00987               }
00988 
00989               filePath.CopyToDir(newDir);
00990        }
00991 } // nsFileSpec::RecursiveCopy
00992 
00993 //----------------------------------------------------------------------------------------
00994 nsresult nsFileSpec::Truncate(PRInt32 aNewLength) const
00995 //----------------------------------------------------------------------------------------
00996 {
00997     short   refNum;
00998     OSErr   err;
00999         
01000     // First see if we have an internal error set
01001     if (NS_FAILED(mError))
01002         return mError;
01003         
01004     // Need to open the file to trunc
01005     if (::FSpOpenDF(&mSpec, fsWrPerm, &refNum) != noErr)
01006         return NS_FILE_FAILURE;
01007 
01008     err = ::SetEOF(refNum, aNewLength);
01009         
01010     // Close the file unless we got an error that it was already closed
01011     if (err != fnOpnErr)
01012         (void)::FSClose(refNum);
01013         
01014     if (err != noErr)
01015         return NS_FILE_FAILURE;
01016         
01017     return NS_OK;
01018 } // nsFileSpec::Truncate
01019 
01020 //----------------------------------------------------------------------------------------
01021 nsresult nsFileSpec::Rename(const char* inNewName)
01022 //----------------------------------------------------------------------------------------
01023 {
01024        NS_ASSERTION(inNewName, "Attempt to rename with null new name");
01025 
01026        if (strchr(inNewName, '/'))
01027               return NS_FILE_FAILURE; // no relative paths here!
01028        
01029        Str255 pName;
01030        MacFileHelpers::PLstrcpy(pName, inNewName);
01031        if (::FSpRename(&mSpec, pName) != noErr)
01032               return NS_FILE_FAILURE;
01033        SetLeafName(inNewName);
01034        return NS_OK;
01035 } // nsFileSpec::Rename
01036 
01037 //----------------------------------------------------------------------------------------
01038 nsresult nsFileSpec::CopyToDir(const nsFileSpec& newParentDir) const
01039 //----------------------------------------------------------------------------------------
01040 {
01041        // We can only copy into a directory, and (for now) can not copy entire directories
01042 
01043        if (!newParentDir.IsDirectory() || (IsDirectory() ) )
01044               return NS_FILE_FAILURE;
01045        
01046        nsresult rv = NS_FILE_RESULT(::FSpFileCopy(&mSpec,
01047                                                  &newParentDir.mSpec,
01048                                                  const_cast<StringPtr>(GetLeafPName()),
01049                                                  nsnull,
01050                                                  0,
01051                                                  true));
01052 
01053        return rv;
01054 
01055 } // nsFileSpec::CopyToDir
01056 
01057 //----------------------------------------------------------------------------------------
01058 nsresult nsFileSpec::MoveToDir(const nsFileSpec& newParentDir) 
01059 //----------------------------------------------------------------------------------------
01060 {
01061        // We can only move into a directory
01062        
01063        if (!newParentDir.IsDirectory())
01064               return NS_FILE_FAILURE;
01065  
01066        nsresult result = NS_FILE_RESULT(::FSpMoveRenameCompat(&mSpec,
01067                                                                &newParentDir.mSpec,
01068                                                                const_cast<StringPtr>(GetLeafPName())));
01069 
01070        if ( NS_SUCCEEDED(result) )
01071        {
01072               char* leafName = GetLeafName();
01073               *this = newParentDir + leafName;
01074               nsCRT::free(leafName);
01075        }
01076        return result;
01077 } // nsFileSpec::MoveToDir
01078 
01079 //----------------------------------------------------------------------------------------
01080 nsresult nsFileSpec::Execute(const char* /*args - how can this be cross-platform?  problem! */ ) const
01081 //----------------------------------------------------------------------------------------
01082 {
01083        if (IsDirectory())
01084               return NS_FILE_FAILURE;
01085 
01086        LaunchParamBlockRec launchThis;
01087        launchThis.launchAppSpec = const_cast<FSSpec*>(&mSpec);
01088        launchThis.launchAppParameters = nsnull; // args;
01089        /* launch the thing */
01090        launchThis.launchBlockID    = extendedBlock;
01091        launchThis.launchEPBLength  = extendedBlockLen;
01092        launchThis.launchFileFlags  = nsnull;
01093        launchThis.launchControlFlags = launchContinue + launchNoFileFlags + launchUseMinimum;
01094        launchThis.launchControlFlags += launchDontSwitch;
01095 
01096        nsresult result = NS_FILE_RESULT(::LaunchApplication(&launchThis));
01097        return result;
01098   
01099 } // nsFileSpec::Execute
01100 
01101 //----------------------------------------------------------------------------------------
01102 OSErr nsFileSpec::GetCatInfo(CInfoPBRec& outInfo) const
01103 //----------------------------------------------------------------------------------------
01104 {
01105        DirInfo          *dipb=(DirInfo *)&outInfo;
01106        dipb->ioCompletion = nsnull;
01107        dipb->ioFDirIndex = 0; // use dirID and name
01108        dipb->ioVRefNum = mSpec.vRefNum;
01109        dipb->ioDrDirID = mSpec.parID;
01110        dipb->ioNamePtr = const_cast<nsFileSpec*>(this)->mSpec.name;
01111        return PBGetCatInfoSync(&outInfo);
01112 } // nsFileSpec::GetCatInfo()
01113 
01114 //----------------------------------------------------------------------------------------
01115 OSErr nsFileSpec::SetFileTypeAndCreator(OSType type, OSType creator)
01116 //----------------------------------------------------------------------------------------
01117 {
01118        FInfo info;
01119        OSErr err = ::FSpGetFInfo(&mSpec, &info);
01120        if (err != noErr)
01121               return err;
01122        info.fdType = type;
01123        info.fdCreator = creator;
01124        err = ::FSpSetFInfo(&mSpec, &info);
01125        return err;
01126 }
01127 
01128 //----------------------------------------------------------------------------------------
01129 OSErr nsFileSpec::GetFileTypeAndCreator(OSType* type, OSType* creator)
01130 //----------------------------------------------------------------------------------------
01131 {
01132        FInfo info;
01133        OSErr err = ::FSpGetFInfo(&mSpec, &info);
01134        if (err != noErr)
01135               return err;
01136        *type = info.fdType;
01137        *creator = info.fdCreator;  
01138        return noErr;
01139 }
01140 
01141 //----------------------------------------------------------------------------------------
01142 PRInt64 nsFileSpec::GetDiskSpaceAvailable() const
01143 //----------------------------------------------------------------------------------------
01144 {
01145        PRInt64 space64Bits;
01146 
01147        LL_I2L(space64Bits , LONG_MAX);
01148 
01149        XVolumeParam  pb;
01150        pb.ioCompletion = nsnull;
01151        pb.ioVolIndex = 0;
01152        pb.ioNamePtr = nsnull;
01153        pb.ioVRefNum = mSpec.vRefNum;
01154        
01155        // PBXGetVolInfoSync works on HFS+ volumes too!
01156        OSErr err = ::PBXGetVolInfoSync(&pb);
01157        
01158        if (err == noErr)
01159        {
01160 #ifdef HAVE_LONG_LONG
01161               space64Bits = pb.ioVFreeBytes;
01162 #else
01163               const UnsignedWide& freeBytes = UInt64ToUnsignedWide(pb.ioVFreeBytes);
01164               space64Bits.lo = freeBytes.lo;
01165               space64Bits.hi = freeBytes.hi;
01166 #endif
01167        }
01168               
01169        return space64Bits;
01170 } // nsFileSpec::GetDiskSpace()
01171 
01172 //----------------------------------------------------------------------------------------
01173 const char* nsFileSpec::GetCString() const
01174 // This is the only conversion to const char* that is provided, and it allows the
01175 // path to be "passed" to NSPR file routines.  This practice is VERY EVIL and should only
01176 // be used to support legacy code. Using it guarantees bugs on Macintosh. The string is
01177 // cached and freed by the nsFileSpec destructor, so do not delete (or free) it.
01178 //----------------------------------------------------------------------------------------
01179 {
01180        if (mPath.IsEmpty())
01181        {
01182               char* path = MacFileHelpers::PathNameFromFSSpec(mSpec);
01183               if (path != NULL) {
01184                      const_cast<nsFileSpec*>(this)->mPath = path;     // operator =() copies the string!!!
01185                      delete[] path;
01186               } else {
01187                      const_cast<nsFileSpec*>(this)->mError = NS_ERROR_OUT_OF_MEMORY;
01188               }
01189        }
01190        return mPath;
01191 }
01192 
01193 #pragma mark -
01194 
01195 //========================================================================================
01196 //                                   Macintosh nsFilePath implementation
01197 //========================================================================================
01198 
01199 //----------------------------------------------------------------------------------------
01200 static void AssignFromPath(nsFilePath& ioPath, const char* inString, PRBool inCreateDirs)
01201 //----------------------------------------------------------------------------------------
01202 {
01203        NS_ASSERTION(inString, "AssignFromPath called with null inString");
01204        NS_ASSERTION(strstr(inString, kFileURLPrefix) != inString, "URL passed as path");
01205 
01206        FSSpec spec;
01207        spec.vRefNum = 0;
01208        spec.parID = 0;
01209        spec.name[0] = 0;
01210        MacFileHelpers::FSSpecFromUnixPath(
01211               inString,
01212               spec,
01213               false,
01214               true, // resolve alias
01215               true,
01216               inCreateDirs);
01217        // Now we have a spec,
01218        // Invoke operator = (const nsFileSpec&) to do the rest.
01219        // Why didn't we just say mPath = inString to get the path? Well, we want it to be
01220        // canonical and absolute.
01221        ioPath = spec;
01222 }
01223 
01224 //----------------------------------------------------------------------------------------
01225 nsFilePath::nsFilePath(const char* inString, PRBool inCreateDirs)
01226 //----------------------------------------------------------------------------------------
01227 {
01228        AssignFromPath(*this, inString, inCreateDirs);
01229 } //nsFilePath::nsFilePath
01230 
01231 //----------------------------------------------------------------------------------------
01232 nsFilePath::nsFilePath(const nsString& inString, PRBool inCreateDirs)
01233 //----------------------------------------------------------------------------------------
01234 {
01235        AssignFromPath(*this, NS_LossyConvertUCS2toASCII(inString).get(),
01236                       inCreateDirs);
01237 }
01238 
01239 //----------------------------------------------------------------------------------------
01240 void nsFilePath::operator = (const char* inString)
01241 //----------------------------------------------------------------------------------------
01242 {
01243        AssignFromPath(*this, inString, PR_FALSE);
01244 }
01245 
01246 //----------------------------------------------------------------------------------------
01247 nsFilePath::nsFilePath(const nsFileSpec& inSpec)
01248 //----------------------------------------------------------------------------------------
01249 {
01250        *this = inSpec;
01251 }
01252 
01253 //----------------------------------------------------------------------------------------
01254 nsFilePath::nsFilePath(const nsFileURL& inOther)
01255 //----------------------------------------------------------------------------------------
01256 {
01257        *this = inOther;
01258 }
01259 
01260 //----------------------------------------------------------------------------------------
01261 void nsFilePath::operator = (const nsFileSpec& inSpec)
01262 //----------------------------------------------------------------------------------------
01263 {
01264        char * path = MacFileHelpers::PathNameFromFSSpec(inSpec);
01265        path = MacFileHelpers::EncodeMacPath(path, true, false);
01266        mPath = path;
01267        nsCRT::free(path);
01268        mFileSpec = inSpec;
01269 } // nsFilePath::operator =
01270 
01271 //----------------------------------------------------------------------------------------
01272 void nsFilePath::operator = (const nsFileURL& inOther)
01273 //----------------------------------------------------------------------------------------
01274 {
01275        char * path = MacFileHelpers::PathNameFromFSSpec(inOther.mFileSpec);
01276        path = MacFileHelpers::EncodeMacPath(path, true, false);
01277        mPath = path;
01278        nsCRT::free(path);
01279        mFileSpec = inOther.GetFileSpec();
01280 }
01281 
01282 #pragma mark -
01283 
01284 //========================================================================================
01285 //                                                        nsFileURL implementation
01286 //========================================================================================
01287 
01288 //----------------------------------------------------------------------------------------
01289 nsFileURL::nsFileURL(const char* inString, PRBool inCreateDirs)
01290 //----------------------------------------------------------------------------------------
01291 :       mURL(inString)
01292 {       
01293        NS_ASSERTION(inString, "nsFileURL constructed with null inString");
01294        NS_ASSERTION(strstr(inString, kFileURLPrefix) == inString, "Not a URL!");
01295        mFileSpec.mError = NS_FILE_RESULT(MacFileHelpers::FSSpecFromUnixPath(
01296               inString + kFileURLPrefixLength,
01297               mFileSpec.mSpec,
01298               true, // need to decode
01299               false, // resolve alias
01300               false, // must be a full path
01301               inCreateDirs));
01302        if (mFileSpec.mError == NS_FILE_RESULT(fnfErr))
01303               mFileSpec.mError = NS_OK;
01304 } // nsFileURL::nsFileURL
01305 
01306 //----------------------------------------------------------------------------------------
01307 nsFileURL::nsFileURL(const nsString& inString, PRBool inCreateDirs)
01308 //----------------------------------------------------------------------------------------
01309 :       mURL(nsnull)
01310 {
01311        NS_LossyConvertUCS2toASCII cstring(inString);
01312        mURL = cstring.get();
01313        NS_ASSERTION(strstr(cstring.get(), kFileURLPrefix) == cstring.get(),
01314                     "Not a URL!");
01315        mFileSpec.mError = NS_FILE_RESULT(MacFileHelpers::FSSpecFromUnixPath(
01316               cstring.get() + kFileURLPrefixLength,
01317               mFileSpec.mSpec,
01318               true, // need to decode
01319               false, // resolve alias
01320               false, // must be a full path
01321               inCreateDirs));
01322        if (mFileSpec.mError == NS_FILE_RESULT(fnfErr))
01323               mFileSpec.mError = NS_OK;
01324 } // nsFileURL::nsFileURL
01325 
01326 //----------------------------------------------------------------------------------------
01327 nsFileURL::nsFileURL(const nsFilePath& inOther)
01328 //----------------------------------------------------------------------------------------
01329 {
01330        *this = inOther.GetFileSpec();
01331 } // nsFileURL::nsFileURL
01332 
01333 //----------------------------------------------------------------------------------------
01334 nsFileURL::nsFileURL(const nsFileSpec& inOther)
01335 //----------------------------------------------------------------------------------------
01336 {
01337        *this = inOther;
01338 } // nsFileURL::nsFileURL
01339 
01340 //----------------------------------------------------------------------------------------
01341 void nsFileURL::operator = (const nsFilePath& inOther)
01342 //----------------------------------------------------------------------------------------
01343 {
01344        *this = inOther.GetFileSpec();
01345 } // nsFileURL::operator =
01346 
01347 //----------------------------------------------------------------------------------------
01348 void nsFileURL::operator = (const nsFileSpec& inOther)
01349 //----------------------------------------------------------------------------------------
01350 {
01351        mFileSpec  = inOther;
01352        char* path = MacFileHelpers::PathNameFromFSSpec( mFileSpec );
01353        char* encodedPath = MacFileHelpers::EncodeMacPath(path, true, true);
01354        nsSimpleCharString encodedURL(kFileURLPrefix);
01355        encodedURL += encodedPath;
01356        nsCRT::free(encodedPath);
01357        mURL = encodedURL;
01358        if (encodedURL[encodedURL.Length() - 1] != '/' && inOther.IsDirectory())
01359               mURL += "/";
01360 } // nsFileURL::operator =
01361 
01362 //----------------------------------------------------------------------------------------
01363 void nsFileURL::operator = (const char* inString)
01364 //----------------------------------------------------------------------------------------
01365 {
01366        NS_ASSERTION(inString, "nsFileURL operator= constructed with null inString");
01367 
01368        mURL = inString;
01369        NS_ASSERTION(strstr(inString, kFileURLPrefix) == inString, "Not a URL!");
01370        mFileSpec.mError = NS_FILE_RESULT(MacFileHelpers::FSSpecFromUnixPath(
01371               inString + kFileURLPrefixLength,
01372               mFileSpec.mSpec,
01373               true, // need to decode
01374               true, // resolve alias
01375               false, // must be a full path
01376               false)); // don't create dirs.
01377        if (mFileSpec.mError == NS_FILE_RESULT(fnfErr))
01378               mFileSpec.mError = NS_OK;
01379 } // nsFileURL::operator =
01380 
01381 #pragma mark -
01382 
01383 //========================================================================================
01384 //                                                        nsDirectoryIterator
01385 //========================================================================================
01386 
01387 //----------------------------------------------------------------------------------------
01388 nsDirectoryIterator::nsDirectoryIterator(
01389        const nsFileSpec& inDirectory
01390 ,      PRBool resolveSymLinks)
01391 //----------------------------------------------------------------------------------------
01392        : mCurrent(inDirectory)
01393        , mExists(false)
01394        , mResoveSymLinks(resolveSymLinks)
01395        , mIndex(-1)
01396 {
01397        CInfoPBRec pb;
01398        OSErr err = inDirectory.GetCatInfo(pb);
01399        
01400        // test that we have got a directory back, not a file
01401        DirInfo* dipb = (DirInfo*)&pb;
01402        if (err != noErr  || !( dipb->ioFlAttrib & 0x0010))
01403               return;
01404        // Sorry about this, there seems to be a bug in CWPro 4:
01405        FSSpec& currentSpec = mCurrent.nsFileSpec::operator FSSpec&();
01406        mVRefNum = currentSpec.vRefNum;
01407        mParID = dipb->ioDrDirID;
01408        mMaxIndex = pb.dirInfo.ioDrNmFls;
01409        mIndex = 0; // ready to increment
01410        ++(*this); // the pre-increment operator
01411        
01412 } // nsDirectoryIterator::nsDirectoryIterator
01413 
01414 //----------------------------------------------------------------------------------------
01415 OSErr nsDirectoryIterator::SetToIndex()
01416 //----------------------------------------------------------------------------------------
01417 {
01418        CInfoPBRec cipb;
01419        DirInfo          *dipb=(DirInfo *)&cipb;
01420        Str255 objectName;
01421        dipb->ioCompletion = nsnull;
01422        dipb->ioFDirIndex = mIndex;
01423        // Sorry about this, there seems to be a bug in CWPro 4:
01424        FSSpec& currentSpec = mCurrent.nsFileSpec::operator FSSpec&();
01425        dipb->ioVRefNum = mVRefNum; /* Might need to use vRefNum, not sure*/
01426        dipb->ioDrDirID = mParID;
01427        dipb->ioNamePtr = objectName;
01428        OSErr err = PBGetCatInfoSync(&cipb);
01429        FSSpec temp;
01430        if (err == noErr)
01431               err = FSMakeFSSpec(mVRefNum, mParID, objectName, &temp);
01432        mCurrent = temp; // use the operator: it clears the string cache.
01433        mExists = err == noErr;
01434 
01435        if (mExists && mResoveSymLinks)
01436        {      
01437               PRBool ignore;
01438               mCurrent.ResolveSymlink(ignore);
01439        }
01440        return err;
01441 } // nsDirectoryIterator::SetToIndex()
01442 
01443 //----------------------------------------------------------------------------------------
01444 nsDirectoryIterator& nsDirectoryIterator::operator -- ()
01445 //----------------------------------------------------------------------------------------
01446 {
01447        mExists = false;
01448        while (--mIndex > 0)
01449        {
01450               OSErr err = SetToIndex();
01451               if (err == noErr)
01452                      break;
01453        }
01454        return *this;
01455 } // nsDirectoryIterator::operator --
01456 
01457 //----------------------------------------------------------------------------------------
01458 nsDirectoryIterator& nsDirectoryIterator::operator ++ ()
01459 //----------------------------------------------------------------------------------------
01460 {
01461        mExists = false;
01462        if (mIndex >= 0) // probably trying to use a file as a directory!
01463               while (++mIndex <= mMaxIndex)
01464               {
01465                      OSErr err = SetToIndex();
01466                      if (err == noErr)
01467                             break;
01468               }
01469        return *this;
01470 } // nsDirectoryIterator::operator ++
01471