/* -*- 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 // XP #ifndef _XP_H_ #include "xp.h" #endif #include "PascalString.h" // MacOS #include #include #include #include //#include #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); // ¥¥ 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); } // ¥¥ 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: : 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; // ¥ get the starting point const char* nameStart = strrchr( urlString, '/' ); // ¥Êfilename starts *after* the colon if ( nameStart && ( strlen( nameStart ) > 1) ) nameStart++; // ¥ no colon, must be simple path, go after the hostname else { nameStart = strchr( urlString, ':' ); // ¥Êfilename starts *after* the colon if ( nameStart ) nameStart++; // ¥Êno host, assume it's a simple local URL else nameStart = urlString; } // ¥Ê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; // ¥Êtake out any : characters as well if ( current == ':' ) current = '¥'; 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: ¥ Preserve filename as much as possible, including extension, if any (even though this doesn't quite make sense on the Mac). ¥ 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:/// 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:///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 == '/') // ¥ full path src++; else if (strchr(src, '/')) // ¥ 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 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 ©Name) { return FSpFileCopy(&srcSpec, &dstDirSpec, (StringPtr)©Name, 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)); }