/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Netscape Portable Runtime (NSPR).
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998-2000
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <string.h>
#include <Types.h>
#include <Files.h>
#include <Devices.h>
#include <Folders.h>
#include <Errors.h>
#include <Resources.h>
#include <Processes.h>
#include <TextUtils.h>
#include <fcntl.h>
#include "FullPath.h" /* MoreFiles */
#include "primpl.h"
#include "MacErrorHandling.h"
#include "mdmac.h"
#include "macio.h"
/* forward declarations */
extern unsigned long gJanuaryFirst1970Seconds;
extern void WaitOnThisThread(PRThread *thread, PRIntervalTime timeout);
extern void DoneWaitingOnThisThread(PRThread *thread);
extern void AsyncNotify(PRThread *thread);
/* PB for Read and Write */
struct ExtendedParamBlock {
/* PB must be first so that the file system can get the right data. */
ParamBlockRec pb;
PRThread *thread;
};
typedef struct ExtendedParamBlock ExtendedParamBlock;
/* XXX Not done yet for 68K */
/* I/O completion routne for _MD_READ and _MD_WRITE */
static void AsyncIOCompletion (ExtendedParamBlock *pbAsyncPtr)
{
_PRCPU *cpu = _PR_MD_CURRENT_CPU();
PRThread *thread = pbAsyncPtr->thread;
PRIntn is;
if (_PR_MD_GET_INTSOFF()) {
thread->md.missedIONotify = PR_TRUE;
cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
} else {
_PR_INTSOFF(is);
thread->md.osErrCode = noErr;
DoneWaitingOnThisThread(thread);
_PR_FAST_INTSON(is);
}
SignalIdleSemaphore();
}
void _MD_SetError(OSErr oserror)
{
PRErrorCode code;
switch (oserror) {
case memFullErr:
code = PR_OUT_OF_MEMORY_ERROR;
break;
case fnfErr:
code = PR_FILE_NOT_FOUND_ERROR;
break;
case dupFNErr:
code = PR_FILE_EXISTS_ERROR;
break;
case ioErr:
code = PR_IO_ERROR;
break;
case nsvErr:
case wrgVolTypErr:
code = PR_INVALID_DEVICE_STATE_ERROR;
break;
case bdNamErr:
case fsRnErr:
code = PR_NAME_TOO_LONG_ERROR;
break;
case tmfoErr:
code = PR_INSUFFICIENT_RESOURCES_ERROR;
break;
case opWrErr:
case wrPermErr:
case permErr:
case afpAccessDenied:
code = PR_NO_ACCESS_RIGHTS_ERROR;
break;
case afpObjectTypeErr:
code = PR_DIRECTORY_LOOKUP_ERROR;
break;
case wPrErr:
case vLckdErr:
code = PR_DEVICE_IS_LOCKED_ERROR;
break;
case fLckdErr:
code = PR_FILE_IS_LOCKED_ERROR;
break;
case dirNFErr:
code = PR_NOT_DIRECTORY_ERROR;
break;
case dirFulErr:
code = PR_MAX_DIRECTORY_ENTRIES_ERROR;
break;
case dskFulErr:
code = PR_NO_DEVICE_SPACE_ERROR;
break;
case rfNumErr:
case fnOpnErr:
code = PR_BAD_DESCRIPTOR_ERROR;
break;
case eofErr:
code = PR_END_OF_FILE_ERROR;
break;
case posErr:
case gfpErr:
code = PR_FILE_SEEK_ERROR;
break;
case fBsyErr:
code = PR_FILE_IS_BUSY_ERROR;
break;
case extFSErr:
code = PR_REMOTE_FILE_ERROR;
break;
case abortErr:
code = PR_PENDING_INTERRUPT_ERROR;
break;
case paramErr:
code = PR_INVALID_ARGUMENT_ERROR;
break;
case unimpErr:
code = PR_NOT_IMPLEMENTED_ERROR;
break;
}
PR_SetError(code, oserror);
}
void _MD_IOInterrupt(void)
{
PRCList *qp;
PRThread *thread, *me = _PR_MD_CURRENT_THREAD();
PR_ASSERT(_PR_MD_GET_INTSOFF() != 0);
_PR_SLEEPQ_LOCK(me->cpu);
qp = _PR_PAUSEQ(me->cpu).next;
while (qp != &_PR_PAUSEQ(me->cpu)) {
thread = _PR_THREAD_PTR(qp);
PR_ASSERT(thread->flags & _PR_ON_PAUSEQ);
qp = qp->next;
if (thread->md.missedIONotify) {
thread->md.missedIONotify = PR_FALSE;
DoneWaitingOnThisThread(thread);
}
if (thread->md.missedAsyncNotify) {
thread->md.missedAsyncNotify = PR_FALSE;
AsyncNotify(thread);
}
}
qp = _PR_SLEEPQ(me->cpu).next;
while (qp != &_PR_SLEEPQ(me->cpu)) {
thread = _PR_THREAD_PTR(qp);
PR_ASSERT(thread->flags & _PR_ON_SLEEPQ);
qp = qp->next;
if (thread->md.missedIONotify) {
thread->md.missedIONotify = PR_FALSE;
DoneWaitingOnThisThread(thread);
}
if (thread->md.missedAsyncNotify) {
thread->md.missedAsyncNotify = PR_FALSE;
AsyncNotify(thread);
}
}
_PR_SLEEPQ_UNLOCK(thread->cpu);
}
/*
** All PR_read and PR_Write calls are synchronous from caller's perspective.
** They are internally made asynchronous calls. This gives cpu to other
** user threads while the async io is in progress.
*/
PRInt32 ReadWriteProc(PRFileDesc *fd, void *buf, PRUint32 bytes, IOOperation op)
{
PRInt32 refNum = fd->secret->md.osfd;
OSErr err;
ExtendedParamBlock pbAsync;
PRThread *me = _PR_MD_CURRENT_THREAD();
_PRCPU *cpu = _PR_MD_CURRENT_CPU();
/* quick hack to allow PR_fprintf, etc to work with stderr, stdin, stdout */
/* note, if a user chooses "seek" or the like as an operation in another function */
/* this will not work */
if (refNum >= 0 && refNum < 3)
{
switch (refNum)
{
case 0:
/* stdin - not on a Mac for now */
err = paramErr;
goto ErrorExit;
break;
case 1: /* stdout */
case 2: /* stderr */
puts(buf);
break;
}
return (bytes);
}
else
{
static IOCompletionUPP sCompletionUPP = NULL;
PRBool doingAsync = PR_FALSE;
/* allocate the callback Universal Procedure Pointer (UPP). This actually allocates
a 32 byte Ptr in the heap, so only do this once
*/
if (!sCompletionUPP)
sCompletionUPP = NewIOCompletionUPP((IOCompletionProcPtr)&AsyncIOCompletion);
/* grab the thread so we know which one to post to at completion */
pbAsync.thread = me;
pbAsync.pb.ioParam.ioCompletion = sCompletionUPP;
pbAsync.pb.ioParam.ioResult = noErr;
pbAsync.pb.ioParam.ioRefNum = refNum;
pbAsync.pb.ioParam.ioBuffer = buf;
pbAsync.pb.ioParam.ioReqCount = bytes;
pbAsync.pb.ioParam.ioPosMode = fsAtMark;
pbAsync.pb.ioParam.ioPosOffset = 0;
/*
** Issue the async read call and wait for the io semaphore associated
** with this thread.
** Async file system calls *never* return error values, so ignore their
** results (see <http://developer.apple.com/technotes/fl/fl_515.html>);
** the completion routine is always called.
*/
me->io_fd = refNum;
me->md.osErrCode = noErr;
if (op == READ_ASYNC)
{
/*
** Skanky optimization so that reads < 20K are actually done synchronously
** to optimize performance on small reads (e.g. registry reads on startup)
*/
if ( bytes > 20480L )
{
doingAsync = PR_TRUE;
me->io_pending = PR_TRUE;
(void)PBReadAsync(&pbAsync.pb);
}
else
{
pbAsync.pb.ioParam.ioCompletion = NULL;
me->io_pending = PR_FALSE;
err = PBReadSync(&pbAsync.pb);
if (err != noErr && err != eofErr)
goto ErrorExit;
}
}
else
{
doingAsync = PR_TRUE;
me->io_pending = PR_TRUE;
/* writes are currently always async */
(void)PBWriteAsync(&pbAsync.pb);
}
if (doingAsync) {
WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);
}
}
err = me->md.osErrCode;
if (err != noErr)
goto ErrorExit;
err = pbAsync.pb.ioParam.ioResult;
if (err != noErr && err != eofErr)
goto ErrorExit;
return pbAsync.pb.ioParam.ioActCount;
ErrorExit:
me->md.osErrCode = err;
_MD_SetError(err);
return -1;
}
/*
Special WriteSyncProc for logging only. IO occurs synchronously. Otherwise,
logging internal to NSPR causes ReadWriteProc above to recurse on PR_WaitSem logging.
*/
PRInt32 WriteSyncProc(PRFileDesc *fd, void *buf, PRUint32 bytes)
{
PRInt32 refNum = fd->secret->md.osfd;
OSErr err;
ParamBlockRec pb;
PRThread *me = _PR_MD_CURRENT_THREAD();
if (refNum >= 0 && refNum < 3)
{
PR_ASSERT(FALSE); /* writing to these is hazardous to a Mac's health (refNum 2 is the system file) */
err = paramErr;
goto ErrorExit;
}
pb.ioParam.ioCompletion = NULL;
pb.ioParam.ioResult = noErr;
pb.ioParam.ioRefNum = refNum;
pb.ioParam.ioBuffer = buf;
pb.ioParam.ioReqCount = bytes;
pb.ioParam.ioPosMode = fsAtMark;
pb.ioParam.ioPosOffset = 0;
err = PBWriteSync(&pb);
if (err != noErr)
goto ErrorExit;
else
return pb.ioParam.ioActCount;
ErrorExit:
me->md.osErrCode = err;
_MD_SetError(err);
return -1;
}
/* File I/O functions called by PR I/O routines */
PRInt32 _MD_Open(const char *path, PRIntn flags, int mode)
{
// Macintosh doesn't really have mode bits, just drop them
#pragma unused (mode)
OSErr err;
HParamBlockRec hpb;
ParamBlockRec pb;
char *macFileName = NULL;
Str255 pascalName;
PRInt8 perm;
err = ConvertUnixPathToMacPath(path, &macFileName);
if (err != noErr)
goto ErrorExit;
hpb.ioParam.ioCompletion = NULL;
PStrFromCStr(macFileName, pascalName);
PR_DELETE(macFileName);
hpb.ioParam.ioNamePtr = pascalName;
hpb.ioParam.ioVRefNum = 0;
hpb.ioParam.ioVersNum = 0;
hpb.fileParam.ioDirID = 0;
if (flags & PR_RDWR)
perm = fsRdWrPerm;
else if (flags & PR_WRONLY)
perm = fsWrPerm;
else
perm = fsRdPerm;
hpb.ioParam.ioPermssn = perm;
if (flags & PR_CREATE_FILE) {
err = PBHCreateSync(&hpb);
/* If opening with the PR_EXCL flag the existence of the file prior to opening is an error */
if ((flags & PR_EXCL) && (err == dupFNErr)) {
err = PR_FILE_EXISTS_ERROR;
goto ErrorExit;
}
if ((err != noErr) && (err != dupFNErr))
goto ErrorExit;
}
err = PBHOpenDFSync(&hpb);
if (err != noErr)
goto ErrorExit;
if (flags & PR_TRUNCATE) {
pb.ioParam.ioCompletion = NULL;
pb.ioParam.ioRefNum = hpb.ioParam.ioRefNum;
pb.ioParam.ioMisc = NULL;
err = PBSetEOFSync(&pb);
if (err != noErr)
goto ErrorExit;
} else if (flags & PR_APPEND) {
pb.ioParam.ioCompletion = NULL;
pb.ioParam.ioRefNum = hpb.ioParam.ioRefNum;
pb.ioParam.ioPosMode = fsFromLEOF;
pb.ioParam.ioPosOffset = 0;
err = PBSetFPosSync(&pb);
if (err != noErr)
goto ErrorExit;
}
return hpb.ioParam.ioRefNum;
ErrorExit:
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
_MD_SetError(err);
return -1;
}
/* _MD_CLOSE_FILE, _MD_READ, _MD_WRITE, _MD_GET_FILE_ERROR are defined in _macos.h */
PROffset32 _MD_LSeek(PRFileDesc *fd, PROffset32 offset, PRSeekWhence how)
{
PRInt32 refNum = fd->secret->md.osfd;
OSErr err = noErr;
long curPos, endPos;
/* compute new mark */
switch (how) {
case PR_SEEK_SET:
endPos = offset;
break;
case PR_SEEK_CUR:
err = GetFPos(refNum, &curPos);
endPos = curPos + offset;
break;
case PR_SEEK_END:
err = GetEOF(refNum, &curPos);
endPos = curPos + offset;
break;
default:
err = paramErr;
break;
}
/* set the new mark and extend the file if seeking beyond current EOF */
/* making sure to set the mark after any required extend */
if (err == noErr) {
err = SetFPos(refNum, fsFromStart, endPos);
if (err == eofErr) {
err = SetEOF(refNum, endPos);
if (err == noErr) {
err = SetFPos(refNum, fsFromStart, endPos);
}
}
}
if (err == noErr) {
return endPos;
} else {
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
_MD_SetError(err);
return -1;
}
}
PRInt32 _MD_FSync(PRFileDesc *fd)
{
PRInt32 refNum = fd->secret->md.osfd;
OSErr err;
ParamBlockRec pb;
pb.ioParam.ioCompletion = NULL;
pb.ioParam.ioRefNum = refNum;
err = PBFlushFileSync(&pb);
if (err != noErr)
goto ErrorExit;
return 0;
ErrorExit:
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
_MD_SetError(err);
return -1;
}
#include "plstr.h"
PRStatus _MD_OpenDir(_MDDir *mdDir,const char *name)
{
// Emulate the Unix opendir() routine.
OSErr err;
CInfoPBRec pb;
char *macDirName = NULL;
char *position = NULL;
char volumeName[32];
Str255 pascalName;
// Get the Macintosh path
err = ConvertUnixPathToMacPath(name, &macDirName);
if (err != noErr)
goto ErrorExit;
// Get the vRefNum
position = PL_strchr(macDirName, PR_PATH_SEPARATOR);
if ((position == macDirName) || (position == NULL))
mdDir->ioVRefNum = 0; // Use application relative searching
else {
memset(volumeName, 0, sizeof(volumeName));
strncpy(volumeName, macDirName, position-macDirName);
mdDir->ioVRefNum = GetVolumeRefNumFromName(volumeName);
}
// Get info about the object.
PStrFromCStr(macDirName, pascalName);
PR_DELETE(macDirName);
pb.dirInfo.ioNamePtr = pascalName;
pb.dirInfo.ioVRefNum = mdDir->ioVRefNum;
pb.dirInfo.ioDrDirID = 0;
pb.dirInfo.ioFDirIndex = 0;
err = PBGetCatInfoSync(&pb);
if (err != noErr)
goto ErrorExit;
// Are we dealing with a directory?
if ((pb.dirInfo.ioFlAttrib & ioDirMask) == 0) {
err = dirNFErr;
goto ErrorExit;
}
/* This is a directory, store away the pertinent information.
** We post increment. I.e. index is always the nth. item we
** should get on the next call
*/
mdDir->ioDirID = pb.dirInfo.ioDrDirID;
mdDir->currentEntryName = NULL;
mdDir->ioFDirIndex = 1;
return PR_SUCCESS;
ErrorExit:
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
_MD_SetError(err);
return PR_FAILURE;
}
char *_MD_ReadDir(_MDDir *mdDir, PRIntn flags)
{
// Emulate the Unix readdir() routine.
// Mac doesnÕt have the concept of .(PR_SKIP_DOT) & ..(PR_SKIP_DOT_DOT)
OSErr err;
CInfoPBRec pb;
char *returnedCStr;
Str255 pascalName = "\p";
PRBool foundEntry;
PR_ASSERT(mdDir != NULL);
do {
// Release the last name read.
PR_DELETE(mdDir->currentEntryName);
mdDir->currentEntryName = NULL;
// WeÕve got all the info we need, just get info about this guy.
pb.hFileInfo.ioNamePtr = pascalName;
pb.hFileInfo.ioVRefNum = mdDir->ioVRefNum;
pb.hFileInfo.ioFDirIndex = mdDir->ioFDirIndex;
pb.hFileInfo.ioDirID = mdDir->ioDirID;
err = PBGetCatInfoSync(&pb);
if (err != noErr)
goto ErrorExit;
// Convert the Pascal string to a C string (actual allocation occurs in CStrFromPStr)
CStrFromPStr(pascalName, &returnedCStr);
mdDir->currentEntryName = returnedCStr;
mdDir->ioFDirIndex++;
// If it is not a hidden file and the flags did not specify skipping, we are done.
if ((flags & PR_SKIP_HIDDEN) && (pb.hFileInfo.ioFlFndrInfo.fdFlags & fInvisible))
foundEntry = PR_FALSE;
else
foundEntry = PR_TRUE;
} while (!foundEntry);
return (mdDir->currentEntryName);
ErrorExit:
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
_MD_SetError(err);
return NULL;
}
void _MD_CloseDir(_MDDir *mdDir)
{
// Emulate the Unix closedir() routine
PR_DELETE(mdDir->currentEntryName);
}
PRInt32 _MD_MkDir(char *unixPath, PRIntn mode)
{
HFileParam fpb;
Str255 pascalName = "\p";
char *cMacPath = NULL;
OSErr err;
#pragma unused (mode) // Mode is ignored on the Mac
if (unixPath) {
err = ConvertUnixPathToMacPath(unixPath, &cMacPath);
if (err != noErr)
goto ErrorExit;
PStrFromCStr(cMacPath, pascalName);
PR_DELETE(cMacPath);
fpb.ioNamePtr = pascalName;
fpb.ioVRefNum = 0;
fpb.ioDirID = 0L;
err = PBDirCreateSync((HParmBlkPtr)&fpb);
if (err != noErr)
goto ErrorExit;
}
return 0;
ErrorExit:
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
_MD_SetError(err);
return -1;
}
PRInt32 _MD_Delete(char *unixPath)
{
HFileParam fpb;
Str255 pascalName = "\p";
char *cMacPath = NULL;
OSErr err;
if (unixPath) {
err = ConvertUnixPathToMacPath(unixPath, &cMacPath);
if (err != noErr)
goto ErrorExit;
PStrFromCStr(cMacPath, pascalName);
PR_DELETE(cMacPath);
fpb.ioNamePtr = pascalName;
fpb.ioVRefNum = 0;
fpb.ioDirID = 0L;
err = PBHDeleteSync((HParmBlkPtr)&fpb);
if (err != noErr)
goto ErrorExit;
}
return 0;
ErrorExit:
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
_MD_SetError(err);
return -1;
}
PRInt32 _MD_Rename(char *fromUnixPath, char *toUnixPath)
{
OSErr err;
FSSpec fromSpec;
FSSpec toSpec;
FSSpec destDirSpec;
FSSpec beforeRenameSpec;
if (fromUnixPath && toUnixPath) {
err = ConvertUnixPathToFSSpec(fromUnixPath, &fromSpec);
if (err != noErr)
goto ErrorExit;
err = ConvertUnixPathToFSSpec(toUnixPath, &toSpec);
if (err != noErr && err != fnfErr)
goto ErrorExit;
/* make an FSSpec for the destination directory */
err = FSMakeFSSpec(toSpec.vRefNum, toSpec.parID, nil, &destDirSpec);
if (err != noErr) /* parent directory must exist */
goto ErrorExit;
// move it to the directory specified
err = FSpCatMove(&fromSpec, &destDirSpec);
if (err != noErr)
goto ErrorExit;
// make a new FSSpec for the file or directory in its new location
err = FSMakeFSSpec(toSpec.vRefNum, toSpec.parID, fromSpec.name, &beforeRenameSpec);
if (err != noErr)
goto ErrorExit;
// rename the file or directory
err = FSpRename(&beforeRenameSpec, toSpec.name);
if (err != noErr)
goto ErrorExit;
} else {
err = paramErr;
goto ErrorExit;
}
return 0;
ErrorExit:
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
_MD_SetError(err);
return -1;
}
#define kWriteAccessAllowed (0x100)
PRInt32 _MD_Access(char *unixPath, int amode)
{
//
// Emulate the Unix access routine
//
OSErr err;
CInfoPBRec pb;
FCBPBRec fcbpb;
char *cMacPath = NULL;
Str255 pascalMacPath;
struct stat info;
// Convert to a Mac style path
err = ConvertUnixPathToMacPath(unixPath, &cMacPath);
if (err != noErr)
goto ErrorExit;
err = stat(cMacPath, &info);
if (err != noErr)
goto ErrorExit;
// If all weÕre doing is checking for the existence of the file, weÕre out of here.
// On the Mac, if a file exists, you can read from it.
// This doesnÕt handle remote AppleShare volumes. Does it need to?
if ((amode == PR_ACCESS_EXISTS) || (amode == PR_ACCESS_READ_OK)) {
goto success;
}
PStrFromCStr(cMacPath, pascalMacPath);
pb.hFileInfo.ioNamePtr = pascalMacPath;
pb.hFileInfo.ioVRefNum = info.st_dev;
pb.hFileInfo.ioDirID = 0;
pb.hFileInfo.ioFDirIndex = 0;
err = PBGetCatInfoSync(&pb);
if (err != noErr)
goto ErrorExit;
// Check out all the access permissions.
if (amode == PR_ACCESS_WRITE_OK) {
fcbpb.ioNamePtr = NULL;
fcbpb.ioVRefNum = pb.hFileInfo.ioVRefNum;
fcbpb.ioRefNum = pb.hFileInfo.ioFRefNum;
fcbpb.ioFCBIndx = 0;
err = PBGetFCBInfoSync(&fcbpb);
if (err != noErr)
goto ErrorExit;
/* Look at Inside Mac IV-180 */
if ((fcbpb.ioFCBFlags & kWriteAccessAllowed) == 0) {
err = permErr;
goto ErrorExit;
}
}
success:
PR_DELETE(cMacPath);
return 0;
ErrorExit:
if (cMacPath != NULL)
PR_DELETE(cMacPath);
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
_MD_SetError(err);
return -1;
}
PRInt32 _MD_GetFileInfo(char *unixPath, PRFileInfo *info)
{
CInfoPBRec pb;
OSErr err;
char *cMacPath = NULL;
Str255 pascalMacPath;
PRTime oneMillion, dateInMicroSeconds;
// Convert to a Mac style path
err = ConvertUnixPathToMacPath(unixPath, &cMacPath);
if (err != noErr)
goto ErrorExit;
PStrFromCStr(cMacPath, pascalMacPath);
PR_DELETE(cMacPath);
pb.hFileInfo.ioNamePtr = pascalMacPath;
pb.hFileInfo.ioVRefNum = 0;
pb.hFileInfo.ioDirID = 0;
pb.hFileInfo.ioFDirIndex = 0;
err = PBGetCatInfoSync(&pb);
if (err != noErr)
goto ErrorExit;
if (pb.hFileInfo.ioFlAttrib & ioDirMask) {
info->type = PR_FILE_DIRECTORY;
info->size = 0;
} else {
info->type = PR_FILE_FILE;
info->size = pb.hFileInfo.ioFlLgLen + pb.hFileInfo.ioFlRLgLen;
}
pb.hFileInfo.ioFlCrDat -= gJanuaryFirst1970Seconds;
LL_I2L(dateInMicroSeconds, pb.hFileInfo.ioFlCrDat);
LL_I2L(oneMillion, PR_USEC_PER_SEC);
LL_MUL(info->creationTime, oneMillion, dateInMicroSeconds);
pb.hFileInfo.ioFlMdDat -= gJanuaryFirst1970Seconds;
LL_I2L(dateInMicroSeconds, pb.hFileInfo.ioFlMdDat);
LL_MUL(info->modifyTime, oneMillion, dateInMicroSeconds);
return 0;
ErrorExit:
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
_MD_SetError(err);
return -1;
}
PRInt32 _MD_GetOpenFileInfo(const PRFileDesc *fd, PRFileInfo *info)
{
OSErr err;
FCBPBRec fcbpb;
CInfoPBRec pb;
Str255 pascalMacPath;
PRTime oneMillion, dateInMicroSeconds;
fcbpb.ioNamePtr = pascalMacPath;
fcbpb.ioVRefNum = 0;
fcbpb.ioRefNum = fd->secret->md.osfd;
fcbpb.ioFCBIndx = 0;
err = PBGetFCBInfoSync(&fcbpb);
if (err != noErr)
goto ErrorExit;
info->type = PR_FILE_FILE;
info->size = fcbpb.ioFCBEOF;
pb.hFileInfo.ioNamePtr = pascalMacPath;
pb.hFileInfo.ioVRefNum = fcbpb.ioFCBVRefNum;
pb.hFileInfo.ioDirID = fcbpb.ioFCBParID;
pb.hFileInfo.ioFDirIndex = 0;
err = PBGetCatInfoSync(&pb);
if (err != noErr)
goto ErrorExit;
pb.hFileInfo.ioFlCrDat -= gJanuaryFirst1970Seconds;
LL_I2L(dateInMicroSeconds, pb.hFileInfo.ioFlCrDat);
LL_I2L(oneMillion, PR_USEC_PER_SEC);
LL_MUL(info->creationTime, oneMillion, dateInMicroSeconds);
pb.hFileInfo.ioFlMdDat -= gJanuaryFirst1970Seconds;
LL_I2L(dateInMicroSeconds, pb.hFileInfo.ioFlMdDat);
LL_MUL(info->modifyTime, oneMillion, dateInMicroSeconds);
return 0;
ErrorExit:
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
_MD_SetError(err);
return -1;
}
PRInt32 _MD_Stat(const char *path, struct stat *buf)
{
OSErr err;
char *macFileName = NULL;
err = ConvertUnixPathToMacPath(path, &macFileName);
if (err != noErr)
goto ErrorExit;
err = stat(macFileName, buf);
if (err != noErr)
goto ErrorExit;
PR_DELETE(macFileName);
return 0;
ErrorExit:
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
_MD_SetError(err);
return -1;
}
PRStatus _MD_LockFile(PRInt32 fd)
{
OSErr err;
FCBPBRec fcbpb;
HFileParam fpb;
Str255 pascalName;
fcbpb.ioNamePtr = pascalName;
fcbpb.ioVRefNum = 0;
fcbpb.ioRefNum = fd;
fcbpb.ioFCBIndx = 0;
err = PBGetFCBInfoSync(&fcbpb);
if (err != noErr)
goto ErrorExit;
fpb.ioCompletion = NULL;
fpb.ioNamePtr = pascalName;
fpb.ioVRefNum = fcbpb.ioFCBVRefNum;
fpb.ioDirID = fcbpb.ioFCBParID;
err = PBHSetFLockSync((HParmBlkPtr)&fpb);
if (err != noErr)
goto ErrorExit;
return PR_SUCCESS;
ErrorExit:
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
_MD_SetError(err);
return PR_FAILURE;
}
PRStatus _MD_TLockFile(PRInt32 fd)
{
return (_MD_LockFile(fd));
}
PRStatus _MD_UnlockFile(PRInt32 fd)
{
OSErr err;
FCBPBRec fcbpb;
HFileParam fpb;
Str255 pascalName;
fcbpb.ioNamePtr = pascalName;
fcbpb.ioVRefNum = 0;
fcbpb.ioRefNum = fd;
fcbpb.ioFCBIndx = 0;
err = PBGetFCBInfoSync(&fcbpb);
if (err != noErr)
goto ErrorExit;
fpb.ioCompletion = NULL;
fpb.ioNamePtr = pascalName;
fpb.ioVRefNum = fcbpb.ioFCBVRefNum;
fpb.ioDirID = fcbpb.ioFCBParID;
err = PBHRstFLockSync((HParmBlkPtr)&fpb);
if (err != noErr)
goto ErrorExit;
return PR_SUCCESS;
ErrorExit:
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
_MD_SetError(err);
return PR_FAILURE;
}
void SetLogFileTypeCreator(const char *logFile)
{
HParamBlockRec pb;
OSErr err;
Str31 pName;
PStrFromCStr(logFile, pName);
pb.fileParam.ioCompletion = nil;
pb.fileParam.ioNamePtr = pName;
pb.fileParam.ioVRefNum = 0;
pb.fileParam.ioFDirIndex = 0;
pb.fileParam.ioDirID = 0;
err = PBHGetFInfoSync(&pb);
PR_ASSERT(err == noErr);
pb.fileParam.ioDirID = 0;
pb.fileParam.ioFlFndrInfo.fdType = 'TEXT';
pb.fileParam.ioFlFndrInfo.fdCreator = 'ttxt';
err = PBHSetFInfoSync(&pb);
PR_ASSERT(err == noErr);
}
#if DEVELOPER_DEBUG
PR_IMPLEMENT (void)
SetupMacPrintfLog(char *logFile)
{
/*
* We do _PR_InitLog() twice. The first to force the implicit initialization which
* will set logging to highest levels in _MD_EARLY_INIT. Then, change the env variable
* to disable kernel logging and call _PR_InitLog() again to make it effective. Since
* we are using logging to log test program output, we disable kernel logging to avoid
* all Kernel logging output.
*/
#ifdef PR_INTERNAL_LOGGING
_PR_InitLog();
_MD_PutEnv("NSPR_LOG_MODULES=clock:0,cmon:0,io:0,mon:0,linker:0,cvar:0,sched:0,thread:0");
_PR_InitLog();
#endif
PR_ASSERT(PR_SetLogFile(logFile) == PR_TRUE);
SetLogFileTypeCreator(logFile);
}
#endif
/*
********************** Old name related stuff that is unchanged. **********************
*/
#if !defined(MAC_NSPR_STANDALONE)
short GetVolumeRefNumFromName(const char *cTgtVolName)
{
OSErr err;
Str32 pVolName;
char *cVolName = NULL;
HParamBlockRec hPB;
short refNum = 0;
hPB.volumeParam.ioVolIndex = 0;
hPB.volumeParam.ioNamePtr = pVolName;
do {
hPB.volumeParam.ioVolIndex++;
err = PBHGetVInfoSync(&hPB);
CStrFromPStr(pVolName, &cVolName);
if (strcmp(cTgtVolName, cVolName) == 0) {
refNum = hPB.volumeParam.ioVRefNum;
PR_DELETE(cVolName);
break;
}
PR_DELETE(cVolName);
} while (err == noErr);
return refNum;
}
static OSErr CreateMacPathFromUnixPath(const char *unixPath, char **macPath)
{
// Given a Unix style path with '/' directory separators, this allocates
// a path with Mac style directory separators in the path.
//
// It does not do any special directory translation; use ConvertUnixPathToMacPath
// for that.
const char *src;
char *tgt;
OSErr err = noErr;
PR_ASSERT(unixPath != nil);
if (nil == unixPath) {
err = paramErr;
goto exit;
}
// If unixPath is a zero-length string, we copy ":" into
// macPath, so we need a minimum of two bytes to handle
// the case of ":".
*macPath = malloc(strlen(unixPath) + 2); // Will be enough extra space.
require_action (*macPath != NULL, exit, err = memFullErr;);
src = unixPath;
tgt = *macPath;
if (PL_strchr(src, PR_DIRECTORY_SEPARATOR) == src) // If weÕre dealing with an absolute
src++; // path, skip the separator
else
*(tgt++) = PR_PATH_SEPARATOR;
if (PL_strstr(src, UNIX_THIS_DIRECTORY_STR) == src) // If it starts with /
src += 2; // skip it.
while (*src)
{ // deal with the rest of the path
if (PL_strstr(src, UNIX_PARENT_DIRECTORY_STR) == src) { // Going up?
*(tgt++) = PR_PATH_SEPARATOR; // simply add an extra colon.
src +=3;
}
else if (*src == PR_DIRECTORY_SEPARATOR) { // Change the separator
*(tgt++) = PR_PATH_SEPARATOR;
src++;
}
else
*(tgt++) = *(src++);
}
*tgt = NULL; // make sure itÕs null terminated.
exit:
return err;
}
static ProcessInfoRec gNavigatorProcInfo;
static FSSpec gGutsFolder;
static FSSpec gNetscapeFolder;
static OSErr SetupRequiredFSSpecs(void)
{
OSErr err;
CInfoPBRec pb;
ProcessSerialNumber curPSN = {0, kCurrentProcess};
gNavigatorProcInfo.processInfoLength = sizeof(ProcessInfoRec);
gNavigatorProcInfo.processName = NULL;
gNavigatorProcInfo.processAppSpec = &gNetscapeFolder;
err = GetProcessInformation (&curPSN, &gNavigatorProcInfo);
if (err != noErr)
goto ErrorExit;
/* guts folder resides at the same place as the app file itself */
gGutsFolder = gNetscapeFolder;
/* How else do we do this hack???
* Should NSPR have a string resource for this ?
*/
GetIndString( gGutsFolder.name, 300, 34);
/*
* vRefNum and parentDirID are now set up correctly for the app file itself.
* parentDirID is the Netscape Folder's ID. Then Find it's parent ID to
* set up the FSSpec and its own name.
*/
pb.dirInfo.ioCompletion = NULL;
pb.dirInfo.ioNamePtr = gNetscapeFolder.name;
pb.dirInfo.ioVRefNum = gNetscapeFolder.vRefNum;
pb.dirInfo.ioFDirIndex = -1;
pb.dirInfo.ioDrDirID = gNetscapeFolder.parID;
err = PBGetCatInfoSync(&pb);
if (err != noErr)
goto ErrorExit;
gNetscapeFolder.parID = pb.dirInfo.ioDrParID;
return noErr;
ErrorExit:
return err;
}
static OSErr FindGutsFolder(FSSpec *foundSpec)
{
OSErr err;
if (gNavigatorProcInfo.processInfoLength == 0) { /* Uninitialized? */
err = SetupRequiredFSSpecs();
if (err != noErr)
goto ErrorExit;
}
*foundSpec = gGutsFolder;
return noErr;
ErrorExit:
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
return err;
}
static OSErr FindNetscapeFolder(FSSpec *foundSpec)
{
OSErr err;
if (gNavigatorProcInfo.processInfoLength == 0) { /* Uninitialized? */
err = SetupRequiredFSSpecs();
if (err != noErr)
goto ErrorExit;
}
*foundSpec = gNetscapeFolder;
return noErr;
ErrorExit:
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
return err;
}
PR_IMPLEMENT (OSErr)
ConvertUnixPathToMacPath(const char *unixPath, char **macPath)
{
OSErr err = noErr;
// ******** HACK ALERT ********
//
// Java really wants long file names (>31 chars). We truncate file names
// greater than 31 characters long. Truncation is from the middle.
//
// Convert UNIX style path names (with . and / separators) into a Macintosh
// style path (with :).
//
// There are also a couple of special paths that need to be dealt with
// by translating them to the appropriate Mac special folders. These include:
//
// /usr/tmp/file => {TempFolder}file
//
// The file conversions we need to do are as follows:
//
// file => file
// dir/file => :dir:file
// ./file => file
// ../file => ::file
// ../dir/file => ::dir:file
// /file => ::BootDrive:file
// /dir/file => ::BootDrive:dir:file
if (!strcmp(unixPath, "."))
{
*macPath = malloc(sizeof(":"));
if (*macPath == NULL)
err = memFullErr;
(*macPath)[0] = ':';
(*macPath)[1] = '\0';
}
else
if (*unixPath != PR_DIRECTORY_SEPARATOR) { // Not root relative, just convert it.
err = CreateMacPathFromUnixPath(unixPath, macPath);
}
else {
// WeÕre root-relative. This is either a special Unix directory, or a
// full path (which weÕll support on the Mac since they might be generated).
// This is not condoning the use of full-paths on the Macintosh for file
// specification.
FSSpec foundSpec;
short pathBufferSize;
#if DEBUG
char *temp;
#endif
int tempLen;
// Are we dealing with the temp folder?
if ((strncmp(unixPath, "/usr/tmp", strlen("/usr/tmp")) == 0) ||
((strncmp(unixPath, "/tmp", strlen("/tmp")) == 0))) {
CInfoPBRec pb;
unixPath = PL_strchr(unixPath, PR_DIRECTORY_SEPARATOR);
if (strncmp(unixPath, "/tmp", strlen("/tmp")) == 0) // skip past temp spec
unixPath += 5;
else
unixPath += 9;
err = FindFolder(kOnSystemDisk, kTemporaryFolderType, kCreateFolder, // Create if needed
&foundSpec.vRefNum, &foundSpec.parID);
if (err == noErr) {
pb.dirInfo.ioCompletion = NULL;
pb.dirInfo.ioNamePtr = foundSpec.name;
pb.dirInfo.ioVRefNum = foundSpec.vRefNum;
pb.dirInfo.ioFDirIndex = -1;
pb.dirInfo.ioDrDirID = foundSpec.parID;
err = PBGetCatInfoSync(&pb);
foundSpec.parID = pb.dirInfo.ioDrParID;
}
}
else if (!strncmp(unixPath, "/usr/local/netscape/", (tempLen = strlen("/usr/local/netscape/")))) {
unixPath += tempLen;
if (!strncmp(unixPath, "RequiredGuts/", (tempLen = strlen("RequiredGuts/"))))
{
unixPath += tempLen;
err = FindGutsFolder(&foundSpec);
}
else if (!strncmp(unixPath, "bin/", (tempLen = strlen("bin/"))))
{
unixPath += tempLen;
err = FindNetscapeFolder(&foundSpec);
}
else if (*unixPath == '\0')
{
// it's /usr/local/netscape
err = FindGutsFolder(&foundSpec);
}
}
else {
// This is a root relative directory, weÕll just convert the whole thing.
err = CreateMacPathFromUnixPath(unixPath, macPath);
goto Exit_ConvertUnixPathToMacPath;
}
// WeÕre dealing with a special folder
if (err == noErr)
{
Handle hPathStr;
// Get the path to the root-relative directory
err = FSpGetFullPath(&foundSpec, &pathBufferSize, &hPathStr); // NewHandle's hPathStr
if (noErr == err)
{
// convert handle to c-string
// add one for NULL termination
// pathBufferSize is now one greater than the length of the string
pathBufferSize++;
*macPath = (char*) malloc(sizeof(char) * pathBufferSize);
(*macPath)[pathBufferSize - 1] = '\0';
BlockMoveData(*hPathStr, *macPath, pathBufferSize - 1);
DisposeHandle(hPathStr);
}
}
if (err == noErr)
{
UInt32 unixPathLeft;
UInt32 macPathLen;
unixPathLeft = strlen(unixPath);
macPathLen = strlen(*macPath);
// copy over the remaining file name, converting
if (pathBufferSize - 1 < macPathLen + unixPathLeft)
{
// need to grow string
*macPath = realloc(*macPath, macPathLen + unixPathLeft + 1);
err = (*macPath == NULL ? memFullErr : noErr);
}
if (err == noErr)
{
// carefully remove the '/''s out of the unix path. If we see an "escaped" /
// we will leave it in there, otherwise we take it out and replace it with a :
// we have to do this before we convert to a mac-path, so we can tell what is
// really a path separator and what is in the name of a file or directory
// Make sure that all of the /Õs are :Õs in the final pathname
// effectively we do a
// strcat(*macPath, unixPath); while replace all occurrences of / with : in unixPath
char* dp;
const char* sp;
sp = unixPath;
dp = *macPath + macPathLen;
for (;*sp != '\0'; sp++, dp++)
{
if (*sp == PR_DIRECTORY_SEPARATOR)
{
// if we can look at the previous character
if (sp > unixPath)
{
// check to see if previous character is an escape
if (sp[-1] == '\\')
{
// leave it in, and cycle
continue;
}
else
{
*dp = PR_PATH_SEPARATOR;
}
}
else
*dp = PR_PATH_SEPARATOR;
}
else
{
// just copy;
*dp = *sp;
}
}
*dp = '\0'; // NULL terminate *macPath
}
#if DEBUG
// we used to check here, now we check above, we leave this in
// the debug build to make sure we didn't screw up
// Make sure that all of the /Õs are :Õs in the final pathname
for (temp = *macPath + strlen(*macPath) - strlen(unixPath); *temp != '\0'; temp++) {
if (*temp == PR_DIRECTORY_SEPARATOR)
{
DebugStr("\pFound a slash");
*temp = PR_PATH_SEPARATOR;
}
}
#endif
}
}
Exit_ConvertUnixPathToMacPath:
return err;
}
// Hey! Before you delete this "hack" you should look at how it's being
// used by sun-java/netscape/applet/appletStubs.c.
PR_IMPLEMENT (OSErr)
ConvertMacPathToUnixPath(const char *macPath, char **unixPath)
{
// *** HACK ***
// Get minimal version working
char *unixPathPtr;
*unixPath = malloc(strlen(macPath) + 2); // Add one for the front slash, one for null
if (*unixPath == NULL)
return (memFullErr);
unixPathPtr = *unixPath;
*unixPathPtr++ = PR_DIRECTORY_SEPARATOR;
do {
// Translate all colons to slashes
if (*macPath == PR_PATH_SEPARATOR)
*unixPathPtr = PR_DIRECTORY_SEPARATOR;
else
*unixPathPtr = *macPath;
unixPathPtr++;
macPath++;
} while (*macPath != NULL);
// Terminate the string
*unixPathPtr = '\0';
return (noErr);
}
OSErr
ConvertUnixPathToFSSpec(const char *unixPath, FSSpec *fileSpec)
{
char* macPath;
OSErr convertError;
int len;
convertError = ConvertUnixPathToMacPath(unixPath, &macPath);
if (convertError != noErr)
return convertError;
len = strlen(macPath);
if (*macPath == PR_PATH_SEPARATOR)
{
if (len < sizeof(Str255))
{
short vRefNum;
long dirID;
Str255 pascalMacPath;
convertError = HGetVol(NULL, &vRefNum, &dirID);
if (convertError == noErr)
{
PStrFromCStr(macPath, pascalMacPath);
convertError = FSMakeFSSpec(vRefNum, dirID, pascalMacPath, fileSpec);
}
}
else
convertError = paramErr;
}
else
{
convertError = FSpLocationFromFullPath(len, macPath, fileSpec);
if (convertError == fnfErr)
{
CInfoPBRec pb;
Str255 pascalMacPath;
OSErr err;
PStrFromCStr(macPath, pascalMacPath);
/*
FSpLocationFromFullPath does not work for directories unless there is
a ":" at the end. We will make sure of an existence of a directory.
If so, the returned fileSpec is valid from FSpLocationFromFullPath eventhough
it returned an error.
*/
pb.hFileInfo.ioNamePtr = pascalMacPath;
pb.hFileInfo.ioVRefNum = 0;
pb.hFileInfo.ioDirID = 0;
pb.hFileInfo.ioFDirIndex = 0;
err = PBGetCatInfoSync(&pb);
if (err == noErr)
convertError = noErr;
}
}
free(macPath);
return (convertError);
}
FILE *_OS_FOPEN(const char *filename, const char *mode)
{
OSErr err = noErr;
char *macFileName = NULL;
FILE *result;
err = ConvertUnixPathToMacPath(filename, &macFileName);
if (err != noErr)
goto ErrorExit;
result = fopen(macFileName, mode);
PR_DELETE(macFileName);
return result;
ErrorExit:
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
_MD_SetError(err);
return NULL;
}
#else
short GetVolumeRefNumFromName(const char *cTgtVolName)
{
OSErr err;
Str32 pVolName;
char *cVolName = NULL;
HParamBlockRec hPB;
short refNum = 0;
hPB.volumeParam.ioVolIndex = 0;
hPB.volumeParam.ioNamePtr = pVolName;
do {
hPB.volumeParam.ioVolIndex++;
err = PBHGetVInfoSync(&hPB);
CStrFromPStr(pVolName, &cVolName);
if (strcmp(cTgtVolName, cVolName) == 0) {
refNum = hPB.volumeParam.ioVRefNum;
PR_DELETE(cVolName);
break;
}
PR_DELETE(cVolName);
} while (err == noErr);
return refNum;
}
static OSErr GetFullPath(short vRefNum, long dirID, char **fullPath, int *strSize)
{
Str255 pascalDirName;
char cDirName[256];
char *tmpPath = NULL; // needed since sprintf isnÕt safe
CInfoPBRec myPB;
OSErr err = noErr;
// get the full path of the temp folder.
*strSize = 256;
*fullPath = NULL;
*fullPath = malloc(*strSize); // How big should this thing be?
require_action (*fullPath != NULL, errorExit, err = memFullErr;);
tmpPath = malloc(*strSize);
require_action (tmpPath != NULL, errorExit, err = memFullErr;);
strcpy(*fullPath, ""); // Clear C result
strcpy(tmpPath, "");
pascalDirName[0] = 0; // Clear Pascal intermediate string
myPB.dirInfo.ioNamePtr = &pascalDirName[0];
myPB.dirInfo.ioVRefNum = vRefNum;
myPB.dirInfo.ioDrParID = dirID;
myPB.dirInfo.ioFDirIndex = -1; // Getting info about
do {
myPB.dirInfo.ioDrDirID = myPB.dirInfo.ioDrParID;
err = PBGetCatInfoSync(&myPB);
require(err == noErr, errorExit);
// Move the name into C domain
memcpy(&cDirName, &pascalDirName, 256);
p2cstr((unsigned char *)&cDirName); // Changes in place!
if ((strlen(cDirName) + strlen(*fullPath)) > *strSize) {
// We need to grow the string, do it in 256 byte chunks
(*strSize) += 256;
*fullPath = PR_REALLOC(*fullPath, *strSize);
require_action (*fullPath != NULL, errorExit, err = memFullErr;);
tmpPath = PR_REALLOC(tmpPath, *strSize);
require_action (tmpPath != NULL, errorExit, err = memFullErr;);
}
sprintf(tmpPath, "%s:%s", cDirName, *fullPath);
strcpy(*fullPath, tmpPath);
} while (myPB.dirInfo.ioDrDirID != fsRtDirID);
PR_DELETE(tmpPath);
return noErr;
errorExit:
PR_DELETE(*fullPath);
PR_DELETE(tmpPath);
return err;
}
static OSErr CreateMacPathFromUnixPath(const char *unixPath, char **macPath)
{
// Given a Unix style path with '/' directory separators, this allocates
// a path with Mac style directory separators in the path.
//
// It does not do any special directory translation; use ConvertUnixPathToMacPath
// for that.
const char *src;
char *tgt;
OSErr err = noErr;
PR_ASSERT(unixPath != nil);
if (nil == unixPath) {
err = paramErr;
goto exit;
}
// If unixPath is a zero-length string, we copy ":" into
// macPath, so we need a minimum of two bytes to handle
// the case of ":".
*macPath = malloc(strlen(unixPath) + 2); // Will be enough extra space.
require_action (*macPath != NULL, exit, err = memFullErr;);
src = unixPath;
tgt = *macPath;
if (PL_strchr(src, PR_DIRECTORY_SEPARATOR) == src) // If weÕre dealing with an absolute
src++; // path, skip the separator
else
*(tgt++) = PR_PATH_SEPARATOR;
if (PL_strstr(src, UNIX_THIS_DIRECTORY_STR) == src) // If it starts with ./
src += 2; // skip it.
while (*src)
{ // deal with the rest of the path
if (PL_strstr(src, UNIX_PARENT_DIRECTORY_STR) == src) { // Going up?
*(tgt++) = PR_PATH_SEPARATOR; // simply add an extra colon.
src +=3;
}
else if (*src == PR_DIRECTORY_SEPARATOR) { // Change the separator
*(tgt++) = PR_PATH_SEPARATOR;
src++;
}
else
*(tgt++) = *(src++);
}
*tgt = NULL; // make sure itÕs null terminated.
exit:
return err;
}
static OSErr ConvertUnixPathToMacPath(const char *unixPath, char **macPath)
{
OSErr err = noErr;
//
// Convert UNIX style path names (with . and / separators) into a Macintosh
// style path (with :).
//
// There are also a couple of special paths that need to be dealt with
// by translating them to the appropriate Mac special folders. These include:
//
// /usr/tmp/file => {TempFolder}file
//
// The file conversions we need to do are as follows:
//
// file => file
// dir/file => :dir:file
// ./file => file
// ../file => ::file
// ../dir/file => ::dir:file
// /file => ::BootDrive:file
// /dir/file => ::BootDrive:dir:file
if (*unixPath != PR_DIRECTORY_SEPARATOR) { // Not root relative, just convert it.
err = CreateMacPathFromUnixPath(unixPath, macPath);
}
else {
// WeÕre root-relative. This is either a special Unix directory, or a
// full path (which weÕll support on the Mac since they might be generated).
// This is not condoning the use of full-paths on the Macintosh for file
// specification.
short foundVRefNum;
long foundDirID;
int pathBufferSize;
char *temp;
char isNetscapeDir = false;
// Are we dealing with the temp folder?
if (strncmp(unixPath, "/usr/tmp", strlen("/usr/tmp")) == 0){
unixPath += 8;
if (*unixPath == PR_DIRECTORY_SEPARATOR)
unixPath++; // Skip the slash
err = FindFolder(kOnSystemDisk, kTemporaryFolderType, kCreateFolder, // Create if needed
&foundVRefNum, &foundDirID);
}
if (strncmp(unixPath, "/tmp", strlen("/tmp")) == 0) {
unixPath += 4; // Skip the slash
if (*unixPath == PR_DIRECTORY_SEPARATOR)
unixPath++; // Skip the slash
err = FindFolder(kOnSystemDisk, kTemporaryFolderType, kCreateFolder, // Create if needed
&foundVRefNum, &foundDirID);
}
else if (strncmp(unixPath, "/usr", strlen("/usr")) == 0) {
int usrNetscapePathLen;
usrNetscapePathLen = strlen("/usr/local/netscape/");
if (strncmp(unixPath, "/usr/local/netscape/", usrNetscapePathLen) == 0) {
unixPath += usrNetscapePathLen;
// err = FindPreferencesFolder(&foundVRefNum, &foundDirID);
err = paramErr;
isNetscapeDir = true;
}
else {
dprintf("Unable to translate Unix file path %s to Mac path\n", unixPath);
err = -1;
goto Exit_ConvertUnixPathToMacPath;
}
}
else {
// This is a root relative directory, weÕll just convert the whole thing.
err = CreateMacPathFromUnixPath(unixPath, macPath);
goto Exit_ConvertUnixPathToMacPath;
}
// WeÕre dealing with a special folder
if (err == noErr)
// Get the path to the root-relative directory
err = GetFullPath(foundVRefNum, foundDirID, macPath, &pathBufferSize); // mallocs macPath
if (err == noErr){
// copy over the remaining file name, converting
if (pathBufferSize < (strlen(*macPath) + strlen(unixPath))) {
// need to grow string
*macPath = PR_REALLOC(*macPath, (strlen(*macPath) + strlen(unixPath) +
(isNetscapeDir ? strlen("Netscape Ä:") : 0)));
err = (*macPath == NULL ? memFullErr : noErr);
}
if (isNetscapeDir)
strcat(*macPath, "Netscape Ä:");
if (err == noErr)
strcat(*macPath, unixPath);
// Make sure that all of the /Õs are :Õs in the final pathname
for (temp = *macPath + strlen(*macPath) - strlen(unixPath); *temp != '\0'; temp++) {
if (*temp == PR_DIRECTORY_SEPARATOR)
*temp = PR_PATH_SEPARATOR;
}
}
}
Exit_ConvertUnixPathToMacPath:
return err;
}
OSErr
ConvertUnixPathToFSSpec(const char *unixPath, FSSpec *fileSpec)
{
char* macPath;
OSErr convertError;
int len;
convertError = ConvertUnixPathToMacPath(unixPath, &macPath);
if (convertError != noErr)
return convertError;
len = strlen(macPath);
if (*macPath == PR_PATH_SEPARATOR)
{
if (len < sizeof(Str255))
{
short vRefNum;
long dirID;
Str255 pascalMacPath;
convertError = HGetVol(NULL, &vRefNum, &dirID);
if (convertError == noErr)
{
PStrFromCStr(macPath, pascalMacPath);
convertError = FSMakeFSSpec(vRefNum, dirID, pascalMacPath, fileSpec);
}
}
else
convertError = paramErr;
}
else
{
convertError = FSpLocationFromFullPath(len, macPath, fileSpec);
}
free(macPath);
return (convertError);
}
#endif
/*
**********************************************************************
*
* Memory-mapped files are not implementable on the Mac.
*
**********************************************************************
*/
PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size)
{
#pragma unused (fmap, size)
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
return PR_FAILURE;
}
PRInt32 _MD_GetMemMapAlignment(void)
{
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
return -1;
}
void * _MD_MemMap(
PRFileMap *fmap,
PROffset64 offset,
PRUint32 len)
{
#pragma unused (fmap, offset, len)
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
return NULL;
}
PRStatus _MD_MemUnmap(void *addr, PRUint32 len)
{
#pragma unused (addr, len)
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
return PR_FAILURE;
}
PRStatus _MD_CloseFileMap(PRFileMap *fmap)
{
#pragma unused (fmap)
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
return PR_FAILURE;
}
syntax highlighted by Code2HTML, v. 0.9.1