pjs/xpcom/io/nsFileSpecMac.cpp

1301 строка
50 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
// This file is included by nsFile.cp, and includes the Macintosh-specific
// implementations.
#include "FullPath.h"
#include "FileCopy.h"
#include "MoreFilesExtras.h"
#include <Aliases.h>
#include <Folders.h>
#include <Errors.h>
#include <TextUtils.h>
#include <Processes.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,
Boolean wantLeafName );
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;
}
*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 <path> 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, Boolean wantLeafName )
// Returns a full pathname to the given file
// Returned value is allocated with new [], and must be freed with delete []
// This is taken from FSpGetFullPath in MoreFiles, except that we need to tolerate
// fnfErr.
//-----------------------------------
{
char* result = nil;
OSErr err = noErr;
short fullPathLength = 0;
Handle fullPath = nsnull;
FSSpec tempSpec = inSpec;
if ( tempSpec.parID == fsRtParID )
{
/* The object is a volume */
/* Add a colon to make it a full pathname */
tempSpec.name[++tempSpec.name[0]] = ':';
/* We're done */
err = PtrToHand(&tempSpec.name[1], &fullPath, tempSpec.name[0]);
}
else
{
/* The object isn't a volume */
CInfoPBRec pb = { 0 };
Str63 dummyFileName;
MacFileHelpers::PLstrcpy(dummyFileName, "\pG'day!");
/* Is the object a file or a directory? */
pb.dirInfo.ioNamePtr = (! tempSpec.name[0]) ? (StringPtr)dummyFileName : tempSpec.name;
pb.dirInfo.ioVRefNum = tempSpec.vRefNum;
pb.dirInfo.ioDrDirID = tempSpec.parID;
pb.dirInfo.ioFDirIndex = 0;
err = PBGetCatInfoSync(&pb);
if ( err == noErr || err == fnfErr)
{
// if the object is a directory, append a colon so full pathname ends with colon
// Beware of the "illegal spec" case that Netscape uses (empty name string). In
// this case, we don't want the colon.
if ( err == noErr && tempSpec.name[0] && (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0 )
{
++tempSpec.name[0];
tempSpec.name[tempSpec.name[0]] = ':';
}
/* Put the object name in first */
err = PtrToHand(&tempSpec.name[1], &fullPath, tempSpec.name[0]);
if ( err == noErr )
{
/* Get the ancestor directory names */
pb.dirInfo.ioNamePtr = tempSpec.name;
pb.dirInfo.ioVRefNum = tempSpec.vRefNum;
pb.dirInfo.ioDrParID = tempSpec.parID;
do /* loop until we have an error or find the root directory */
{
pb.dirInfo.ioFDirIndex = -1;
pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID;
err = PBGetCatInfoSync(&pb);
if ( err == noErr )
{
/* Append colon to directory name */
++tempSpec.name[0];
tempSpec.name[tempSpec.name[0]] = ':';
/* Add directory name to beginning of fullPath */
(void) Munger(fullPath, 0, nsnull, 0, &tempSpec.name[1], tempSpec.name[0]);
err = MemError();
}
} while ( err == noErr && pb.dirInfo.ioDrDirID != fsRtDirID );
}
}
}
if ( err != noErr && err != fnfErr)
goto Clean;
fullPathLength = GetHandleSize(fullPath);
err = noErr;
int allocSize = 1 + fullPathLength;
// We only want the leaf name if it's the root directory or wantLeafName is true.
if (inSpec.parID != fsRtParID && !wantLeafName)
allocSize -= inSpec.name[0];
result = new char[allocSize];
if (!result)
goto Clean;
memcpy(result, *fullPath, allocSize - 1);
result[ allocSize - 1 ] = 0;
Clean:
if (fullPath)
DisposeHandle(fullPath);
NS_ASSERTION(result, "Out of memory"); // OOPS! very bad.
return result;
} // MacFileHelpers::PathNameFromFSSpec
//========================================================================================
// Macintosh nsFileSpec implementation
//========================================================================================
//----------------------------------------------------------------------------------------
nsFileSpec::nsFileSpec()
//----------------------------------------------------------------------------------------
: mError(NS_ERROR_NOT_INITIALIZED)
{
mSpec.name[0] = '\0';
}
//----------------------------------------------------------------------------------------
nsFileSpec::nsFileSpec(const FSSpec& inSpec)
//----------------------------------------------------------------------------------------
: mSpec(inSpec)
, mError(NS_OK)
{
PRBool dummy;
ResolveAlias(dummy);
}
//----------------------------------------------------------------------------------------
void nsFileSpec::operator = (const FSSpec& inSpec)
//----------------------------------------------------------------------------------------
{
mSpec = inSpec;
mError = NS_OK;
PRBool dummy;
ResolveAlias(dummy);
}
//----------------------------------------------------------------------------------------
nsFileSpec::nsFileSpec(const nsFileSpec& inSpec)
//----------------------------------------------------------------------------------------
: mSpec(inSpec.mSpec)
, mError(inSpec.Error())
{
}
//----------------------------------------------------------------------------------------
nsFileSpec::nsFileSpec(const char* inNativePathString, PRBool inCreateDirs)
//----------------------------------------------------------------------------------------
{
mSpec.vRefNum = 0;
mSpec.parID = 0;
mError = NS_FILE_RESULT(
MacFileHelpers::FSSpecFromPathname(
inNativePathString,
mSpec, inCreateDirs));
if (mError == NS_FILE_RESULT(fnfErr))
mError = NS_OK;
PRBool dummy;
ResolveAlias(dummy);
} // nsFileSpec::nsFileSpec
//----------------------------------------------------------------------------------------
nsFileSpec::nsFileSpec(const nsString& inNativePathString, PRBool inCreateDirs)
//----------------------------------------------------------------------------------------
{
mSpec.vRefNum = 0;
mSpec.parID = 0;
mError = NS_FILE_RESULT(
MacFileHelpers::FSSpecFromPathname(
nsAutoCString(inNativePathString),
mSpec, inCreateDirs));
if (mError == NS_FILE_RESULT(fnfErr))
mError = NS_OK;
PRBool dummy;
ResolveAlias(dummy);
} // nsFileSpec::nsFileSpec
//----------------------------------------------------------------------------------------
nsFileSpec::nsFileSpec(short vRefNum, long parID, ConstStr255Param name)
//----------------------------------------------------------------------------------------
{
mError = NS_FILE_RESULT(::FSMakeFSSpec(vRefNum, parID, name, &mSpec));
if (mError == NS_FILE_RESULT(fnfErr))
mError = NS_OK;
PRBool dummy;
ResolveAlias(dummy);
}
//----------------------------------------------------------------------------------------
nsFileSpec::nsFileSpec(const nsFilePath& inPath)
//----------------------------------------------------------------------------------------
{
*this = inPath.GetFileSpec();
}
//----------------------------------------------------------------------------------------
void nsFileSpec::operator = (const char* inString)
//----------------------------------------------------------------------------------------
{
mPath.SetToEmpty();
mSpec.vRefNum = 0;
mSpec.parID = 0;
mError = NS_FILE_RESULT(
MacFileHelpers::FSSpecFromPathname(inString, mSpec, true));
if (mError == NS_FILE_RESULT(fnfErr))
mError = NS_OK;
PRBool dummy;
ResolveAlias(dummy);
} // nsFileSpec::operator =
//----------------------------------------------------------------------------------------
void nsFileSpec::operator = (const nsFileSpec& inSpec)
//----------------------------------------------------------------------------------------
{
mPath.SetToEmpty();
mSpec.vRefNum = inSpec.mSpec.vRefNum;
mSpec.parID = inSpec.mSpec.parID;
memcpy(mSpec.name, inSpec.mSpec.name, inSpec.mSpec.name[0] + 1);
mError = inSpec.Error();
} // nsFileSpec::operator =
//----------------------------------------------------------------------------------------
void nsFileSpec::operator = (const nsFilePath& inPath)
//----------------------------------------------------------------------------------------
{
*this = inPath.GetFileSpec();
} // nsFileSpec::operator =
//----------------------------------------------------------------------------------------
PRBool nsFileSpec::Exists() const
//----------------------------------------------------------------------------------------
{
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...
//----------------------------------------------------------------------------------------
{
mPath.SetToEmpty();
// 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[64];
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
//----------------------------------------------------------------------------------------
void nsFileSpec::ResolveAlias(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;
} // nsFileSpec::ResolveAlias
//----------------------------------------------------------------------------------------
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
//----------------------------------------------------------------------------------------
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)
//----------------------------------------------------------------------------------------
{
// Invalidate the path cache string, since we're changing ourselves.
mPath.SetToEmpty();
// 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;
PRBool dummy;
ResolveAlias(dummy);
} // nsFileSpec::operator +=
//----------------------------------------------------------------------------------------
void nsFileSpec::CreateDirectory(int /* unix mode */)
//----------------------------------------------------------------------------------------
{
long ignoredDirID;
mError = NS_FILE_RESULT(FSpDirCreate(&mSpec, smCurrentScript, &ignoredDirID));
} // nsFileSpec::CreateDirectory
//----------------------------------------------------------------------------------------
void nsFileSpec::Delete(PRBool inRecursive) const
//----------------------------------------------------------------------------------------
{
OSErr anErr;
nsresult& mutableError = const_cast<nsFileSpec*>(this)->mError;
if (inRecursive)
{
// MoreFilesExtras
anErr = ::DeleteDirectory(
mSpec.vRefNum,
mSpec.parID,
const_cast<unsigned char*>(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
//----------------------------------------------------------------------------------------
nsresult nsFileSpec::Rename(const char* inNewName)
//----------------------------------------------------------------------------------------
{
if (strchr(inNewName, '/'))
return -1; // no relative paths here!
Str255 pName;
MacFileHelpers::PLstrcpy(pName, inNewName);
if (FSpRename(&mSpec, pName) != noErr)
return -1;
SetLeafName(inNewName);
return 0;
} // nsFileSpec::Rename
//----------------------------------------------------------------------------------------
nsresult nsFileSpec::Copy(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 result = NS_FILE_RESULT(::FSpFileCopy( &mSpec,
&newParentDir.mSpec,
const_cast<StringPtr>(GetLeafPName()),
nsnull,
0,
true));
return result;
} // nsFileSpec::Copy
//----------------------------------------------------------------------------------------
nsresult nsFileSpec::Move(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<StringPtr>(GetLeafPName())));
if ( NS_SUCCEEDED(result) )
{
char* leafName = GetLeafName();
*this = newParentDir + leafName;
nsCRT::free(leafName);
}
return result;
} // nsFileSpec::Move
//----------------------------------------------------------------------------------------
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<FSSpec*>(&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<nsFileSpec*>(this)->mSpec.name;
return PBGetCatInfoSync(&outInfo);
} // nsFileSpec::GetCatInfo()
//----------------------------------------------------------------------------------------
PRUint32 nsFileSpec::GetDiskSpaceAvailable() const
//----------------------------------------------------------------------------------------
{
HVolumeParam pb;
pb.ioCompletion = nsnull;
pb.ioVolIndex = 0;
pb.ioNamePtr = nsnull;
pb.ioVRefNum = mSpec.vRefNum;
OSErr err = PBHGetVInfoSync( (HParmBlkPtr)&pb );
if ( err == noErr )
return pb.ioVFrBlk * pb.ioVAlBlkSiz;
return ULONG_MAX;
} // 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())
{
const_cast<nsFileSpec*>(this)->mPath
= MacFileHelpers::PathNameFromFSSpec(mSpec, true);
if (mPath.IsEmpty())
const_cast<nsFileSpec*>(this)->mError = NS_ERROR_OUT_OF_MEMORY;
}
return mPath;
}
//========================================================================================
// Macintosh nsFilePath implementation
//========================================================================================
//----------------------------------------------------------------------------------------
static void AssignFromPath(nsFilePath& ioPath, const char* inString, PRBool inCreateDirs)
//----------------------------------------------------------------------------------------
{
NS_ASSERTION(strstr(inString, kFileURLPrefix) != inString, "URL passed as path");
FSSpec spec;
spec.vRefNum = 0;
spec.parID = 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, nsAutoCString(inString), inCreateDirs);
}
//----------------------------------------------------------------------------------------
void nsFilePath::operator = (const char* inString)
//----------------------------------------------------------------------------------------
{
AssignFromPath(*this, nsAutoCString(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, true);
path = MacFileHelpers::EncodeMacPath(path, true, false);
mPath = path;
delete [] path;
mFileSpec = inSpec;
} // nsFilePath::operator =
//----------------------------------------------------------------------------------------
void nsFilePath::operator = (const nsFileURL& inOther)
//----------------------------------------------------------------------------------------
{
char * path = MacFileHelpers::PathNameFromFSSpec(inOther.mFileSpec, true);
path = MacFileHelpers::EncodeMacPath(path, true, false);
mPath = path;
delete [] path;
mFileSpec = inOther.GetFileSpec();
}
//========================================================================================
// nsFileURL implementation
//========================================================================================
//----------------------------------------------------------------------------------------
nsFileURL::nsFileURL(const char* inString, PRBool inCreateDirs)
//----------------------------------------------------------------------------------------
: 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
inCreateDirs));
if (mFileSpec.mError == NS_FILE_RESULT(fnfErr))
mFileSpec.mError = NS_OK;
} // nsFileURL::nsFileURL
//----------------------------------------------------------------------------------------
nsFileURL::nsFileURL(const nsString& inString, PRBool inCreateDirs)
//----------------------------------------------------------------------------------------
: mURL(nsnull)
{
nsAutoCString autostring(inString);
const char* cstring = (const char*)autostring;
mURL = cstring;
NS_ASSERTION(strstr(cstring, kFileURLPrefix) == cstring, "Not a URL!");
mFileSpec.mError = NS_FILE_RESULT(MacFileHelpers::FSSpecFromUnixPath(
cstring + kFileURLPrefixLength,
mFileSpec.mSpec,
true, // need to decode
true, // 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, true );
char* encodedPath = MacFileHelpers::EncodeMacPath(path, true, true);
nsSimpleCharString encodedURL(kFileURLPrefix);
encodedURL += encodedPath;
delete [] encodedPath;
mURL = encodedURL;
if (encodedURL[encodedURL.Length() - 1] != '/' && inOther.IsDirectory())
mURL += "/";
} // nsFileURL::operator =
//----------------------------------------------------------------------------------------
void nsFileURL::operator = (const char* 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 =
//========================================================================================
// nsDirectoryIterator
//========================================================================================
//----------------------------------------------------------------------------------------
nsDirectoryIterator::nsDirectoryIterator(
const nsFileSpec& inDirectory
, int inIterateDirection)
//----------------------------------------------------------------------------------------
: mCurrent(inDirectory)
, mExists(false)
, 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;
if (inIterateDirection > 0)
{
mIndex = 0; // ready to increment
++(*this); // the pre-increment operator
}
else
{
mIndex = mMaxIndex + 1; // ready to decrement
--(*this); // the pre-decrement 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 and resolves the alias.
mExists = err == noErr;
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 ++