1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* ***** BEGIN LICENSE BLOCK *****
3
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
5
* The contents of this file are subject to the Mozilla Public License Version
6
* 1.1 (the "License"); you may not use this file except in compliance with
7
* the License. You may obtain a copy of the License at
8
* http://www.mozilla.org/MPL/
10
* Software distributed under the License is distributed on an "AS IS" basis,
11
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12
* for the specific language governing rights and limitations under the
15
* The Original Code is the Netscape Portable Runtime (NSPR).
17
* The Initial Developer of the Original Code is
18
* Netscape Communications Corporation.
19
* Portions created by the Initial Developer are Copyright (C) 1998-2000
20
* the Initial Developer. All Rights Reserved.
24
* Alternatively, the contents of this file may be used under the terms of
25
* either the GNU General Public License Version 2 or later (the "GPL"), or
26
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27
* in which case the provisions of the GPL or the LGPL are applicable instead
28
* of those above. If you wish to allow use of your version of this file only
29
* under the terms of either the GPL or the LGPL, and not to allow others to
30
* use your version of this file under the terms of the MPL, indicate your
31
* decision by deleting the provisions above and replace them with the notice
32
* and other provisions required by the GPL or the LGPL. If you do not delete
33
* the provisions above, a recipient may use your version of this file under
34
* the terms of any one of the MPL, the GPL or the LGPL.
36
* ***** END LICENSE BLOCK ***** */
45
#include <Resources.h>
46
#include <Processes.h>
47
#include <TextUtils.h>
51
#include "FullPath.h" /* MoreFiles */
54
#include "MacErrorHandling.h"
59
/* forward declarations */
60
extern unsigned long gJanuaryFirst1970Seconds;
62
extern void WaitOnThisThread(PRThread *thread, PRIntervalTime timeout);
63
extern void DoneWaitingOnThisThread(PRThread *thread);
64
extern void AsyncNotify(PRThread *thread);
67
/* PB for Read and Write */
68
struct ExtendedParamBlock {
69
/* PB must be first so that the file system can get the right data. */
73
typedef struct ExtendedParamBlock ExtendedParamBlock;
76
/* XXX Not done yet for 68K */
77
/* I/O completion routne for _MD_READ and _MD_WRITE */
78
static void AsyncIOCompletion (ExtendedParamBlock *pbAsyncPtr)
80
_PRCPU *cpu = _PR_MD_CURRENT_CPU();
81
PRThread *thread = pbAsyncPtr->thread;
84
if (_PR_MD_GET_INTSOFF()) {
85
thread->md.missedIONotify = PR_TRUE;
86
cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
90
thread->md.osErrCode = noErr;
91
DoneWaitingOnThisThread(thread);
96
SignalIdleSemaphore();
99
void _MD_SetError(OSErr oserror)
105
code = PR_OUT_OF_MEMORY_ERROR;
108
code = PR_FILE_NOT_FOUND_ERROR;
111
code = PR_FILE_EXISTS_ERROR;
118
code = PR_INVALID_DEVICE_STATE_ERROR;
122
code = PR_NAME_TOO_LONG_ERROR;
125
code = PR_INSUFFICIENT_RESOURCES_ERROR;
130
case afpAccessDenied:
131
code = PR_NO_ACCESS_RIGHTS_ERROR;
133
case afpObjectTypeErr:
134
code = PR_DIRECTORY_LOOKUP_ERROR;
138
code = PR_DEVICE_IS_LOCKED_ERROR;
141
code = PR_FILE_IS_LOCKED_ERROR;
144
code = PR_NOT_DIRECTORY_ERROR;
147
code = PR_MAX_DIRECTORY_ENTRIES_ERROR;
150
code = PR_NO_DEVICE_SPACE_ERROR;
154
code = PR_BAD_DESCRIPTOR_ERROR;
157
code = PR_END_OF_FILE_ERROR;
161
code = PR_FILE_SEEK_ERROR;
164
code = PR_FILE_IS_BUSY_ERROR;
167
code = PR_REMOTE_FILE_ERROR;
170
code = PR_PENDING_INTERRUPT_ERROR;
173
code = PR_INVALID_ARGUMENT_ERROR;
176
code = PR_NOT_IMPLEMENTED_ERROR;
180
PR_SetError(code, oserror);
183
void _MD_IOInterrupt(void)
186
PRThread *thread, *me = _PR_MD_CURRENT_THREAD();
188
PR_ASSERT(_PR_MD_GET_INTSOFF() != 0);
190
_PR_SLEEPQ_LOCK(me->cpu);
191
qp = _PR_PAUSEQ(me->cpu).next;
192
while (qp != &_PR_PAUSEQ(me->cpu)) {
194
thread = _PR_THREAD_PTR(qp);
195
PR_ASSERT(thread->flags & _PR_ON_PAUSEQ);
199
if (thread->md.missedIONotify) {
200
thread->md.missedIONotify = PR_FALSE;
201
DoneWaitingOnThisThread(thread);
204
if (thread->md.missedAsyncNotify) {
205
thread->md.missedAsyncNotify = PR_FALSE;
209
qp = _PR_SLEEPQ(me->cpu).next;
210
while (qp != &_PR_SLEEPQ(me->cpu)) {
212
thread = _PR_THREAD_PTR(qp);
213
PR_ASSERT(thread->flags & _PR_ON_SLEEPQ);
217
if (thread->md.missedIONotify) {
218
thread->md.missedIONotify = PR_FALSE;
219
DoneWaitingOnThisThread(thread);
222
if (thread->md.missedAsyncNotify) {
223
thread->md.missedAsyncNotify = PR_FALSE;
227
_PR_SLEEPQ_UNLOCK(thread->cpu);
231
** All PR_read and PR_Write calls are synchronous from caller's perspective.
232
** They are internally made asynchronous calls. This gives cpu to other
233
** user threads while the async io is in progress.
235
PRInt32 ReadWriteProc(PRFileDesc *fd, void *buf, PRUint32 bytes, IOOperation op)
237
PRInt32 refNum = fd->secret->md.osfd;
239
ExtendedParamBlock pbAsync;
240
PRThread *me = _PR_MD_CURRENT_THREAD();
241
_PRCPU *cpu = _PR_MD_CURRENT_CPU();
243
/* quick hack to allow PR_fprintf, etc to work with stderr, stdin, stdout */
244
/* note, if a user chooses "seek" or the like as an operation in another function */
245
/* this will not work */
246
if (refNum >= 0 && refNum < 3)
251
/* stdin - not on a Mac for now */
265
static IOCompletionUPP sCompletionUPP = NULL;
267
PRBool doingAsync = PR_FALSE;
269
/* allocate the callback Universal Procedure Pointer (UPP). This actually allocates
270
a 32 byte Ptr in the heap, so only do this once
273
sCompletionUPP = NewIOCompletionUPP((IOCompletionProcPtr)&AsyncIOCompletion);
275
/* grab the thread so we know which one to post to at completion */
278
pbAsync.pb.ioParam.ioCompletion = sCompletionUPP;
279
pbAsync.pb.ioParam.ioResult = noErr;
280
pbAsync.pb.ioParam.ioRefNum = refNum;
281
pbAsync.pb.ioParam.ioBuffer = buf;
282
pbAsync.pb.ioParam.ioReqCount = bytes;
283
pbAsync.pb.ioParam.ioPosMode = fsAtMark;
284
pbAsync.pb.ioParam.ioPosOffset = 0;
287
** Issue the async read call and wait for the io semaphore associated
289
** Async file system calls *never* return error values, so ignore their
290
** results (see <http://developer.apple.com/technotes/fl/fl_515.html>);
291
** the completion routine is always called.
294
me->md.osErrCode = noErr;
295
if (op == READ_ASYNC)
298
** Skanky optimization so that reads < 20K are actually done synchronously
299
** to optimize performance on small reads (e.g. registry reads on startup)
301
if ( bytes > 20480L )
303
doingAsync = PR_TRUE;
304
me->io_pending = PR_TRUE;
306
(void)PBReadAsync(&pbAsync.pb);
310
pbAsync.pb.ioParam.ioCompletion = NULL;
311
me->io_pending = PR_FALSE;
313
err = PBReadSync(&pbAsync.pb);
314
if (err != noErr && err != eofErr)
320
doingAsync = PR_TRUE;
321
me->io_pending = PR_TRUE;
323
/* writes are currently always async */
324
(void)PBWriteAsync(&pbAsync.pb);
328
WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);
332
err = me->md.osErrCode;
336
err = pbAsync.pb.ioParam.ioResult;
337
if (err != noErr && err != eofErr)
340
return pbAsync.pb.ioParam.ioActCount;
343
me->md.osErrCode = err;
349
Special WriteSyncProc for logging only. IO occurs synchronously. Otherwise,
350
logging internal to NSPR causes ReadWriteProc above to recurse on PR_WaitSem logging.
352
PRInt32 WriteSyncProc(PRFileDesc *fd, void *buf, PRUint32 bytes)
354
PRInt32 refNum = fd->secret->md.osfd;
357
PRThread *me = _PR_MD_CURRENT_THREAD();
359
if (refNum >= 0 && refNum < 3)
361
PR_ASSERT(FALSE); /* writing to these is hazardous to a Mac's health (refNum 2 is the system file) */
366
pb.ioParam.ioCompletion = NULL;
367
pb.ioParam.ioResult = noErr;
368
pb.ioParam.ioRefNum = refNum;
369
pb.ioParam.ioBuffer = buf;
370
pb.ioParam.ioReqCount = bytes;
371
pb.ioParam.ioPosMode = fsAtMark;
372
pb.ioParam.ioPosOffset = 0;
374
err = PBWriteSync(&pb);
379
return pb.ioParam.ioActCount;
382
me->md.osErrCode = err;
387
/* File I/O functions called by PR I/O routines */
388
PRInt32 _MD_Open(const char *path, PRIntn flags, int mode)
390
// Macintosh doesn't really have mode bits, just drop them
391
#pragma unused (mode)
396
char *macFileName = NULL;
400
err = ConvertUnixPathToMacPath(path, &macFileName);
405
hpb.ioParam.ioCompletion = NULL;
406
PStrFromCStr(macFileName, pascalName);
407
PR_DELETE(macFileName);
408
hpb.ioParam.ioNamePtr = pascalName;
409
hpb.ioParam.ioVRefNum = 0;
410
hpb.ioParam.ioVersNum = 0;
411
hpb.fileParam.ioDirID = 0;
415
else if (flags & PR_WRONLY)
419
hpb.ioParam.ioPermssn = perm;
422
if (flags & PR_CREATE_FILE) {
423
err = PBHCreateSync(&hpb);
425
/* If opening with the PR_EXCL flag the existence of the file prior to opening is an error */
426
if ((flags & PR_EXCL) && (err == dupFNErr)) {
427
err = PR_FILE_EXISTS_ERROR;
431
if ((err != noErr) && (err != dupFNErr))
435
err = PBHOpenDFSync(&hpb);
440
if (flags & PR_TRUNCATE) {
441
pb.ioParam.ioCompletion = NULL;
442
pb.ioParam.ioRefNum = hpb.ioParam.ioRefNum;
443
pb.ioParam.ioMisc = NULL;
444
err = PBSetEOFSync(&pb);
447
} else if (flags & PR_APPEND) {
448
pb.ioParam.ioCompletion = NULL;
449
pb.ioParam.ioRefNum = hpb.ioParam.ioRefNum;
450
pb.ioParam.ioPosMode = fsFromLEOF;
451
pb.ioParam.ioPosOffset = 0;
452
err = PBSetFPosSync(&pb);
456
return hpb.ioParam.ioRefNum;
459
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
464
/* _MD_CLOSE_FILE, _MD_READ, _MD_WRITE, _MD_GET_FILE_ERROR are defined in _macos.h */
466
PROffset32 _MD_LSeek(PRFileDesc *fd, PROffset32 offset, PRSeekWhence how)
468
PRInt32 refNum = fd->secret->md.osfd;
472
/* compute new mark */
479
err = GetFPos(refNum, &curPos);
480
endPos = curPos + offset;
484
err = GetEOF(refNum, &curPos);
485
endPos = curPos + offset;
493
/* set the new mark and extend the file if seeking beyond current EOF */
494
/* making sure to set the mark after any required extend */
496
err = SetFPos(refNum, fsFromStart, endPos);
498
err = SetEOF(refNum, endPos);
500
err = SetFPos(refNum, fsFromStart, endPos);
508
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
514
PRInt32 _MD_FSync(PRFileDesc *fd)
516
PRInt32 refNum = fd->secret->md.osfd;
520
pb.ioParam.ioCompletion = NULL;
521
pb.ioParam.ioRefNum = refNum;
523
err = PBFlushFileSync(&pb);
530
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
537
PRStatus _MD_OpenDir(_MDDir *mdDir,const char *name)
539
// Emulate the Unix opendir() routine.
543
char *macDirName = NULL;
544
char *position = NULL;
548
// Get the Macintosh path
549
err = ConvertUnixPathToMacPath(name, &macDirName);
554
position = PL_strchr(macDirName, PR_PATH_SEPARATOR);
555
if ((position == macDirName) || (position == NULL))
556
mdDir->ioVRefNum = 0; // Use application relative searching
558
memset(volumeName, 0, sizeof(volumeName));
559
strncpy(volumeName, macDirName, position-macDirName);
560
mdDir->ioVRefNum = GetVolumeRefNumFromName(volumeName);
563
// Get info about the object.
564
PStrFromCStr(macDirName, pascalName);
565
PR_DELETE(macDirName);
567
pb.dirInfo.ioNamePtr = pascalName;
568
pb.dirInfo.ioVRefNum = mdDir->ioVRefNum;
569
pb.dirInfo.ioDrDirID = 0;
570
pb.dirInfo.ioFDirIndex = 0;
571
err = PBGetCatInfoSync(&pb);
575
// Are we dealing with a directory?
576
if ((pb.dirInfo.ioFlAttrib & ioDirMask) == 0) {
581
/* This is a directory, store away the pertinent information.
582
** We post increment. I.e. index is always the nth. item we
583
** should get on the next call
585
mdDir->ioDirID = pb.dirInfo.ioDrDirID;
586
mdDir->currentEntryName = NULL;
587
mdDir->ioFDirIndex = 1;
591
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
596
char *_MD_ReadDir(_MDDir *mdDir, PRIntn flags)
598
// Emulate the Unix readdir() routine.
600
// Mac doesn�t have the concept of .(PR_SKIP_DOT) & ..(PR_SKIP_DOT_DOT)
605
Str255 pascalName = "\p";
608
PR_ASSERT(mdDir != NULL);
612
// Release the last name read.
613
PR_DELETE(mdDir->currentEntryName);
614
mdDir->currentEntryName = NULL;
616
// We�ve got all the info we need, just get info about this guy.
617
pb.hFileInfo.ioNamePtr = pascalName;
618
pb.hFileInfo.ioVRefNum = mdDir->ioVRefNum;
619
pb.hFileInfo.ioFDirIndex = mdDir->ioFDirIndex;
620
pb.hFileInfo.ioDirID = mdDir->ioDirID;
621
err = PBGetCatInfoSync(&pb);
625
// Convert the Pascal string to a C string (actual allocation occurs in CStrFromPStr)
626
CStrFromPStr(pascalName, &returnedCStr);
628
mdDir->currentEntryName = returnedCStr;
629
mdDir->ioFDirIndex++;
631
// If it is not a hidden file and the flags did not specify skipping, we are done.
632
if ((flags & PR_SKIP_HIDDEN) && (pb.hFileInfo.ioFlFndrInfo.fdFlags & fInvisible))
633
foundEntry = PR_FALSE;
635
foundEntry = PR_TRUE;
637
} while (!foundEntry);
639
return (mdDir->currentEntryName);
642
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
648
void _MD_CloseDir(_MDDir *mdDir)
650
// Emulate the Unix closedir() routine
652
PR_DELETE(mdDir->currentEntryName);
655
PRInt32 _MD_MkDir(char *unixPath, PRIntn mode)
658
Str255 pascalName = "\p";
659
char *cMacPath = NULL;
662
#pragma unused (mode) // Mode is ignored on the Mac
665
err = ConvertUnixPathToMacPath(unixPath, &cMacPath);
669
PStrFromCStr(cMacPath, pascalName);
671
fpb.ioNamePtr = pascalName;
675
err = PBDirCreateSync((HParmBlkPtr)&fpb);
683
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
688
PRInt32 _MD_Delete(char *unixPath)
691
Str255 pascalName = "\p";
692
char *cMacPath = NULL;
696
err = ConvertUnixPathToMacPath(unixPath, &cMacPath);
700
PStrFromCStr(cMacPath, pascalName);
702
fpb.ioNamePtr = pascalName;
706
err = PBHDeleteSync((HParmBlkPtr)&fpb);
714
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
719
PRInt32 _MD_Rename(char *fromUnixPath, char *toUnixPath)
725
FSSpec beforeRenameSpec;
727
if (fromUnixPath && toUnixPath) {
728
err = ConvertUnixPathToFSSpec(fromUnixPath, &fromSpec);
732
err = ConvertUnixPathToFSSpec(toUnixPath, &toSpec);
733
if (err != noErr && err != fnfErr)
736
/* make an FSSpec for the destination directory */
737
err = FSMakeFSSpec(toSpec.vRefNum, toSpec.parID, nil, &destDirSpec);
738
if (err != noErr) /* parent directory must exist */
741
// move it to the directory specified
742
err = FSpCatMove(&fromSpec, &destDirSpec);
746
// make a new FSSpec for the file or directory in its new location
747
err = FSMakeFSSpec(toSpec.vRefNum, toSpec.parID, fromSpec.name, &beforeRenameSpec);
751
// rename the file or directory
752
err = FSpRename(&beforeRenameSpec, toSpec.name);
764
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
769
#define kWriteAccessAllowed (0x100)
770
PRInt32 _MD_Access(char *unixPath, int amode)
773
// Emulate the Unix access routine
779
char *cMacPath = NULL;
780
Str255 pascalMacPath;
783
// Convert to a Mac style path
784
err = ConvertUnixPathToMacPath(unixPath, &cMacPath);
788
err = stat(cMacPath, &info);
793
// If all we�re doing is checking for the existence of the file, we�re out of here.
794
// On the Mac, if a file exists, you can read from it.
795
// This doesn�t handle remote AppleShare volumes. Does it need to?
796
if ((amode == PR_ACCESS_EXISTS) || (amode == PR_ACCESS_READ_OK)) {
800
PStrFromCStr(cMacPath, pascalMacPath);
802
pb.hFileInfo.ioNamePtr = pascalMacPath;
803
pb.hFileInfo.ioVRefNum = info.st_dev;
804
pb.hFileInfo.ioDirID = 0;
805
pb.hFileInfo.ioFDirIndex = 0;
807
err = PBGetCatInfoSync(&pb);
810
// Check out all the access permissions.
812
if (amode == PR_ACCESS_WRITE_OK) {
813
fcbpb.ioNamePtr = NULL;
814
fcbpb.ioVRefNum = pb.hFileInfo.ioVRefNum;
815
fcbpb.ioRefNum = pb.hFileInfo.ioFRefNum;
818
err = PBGetFCBInfoSync(&fcbpb);
822
/* Look at Inside Mac IV-180 */
823
if ((fcbpb.ioFCBFlags & kWriteAccessAllowed) == 0) {
834
if (cMacPath != NULL)
836
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
841
PRInt32 _MD_GetFileInfo(char *unixPath, PRFileInfo *info)
845
char *cMacPath = NULL;
846
Str255 pascalMacPath;
847
PRTime oneMillion, dateInMicroSeconds;
849
// Convert to a Mac style path
850
err = ConvertUnixPathToMacPath(unixPath, &cMacPath);
854
PStrFromCStr(cMacPath, pascalMacPath);
857
pb.hFileInfo.ioNamePtr = pascalMacPath;
858
pb.hFileInfo.ioVRefNum = 0;
859
pb.hFileInfo.ioDirID = 0;
860
pb.hFileInfo.ioFDirIndex = 0;
862
err = PBGetCatInfoSync(&pb);
866
if (pb.hFileInfo.ioFlAttrib & ioDirMask) {
867
info->type = PR_FILE_DIRECTORY;
870
info->type = PR_FILE_FILE;
871
info->size = pb.hFileInfo.ioFlLgLen + pb.hFileInfo.ioFlRLgLen;
874
pb.hFileInfo.ioFlCrDat -= gJanuaryFirst1970Seconds;
875
LL_I2L(dateInMicroSeconds, pb.hFileInfo.ioFlCrDat);
876
LL_I2L(oneMillion, PR_USEC_PER_SEC);
877
LL_MUL(info->creationTime, oneMillion, dateInMicroSeconds);
879
pb.hFileInfo.ioFlMdDat -= gJanuaryFirst1970Seconds;
880
LL_I2L(dateInMicroSeconds, pb.hFileInfo.ioFlMdDat);
881
LL_MUL(info->modifyTime, oneMillion, dateInMicroSeconds);
886
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
891
PRInt32 _MD_GetOpenFileInfo(const PRFileDesc *fd, PRFileInfo *info)
896
Str255 pascalMacPath;
897
PRTime oneMillion, dateInMicroSeconds;
899
fcbpb.ioNamePtr = pascalMacPath;
901
fcbpb.ioRefNum = fd->secret->md.osfd;
904
err = PBGetFCBInfoSync(&fcbpb);
908
info->type = PR_FILE_FILE;
909
info->size = fcbpb.ioFCBEOF;
911
pb.hFileInfo.ioNamePtr = pascalMacPath;
912
pb.hFileInfo.ioVRefNum = fcbpb.ioFCBVRefNum;
913
pb.hFileInfo.ioDirID = fcbpb.ioFCBParID;
914
pb.hFileInfo.ioFDirIndex = 0;
916
err = PBGetCatInfoSync(&pb);
920
pb.hFileInfo.ioFlCrDat -= gJanuaryFirst1970Seconds;
921
LL_I2L(dateInMicroSeconds, pb.hFileInfo.ioFlCrDat);
922
LL_I2L(oneMillion, PR_USEC_PER_SEC);
923
LL_MUL(info->creationTime, oneMillion, dateInMicroSeconds);
925
pb.hFileInfo.ioFlMdDat -= gJanuaryFirst1970Seconds;
926
LL_I2L(dateInMicroSeconds, pb.hFileInfo.ioFlMdDat);
927
LL_MUL(info->modifyTime, oneMillion, dateInMicroSeconds);
932
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
937
PRInt32 _MD_Stat(const char *path, struct stat *buf)
940
char *macFileName = NULL;
942
err = ConvertUnixPathToMacPath(path, &macFileName);
946
err = stat(macFileName, buf);
950
PR_DELETE(macFileName);
955
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
960
PRStatus _MD_LockFile(PRInt32 fd)
967
fcbpb.ioNamePtr = pascalName;
972
err = PBGetFCBInfoSync(&fcbpb);
976
fpb.ioCompletion = NULL;
977
fpb.ioNamePtr = pascalName;
978
fpb.ioVRefNum = fcbpb.ioFCBVRefNum;
979
fpb.ioDirID = fcbpb.ioFCBParID;
981
err = PBHSetFLockSync((HParmBlkPtr)&fpb);
988
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
993
PRStatus _MD_TLockFile(PRInt32 fd)
995
return (_MD_LockFile(fd));
998
PRStatus _MD_UnlockFile(PRInt32 fd)
1005
fcbpb.ioNamePtr = pascalName;
1006
fcbpb.ioVRefNum = 0;
1007
fcbpb.ioRefNum = fd;
1008
fcbpb.ioFCBIndx = 0;
1010
err = PBGetFCBInfoSync(&fcbpb);
1014
fpb.ioCompletion = NULL;
1015
fpb.ioNamePtr = pascalName;
1016
fpb.ioVRefNum = fcbpb.ioFCBVRefNum;
1017
fpb.ioDirID = fcbpb.ioFCBParID;
1019
err = PBHRstFLockSync((HParmBlkPtr)&fpb);
1026
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
1031
void SetLogFileTypeCreator(const char *logFile)
1037
PStrFromCStr(logFile, pName);
1038
pb.fileParam.ioCompletion = nil;
1039
pb.fileParam.ioNamePtr = pName;
1040
pb.fileParam.ioVRefNum = 0;
1041
pb.fileParam.ioFDirIndex = 0;
1042
pb.fileParam.ioDirID = 0;
1043
err = PBHGetFInfoSync(&pb);
1044
PR_ASSERT(err == noErr);
1046
pb.fileParam.ioDirID = 0;
1047
pb.fileParam.ioFlFndrInfo.fdType = 'TEXT';
1048
pb.fileParam.ioFlFndrInfo.fdCreator = 'ttxt';
1049
err = PBHSetFInfoSync(&pb);
1050
PR_ASSERT(err == noErr);
1055
SetupMacPrintfLog(char *logFile)
1058
* We do _PR_InitLog() twice. The first to force the implicit initialization which
1059
* will set logging to highest levels in _MD_EARLY_INIT. Then, change the env variable
1060
* to disable kernel logging and call _PR_InitLog() again to make it effective. Since
1061
* we are using logging to log test program output, we disable kernel logging to avoid
1062
* all Kernel logging output.
1064
#ifdef PR_INTERNAL_LOGGING
1066
_MD_PutEnv("NSPR_LOG_MODULES=clock:0,cmon:0,io:0,mon:0,linker:0,cvar:0,sched:0,thread:0");
1069
PR_ASSERT(PR_SetLogFile(logFile) == PR_TRUE);
1071
SetLogFileTypeCreator(logFile);
1077
********************** Old name related stuff that is unchanged. **********************
1080
#if !defined(MAC_NSPR_STANDALONE)
1082
short GetVolumeRefNumFromName(const char *cTgtVolName)
1086
char *cVolName = NULL;
1090
hPB.volumeParam.ioVolIndex = 0;
1091
hPB.volumeParam.ioNamePtr = pVolName;
1093
hPB.volumeParam.ioVolIndex++;
1094
err = PBHGetVInfoSync(&hPB);
1095
CStrFromPStr(pVolName, &cVolName);
1096
if (strcmp(cTgtVolName, cVolName) == 0) {
1097
refNum = hPB.volumeParam.ioVRefNum;
1098
PR_DELETE(cVolName);
1101
PR_DELETE(cVolName);
1102
} while (err == noErr);
1107
static OSErr CreateMacPathFromUnixPath(const char *unixPath, char **macPath)
1109
// Given a Unix style path with '/' directory separators, this allocates
1110
// a path with Mac style directory separators in the path.
1112
// It does not do any special directory translation; use ConvertUnixPathToMacPath
1119
PR_ASSERT(unixPath != nil);
1120
if (nil == unixPath) {
1125
// If unixPath is a zero-length string, we copy ":" into
1126
// macPath, so we need a minimum of two bytes to handle
1128
*macPath = malloc(strlen(unixPath) + 2); // Will be enough extra space.
1129
require_action (*macPath != NULL, exit, err = memFullErr;);
1134
if (PL_strchr(src, PR_DIRECTORY_SEPARATOR) == src) // If we�re dealing with an absolute
1135
src++; // path, skip the separator
1137
*(tgt++) = PR_PATH_SEPARATOR;
1139
if (PL_strstr(src, UNIX_THIS_DIRECTORY_STR) == src) // If it starts with /
1140
src += 2; // skip it.
1143
{ // deal with the rest of the path
1144
if (PL_strstr(src, UNIX_PARENT_DIRECTORY_STR) == src) { // Going up?
1145
*(tgt++) = PR_PATH_SEPARATOR; // simply add an extra colon.
1148
else if (*src == PR_DIRECTORY_SEPARATOR) { // Change the separator
1149
*(tgt++) = PR_PATH_SEPARATOR;
1153
*(tgt++) = *(src++);
1156
*tgt = NULL; // make sure it�s null terminated.
1163
static ProcessInfoRec gNavigatorProcInfo;
1164
static FSSpec gGutsFolder;
1165
static FSSpec gNetscapeFolder;
1167
static OSErr SetupRequiredFSSpecs(void)
1171
ProcessSerialNumber curPSN = {0, kCurrentProcess};
1173
gNavigatorProcInfo.processInfoLength = sizeof(ProcessInfoRec);
1174
gNavigatorProcInfo.processName = NULL;
1175
gNavigatorProcInfo.processAppSpec = &gNetscapeFolder;
1177
err = GetProcessInformation (&curPSN, &gNavigatorProcInfo);
1181
/* guts folder resides at the same place as the app file itself */
1182
gGutsFolder = gNetscapeFolder;
1183
/* How else do we do this hack???
1184
* Should NSPR have a string resource for this ?
1186
GetIndString( gGutsFolder.name, 300, 34);
1189
* vRefNum and parentDirID are now set up correctly for the app file itself.
1190
* parentDirID is the Netscape Folder's ID. Then Find it's parent ID to
1191
* set up the FSSpec and its own name.
1194
pb.dirInfo.ioCompletion = NULL;
1195
pb.dirInfo.ioNamePtr = gNetscapeFolder.name;
1196
pb.dirInfo.ioVRefNum = gNetscapeFolder.vRefNum;
1197
pb.dirInfo.ioFDirIndex = -1;
1198
pb.dirInfo.ioDrDirID = gNetscapeFolder.parID;
1200
err = PBGetCatInfoSync(&pb);
1204
gNetscapeFolder.parID = pb.dirInfo.ioDrParID;
1212
static OSErr FindGutsFolder(FSSpec *foundSpec)
1216
if (gNavigatorProcInfo.processInfoLength == 0) { /* Uninitialized? */
1217
err = SetupRequiredFSSpecs();
1222
*foundSpec = gGutsFolder;
1227
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
1231
static OSErr FindNetscapeFolder(FSSpec *foundSpec)
1235
if (gNavigatorProcInfo.processInfoLength == 0) { /* Uninitialized? */
1236
err = SetupRequiredFSSpecs();
1241
*foundSpec = gNetscapeFolder;
1246
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
1251
PR_IMPLEMENT (OSErr)
1252
ConvertUnixPathToMacPath(const char *unixPath, char **macPath)
1256
// ******** HACK ALERT ********
1258
// Java really wants long file names (>31 chars). We truncate file names
1259
// greater than 31 characters long. Truncation is from the middle.
1261
// Convert UNIX style path names (with . and / separators) into a Macintosh
1262
// style path (with :).
1264
// There are also a couple of special paths that need to be dealt with
1265
// by translating them to the appropriate Mac special folders. These include:
1267
// /usr/tmp/file => {TempFolder}file
1269
// The file conversions we need to do are as follows:
1272
// dir/file => :dir:file
1274
// ../file => ::file
1275
// ../dir/file => ::dir:file
1276
// /file => ::BootDrive:file
1277
// /dir/file => ::BootDrive:dir:file
1280
if (!strcmp(unixPath, "."))
1282
*macPath = malloc(sizeof(":"));
1283
if (*macPath == NULL)
1285
(*macPath)[0] = ':';
1286
(*macPath)[1] = '\0';
1290
if (*unixPath != PR_DIRECTORY_SEPARATOR) { // Not root relative, just convert it.
1291
err = CreateMacPathFromUnixPath(unixPath, macPath);
1295
// We�re root-relative. This is either a special Unix directory, or a
1296
// full path (which we�ll support on the Mac since they might be generated).
1297
// This is not condoning the use of full-paths on the Macintosh for file
1301
short pathBufferSize;
1307
// Are we dealing with the temp folder?
1308
if ((strncmp(unixPath, "/usr/tmp", strlen("/usr/tmp")) == 0) ||
1309
((strncmp(unixPath, "/tmp", strlen("/tmp")) == 0))) {
1312
unixPath = PL_strchr(unixPath, PR_DIRECTORY_SEPARATOR);
1313
if (strncmp(unixPath, "/tmp", strlen("/tmp")) == 0) // skip past temp spec
1318
err = FindFolder(kOnSystemDisk, kTemporaryFolderType, kCreateFolder, // Create if needed
1319
&foundSpec.vRefNum, &foundSpec.parID);
1321
pb.dirInfo.ioCompletion = NULL;
1322
pb.dirInfo.ioNamePtr = foundSpec.name;
1323
pb.dirInfo.ioVRefNum = foundSpec.vRefNum;
1324
pb.dirInfo.ioFDirIndex = -1;
1325
pb.dirInfo.ioDrDirID = foundSpec.parID;
1327
err = PBGetCatInfoSync(&pb);
1328
foundSpec.parID = pb.dirInfo.ioDrParID;
1332
else if (!strncmp(unixPath, "/usr/local/netscape/", (tempLen = strlen("/usr/local/netscape/")))) {
1334
unixPath += tempLen;
1336
if (!strncmp(unixPath, "RequiredGuts/", (tempLen = strlen("RequiredGuts/"))))
1338
unixPath += tempLen;
1339
err = FindGutsFolder(&foundSpec);
1341
else if (!strncmp(unixPath, "bin/", (tempLen = strlen("bin/"))))
1343
unixPath += tempLen;
1344
err = FindNetscapeFolder(&foundSpec);
1346
else if (*unixPath == '\0')
1348
// it's /usr/local/netscape
1349
err = FindGutsFolder(&foundSpec);
1355
// This is a root relative directory, we�ll just convert the whole thing.
1356
err = CreateMacPathFromUnixPath(unixPath, macPath);
1357
goto Exit_ConvertUnixPathToMacPath;
1362
// We�re dealing with a special folder
1366
// Get the path to the root-relative directory
1367
err = FSpGetFullPath(&foundSpec, &pathBufferSize, &hPathStr); // NewHandle's hPathStr
1371
// convert handle to c-string
1372
// add one for NULL termination
1373
// pathBufferSize is now one greater than the length of the string
1376
*macPath = (char*) malloc(sizeof(char) * pathBufferSize);
1377
(*macPath)[pathBufferSize - 1] = '\0';
1378
BlockMoveData(*hPathStr, *macPath, pathBufferSize - 1);
1380
DisposeHandle(hPathStr);
1386
UInt32 unixPathLeft;
1389
unixPathLeft = strlen(unixPath);
1390
macPathLen = strlen(*macPath);
1393
// copy over the remaining file name, converting
1394
if (pathBufferSize - 1 < macPathLen + unixPathLeft)
1396
// need to grow string
1397
*macPath = realloc(*macPath, macPathLen + unixPathLeft + 1);
1398
err = (*macPath == NULL ? memFullErr : noErr);
1403
// carefully remove the '/''s out of the unix path. If we see an "escaped" /
1404
// we will leave it in there, otherwise we take it out and replace it with a :
1405
// we have to do this before we convert to a mac-path, so we can tell what is
1406
// really a path separator and what is in the name of a file or directory
1407
// Make sure that all of the /�s are :�s in the final pathname
1408
// effectively we do a
1409
// strcat(*macPath, unixPath); while replace all occurrences of / with : in unixPath
1414
dp = *macPath + macPathLen;
1416
for (;*sp != '\0'; sp++, dp++)
1418
if (*sp == PR_DIRECTORY_SEPARATOR)
1420
// if we can look at the previous character
1423
// check to see if previous character is an escape
1426
// leave it in, and cycle
1431
*dp = PR_PATH_SEPARATOR;
1435
*dp = PR_PATH_SEPARATOR;
1444
*dp = '\0'; // NULL terminate *macPath
1447
// we used to check here, now we check above, we leave this in
1448
// the debug build to make sure we didn't screw up
1449
// Make sure that all of the /�s are :�s in the final pathname
1450
for (temp = *macPath + strlen(*macPath) - strlen(unixPath); *temp != '\0'; temp++) {
1452
if (*temp == PR_DIRECTORY_SEPARATOR)
1454
DebugStr("\pFound a slash");
1455
*temp = PR_PATH_SEPARATOR;
1463
Exit_ConvertUnixPathToMacPath:
1468
// Hey! Before you delete this "hack" you should look at how it's being
1469
// used by sun-java/netscape/applet/appletStubs.c.
1470
PR_IMPLEMENT (OSErr)
1471
ConvertMacPathToUnixPath(const char *macPath, char **unixPath)
1474
// Get minimal version working
1478
*unixPath = malloc(strlen(macPath) + 2); // Add one for the front slash, one for null
1479
if (*unixPath == NULL)
1480
return (memFullErr);
1482
unixPathPtr = *unixPath;
1484
*unixPathPtr++ = PR_DIRECTORY_SEPARATOR;
1487
// Translate all colons to slashes
1488
if (*macPath == PR_PATH_SEPARATOR)
1489
*unixPathPtr = PR_DIRECTORY_SEPARATOR;
1491
*unixPathPtr = *macPath;
1495
} while (*macPath != NULL);
1497
// Terminate the string
1498
*unixPathPtr = '\0';
1504
ConvertUnixPathToFSSpec(const char *unixPath, FSSpec *fileSpec)
1510
convertError = ConvertUnixPathToMacPath(unixPath, &macPath);
1511
if (convertError != noErr)
1512
return convertError;
1514
len = strlen(macPath);
1516
if (*macPath == PR_PATH_SEPARATOR)
1518
if (len < sizeof(Str255))
1522
Str255 pascalMacPath;
1524
convertError = HGetVol(NULL, &vRefNum, &dirID);
1525
if (convertError == noErr)
1527
PStrFromCStr(macPath, pascalMacPath);
1528
convertError = FSMakeFSSpec(vRefNum, dirID, pascalMacPath, fileSpec);
1532
convertError = paramErr;
1536
convertError = FSpLocationFromFullPath(len, macPath, fileSpec);
1537
if (convertError == fnfErr)
1540
Str255 pascalMacPath;
1543
PStrFromCStr(macPath, pascalMacPath);
1545
FSpLocationFromFullPath does not work for directories unless there is
1546
a ":" at the end. We will make sure of an existence of a directory.
1547
If so, the returned fileSpec is valid from FSpLocationFromFullPath eventhough
1548
it returned an error.
1550
pb.hFileInfo.ioNamePtr = pascalMacPath;
1551
pb.hFileInfo.ioVRefNum = 0;
1552
pb.hFileInfo.ioDirID = 0;
1553
pb.hFileInfo.ioFDirIndex = 0;
1555
err = PBGetCatInfoSync(&pb);
1557
convertError = noErr;
1563
return (convertError);
1567
FILE *_OS_FOPEN(const char *filename, const char *mode)
1570
char *macFileName = NULL;
1573
err = ConvertUnixPathToMacPath(filename, &macFileName);
1577
result = fopen(macFileName, mode);
1579
PR_DELETE(macFileName);
1584
_PR_MD_CURRENT_THREAD()->md.osErrCode = err;
1591
short GetVolumeRefNumFromName(const char *cTgtVolName)
1595
char *cVolName = NULL;
1599
hPB.volumeParam.ioVolIndex = 0;
1600
hPB.volumeParam.ioNamePtr = pVolName;
1602
hPB.volumeParam.ioVolIndex++;
1603
err = PBHGetVInfoSync(&hPB);
1604
CStrFromPStr(pVolName, &cVolName);
1605
if (strcmp(cTgtVolName, cVolName) == 0) {
1606
refNum = hPB.volumeParam.ioVRefNum;
1607
PR_DELETE(cVolName);
1610
PR_DELETE(cVolName);
1611
} while (err == noErr);
1618
static OSErr GetFullPath(short vRefNum, long dirID, char **fullPath, int *strSize)
1620
Str255 pascalDirName;
1622
char *tmpPath = NULL; // needed since sprintf isn�t safe
1627
// get the full path of the temp folder.
1630
*fullPath = malloc(*strSize); // How big should this thing be?
1631
require_action (*fullPath != NULL, errorExit, err = memFullErr;);
1633
tmpPath = malloc(*strSize);
1634
require_action (tmpPath != NULL, errorExit, err = memFullErr;);
1636
strcpy(*fullPath, ""); // Clear C result
1637
strcpy(tmpPath, "");
1638
pascalDirName[0] = 0; // Clear Pascal intermediate string
1640
myPB.dirInfo.ioNamePtr = &pascalDirName[0];
1641
myPB.dirInfo.ioVRefNum = vRefNum;
1642
myPB.dirInfo.ioDrParID = dirID;
1643
myPB.dirInfo.ioFDirIndex = -1; // Getting info about
1646
myPB.dirInfo.ioDrDirID = myPB.dirInfo.ioDrParID;
1648
err = PBGetCatInfoSync(&myPB);
1649
require(err == noErr, errorExit);
1651
// Move the name into C domain
1652
memcpy(&cDirName, &pascalDirName, 256);
1653
p2cstr((unsigned char *)&cDirName); // Changes in place!
1655
if ((strlen(cDirName) + strlen(*fullPath)) > *strSize) {
1656
// We need to grow the string, do it in 256 byte chunks
1658
*fullPath = PR_REALLOC(*fullPath, *strSize);
1659
require_action (*fullPath != NULL, errorExit, err = memFullErr;);
1661
tmpPath = PR_REALLOC(tmpPath, *strSize);
1662
require_action (tmpPath != NULL, errorExit, err = memFullErr;);
1664
sprintf(tmpPath, "%s:%s", cDirName, *fullPath);
1665
strcpy(*fullPath, tmpPath);
1666
} while (myPB.dirInfo.ioDrDirID != fsRtDirID);
1674
PR_DELETE(*fullPath);
1681
static OSErr CreateMacPathFromUnixPath(const char *unixPath, char **macPath)
1683
// Given a Unix style path with '/' directory separators, this allocates
1684
// a path with Mac style directory separators in the path.
1686
// It does not do any special directory translation; use ConvertUnixPathToMacPath
1693
PR_ASSERT(unixPath != nil);
1694
if (nil == unixPath) {
1699
// If unixPath is a zero-length string, we copy ":" into
1700
// macPath, so we need a minimum of two bytes to handle
1702
*macPath = malloc(strlen(unixPath) + 2); // Will be enough extra space.
1703
require_action (*macPath != NULL, exit, err = memFullErr;);
1708
if (PL_strchr(src, PR_DIRECTORY_SEPARATOR) == src) // If we�re dealing with an absolute
1709
src++; // path, skip the separator
1711
*(tgt++) = PR_PATH_SEPARATOR;
1713
if (PL_strstr(src, UNIX_THIS_DIRECTORY_STR) == src) // If it starts with ./
1714
src += 2; // skip it.
1717
{ // deal with the rest of the path
1718
if (PL_strstr(src, UNIX_PARENT_DIRECTORY_STR) == src) { // Going up?
1719
*(tgt++) = PR_PATH_SEPARATOR; // simply add an extra colon.
1722
else if (*src == PR_DIRECTORY_SEPARATOR) { // Change the separator
1723
*(tgt++) = PR_PATH_SEPARATOR;
1727
*(tgt++) = *(src++);
1730
*tgt = NULL; // make sure it�s null terminated.
1736
static OSErr ConvertUnixPathToMacPath(const char *unixPath, char **macPath)
1742
// Convert UNIX style path names (with . and / separators) into a Macintosh
1743
// style path (with :).
1745
// There are also a couple of special paths that need to be dealt with
1746
// by translating them to the appropriate Mac special folders. These include:
1748
// /usr/tmp/file => {TempFolder}file
1750
// The file conversions we need to do are as follows:
1753
// dir/file => :dir:file
1755
// ../file => ::file
1756
// ../dir/file => ::dir:file
1757
// /file => ::BootDrive:file
1758
// /dir/file => ::BootDrive:dir:file
1761
if (*unixPath != PR_DIRECTORY_SEPARATOR) { // Not root relative, just convert it.
1762
err = CreateMacPathFromUnixPath(unixPath, macPath);
1766
// We�re root-relative. This is either a special Unix directory, or a
1767
// full path (which we�ll support on the Mac since they might be generated).
1768
// This is not condoning the use of full-paths on the Macintosh for file
1775
char isNetscapeDir = false;
1777
// Are we dealing with the temp folder?
1778
if (strncmp(unixPath, "/usr/tmp", strlen("/usr/tmp")) == 0){
1780
if (*unixPath == PR_DIRECTORY_SEPARATOR)
1781
unixPath++; // Skip the slash
1782
err = FindFolder(kOnSystemDisk, kTemporaryFolderType, kCreateFolder, // Create if needed
1783
&foundVRefNum, &foundDirID);
1786
if (strncmp(unixPath, "/tmp", strlen("/tmp")) == 0) {
1787
unixPath += 4; // Skip the slash
1788
if (*unixPath == PR_DIRECTORY_SEPARATOR)
1789
unixPath++; // Skip the slash
1790
err = FindFolder(kOnSystemDisk, kTemporaryFolderType, kCreateFolder, // Create if needed
1791
&foundVRefNum, &foundDirID);
1794
else if (strncmp(unixPath, "/usr", strlen("/usr")) == 0) {
1796
int usrNetscapePathLen;
1798
usrNetscapePathLen = strlen("/usr/local/netscape/");
1800
if (strncmp(unixPath, "/usr/local/netscape/", usrNetscapePathLen) == 0) {
1801
unixPath += usrNetscapePathLen;
1802
// err = FindPreferencesFolder(&foundVRefNum, &foundDirID);
1804
isNetscapeDir = true;
1808
dprintf("Unable to translate Unix file path %s to Mac path\n", unixPath);
1810
goto Exit_ConvertUnixPathToMacPath;
1816
// This is a root relative directory, we�ll just convert the whole thing.
1817
err = CreateMacPathFromUnixPath(unixPath, macPath);
1818
goto Exit_ConvertUnixPathToMacPath;
1821
// We�re dealing with a special folder
1823
// Get the path to the root-relative directory
1824
err = GetFullPath(foundVRefNum, foundDirID, macPath, &pathBufferSize); // mallocs macPath
1828
// copy over the remaining file name, converting
1829
if (pathBufferSize < (strlen(*macPath) + strlen(unixPath))) {
1830
// need to grow string
1831
*macPath = PR_REALLOC(*macPath, (strlen(*macPath) + strlen(unixPath) +
1832
(isNetscapeDir ? strlen("Netscape �:") : 0)));
1833
err = (*macPath == NULL ? memFullErr : noErr);
1837
strcat(*macPath, "Netscape �:");
1840
strcat(*macPath, unixPath);
1842
// Make sure that all of the /�s are :�s in the final pathname
1844
for (temp = *macPath + strlen(*macPath) - strlen(unixPath); *temp != '\0'; temp++) {
1845
if (*temp == PR_DIRECTORY_SEPARATOR)
1846
*temp = PR_PATH_SEPARATOR;
1853
Exit_ConvertUnixPathToMacPath:
1859
ConvertUnixPathToFSSpec(const char *unixPath, FSSpec *fileSpec)
1865
convertError = ConvertUnixPathToMacPath(unixPath, &macPath);
1866
if (convertError != noErr)
1867
return convertError;
1869
len = strlen(macPath);
1871
if (*macPath == PR_PATH_SEPARATOR)
1873
if (len < sizeof(Str255))
1877
Str255 pascalMacPath;
1879
convertError = HGetVol(NULL, &vRefNum, &dirID);
1880
if (convertError == noErr)
1882
PStrFromCStr(macPath, pascalMacPath);
1883
convertError = FSMakeFSSpec(vRefNum, dirID, pascalMacPath, fileSpec);
1887
convertError = paramErr;
1891
convertError = FSpLocationFromFullPath(len, macPath, fileSpec);
1896
return (convertError);
1903
**********************************************************************
1905
* Memory-mapped files are not implementable on the Mac.
1907
**********************************************************************
1910
PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size)
1912
#pragma unused (fmap, size)
1914
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
1918
PRInt32 _MD_GetMemMapAlignment(void)
1920
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
1929
#pragma unused (fmap, offset, len)
1931
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
1935
PRStatus _MD_MemUnmap(void *addr, PRUint32 len)
1937
#pragma unused (addr, len)
1939
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
1943
PRStatus _MD_CloseFileMap(PRFileMap *fmap)
1945
#pragma unused (fmap)
1947
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);