/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ // This file is included by nsFile.cp, and includes the Macintosh-specific // implementations. #include #include "prtypes.h" #include "nscore.h" #include "FullPath.h" #include "FileCopy.h" #include "MoreFilesExtras.h" #include #include #include #include #include #include // ULONG_MAX #include "nsFileSpec.h" #include "nsEscape.h" #include "nsXPIDLString.h" const unsigned char* kAliasHavenFolderName = "\pnsAliasHaven"; //======================================================================================== namespace MacFileHelpers //======================================================================================== { inline void PLstrcpy(Str255 dst, ConstStr255Param src) { memcpy(dst, src, 1 + src[0]); } void PLstrcpy(Str255 dst, const char* src, int inMaxLen=255); void PLstrncpy(Str255 dst, const char* src, int inMaxLen); void SwapSlashColon(char * s); OSErr FSSpecFromUnixPath( const char * unixPath, FSSpec& ioSpec, Boolean hexDecode, Boolean resolveAlias, Boolean allowPartial = false, Boolean createDirs = false); char* MacPathFromUnixPath( const char* unixPath, Boolean hexDecode); char* EncodeMacPath( char* inPath, // NOT const - gets clobbered Boolean prependSlash, Boolean doEscape ); OSErr FSSpecFromPathname( const char* inPathNamePtr, FSSpec& ioSpec, Boolean inCreateDirs); char* PathNameFromFSSpec( const FSSpec& inSpec ); OSErr CreateFolderInFolder( short refNum, // Parent directory/volume long dirID, ConstStr255Param folderName, // Name of the new folder short& outRefNum, // Volume of the created folder long& outDirID); // // Some routines to support an "alias haven" directory. Aliases in this directory // are never resolved. There is a ResolveAlias here that respects that. This is // to support attaching of aliases in mail. void EnsureAliasHaven(); void SetNoResolve(Boolean inResolve); PRBool IsAliasSafe(const FSSpec& inSpec); OSErr MakeAliasSafe(FSSpec& inOutSpec); OSErr ResolveAliasFile(FSSpec& inOutSpec, Boolean& wasAliased); Boolean sNoResolve = false; long sAliasHavenDirID = 0; short sAliasHavenVRefNum = 0; } // namespace MacFileHelpers //---------------------------------------------------------------------------------------- void MacFileHelpers::PLstrcpy(Str255 dst, const char* src, int inMax) //---------------------------------------------------------------------------------------- { int srcLength = strlen(src); NS_ASSERTION(srcLength <= inMax, "Oops, string is too long!"); if (srcLength > inMax) srcLength = inMax; dst[0] = srcLength; memcpy(&dst[1], src, srcLength); } //---------------------------------------------------------------------------------------- void MacFileHelpers::PLstrncpy(Str255 dst, const char* src, int inMax) //---------------------------------------------------------------------------------------- { int srcLength = strlen(src); if (srcLength > inMax) srcLength = inMax; dst[0] = srcLength; memcpy(&dst[1], src, srcLength); } //----------------------------------- void MacFileHelpers::SwapSlashColon(char * s) //----------------------------------- { while (*s) { if (*s == '/') *s++ = ':'; else if (*s == ':') *s++ = '/'; else *s++; } } // MacFileHelpers::SwapSlashColon //----------------------------------- char* MacFileHelpers::EncodeMacPath( char* inPath, // NOT const, gets clobbered Boolean prependSlash, Boolean doEscape ) // Transforms Macintosh style path into Unix one // Method: Swap ':' and '/', hex escape the result //----------------------------------- { if (inPath == nsnull) return nsnull; int pathSize = strlen(inPath); // XP code sometimes chokes if there's a final slash in the unix path. // Since correct mac paths to folders and volumes will end in ':', strip this // first. char* c = inPath + pathSize - 1; if (*c == ':') { *c = 0; pathSize--; } char * newPath = nsnull; char * finalPath = nsnull; if (prependSlash) { newPath = new char[pathSize + 2]; newPath[0] = ':'; // It will be converted to '/' memcpy(&newPath[1], inPath, pathSize + 1); } else { newPath = new char[pathSize + 1]; strcpy(newPath, inPath); } if (newPath) { SwapSlashColon( newPath ); if (doEscape) { finalPath = nsEscape(newPath, url_Path); delete [] newPath; } else finalPath = newPath; } delete [] inPath; return finalPath; } // MacFileHelpers::EncodeMacPath //---------------------------------------------------------------------------------------- inline void MacFileHelpers::SetNoResolve(Boolean inResolve) //---------------------------------------------------------------------------------------- { sNoResolve = inResolve; } // MacFileHelpers::SetNoResolve //---------------------------------------------------------------------------------------- OSErr MacFileHelpers::MakeAliasSafe(FSSpec& inOutSpec) // Pass in the spec of an alias. This copies the file to the safe haven folder, and // returns the spec of the copy to the caller //---------------------------------------------------------------------------------------- { EnsureAliasHaven(); nsFileSpec dstDirSpec(sAliasHavenVRefNum, sAliasHavenDirID, "\p"); // Make sure its name is unique nsFileSpec havenSpec(sAliasHavenVRefNum, sAliasHavenDirID, "\pG'day"); if (havenSpec.Valid()) havenSpec.MakeUnique(inOutSpec.name); // Copy the file into the haven directory if (havenSpec.Valid()) { OSErr err = ::FSpFileCopy( &inOutSpec, dstDirSpec, havenSpec.GetLeafPName(), nil, 0, true); // Return the spec of the copy to the caller. if (err != noErr) return err; inOutSpec = havenSpec; } return noErr; } // MacFileHelpers::MakeAliasSafe //---------------------------------------------------------------------------------------- char* MacFileHelpers::MacPathFromUnixPath(const char* unixPath, Boolean hexDecode) //---------------------------------------------------------------------------------------- { // Relying on the fact that the unix path is always longer than the mac path: size_t len = strlen(unixPath); char* result = new char[len + 2]; // ... but allow for the initial colon in a partial name // REMEMBER: at the end we call SwapSlashColon, so bear that in mind when you see // this code using '/' as a separator in what is supposed to be a Macintosh path! if (result) { char* dst = result; const char* src = unixPath; if (*src == '/') // * full path src++; else if (strchr(src, '/') && *src != '.') { // * partial path, and not just a leaf name. The '.' test is there because // the loop below will add sufficient colons in that case. *dst++ = '/'; } // Copy src to dst, but watch out for .. and . char c = '/'; do { char cprev = c; // remember the previous char (initially /) c = *src++; if (c == '.' && cprev == '/') { char* dstSaved = dst; // Special cases: "." and "..". Convert to ':' and '::' *dst++ = '/'; // . becomes : c = *src++; if (c == '.') { *dst++ = '/'; // .. becomes :: c = *src++; } if (c == '/') { // ../ becomes :: so just skip the slash // ./ becomes : " " " " " src++; } else if (c) { // Oh. A file called ".foo" or "..foo" // Back up and just do the normal thing. src -= (dst - dstSaved); dst = dstSaved; // Since c is not '/', we won't do this stuff on the // next iteration. } continue; } else if (c == '/' && cprev == '/') { // Hmm. A 'run on' path with two slashes right next to each other. // This is an illegal path, but, hey, we'll be tough and try to // deal with it (especially since '::' has loaded semantics in // a Mac path) continue; } *dst++ = c; } while (c); if (hexDecode) nsUnescape(result); // Hex Decode MacFileHelpers::SwapSlashColon(result); } return result; } // MacFileHelpers::MacPathFromUnixPath //---------------------------------------------------------------------------------------- OSErr MacFileHelpers::FSSpecFromPathname( const char* inPathNamePtr, FSSpec& ioSpec, // used as in-parameter for a relative path. Boolean inCreateDirs) // FSSpecFromPathname reverses PathNameFromFSSpec. // It returns a FSSpec given a c string which is a mac pathname. //---------------------------------------------------------------------------------------- { OSErr err; // Simplify this routine to use FSMakeFSSpec if length < 255. Otherwise use the MoreFiles // routine FSpLocationFromFullPath, which allocates memory, to handle longer pathnames. short inVRefNum = ioSpec.vRefNum; long inParID = ioSpec.parID; size_t inLength = strlen(inPathNamePtr); bool isRelative = (strchr(inPathNamePtr, ':') == 0 || *inPathNamePtr == ':'); #ifdef NS_DEBUG // Attempt to catch people sending unix paths in to routines that expect native ones. NS_ASSERTION(strchr(inPathNamePtr, '/') == 0, "Possible unix path where native path is required"); #endif if (inLength < 255) { Str255 pascalpath; MacFileHelpers::PLstrcpy(pascalpath, inPathNamePtr); if (isRelative) err = ::FSMakeFSSpec(inVRefNum, inParID, pascalpath, &ioSpec); else err = ::FSMakeFSSpec(0, 0, pascalpath, &ioSpec); } else if (!isRelative) err = FSpLocationFromFullPath(inLength, inPathNamePtr, &ioSpec); else err = bdNamErr; if ((err == dirNFErr || err == bdNamErr) && inCreateDirs) { const char* path = inPathNamePtr; if (isRelative) { ioSpec.vRefNum = inVRefNum; ioSpec.parID = inParID; } else { ioSpec.vRefNum = 0; ioSpec.parID = 0; } do { // Locate the colon that terminates the node. // But if we've a partial path (starting with a colon), find the second one. const char* nextColon = strchr(path + (*path == ':'), ':'); // Well, if there are no more colons, point to the end of the string. if (!nextColon) nextColon = path + strlen(path); // Make a pascal string out of this node. Include initial // and final colon, if any! Str255 ppath; MacFileHelpers::PLstrncpy(ppath, path, nextColon - path + 1); // Use this string as a relative path using the directory created // on the previous round (or directory 0,0 on the first round). err = ::FSMakeFSSpec(ioSpec.vRefNum, ioSpec.parID, ppath, &ioSpec); // If this was the leaf node, then we are done. if (!*nextColon) break; // Since there's more to go, we have to get the directory ID, which becomes // the parID for the next round. if (err == noErr) { // The directory (or perhaps a file) exists. Find its dirID. long dirID; Boolean isDirectory; err = ::FSpGetDirectoryID(&ioSpec, &dirID, &isDirectory); if (!isDirectory) return dupFNErr; // oops! a file exists with that name. if (err) return err; ioSpec.parID = dirID; } else if (err == fnfErr) { // If we got "file not found", then // we need to create a directory. err = ::FSpDirCreate(&ioSpec, smCurrentScript, &ioSpec.parID); // For some reason, this usually returns fnfErr, even though it works. if (err == fnfErr) err = noErr; } if (err != noErr) return err; path = nextColon; // next round } while (1); } return err; } // MacFileHelpers::FSSpecFromPathname //---------------------------------------------------------------------------------------- OSErr MacFileHelpers::CreateFolderInFolder( short refNum, // Parent directory/volume long dirID, ConstStr255Param folderName, // Name of the new folder short& outRefNum, // Volume of the created folder long& outDirID) // // Creates a folder named 'folderName' inside a folder. // The errors returned are same as PBDirCreate //---------------------------------------------------------------------------------------- { HFileParam hpb; hpb.ioVRefNum = refNum; hpb.ioDirID = dirID; hpb.ioNamePtr = (StringPtr)&folderName; OSErr err = PBDirCreateSync((HParmBlkPtr)&hpb); if (err == noErr) { outRefNum = hpb.ioVRefNum; outDirID = hpb.ioDirID; } else { outRefNum = 0; outDirID = 0; } return err; } // MacFileHelpers::CreateFolderInFolder //---------------------------------------------------------------------------------------- void MacFileHelpers::EnsureAliasHaven() //---------------------------------------------------------------------------------------- { // Alias Haven is a directory in which we never resolve aliases. if (sAliasHavenVRefNum != 0) return; FSSpec temp; if (FindFolder(0, kTemporaryFolderType, true, & temp.vRefNum, &temp.parID) == noErr) { CreateFolderInFolder( temp.vRefNum, // Parent directory/volume temp.parID, kAliasHavenFolderName, // Name of the new folder sAliasHavenVRefNum, // Volume of the created folder sAliasHavenDirID); } } // MacFileHelpers::EnsureAliasHaven //---------------------------------------------------------------------------------------- PRBool MacFileHelpers::IsAliasSafe(const FSSpec& inSpec) // Returns true if the alias is in the alias haven directory, or if alias resolution // has been turned off. //---------------------------------------------------------------------------------------- { return sNoResolve || (inSpec.parID == sAliasHavenDirID && inSpec.vRefNum == sAliasHavenVRefNum); } // MacFileHelpers::IsAliasSafe //---------------------------------------------------------------------------------------- OSErr MacFileHelpers::ResolveAliasFile(FSSpec& inOutSpec, Boolean& wasAliased) //---------------------------------------------------------------------------------------- { wasAliased = false; if (IsAliasSafe(inOutSpec)) return noErr; Boolean dummy; return ::ResolveAliasFile(&inOutSpec, TRUE, &dummy, &wasAliased); } // MacFileHelpers::ResolveAliasFile //----------------------------------- OSErr MacFileHelpers::FSSpecFromUnixPath( const char * unixPath, FSSpec& ioSpec, Boolean hexDecode, Boolean resolveAlias, Boolean allowPartial, Boolean createDirs) // File spec from URL. Reverses GetURLFromFileSpec // Its input is only the part of the URL // JRM 97/01/08 changed this so that if it's a partial path (doesn't start with '/'), // then it is combined with inOutSpec's vRefNum and parID to form a new spec. //----------------------------------- { if (unixPath == nsnull) return badFidErr; char* macPath = MacPathFromUnixPath(unixPath, hexDecode); if (!macPath) return memFullErr; OSErr err = noErr; if (!allowPartial) { NS_ASSERTION(*unixPath == '/' /*full path*/, "Not a full Unix path!"); } err = FSSpecFromPathname(macPath, ioSpec, createDirs); if (err == fnfErr) err = noErr; Boolean dummy; if (err == noErr && resolveAlias) // Added err = MacFileHelpers::ResolveAliasFile(ioSpec, dummy); delete [] macPath; NS_ASSERTION(err==noErr||err==fnfErr||err==dirNFErr||err==nsvErr, "Not a path!"); return err; } // MacFileHelpers::FSSpecFromLocalUnixPath //----------------------------------- char* MacFileHelpers::PathNameFromFSSpec( const FSSpec& inSpec ) // Returns a full pathname to the given file // Returned value is allocated with new [], and must be freed with delete [] // For consistency and to work under OS X this creates an nsILocalFileMac and has it do the work. //----------------------------------- { char* result = nil; nsresult rv; FSSpec nonConstSpec = inSpec; nsCAutoString path; nsCOMPtr macFile; rv = NS_NewLocalFileWithFSSpec(&nonConstSpec, PR_TRUE, getter_AddRefs(macFile)); if (NS_FAILED(rv)) return nsnull; nsCOMPtr localFile(do_QueryInterface(macFile, &rv)); if (NS_FAILED(rv)) return nsnull; rv = localFile->GetNativePath(path); if (NS_FAILED(rv)) return nsnull; PRInt32 strLen = path.Length(); result = new char [strLen + 1]; if (!result) return nsnull; memcpy(result, path.get(), strLen); result[ strLen ] = 0; return result; } // MacFileHelpers::PathNameFromFSSpec #pragma mark - //======================================================================================== // Macintosh nsFileSpec implementation //======================================================================================== //---------------------------------------------------------------------------------------- nsFileSpec::nsFileSpec() //---------------------------------------------------------------------------------------- { // NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!"); Clear(); } //---------------------------------------------------------------------------------------- nsFileSpec::nsFileSpec(const FSSpec& inSpec, PRBool resolveAlias) //---------------------------------------------------------------------------------------- : mSpec(inSpec) , mError(NS_OK) { // NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!"); if (resolveAlias) { PRBool dummy; ResolveSymlink(dummy); } } //---------------------------------------------------------------------------------------- void nsFileSpec::operator = (const FSSpec& inSpec) //---------------------------------------------------------------------------------------- { mSpec = inSpec; mError = NS_OK; } //---------------------------------------------------------------------------------------- nsFileSpec::nsFileSpec(const nsFileSpec& inSpec) //---------------------------------------------------------------------------------------- : mSpec(inSpec.mSpec) , mError(inSpec.Error()) { // NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!"); } //---------------------------------------------------------------------------------------- nsFileSpec::nsFileSpec(const char* inNativePathString, PRBool inCreateDirs) //---------------------------------------------------------------------------------------- { // NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!"); Clear(); // this sets mError to NS_ERROR_NOT_INITIALIZED if (inNativePathString) { mError = NS_FILE_RESULT(MacFileHelpers::FSSpecFromPathname( inNativePathString, mSpec, inCreateDirs)); if (mError == NS_FILE_RESULT(fnfErr)) mError = NS_OK; } } // nsFileSpec::nsFileSpec //---------------------------------------------------------------------------------------- nsFileSpec::nsFileSpec(const nsString& inNativePathString, PRBool inCreateDirs) //---------------------------------------------------------------------------------------- { // NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!"); Clear(); // this sets mError to NS_ERROR_NOT_INITIALIZED mError = NS_FILE_RESULT( MacFileHelpers::FSSpecFromPathname( NS_LossyConvertUCS2toASCII(inNativePathString).get(), mSpec, inCreateDirs)); if (mError == NS_FILE_RESULT(fnfErr)) mError = NS_OK; } // nsFileSpec::nsFileSpec //---------------------------------------------------------------------------------------- nsFileSpec::nsFileSpec(short vRefNum, long parID, ConstStr255Param fileName, PRBool resolveAlias) //---------------------------------------------------------------------------------------- { // NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!"); mError = NS_FILE_RESULT(::FSMakeFSSpec(vRefNum, parID, fileName, &mSpec)); if (mError == NS_FILE_RESULT(fnfErr)) mError = NS_OK; if (resolveAlias) { PRBool dummy; ResolveSymlink(dummy); } } //---------------------------------------------------------------------------------------- nsFileSpec::nsFileSpec(const nsFilePath& inPath) //---------------------------------------------------------------------------------------- { // NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!"); *this = inPath.GetFileSpec(); } //---------------------------------------------------------------------------------------- void nsFileSpec::operator = (const char* inString) //---------------------------------------------------------------------------------------- { Clear(); // this sets mError to NS_ERROR_NOT_INITIALIZED if (inString) { mError = NS_FILE_RESULT(MacFileHelpers::FSSpecFromPathname(inString, mSpec, true)); if (mError == NS_FILE_RESULT(fnfErr)) mError = NS_OK; } } // nsFileSpec::operator = //---------------------------------------------------------------------------------------- void nsFileSpec::operator = (const nsFileSpec& inSpec) //---------------------------------------------------------------------------------------- { mPath.SetToEmpty(); mSpec.vRefNum = inSpec.mSpec.vRefNum; mSpec.parID = inSpec.mSpec.parID; PRInt32 copySize = inSpec.mSpec.name[0] + 1; if (copySize > sizeof(inSpec.mSpec.name)) copySize = sizeof(inSpec.mSpec.name); memcpy(mSpec.name, inSpec.mSpec.name, copySize); mError = inSpec.Error(); // note that the error is propagated } // nsFileSpec::operator = //---------------------------------------------------------------------------------------- void nsFileSpec::operator = (const nsFilePath& inPath) //---------------------------------------------------------------------------------------- { *this = inPath.GetFileSpec(); } // nsFileSpec::operator = //---------------------------------------------------------------------------------------- inline void nsFileSpec::Clear() //---------------------------------------------------------------------------------------- { mPath.SetToEmpty(); mSpec.vRefNum = 0; mSpec.parID = 0; mSpec.name[0] = 0; mError = NS_ERROR_NOT_INITIALIZED; } //---------------------------------------------------------------------------------------- PRBool nsFileSpec::Exists() const //---------------------------------------------------------------------------------------- { if (NS_FAILED(mError)) return PR_FALSE; FSSpec temp; return ::FSMakeFSSpec(mSpec.vRefNum, mSpec.parID, mSpec.name, &temp) == noErr; } // nsFileSpec::Exists() //---------------------------------------------------------------------------------------- void nsFileSpec::GetModDate(TimeStamp& outStamp) const //---------------------------------------------------------------------------------------- { CInfoPBRec pb; if (GetCatInfo(pb) == noErr) outStamp = ((DirInfo*)&pb)->ioDrMdDat; // The mod date is in the same spot for files and dirs. else outStamp = 0; } // nsFileSpec::GetModDate //---------------------------------------------------------------------------------------- PRUint32 nsFileSpec::GetFileSize() const //---------------------------------------------------------------------------------------- { CInfoPBRec pb; if (noErr == GetCatInfo(pb)) return (PRUint32)((HFileInfo*)&pb)->ioFlLgLen; return 0; } // nsFileSpec::GetFileSize //---------------------------------------------------------------------------------------- void nsFileSpec::SetLeafName(const char* inLeafName) // In leaf name can actually be a partial path... //---------------------------------------------------------------------------------------- { NS_ASSERTION(inLeafName, "Attempt to set leaf name with a null string"); mPath.SetToEmpty(); if (inLeafName) { // what about long relative paths? Hmm? We don't have a routine for this anywhere. Str255 partialPath; MacFileHelpers::PLstrcpy(partialPath, inLeafName); mError = NS_FILE_RESULT( ::FSMakeFSSpec(mSpec.vRefNum, mSpec.parID, partialPath, &mSpec)); if (mError == NS_FILE_RESULT(fnfErr)) mError = NS_OK; } } // nsFileSpec::SetLeafName //---------------------------------------------------------------------------------------- char* nsFileSpec::GetLeafName() const // Result needs to be nsCRT::free()ed. //---------------------------------------------------------------------------------------- { char leaf[sizeof(mSpec.name)]; memcpy(leaf, &mSpec.name[1], mSpec.name[0]); leaf[mSpec.name[0]] = '\0'; return nsCRT::strdup(leaf); } // nsFileSpec::GetLeafName //---------------------------------------------------------------------------------------- void nsFileSpec::MakeAliasSafe() //---------------------------------------------------------------------------------------- { mPath.SetToEmpty(); mError = NS_FILE_RESULT(MacFileHelpers::MakeAliasSafe(mSpec)); } // nsFileSpec::MakeAliasSafe //---------------------------------------------------------------------------------------- void nsFileSpec::MakeUnique(ConstStr255Param inSuggestedLeafName) //---------------------------------------------------------------------------------------- { mPath.SetToEmpty(); if (inSuggestedLeafName[0] > 0) MacFileHelpers::PLstrcpy(mSpec.name, inSuggestedLeafName); MakeUnique(); } // nsFileSpec::MakeUnique //---------------------------------------------------------------------------------------- PRBool nsFileSpec::IsFile() const //---------------------------------------------------------------------------------------- { long dirID; Boolean isDirectory; return (noErr == ::FSpGetDirectoryID(&mSpec, &dirID, &isDirectory) && !isDirectory); } // nsFileSpec::IsFile //---------------------------------------------------------------------------------------- PRBool nsFileSpec::IsDirectory() const //---------------------------------------------------------------------------------------- { long dirID; Boolean isDirectory; return (noErr == ::FSpGetDirectoryID(&mSpec, &dirID, &isDirectory) && isDirectory); } // nsFileSpec::IsDirectory //---------------------------------------------------------------------------------------- PRBool nsFileSpec::IsHidden() const //---------------------------------------------------------------------------------------- { CInfoPBRec cInfo; PRBool hidden = PR_FALSE; if (noErr == GetCatInfo(cInfo)) if (cInfo.hFileInfo.ioFlFndrInfo.fdFlags & kIsInvisible) hidden = PR_TRUE; return hidden; } // nsFileSpec::IsHidden //---------------------------------------------------------------------------------------- PRBool nsFileSpec::IsSymlink() const //---------------------------------------------------------------------------------------- { CInfoPBRec cInfo; PRBool hidden = PR_FALSE; if (noErr == GetCatInfo(cInfo)) if (cInfo.hFileInfo.ioFlFndrInfo.fdFlags & kIsAlias) hidden = PR_TRUE; return hidden; } // nsFileSpec::IsSymlink //---------------------------------------------------------------------------------------- nsresult nsFileSpec::ResolveSymlink(PRBool& wasAliased) //---------------------------------------------------------------------------------------- { Boolean wasAliased2; // Type conversion Boolean <--> PRBool OSErr err = MacFileHelpers::ResolveAliasFile(mSpec, wasAliased2); if (wasAliased2) { mError = NS_FILE_RESULT(err); wasAliased = PR_TRUE; } else wasAliased = PR_FALSE; return mError; } // nsFileSpec::ResolveSymlink //---------------------------------------------------------------------------------------- void nsFileSpec::GetParent(nsFileSpec& outSpec) const //---------------------------------------------------------------------------------------- { if (NS_SUCCEEDED(mError)) outSpec.mError = NS_FILE_RESULT(::FSMakeFSSpec(mSpec.vRefNum, mSpec.parID, nsnull, outSpec)); } // nsFileSpec::GetParent //---------------------------------------------------------------------------------------- void nsFileSpec::operator += (const char* inRelativePath) //---------------------------------------------------------------------------------------- { NS_ASSERTION(inRelativePath, "Attempt to append relative path with null path"); // Invalidate the path cache string, since we're changing ourselves. mPath.SetToEmpty(); // if we are already bad, don't allow appendage if (NS_FAILED(Error())) { NS_WARNING("trying to append to a bad nsFileSpec"); return; } // Find the dirID of the directory described by this spec long dirID; Boolean isDirectory; mError = NS_FILE_RESULT(::FSpGetDirectoryID(&mSpec, &dirID, &isDirectory)); if (NS_FAILED(mError) || !isDirectory || !inRelativePath) return; // mSpec.vRefNum is already correct. mSpec.parID = dirID; // Next, determine if it is a UNIX or Mac style path. Now, Macintosh relative paths // are either leaf names (in which the distinction between unix and macintosh // relative paths disappears) or they start with a colon. If we find an initial colon, // then assume it's a macintosh path. // If it is a UNIX path (including just a leaf name), we will also look for ':' and // assert if we find one. if (*inRelativePath != ':') { // Looks like a UNIX path (including possibly just a leaf name) NS_ASSERTION(strchr(inRelativePath, ':') == nsnull, "Can not determine path type"); // Convert unix path (which is unencoded) to a spec mError = NS_FILE_RESULT( MacFileHelpers::FSSpecFromUnixPath(inRelativePath, mSpec, false, false, true, true)); } else { // We must be a mac path! mError = NS_FILE_RESULT(MacFileHelpers::FSSpecFromPathname(inRelativePath, mSpec, true)); } if (mError == NS_FILE_RESULT(fnfErr)) mError = NS_OK; } // nsFileSpec::operator += //---------------------------------------------------------------------------------------- void nsFileSpec::CreateDirectory(int /* unix mode */) //---------------------------------------------------------------------------------------- { long ignoredDirID; OSErr err = ::FSpDirCreate(&mSpec, smCurrentScript, &ignoredDirID); // it's OK if the dir already exists if (err != noErr && IsDirectory()) err = noErr; mError = NS_FILE_RESULT(err); } // nsFileSpec::CreateDirectory //---------------------------------------------------------------------------------------- void nsFileSpec::Delete(PRBool inRecursive) const //---------------------------------------------------------------------------------------- { OSErr anErr; nsresult& mutableError = const_cast(this)->mError; if (inRecursive) { // MoreFilesExtras anErr = ::DeleteDirectory( mSpec.vRefNum, mSpec.parID, const_cast(mSpec.name)); } else anErr = ::FSpDelete(&mSpec); if (anErr == fnfErr) // deleting a file that doesn't exist isn't an error! anErr = noErr; mutableError = NS_FILE_RESULT(anErr); } // nsFileSpec::Delete //---------------------------------------------------------------------------------------- void nsFileSpec::RecursiveCopy(nsFileSpec newDir) const //---------------------------------------------------------------------------------------- { if (IsDirectory()) { if (!(newDir.Exists())) { newDir.CreateDirectory(); } for (nsDirectoryIterator i(*this, PR_FALSE); i.Exists(); i++) { nsFileSpec& child = (nsFileSpec&)i; if (child.IsDirectory()) { nsFileSpec tmpDirSpec(newDir); char *leafname = child.GetLeafName(); tmpDirSpec += leafname; nsCRT::free(leafname); child.RecursiveCopy(tmpDirSpec); } else { child.RecursiveCopy(newDir); } } } else { nsFileSpec& filePath = (nsFileSpec&) *this; if (!(newDir.Exists())) { newDir.CreateDirectory(); } filePath.CopyToDir(newDir); } } // nsFileSpec::RecursiveCopy //---------------------------------------------------------------------------------------- nsresult nsFileSpec::Truncate(PRInt32 aNewLength) const //---------------------------------------------------------------------------------------- { short refNum; OSErr err; // First see if we have an internal error set if (NS_FAILED(mError)) return mError; // Need to open the file to trunc if (::FSpOpenDF(&mSpec, fsWrPerm, &refNum) != noErr) return NS_FILE_FAILURE; err = ::SetEOF(refNum, aNewLength); // Close the file unless we got an error that it was already closed if (err != fnOpnErr) (void)::FSClose(refNum); if (err != noErr) return NS_FILE_FAILURE; return NS_OK; } // nsFileSpec::Truncate //---------------------------------------------------------------------------------------- nsresult nsFileSpec::Rename(const char* inNewName) //---------------------------------------------------------------------------------------- { NS_ASSERTION(inNewName, "Attempt to rename with null new name"); if (strchr(inNewName, '/')) return NS_FILE_FAILURE; // no relative paths here! Str255 pName; MacFileHelpers::PLstrcpy(pName, inNewName); if (::FSpRename(&mSpec, pName) != noErr) return NS_FILE_FAILURE; SetLeafName(inNewName); return NS_OK; } // nsFileSpec::Rename //---------------------------------------------------------------------------------------- nsresult nsFileSpec::CopyToDir(const nsFileSpec& newParentDir) const //---------------------------------------------------------------------------------------- { // We can only copy into a directory, and (for now) can not copy entire directories if (!newParentDir.IsDirectory() || (IsDirectory() ) ) return NS_FILE_FAILURE; nsresult rv = NS_FILE_RESULT(::FSpFileCopy(&mSpec, &newParentDir.mSpec, const_cast(GetLeafPName()), nsnull, 0, true)); return rv; } // nsFileSpec::CopyToDir //---------------------------------------------------------------------------------------- nsresult nsFileSpec::MoveToDir(const nsFileSpec& newParentDir) //---------------------------------------------------------------------------------------- { // We can only move into a directory if (!newParentDir.IsDirectory()) return NS_FILE_FAILURE; nsresult result = NS_FILE_RESULT(::FSpMoveRenameCompat(&mSpec, &newParentDir.mSpec, const_cast(GetLeafPName()))); if ( NS_SUCCEEDED(result) ) { char* leafName = GetLeafName(); *this = newParentDir + leafName; nsCRT::free(leafName); } return result; } // nsFileSpec::MoveToDir //---------------------------------------------------------------------------------------- nsresult nsFileSpec::Execute(const char* /*args - how can this be cross-platform? problem! */ ) const //---------------------------------------------------------------------------------------- { if (IsDirectory()) return NS_FILE_FAILURE; LaunchParamBlockRec launchThis; launchThis.launchAppSpec = const_cast(&mSpec); launchThis.launchAppParameters = nsnull; // args; /* launch the thing */ launchThis.launchBlockID = extendedBlock; launchThis.launchEPBLength = extendedBlockLen; launchThis.launchFileFlags = nsnull; launchThis.launchControlFlags = launchContinue + launchNoFileFlags + launchUseMinimum; launchThis.launchControlFlags += launchDontSwitch; nsresult result = NS_FILE_RESULT(::LaunchApplication(&launchThis)); return result; } // nsFileSpec::Execute //---------------------------------------------------------------------------------------- OSErr nsFileSpec::GetCatInfo(CInfoPBRec& outInfo) const //---------------------------------------------------------------------------------------- { DirInfo *dipb=(DirInfo *)&outInfo; dipb->ioCompletion = nsnull; dipb->ioFDirIndex = 0; // use dirID and name dipb->ioVRefNum = mSpec.vRefNum; dipb->ioDrDirID = mSpec.parID; dipb->ioNamePtr = const_cast(this)->mSpec.name; return PBGetCatInfoSync(&outInfo); } // nsFileSpec::GetCatInfo() //---------------------------------------------------------------------------------------- OSErr nsFileSpec::SetFileTypeAndCreator(OSType type, OSType creator) //---------------------------------------------------------------------------------------- { FInfo info; OSErr err = ::FSpGetFInfo(&mSpec, &info); if (err != noErr) return err; info.fdType = type; info.fdCreator = creator; err = ::FSpSetFInfo(&mSpec, &info); return err; } //---------------------------------------------------------------------------------------- OSErr nsFileSpec::GetFileTypeAndCreator(OSType* type, OSType* creator) //---------------------------------------------------------------------------------------- { FInfo info; OSErr err = ::FSpGetFInfo(&mSpec, &info); if (err != noErr) return err; *type = info.fdType; *creator = info.fdCreator; return noErr; } //---------------------------------------------------------------------------------------- PRInt64 nsFileSpec::GetDiskSpaceAvailable() const //---------------------------------------------------------------------------------------- { PRInt64 space64Bits; LL_I2L(space64Bits , LONG_MAX); XVolumeParam pb; pb.ioCompletion = nsnull; pb.ioVolIndex = 0; pb.ioNamePtr = nsnull; pb.ioVRefNum = mSpec.vRefNum; // PBXGetVolInfoSync works on HFS+ volumes too! OSErr err = ::PBXGetVolInfoSync(&pb); if (err == noErr) { #ifdef HAVE_LONG_LONG space64Bits = pb.ioVFreeBytes; #else const UnsignedWide& freeBytes = UInt64ToUnsignedWide(pb.ioVFreeBytes); space64Bits.lo = freeBytes.lo; space64Bits.hi = freeBytes.hi; #endif } return space64Bits; } // nsFileSpec::GetDiskSpace() //---------------------------------------------------------------------------------------- const char* nsFileSpec::GetCString() const // This is the only conversion to const char* that is provided, and it allows the // path to be "passed" to NSPR file routines. This practice is VERY EVIL and should only // be used to support legacy code. Using it guarantees bugs on Macintosh. The string is // cached and freed by the nsFileSpec destructor, so do not delete (or free) it. //---------------------------------------------------------------------------------------- { if (mPath.IsEmpty()) { char* path = MacFileHelpers::PathNameFromFSSpec(mSpec); if (path != NULL) { const_cast(this)->mPath = path; // operator =() copies the string!!! delete[] path; } else { const_cast(this)->mError = NS_ERROR_OUT_OF_MEMORY; } } return mPath; } #pragma mark - //======================================================================================== // Macintosh nsFilePath implementation //======================================================================================== //---------------------------------------------------------------------------------------- static void AssignFromPath(nsFilePath& ioPath, const char* inString, PRBool inCreateDirs) //---------------------------------------------------------------------------------------- { NS_ASSERTION(inString, "AssignFromPath called with null inString"); NS_ASSERTION(strstr(inString, kFileURLPrefix) != inString, "URL passed as path"); FSSpec spec; spec.vRefNum = 0; spec.parID = 0; spec.name[0] = 0; MacFileHelpers::FSSpecFromUnixPath( inString, spec, false, true, // resolve alias true, inCreateDirs); // Now we have a spec, // Invoke operator = (const nsFileSpec&) to do the rest. // Why didn't we just say mPath = inString to get the path? Well, we want it to be // canonical and absolute. ioPath = spec; } //---------------------------------------------------------------------------------------- nsFilePath::nsFilePath(const char* inString, PRBool inCreateDirs) //---------------------------------------------------------------------------------------- { AssignFromPath(*this, inString, inCreateDirs); } //nsFilePath::nsFilePath //---------------------------------------------------------------------------------------- nsFilePath::nsFilePath(const nsString& inString, PRBool inCreateDirs) //---------------------------------------------------------------------------------------- { AssignFromPath(*this, NS_LossyConvertUCS2toASCII(inString).get(), inCreateDirs); } //---------------------------------------------------------------------------------------- void nsFilePath::operator = (const char* inString) //---------------------------------------------------------------------------------------- { AssignFromPath(*this, inString, PR_FALSE); } //---------------------------------------------------------------------------------------- nsFilePath::nsFilePath(const nsFileSpec& inSpec) //---------------------------------------------------------------------------------------- { *this = inSpec; } //---------------------------------------------------------------------------------------- nsFilePath::nsFilePath(const nsFileURL& inOther) //---------------------------------------------------------------------------------------- { *this = inOther; } //---------------------------------------------------------------------------------------- void nsFilePath::operator = (const nsFileSpec& inSpec) //---------------------------------------------------------------------------------------- { char * path = MacFileHelpers::PathNameFromFSSpec(inSpec); path = MacFileHelpers::EncodeMacPath(path, true, false); mPath = path; nsCRT::free(path); mFileSpec = inSpec; } // nsFilePath::operator = //---------------------------------------------------------------------------------------- void nsFilePath::operator = (const nsFileURL& inOther) //---------------------------------------------------------------------------------------- { char * path = MacFileHelpers::PathNameFromFSSpec(inOther.mFileSpec); path = MacFileHelpers::EncodeMacPath(path, true, false); mPath = path; nsCRT::free(path); mFileSpec = inOther.GetFileSpec(); } #pragma mark - //======================================================================================== // nsFileURL implementation //======================================================================================== //---------------------------------------------------------------------------------------- nsFileURL::nsFileURL(const char* inString, PRBool inCreateDirs) //---------------------------------------------------------------------------------------- : mURL(inString) { NS_ASSERTION(inString, "nsFileURL constructed with null inString"); NS_ASSERTION(strstr(inString, kFileURLPrefix) == inString, "Not a URL!"); mFileSpec.mError = NS_FILE_RESULT(MacFileHelpers::FSSpecFromUnixPath( inString + kFileURLPrefixLength, mFileSpec.mSpec, true, // need to decode false, // resolve alias false, // must be a full path inCreateDirs)); if (mFileSpec.mError == NS_FILE_RESULT(fnfErr)) mFileSpec.mError = NS_OK; } // nsFileURL::nsFileURL //---------------------------------------------------------------------------------------- nsFileURL::nsFileURL(const nsString& inString, PRBool inCreateDirs) //---------------------------------------------------------------------------------------- : mURL(nsnull) { NS_LossyConvertUCS2toASCII cstring(inString); mURL = cstring.get(); NS_ASSERTION(strstr(cstring.get(), kFileURLPrefix) == cstring.get(), "Not a URL!"); mFileSpec.mError = NS_FILE_RESULT(MacFileHelpers::FSSpecFromUnixPath( cstring.get() + kFileURLPrefixLength, mFileSpec.mSpec, true, // need to decode false, // resolve alias false, // must be a full path inCreateDirs)); if (mFileSpec.mError == NS_FILE_RESULT(fnfErr)) mFileSpec.mError = NS_OK; } // nsFileURL::nsFileURL //---------------------------------------------------------------------------------------- nsFileURL::nsFileURL(const nsFilePath& inOther) //---------------------------------------------------------------------------------------- { *this = inOther.GetFileSpec(); } // nsFileURL::nsFileURL //---------------------------------------------------------------------------------------- nsFileURL::nsFileURL(const nsFileSpec& inOther) //---------------------------------------------------------------------------------------- { *this = inOther; } // nsFileURL::nsFileURL //---------------------------------------------------------------------------------------- void nsFileURL::operator = (const nsFilePath& inOther) //---------------------------------------------------------------------------------------- { *this = inOther.GetFileSpec(); } // nsFileURL::operator = //---------------------------------------------------------------------------------------- void nsFileURL::operator = (const nsFileSpec& inOther) //---------------------------------------------------------------------------------------- { mFileSpec = inOther; char* path = MacFileHelpers::PathNameFromFSSpec( mFileSpec ); char* encodedPath = MacFileHelpers::EncodeMacPath(path, true, true); nsSimpleCharString encodedURL(kFileURLPrefix); encodedURL += encodedPath; nsCRT::free(encodedPath); mURL = encodedURL; if (encodedURL[encodedURL.Length() - 1] != '/' && inOther.IsDirectory()) mURL += "/"; } // nsFileURL::operator = //---------------------------------------------------------------------------------------- void nsFileURL::operator = (const char* inString) //---------------------------------------------------------------------------------------- { NS_ASSERTION(inString, "nsFileURL operator= constructed with null inString"); mURL = inString; NS_ASSERTION(strstr(inString, kFileURLPrefix) == inString, "Not a URL!"); mFileSpec.mError = NS_FILE_RESULT(MacFileHelpers::FSSpecFromUnixPath( inString + kFileURLPrefixLength, mFileSpec.mSpec, true, // need to decode true, // resolve alias false, // must be a full path false)); // don't create dirs. if (mFileSpec.mError == NS_FILE_RESULT(fnfErr)) mFileSpec.mError = NS_OK; } // nsFileURL::operator = #pragma mark - //======================================================================================== // nsDirectoryIterator //======================================================================================== //---------------------------------------------------------------------------------------- nsDirectoryIterator::nsDirectoryIterator( const nsFileSpec& inDirectory , PRBool resolveSymLinks) //---------------------------------------------------------------------------------------- : mCurrent(inDirectory) , mExists(false) , mResoveSymLinks(resolveSymLinks) , mIndex(-1) { CInfoPBRec pb; OSErr err = inDirectory.GetCatInfo(pb); // test that we have got a directory back, not a file DirInfo* dipb = (DirInfo*)&pb; if (err != noErr || !( dipb->ioFlAttrib & 0x0010)) return; // Sorry about this, there seems to be a bug in CWPro 4: FSSpec& currentSpec = mCurrent.nsFileSpec::operator FSSpec&(); mVRefNum = currentSpec.vRefNum; mParID = dipb->ioDrDirID; mMaxIndex = pb.dirInfo.ioDrNmFls; mIndex = 0; // ready to increment ++(*this); // the pre-increment operator } // nsDirectoryIterator::nsDirectoryIterator //---------------------------------------------------------------------------------------- OSErr nsDirectoryIterator::SetToIndex() //---------------------------------------------------------------------------------------- { CInfoPBRec cipb; DirInfo *dipb=(DirInfo *)&cipb; Str255 objectName; dipb->ioCompletion = nsnull; dipb->ioFDirIndex = mIndex; // Sorry about this, there seems to be a bug in CWPro 4: FSSpec& currentSpec = mCurrent.nsFileSpec::operator FSSpec&(); dipb->ioVRefNum = mVRefNum; /* Might need to use vRefNum, not sure*/ dipb->ioDrDirID = mParID; dipb->ioNamePtr = objectName; OSErr err = PBGetCatInfoSync(&cipb); FSSpec temp; if (err == noErr) err = FSMakeFSSpec(mVRefNum, mParID, objectName, &temp); mCurrent = temp; // use the operator: it clears the string cache. mExists = err == noErr; if (mExists && mResoveSymLinks) { PRBool ignore; mCurrent.ResolveSymlink(ignore); } return err; } // nsDirectoryIterator::SetToIndex() //---------------------------------------------------------------------------------------- nsDirectoryIterator& nsDirectoryIterator::operator -- () //---------------------------------------------------------------------------------------- { mExists = false; while (--mIndex > 0) { OSErr err = SetToIndex(); if (err == noErr) break; } return *this; } // nsDirectoryIterator::operator -- //---------------------------------------------------------------------------------------- nsDirectoryIterator& nsDirectoryIterator::operator ++ () //---------------------------------------------------------------------------------------- { mExists = false; if (mIndex >= 0) // probably trying to use a file as a directory! while (++mIndex <= mMaxIndex) { OSErr err = SetToIndex(); if (err == noErr) break; } return *this; } // nsDirectoryIterator::operator ++