gecko-dev/cmd/macfe/central/ufilemgr.cp
1998-08-05 08:26:45 +00:00

1211 lines
33 KiB
C++
Raw Blame History

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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.
*/
// ===========================================================================
// ufilemgr.cp
// Created by atotic, June 15th, 1994
// File utility routines for:
// Creating a temporary file name
// Bookkeeping of the temporary files
// ===========================================================================
// Front End
#include "ufilemgr.h"
#include "uprefd.h"
#include "BufferStream.h"
#include "uerrmgr.h"
#include "macutil.h"
#include "resgui.h"
#include "ulaunch.h"
#include "libmime.h"
#include "uerrmgr.h"
#include "FullPath.h"
#include "FileCopy.h" //in MoreFiles
#include "AERegistry.h"
#include "Processes.h"
#include "LArrayIterator.h"
#include "UMemoryMgr.h"
// XP
#ifndef _XP_H_
#include "xp.h"
#endif
#include "PascalString.h"
// MacOS
#include <AppleEvents.h>
#include <Folders.h>
#include <Errors.h>
#include <Finder.h>
//#include <UAEGizmos.h>
#define MAX_FILENAME_LEN 31
#define MAX_ALT_DIGIT_LEN 5
#define MAX_ALT_NAME_LEN (MAX_FILENAME_LEN - (MAX_ALT_DIGIT_LEN + 1) )
/*****************************************************************************
* class CFileMgr
*****************************************************************************/
CFileMgr CFileMgr::sFileManager; // The manager
unsigned int CFileMgr::sMungeNum = 1;
extern "C" OSErr FSSpecFromPathname_CWrapper(char * path, FSSpec * outSpec);
// <20><> constructors/destructors
// Tries to delete all the files.
// It will not be able to delete all the files, since users might have
// moved them, deleted them, etc.
CFileMgr::~CFileMgr()
{
Boolean allDeleted = TRUE;
LFile * aFile;
for (int i =1; i <= fFiles.GetCount(); i++) // Loop through
{
fFiles.FetchItemAt(i, &aFile);
Try_ {
FSSpec fileSpec;
aFile->GetSpecifier(fileSpec);
FSpDelete(&fileSpec);
delete aFile;
}
Catch_(inErr) {
allDeleted = FALSE;
} EndCatch_
}
fFiles.RemoveItemsAt(fFiles.GetCount(), 1);
}
// <20><> file management interface
// Register a file
void CFileMgr::RegisterFile(LFileBufferStream * inFile)
{
fFiles.InsertItemsAt(1, LArray::index_Last, &inFile);
}
// Cancel file registration. Just deletes it from the queue
void CFileMgr::CancelRegister(LFileBufferStream * inFile)
{
fFiles.Remove(&inFile);
}
// Cancels registration, and deletes the file from disk, and its file object
void CFileMgr::CancelAndDelete(LFileBufferStream * inFile)
{
fFiles.Remove(&inFile); // Remove it from the queue
FSSpec fileSpec;
inFile->GetSpecifier(fileSpec);
OSErr err = FSpDelete(&fileSpec); // Delete disk file
delete inFile; // Delete the object
}
// FindURL occurs when we have launched an external file
void CFileMgr::HandleFindURLEvent(
const AppleEvent &inAppleEvent,
AppleEvent &outAEReply,
AEDesc &/*outResult*/,
long /*inAENumber*/)
{
FSSpec lookingFor;
Size actualSize;
DescType realType;
OSErr err = ::AEGetParamPtr(&inAppleEvent, keyDirectObject,
typeFSS, &realType,
&lookingFor,
sizeof(lookingFor), &actualSize);
ThrowIfOSErr_(err);
// Have the file spec, now look for the same one
LFileBufferStream *file;
LArrayIterator iter(fFiles);
while (iter.Next(&file))
{
FSSpec macSpec;
file->GetSpecifier(macSpec);
if ((macSpec.vRefNum == lookingFor.vRefNum) &&
(macSpec.parID == lookingFor.parID) &&
(CStr32(macSpec.name) == CStr32(lookingFor.name)))
{
char * url = file->GetURL();
if (url)
{
err = ::AEPutParamPtr(&outAEReply, keyAEResult, typeChar, url, strlen(url));
return;
}
else
Throw_(errAEDescNotFound);
}
}
Throw_(errAEDescNotFound);
}
/*
Turning an URL into a filename
Returns a file name that works on a Mac
Requirements:
Length < 32 characters
No colons
Name should look similar to URL (which is closer to the URN than the anchor)
Input:
URLs match this expression: <host>:<top><paths><name><crud>
Top can be a /
Paths end with a /
Crud begins with a # or ?
Host ends with a :
Method:
Start at the end of the paths - after last /
Or at the end of the hostname - after first :
Go until a # or ?
Notes:
This does not guarantee that the filename will work. We may still need to
munge it later (if for example such a filename already existed)
*/
CStr31 CFileMgr::FileNameFromURL( const char* url )
{
CStr255 newName;
Byte size = 0;
if ( !url )
return CStr31(::GetCString(FILE_NAME_NONE));
const char* urlString = url;
// <20> get the starting point
const char* nameStart = strrchr( urlString, '/' );
// <20><>filename starts *after* the colon
if ( nameStart && ( strlen( nameStart ) > 1) )
nameStart++;
// <20> no colon, must be simple path, go after the hostname
else
{
nameStart = strchr( urlString, ':' );
// <20><>filename starts *after* the colon
if ( nameStart )
nameStart++;
// <20><>no host, assume it's a simple local URL
else
nameStart = urlString;
}
// <20><>copy it until we hit the crud (# or ?) or max out
size = 0;
while ( size < MAX_FILENAME_LEN )
{
// 0 indexed
char current = nameStart[ size ];
if ( current == '#' || current == '?' || !current )
break;
// <20><>take out any : characters as well
if ( current == ':' )
current = '<EFBFBD>';
size++;
newName[ size ] = current;
newName.Length() = size;
}
// If size was 0 then we have a problem. Make a temp filename.
// A reasonable convention would be to have names like "Untitled 1", "Untitled 2", etc
// since we really don't have any other information. We'll leave off the number
// here and let the arbitrator add it on later. We don't have an english file kind
// either, which the arbitrator might have, so this could get folded in later (just
// have a "Untitled" hack in here now)
if ( size == 0 )
newName = (char*)GetCString( UNTITLED_RESID );
return newName;
}
// Creates a FSSpec of a file that does not exist, given the template
OSErr CFileMgr::UniqueFileSpec(const FSSpec& inSpec, const CStr31& genericName,
FSSpec & outSpec)
{
CStr255 goodName = genericName;
OSErr err;
err = FSMakeFSSpec( inSpec.vRefNum, inSpec.parID, goodName, &outSpec );
if (err == fnfErr)
return noErr;
if (err)
return err;
// If the filename exists (noErr) then we have a problem!
// Argh. We can do this: filename.gif-2
// Or this: #2 filename.gif
// One messes with the extension (which some people care about) and the other
// with the "proper" name.
#define USE_RANDOM 0
#if USE_RANDOM
long index = 0;
#else
static short index = 1;
#endif
CStr31 indexStr;
Boolean done = FALSE;
CStr255 altName;
do {
index++; // start with "Picture 2" after "Picture" exists
if ( index > 999 ) // something's very wrong
return ioErr;
altName = goodName;
if ( altName.Length() > MAX_ALT_NAME_LEN )
altName.Length() = MAX_ALT_NAME_LEN;
#if USE_RANDOM
long randomNum = abs(::Random())*9999/32767;
NumToString( randomNum, indexStr );
#else
NumToString( index, indexStr );
#endif
altName += "-";
altName += indexStr;
err = FSMakeFSSpec( inSpec.vRefNum, inSpec.parID, altName, &outSpec );
if ( err == fnfErr )
return noErr;
} while ( err == noErr );
return noErr;
}
/*
Turning an URL into a fileSpec of a new file.
A file spec for non-existent file is created.
Requirements:
<09> Preserve filename as much as possible, including extension, if any (even though
this doesn't quite make sense on the Mac).
<09> Make the new filename look "nice". Provide as much information as possible to the
user (they might want to keep the file) and don't use random numbers or the date.
Well, the date might be OK. Something like "Picture 1", "Picture 2" might do,
or "#2 madonna.gif".
Method:
For file name generation see FileNameFromURL
If file with default name already exists, munge the name by appending
it a number.
*/
OSErr CFileMgr::NewFileSpecFromURLStruct (const char * location,
const FSSpec& inSpec,
FSSpec &outSpec)
{
CStr31 goodName = FileNameFromURL(location);
return UniqueFileSpec( inSpec, goodName, outSpec );
}
/*
FL_SetComment
*/
void CFileMgr::FileSetComment (const FSSpec& file, const CStr255& comment)
{
// Set GetInfo box in Finder to the URL. Not really necessary, but a neat hack
Boolean hasDesktop;
FSSpec tempSpec = CPrefs::GetFolderSpec(CPrefs::DownloadFolder);
OSErr err = VolHasDesktopDB(tempSpec.vRefNum, hasDesktop);
if (err || !hasDesktop)
return;
DTPBRec pb;
pb.ioCompletion = NULL;
pb.ioVRefNum = tempSpec.vRefNum;
pb.ioNamePtr = NULL;
err = PBDTGetPath(&pb);
if (err)
return;
short refNum = pb.ioDTRefNum;
pb.ioNamePtr = (StringPtr)&file.name; // A pointer to a file or directory name.
char *ccomment = comment;
pb.ioDTBuffer = ccomment;
pb.ioDTReqCount = strlen(ccomment);
pb.ioDirID = file.parID; // The parent directory of the file or directory.
err = ::PBDTSetCommentSync(&pb);
}
/*
FL_VolumeHasDesktopDB
*/
OSErr CFileMgr::VolHasDesktopDB(short vRefNum, Boolean& hasDesktop)
{
HParamBlockRec pb;
GetVolParmsInfoBuffer info;
pb.ioParam.ioCompletion = NULL;
pb.ioParam.ioVRefNum = vRefNum;
pb.ioParam.ioNamePtr = NULL;
pb.ioParam.ioBuffer = (Ptr) & info;
pb.ioParam.ioReqCount = sizeof(GetVolParmsInfoBuffer);
OSErr err = ::PBHGetVolParmsSync(&pb);
hasDesktop = (err == noErr) && ((info.vMAttrib & (1L << bHasDesktopMgr)) != 0);
return err;
} // VolHasDesktopDB
// From MacApp
// Get the vRefNum of an indexed on-line volume
OSErr CFileMgr::GetIndVolume(short index,
short& vRefNum)
{
ParamBlockRec pb;
OSErr err;
pb.volumeParam.ioCompletion = NULL;
pb.volumeParam.ioNamePtr = NULL;
pb.volumeParam.ioVolIndex = index;
err = PBGetVInfoSync(&pb);
vRefNum = pb.volumeParam.ioVRefNum;
return err;
} // GetIndVolume
// Copied from MacApp
// Get the vRefNum of the system (boot) volume
OSErr CFileMgr::GetSysVolume(short& vRefNum)
{
OSErr theErr;
long dir;
theErr = FindFolder(kOnSystemDisk, kSystemFolderType, false, &vRefNum, &dir);
return theErr;
}
// Finds a folder inside a folder.
// The folder must be writeable
// Signals error if folder cannot be found
// Success: returns noErr and the location of the found folder in foundRefNum, foundDirID
// Failure: returns PBGetCatInfo error, and undefined foundRefNum, foundDirID
OSErr CFileMgr::FindWFolderInFolder(short refNum, // Directory/folder to be searched
long dirID,
const CStr255& folderName, // Name of the folder to search for
short * outRefNum,// Location of the found folder
long * outDirID)
{
CInfoPBRec cipb;
DirInfo *dipb=(DirInfo *)&cipb; // Typecast to what we need
OSErr err;
dipb->ioNamePtr = (unsigned char *) &folderName;
dipb->ioFDirIndex = 0;
dipb->ioVRefNum = refNum;
dipb->ioDrDirID = dirID;
err = PBGetCatInfoSync(&cipb);
if (err != noErr)
return err;
if ( ( dipb->ioFlAttrib & 0x0010 ) )// && !(cipb.ioACUser && 2)) // Is it a directory and writable?
{
*outRefNum = dipb->ioVRefNum;
*outDirID = dipb->ioDrDirID;
return noErr;
}
return fnfErr;
} // FindWFolderInFolder
// Creates a folder named 'folderName' inside a folder.
// The errors returned are same as PBDirCreate
OSErr CFileMgr::CreateFolderInFolder(short refNum, // Parent directory/volume
long dirID,
const CStr255 &folderName, // Name of the new folder
short * outRefNum, // Volume of the created folder
long * outDirID) //
{
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;
}
// Creates a folder spec from folder id;
OSErr CFileMgr::FolderSpecFromFolderID(short vRefNum, long dirID, FSSpec& folderSpec)
{
folderSpec.vRefNum = 0; // Initialize them to 0
folderSpec.parID = 0;
CInfoPBRec cinfo;
DirInfo *dipb=(DirInfo *)&cinfo;
dipb->ioNamePtr = (StringPtr)&folderSpec.name;
dipb->ioVRefNum = vRefNum;
dipb->ioFDirIndex = -1;
dipb->ioDrDirID = dirID;
OSErr err = PBGetCatInfoSync(&cinfo);
if (err == noErr)
{
folderSpec.vRefNum = dipb->ioVRefNum;
folderSpec.parID = dipb->ioDrParID;
}
return err;
}
//-----------------------------------
char* CFileMgr::PathNameFromFSSpec( const FSSpec& inSpec, Boolean wantLeafName )
// Returns a full pathname to the given file
// Returned value is allocated with XP_ALLOC, and must be freed with XP_FREE
// This is taken from FSpGetFullPath in MoreFiles, except that we need to tolerate
// fnfErr.
//-----------------------------------
{
char* result = nil;
FSSpec tempSpec;
OSErr err = noErr;
short fullPathLength = 0;
Handle fullPath = NULL;
/* Make a copy of the input FSSpec that can be modified */
BlockMoveData(&inSpec, &tempSpec, sizeof(FSSpec));
if ( tempSpec.parID == fsRtParID )
{
/* The object is a volume */
/* Add a colon to make it a full pathname */
++tempSpec.name[0];
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 };
CStr63 dummyFileName("\pGrippy Lives!");
/* 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, NULL, 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 = (char*)XP_ALLOC(allocSize);
if (!result)
goto Clean;
memcpy(result, *fullPath, allocSize - 1);
result[ allocSize - 1 ] = 0;
Clean:
if (fullPath)
DisposeHandle(fullPath);
Assert_(result); // OOPS! very bad.
return result;
} // CFileMgr::PathNameFromFSSpec
// PathNameFromFSSpec + hex encoding
// pass this in to netlib when specifying filesToPost, etch
char* CFileMgr::EncodedPathNameFromFSSpec( const FSSpec& inSpec, Boolean wantLeafName )
{
char* path = CFileMgr::PathNameFromFSSpec( inSpec, wantLeafName );
path = CFileMgr::EncodeMacPath( path );
return path;
}
// GetElement is a routine used by FSSpecFromPathname, routine adopted from ParseFullPathName.c on dev disk
// I am not sure how does it work.
static Boolean GetElement(StringPtr Result,char * PathNamePtr,short ElementNumber);
static Boolean GetElement(StringPtr Result,char *PathNamePtr,short ElementNumber)
{
char *eStart, *eEnd;
eStart = eEnd = PathNamePtr;
while (ElementNumber) { // Search for the element
if (*eEnd == ':' || !(*eEnd)) { // if we see colon or a null , we're at the end of element
--ElementNumber; // one down, n-1 to go
if (ElementNumber == 1) // are we at the second to last element??
eStart = eEnd + 1; // mark it.
}
if (!(*eEnd)) break;
++eEnd; // always increment
}
if (ElementNumber || (eEnd - eStart > 32) || (eEnd - eStart == 0)) // If n > 0 or the element is too big or there is no element
return false; // then croak.
Result[0] = (char)(eEnd - eStart); // Move the substring into the Result
BlockMove ((Ptr) eStart, (Ptr) (Result + 1),Result[0]);
return true;
}
//-----------------------------------
OSErr CFileMgr::FSSpecFromPathname(const char* inPathNamePtr, FSSpec* outSpec)
// FSSpecFromPathname reverses PathNameFromFSSpec.
// It returns a FSSpec given a c string which is a mac pathname.
//-----------------------------------
{
// Simplify this routine to use FSMakeFSSpec if length < 255. Otherwise use the MoreFiles
// routine FSpLocationFromFullPath, which allocates memory, to handle longer pathnames.
if (strlen(inPathNamePtr) < 255)
return ::FSMakeFSSpec(0, 0, CStr255(inPathNamePtr), outSpec);
return FSpLocationFromFullPath(strlen(inPathNamePtr), inPathNamePtr, outSpec);
} // CFileMgr::FSSpecFromPathname
// Changes the creator/file
OSErr CFileMgr::SetFileTypeCreator(OSType creator, OSType type, const FSSpec * fileSpec)
{
FInfo info;
OSErr err = ::FSpGetFInfo (fileSpec, &info);
if (err != noErr)
return err;
info.fdCreator = creator;
info.fdType = type;
err = ::FSpSetFInfo (fileSpec, &info);
return err;
}
// Set or clear a Finder flag
OSErr CFileMgr::SetFileFinderFlag(const FSSpec& fileSpec, Uint16 flagMask, Uint8 value)
{
FInfo info;
OSErr err = ::FSpGetFInfo (&fileSpec, &info);
if (err != noErr)
return err;
if (value) //set the bit
info.fdFlags |= flagMask;
else //clear the bit
info.fdFlags &= ~flagMask;
err = ::FSpSetFInfo (&fileSpec, &info);
return err;
}
//-----------------------------------
Boolean CFileMgr::IsFolder(const FSSpec& spec)
//-----------------------------------
{
CStr31 name = spec.name;
CInfoPBRec pb;
DirInfo *dipb=(DirInfo *)&pb;
pb.hFileInfo.ioNamePtr = name;
pb.hFileInfo.ioVRefNum = spec.vRefNum;
pb.hFileInfo.ioDirID = spec.parID;
pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */
OSErr err = PBGetCatInfoSync(&pb);
if (err != noErr)
return FALSE;
if (dipb->ioFlAttrib & 0x10)
return TRUE;
// else
return FALSE;
}
//-----------------------------------
OSErr CFileMgr::FindAppOnVolume(OSType sig,
short vRefNum,
FSSpec& thefile)
// Copied from MacApp. It did not have any docs in MacApp eithere
// Ask vol's desktop db for application
//-----------------------------------
{
DTPBRec pb;
OSErr err;
pb.ioCompletion = NULL;
pb.ioVRefNum = vRefNum;
pb.ioNamePtr = NULL;
if ((err = PBDTGetPath(&pb)) != noErr)
return err; // Puts DT refnum into pb.ioDTRefNum
short refNum = pb.ioDTRefNum;
pb.ioCompletion = NULL;
pb.ioDTRefNum = refNum;
pb.ioIndex = 0;
pb.ioFileCreator = sig;
pb.ioNamePtr = (StringPtr) thefile.name;
err = PBDTGetAPPLSync(&pb); // Find it!
if( err == fnfErr )
err = afpItemNotFound; // Bug in PBDTGetAPPL
if( err )
return err; // Returns afpItemNotFound if app wasn't found.
thefile.vRefNum = vRefNum;
thefile.parID = pb.ioAPPLParID;
return err;
}
//-----------------------------------
OSErr CFileMgr::FindApplication(OSType sig, FSSpec& file)
// Finds application info given a file, based on file's creator
// Copied almost exactly from LaunchBySignature volume search
//-----------------------------------
{
short sysVRefNum;
OSErr err = CFileMgr::GetSysVolume(sysVRefNum);
if (err)
return err; // Find boot volume
short vRefNum = sysVRefNum; // Start search with boot volume
short index = 0;
do {
if (index == 0 || vRefNum != sysVRefNum) {
Boolean hasDesktopDB;
err = CFileMgr::VolHasDesktopDB(vRefNum, hasDesktopDB);
if (err)
return err;
if (hasDesktopDB) {
// If volume has a desktop DB,
err = FindAppOnVolume(sig, vRefNum, file); // ask it to find app
if (err == noErr)
return err;
// else if (err != afpItemNotFound) on broken file systems, the error returned might be spurious
// return err;
}
}
err = CFileMgr::GetIndVolume(++index, vRefNum); // Else go to next volume
} while (err != nsvErr); // Keep going until we run out of vols
if( err==nsvErr || err==afpItemNotFound )
err= fnfErr; // File not found on any volume
return err;
}
//-----------------------------------
void SwapSlashColon(char * s)
// Swaps ':' with '/'
//-----------------------------------
{
while ( *s != 0)
{
if (*s == '/')
*s++ = ':';
else if (*s == ':')
*s++ = '/';
else
*s++;
}
}
//-----------------------------------
char* CFileMgr::EncodeMacPath( char* inPath, Boolean prependSlash )
// Transforms Macintosh style path into Unix one
// Method: Swap ':' and '/', hex escape the result
//-----------------------------------
{
if (inPath == NULL)
return NULL;
int pathSize = XP_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 = NULL;
char * finalPath = NULL;
if (prependSlash)
{
newPath = (char*) XP_ALLOC(pathSize + 2);
newPath[0] = ':'; // It will be converted to '/'
XP_MEMCPY(&newPath[1], inPath, pathSize + 1);
}
else
newPath = XP_STRDUP(inPath);
if (newPath)
{
SwapSlashColon( newPath );
finalPath = NET_Escape(newPath, URL_PATH);
XP_FREE(newPath);
}
XP_FREE( inPath );
return finalPath;
} // CFileMgr::EncodeMacPath
//-----------------------------------
char * CFileMgr::GetURLFromFileSpec(const FSSpec& inSpec)
//-----------------------------------
/* GetURLFromFileSpec generates a local file URL given a file spec.
Requirements:
Unix-style file name (':' is replaced with '/')
url looks like file:///<path>
<path> is mac path, where all reserved characters (= | ; | / | # | ? | space)
have been escaped. (except :).
':' is then changed to '/' for compatibility with UNIX style file names.
Input:
Valid FSSpec
Method:
Generate a full path name
Notes:
This does not guarantee that the filename will work. We may still need to
munge it later (if for example such a filename already existed)
*/
{
char * path = PathNameFromFSSpec( inSpec, TRUE );
char * unixPath = EncodeMacPath(path);
char * finalPath = (char*)XP_ALLOC(strlen(unixPath) + 8 + 1); // file:///<path>0
if ( finalPath == NULL )
return NULL;
finalPath[0] = 0;
if ( unixPath == NULL )
return NULL;
strcat(finalPath, "file://");
strcat(finalPath, unixPath);
XP_FREE(unixPath);
return finalPath;
}
//-----------------------------------
char* CFileMgr::MacPathFromUnixPath(const char* unixPath)
//-----------------------------------
{
// Relying on the fact that the unix path is always longer than the mac path:
size_t len = XP_STRLEN(unixPath);
char* result = (char*)XP_ALLOC(len + 2); // ... but allow for the initial colon in a partial name
if (result)
{
char* dst = result;
const char* src = unixPath;
if (*src == '/') // <20> full path
src++;
else if (strchr(src, '/')) // <20> partial path, and not just a leaf name
*dst++ = ':';
XP_STRCPY(dst, src);
NET_UnEscape(dst); // Hex Decode
SwapSlashColon(dst);
}
return result;
} // CFileMgr::MacPathFromUnixPath
//-----------------------------------
OSErr CFileMgr::FSSpecFromLocalUnixPath(
const char * unixPath,
FSSpec * inOutSpec,
Boolean resolveAlias)
// 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 == NULL)
return badFidErr;
char* macPath = MacPathFromUnixPath(unixPath);
if (!macPath)
return memFullErr;
OSErr err = noErr;
if (*unixPath == '/' /*full path*/)
err = FSSpecFromPathname(macPath, inOutSpec);
else
err = ::FSMakeFSSpec(inOutSpec->vRefNum, inOutSpec->parID, CStr255(macPath), inOutSpec);
if (err == fnfErr)
err = noErr;
Boolean dummy, dummy2;
if (err == noErr && resolveAlias) // Added
err = ::ResolveAliasFile(inOutSpec,TRUE,&dummy,&dummy2);
XP_FREE(macPath);
Assert_(err==noErr||err==fnfErr||err==dirNFErr||err==nsvErr);
return err;
} // CFileMgr::FSSpecFromLocalUnixPath
Boolean CFileMgr::FileExists( const FSSpec& fsSpec )
{
FSSpec temp;
OSErr err;
err = FSMakeFSSpec( fsSpec.vRefNum, fsSpec.parID, fsSpec.name, &temp );
return ( err == noErr );
}
Boolean CFileMgr::FileHasDataFork(const FSSpec& fsSpec )
{
CStr31 name = fsSpec.name;
CInfoPBRec pb;
pb.hFileInfo.ioNamePtr = name;
pb.hFileInfo.ioVRefNum = fsSpec.vRefNum;
pb.hFileInfo.ioDirID = fsSpec.parID;
pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */
OSErr err = PBGetCatInfoSync(&pb);
if (err != noErr)
return FALSE;
else if (pb.hFileInfo.ioFlLgLen <= 0)
return FALSE;
else
return TRUE;
}
// Does the file have resource fork?
Boolean CFileMgr::FileHasResourceFork(const FSSpec& fsSpec )
{
CStr31 name = fsSpec.name;
CInfoPBRec pb;
pb.hFileInfo.ioNamePtr = name;
pb.hFileInfo.ioVRefNum = fsSpec.vRefNum;
pb.hFileInfo.ioDirID = fsSpec.parID;
pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */
OSErr err = PBGetCatInfoSync(&pb);
if (err != noErr)
return FALSE;
else if (pb.hFileInfo.ioFlRLgLen <= 0)
return FALSE;
else
return TRUE;
}
void CFileMgr::CopyFSSpec(const FSSpec & srcSpec, FSSpec & destSpec)
{
destSpec.vRefNum = srcSpec.vRefNum;
destSpec.parID = srcSpec.parID;
*(CStr31*)&destSpec.name = srcSpec.name;
}
// delete any items we may have left in the "Temporary Items" folder.
void CFileMgr::DeleteCommTemporaryItems()
{
FSSpec commFolderSpec = {0}; // prototype filespec *within* the temp items folder
long tempItemsDirID;
short vRefNum;
OSErr err;
err = ::FindFolder(kOnSystemDisk, kTemporaryFolderType, kDontCreateFolder,
&vRefNum, &tempItemsDirID);
if (err != noErr) return; //no temp items folder; do nothing
if ( (noErr == ::FSMakeFSSpec(vRefNum, tempItemsDirID, "\pnscomm40", &commFolderSpec) ) && IsFolder(commFolderSpec) )
{
// blow it away. This is recursive.
err = DeleteFolder(commFolderSpec);
}
Assert_(err == noErr);
}
OSErr CFileMgr::GetFolderID(FSSpec& folderSpec, long& dirID)
{
CInfoPBRec pb;
pb.hFileInfo.ioNamePtr = folderSpec.name;
pb.hFileInfo.ioVRefNum = folderSpec.vRefNum;
pb.hFileInfo.ioDirID = folderSpec.parID;
pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */
OSErr err = PBGetCatInfoSync(&pb);
if (err == noErr)
dirID = pb.dirInfo.ioDrDirID;
return err;
}
OSErr CFileMgr::DeleteFolder(const FSSpec& folderSpec)
{
CFileIter iter(folderSpec);
FSSpec nextFile;
FInfo finderInfo;
Boolean isFolder;
OSErr err = noErr;
OSErr storedErr = noErr;
if (FileExists(folderSpec) == false)
return noErr;
// Delete all the items in the folder
while ( iter.Next(nextFile, finderInfo, isFolder) )
{
if (isFolder)
err = DeleteFolder(nextFile);
else
err = ::FSpDelete(&nextFile);
if (err != noErr)
storedErr = err;
}
// Delete the folder
err = FSpDelete(&folderSpec);
if (err != noErr)
storedErr = err;
return storedErr;
}
CFileIter::CFileIter(const FSSpec &folderSpec)
{
fDir = folderSpec;
fIndex = 0;
CInfoPBRec pb;
pb.hFileInfo.ioNamePtr = (unsigned char*)folderSpec.name; // cast avoids warning
pb.hFileInfo.ioVRefNum = folderSpec.vRefNum;
pb.hFileInfo.ioDirID = folderSpec.parID;
pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */
OSErr err = PBGetCatInfoSync(&pb);
if (err == noErr)
{
fIndex = pb.dirInfo.ioDrNmFls;
fDir.parID = pb.dirInfo.ioDrDirID;
}
}
Boolean CFileIter::Next(FSSpec &nextFile, FInfo& finderInfo, Boolean& folder)
{
tryagain:
if (fIndex <= 0)
return FALSE;
CInfoPBRec cipb;
DirInfo *dipb=(DirInfo *)&cipb;
dipb->ioCompletion = NULL;
dipb->ioFDirIndex = fIndex--;
dipb->ioVRefNum = fDir.vRefNum;
dipb->ioDrDirID = fDir.parID;
dipb->ioNamePtr = (StringPtr)&nextFile.name;
OSErr err = PBGetCatInfoSync (&cipb);
if (err != noErr)
goto tryagain; // Go backwards, skip the directories
if ((dipb->ioFlAttrib & 0x10) != 0)
folder = TRUE;
else
folder = FALSE;
nextFile.vRefNum = fDir.vRefNum;
nextFile.parID = fDir.parID;
Boolean dummy,wasAliased;
err = ::ResolveAliasFile(&nextFile,TRUE,&dummy,&wasAliased);
if ((err == noErr) && wasAliased)
{ // Need to get info again
CInfoPBRec pb;
pb.hFileInfo.ioNamePtr = (StringPtr)&nextFile.name;
pb.hFileInfo.ioVRefNum = nextFile.vRefNum;
pb.hFileInfo.ioDirID = nextFile.parID;
pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */
err = PBGetCatInfoSync(&pb);
finderInfo = pb.hFileInfo.ioFlFndrInfo;
if ((pb.hFileInfo.ioFlAttrib & 0x10) != 0)
folder = TRUE;
else
folder = FALSE;
}
else
finderInfo = cipb.hFileInfo.ioFlFndrInfo;
return TRUE;
}
OSErr CFileMgr::UpdateFinderDisplay(const FSSpec& inSpec)
{
AppleEvent theEvent;
AliasHandle theAlias = NULL;
OSErr theErr = noErr;
try
{
theErr = ::NewAlias(NULL, &inSpec, &theAlias);
ThrowIfOSErr_(theErr);
FSSpec theFinderSpec;
ProcessSerialNumber theFinderPSN;
theErr = FindProcessBySignature('MACS', 'FNDR', theFinderPSN, &theFinderSpec);
ThrowIfOSErr_(theErr);
StHandleLocker theAliasLocker((Handle)theAlias);
//LAEStream theEventStream('fndr', 'fupd', typeProcessSerialNumber, &theFinderPSN, sizeof( ProcessSerialNumber ));
//theEventStream.WriteKeyDesc(keyDirectObject, typeAlias, *theAlias, ::GetHandleSize((Handle)theAlias));
//theEventStream.Close(&theEvent);
//UAppleEventsMgr::SendAppleEvent(theEvent);
}
catch(...)
{
if (theAlias != NULL)
::DisposeHandle((Handle)theAlias);
}
return theErr;
}
void CFileMgr::MakeAliasFile(const FSSpec& aliasSpec, const FSSpec& target)
{
AliasHandle alias;
OSErr err = NewAlias( nil, &target, &alias );
ThrowIfOSErr_(err);
LFile aliasFile(aliasSpec);
aliasFile.CreateNewFile('MACS', kContainerFolderAliasType);
aliasFile.OpenResourceFork(fsRdWrPerm);
AddResource((Handle) alias, 'alis', 0, nil);
FInfo info;
err = ::FSpGetFInfo(&aliasSpec, &info);
ThrowIfOSErr_(err);
info.fdFlags |= kIsAlias;
err = ::FSpSetFInfo(&aliasSpec, &info);
ThrowIfOSErr_(err);
}
OSErr CFileMgr::CopyFile(const FSSpec& srcSpec, const FSSpec &dstDirSpec, const CStr255 &copyName)
{
return FSpFileCopy(&srcSpec, &dstDirSpec, (StringPtr)&copyName, nil, 0, true);
}
//-----------------------------------------------------------------------------
// More Utilities
//-----------------------------------------------------------------------------
void WriteCString (LStream * s, const char * c)
{
s->WriteData (c, strlen(c));
}
void
WriteChar (LStream * s, char c)
{
s->WriteData (&c, 1);
}
//
// fe_FileNameFromContext
//
// Suggest a name for this document based on its title or it's URL content type. If there
// are colons, be sure to swa[ them with /
//
void fe_FileNameFromContext( MWContext* context, const char* url, CStr31& defaultName )
{
#ifdef MOZ_MAIL_NEWS
char* urlString;
urlString = MimeGuessURLContentName( context, url );
if ( urlString )
{
SwapSlashColon( urlString );
defaultName = urlString;
XP_FREE( urlString );
}
else
#endif // MOZ_MAIL_NEWS
{
// If we have a context title and it's not an ftp URL
if (context && context->title && context->title[0] &&
strncasecomp(url, "ftp://", 6))
{
defaultName = context->title;
}
else
{
defaultName = CFileMgr::FileNameFromURL( url );
}
}
// now make sure the resulting name doesn't have any colons.
char buffer[50];
short nextInsertPos = 0;
for ( short loop = 1; loop <= defaultName.Length(); loop++ )
{
char currChar = defaultName[loop];
if ( currChar != ':' )
{
buffer[nextInsertPos] = currChar;
nextInsertPos++;
}
} // for each character
buffer[nextInsertPos] = NULL; // null terminate the new name
defaultName = buffer; // make the string w/out colons the default
} // fe_FileNameFromContext
OSErr FSSpecFromPathname_CWrapper(char * path, FSSpec * outSpec)
{
return (CFileMgr::FSSpecFromPathname(path, outSpec));
}