/* -*- 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;
}
